Kernel: an extrusion's cap is in its own plane however thin the extrusion

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 <noreply@anthropic.com>
This commit is contained in:
jyviko
2026-10-28 23:48:58 -04:00
committed by Irakli Kourtis
parent 1cb713c966
commit db9cf693a5
2 changed files with 9 additions and 1 deletions
+3
View File
@@ -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();
+6 -1
View File
@@ -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)))