mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-08-09 09:21:46 +00:00
493a798283
IfcGeom::util::wire_intersections only detected self-intersecting profile boundaries where two edges cross transversally, since GeomAPI_ExtremaCurveCurve never reports a single well-defined extremum for edges that are parallel to each other. A profile made of several disjoint loops chained together through a shared, overlapping return path (a self-intersection built from collinear overlapping edges rather than a crossing) went entirely undetected, so the raw invalid wire reached OCCT's face builder unmodified. OpenCascadeKernel::convert for a face additionally collapsed any detected self-intersection cycles down to the single largest one via select_largest, which is correct for spurious export noise but silently discards real loops when a lone boundary (no holes) resolves into several disjoint, genuinely separate parts of the same profile. Fixes issue #6287: an IfcArbitraryClosedProfileDef boundary shaped like a comb of four rectangles joined by overlapping zero-width vertical segments produced one merged, overlong extrusion instead of four separate ones, because the overlap was invisible to the crossing-only detector and, once detected in other cases, all but the largest cycle was thrown away. wire_utils.cpp adds collinear_overlap_point, which checks whether an edge endpoint lies strictly inside another parallel edge's span along their common line, and feeds that point into the existing crossing based cycle isolation logic unchanged. face.cpp now keeps every sufficiently large cycle (at least a tenth of the largest one's area) as its own outer wire when a lone boundary self-intersects, mirroring how multiple genuine outer boundaries are already turned into a compound of faces, instead of reducing them all to one. Verified against the reporter's attached file: the opening's own comb shaped profile previously produced a single merged solid spanning the full profile length (72 to 36 triangles, no gaps between window positions); it now produces four disjoint prisms with the correct gaps between them. A sweep of test/input (258 files) is byte identical for 252 files; the remaining differences recover previously missing subtraction geometry in known regression fixtures (1015, 336) or are floating point noise below 1e-12 relative (377, 487), consistent with a pre-existing nondeterminism already present in the unmodified build on at least one other fixture (423). Generated with the assistance of an AI coding tool.