STEP import: 65/66 Fusion files; bounded meshing

Meshing (vendored truck-meshalgo) works within a budget so hostile files
cannot hang: bounded edge and surface divisions (point caps, depth and
round limits, NaN-safe), and a grid budget per face shared out per shell.
Surface grids refine only the direction that bends; rulings left undivided
get cells comparable to the other direction, so trimmed cylinders (holes,
tee joints) no longer lose volume to long triangles. The helical sweep
imports in 12 s instead of 7 minutes.

Small sphere patches get poles away from them even when the file runs a
seam through the patch (a pyramid's corner blend). Faces the mesher cannot
mesh are reported as import warnings. Test: wild B-spline and far-off
boundaries import in bounded time.

STEP corpus 65/66 (50-sweep-tight-bend: a horn-torus bend whose inner side
collapses to a point; Fusion's own area for it is also off).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
This commit is contained in:
bherbruckandClaude Opus 5.5 committed 2026-10-08 01:28:16 +00:00
1 parent 7a0cc85392
commit 1c10d1e56a
7 files changed
+359 -76

No files matched your search

+10
View File
@@ -233,6 +233,8 @@ struct Reader<'a> {
node_items: HashSet<u64>,
/// Occurrences and mapped representations visited so far.
visits: usize,
/// Items whose meshing was checked.
unmeshed_checked: HashSet<u64>,
bodies: Vec<ImportedBody>,
}
@@ -454,6 +456,13 @@ impl<'a> Reader<'a> {
Ok(body) => {
let own = e.params().first().and_then(Param::as_str).unwrap_or("").trim().to_string();
let name = if own.is_empty() { product.to_string() } else { own };
// Faces the mesher gives up on (or that the budget cut short) are reported.
if self.unmeshed_checked.insert(item) {
let n = body.unmeshed_faces();
if n > 0 {
self.warn(format!("solid '{name}': {n} face(s) could not be meshed and are not shown"));
}
}
out.push(self.bodies.len());
let color = self.colors.get(&item).copied();
self.bodies.push(ImportedBody { name, body: body.with_color(color), color, path: path.to_vec(), closed, file_faces });
@@ -682,6 +691,7 @@ pub fn step_import(text: &str) -> Result<StepImport> {
used_items: HashSet::new(),
node_items: HashSet::new(),
visits: 0,
unmeshed_checked: HashSet::new(),
bodies: Vec::new(),
};
rd.index();