face.delete heal: false leaves an open surface body (#861)
Delete Face takes heal (default true, as before). With heal: false the picked faces are removed and the gap stays open, leaving the remaining faces as a surface body (kernel remove_faces); deleting every face is refused. Saved only when false, noted under format 18.
This commit is contained in:
1 parent
c616f50bd0
commit
b52d4390a0
7 files changed
+80
-5
No files matched your search
@@ -113,7 +113,7 @@ pub use step::step_export;
|
||||
pub use step_in::{ImportedBody, StepImport, StepNode, step_import, step_import_shared, step_orientation_errors, step_validate};
|
||||
pub use step_out::{ExportBody, ExportProduct, StepHeader, step_export_bodies, step_export_products};
|
||||
pub use surface_kind::{FaceSurface, face_surface};
|
||||
pub use surfaces::{copy_faces, extend, extrude_sheet, patch_edges, patch_region, reverse_faces, stitch, thicken, trim, unstitch};
|
||||
pub use surfaces::{copy_faces, extend, extrude_sheet, patch_edges, patch_region, remove_faces, reverse_faces, stitch, thicken, trim, unstitch};
|
||||
pub use sweep_path::sweep_path;
|
||||
pub use topo::{CylinderFace, cylinder_face_at};
|
||||
pub use topo::{TopoCounts, merged_topology, seam_flags};
|
||||
|
||||
@@ -247,6 +247,21 @@ pub fn reverse_faces(body: &Body, at: &[Vec3]) -> Result<Body> {
|
||||
Ok(rebuilt_body(surface_body(faces), &[&body.solid], &rec))
|
||||
}
|
||||
|
||||
/// A body with some of its faces (indices in its face order) taken away and the gap left open:
|
||||
/// a surface body of the faces left. Deleting every face is refused. The history is over
|
||||
/// `[body]`: each face left is the body's.
|
||||
pub fn remove_faces(body: &Body, faces: &[usize]) -> Result<Body> {
|
||||
body.require_brep("Delete Face")?;
|
||||
if faces.is_empty() {
|
||||
return Err(KernelError::Invalid("no faces given".into()));
|
||||
}
|
||||
let left: Vec<mt::Face> = body.solid.face_iter().enumerate().filter(|(i, _)| !faces.contains(i)).map(|(_, f)| f.clone()).collect();
|
||||
if left.is_empty() {
|
||||
return Err(KernelError::Invalid("that would delete every face of the body".into()));
|
||||
}
|
||||
Ok(rebuilt_body(surface_body(left), &[&body.solid], &Rebuild::default()))
|
||||
}
|
||||
|
||||
/// Index of the face nearest a point (over its mesh triangles).
|
||||
fn nearest_face(body: &Body, p: Vec3) -> Result<usize> {
|
||||
let m = body.tessellate(body.size() * 2e-3)?;
|
||||
@@ -807,6 +822,23 @@ mod tests {
|
||||
assert!(reverse_faces(&box_solid(Vec3::ZERO, Vec3::new(1.0, 1.0, 1.0)).unwrap(), &[]).is_err());
|
||||
}
|
||||
|
||||
/// A box's top taken away without healing: a five-face surface; patching the opening and
|
||||
/// stitching closes the same box. Every face, or none, is refused.
|
||||
#[test]
|
||||
fn remove_faces_leaves_the_gap_open() {
|
||||
let b = box_solid(Vec3::ZERO, Vec3::new(10.0, 10.0, 10.0)).unwrap();
|
||||
let top = nearest_face(&b, Vec3::new(5.0, 5.0, 10.0)).unwrap();
|
||||
let open = remove_faces(&b, &[top]).unwrap();
|
||||
assert!(open.is_surface() && open.face_count() == 5);
|
||||
assert!(rel(measure(&open).unwrap().area, 500.0) < 1e-9);
|
||||
let lid = patch_edges(&open, &[Vec3::new(5.0, 0.0, 10.0), Vec3::new(10.0, 5.0, 10.0), Vec3::new(5.0, 10.0, 10.0), Vec3::new(0.0, 5.0, 10.0)])
|
||||
.unwrap();
|
||||
let back = stitch(&[&open, &lid], 1e-6).unwrap();
|
||||
assert!(!back.is_surface() && rel(measure(&back).unwrap().volume, 1000.0) < 1e-9);
|
||||
assert!(remove_faces(&b, &(0..6).collect::<Vec<_>>()).is_err());
|
||||
assert!(remove_faces(&b, &[]).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn patch_and_thicken_a_square() {
|
||||
let r = Region2 {
|
||||
|
||||
Reference in new issue
Block a user