From ff0f7ccfa166edbd2b21827131ab3e374ae1b5a0 Mon Sep 17 00:00:00 2001 From: jyviko <2501648+jyviko@users.noreply.github.com> Date: Wed, 21 Oct 2026 06:30:20 -0400 Subject: [PATCH] kernel: a body's record for SMLib takes a larger tolerance where an edge needs it The record of a truck body is built to the body's working tolerance (1e-6 of its size, at least 1e-5 mm), and truck's offsets and blends leave some edges a few times farther than that from a face they bound: no pcurve fits, so the record and every SMLib boolean on the body failed ("pcurve of loop 0 use 0 ...: the curve at 0 lies 3.2e-5 mm off the surface" in a shelled revolve of the robustness generator). The record for SMLib now retries at ten and a hundred times the working tolerance for the body, its edges and vertices before refusing; the saved native body keeps the working tolerance. Robustness seed 64 (a filleted extrusion joined to a cylinder), refused this way before, now builds as truck does to 1.6e-6 of its volume. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01QWhMwdqJYQKQq8RCLSL1LT --- crates/kernel/src/native.rs | 27 +++++++++++++++++++++++++-- crates/kernel/src/native/adapter.rs | 26 +++++++++++++++++++------- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/crates/kernel/src/native.rs b/crates/kernel/src/native.rs index 79e13a8f0b..aba5ccab9f 100644 --- a/crates/kernel/src/native.rs +++ b/crates/kernel/src/native.rs @@ -98,10 +98,33 @@ pub fn brep_record(body: &Body) -> Result { /// 9e-7 where the record as given is exact to 1e-7. #[cfg(feature = "smlib")] pub fn smlib_body(body: &Body) -> Result { - let record = brep_record(body)?; + let record = tolerant_record(body)?; solidcraft_smlib::Brep::from_record(&record, true).map_err(|e| KernelError::Failed(format!("SMLib: {e}"))) } +/// [`brep_record`] for another kernel: where an edge lies a little farther from a face it bounds +/// than the body's working tolerance (truck's offsets leave edges some 3e-5 mm off their faces), +/// the record takes a tolerance ten, then a hundred times as large for the body, its edges and +/// vertices, as the kernel it goes to works to tolerances (SMLib's own are far larger). Past +/// that it is refused. +#[cfg(feature = "smlib")] +fn tolerant_record(body: &Body) -> Result { + guard("B-rep record", || { + if body.mesh.is_some() { + return Err(invalid("mesh bodies are source blobs, not B-rep")); + } + let working = crate::fill::face_tolerance(body); + let mut last = None; + for factor in [1.0, 10.0, 100.0] { + match adapter::encode_within(body, working * factor)? { + Ok(record) => return Ok(record), + Err(e) => last = Some(e), + } + } + Err(last.unwrap_or_else(|| invalid("no record"))) + }) +} + /// The truck solid of a schema 4 record, as a saved body's is built: tests compare records /// made elsewhere (the STEP reader's) with truck's own import. #[cfg(test)] @@ -181,7 +204,7 @@ pub(crate) fn body_from_smlib_ordered(brep: &solidcraft_smlib::Brep) -> Result<( #[cfg(feature = "smlib")] pub(crate) fn smlib_body_ordered(body: &Body) -> Result<(solidcraft_smlib::Brep, Option)> { use solidcraft_smlib::{Brep, CheckLevel}; - let record = brep_record(body)?; + let record = tolerant_record(body)?; let import = |trusted: bool| Brep::from_record(&record, trusted).map_err(|e| KernelError::Failed(format!("SMLib: {e}"))); let valid = |b: &Brep| b.check(CheckLevel::All).is_ok_and(|c| c.is_valid()); // A record SMLib's check refuses as given (a gap a little over the record's tolerance, a diff --git a/crates/kernel/src/native/adapter.rs b/crates/kernel/src/native/adapter.rs index 55971cd1ef..f0a8a7cad1 100644 --- a/crates/kernel/src/native/adapter.rs +++ b/crates/kernel/src/native/adapter.rs @@ -97,8 +97,14 @@ fn working_tolerance(body: &Body) -> f64 { /// The record of `body`'s solid. Each face's pcurves come from the records written for it /// (`brep::pcurve`, the routine every backend shares), within the body's working tolerance. pub(super) fn encode(body: &Body) -> Result { + encode_within(body, working_tolerance(body))? +} + +/// The record of `body`'s solid with `tolerance` for the body, its edges and vertices. +/// `Ok(Err(_))`: some edge lies farther than that from a face it bounds, so no pcurve fits it +/// within the tolerance. +pub(super) fn encode_within(body: &Body, tolerance: f64) -> Result> { let solid = &body.solid; - let tolerance = working_tolerance(body); let shells = solid.boundaries(); let regions = regions(body)?; @@ -140,7 +146,10 @@ pub(super) fn encode(body: &Body) -> Result { uses.get_mut(i).ok_or_else(|| invalid("edge use"))?.push(at); } } - faces.push(face_record(face, wires, &edge_index, &edges, tolerance)?); + match face_record(face, wires, &edge_index, &edges, tolerance)? { + Ok(f) => faces.push(f), + Err(e) => return Ok(Err(e)), + } } shell_records.push(b::Shell::Faces { faces: oriented, wires: Vec::new() }); } @@ -163,7 +172,7 @@ pub(super) fn encode(body: &Body) -> Result { let result = b::Brep { version: b::VERSION, tolerance, regions, shells: shell_records, faces, edges: edge_records, vertices }; result.validate().map_err(invalid)?; compatible(&result, Producer::Own)?; - Ok(result) + Ok(Ok(result)) } /// A face's record: its surface, its loops as truck holds them (relative to the surface) and @@ -174,7 +183,7 @@ fn face_record( edge_index: &HashMap, edges: &[(mt::Edge, b::Curve, [f64; 2])], tolerance: f64, -) -> Result { +) -> Result> { let surface = save_surface(&face.surface(), 0)?; let mut uses = Vec::with_capacity(wires.len()); for wire in wires { @@ -188,7 +197,10 @@ fn face_record( .collect::>>()?; uses.push(l); } - let curves = b::pcurve::pcurves(&surface, &uses, tolerance).map_err(pcurve_error)?; + let curves = match b::pcurve::pcurves(&surface, &uses, tolerance) { + Ok(c) => c, + Err(e) => return Ok(Err(pcurve_error(e))), + }; let loops = uses .iter() .zip(curves) @@ -197,7 +209,7 @@ fn face_record( }) .collect::>(); let domain = domain(&surface, &loops)?; - Ok(b::Face { surface, domain, loops }) + Ok(Ok(b::Face { surface, domain, loops })) } /// `face`'s record on its own, its edges numbered in first appearance: what [`encode`] writes @@ -214,7 +226,7 @@ pub(super) fn face_alone(face: &mt::Face, tolerance: f64) -> Result { slot.insert(edges.len() - 1); } } - face_record(face, wires, &edge_index, &edges, tolerance) + face_record(face, wires, &edge_index, &edges, tolerance)? } /// The kernel's error for the pcurve routine's.