STEP: reader

Own ISO 10303-21 parser and AP203/AP214/AP242 geometry reader in the kernel
(step_import): planes, cylinders, cones, spheres, tori, B-spline and rational
surfaces, surfaces of revolution/extrusion; lines, conics, B-spline curves;
breps with voids, faceted breps, shell models; units (SI prefixes, conversion
based), product names, colours, assemblies (NAUO + item-defined
transformations, mapped items). Hostile input is capped and guarded; faces
that cannot be read are left out and reported. Faces that wrap around a
closed non-periodic direction (pipe tori, revolved B-spline bands) are split
for truck's mesher. Bodies are meshed per shell so open imports never panic.

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-07 21:01:04 +00:00
1 parent 6c819a7b06
commit ed611ed7ba
8 files changed
+3109 -6

No files matched your search

+22 -5
View File
@@ -34,6 +34,23 @@ pub struct FaceInfo {
pub plane_normal: Option<Vec3>,
}
type MeshedShell = truck_topology::Shell<mt::Point3, PolylineCurve<mt::Point3>, Option<PolygonMesh>>;
/// Triangulate each shell (not the solid: truck's solid constructor panics on the open or
/// non-manifold shells an import can produce). Faces whose boundary is not exactly on the
/// surface (imported geometry within the file's tolerance) fail the exact parameter search;
/// such shells are meshed again projecting onto the surface.
fn mesh_shells(solid: &Solid, tol: f64) -> Vec<MeshedShell> {
solid
.boundaries()
.iter()
.map(|sh| {
let m = sh.triangulation(tol);
if m.face_iter().any(|f| f.surface().is_none()) { sh.robust_triangulation(tol) } else { m }
})
.collect()
}
pub(crate) fn p3(v: Vec3) -> mt::Point3 {
mt::Point3::new(v.x, v.y, v.z)
}
@@ -65,7 +82,7 @@ impl Body {
return Err(KernelError::Failed("empty result".into()));
}
// Orient outward: a closed solid must have positive volume.
let v = guard("orient", || Ok(solid.triangulation(1.0).to_polygon().volume()))?;
let v = guard("orient", || Ok(mesh_shells(&solid, 1.0).iter().map(|s| s.to_polygon().volume()).sum::<f64>()))?;
if v < 0.0 {
solid.not();
}
@@ -98,9 +115,9 @@ impl Body {
let tol = if tol.is_finite() && tol > 0.0 { tol } else { 0.05 };
let tol = tol.max(self.size() * 1e-6);
guard("tessellate", || {
let meshed = self.solid.triangulation(tol);
let meshed = mesh_shells(&self.solid, tol);
let mut out = Mesh::default();
for (fi, face) in meshed.face_iter().enumerate() {
for (fi, face) in meshed.iter().flat_map(|s| s.face_iter()).enumerate() {
let Some(pm) = face.surface() else { continue };
let flip = !face.orientation();
let base = u32::try_from(out.positions.len()).unwrap_or(u32::MAX);
@@ -142,7 +159,7 @@ impl Body {
}
}
let mut seen = std::collections::HashMap::new();
for e in meshed.edge_iter() {
for e in meshed.iter().flat_map(|s| s.edge_iter()) {
if seen.contains_key(&e.id()) {
continue;
}
@@ -152,7 +169,7 @@ impl Body {
}
// Which faces each edge bounds.
out.edge_faces = vec![Vec::new(); out.edges.len()];
for (fi, face) in meshed.face_iter().enumerate() {
for (fi, face) in meshed.iter().flat_map(|s| s.face_iter()).enumerate() {
for e in face.edge_iter() {
if let Some(slot) = seen.get(&e.id()).and_then(|i| out.edge_faces.get_mut(*i)) {
let f = u32::try_from(fi).unwrap_or(u32::MAX);