From 04310a5c9a95bb7e86de9b2497619b583812dcec Mon Sep 17 00:00:00 2001 From: Petru Conduraru Date: Sun, 19 Jul 2026 21:37:37 +0300 Subject: [PATCH] ifcgeom: retry failed batched opening cuts sequentially (#487) convert_openings groups openings with similar minimal edge lengths into a single multi-tool BOPAlgo_CUT (batching introduced in 7c1071f588 for robustness and speed on the common case). On the file attached to #487 one such batch of 3 openings fails the interference checks at every fuzziness the retry ladder is allowed to attempt, because the ladder rightly refuses to raise fuzziness past the smallest real edge length of the 20mm recess operand, and the whole batch is dropped, leaving the wall uncut. Each of those openings subtracts cleanly when cut on its own. This adds a gated last resort in boolean_operation: only after a multi-tool CUT has exhausted every fuzziness attempt, reattempt it by applying the same tool operands one at a time, each cut feeding the next. The batched path stays the default; single-tool cuts and non-CUT operations are unaffected, and the fallback is all-or-nothing so a partial sequential success never silently replaces the operand. This change was written with the assistance of an AI coding tool. --- .../kernels/opencascade/boolean_utils.cpp | 34 +++++++++++++++++-- .../kernels/opencascade/boolean_utils.h | 2 +- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/ifcgeom/kernels/opencascade/boolean_utils.cpp b/src/ifcgeom/kernels/opencascade/boolean_utils.cpp index d104c7ff94..6e1f1d1ba3 100644 --- a/src/ifcgeom/kernels/opencascade/boolean_utils.cpp +++ b/src/ifcgeom/kernels/opencascade/boolean_utils.cpp @@ -832,7 +832,7 @@ bool IfcGeom::util::points_on_planar_face_generator::operator()(gp_Pnt& p) { } -bool IfcGeom::util::boolean_operation(const boolean_settings& settings, const TopoDS_Shape& a_input, const NCollection_List& b_input, BOPAlgo_Operation op, TopoDS_Shape& result, double fuzziness) { +bool IfcGeom::util::boolean_operation(const boolean_settings& settings, const TopoDS_Shape& a_input, const NCollection_List& b_input, BOPAlgo_Operation op, TopoDS_Shape& result, double fuzziness, bool retry_sequentially_on_failure) { using namespace std::string_literals; const bool do_unify = true; @@ -1417,7 +1417,37 @@ bool IfcGeom::util::boolean_operation(const boolean_settings& settings, const To } if (!success) { if (allow_retry) { - return boolean_operation(settings, a, b, op, result, new_fuzziness); + return boolean_operation(settings, a, b, op, result, new_fuzziness, retry_sequentially_on_failure); + } else if (op == BOPAlgo_CUT && retry_sequentially_on_failure && b.Extent() > 1) { + // #487: a batched cut with multiple simultaneous tool operands (see + // OpenCascadeKernel::convert_openings, which groups openings by + // similar edge length) can fail every fuzziness attempt above even + // when each tool cuts the operand individually without issue. Retry + // by applying the tools one at a time instead, feeding the result of + // each cut into the next. b.Extent() > 1 both gates this to genuine + // multi-tool batches and guards against re-entering this branch, + // since every sequential sub-cut below has exactly one tool operand. + settings.log().Notice("GEO", 403, "Retrying cut with " + std::to_string(b.Extent()) + " operands applied sequentially"); + + TopoDS_Shape sequential_result = a; + bool sequential_ok = true; + NCollection_List::Iterator it(b); + for (; it.More(); it.Next()) { + TopoDS_Shape step_result; + if (!boolean_operation(settings, sequential_result, it.Value(), op, step_result)) { + sequential_ok = false; + break; + } + sequential_result = step_result; + } + + if (sequential_ok) { + settings.log().Notice("GEO", 404, "Sequential opening subtraction fallback succeeded"); + result = sequential_result; + return true; + } + + settings.log().Notice("GEO", 154, "No longer attempting boolean operation with higher fuzziness"); } else { settings.log().Notice("GEO", 154, "No longer attempting boolean operation with higher fuzziness"); } diff --git a/src/ifcgeom/kernels/opencascade/boolean_utils.h b/src/ifcgeom/kernels/opencascade/boolean_utils.h index 55bd960130..8013ebdd79 100644 --- a/src/ifcgeom/kernels/opencascade/boolean_utils.h +++ b/src/ifcgeom/kernels/opencascade/boolean_utils.h @@ -104,7 +104,7 @@ namespace IfcGeom { Logger& log() const { return logger ? *logger : Logger::Root(); } }; - bool boolean_operation(const boolean_settings& settings, const TopoDS_Shape&, const NCollection_List&, BOPAlgo_Operation, TopoDS_Shape&, double fuzziness = -1.); + bool boolean_operation(const boolean_settings& settings, const TopoDS_Shape&, const NCollection_List&, BOPAlgo_Operation, TopoDS_Shape&, double fuzziness = -1., bool retry_sequentially_on_failure = true); bool boolean_operation(const boolean_settings& settings, const TopoDS_Shape&, const TopoDS_Shape&, BOPAlgo_Operation, TopoDS_Shape&, double fuzziness = -1.);