diff --git a/src/ifcgeom/IfcBooleanResult.cpp b/src/ifcgeom/IfcBooleanResult.cpp index 94eccfeec0..760c2a614a 100644 --- a/src/ifcgeom/IfcBooleanResult.cpp +++ b/src/ifcgeom/IfcBooleanResult.cpp @@ -25,6 +25,45 @@ #define Kernel MAKE_TYPE_NAME(Kernel) +namespace { + struct opening_sorter { + bool operator()(const std::pair& a, const std::pair& b) const { + return a.first > b.first; + } + }; + + bool apply_in_batches(IfcGeom::util::boolean_settings bst, const TopoDS_Shape& first_operand, std::vector< std::pair >& opening_vector, BOPAlgo_Operation occ_op, TopoDS_Shape& result) { + auto it = opening_vector.begin(); + auto jt = it; + + result = first_operand; + for (;; ++it) { + if (it == opening_vector.end() || jt->first / it->first > 10.) { + + TopTools_ListOfShape opening_list; + for (auto kt = jt; kt < it; ++kt) { + opening_list.Append(kt->second); + } + + TopoDS_Shape intermediate_result; + if (IfcGeom::util::boolean_operation(bst, result, opening_list, BOPAlgo_CUT, intermediate_result)) { + result = intermediate_result; + } else { + return false; + } + + jt = it; + } + + if (it == opening_vector.end()) { + break; + } + } + + return true; + } +} + bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape& shape) { TopoDS_Shape s1; @@ -106,7 +145,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape Logger::Message(Logger::LOG_WARNING, "Empty solid for:", l->FirstOperand()); } - TopTools_ListOfShape second_operand_shapes; + std::vector< std::pair > opening_vector; for (auto& op2 : second_operands) { TopoDS_Shape s2; @@ -135,7 +174,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape if (is_unbounded_halfspace) { TopoDS_Shape temp; double d; - if (util::fit_halfspace(s1, s2, temp, d, getValue(GV_PRECISION))) { + if (util::fit_halfspace(s1, s2, temp, d, getValue(GV_PRECISION) * 1000.)) { // #2665 we also set a precision-independent treshold, because in the boolean op routine // the working fuzziness might still be increased. if (d < getValue(GV_PRECISION) * 20. || d < 0.00002) { @@ -159,12 +198,13 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape } } - second_operand_shapes.Append(s2); + opening_vector.push_back(std::make_pair(util::min_edge_length(s2), s2)); } - /* + std::sort(opening_vector.begin(), opening_vector.end(), opening_sorter()); + // TK: A little debugging trick to output both operands for visual inspection - + /* BRep_Builder builder; TopoDS_Compound compound; builder.MakeCompound(compound); @@ -174,7 +214,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape } shape = compound; return true; - */ + */ #if OCC_VERSION_HEX < 0x60900 // @todo: this currently does not compile anymore, do we still need this? @@ -196,7 +236,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape valid_result = true; for (; it.More(); it.Next()) { TopoDS_Shape part; - if (util::boolean_operation(bst, it.Value(), second_operand_shapes, occ_op, part)) { + if (apply_in_batches(bst, it.Value(), opening_vector, occ_op, part)) { B.Add(C, part); } else { valid_result = false; @@ -204,7 +244,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape } shape = C; } else { - valid_result = util::boolean_operation(bst, s1, second_operand_shapes, occ_op, shape); + valid_result = apply_in_batches(bst, s1, opening_vector, occ_op, shape); } #endif @@ -226,9 +266,8 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape TopoDS_Compound C; B.MakeCompound(C); B.Add(C, s1); - TopTools_ListIteratorOfListOfShape it(second_operand_shapes); - for (; it.More(); it.Next()) { - B.Add(C, it.Value()); + for (auto& p : opening_vector) { + B.Add(C, p.second); } Logger::Message(Logger::LOG_ERROR, "Failed to process union, creating compound:", l); shape = C;