From b9d4ad74f4af133df144fc34f9be91378f449be9 Mon Sep 17 00:00:00 2001 From: jyviko <2501648+jyviko@users.noreply.github.com> Date: Tue, 27 Oct 2026 21:35:30 -0400 Subject: [PATCH] kernel: a planar region inside another's hole keeps its material MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SMLib planar sheet united every region's material and then cut every region's holes from it, so a region lying in another's hole (the island inside a ring cut) was cut away with that hole. Each region is now its own material less its own holes, and the sheet is the regions united. Fixes sheet::tests::ring_cut_retains_exact_central_material_island (the flat solid lost the island's π·25·2.5 mm³) and the kernel's compound_planar_extrusion_normalizes_overlap_and_retains_islands. Co-Authored-By: Claude Opus 5.5 --- crates/kernel/src/prism.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/crates/kernel/src/prism.rs b/crates/kernel/src/prism.rs index 6e49fe704e..7f7d97dada 100644 --- a/crates/kernel/src/prism.rs +++ b/crates/kernel/src/prism.rs @@ -128,23 +128,27 @@ mod sheets { /// SmBrep.cpp: "spine points"), as where pieces of material touch at a corner or an outline /// passes a point twice. So each loop, oriented (outer counter-clockwise, holes clockwise), is /// split where it passes a point twice into simple loops, and each simple loop is a face of its - /// own; those running counter-clockwise are material, the others holes. The sheet is the - /// material united less the holes united (SMLib's 2D boolean, which takes touching faces). - /// `tol` (mm): how close a loop's passes through one point are. + /// own; those running counter-clockwise are material, the others holes. Each region is its + /// material united less its holes united (SMLib's 2D boolean, which takes touching faces), and + /// the sheet is the regions united: a region may lie in another's hole (the island inside a + /// ring), where the other's hole must not take it. `tol` (mm): how close a loop's passes + /// through one point are. pub(super) fn sheet(regions: &[Region2], tol: f64) -> Result { - let (mut material, mut holes) = (Vec::new(), Vec::new()); + let mut sheets = Vec::with_capacity(regions.len()); for r in regions { + let (mut material, mut holes) = (Vec::new(), Vec::new()); let loops = std::iter::once(r.outer.ccw()).chain(r.holes.iter().map(|h| h.ccw().reversed())); for piece in loops.flat_map(|lp| simple_loops(lp, tol * 10.0)) { let face = face(&piece)?; if piece.signed_area() > 0.0 { material.push(face) } else { holes.push(face) } } + let Some(material) = united(material)? else { continue }; + sheets.push(match united(holes)? { + Some(holes) => combine(&material, &holes, BoolOp::Cut)?.ok_or_else(|| failed("sheet", "the holes take all the material"))?, + None => material, + }); } - let material = united(material)?.ok_or_else(|| failed("sheet", "the regions hold no material"))?; - match united(holes)? { - Some(holes) => combine(&material, &holes, BoolOp::Cut)?.ok_or_else(|| failed("sheet", "the holes take all the material")), - None => Ok(material), - } + united(sheets)?.ok_or_else(|| failed("sheet", "the regions hold no material")) } /// A simple closed loop as one planar face.