Retry boolean CUT that OCCT reports done but subtracts no volume (#5630)

An IfcOpeningElement voiding a dense tessellated first operand (an
IfcGeographicElement terrain built from an IfcPolygonalFaceSet) is not
cut: the opening solid overlaps the terrain, yet the result keeps the
full volume. OCCT BRepAlgoAPI_Cut returns IsDone at the tight default
fuzzy value but subtracts essentially nothing, and because it reports
success the existing escalating-fuzziness retry never runs. At a higher
fuzziness (still well under the min-edge ceiling) the section is computed
correctly.

After a CUT is deemed valid, and only while a higher-fuzziness retry is
still permitted, compare the result volume to the first operand. If the
removed fraction is negligible (< 1e-4 of operand A) treat the CUT as a
failure so the escalation runs and finds a fuzzy value at which the cut
actually happens. When no retry is left the result is accepted as before,
so a genuinely tiny legitimate cut can never lose its outcome. New
diagnostic GEO 156.

Verified on OCC 7.9.2: the terrain goes from 7330.878 (no hole, 459 verts)
to 6058.593 (real hole, 377 verts, still a closed manifold), removing the
expected 1272 wedge. Regression: five currently-passing cut models
(#619, #4118, #5473, #5186, #511) are byte-identical, and across the full
1447-object source file exactly one product (the terrain) changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Petru Conduraru
2026-07-11 07:48:52 +03:00
parent ade03b171a
commit 60033981b3
@@ -1218,6 +1218,24 @@ bool IfcGeom::util::boolean_operation(const boolean_settings& settings, const To
}
}
// #5630 A solid-solid CUT whose tool operands survived the disjoint/
// touching/narrow elimination above is expected to remove material. On
// dense tessellated first operands (e.g. IfcPolygonalFaceSet terrain)
// OCCT's BOP can silently return an essentially unchanged result at a
// tight fuzzy value, reporting IsDone yet subtracting ~nothing. Detect
// this near zero volume removal and, while a higher fuzziness retry is
// still permitted, treat it as a failure so the existing escalation can
// find a fuzzy value at which the section is actually computed. When no
// retry is left the result is accepted as is, preserving prior behaviour.
if (success && op == BOPAlgo_CUT && allow_retry) {
const double va = shape_volume(a);
const double vr = shape_volume(r);
if (va > 1.e-9 && (va - vr) < va * 1.e-4) {
success = false;
Logger::Root().Notice("GEO", 156, "Boolean subtraction removed no volume, retrying with higher fuzziness");
}
}
if (success) {
{