From 3736a8b4b43d5d72c8f6d05423a61b991f46da23 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Sun, 13 Nov 2022 11:04:16 +0100 Subject: [PATCH] Refactoring --- src/ifcconvert/IfcConvert.cpp | 3 +- src/ifcgeom/IfcAxis2Placement2D.cpp | 3 +- src/ifcgeom/IfcAxis2Placement3D.cpp | 3 +- src/ifcgeom/IfcBooleanResult.cpp | 11 +- .../IfcCartesianTransformationOperator2D.cpp | 3 +- ...sianTransformationOperator2DnonUniform.cpp | 3 +- .../IfcCartesianTransformationOperator3D.cpp | 3 +- ...sianTransformationOperator3DnonUniform.cpp | 3 +- src/ifcgeom/IfcFace.cpp | 8 +- src/ifcgeom/IfcGeom.cpp | 913 +----------------- src/ifcgeom/IfcGeom.h | 11 - src/ifcgeom/IfcGeomIteratorImplementation.h | 2 +- src/ifcgeom/IfcIndexedPolyCurve.cpp | 2 +- src/ifcgeom/IfcPolygonalFaceSet.cpp | 4 +- src/ifcgeom/IfcRepresentation.cpp | 6 +- src/ifcgeom/IfcSurfaceCurveSweptAreaSolid.cpp | 3 +- src/ifcgeom/IfcSweptDiskSolid.cpp | 15 +- .../IfcGeomRepresentation.cpp | 5 +- src/ifcgeom_schema_agnostic/IfcGeomTree.h | 4 +- src/ifcgeom_schema_agnostic/Kernel.cpp | 126 --- src/ifcgeom_schema_agnostic/Kernel.h | 34 +- src/ifcgeom_schema_agnostic/base_utils.cpp | 155 +++ src/ifcgeom_schema_agnostic/base_utils.h | 32 + src/ifcgeom_schema_agnostic/boolean_utils.cpp | 572 ++++++++++- src/ifcgeom_schema_agnostic/boolean_utils.h | 9 + src/ifcgeom_schema_agnostic/sweep_utils.cpp | 9 +- src/ifcgeom_schema_agnostic/wire_utils.cpp | 7 +- src/serializers/SvgSerializer.cpp | 5 +- 28 files changed, 849 insertions(+), 1105 deletions(-) create mode 100644 src/ifcgeom_schema_agnostic/base_utils.cpp create mode 100644 src/ifcgeom_schema_agnostic/base_utils.h diff --git a/src/ifcconvert/IfcConvert.cpp b/src/ifcconvert/IfcConvert.cpp index 8fcf97662d..c2dc67c7e8 100644 --- a/src/ifcconvert/IfcConvert.cpp +++ b/src/ifcconvert/IfcConvert.cpp @@ -38,6 +38,7 @@ #include "../ifcgeom_schema_agnostic/IfcGeomFilter.h" #include "../ifcgeom_schema_agnostic/IfcGeomIterator.h" #include "../ifcgeom_schema_agnostic/IfcGeomRenderStyles.h" +#include "../ifcgeom_schema_agnostic/base_utils.h" #include "../ifcparse/utils.h" @@ -1632,7 +1633,7 @@ void fix_quantities(IfcParse::IfcFile& f, bool no_progress, bool quiet, bool std auto quantity_count = latebound_access::create(f, "IfcQuantityCount"); latebound_access::set(quantity_count, "Name", std::string("Surface Genus")); latebound_access::set(quantity_count, "Description", '#' + boost::lexical_cast(part.ItemId())); - latebound_access::set(quantity_count, "CountValue", IfcGeom::Kernel::surface_genus(part.Shape())); + latebound_access::set(quantity_count, "CountValue", IfcGeom::util::surface_genus(part.Shape())); quantities_2->push(quantity_count); } diff --git a/src/ifcgeom/IfcAxis2Placement2D.cpp b/src/ifcgeom/IfcAxis2Placement2D.cpp index fe8a849a59..6bb90fff9b 100644 --- a/src/ifcgeom/IfcAxis2Placement2D.cpp +++ b/src/ifcgeom/IfcAxis2Placement2D.cpp @@ -24,6 +24,7 @@ #include #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/base_utils.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -44,7 +45,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcAxis2Placement2D* l, gp_Trsf2d gp_Ax2d axis(gp_Pnt2d(P.X(),P.Y()), gp_Dir2d(V.X(),V.Y())); - if (!axis_equal(axis, gp_Ax2d(), getValue(GV_PRECISION))) { + if (!util::axis_equal(axis, gp_Ax2d(), getValue(GV_PRECISION))) { trsf.SetTransformation(axis, gp_Ax2d()); } diff --git a/src/ifcgeom/IfcAxis2Placement3D.cpp b/src/ifcgeom/IfcAxis2Placement3D.cpp index 8b41e67cf2..1b2430f2f5 100644 --- a/src/ifcgeom/IfcAxis2Placement3D.cpp +++ b/src/ifcgeom/IfcAxis2Placement3D.cpp @@ -23,6 +23,7 @@ #include #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/base_utils.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -66,7 +67,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcAxis2Placement3D* l, gp_Trsf& gp_Ax3 ax3(o, axis, refDirection); - if (!axis_equal(ax3, (gp_Ax3) gp::XOY(), getValue(GV_PRECISION))) { + if (!util::axis_equal(ax3, (gp_Ax3) gp::XOY(), getValue(GV_PRECISION))) { trsf.SetTransformation(ax3, gp::XOY()); } diff --git a/src/ifcgeom/IfcBooleanResult.cpp b/src/ifcgeom/IfcBooleanResult.cpp index 046c5d647a..a99b1e6419 100644 --- a/src/ifcgeom/IfcBooleanResult.cpp +++ b/src/ifcgeom/IfcBooleanResult.cpp @@ -20,6 +20,8 @@ #include #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/base_utils.h" +#include "../ifcgeom_schema_agnostic/boolean_utils.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -179,6 +181,11 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape bool valid_result; + util::boolean_settings bst; + bst.attempt_2d = getValue(GV_BOOLEAN_ATTEMPT_2D) > 0.; + bst.debug = getValue(GV_DEBUG_BOOLEAN) > 0.; + bst.precision = getValue(GV_PRECISION); + if (s1.ShapeType() == TopAbs_COMPOUND && TopoDS_Iterator(s1).More() && util::is_nested_compound_of_solid(s1)) { TopoDS_Compound C; BRep_Builder B; @@ -187,7 +194,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape valid_result = true; for (; it.More(); it.Next()) { TopoDS_Shape part; - if (boolean_operation(it.Value(), second_operand_shapes, occ_op, part)) { + if (util::boolean_operation(bst, it.Value(), second_operand_shapes, occ_op, part)) { B.Add(C, part); } else { valid_result = false; @@ -195,7 +202,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape } shape = C; } else { - valid_result = boolean_operation(s1, second_operand_shapes, occ_op, shape); + valid_result = util::boolean_operation(bst, s1, second_operand_shapes, occ_op, shape); } #endif diff --git a/src/ifcgeom/IfcCartesianTransformationOperator2D.cpp b/src/ifcgeom/IfcCartesianTransformationOperator2D.cpp index d91e0c707f..b3273b8256 100644 --- a/src/ifcgeom/IfcCartesianTransformationOperator2D.cpp +++ b/src/ifcgeom/IfcCartesianTransformationOperator2D.cpp @@ -24,6 +24,7 @@ #include #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/base_utils.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -60,7 +61,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCartesianTransformationOperato trsf.SetScaleFactor(*l->Scale()); } - if (is_identity(trsf, getValue(GV_PRECISION))) { + if (util::is_identity(trsf, getValue(GV_PRECISION))) { trsf = gp_Trsf2d(); } diff --git a/src/ifcgeom/IfcCartesianTransformationOperator2DnonUniform.cpp b/src/ifcgeom/IfcCartesianTransformationOperator2DnonUniform.cpp index c1735399c4..b0a3c5d72a 100644 --- a/src/ifcgeom/IfcCartesianTransformationOperator2DnonUniform.cpp +++ b/src/ifcgeom/IfcCartesianTransformationOperator2DnonUniform.cpp @@ -25,6 +25,7 @@ #include #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/base_utils.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -61,7 +62,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCartesianTransformationOperato gtrsf.SetValue(2,2,scale2); gtrsf.Multiply(trsf); - if (is_identity(gtrsf, getValue(GV_PRECISION))) { + if (util::is_identity(gtrsf, getValue(GV_PRECISION))) { gtrsf = gp_GTrsf2d(); } diff --git a/src/ifcgeom/IfcCartesianTransformationOperator3D.cpp b/src/ifcgeom/IfcCartesianTransformationOperator3D.cpp index 730a2e9960..12a080666b 100644 --- a/src/ifcgeom/IfcCartesianTransformationOperator3D.cpp +++ b/src/ifcgeom/IfcCartesianTransformationOperator3D.cpp @@ -22,6 +22,7 @@ #include #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/base_utils.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -38,7 +39,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCartesianTransformationOperato gp_Ax3 ax3 (origin,axis3,axis1); if ( axis2.Dot(ax3.YDirection()) < 0 ) ax3.YReverse(); - if (!axis_equal(ax3, (gp_Ax3) gp::XOY(), getValue(GV_PRECISION))) { + if (!util::axis_equal(ax3, (gp_Ax3) gp::XOY(), getValue(GV_PRECISION))) { trsf.SetTransformation(ax3); trsf.Invert(); } diff --git a/src/ifcgeom/IfcCartesianTransformationOperator3DnonUniform.cpp b/src/ifcgeom/IfcCartesianTransformationOperator3DnonUniform.cpp index 9bedf1c115..4dfdac0076 100644 --- a/src/ifcgeom/IfcCartesianTransformationOperator3DnonUniform.cpp +++ b/src/ifcgeom/IfcCartesianTransformationOperator3DnonUniform.cpp @@ -23,6 +23,7 @@ #include #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/base_utils.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -50,7 +51,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCartesianTransformationOperato gtrsf.SetValue(3,3,scale3); gtrsf.PreMultiply(trsf); - if (is_identity(gtrsf, getValue(GV_PRECISION))) { + if (util::is_identity(gtrsf, getValue(GV_PRECISION))) { gtrsf = gp_GTrsf(); } diff --git a/src/ifcgeom/IfcFace.cpp b/src/ifcgeom/IfcFace.cpp index 32cca9a3e1..c544bcd612 100644 --- a/src/ifcgeom/IfcFace.cpp +++ b/src/ifcgeom/IfcFace.cpp @@ -125,14 +125,14 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& result) } } - for (auto& wire : wires) { + for (auto& w : wires) { if (!same_sense) { - wire.Reverse(); + w.Reverse(); } - wire_senses.Bind(wire.Oriented(TopAbs_FORWARD), same_sense ? TopAbs_FORWARD : TopAbs_REVERSED); + wire_senses.Bind(w.Oriented(TopAbs_FORWARD), same_sense ? TopAbs_FORWARD : TopAbs_REVERSED); - fd.wires().emplace_back(TopoDS::Wire(wire)); + fd.wires().emplace_back(TopoDS::Wire(w)); } } } diff --git a/src/ifcgeom/IfcGeom.cpp b/src/ifcgeom/IfcGeom.cpp index 6bc8a8389f..91ae38a34a 100644 --- a/src/ifcgeom/IfcGeom.cpp +++ b/src/ifcgeom/IfcGeom.cpp @@ -140,6 +140,7 @@ #include "../ifcgeom_schema_agnostic/IfcGeomTree.h" #include "../ifcgeom_schema_agnostic/boolean_utils.h" #include "../ifcgeom_schema_agnostic/wire_utils.h" +#include "../ifcgeom_schema_agnostic/base_utils.h" #include #include @@ -217,13 +218,13 @@ void MAKE_INIT_FN(KernelImplementation_)(IfcGeom::impl::KernelFactoryImplementat void IfcGeom::Kernel::set_offset(const std::array &p_offset) { offset = gp_Vec(p_offset[0], p_offset[1], p_offset[2]); - offset_and_rotation = combine_offset_and_rotation(offset, rotation); + offset_and_rotation = util::combine_offset_and_rotation(offset, rotation); } void IfcGeom::Kernel::set_rotation(const std::array &p_rotation) { rotation = gp_Quaternion(p_rotation[0], p_rotation[1], p_rotation[2], p_rotation[3]); - offset_and_rotation = combine_offset_and_rotation(offset, rotation); + offset_and_rotation = util::combine_offset_and_rotation(offset, rotation); } bool IfcGeom::Kernel::shape_to_face_list(const TopoDS_Shape& s, TopTools_ListOfShape& li) { @@ -319,7 +320,7 @@ bool IfcGeom::Kernel::create_solid_from_faces(const TopTools_ListOfShape& face_l valid_shell = reana.IsValid(); } - valid_shell &= count(shape, TopAbs_SHELL) > 0; + valid_shell &= util::count(shape, TopAbs_SHELL) > 0; } catch (const Standard_Failure& e) { if (e.GetMessageString() && strlen(e.GetMessageString())) { Logger::Error(e.GetMessageString()); @@ -437,264 +438,6 @@ const TopoDS_Shape& IfcGeom::Kernel::ensure_fit_for_subtraction(const TopoDS_Sha return solid; } -// @nb this function is only in use on older versions of occt. -bool IfcGeom::Kernel::convert_openings(const IfcSchema::IfcProduct* entity, const IfcSchema::IfcRelVoidsElement::list::ptr& openings, - const IfcGeom::IfcRepresentationShapeItems& entity_shapes, const gp_Trsf& entity_trsf, IfcGeom::IfcRepresentationShapeItems& cut_shapes) { - - // TODO: Refactor convert_openings() convert_openings_fast() and convert(IfcBooleanResult) to use - // the same code base and conform to the same checks and logging messages. - - // Iterate over IfcOpeningElements - IfcGeom::IfcRepresentationShapeItems opening_shapes; - size_t last_size = 0; - for ( IfcSchema::IfcRelVoidsElement::list::it it = openings->begin(); it != openings->end(); ++ it ) { - IfcSchema::IfcRelVoidsElement* v = *it; - IfcSchema::IfcFeatureElementSubtraction* fes = v->RelatedOpeningElement(); - if ( fes->declaration().is(IfcSchema::IfcOpeningElement::Class()) ) { - if (!fes->Representation()) continue; - - // Convert the IfcRepresentation of the IfcOpeningElement - gp_Trsf opening_trsf; - if (fes->ObjectPlacement()) { - try { - convert(fes->ObjectPlacement(),opening_trsf); - } catch (const std::exception& e) { - Logger::Error(e); - } catch (...) { - Logger::Error("Failed to construct placement"); - } - } - - // Move the opening into the coordinate system of the IfcProduct - opening_trsf.PreMultiply(entity_trsf.Inverted()); - - IfcSchema::IfcProductRepresentation* prodrep = fes->Representation(); - IfcSchema::IfcRepresentation::list::ptr reps = prodrep->Representations(); - - for ( IfcSchema::IfcRepresentation::list::it it2 = reps->begin(); it2 != reps->end(); ++ it2 ) { - convert_shapes(*it2,opening_shapes); - } - - auto current_size = opening_shapes.size(); - for ( auto i = last_size; i < current_size; ++ i ) { - opening_shapes[i].prepend(opening_trsf); - } - last_size = current_size; - } - } - - // Iterate over the shapes of the IfcProduct - for ( IfcGeom::IfcRepresentationShapeItems::const_iterator it3 = entity_shapes.begin(); it3 != entity_shapes.end(); ++ it3 ) { - TopoDS_Shape entity_shape_solid; - const TopoDS_Shape& 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); - - // Iterate over the shapes of the IfcOpeningElements - for ( IfcGeom::IfcRepresentationShapeItems::const_iterator it4 = opening_shapes.begin(); it4 != opening_shapes.end(); ++ it4 ) { - TopoDS_Shape opening_shape_solid; - const TopoDS_Shape& opening_shape_unlocated = ensure_fit_for_subtraction(it4->Shape(),opening_shape_solid); - const gp_GTrsf& opening_shape_gtrsf = it4->Placement(); - if ( opening_shape_gtrsf.Form() == gp_Other ) { - Logger::Message(Logger::LOG_WARNING,"Applying non uniform transformation to opening of:",entity); - } - TopoDS_Shape opening_shape = apply_transformation(opening_shape_unlocated, opening_shape_gtrsf); - - double opening_volume; - if (Logger::LOG_WARNING >= Logger::Verbosity()) { - opening_volume = shape_volume(opening_shape); - if ( opening_volume <= ALMOST_ZERO ) - Logger::Message(Logger::LOG_WARNING,"Empty opening for:",entity); - } - - if (entity_shape.ShapeType() == TopAbs_COMPSOLID) { - - // For compound solids process the subtraction for the constituent - // solids individually and write the result back as a compound solid. - - TopoDS_CompSolid compound; - BRep_Builder builder; - builder.MakeCompSolid(compound); - - TopExp_Explorer exp(entity_shape, TopAbs_SOLID); - - for (; exp.More(); exp.Next()) { - -#if OCC_VERSION_HEX < 0x60900 - BRepAlgoAPI_Cut brep_cut(exp.Current(), opening_shape); -#else - BRepAlgoAPI_Cut brep_cut; - TopTools_ListOfShape s1s; - s1s.Append(exp.Current()); - TopTools_ListOfShape s2s; - s2s.Append(opening_shape); - brep_cut.SetFuzzyValue(getValue(GV_PRECISION)); - brep_cut.SetArguments(s1s); - brep_cut.SetTools(s2s); - brep_cut.Build(); -#endif - - bool added = false; - if ( brep_cut.IsDone() ) { - TopoDS_Shape brep_cut_result = brep_cut; - BRepCheck_Analyzer analyser(brep_cut_result); - bool is_valid = analyser.IsValid() != 0; - if (is_valid) { - TopExp_Explorer exp2(brep_cut_result, TopAbs_SOLID); - for (; exp2.More(); exp2.Next()) { - builder.Add(compound, exp2.Current()); - added = true; - } - } - } - if (!added) { - // Add the original in case subtraction fails - builder.Add(compound, exp.Current()); - } else { - Logger::Message(Logger::LOG_ERROR,"Failed to process subtraction:",entity); - } - } - - entity_shape = compound; - - } else { -#if OCC_VERSION_HEX < 0x60900 - BRepAlgoAPI_Cut brep_cut(entity_shape,opening_shape); -#else - BRepAlgoAPI_Cut brep_cut; - TopTools_ListOfShape s1s; - s1s.Append(entity_shape); - TopTools_ListOfShape s2s; - s2s.Append(opening_shape); - brep_cut.SetFuzzyValue(getValue(GV_PRECISION)); - brep_cut.SetArguments(s1s); - brep_cut.SetTools(s2s); - brep_cut.Build(); -#endif - - if ( brep_cut.IsDone() ) { - TopoDS_Shape brep_cut_result = brep_cut; - - ShapeFix_Shape fix(brep_cut_result); - try { - fix.Perform(); - brep_cut_result = fix.Shape(); - } catch (...) { - Logger::Error("Shape healing failed on opening subtraction result", entity); - } - - BRepCheck_Analyzer analyser(brep_cut_result); - bool is_valid = analyser.IsValid() != 0; - if ( is_valid ) { - entity_shape = brep_cut_result; - if (Logger::LOG_WARNING >= Logger::Verbosity()) { - const double volume_after_subtraction = shape_volume(entity_shape); - double original_shape_volume = shape_volume(entity_shape); - if ( ALMOST_THE_SAME(original_shape_volume,volume_after_subtraction) ) - Logger::Message(Logger::LOG_WARNING,"Subtraction yields unchanged volume:",entity); - } - } else { - Logger::Message(Logger::LOG_ERROR,"Invalid result from subtraction:",entity); - } - } else { - Logger::Message(Logger::LOG_ERROR,"Failed to process subtraction:",entity); - } - } - - } - cut_shapes.push_back(IfcGeom::IfcRepresentationShapeItem(it3->ItemId(), it3->Placement(), entity_shape, it3->StylePtr())); - } - - return true; -} - -#if OCC_VERSION_HEX < 0x60900 -bool IfcGeom::Kernel::convert_openings_fast(const IfcSchema::IfcProduct* entity, const IfcSchema::IfcRelVoidsElement::list::ptr& openings, - const IfcGeom::IfcRepresentationShapeItems& entity_shapes, const gp_Trsf& entity_trsf, IfcGeom::IfcRepresentationShapeItems& cut_shapes) { - - // Create a compound of all opening shapes in order to speed up the boolean operations - TopoDS_Compound opening_compound; - BRep_Builder builder; - builder.MakeCompound(opening_compound); - - for ( IfcSchema::IfcRelVoidsElement::list::it it = openings->begin(); it != openings->end(); ++ it ) { - IfcSchema::IfcRelVoidsElement* v = *it; - IfcSchema::IfcFeatureElementSubtraction* fes = v->RelatedOpeningElement(); - if ( fes->declaration().is(IfcSchema::IfcOpeningElement::Class()) ) { - if (!fes->Representation()) continue; - - // Convert the IfcRepresentation of the IfcOpeningElement - gp_Trsf opening_trsf; - if (fes->ObjectPlacement()) { - try { - convert(fes->ObjectPlacement(),opening_trsf); - } catch (const std::exception& e) { - Logger::Error(e); - } catch (...) { - Logger::Error("Failed to construct placement"); - } - } - - // Move the opening into the coordinate system of the IfcProduct - opening_trsf.PreMultiply(entity_trsf.Inverted()); - - IfcSchema::IfcProductRepresentation* prodrep = fes->Representation(); - IfcSchema::IfcRepresentation::list::ptr reps = prodrep->Representations(); - - IfcGeom::IfcRepresentationShapeItems opening_shapes; - - for ( IfcSchema::IfcRepresentation::list::it it2 = reps->begin(); it2 != reps->end(); ++ it2 ) { - convert_shapes(*it2,opening_shapes); - } - - for ( unsigned int i = 0; i < opening_shapes.size(); ++ i ) { - gp_GTrsf gtrsf = opening_shapes[i].Placement(); - gtrsf.PreMultiply(opening_trsf); - TopoDS_Shape opening_shape = apply_transformation(opening_shapes[i].Shape(), gtrsf); - builder.Add(opening_compound, opening_shape); - } - - } - } - - // Iterate over the shapes of the IfcProduct - for ( IfcGeom::IfcRepresentationShapeItems::const_iterator it3 = entity_shapes.begin(); it3 != entity_shapes.end(); ++ it3 ) { - TopoDS_Shape entity_shape_solid; - const TopoDS_Shape& 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); - - BRepAlgoAPI_Cut brep_cut(entity_shape,opening_compound); - - bool is_valid = false; - if ( brep_cut.IsDone() ) { - TopoDS_Shape brep_cut_result = brep_cut; - - BRepCheck_Analyzer analyser(brep_cut_result); - is_valid = analyser.IsValid() != 0; - if ( is_valid ) { - cut_shapes.push_back(IfcGeom::IfcRepresentationShapeItem(it3->ItemId(), brep_cut_result, &it3->Style())); - } - } - if ( !is_valid ) { - // Apparently processing the boolean operation failed or resulted in an invalid result - // in which case the original shape without the subtractions is returned instead - // we try convert the openings in the original way, one by one. - Logger::Message(Logger::LOG_WARNING,"Subtracting combined openings compound failed:",entity); - return false; - } - - } - return true; -} -#else - namespace { struct opening_sorter { bool operator()(const std::pair& a, const std::pair& b) const { @@ -703,9 +446,14 @@ namespace { }; } -bool IfcGeom::Kernel::convert_openings_fast(const IfcSchema::IfcProduct* entity, const IfcSchema::IfcRelVoidsElement::list::ptr& openings, +bool IfcGeom::Kernel::convert_openings(const IfcSchema::IfcProduct* entity, const IfcSchema::IfcRelVoidsElement::list::ptr& openings, const IfcGeom::IfcRepresentationShapeItems& entity_shapes, const gp_Trsf& entity_trsf, IfcGeom::IfcRepresentationShapeItems& cut_shapes) { + util::boolean_settings bst; + bst.attempt_2d = getValue(GV_BOOLEAN_ATTEMPT_2D) > 0.; + bst.debug = getValue(GV_DEBUG_BOOLEAN) > 0.; + bst.precision = getValue(GV_PRECISION); + std::vector< std::pair > opening_vector; for (IfcSchema::IfcRelVoidsElement::list::it it = openings->begin(); it != openings->end(); ++it) { @@ -799,7 +547,7 @@ bool IfcGeom::Kernel::convert_openings_fast(const IfcSchema::IfcProduct* entity, for (auto& entity_part : parts) { - bool is_manifold = Kernel::is_manifold(entity_part); + bool is_manifold = util::is_manifold(entity_part); if (!is_manifold) { Logger::Warning("Non-manifold first operand"); @@ -836,7 +584,7 @@ bool IfcGeom::Kernel::convert_openings_fast(const IfcSchema::IfcProduct* entity, } TopoDS_Shape intermediate_result; - if (boolean_operation(result, opening_list, BOPAlgo_CUT, intermediate_result)) { + if (util::boolean_operation(bst, 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); @@ -850,7 +598,7 @@ bool IfcGeom::Kernel::convert_openings_fast(const IfcSchema::IfcProduct* entity, } } - int result_n_faces = count(result, TopAbs_FACE); + int result_n_faces = util::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 @@ -888,7 +636,6 @@ bool IfcGeom::Kernel::convert_openings_fast(const IfcSchema::IfcProduct* entity, } return true; } -#endif double IfcGeom::Kernel::get_wire_intersection_tolerance(const TopoDS_Wire& wire) const { return getValue(GV_NO_WIRE_INTERSECTION_TOLERANCE) > 0. @@ -1621,7 +1368,7 @@ IfcGeom::BRepElement* IfcGeom::Kernel::create_brep_for_representation_and_produc if (settings.get(IteratorSettings::APPLY_LAYERSETS)) { TopoDS_Shape merge; if (flatten_shape_list(shapes, merge, false)) { - if (count(merge, TopAbs_FACE) > 0) { + if (util::count(merge, TopAbs_FACE) > 0) { std::vector thickness; std::vector layers; std::vector< std::vector > folded_layers; @@ -1676,7 +1423,7 @@ IfcGeom::BRepElement* IfcGeom::Kernel::create_brep_for_representation_and_produc } else { bool some_items_without_style = false; for (IfcGeom::IfcRepresentationShapeItems::iterator it = shapes.begin(); it != shapes.end(); ++it) { - if (!it->hasStyle() && count(it->Shape(), TopAbs_FACE)) { + if (!it->hasStyle() && util::count(it->Shape(), TopAbs_FACE)) { some_items_without_style = true; break; } @@ -1742,24 +1489,7 @@ IfcGeom::BRepElement* IfcGeom::Kernel::create_brep_for_representation_and_produc IfcGeom::IfcRepresentationShapeItems opened_shapes; bool caught_error = false; try { -#if OCC_VERSION_HEX < 0x60900 - const bool faster_booleans = false; -#else - const bool faster_booleans = true; -#endif - if (faster_booleans) { - bool success = convert_openings_fast(product,openings,shapes,trsf,opened_shapes); -#if OCC_VERSION_HEX < 0x60900 - if (!success) { - opened_shapes.clear(); - convert_openings(product,openings,shapes,trsf,opened_shapes); - } -#else - (void)success; -#endif - } else { - convert_openings(product,openings,shapes,trsf,opened_shapes); - } + convert_openings(product,openings,shapes,trsf,opened_shapes); } catch (const std::exception& e) { Logger::Message(Logger::LOG_ERROR, std::string("Error processing openings for: ") + e.what() + ":", product); caught_error = true; @@ -1858,7 +1588,7 @@ IfcGeom::BRepElement* IfcGeom::Kernel::create_brep_for_representation_and_produc int genus = (int) q2->as()->CountValue(); for (auto& part : elem->geometry()) { if (part.ItemId() == item_id) { - if (surface_genus(part.Shape()) != genus) { + if (util::surface_genus(part.Shape()) != genus) { all_succeeded = false; } } @@ -2890,12 +2620,12 @@ bool IfcGeom::Kernel::apply_folded_layerset(const IfcRepresentationShapeItems& i TopoDS_Shape Bn = BRepPrimAPI_MakeHalfSpace(B, jt->second.second).Solid(); TopoDS_Shape a = BRepAlgoAPI_Cut(A, Bn); - if (count(a, TopAbs_FACE) == 1) { + if (util::count(a, TopAbs_FACE) == 1) { A = TopoDS::Face(TopExp_Explorer(a, TopAbs_FACE).Current()); } TopoDS_Shape b = BRepAlgoAPI_Cut(B, An); - if (count(b, TopAbs_FACE) == 1) { + if (util::count(b, TopAbs_FACE) == 1) { B = TopoDS::Face(TopExp_Explorer(b, TopAbs_FACE).Current()); } } @@ -3409,611 +3139,6 @@ bool IfcGeom::Kernel::fit_halfspace(const TopoDS_Shape& a, const TopoDS_Shape& b return true; } -#if OCC_VERSION_HEX < 0x60900 -bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a, const TopTools_ListOfShape& b, BOPAlgo_Operation op, TopoDS_Shape& result) { - result = a; - TopTools_ListIteratorOfListOfShape it(b); - for (; it.More(); it.Next()) { - TopoDS_Shape r; - if (!boolean_operation(result, it.Value(), op, r)) { - return false; - } - result = r; - } - return true; -} -bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a, const TopoDS_Shape& b, BOPAlgo_Operation op, TopoDS_Shape& result) { - bool succesful = true; - BRepAlgoAPI_BooleanOperation* builder; - if (op == BOPAlgo_CUT) { - builder = new BRepAlgoAPI_Cut(a, b); - } else if (op == BOPAlgo_COMMON) { - builder = new BRepAlgoAPI_Common(a, b); - } else if (op == BOPAlgo_FUSE) { - builder = new BRepAlgoAPI_Fuse(a, b); - } else { - return false; - } - if (builder->IsDone()) { - TopoDS_Shape r = *builder; - succesful = BRepCheck_Analyzer(r).IsValid() != 0; - if (succesful) { - result = r; - - ShapeFix_Shape fix(result); - try { - fix.Perform(); - result = fix.Shape(); - } catch (...) { - Logger::Error("Shape healing failed on boolean result"); - } - - } else { - // Increase tolerance max 3 times until succesful - TopoDS_Shape a2 = a; - TopoDS_Shape b2 = b; - ShapeAnalysis_ShapeTolerance tolerance; - const double t1 = tolerance.Tolerance(a, 1) * 10.; - const double t2 = tolerance.Tolerance(b, 1) * 10.; - if (((std::max)(t1, t2) + 1e-15) > getValue(GV_PRECISION) * 1000.) { - return false; - } - apply_tolerance(a2, t1); - apply_tolerance(b2, t2); - succesful = boolean_operation(a2, b2, op, result); - } - } - delete builder; - return succesful; -} -#else - -bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a_input, const TopTools_ListOfShape& b_input, BOPAlgo_Operation op, TopoDS_Shape& result, double fuzziness) { - using namespace std::string_literals; - - const bool do_unify = true; - const bool do_subtraction_eliminate_disjoint_bbox = true; - const bool do_subtraction_eliminate_touching = true; - const bool do_attempt_2d_boolean = getValue(GV_BOOLEAN_ATTEMPT_2D) > 0.; - const bool debug = getValue(GV_DEBUG_BOOLEAN) > 0.; - - std::string debug_identifier; - if (debug) { - std::stringstream ss; - ss << "bool-" << std::this_thread::get_id() << "-" << (operation_counter_++); - debug_identifier = ss.str(); - Logger::Notice("Boolean debug identifier: " + debug_identifier); - } - - if (fuzziness < 0.) { - fuzziness = getValue(GV_PRECISION) / 10.; - } - - // @todo, it does seem a bit odd, we first triangulate non-planar faces - // to later unify them again. Can we make this a bit more intelligent? - TopoDS_Shape a; - TopTools_ListOfShape b; - - if (do_unify) { - PERF("boolean operation: unifying operands"); - - a = util::unify(a_input, fuzziness * 1000.); - - Logger::Message( - Logger::LOG_DEBUG, - "Simplified operand A from "s + - std::to_string(count(a_input, TopAbs_FACE)) + - " to "s + - std::to_string(count(a, TopAbs_FACE)) - ); - - { - TopTools_ListIteratorOfListOfShape it(b_input); - for (; it.More(); it.Next()) { - b.Append(util::unify(it.Value(), fuzziness)); - Logger::Message( - Logger::LOG_DEBUG, - "Simplified operand B from "s + - std::to_string(count(it.Value(), TopAbs_FACE)) + - " to "s + - std::to_string(count(b.Last(), TopAbs_FACE)) - ); - } - } - } else { - a = a_input; - b = b_input; - } - - bool success = false; - BRepAlgoAPI_BooleanOperation* builder; - TopTools_ListOfShape b_tmp; - - if (op == BOPAlgo_CUT) { - builder = new BRepAlgoAPI_Cut(); - - if (do_subtraction_eliminate_disjoint_bbox) { - PERF("boolean subtraction: eliminate disjoint bbox"); - - auto N = util::bounding_box_overlap(fuzziness, a, b, b_tmp); - if (N) { - Logger::Notice("Eliminated " + std::to_string(N) + " disjoint operands"); - std::swap(b, b_tmp); - } - } - - if (do_subtraction_eliminate_touching) { - PERF("boolean subtraction: eliminate touching"); - - b_tmp.Clear(); - auto N = util::eliminate_touching_operands(fuzziness, a, b, b_tmp); - if (N) { - Logger::Notice("Eliminated " + std::to_string(N) + " touching operands"); - std::swap(b, b_tmp); - } - } - - } else if (op == BOPAlgo_COMMON) { - builder = new BRepAlgoAPI_Common(); - } else if (op == BOPAlgo_FUSE) { - builder = new BRepAlgoAPI_Fuse(); - } else { - return false; - } - - if (b.Extent() == 0) { - result = a; - return true; - } - - if (Logger::LOG_NOTICE >= Logger::Verbosity()) { - PERF("preliminary manifoldness check"); - - Logger::Notice("Operand A is " + (is_manifold(a) ? ""s : "non-"s) + "manifold"); - - TopTools_ListIteratorOfListOfShape it(b); - for (int i = 0; it.More(); it.Next(), ++i) { - Logger::Notice("Operand B " + std::to_string(i) + " is " + (is_manifold(it.Value()) ? ""s : "non-"s) + "manifold"); - } - } - - // Find a sensible value for the fuzziness, based on precision - // and limited by edge lengths and vertex-edge distances. - double min_length_orig; - - { - PERF("boolean operation: min edge length"); - - min_length_orig = util::min_edge_length(a); - TopTools_ListIteratorOfListOfShape it(b); - for (; it.More(); it.Next()) { - double d = util::min_edge_length(it.Value()); - if (d < min_length_orig) { - min_length_orig = d; - } - } - } - - { - PERF("boolean operation: min vertex-edge dist"); - - double d = util::min_vertex_edge_distance(a, getValue(GV_PRECISION), min_length_orig); - if (d < min_length_orig) { - min_length_orig = d; - } - - TopTools_ListIteratorOfListOfShape it(b); - for (; it.More(); it.Next()) { - d = util::min_vertex_edge_distance(it.Value(), getValue(GV_PRECISION), min_length_orig); - if (d < min_length_orig) { - min_length_orig = d; - } - } - } - - const double fuzz = (std::min)(min_length_orig / 3., fuzziness); - - Logger::Notice("Used fuzziness: " + std::to_string(fuzz)); - - TopTools_ListOfShape s1s; - s1s.Append(util::copy_operand(a)); - - if (debug) { - TopTools_ListOfShape* lists[2] = { &s1s, &b }; - static std::string operand_names[2] = { "a", "b" }; - for (int i = 0; i < 2; ++i) { - TopTools_ListIteratorOfListOfShape it(*lists[i]); - for (int j = 0; it.More(); it.Next(), ++j) { - std::string fn = debug_identifier + "-" + operand_names[i] + "-" + std::to_string(j) + ".brep"; - BRepTools::Write(it.Value(), fn.c_str()); - } - } - } - - if (op == BOPAlgo_CUT) { - TopoDS_Face a_face; - std::pair a_interval; - - TopTools_ListOfShape b_faces, b_remainder_3d; - - bool is_extrusion_a = false; - if (do_attempt_2d_boolean) { - PERF("boolean subtraction: extrusion check"); - - is_extrusion_a = util::is_extrusion(gp::DY(), a, a_face, a_interval); - } - - if (is_extrusion_a) { - Logger::Notice("Operand A 1/1 is an extrusion"); - - TopTools_ListIteratorOfListOfShape it(b); - for (int nb = 1; it.More(); it.Next(), ++nb) { - bool process_2d = false; - TopoDS_Face b_face; - std::pair b_interval; - - bool is_extrusion_b; - { - PERF("boolean subtraction: extrusion check"); - - is_extrusion_b = util::is_extrusion(gp::DY(), it.Value(), b_face, b_interval); - } - - if (is_extrusion_b) { - Logger::Notice("Operand B " + std::to_string(nb) + "/" + std::to_string(b.Extent()) + " is an extrusion"); - - if (b_interval.first < a_interval.first + fuzz && b_interval.second > a_interval.second - fuzz) { - Logger::Notice("Operand B creates a through hole"); - - // Align b with a operand - gp_Trsf trsf; - trsf.SetTranslation(gp_Vec(gp::DY()) * (a_interval.first - b_interval.first)); - - b_faces.Append(b_face.Moved(trsf)); - process_2d = true; - } - } - - if (!process_2d) { - b_remainder_3d.Append(it.Value()); - } - } - - if (b_faces.Extent()) { - TopoDS_Shape face_result; - - bool boolean_op_2d_success; - { - PERF("boolean operation: 2d builder"); - // First try using face builder - - boolean_op_2d_success = util::boolean_subtraction_2d_using_builder(a_face, b_faces, face_result, fuzziness); - } - - if (!boolean_op_2d_success) { - PERF("boolean operation: 2d"); - // Retry using generic 2d using boolean algo on faces - - boolean_op_2d_success = boolean_operation(a_face, b_faces, op, face_result, fuzziness); - } - - if (boolean_op_2d_success) { - PERF("boolean operation: 2d to 3d"); - - BRepPrimAPI_MakePrism mp(face_result, gp_Vec(gp::DY()) * (a_interval.second - a_interval.first)); - if (mp.IsDone()) { - if (b_remainder_3d.Extent()) { - Logger::Notice(std::to_string(b_remainder_3d.Extent()) + " operands remaining to process in 3D"); - b = b_remainder_3d; - s1s.Clear(); - s1s.Append(mp.Shape()); - } else { - Logger::Notice("Processed fully in 2D"); - result = mp.Shape(); - return true; - } - } else { - Logger::Notice("Failed to extrude 2D boolean result. Retrying in 3D."); - } - } else { - Logger::Notice("Failed to perform 2D boolean operation. Retrying in 3D."); - } - } else { - Logger::Notice("No second operands can be processed as 2D inner bounds. Retrying in 3D."); - } - } - } - -#if OCC_VERSION_HEX >= 0x70000 - builder->SetNonDestructive(true); -#endif - builder->SetFuzzyValue(fuzz); - builder->SetArguments(s1s); - util::copy_operand(b, b_tmp); - std::swap(b, b_tmp); - builder->SetTools(b); - { - PERF("boolean operation: build"); - - builder->Build(); - } - if (builder->IsDone()) { - if (builder->DSFiller()->HasWarning(STANDARD_TYPE(BOPAlgo_AlertAcquiredSelfIntersection))) { - Logger::Notice("Builder reports self-intersection in output"); - success = false; - } else { - TopoDS_Shape r = *builder; - - { - PERF("boolean operation: shape healing"); - - ShapeFix_Shape fix(r); - try { - fix.SetMaxTolerance(fuzz); - fix.Perform(); - r = fix.Shape(); - } catch (...) { - Logger::Error("Shape healing failed on boolean result"); - } - } - - { - PERF("boolean operation: shape analysis"); - - BRepCheck_Analyzer ana(r); - success = ana.IsValid() != 0; - - if (!success) { - Logger::Notice("Boolean operation yields invalid result"); - - std::stringstream str; - bool any_emitted = false; - - std::function dump; - dump = [&ana, &str, &dump, &any_emitted](const TopoDS_Shape& s) { - if (!ana.Result(s).IsNull()) { - BRepCheck_ListIteratorOfListOfStatus itl; - itl.Initialize(ana.Result(s)->Status()); - for (; itl.More(); itl.Next()) { - if (itl.Value() != BRepCheck_NoError) { - if (any_emitted) { - str << ", "; - } - BRepCheck::Print(itl.Value(), str); - str.seekp(str.tellp() - (std::streamoff)1); - str << " on "; - TopAbs::Print(s.ShapeType(), str); - any_emitted = true; - } - } - } - for (TopoDS_Iterator it(s); it.More(); it.Next()) { - dump(it.Value()); - } - }; - - dump(r); - - Logger::Notice(str.str()); - } - } - - if (success) { - - { - PERF("boolean operation: manifoldness check"); - - success = !is_manifold(a) || is_manifold(r); - } - - if (!success) { - PERF("boolean operation: manifoldness check excemption"); - - // An excemption for the requirement to be manifold: When the cut operands have overlapping edge belonging to faces that do not overlap. - bool operands_nonmanifold = false; - if (op == BOPAlgo_CUT) { - TopTools_IndexedMapOfShape edges; - TopTools_IndexedDataMapOfShapeListOfShape map; - for (TopTools_ListIteratorOfListOfShape it2(b); it2.More(); it2.Next()) { - auto& bb = it2.Value(); - TopExp::MapShapes(bb, TopAbs_EDGE, edges); - TopExp::MapShapesAndAncestors(bb, TopAbs_EDGE, TopAbs_FACE, map); - } - IfcGeom::impl::tree tree; - for (int i = 1; i <= edges.Extent(); ++i) { - tree.add(i, edges.FindKey(i)); - } - for (int i = 1; i <= edges.Extent(); ++i) { - const TopoDS_Edge& ei = TopoDS::Edge(edges.FindKey(i)); - Bnd_Box bb; - BRepBndLib::Add(ei, bb); - bb.Enlarge(fuzziness); - auto ii = tree.select_box(bb, false); - for (int j : ii) { - if (j != i) { - const TopoDS_Edge& ej = TopoDS::Edge(edges.FindKey(j)); - ShapeAnalysis_Edge sae; - double f = fuzziness; - bool edges_overlapping = sae.CheckOverlapping(ei, ej, f, 0.) || - sae.CheckOverlapping(ej, ei, f, 0.); - - if (edges_overlapping) { - auto faces_i = map.FindFromKey(edges.FindKey(i)); - auto faces_j = map.FindFromKey(edges.FindKey(j)); - bool overlap = false; - for (TopTools_ListIteratorOfListOfShape it4(faces_i); it4.More(); it4.Next()) { - auto& fi = it4.Value(); - for (TopTools_ListIteratorOfListOfShape it2(faces_j); it2.More(); it2.Next()) { - auto& fj = it2.Value(); - if (util::faces_overlap(TopoDS::Face(fi), TopoDS::Face(fj))) { - overlap = true; - } - } - if (overlap) { - break; - } - } - operands_nonmanifold = !overlap; - break; - } - } - } - if (operands_nonmanifold) { - break; - } - } - } - success = operands_nonmanifold; - } - - if (success) { - - bool all_faces_included_in_result = true; - bool has_open_shells = false; - - if (op == BOPAlgo_CUT) { - PERF("boolean operation: open shell face adition check"); - - for (TopExp_Explorer exp(a, TopAbs_SHELL); exp.More(); exp.Next()) { - if (!exp.Current().Closed()) { - // This 'face addition check' is only done when the first operand - // contains open shells (which was initially the aim of this check - // see #1472). - // Later in #1914 we found that the logic to apply openings in groups - // of similar edge lengths can create a situation of inner voids, which - // trigger a false positive in this check. This could have also been - // solved below by checking whether the opening(s) are included as a - // unmodified (interior) shell within a solid of multiple shells. - // Checking for open shells in first operand was quicker and more - // straightforward. The question still is whether in cases like #1472 - // we need to first try the boolean union as solid/solid interference - // to trigger this case or whether we can immediately proceed to a face/ - // solid operation. - has_open_shells = true; - break; - } - } - - if (has_open_shells) { - TopTools_IndexedMapOfShape faces; - TopExp::MapShapes(r, TopAbs_FACE, faces); - for (TopExp_Explorer exp(a, TopAbs_FACE); exp.More(); exp.Next()) { - auto& f = TopoDS::Face(exp.Current()); - if (!faces.Contains(f)) { - all_faces_included_in_result = false; - break; - } - } - } else { - all_faces_included_in_result = false; - } - } - - int result_n_faces = count(r, TopAbs_FACE); - int first_op_n_faces = count(a, TopAbs_FACE); - - if (op == BOPAlgo_CUT && has_open_shells && all_faces_included_in_result && result_n_faces > first_op_n_faces) { - success = false; - Logger::Notice("Boolean result discarded because subtractions results in only the addition of faces"); - } else { - // when there are edges or vertex-edge distances close to the used fuzziness, the - // output is not trusted and the operation is attempted with a higher fuzziness. - int reason = 0; - double v; - - { - PERF("boolean operation: result min edge length check"); - - if ((v = util::min_edge_length(r)) < fuzziness * 3.) { - reason = 0; - success = false; - - goto skip_further_checks; - } - } - - { - PERF("boolean operation: result min vertex-edge dist check"); - - if ((v = util::min_vertex_edge_distance(r, getValue(GV_PRECISION), fuzziness * 3.)) < fuzziness * 3.) { - reason = 1; - success = false; - - goto skip_further_checks; - } - } - - { - PERF("boolean operation: result min face-face dist check"); - - if ((v = util::min_face_face_distance(r, 1.e-4)) < 1.e-4) { - // #2095 Check if this distance wasn't already realized in the input first operand. - if (v < util::min_face_face_distance(a, 1.e-4)) { - reason = 2; - success = false; - } - } - } - - skip_further_checks: - if (!success) { - static const char* const reason_strings[] = { "edge length", "vertex-edge", "face-face" }; - std::stringstream str; - str << "Boolean operation result failing " << reason_strings[reason] << " interference check, with fuzziness " << fuzziness << " with length " << v; - Logger::Notice(str.str()); - } - } - - if (success) { - result = r; - } - - } else { - Logger::Notice("Boolean operation yields non-manifold result"); - } - } - } - } else { - std::stringstream str; - -#if OCC_VERSION_HEX >= 0x70200 - - if (builder->HasError(STANDARD_TYPE(BOPAlgo_AlertBOPNotAllowed))) { - Logger::Error("Invalid operands. Using first operand"); - result = a; - success = true; - } -#endif - -#if OCC_VERSION_HEX >= 0x70000 - builder->DumpErrors(str); -#else - str << "Error code: " << builder->ErrorStatus(); -#endif - std::string str_str = str.str(); - if (str_str.size()) { - Logger::Notice(str_str); - } - } - delete builder; - if (!success) { - const double new_fuzziness = fuzziness * 10.; - if (new_fuzziness - 1e-15 <= getValue(GV_PRECISION) * 10000. && new_fuzziness < min_length_orig) { - return boolean_operation(a, b, op, result, new_fuzziness); - } else { - Logger::Notice("No longer attempting boolean operation with higher fuzziness"); - } - } - return success && !result.IsNull(); -} - -bool IfcGeom::Kernel::boolean_operation(const TopoDS_Shape& a, const TopoDS_Shape& b, BOPAlgo_Operation op, TopoDS_Shape& result, double fuzziness) { - TopTools_ListOfShape bs; - bs.Append(b); - return boolean_operation(a, bs, op, result, fuzziness); -} -#endif - void IfcGeom::Kernel::set_conversion_placement_rel_to_type(const IfcParse::declaration* type) { placement_rel_to_type_ = type; } diff --git a/src/ifcgeom/IfcGeom.h b/src/ifcgeom/IfcGeom.h index ddc02374eb..719a0d07b3 100644 --- a/src/ifcgeom/IfcGeom.h +++ b/src/ifcgeom/IfcGeom.h @@ -167,8 +167,6 @@ private: double boolean_debug_setting; double boolean_attempt_2d; - size_t operation_counter_ = 0; - // For stopping PlacementRelTo recursion in convert(const IfcSchema::IfcObjectPlacement* l, gp_Trsf& trsf) const IfcParse::declaration* placement_rel_to_type_; const IfcUtil::IfcBaseEntity* placement_rel_to_instance_; @@ -273,7 +271,6 @@ public: bool convert_curve(const IfcUtil::IfcBaseInterface* L, Handle(Geom_Curve)& result); bool convert_face(const IfcUtil::IfcBaseInterface* L, TopoDS_Shape& result); bool convert_openings(const IfcSchema::IfcProduct* entity, const IfcSchema::IfcRelVoidsElement::list::ptr& openings, const IfcRepresentationShapeItems& entity_shapes, const gp_Trsf& entity_trsf, IfcRepresentationShapeItems& cut_shapes); - bool convert_openings_fast(const IfcSchema::IfcProduct* entity, const IfcSchema::IfcRelVoidsElement::list::ptr& openings, const IfcRepresentationShapeItems& entity_shapes, const gp_Trsf& entity_trsf, IfcRepresentationShapeItems& cut_shapes); void assert_closed_wire(TopoDS_Wire& wire); bool convert_layerset(const IfcSchema::IfcProduct*, std::vector&, std::vector>&, std::vector&); @@ -284,14 +281,6 @@ public: bool split_solid_by_surface(const TopoDS_Shape&, const Handle_Geom_Surface&, TopoDS_Shape&, TopoDS_Shape&); bool split_solid_by_shell(const TopoDS_Shape&, const TopoDS_Shape& s, TopoDS_Shape&, TopoDS_Shape&); -#if OCC_VERSION_HEX < 0x60900 - bool boolean_operation(const TopoDS_Shape&, const TopTools_ListOfShape&, BOPAlgo_Operation, TopoDS_Shape&); - bool boolean_operation(const TopoDS_Shape&, const TopoDS_Shape&, BOPAlgo_Operation, TopoDS_Shape&); -#else - bool boolean_operation(const TopoDS_Shape&, const TopTools_ListOfShape&, BOPAlgo_Operation, TopoDS_Shape&, double fuzziness = -1.); - bool boolean_operation(const TopoDS_Shape&, const TopoDS_Shape&, BOPAlgo_Operation, TopoDS_Shape&, double fuzziness = -1.); -#endif - bool fit_halfspace(const TopoDS_Shape& a, const TopoDS_Shape& b, TopoDS_Shape& box, double& height); const Handle_Geom_Curve intersect(const Handle_Geom_Surface&, const Handle_Geom_Surface&); diff --git a/src/ifcgeom/IfcGeomIteratorImplementation.h b/src/ifcgeom/IfcGeomIteratorImplementation.h index 7746fa13f9..88083d6643 100644 --- a/src/ifcgeom/IfcGeomIteratorImplementation.h +++ b/src/ifcgeom/IfcGeomIteratorImplementation.h @@ -303,7 +303,7 @@ namespace IfcGeom { task_result_ptr_initialized = true; } - progress_ = ++processed_ * 100 / tasks_.size(); + progress_ = (int) (++processed_ * 100 / tasks_.size()); } void process_concurrently() { diff --git a/src/ifcgeom/IfcIndexedPolyCurve.cpp b/src/ifcgeom/IfcIndexedPolyCurve.cpp index 35886a34db..77820e6327 100644 --- a/src/ifcgeom/IfcIndexedPolyCurve.cpp +++ b/src/ifcgeom/IfcIndexedPolyCurve.cpp @@ -50,7 +50,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcIndexedPolyCurve* l, TopoDS_Wi coords.size() < 3 ? 0. : coords[2] * getValue(GV_LENGTH_UNIT))); } - int max_index = points.size(); + int max_index = (int) points.size(); BRepBuilderAPI_MakeWire w; diff --git a/src/ifcgeom/IfcPolygonalFaceSet.cpp b/src/ifcgeom/IfcPolygonalFaceSet.cpp index 4703a11df5..d00591f2f9 100644 --- a/src/ifcgeom/IfcPolygonalFaceSet.cpp +++ b/src/ifcgeom/IfcPolygonalFaceSet.cpp @@ -41,12 +41,12 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolygonalFaceSet* pfs, TopoDS_ for (auto& f : *polygonal_faces) { loop_grouping.emplace_back(); - loop_grouping.back().push_back(indices.size()); + loop_grouping.back().push_back((int) indices.size()); indices.push_back(f->CoordIndex()); if (f->as()) { auto inner_coordinates = f->as()->InnerCoordIndices(); for (auto& x : inner_coordinates) { - loop_grouping.back().push_back(indices.size()); + loop_grouping.back().push_back((int) indices.size()); indices.push_back(x); } } diff --git a/src/ifcgeom/IfcRepresentation.cpp b/src/ifcgeom/IfcRepresentation.cpp index e4ca45d5e0..8752b20d6b 100644 --- a/src/ifcgeom/IfcRepresentation.cpp +++ b/src/ifcgeom/IfcRepresentation.cpp @@ -33,9 +33,9 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcRepresentation* l, IfcRepresen TopoDS_Shape s; if (convert_shape(representation_item, s)) { if (s.ShapeType() == TopAbs_COMPOUND && TopoDS_Iterator(s).More() && TopoDS_Iterator(s).Value().ShapeType() == TopAbs_SOLID) { - TopoDS_Iterator it(s); - for (; it.More(); it.Next()) { - shapes.push_back(IfcRepresentationShapeItem(representation_item->data().id(), it.Value(), get_style(representation_item))); + TopoDS_Iterator topo_it(s); + for (; topo_it.More(); topo_it.Next()) { + shapes.push_back(IfcRepresentationShapeItem(representation_item->data().id(), topo_it.Value(), get_style(representation_item))); } } else { shapes.push_back(IfcRepresentationShapeItem(representation_item->data().id(), s, get_style(representation_item))); diff --git a/src/ifcgeom/IfcSurfaceCurveSweptAreaSolid.cpp b/src/ifcgeom/IfcSurfaceCurveSweptAreaSolid.cpp index 7837b4f8a5..63a0fdd5c5 100644 --- a/src/ifcgeom/IfcSurfaceCurveSweptAreaSolid.cpp +++ b/src/ifcgeom/IfcSurfaceCurveSweptAreaSolid.cpp @@ -34,6 +34,7 @@ #include #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/base_utils.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -51,7 +52,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcSurfaceCurveSweptAreaSolid* l, Logger::Error("Failed to convert reference surface", l); return false; } - if (count(surface_shell, TopAbs_FACE) != 1) { + if (util::count(surface_shell, TopAbs_FACE) != 1) { Logger::Error("Non-continuous reference surface", l); return false; } diff --git a/src/ifcgeom/IfcSweptDiskSolid.cpp b/src/ifcgeom/IfcSweptDiskSolid.cpp index 1ae3aaffe5..6cdf8f0bcc 100644 --- a/src/ifcgeom/IfcSweptDiskSolid.cpp +++ b/src/ifcgeom/IfcSweptDiskSolid.cpp @@ -37,6 +37,7 @@ #include "../ifcgeom_schema_agnostic/sweep_utils.h" #include "../ifcgeom_schema_agnostic/wire_utils.h" #include "../ifcgeom_schema_agnostic/face_definition.h" +#include "../ifcgeom_schema_agnostic/base_utils.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -81,7 +82,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcSweptDiskSolid* l, TopoDS_Shap ep = l->EndParam(); #endif - if (count(wire, TopAbs_EDGE) == 1 && sp && ep) { + if (util::count(wire, TopAbs_EDGE) == 1 && sp && ep) { TopoDS_Vertex v0, v1; TopExp::Vertices(wire, v0, v1); if (v0.IsSame(v1)) { @@ -153,11 +154,13 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcSweptDiskSolid* l, TopoDS_Shap gp_Pln plane(c1->Position().Location(), n); auto face = BRepBuilderAPI_MakeFace(plane).Face(); - TopoDS_Wire wire; - BB.MakeWire(wire); - BB.Add(wire, a); - BB.Add(wire, b); - BB.Add(face, wire); + { + TopoDS_Wire w; + BB.MakeWire(w); + BB.Add(w, a); + BB.Add(w, b); + BB.Add(face, w); + } TopExp::CommonVertex(a, b, V); BRepFilletAPI_MakeFillet2d mf2d(face); diff --git a/src/ifcgeom_schema_agnostic/IfcGeomRepresentation.cpp b/src/ifcgeom_schema_agnostic/IfcGeomRepresentation.cpp index c85c826aab..c17ce210ee 100644 --- a/src/ifcgeom_schema_agnostic/IfcGeomRepresentation.cpp +++ b/src/ifcgeom_schema_agnostic/IfcGeomRepresentation.cpp @@ -29,6 +29,7 @@ #include "../ifcparse/IfcLogger.h" #include "../ifcgeom_schema_agnostic/Kernel.h" +#include "../ifcgeom_schema_agnostic/base_utils.h" IfcGeom::Representation::Serialization::Serialization(const BRep& brep) : Representation(brep.settings()) @@ -214,7 +215,7 @@ bool IfcGeom::Representation::BRep::calculate_volume(double& volume) const { volume = 0.; for (IfcGeom::IfcRepresentationShapeItems::const_iterator it = begin(); it != end(); ++it) { - if (Kernel::is_manifold(it->Shape())) { + if (util::is_manifold(it->Shape())) { GProp_GProps prop; BRepGProp::VolumeProperties(it->Shape(), prop); volume += prop.Mass(); @@ -238,7 +239,7 @@ bool IfcGeom::Representation::BRep::calculate_projected_surface_area(const gp_Ax double x, y, z; surface_area_along_direction(settings().deflection_tolerance(), it->Shape(), ax, x, y, z); - if (Kernel::is_manifold(it->Shape())) { + if (util::is_manifold(it->Shape())) { x /= 2.; y /= 2.; z /= 2.; diff --git a/src/ifcgeom_schema_agnostic/IfcGeomTree.h b/src/ifcgeom_schema_agnostic/IfcGeomTree.h index 4f0fae3335..f259229c1c 100644 --- a/src/ifcgeom_schema_agnostic/IfcGeomTree.h +++ b/src/ifcgeom_schema_agnostic/IfcGeomTree.h @@ -410,7 +410,7 @@ namespace IfcGeom { // Assumption is that the number of styles is small, so the linear lookup time is not significant. auto sit = std::find(styles_.begin(), styles_.end(), *adaptor); - int index; + size_t index; if (sit == styles_.end()) { index = styles_.size(); styles_.push_back(*adaptor); @@ -420,7 +420,7 @@ namespace IfcGeom { TopExp_Explorer exp(it.Value(), TopAbs_FACE); for (; exp.More(); exp.Next()) { - face_styles_.Bind(exp.Current(), index); + face_styles_.Bind(exp.Current(), (int) index); } } } diff --git a/src/ifcgeom_schema_agnostic/Kernel.cpp b/src/ifcgeom_schema_agnostic/Kernel.cpp index 0310a40146..ad0bf2063b 100644 --- a/src/ifcgeom_schema_agnostic/Kernel.cpp +++ b/src/ifcgeom_schema_agnostic/Kernel.cpp @@ -23,33 +23,6 @@ IfcGeom::Kernel::Kernel(IfcParse::IfcFile* file) { } } -int IfcGeom::Kernel::count(const TopoDS_Shape& s, TopAbs_ShapeEnum t, bool unique) { - if (unique) { - TopTools_IndexedMapOfShape map; - TopExp::MapShapes(s, t, map); - return map.Extent(); - } else { - int i = 0; - TopExp_Explorer exp(s, t); - for (; exp.More(); exp.Next()) { - ++i; - } - return i; - } -} - - -int IfcGeom::Kernel::surface_genus(const TopoDS_Shape& s) { - int nv = count(s, TopAbs_VERTEX, true); - int ne = count(s, TopAbs_EDGE, true); - int nf = count(s, TopAbs_FACE, true); - - const int euler = nv - ne + nf; - const int genus = (2 - euler) / 2; - - return genus; -} - IfcGeom::impl::KernelFactoryImplementation& IfcGeom::impl::kernel_implementations() { static KernelFactoryImplementation impl; return impl; @@ -247,102 +220,3 @@ std::map IfcGeom::Kernel::get_layers(IfcUt throw IfcParse::IfcException("Unexpected entity " + inst->declaration().name()); } - -bool IfcGeom::Kernel::is_manifold(const TopoDS_Shape& a) { - if (a.ShapeType() == TopAbs_COMPOUND || a.ShapeType() == TopAbs_SOLID) { - TopoDS_Iterator it(a); - for (; it.More(); it.Next()) { - if (!is_manifold(it.Value())) { - return false; - } - } - return true; - } else { - TopTools_IndexedDataMapOfShapeListOfShape map; - TopExp::MapShapesAndAncestors(a, TopAbs_EDGE, TopAbs_FACE, map); - - for (int i = 1; i <= map.Extent(); ++i) { - const TopoDS_Edge& e = TopoDS::Edge(map.FindKey(i)); - - TopoDS_Vertex v0, v1; - TopExp::Vertices(e, v0, v1); - const bool degenerate = !v0.IsNull() && !v1.IsNull() && v0.IsSame(v1); - - if (degenerate) { - continue; - } - - if (map.FindFromIndex(i).Extent() != 2) { - return false; - } - } - - return true; - } -} - -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; - } -} - -namespace { - template struct dimension_count {}; - template <> struct dimension_count { static const int n = 2; }; - template <> struct dimension_count { static const int n = 2; }; - template <> struct dimension_count < gp_Trsf > { static const int n = 3; }; - template <> struct dimension_count < gp_GTrsf > { static const int n = 3; }; - - template - bool is_identity_helper(const T& t, double tolerance) { - // Note the {1, n+1} range due to Open Cascade's 1-based indexing - // Note the {1, n+2} range due to the translation part of the matrix - for (int i = 1; i < dimension_count::n + 2; ++i) { - for (int j = 1; j < dimension_count::n + 1; ++j) { - const double iden_value = i == j ? 1. : 0.; - const double trsf_value = t.Value(j, i); - if (fabs(trsf_value - iden_value) > tolerance) { - return false; - } - } - } - return true; - } -} - -bool IfcGeom::Kernel::is_identity(const gp_Trsf2d& t, double tolerance) { - return is_identity_helper(t, tolerance); -} - -bool IfcGeom::Kernel::is_identity(const gp_GTrsf2d& t, double tolerance) { - return is_identity_helper(t, tolerance); -} - -bool IfcGeom::Kernel::is_identity(const gp_Trsf& t, double tolerance) { - return is_identity_helper(t, tolerance); -} - -bool IfcGeom::Kernel::is_identity(const gp_GTrsf& t, double tolerance) { - return is_identity_helper(t, tolerance); -} - -gp_Trsf IfcGeom::Kernel::combine_offset_and_rotation(const gp_Vec & offset, const gp_Quaternion & rotation) { - auto offset_transform = gp_Trsf{}; - offset_transform.SetTranslation(offset); - - auto rotation_transform = gp_Trsf{}; - rotation_transform.SetRotation(rotation); - - return rotation_transform * offset_transform; -} diff --git a/src/ifcgeom_schema_agnostic/Kernel.h b/src/ifcgeom_schema_agnostic/Kernel.h index efbbcec73f..d78c89f79e 100644 --- a/src/ifcgeom_schema_agnostic/Kernel.h +++ b/src/ifcgeom_schema_agnostic/Kernel.h @@ -124,38 +124,10 @@ namespace IfcGeom { return implementation_->convert_placement(item, trsf); } - IFC_PARSE_API static int count(const TopoDS_Shape&, TopAbs_ShapeEnum, bool unique = false); - IFC_PARSE_API static int surface_genus(const TopoDS_Shape&); - - IFC_PARSE_API static bool is_manifold(const TopoDS_Shape& a); + IFC_PARSE_API static IfcUtil::IfcBaseEntity* get_decomposing_entity(IfcUtil::IfcBaseEntity*, bool include_openings = true); IFC_PARSE_API static std::map get_layers(IfcUtil::IfcBaseEntity*); - // For axis placements detect equality early in order for the - // relatively computionaly expensive gp_Trsf calculation to be skipped - IFC_PARSE_API static bool axis_equal(const gp_Ax3& a, const gp_Ax3& b, double tolerance) { - if (!a.Location().IsEqual(b.Location(), tolerance)) return false; - // Note that the tolerance below is angular, above is linear. Since architectural - // objects are about 1m'ish in scale, it should be somewhat equivalent. Besides, - // this is mostly a filter for NULL or default values in the placements. - if (!a.Direction().IsEqual(b.Direction(), tolerance)) return false; - if (!a.XDirection().IsEqual(b.XDirection(), tolerance)) return false; - if (!a.YDirection().IsEqual(b.YDirection(), tolerance)) return false; - return true; - } - - IFC_PARSE_API static bool axis_equal(const gp_Ax2d& a, const gp_Ax2d& b, double tolerance) { - if (!a.Location().IsEqual(b.Location(), tolerance)) return false; - if (!a.Direction().IsEqual(b.Direction(), tolerance)) return false; - return true; - } - - IFC_PARSE_API static bool is_identity(const gp_Trsf2d& t, double tolerance); - IFC_PARSE_API static bool is_identity(const gp_GTrsf2d& t, double tolerance); - IFC_PARSE_API static bool is_identity(const gp_Trsf& t, double tolerance); - IFC_PARSE_API static bool is_identity(const gp_GTrsf& t, double tolerance); - - IFC_PARSE_API static gp_Trsf combine_offset_and_rotation(const gp_Vec &offset, const gp_Quaternion& rotation); }; namespace impl { @@ -171,10 +143,6 @@ namespace IfcGeom { KernelFactoryImplementation& kernel_implementations(); } - namespace util { - bool is_nested_compound_of_solid(const TopoDS_Shape& s, int depth = 0); - } - class IFC_GEOM_API geometry_exception : public std::exception { protected: std::string message; diff --git a/src/ifcgeom_schema_agnostic/base_utils.cpp b/src/ifcgeom_schema_agnostic/base_utils.cpp new file mode 100644 index 0000000000..3a0cc41d1c --- /dev/null +++ b/src/ifcgeom_schema_agnostic/base_utils.cpp @@ -0,0 +1,155 @@ +#include "base_utils.h" + +#include +#include +#include +#include +#include +#include +#include + +// For axis placements detect equality early in order for the +// relatively computionaly expensive gp_Trsf calculation to be skipped +bool IfcGeom::util::axis_equal(const gp_Ax3 & a, const gp_Ax3 & b, double tolerance) { + if (!a.Location().IsEqual(b.Location(), tolerance)) return false; + // Note that the tolerance below is angular, above is linear. Since architectural + // objects are about 1m'ish in scale, it should be somewhat equivalent. Besides, + // this is mostly a filter for NULL or default values in the placements. + if (!a.Direction().IsEqual(b.Direction(), tolerance)) return false; + if (!a.XDirection().IsEqual(b.XDirection(), tolerance)) return false; + if (!a.YDirection().IsEqual(b.YDirection(), tolerance)) return false; + return true; +} + +bool IfcGeom::util::axis_equal(const gp_Ax2d & a, const gp_Ax2d & b, double tolerance) { + if (!a.Location().IsEqual(b.Location(), tolerance)) return false; + if (!a.Direction().IsEqual(b.Direction(), tolerance)) return false; + return true; +} + +int IfcGeom::util::count(const TopoDS_Shape& s, TopAbs_ShapeEnum t, bool unique) { + if (unique) { + TopTools_IndexedMapOfShape map; + TopExp::MapShapes(s, t, map); + return map.Extent(); + } else { + int i = 0; + TopExp_Explorer exp(s, t); + for (; exp.More(); exp.Next()) { + ++i; + } + return i; + } +} + + +int IfcGeom::util::surface_genus(const TopoDS_Shape& s) { + int nv = count(s, TopAbs_VERTEX, true); + int ne = count(s, TopAbs_EDGE, true); + int nf = count(s, TopAbs_FACE, true); + + const int euler = nv - ne + nf; + const int genus = (2 - euler) / 2; + + return genus; +} + +bool IfcGeom::util::is_manifold(const TopoDS_Shape& a) { + if (a.ShapeType() == TopAbs_COMPOUND || a.ShapeType() == TopAbs_SOLID) { + TopoDS_Iterator it(a); + for (; it.More(); it.Next()) { + if (!is_manifold(it.Value())) { + return false; + } + } + return true; + } else { + TopTools_IndexedDataMapOfShapeListOfShape map; + TopExp::MapShapesAndAncestors(a, TopAbs_EDGE, TopAbs_FACE, map); + + for (int i = 1; i <= map.Extent(); ++i) { + const TopoDS_Edge& e = TopoDS::Edge(map.FindKey(i)); + + TopoDS_Vertex v0, v1; + TopExp::Vertices(e, v0, v1); + const bool degenerate = !v0.IsNull() && !v1.IsNull() && v0.IsSame(v1); + + if (degenerate) { + continue; + } + + if (map.FindFromIndex(i).Extent() != 2) { + return false; + } + } + + return true; + } +} + + +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; + } +} + +namespace { + template struct dimension_count {}; + template <> struct dimension_count { static const int n = 2; }; + template <> struct dimension_count { static const int n = 2; }; + template <> struct dimension_count < gp_Trsf > { static const int n = 3; }; + template <> struct dimension_count < gp_GTrsf > { static const int n = 3; }; + + template + bool is_identity_helper(const T& t, double tolerance) { + // Note the {1, n+1} range due to Open Cascade's 1-based indexing + // Note the {1, n+2} range due to the translation part of the matrix + for (int i = 1; i < dimension_count::n + 2; ++i) { + for (int j = 1; j < dimension_count::n + 1; ++j) { + const double iden_value = i == j ? 1. : 0.; + const double trsf_value = t.Value(j, i); + if (fabs(trsf_value - iden_value) > tolerance) { + return false; + } + } + } + return true; + } +} + +bool IfcGeom::util::is_identity(const gp_Trsf2d& t, double tolerance) { + return is_identity_helper(t, tolerance); +} + +bool IfcGeom::util::is_identity(const gp_GTrsf2d& t, double tolerance) { + return is_identity_helper(t, tolerance); +} + +bool IfcGeom::util::is_identity(const gp_Trsf& t, double tolerance) { + return is_identity_helper(t, tolerance); +} + +bool IfcGeom::util::is_identity(const gp_GTrsf& t, double tolerance) { + return is_identity_helper(t, tolerance); +} + +gp_Trsf IfcGeom::util::combine_offset_and_rotation(const gp_Vec & offset, const gp_Quaternion & rotation) { + auto offset_transform = gp_Trsf{}; + offset_transform.SetTranslation(offset); + + auto rotation_transform = gp_Trsf{}; + rotation_transform.SetRotation(rotation); + + return rotation_transform * offset_transform; +} diff --git a/src/ifcgeom_schema_agnostic/base_utils.h b/src/ifcgeom_schema_agnostic/base_utils.h new file mode 100644 index 0000000000..b7c640624e --- /dev/null +++ b/src/ifcgeom_schema_agnostic/base_utils.h @@ -0,0 +1,32 @@ +#ifndef BASE_UTILS_H +#define BASE_UTILS_H + +#include +#include + +namespace IfcGeom { + namespace util { + + int count(const TopoDS_Shape&, TopAbs_ShapeEnum, bool unique = false); + int surface_genus(const TopoDS_Shape&); + + bool is_manifold(const TopoDS_Shape& a); + + // For axis placements detect equality early in order for the + // relatively computionaly expensive gp_Trsf calculation to be skipped + bool axis_equal(const gp_Ax3& a, const gp_Ax3& b, double tolerance); + + bool axis_equal(const gp_Ax2d& a, const gp_Ax2d& b, double tolerance); + + bool is_identity(const gp_Trsf2d& t, double tolerance); + bool is_identity(const gp_GTrsf2d& t, double tolerance); + bool is_identity(const gp_Trsf& t, double tolerance); + bool is_identity(const gp_GTrsf& t, double tolerance); + + gp_Trsf combine_offset_and_rotation(const gp_Vec &offset, const gp_Quaternion& rotation); + + bool is_nested_compound_of_solid(const TopoDS_Shape& s, int depth = 0); + } +} + +#endif \ No newline at end of file diff --git a/src/ifcgeom_schema_agnostic/boolean_utils.cpp b/src/ifcgeom_schema_agnostic/boolean_utils.cpp index 38d2a06f72..b77122de3e 100644 --- a/src/ifcgeom_schema_agnostic/boolean_utils.cpp +++ b/src/ifcgeom_schema_agnostic/boolean_utils.cpp @@ -1,6 +1,7 @@ #include "boolean_utils.h" #include "../ifcgeom_schema_agnostic/IfcGeomTree.h" +#include "../ifcgeom_schema_agnostic/base_utils.h" #include #include @@ -17,8 +18,18 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include void IfcGeom::util::copy_operand(const TopTools_ListOfShape & l, TopTools_ListOfShape & r) { #if OCC_VERSION_HEX < 0x70000 @@ -609,6 +620,18 @@ bool IfcGeom::util::boolean_subtraction_2d_using_builder(const TopoDS_Shape & a_ } } + { + TopoDS_Compound C; + BRep_Builder BB; + BB.MakeCompound(C); + + for (auto& w : wires) { + BB.Add(C, w); + } + + BRepTools::Write(C, "debug.brep"); + } + shape_index = 0; edge_index = 0; @@ -786,4 +809,551 @@ bool IfcGeom::util::points_on_planar_face_generator::operator()(gp_Pnt& p) { } return false; -} \ No newline at end of file +} + + +bool IfcGeom::util::boolean_operation(const boolean_settings& settings, const TopoDS_Shape& a_input, const TopTools_ListOfShape& b_input, BOPAlgo_Operation op, TopoDS_Shape& result, double fuzziness) { + using namespace std::string_literals; + + const bool do_unify = true; + const bool do_subtraction_eliminate_disjoint_bbox = true; + const bool do_subtraction_eliminate_touching = true; + const bool do_attempt_2d_boolean = settings.attempt_2d; + const bool debug = settings.debug; + + std::string debug_identifier; + if (debug) { + static my_thread_local size_t operation_counter_ = 0; + std::stringstream ss; + ss << "bool-" << std::this_thread::get_id() << "-" << (operation_counter_++); + debug_identifier = ss.str(); + Logger::Notice("Boolean debug identifier: " + debug_identifier); + } + + if (fuzziness < 0.) { + fuzziness = settings.precision / 10.; + } + + // @todo, it does seem a bit odd, we first triangulate non-planar faces + // to later unify them again. Can we make this a bit more intelligent? + TopoDS_Shape a; + TopTools_ListOfShape b; + + if (do_unify) { + PERF("boolean operation: unifying operands"); + + a = unify(a_input, fuzziness * 1000.); + + Logger::Message( + Logger::LOG_DEBUG, + "Simplified operand A from "s + + std::to_string(count(a_input, TopAbs_FACE)) + + " to "s + + std::to_string(count(a, TopAbs_FACE)) + ); + + { + TopTools_ListIteratorOfListOfShape it(b_input); + for (; it.More(); it.Next()) { + b.Append(unify(it.Value(), fuzziness)); + Logger::Message( + Logger::LOG_DEBUG, + "Simplified operand B from "s + + std::to_string(count(it.Value(), TopAbs_FACE)) + + " to "s + + std::to_string(count(b.Last(), TopAbs_FACE)) + ); + } + } + } else { + a = a_input; + b = b_input; + } + + bool success = false; + BRepAlgoAPI_BooleanOperation* builder; + TopTools_ListOfShape b_tmp; + + if (op == BOPAlgo_CUT) { + builder = new BRepAlgoAPI_Cut(); + + if (do_subtraction_eliminate_disjoint_bbox) { + PERF("boolean subtraction: eliminate disjoint bbox"); + + auto N = bounding_box_overlap(fuzziness, a, b, b_tmp); + if (N) { + Logger::Notice("Eliminated " + std::to_string(N) + " disjoint operands"); + std::swap(b, b_tmp); + } + } + + if (do_subtraction_eliminate_touching) { + PERF("boolean subtraction: eliminate touching"); + + b_tmp.Clear(); + auto N = eliminate_touching_operands(fuzziness, a, b, b_tmp); + if (N) { + Logger::Notice("Eliminated " + std::to_string(N) + " touching operands"); + std::swap(b, b_tmp); + } + } + + } else if (op == BOPAlgo_COMMON) { + builder = new BRepAlgoAPI_Common(); + } else if (op == BOPAlgo_FUSE) { + builder = new BRepAlgoAPI_Fuse(); + } else { + return false; + } + + if (b.Extent() == 0) { + result = a; + return true; + } + + if (Logger::LOG_NOTICE >= Logger::Verbosity()) { + PERF("preliminary manifoldness check"); + + Logger::Notice("Operand A is " + (is_manifold(a) ? ""s : "non-"s) + "manifold"); + + TopTools_ListIteratorOfListOfShape it(b); + for (int i = 0; it.More(); it.Next(), ++i) { + Logger::Notice("Operand B " + std::to_string(i) + " is " + (is_manifold(it.Value()) ? ""s : "non-"s) + "manifold"); + } + } + + // Find a sensible value for the fuzziness, based on precision + // and limited by edge lengths and vertex-edge distances. + double min_length_orig; + + { + PERF("boolean operation: min edge length"); + + min_length_orig = min_edge_length(a); + TopTools_ListIteratorOfListOfShape it(b); + for (; it.More(); it.Next()) { + double d = min_edge_length(it.Value()); + if (d < min_length_orig) { + min_length_orig = d; + } + } + } + + { + PERF("boolean operation: min vertex-edge dist"); + + double d = min_vertex_edge_distance(a, settings.precision, min_length_orig); + if (d < min_length_orig) { + min_length_orig = d; + } + + TopTools_ListIteratorOfListOfShape it(b); + for (; it.More(); it.Next()) { + d = min_vertex_edge_distance(it.Value(), settings.precision, min_length_orig); + if (d < min_length_orig) { + min_length_orig = d; + } + } + } + + const double fuzz = (std::min)(min_length_orig / 3., fuzziness); + + Logger::Notice("Used fuzziness: " + std::to_string(fuzz)); + + TopTools_ListOfShape s1s; + s1s.Append(copy_operand(a)); + + if (debug) { + TopTools_ListOfShape* lists[2] = { &s1s, &b }; + static std::string operand_names[2] = { "a", "b" }; + for (int i = 0; i < 2; ++i) { + TopTools_ListIteratorOfListOfShape it(*lists[i]); + for (int j = 0; it.More(); it.Next(), ++j) { + std::string fn = debug_identifier + "-" + operand_names[i] + "-" + std::to_string(j) + ".brep"; + BRepTools::Write(it.Value(), fn.c_str()); + } + } + } + + if (op == BOPAlgo_CUT) { + TopoDS_Face a_face; + std::pair a_interval; + + TopTools_ListOfShape b_faces, b_remainder_3d; + + bool is_extrusion_a = false; + if (do_attempt_2d_boolean) { + PERF("boolean subtraction: extrusion check"); + + is_extrusion_a = is_extrusion(gp::DY(), a, a_face, a_interval); + } + + if (is_extrusion_a) { + Logger::Notice("Operand A 1/1 is an extrusion"); + + TopTools_ListIteratorOfListOfShape it(b); + for (int nb = 1; it.More(); it.Next(), ++nb) { + bool process_2d = false; + TopoDS_Face b_face; + std::pair b_interval; + + bool is_extrusion_b; + { + PERF("boolean subtraction: extrusion check"); + + is_extrusion_b = is_extrusion(gp::DY(), it.Value(), b_face, b_interval); + } + + if (is_extrusion_b) { + Logger::Notice("Operand B " + std::to_string(nb) + "/" + std::to_string(b.Extent()) + " is an extrusion"); + + if (b_interval.first < a_interval.first + fuzz && b_interval.second > a_interval.second - fuzz) { + Logger::Notice("Operand B creates a through hole"); + + // Align b with a operand + gp_Trsf trsf; + trsf.SetTranslation(gp_Vec(gp::DY()) * (a_interval.first - b_interval.first)); + + b_faces.Append(b_face.Moved(trsf)); + process_2d = true; + } + } + + if (!process_2d) { + b_remainder_3d.Append(it.Value()); + } + } + + if (b_faces.Extent()) { + TopoDS_Shape face_result; + + bool boolean_op_2d_success; + { + PERF("boolean operation: 2d builder"); + // First try using face builder + + boolean_op_2d_success = boolean_subtraction_2d_using_builder(a_face, b_faces, face_result, fuzziness); + } + + if (!boolean_op_2d_success) { + PERF("boolean operation: 2d"); + // Retry using generic 2d using boolean algo on faces + + boolean_op_2d_success = boolean_operation(settings, a_face, b_faces, op, face_result, fuzziness); + } + + if (boolean_op_2d_success) { + PERF("boolean operation: 2d to 3d"); + + BRepPrimAPI_MakePrism mp(face_result, gp_Vec(gp::DY()) * (a_interval.second - a_interval.first)); + if (mp.IsDone()) { + if (b_remainder_3d.Extent()) { + Logger::Notice(std::to_string(b_remainder_3d.Extent()) + " operands remaining to process in 3D"); + b = b_remainder_3d; + s1s.Clear(); + s1s.Append(mp.Shape()); + } else { + Logger::Notice("Processed fully in 2D"); + result = mp.Shape(); + return true; + } + } else { + Logger::Notice("Failed to extrude 2D boolean result. Retrying in 3D."); + } + } else { + Logger::Notice("Failed to perform 2D boolean operation. Retrying in 3D."); + } + } else { + Logger::Notice("No second operands can be processed as 2D inner bounds. Retrying in 3D."); + } + } + } + +#if OCC_VERSION_HEX >= 0x70000 + builder->SetNonDestructive(true); +#endif + builder->SetFuzzyValue(fuzz); + builder->SetArguments(s1s); + copy_operand(b, b_tmp); + std::swap(b, b_tmp); + builder->SetTools(b); + { + PERF("boolean operation: build"); + + builder->Build(); + } + if (builder->IsDone()) { + if (builder->DSFiller()->HasWarning(STANDARD_TYPE(BOPAlgo_AlertAcquiredSelfIntersection))) { + Logger::Notice("Builder reports self-intersection in output"); + success = false; + } else { + TopoDS_Shape r = *builder; + + { + PERF("boolean operation: shape healing"); + + ShapeFix_Shape fix(r); + try { + fix.SetMaxTolerance(fuzz); + fix.Perform(); + r = fix.Shape(); + } catch (...) { + Logger::Error("Shape healing failed on boolean result"); + } + } + + { + PERF("boolean operation: shape analysis"); + + BRepCheck_Analyzer ana(r); + success = ana.IsValid() != 0; + + if (!success) { + Logger::Notice("Boolean operation yields invalid result"); + + std::stringstream str; + bool any_emitted = false; + + std::function dump; + dump = [&ana, &str, &dump, &any_emitted](const TopoDS_Shape& s) { + if (!ana.Result(s).IsNull()) { + BRepCheck_ListIteratorOfListOfStatus itl; + itl.Initialize(ana.Result(s)->Status()); + for (; itl.More(); itl.Next()) { + if (itl.Value() != BRepCheck_NoError) { + if (any_emitted) { + str << ", "; + } + BRepCheck::Print(itl.Value(), str); + str.seekp(str.tellp() - (std::streamoff)1); + str << " on "; + TopAbs::Print(s.ShapeType(), str); + any_emitted = true; + } + } + } + for (TopoDS_Iterator it(s); it.More(); it.Next()) { + dump(it.Value()); + } + }; + + dump(r); + + Logger::Notice(str.str()); + } + } + + if (success) { + + { + PERF("boolean operation: manifoldness check"); + + success = !is_manifold(a) || is_manifold(r); + } + + if (!success) { + PERF("boolean operation: manifoldness check excemption"); + + // An excemption for the requirement to be manifold: When the cut operands have overlapping edge belonging to faces that do not overlap. + bool operands_nonmanifold = false; + if (op == BOPAlgo_CUT) { + TopTools_IndexedMapOfShape edges; + TopTools_IndexedDataMapOfShapeListOfShape map; + for (TopTools_ListIteratorOfListOfShape it2(b); it2.More(); it2.Next()) { + auto& bb = it2.Value(); + TopExp::MapShapes(bb, TopAbs_EDGE, edges); + TopExp::MapShapesAndAncestors(bb, TopAbs_EDGE, TopAbs_FACE, map); + } + IfcGeom::impl::tree tree; + for (int i = 1; i <= edges.Extent(); ++i) { + tree.add(i, edges.FindKey(i)); + } + for (int i = 1; i <= edges.Extent(); ++i) { + const TopoDS_Edge& ei = TopoDS::Edge(edges.FindKey(i)); + Bnd_Box bb; + BRepBndLib::Add(ei, bb); + bb.Enlarge(fuzziness); + auto ii = tree.select_box(bb, false); + for (int j : ii) { + if (j != i) { + const TopoDS_Edge& ej = TopoDS::Edge(edges.FindKey(j)); + ShapeAnalysis_Edge sae; + double f = fuzziness; + bool edges_overlapping = sae.CheckOverlapping(ei, ej, f, 0.) || + sae.CheckOverlapping(ej, ei, f, 0.); + + if (edges_overlapping) { + auto faces_i = map.FindFromKey(edges.FindKey(i)); + auto faces_j = map.FindFromKey(edges.FindKey(j)); + bool overlap = false; + for (TopTools_ListIteratorOfListOfShape it4(faces_i); it4.More(); it4.Next()) { + auto& fi = it4.Value(); + for (TopTools_ListIteratorOfListOfShape it2(faces_j); it2.More(); it2.Next()) { + auto& fj = it2.Value(); + if (faces_overlap(TopoDS::Face(fi), TopoDS::Face(fj))) { + overlap = true; + } + } + if (overlap) { + break; + } + } + operands_nonmanifold = !overlap; + break; + } + } + } + if (operands_nonmanifold) { + break; + } + } + } + success = operands_nonmanifold; + } + + if (success) { + + bool all_faces_included_in_result = true; + bool has_open_shells = false; + + if (op == BOPAlgo_CUT) { + PERF("boolean operation: open shell face adition check"); + + for (TopExp_Explorer exp(a, TopAbs_SHELL); exp.More(); exp.Next()) { + if (!exp.Current().Closed()) { + // This 'face addition check' is only done when the first operand + // contains open shells (which was initially the aim of this check + // see #1472). + // Later in #1914 we found that the logic to apply openings in groups + // of similar edge lengths can create a situation of inner voids, which + // trigger a false positive in this check. This could have also been + // solved below by checking whether the opening(s) are included as a + // unmodified (interior) shell within a solid of multiple shells. + // Checking for open shells in first operand was quicker and more + // straightforward. The question still is whether in cases like #1472 + // we need to first try the boolean union as solid/solid interference + // to trigger this case or whether we can immediately proceed to a face/ + // solid operation. + has_open_shells = true; + break; + } + } + + if (has_open_shells) { + TopTools_IndexedMapOfShape faces; + TopExp::MapShapes(r, TopAbs_FACE, faces); + for (TopExp_Explorer exp(a, TopAbs_FACE); exp.More(); exp.Next()) { + auto& f = TopoDS::Face(exp.Current()); + if (!faces.Contains(f)) { + all_faces_included_in_result = false; + break; + } + } + } else { + all_faces_included_in_result = false; + } + } + + int result_n_faces = count(r, TopAbs_FACE); + int first_op_n_faces = count(a, TopAbs_FACE); + + if (op == BOPAlgo_CUT && has_open_shells && all_faces_included_in_result && result_n_faces > first_op_n_faces) { + success = false; + Logger::Notice("Boolean result discarded because subtractions results in only the addition of faces"); + } else { + // when there are edges or vertex-edge distances close to the used fuzziness, the + // output is not trusted and the operation is attempted with a higher fuzziness. + int reason = 0; + double v; + + { + PERF("boolean operation: result min edge length check"); + + if ((v = min_edge_length(r)) < fuzziness * 3.) { + reason = 0; + success = false; + + goto skip_further_checks; + } + } + + { + PERF("boolean operation: result min vertex-edge dist check"); + + if ((v = min_vertex_edge_distance(r, settings.precision, fuzziness * 3.)) < fuzziness * 3.) { + reason = 1; + success = false; + + goto skip_further_checks; + } + } + + { + PERF("boolean operation: result min face-face dist check"); + + if ((v = min_face_face_distance(r, 1.e-4)) < 1.e-4) { + // #2095 Check if this distance wasn't already realized in the input first operand. + if (v < min_face_face_distance(a, 1.e-4)) { + reason = 2; + success = false; + } + } + } + + skip_further_checks: + if (!success) { + static const char* const reason_strings[] = { "edge length", "vertex-edge", "face-face" }; + std::stringstream str; + str << "Boolean operation result failing " << reason_strings[reason] << " interference check, with fuzziness " << fuzziness << " with length " << v; + Logger::Notice(str.str()); + } + } + + if (success) { + result = r; + } + + } else { + Logger::Notice("Boolean operation yields non-manifold result"); + } + } + } + } else { + std::stringstream str; + +#if OCC_VERSION_HEX >= 0x70200 + + if (builder->HasError(STANDARD_TYPE(BOPAlgo_AlertBOPNotAllowed))) { + Logger::Error("Invalid operands. Using first operand"); + result = a; + success = true; + } +#endif + +#if OCC_VERSION_HEX >= 0x70000 + builder->DumpErrors(str); +#else + str << "Error code: " << builder->ErrorStatus(); +#endif + std::string str_str = str.str(); + if (str_str.size()) { + Logger::Notice(str_str); + } + } + delete builder; + if (!success) { + const double new_fuzziness = fuzziness * 10.; + if (new_fuzziness - 1e-15 <= settings.precision * 10000. && new_fuzziness < min_length_orig) { + return boolean_operation(settings, a, b, op, result, new_fuzziness); + } else { + Logger::Notice("No longer attempting boolean operation with higher fuzziness"); + } + } + return success && !result.IsNull(); +} + +bool IfcGeom::util::boolean_operation(const boolean_settings& settings, const TopoDS_Shape& a, const TopoDS_Shape& b, BOPAlgo_Operation op, TopoDS_Shape& result, double fuzziness) { + TopTools_ListOfShape bs; + bs.Append(b); + return boolean_operation(settings, a, bs, op, result, fuzziness); +} diff --git a/src/ifcgeom_schema_agnostic/boolean_utils.h b/src/ifcgeom_schema_agnostic/boolean_utils.h index 31cc9ed4f2..3d0cce8b70 100644 --- a/src/ifcgeom_schema_agnostic/boolean_utils.h +++ b/src/ifcgeom_schema_agnostic/boolean_utils.h @@ -28,6 +28,7 @@ #include #include #include +#include namespace IfcGeom { namespace util { @@ -84,6 +85,14 @@ namespace IfcGeom { bool boolean_subtraction_2d_using_builder(const TopoDS_Shape& a_input, const TopTools_ListOfShape& b_input, TopoDS_Shape& result, double eps); + struct boolean_settings { + bool debug, attempt_2d; + double precision; + }; + + bool boolean_operation(const boolean_settings& settings, const TopoDS_Shape&, const TopTools_ListOfShape&, BOPAlgo_Operation, TopoDS_Shape&, double fuzziness = -1.); + + bool boolean_operation(const boolean_settings& settings, const TopoDS_Shape&, const TopoDS_Shape&, BOPAlgo_Operation, TopoDS_Shape&, double fuzziness = -1.); } } diff --git a/src/ifcgeom_schema_agnostic/sweep_utils.cpp b/src/ifcgeom_schema_agnostic/sweep_utils.cpp index 8c166b7dbb..dcb9424b11 100644 --- a/src/ifcgeom_schema_agnostic/sweep_utils.cpp +++ b/src/ifcgeom_schema_agnostic/sweep_utils.cpp @@ -1,8 +1,5 @@ #include "sweep_utils.h" -#include "../ifcparse/IfcLogger.h" -#include "../ifcgeom_schema_agnostic/Kernel.h" - #include #include #include @@ -24,6 +21,10 @@ #include #include +#include "../ifcparse/IfcLogger.h" +#include "../ifcgeom_schema_agnostic/Kernel.h" +#include "../ifcgeom_schema_agnostic/base_utils.h" + bool IfcGeom::util::wire_is_c1_continuous(const TopoDS_Wire & w, double tol) { // NB Note that c0 continuity is NOT checked! @@ -193,7 +194,7 @@ void IfcGeom::util::sort_edges(const TopoDS_Wire & wire, std::vector seen; - auto num_edges = IfcGeom::Kernel::count(wire, TopAbs_EDGE); + auto num_edges = count(wire, TopAbs_EDGE); TopoDS_Vertex v0, v1; // @todo this creates the ancestor map twice diff --git a/src/ifcgeom_schema_agnostic/wire_utils.cpp b/src/ifcgeom_schema_agnostic/wire_utils.cpp index 5c53233012..b9484d48a3 100644 --- a/src/ifcgeom_schema_agnostic/wire_utils.cpp +++ b/src/ifcgeom_schema_agnostic/wire_utils.cpp @@ -2,6 +2,7 @@ #include "../ifcparse/IfcLogger.h" #include "../ifcgeom_schema_agnostic/Kernel.h" +#include "../ifcgeom_schema_agnostic/base_utils.h" #include "../ifcgeom_schema_agnostic/IfcGeomTree.h" #include @@ -84,8 +85,8 @@ bool IfcGeom::util::approximate_plane_through_wire(const TopoDS_Wire& wire, gp_P exp.Init(wire); for (; exp.More(); exp.Next()) { - const TopoDS_Vertex& v = exp.CurrentVertex(); - current = BRep_Tool::Pnt(v); + const TopoDS_Vertex& vrt = exp.CurrentVertex(); + current = BRep_Tool::Pnt(vrt); if (plane.SquareDistance(current) > eps2) { return false; } @@ -355,7 +356,7 @@ bool IfcGeom::util::wire_intersections(const TopoDS_Wire& wire, TopTools_ListOfS return false; } - int n = IfcGeom::Kernel::count(wire, TopAbs_EDGE); + int n = util::count(wire, TopAbs_EDGE); if (n < 3) { wires.Append(wire); return false; diff --git a/src/serializers/SvgSerializer.cpp b/src/serializers/SvgSerializer.cpp index 9fdfc22586..bee8476163 100644 --- a/src/serializers/SvgSerializer.cpp +++ b/src/serializers/SvgSerializer.cpp @@ -78,6 +78,7 @@ #include #include "../ifcparse/IfcGlobalId.h" +#include "../ifcgeom_schema_agnostic/base_utils.h" #include #include @@ -439,7 +440,7 @@ namespace { return boost::none; } - if (IfcGeom::Kernel::count(shell, TopAbs_FACE) != 6) { + if (IfcGeom::util::count(shell, TopAbs_FACE) != 6) { return boost::none; } @@ -1442,7 +1443,7 @@ void SvgSerializer::write(const geometry_data& data) { } - if (file && data.product->declaration().is("IfcBuildingStorey") && storey_height_display_ != SH_NONE && wires->Length() == 1 && IfcGeom::Kernel::count(wire, TopAbs_EDGE) == 1) { + if (file && data.product->declaration().is("IfcBuildingStorey") && storey_height_display_ != SH_NONE && wires->Length() == 1 && IfcGeom::util::count(wire, TopAbs_EDGE) == 1) { std::string elev_str;