From db9cf693a55be928aca64b1d034508ba4cd08f86 Mon Sep 17 00:00:00 2001 From: jyviko <2501648+jyviko@users.noreply.github.com> Date: Wed, 28 Oct 2026 23:48:58 -0400 Subject: [PATCH] Kernel: an extrusion's cap is in its own plane however thin the extrusion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The roles of a swept body's faces take a face as a cap when all its points lie within a flatness tolerance of the start or end plane, sized to the body (1e-5 of it). A box 0.1 µm thick in a 14 mm body is thinner than that, so each cap lay in both planes and box_solid refused "box role correspondence" (engine component_clone boundary_material_proof_requires_resolvable_stock_and_void). Between parallel start and end planes the tolerance stays under a quarter of their gap; a cap's points lie on its plane to the evaluation's precision. Regression case: a 10 x 10 x 1e-4 box in box_certificates_are_six_construction_roles... Co-Authored-By: Claude Opus 5.5 --- crates/kernel/src/face_source_tests.rs | 3 +++ crates/kernel/src/smlib_sweeps.rs | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/crates/kernel/src/face_source_tests.rs b/crates/kernel/src/face_source_tests.rs index 8c4e16c7de..c46f0008bf 100644 --- a/crates/kernel/src/face_source_tests.rs +++ b/crates/kernel/src/face_source_tests.rs @@ -30,6 +30,9 @@ fn box_certificates_are_six_construction_roles_with_dimension_stable_axes() { // A side far shorter than the body (a 0.3 mm label of a metre): its walls' edges lie // closer together than the sweep's body-sized nearness, and each keeps its own role. (Vec3::new(500., -0.3, 1400.), Vec3::new(1500., 0., 1800.)), + // Far thinner than the body's flatness tolerance (0.1 µm in 14 mm): each cap is in its + // own plane only. + (Vec3::ZERO, Vec3::new(10., 10., 1e-4)), ] { let body = crate::box_solid(a, b).unwrap().with_box_certificate(42).unwrap(); let mut roles = std::collections::HashSet::new(); diff --git a/crates/kernel/src/smlib_sweeps.rs b/crates/kernel/src/smlib_sweeps.rs index 6eb0767668..94cbe3406e 100644 --- a/crates/kernel/src/smlib_sweeps.rs +++ b/crates/kernel/src/smlib_sweeps.rs @@ -272,7 +272,12 @@ mod imp { let m = body.mesh(&crate::MeshTolerance::chord(mesh_tol))?; let faces = face_tris(&m); let n = body.face_count(); - let flat_tol = size * 1e-5 + 1e-9; + // A cap's points lie on its plane to the evaluation's precision. Sized to the body, the + // flatness tolerance can exceed the gap between parallel start and end planes (a box + // 0.1 µm thick in a 14 mm body), which would put each cap in both: it stays under a + // quarter of that gap. + let gap = end.filter(|e| e.normal().cross(start.normal()).len() < 1e-9).map_or(f64::INFINITY, |e| start.height(e.origin).abs()); + let flat_tol = (size * 1e-5 + 1e-9).min(0.25 * gap); let near = 3.0 * mesh_tol + flat_tol; let flat = |f: usize, pl: &Plane| { faces.get(f).is_some_and(|ft| !ft.tris.is_empty() && ft.tris.iter().all(|(t, _)| t.iter().all(|q| pl.height(*q).abs() <= flat_tol)))