mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-03 10:35:58 +00:00
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:
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user