IfcIndexedPolyCurve.Segments was ignored — both IfcArcIndex and
IfcLineIndex collapsed to straight chords through the indexed
points. Every Revit MEP pipe / duct (4-point cross-section + two
IfcArcIndex semicircles forming the bore) tessellated as a square
prism with -77% to -83% volume vs ifcopenshell on G55_RIV (~24k
elements affected).
The fix adds `mesh::indexed_curve` — a shared 2D/3D segment
evaluator that walks Segments, computes the circumcircle through
each ArcIndex triple, and samples at the same CURVE_SAMPLES
density (32 per turn → 16 per semicircle) used elsewhere for
IfcCircleProfileDef. Force-CCW orientation on the polyline output
makes the result consumable by the earcut + extrusion path that
expects standard winding; without that the cap triangulation
inverts and the volume comes out 3× too small even with correct
arc tessellation.
Wired into the three IfcIndexedPolyCurve call sites:
- profile.rs (extrusion profile outer + inner curves)
- curveset.rs (curve-set products)
- boolean.rs (CSG operand curves)
Cross-validation on G55_RIV vs ifcopenshell:
- IfcPipeSegment volume ratio: 0.21 (pre) → 1.003 (post)
- IfcDuctSegment volume ratio: 0.997
- IfcFireSuppressionTerminal: 1.000 (PolygonalFaceSet path
unchanged — regression check)
- Pre-fix structural models (G55_ARK walls / slabs): unchanged at
1.000.
Cache schema bumped to 11 — any RIV / HVAC bundle from a prior
version must be re-extracted. 13 new unit tests in indexed_curve
cover ArcIndex / LineIndex / mixed segments / 3D arcs / unknown
segment fallback. Full Rust suite 129 / 129 green (was 116).
Also bumps __version__ from the stale 0.4.28 to 0.4.30 — proper
fix for the derive-from-one-source mismatch tracked separately as
GH #46.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>