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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QWhMwdqJYQKQq8RCLSL1LT
This commit is contained in:
jyviko
2026-10-21 06:30:20 -04:00
parent 94c1598259
commit ff0f7ccfa1
2 changed files with 44 additions and 9 deletions
+25 -2
View File
@@ -98,10 +98,33 @@ pub fn brep_record(body: &Body) -> Result<solidcraft_geom::brep::Brep> {
/// 9e-7 where the record as given is exact to 1e-7.
#[cfg(feature = "smlib")]
pub fn smlib_body(body: &Body) -> Result<solidcraft_smlib::Brep> {
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<solidcraft_geom::brep::Brep> {
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<solidcraft_smlib::history::Order>)> {
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
+19 -7
View File
@@ -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<b::Brep> {
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<Result<b::Brep>> {
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<b::Brep> {
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<b::Brep> {
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<mt::EdgeID, usize>,
edges: &[(mt::Edge, b::Curve, [f64; 2])],
tolerance: f64,
) -> Result<b::Face> {
) -> Result<Result<b::Face>> {
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::<Result<Vec<_>>>()?;
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::<Vec<_>>();
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<b::Face> {
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.