diff --git a/src/ifcgeom/IfcGeomFunctions.cpp b/src/ifcgeom/IfcGeomFunctions.cpp index e9519d88c6..119075745b 100644 --- a/src/ifcgeom/IfcGeomFunctions.cpp +++ b/src/ifcgeom/IfcGeomFunctions.cpp @@ -1375,78 +1375,115 @@ bool IfcGeom::Kernel::convert_openings_fast(const IfcSchema::IfcProduct* entity, std::sort(opening_vector.begin(), opening_vector.end(), opening_sorter()); // Iterate over the shapes of the IfcProduct - for ( IfcGeom::IfcRepresentationShapeItems::const_iterator it3 = entity_shapes.begin(); it3 != entity_shapes.end(); ++ it3 ) { + for (IfcGeom::IfcRepresentationShapeItems::const_iterator it3 = entity_shapes.begin(); it3 != entity_shapes.end(); ++it3) { - bool is_manifold = Kernel::is_manifold(it3->Shape()); + TopoDS_Compound C; + BRep_Builder B; + B.MakeCompound(C); + TopoDS_Shape combined_result; - if (!is_manifold) { - Logger::Warning("Non-manifold first operand"); + std::list parts; + + bool is_multiple = it3->Shape().ShapeType() == TopAbs_COMPOUND && TopoDS_Iterator(it3->Shape()).More() && util::is_nested_compound_of_solid(it3->Shape()); + + if (is_multiple) { + TopoDS_Iterator sit(it3->Shape()); + for (; sit.More(); sit.Next()) { + parts.push_back(sit.Value()); + } + } else { + parts.push_back(it3->Shape()); } - for (int as_shell = 0; as_shell < 2; ++as_shell) { + for (auto& entity_part : parts) { - TopoDS_Shape entity_shape_solid; - TopoDS_Shape entity_shape_unlocated; - if (as_shell) { - entity_shape_unlocated = it3->Shape(); + + bool is_manifold = Kernel::is_manifold(entity_part); + + if (!is_manifold) { + Logger::Warning("Non-manifold first operand"); + } + + TopoDS_Shape entity_part_result; + + for (int as_shell = 0; as_shell < 2; ++as_shell) { + + TopoDS_Shape entity_shape_solid; + TopoDS_Shape entity_shape_unlocated; + if (as_shell) { + entity_shape_unlocated = entity_part; + } else { + entity_shape_unlocated = ensure_fit_for_subtraction(entity_part, entity_shape_solid); + } + const gp_GTrsf& entity_shape_gtrsf = it3->Placement(); + if (entity_shape_gtrsf.Form() == gp_Other) { + Logger::Message(Logger::LOG_WARNING, "Applying non uniform transformation to:", entity); + } + TopoDS_Shape entity_shape = apply_transformation(entity_shape_unlocated, entity_shape_gtrsf); + + TopoDS_Shape result = entity_shape; + + auto it = opening_vector.begin(); + auto jt = it; + + 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 (boolean_operation(result, opening_list, BOPAlgo_CUT, intermediate_result)) { + result = intermediate_result; + } else { + Logger::Message(Logger::LOG_ERROR, "Opening subtraction failed for " + boost::lexical_cast(std::distance(jt, it)) + " openings", entity); + } + + jt = it; + } + + if (it == opening_vector.end()) { + break; + } + } + + int result_n_faces = count(result, TopAbs_FACE); + + if (!is_manifold && as_shell == 0 && result_n_faces == 0) { + // If we have a non-manifold first operand and our first attempt + // on a Solid-Solid subtraction yielded a empty result (no faces) + // or a strange result, a larger number of faces with the original input + // included. Then retry (another iteration on the for-loop on as-shell) + // where we keep the first operand as is (a compound of faces probably, + // unless --orient-shells was activated in which case we're already lost). + if (!is_manifold) { + Logger::Warning("Retrying boolean operation on individual faces"); + } + continue; + } + + entity_part_result = result; + + // For manifold first operands we're not even going to try if processing + // as loose faces gives a better result. + break; + } + + if (is_multiple) { + B.Add(C, entity_part_result); } else { - entity_shape_unlocated = ensure_fit_for_subtraction(it3->Shape(), entity_shape_solid); - } - const gp_GTrsf& entity_shape_gtrsf = it3->Placement(); - if (entity_shape_gtrsf.Form() == gp_Other) { - Logger::Message(Logger::LOG_WARNING, "Applying non uniform transformation to:", entity); - } - TopoDS_Shape entity_shape = apply_transformation(entity_shape_unlocated, entity_shape_gtrsf); - - TopoDS_Shape result = entity_shape; - - auto it = opening_vector.begin(); - auto jt = it; - - 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 (boolean_operation(result, opening_list, BOPAlgo_CUT, intermediate_result)) { - result = intermediate_result; - } else { - Logger::Message(Logger::LOG_ERROR, "Opening subtraction failed for " + boost::lexical_cast(std::distance(jt, it)) + " openings", entity); - } - - jt = it; - } - - if (it == opening_vector.end()) { - break; - } + combined_result = entity_part_result; } - int result_n_faces = count(result, TopAbs_FACE); - - if (!is_manifold && as_shell == 0 && result_n_faces == 0) { - // If we have a non-manifold first operand and our first attempt - // on a Solid-Solid subtraction yielded a empty result (no faces) - // or a strange result, a larger number of faces with the original input - // included. Then retry (another iteration on the for-loop on as-shell) - // where we keep the first operand as is (a compound of faces probably, - // unless --orient-shells was activated in which case we're already lost). - if (!is_manifold) { - Logger::Warning("Retrying boolean operation on individual faces"); - } - continue; - } - - cut_shapes.push_back(IfcGeom::IfcRepresentationShapeItem(it3->ItemId(), result, it3->StylePtr())); - - // For manifold first operands we're not even going to try if processing - // as loose faces gives a better result. - break; } + + if (is_multiple) { + combined_result = C; + } + + cut_shapes.push_back(IfcGeom::IfcRepresentationShapeItem(it3->ItemId(), combined_result, it3->StylePtr())); } return true; } diff --git a/src/ifcgeom/IfcGeomShapes.cpp b/src/ifcgeom/IfcGeomShapes.cpp index 8399a41708..73ba45f0b8 100644 --- a/src/ifcgeom/IfcGeomShapes.cpp +++ b/src/ifcgeom/IfcGeomShapes.cpp @@ -541,24 +541,6 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcShellBasedSurfaceModel* l, Ifc return true; } -namespace { - bool is_nested_compound_of_solid(const TopoDS_Shape& s, int depth=0) { - if (s.ShapeType() == TopAbs_COMPOUND) { - TopoDS_Iterator it(s); - for (; it.More(); it.Next()) { - if (!is_nested_compound_of_solid(it.Value(), depth + 1)) { - return false; - } - } - return true; - } else if (s.ShapeType() == TopAbs_SOLID) { - return depth > 0; - } else { - return false; - } - } -} - bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape& shape) { TopoDS_Shape s1; @@ -715,7 +697,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape bool valid_result; - if (s1.ShapeType() == TopAbs_COMPOUND && TopoDS_Iterator(s1).More() && is_nested_compound_of_solid(s1)) { + if (s1.ShapeType() == TopAbs_COMPOUND && TopoDS_Iterator(s1).More() && util::is_nested_compound_of_solid(s1)) { TopoDS_Compound C; BRep_Builder B; B.MakeCompound(C); diff --git a/src/ifcgeom_schema_agnostic/Kernel.cpp b/src/ifcgeom_schema_agnostic/Kernel.cpp index 1de3d8e335..57a27be97c 100644 --- a/src/ifcgeom_schema_agnostic/Kernel.cpp +++ b/src/ifcgeom_schema_agnostic/Kernel.cpp @@ -353,4 +353,20 @@ bool IfcGeom::Kernel::is_manifold(const TopoDS_Shape& a) { return true; } -} \ No newline at end of file +} + +bool IfcGeom::util::is_nested_compound_of_solid(const TopoDS_Shape& s, int depth) { + if (s.ShapeType() == TopAbs_COMPOUND) { + TopoDS_Iterator it(s); + for (; it.More(); it.Next()) { + if (!is_nested_compound_of_solid(it.Value(), depth + 1)) { + return false; + } + } + return true; + } else if (s.ShapeType() == TopAbs_SOLID) { + return depth > 0; + } else { + return false; + } +} diff --git a/src/ifcgeom_schema_agnostic/Kernel.h b/src/ifcgeom_schema_agnostic/Kernel.h index 9b962d2278..4485143a89 100644 --- a/src/ifcgeom_schema_agnostic/Kernel.h +++ b/src/ifcgeom_schema_agnostic/Kernel.h @@ -130,6 +130,10 @@ namespace IfcGeom { KernelFactoryImplementation& kernel_implementations(); } + + namespace util { + bool is_nested_compound_of_solid(const TopoDS_Shape& s, int depth = 0); + } } #endif \ No newline at end of file