IGES 5.3 import and export; OBJ import; STL reading hardened

- IGES import (kernel iges_in, from the published specification). The file
  is restated as STEP and read by the STEP reader, so an IGES file becomes
  the same Import base feature.
  - Manifold solid B-reps (186/514/510/508/504/502).
  - Trimmed (144), bounded (143) and bare B-spline surfaces, sewn into a
    solid by shared edges, with face orientation made consistent.
  - Curves: lines, arcs, ellipses, composite and rational B-spline curves.
  - Surfaces: planes, analytic surfaces 190-198, B-spline surfaces,
    surfaces of revolution and tabulated cylinders.
  - Transformations, units, colours, names (406 form 15 or labels).
  Open, insert and drop .igs/.iges like STEP.
- IGES export (kernel iges_out): each body as a manifold solid B-rep on the
  surfaces a STEP export uses, with name properties and colours.
  ExportCommand to .igs.
- OBJ import as mesh bodies: objects/groups, polygons fanned, negative
  indices, bad lines reported.
- STL: binary files whose header starts with "solid" and those with a zero
  triangle count read as binary; non-finite vertices dropped; facet
  normals ignored; ASCII with non-UTF-8 names. Mesh bodies get consistent
  winding (stray inverted facets turned over).
- Tests: primitives and features round-trip through IGES (volume, area,
  name, colour). Six trimmed patches sew into a closed box. Hostile IGES,
  OBJ and STL inputs never panic. STL quirks. The engine exports a design
  to IGES and opens it again. All 76 oracle parts round-trip through IGES
  with matching volume (local check).

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 15:17:25 +00:00
1 parent 7a2eb059c4
commit 51ffaf9945
13 files changed
+2615 -31

No files matched your search

+23
View File
@@ -1016,6 +1016,29 @@ fn face_colours_and_opacity_export() {
assert_eq!(looks[0].1.color, [255, 0, 0]);
}
/// IGES: a design exports as IGES and the file opens again as an Import feature with the same
/// bodies, names and colours.
#[test]
fn iges_export_and_open() {
let mut s = Session::default();
run(&mut s, "PrimitiveBox", json!({"length": 40, "width": 30, "height": 5, "body_name": "Plate"}));
run(&mut s, "PrimitiveCylinder", json!({"radius": 4, "height": 20, "base": [10, 10, 5], "body_name": "Pin"}));
run(&mut s, "AppearanceCommand", json!({"bodies": ["Pin"], "color": "#ff0000"}));
let want: Vec<f64> = s.world_state().bodies.iter().map(|b| solvecraft_kernel::measure(&b.body).unwrap().volume).collect();
let p = std::env::temp_dir().join(format!("solvecraft-iges-{}.igs", std::process::id()));
run(&mut s, "ExportCommand", json!({"path": p.to_string_lossy()}));
let mut s2 = Session::default();
run(&mut s2, "doc.open", json!({"path": p.to_string_lossy()}));
let _ = std::fs::remove_file(&p);
let st = s2.world_state();
let names: Vec<&str> = st.bodies.iter().map(|b| b.name.as_str()).collect();
assert_eq!(names, ["Plate", "Pin"]);
for (b, v) in st.bodies.iter().zip(&want) {
assert!(rel(solvecraft_kernel::measure(&b.body).unwrap().volume, *v) < 1e-4);
}
assert_eq!(st.bodies[1].body.color(), Some([1.0, 0.0, 0.0]));
}
/// Components export as a STEP assembly: products, occurrences, placements.
#[test]
fn components_export_as_step_assembly() {