From 9351038009f5fbfcde9fe54069842e1e3cf947e4 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Mon, 14 Nov 2022 09:23:59 +0100 Subject: [PATCH] Refactoring --- src/ifcgeom/IfcAnnotationFillArea.cpp | 5 +- src/ifcgeom/IfcArbitraryClosedProfileDef.cpp | 5 +- .../IfcArbitraryProfileDefWithVoids.cpp | 7 +- src/ifcgeom/IfcBooleanResult.cpp | 16 +- src/ifcgeom/IfcCShapeProfileDef.cpp | 3 +- src/ifcgeom/IfcCircleProfileDef.cpp | 3 +- src/ifcgeom/IfcConnectedFaceSet.cpp | 17 +- src/ifcgeom/IfcCraneRailAShapeProfileDef.cpp | 3 +- src/ifcgeom/IfcEllipseProfileDef.cpp | 3 +- src/ifcgeom/IfcExtrudedAreaSolidTapered.cpp | 5 +- src/ifcgeom/IfcGeom.cpp | 1509 +---------------- src/ifcgeom/IfcGeom.h | 48 +- src/ifcgeom/IfcGeometricSet.cpp | 2 +- src/ifcgeom/IfcIShapeProfileDef.cpp | 3 +- src/ifcgeom/IfcLShapeProfileDef.cpp | 3 +- src/ifcgeom/IfcPolyLoop.cpp | 4 +- src/ifcgeom/IfcPolygonalBoundedHalfSpace.cpp | 9 +- src/ifcgeom/IfcPolygonalFaceSet.cpp | 5 +- src/ifcgeom/IfcPolyline.cpp | 3 +- src/ifcgeom/IfcRectangleHollowProfileDef.cpp | 5 +- src/ifcgeom/IfcRectangleProfileDef.cpp | 3 +- src/ifcgeom/IfcRoundedRectangleProfileDef.cpp | 3 +- src/ifcgeom/IfcTShapeProfileDef.cpp | 3 +- src/ifcgeom/IfcTrapeziumProfileDef.cpp | 4 +- src/ifcgeom/IfcTriangulatedFaceSet.cpp | 3 +- src/ifcgeom/IfcUShapeProfileDef.cpp | 3 +- src/ifcgeom/IfcZShapeProfileDef.cpp | 3 +- src/ifcgeom/faceset_helper.cpp | 2 +- src/ifcgeom/mapping.cpp | 11 +- .../IfcRepresentationShapeItem.h | 10 +- src/ifcgeom_schema_agnostic/base_utils.cpp | 588 ++++++- src/ifcgeom_schema_agnostic/base_utils.h | 41 +- src/ifcgeom_schema_agnostic/boolean_utils.cpp | 13 + src/ifcgeom_schema_agnostic/boolean_utils.h | 2 + src/ifcgeom_schema_agnostic/wire_utils.cpp | 382 ++++- src/ifcgeom_schema_agnostic/wire_utils.h | 38 +- 36 files changed, 1162 insertions(+), 1605 deletions(-) diff --git a/src/ifcgeom/IfcAnnotationFillArea.cpp b/src/ifcgeom/IfcAnnotationFillArea.cpp index 1fd33ba896..c6d761d093 100644 --- a/src/ifcgeom/IfcAnnotationFillArea.cpp +++ b/src/ifcgeom/IfcAnnotationFillArea.cpp @@ -22,6 +22,7 @@ #include #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/wire_utils.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -31,7 +32,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcAnnotationFillArea* l, TopoDS_ return false; } - assert_closed_wire(outer_boundary); + util::assert_closed_wire(outer_boundary, getValue(GV_PRECISION)); BRepBuilderAPI_MakeFace mf(outer_boundary); @@ -41,7 +42,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcAnnotationFillArea* l, TopoDS_ for(IfcSchema::IfcCurve::list::it it = inner_boundaries->begin(); it != inner_boundaries->end(); ++it) { TopoDS_Wire hole; if (convert_wire(*it, hole)) { - assert_closed_wire(hole); + util::assert_closed_wire(hole, getValue(GV_PRECISION)); mf.Add(hole); } } diff --git a/src/ifcgeom/IfcArbitraryClosedProfileDef.cpp b/src/ifcgeom/IfcArbitraryClosedProfileDef.cpp index fb4955164e..730497588a 100644 --- a/src/ifcgeom/IfcArbitraryClosedProfileDef.cpp +++ b/src/ifcgeom/IfcArbitraryClosedProfileDef.cpp @@ -19,6 +19,7 @@ #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/wire_utils.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -28,10 +29,10 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcArbitraryClosedProfileDef* l, return false; } - assert_closed_wire(wire); + util::assert_closed_wire(wire, getValue(GV_PRECISION)); TopoDS_Compound f; - bool success = convert_wire_to_faces(wire, f); + bool success = util::convert_wire_to_faces(wire, f, {getValue(GV_NO_WIRE_INTERSECTION_CHECK) < 0., getValue(GV_NO_WIRE_INTERSECTION_TOLERANCE) < 0., 0., getValue(GV_PRECISION)}); if (success) { face = f; } diff --git a/src/ifcgeom/IfcArbitraryProfileDefWithVoids.cpp b/src/ifcgeom/IfcArbitraryProfileDefWithVoids.cpp index c418edf369..4b58093077 100644 --- a/src/ifcgeom/IfcArbitraryProfileDefWithVoids.cpp +++ b/src/ifcgeom/IfcArbitraryProfileDefWithVoids.cpp @@ -22,6 +22,7 @@ #include #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/wire_utils.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -31,8 +32,10 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcArbitraryProfileDefWithVoids* return false; } - assert_closed_wire(profile); + util::assert_closed_wire(profile, getValue(GV_PRECISION)); + // @todo this is a discrepancy with IfcArbitraryClosedProfileDef without voids. + // wire intersection code is not triggered this way. BRepBuilderAPI_MakeFace mf(profile); IfcSchema::IfcCurve::list::ptr voids = l->InnerCurves(); @@ -40,7 +43,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcArbitraryProfileDefWithVoids* for(IfcSchema::IfcCurve::list::it it = voids->begin(); it != voids->end(); ++it) { TopoDS_Wire hole; if (convert_wire(*it, hole)) { - assert_closed_wire(hole); + util::assert_closed_wire(hole, getValue(GV_PRECISION)); mf.Add(hole); } } diff --git a/src/ifcgeom/IfcBooleanResult.cpp b/src/ifcgeom/IfcBooleanResult.cpp index a99b1e6419..76cb4f961a 100644 --- a/src/ifcgeom/IfcBooleanResult.cpp +++ b/src/ifcgeom/IfcBooleanResult.cpp @@ -82,7 +82,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape } if ( shape_type(operand1) == ST_SHAPELIST ) { - if (!(convert_shapes(operand1, items1) && flatten_shape_list(items1, s1, true))) { + if (!(convert_shapes(operand1, items1) && util::flatten_shape_list(items1, s1, true, getValue(GV_PRECISION)))) { return false; } } else if ( shape_type(operand1) == ST_SHAPE ) { @@ -90,7 +90,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape return false; } { TopoDS_Solid temp_solid; - s1 = ensure_fit_for_subtraction(s1, temp_solid); } + s1 = util::ensure_fit_for_subtraction(s1, temp_solid, getValue(GV_PRECISION)); } } else { Logger::Message(Logger::LOG_ERROR, "Invalid representation item for boolean operation", operand1); return false; @@ -101,7 +101,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape return true; } - const double first_operand_volume = shape_volume(s1); + const double first_operand_volume = util::shape_volume(s1); if (first_operand_volume <= ALMOST_ZERO) { Logger::Message(Logger::LOG_WARNING, "Empty solid for:", l->FirstOperand()); } @@ -120,12 +120,12 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape { if (shape_type(op2) == ST_SHAPELIST) { IfcRepresentationShapeItems items2; - shape2_processed = convert_shapes(op2, items2) && flatten_shape_list(items2, s2, true); + shape2_processed = convert_shapes(op2, items2) && util::flatten_shape_list(items2, s2, true, getValue(GV_PRECISION)); } else if (shape_type(op2) == ST_SHAPE) { shape2_processed = convert_shape(op2, s2); if (shape2_processed) { TopoDS_Solid temp_solid; - s2 = ensure_fit_for_subtraction(s2, temp_solid); + s2 = util::ensure_fit_for_subtraction(s2, temp_solid, getValue(GV_PRECISION)); } } else { Logger::Message(Logger::LOG_ERROR, "Invalid representation item for boolean operation", op2); @@ -135,7 +135,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape if (is_unbounded_halfspace) { TopoDS_Shape temp; double d; - if (fit_halfspace(s1, s2, temp, d)) { + if (util::fit_halfspace(s1, s2, temp, d, getValue(GV_PRECISION))) { if (d < getValue(GV_PRECISION)) { Logger::Message(Logger::LOG_WARNING, "Halfspace subtraction yields unchanged volume:", l); continue; @@ -151,7 +151,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape } if (op2->declaration().is(IfcSchema::IfcHalfSpaceSolid::Class())) { - const double second_operand_volume = shape_volume(s2); + const double second_operand_volume = util::shape_volume(s2); if (second_operand_volume <= ALMOST_ZERO) { Logger::Message(Logger::LOG_WARNING, "Empty solid for:", op2); } @@ -210,7 +210,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcBooleanResult* l, TopoDS_Shape if (op == IfcSchema::IfcBooleanOperator::IfcBooleanOperator_DIFFERENCE) { // In case of a subtraction, a check on volume is performed. if (valid_result) { - const double volume_after_subtraction = shape_volume(shape); + const double volume_after_subtraction = util::shape_volume(shape); if ( ALMOST_THE_SAME(first_operand_volume,volume_after_subtraction) ) Logger::Message(Logger::LOG_WARNING,"Subtraction yields unchanged volume:",l); } else { diff --git a/src/ifcgeom/IfcCShapeProfileDef.cpp b/src/ifcgeom/IfcCShapeProfileDef.cpp index cab56aae3e..3f1df702e4 100644 --- a/src/ifcgeom/IfcCShapeProfileDef.cpp +++ b/src/ifcgeom/IfcCShapeProfileDef.cpp @@ -19,6 +19,7 @@ #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/profile_helper.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -52,5 +53,5 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCShapeProfileDef* l, TopoDS_Sh double coords[24] = {-x,-y,x,-y,x,-y+d2,x-d1,-y+d2,x-d1,-y+d1,-x+d1,-y+d1,-x+d1,y-d1,x-d1,y-d1,x-d1,y-d2,x,y-d2,x,y,-x,y}; int fillets[8] = {0,1,4,5,6,7,10,11}; double radii[8] = {f2,f2,f1,f1,f1,f1,f2,f2}; - return profile_helper(12,coords,doFillet ? 8 : 0,fillets,radii,trsf2d,face); + return util::profile_helper(12,coords,doFillet ? 8 : 0,fillets,radii,trsf2d,face); } diff --git a/src/ifcgeom/IfcCircleProfileDef.cpp b/src/ifcgeom/IfcCircleProfileDef.cpp index d97ff05041..6bd54dd2ae 100644 --- a/src/ifcgeom/IfcCircleProfileDef.cpp +++ b/src/ifcgeom/IfcCircleProfileDef.cpp @@ -24,6 +24,7 @@ #include #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/wire_utils.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -52,7 +53,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCircleProfileDef* l, TopoDS_Sh w.Add(edge); TopoDS_Face f; - bool success = convert_wire_to_face(w, f); + bool success = util::convert_wire_to_face(w, f, {false, false, 0., 0.}); if (success) face = f; return success; } diff --git a/src/ifcgeom/IfcConnectedFaceSet.cpp b/src/ifcgeom/IfcConnectedFaceSet.cpp index b9a419c990..ef9556a325 100644 --- a/src/ifcgeom/IfcConnectedFaceSet.cpp +++ b/src/ifcgeom/IfcConnectedFaceSet.cpp @@ -17,9 +17,12 @@ * * ********************************************************************************/ +#include "../ifcgeom_schema_agnostic/base_utils.h" + +#include "../ifcgeom/IfcGeom.h" + #include #include -#include "../ifcgeom/IfcGeom.h" #include #define Kernel MAKE_TYPE_NAME(Kernel) @@ -38,7 +41,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcConnectedFaceSet* l, TopoDS_Sh points_, loops_, l->declaration().is(IfcSchema::IfcClosedShell::Class()) - )); + )); faceset_helper_ = helper_scope.get(); @@ -52,7 +55,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcConnectedFaceSet* l, TopoDS_Sh for (IfcSchema::IfcFace::list::it it = faces->begin(); it != faces->end(); ++it) { bool success = false; TopoDS_Face face; - + try { success = convert_face(*it, face); } catch (const std::exception& e) { @@ -78,7 +81,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcConnectedFaceSet* l, TopoDS_Sh if (face_it.Value().ShapeType() == TopAbs_FACE) { // This should really be the case. This is not asserted. const TopoDS_Face& triangle = TopoDS::Face(face_it.Value()); - if (face_area(triangle) > min_face_area) { + if (util::face_area(triangle) > min_face_area) { face_list.Append(triangle); } else { Logger::Message(Logger::LOG_WARNING, "Degenerate face:", (*it)); @@ -86,7 +89,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcConnectedFaceSet* l, TopoDS_Sh } } } else { - if (face_area(face) > min_face_area) { + if (util::face_area(face) > min_face_area) { face_list.Append(face); } else { Logger::Message(Logger::LOG_WARNING, "Degenerate face:", (*it)); @@ -98,11 +101,11 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcConnectedFaceSet* l, TopoDS_Sh return false; } - if (face_list.Extent() > getValue(GV_MAX_FACES_TO_ORIENT) || !create_solid_from_faces(face_list, shape)) { + if (face_list.Extent() > getValue(GV_MAX_FACES_TO_ORIENT) || !util::create_solid_from_faces(face_list, shape, getValue(GV_PRECISION))) { TopoDS_Compound compound; BRep_Builder builder; builder.MakeCompound(compound); - + TopTools_ListIteratorOfListOfShape face_iterator; for (face_iterator.Initialize(face_list); face_iterator.More(); face_iterator.Next()) { builder.Add(compound, face_iterator.Value()); diff --git a/src/ifcgeom/IfcCraneRailAShapeProfileDef.cpp b/src/ifcgeom/IfcCraneRailAShapeProfileDef.cpp index 95d2306616..f451fbd886 100644 --- a/src/ifcgeom/IfcCraneRailAShapeProfileDef.cpp +++ b/src/ifcgeom/IfcCraneRailAShapeProfileDef.cpp @@ -18,6 +18,7 @@ ********************************************************************************/ #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/profile_helper.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -60,6 +61,6 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcCraneRailAShapeProfileDef* l, +hw / 2., +oh / 2. }; - return profile_helper(14, coords, 0, 0, 0, trsf2d, face); + return util::profile_helper(14, coords, 0, 0, 0, trsf2d, face); } #endif diff --git a/src/ifcgeom/IfcEllipseProfileDef.cpp b/src/ifcgeom/IfcEllipseProfileDef.cpp index 77de9babc5..ffb1b4e920 100644 --- a/src/ifcgeom/IfcEllipseProfileDef.cpp +++ b/src/ifcgeom/IfcEllipseProfileDef.cpp @@ -24,6 +24,7 @@ #include #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/wire_utils.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -60,7 +61,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcEllipseProfileDef* l, TopoDS_S w.Add(edge); TopoDS_Face f; - bool success = convert_wire_to_face(w, f); + bool success = util::convert_wire_to_face(w, f, {false, false, 0., 0.}); if (success) face = f; return success; } diff --git a/src/ifcgeom/IfcExtrudedAreaSolidTapered.cpp b/src/ifcgeom/IfcExtrudedAreaSolidTapered.cpp index bcf0dc6d5e..4df42d7138 100644 --- a/src/ifcgeom/IfcExtrudedAreaSolidTapered.cpp +++ b/src/ifcgeom/IfcExtrudedAreaSolidTapered.cpp @@ -32,6 +32,7 @@ #include #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/base_utils.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -88,11 +89,11 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcExtrudedAreaSolidTapered* l, T TopoDS_Shape result = builder.Shape(); TopTools_ListOfShape li; - shape_to_face_list(result, li); + util::shape_to_face_list(result, li); li.Append(BRepBuilderAPI_MakeFace(w1).Face().Reversed()); li.Append(BRepBuilderAPI_MakeFace(w2).Face().Moved(end_profile)); - create_solid_from_faces(li, result, true); + util::create_solid_from_faces(li, result, true); // @todo ugly hack diff --git a/src/ifcgeom/IfcGeom.cpp b/src/ifcgeom/IfcGeom.cpp index 91ae38a34a..a489d3caf9 100644 --- a/src/ifcgeom/IfcGeom.cpp +++ b/src/ifcgeom/IfcGeom.cpp @@ -141,6 +141,7 @@ #include "../ifcgeom_schema_agnostic/boolean_utils.h" #include "../ifcgeom_schema_agnostic/wire_utils.h" #include "../ifcgeom_schema_agnostic/base_utils.h" +#include "../ifcgeom_schema_agnostic/layerset.h" #include #include @@ -227,217 +228,6 @@ void IfcGeom::Kernel::set_rotation(const std::array &p_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) { - TopExp_Explorer exp(s, TopAbs_FACE); - for (; exp.More(); exp.Next()) { - TopoDS_Face face = TopoDS::Face(exp.Current()); - li.Append(face); - } - return true; -} - -bool IfcGeom::Kernel::create_solid_from_compound(const TopoDS_Shape& compound, TopoDS_Shape& shape) { - TopTools_ListOfShape face_list; - shape_to_face_list(compound, face_list); - if (face_list.Extent() == 0) { - return false; - } - return create_solid_from_faces(face_list, shape); -} - -bool IfcGeom::Kernel::create_solid_from_faces(const TopTools_ListOfShape& face_list, TopoDS_Shape& shape, bool force_sewing) { - bool valid_shell = false; - - if (face_list.Extent() == 1) { - shape = face_list.First(); - // A bit dubious what to return here. - return true; - } else if (face_list.Extent() == 0) { - return false; - } - - TopTools_ListIteratorOfListOfShape face_iterator; - - bool has_shared_edges = false; - TopTools_MapOfShape edge_set; - - // In case there are wire interesections or failures in non-planar wire triangulations - // the idea is to let occt do an exhaustive search of edge partners. But we have not - // found a case where this actually improves boolean ops later on. - // if (!faceset_helper_ || !faceset_helper_->non_manifold()) { - - for (face_iterator.Initialize(face_list); !force_sewing && face_iterator.More(); face_iterator.Next()) { - // As soon as is detected one of the edges is shared, the assumption is made no - // additional sewing is necessary. - if (!has_shared_edges) { - TopExp_Explorer exp(face_iterator.Value(), TopAbs_EDGE); - for (; exp.More(); exp.Next()) { - if (edge_set.Contains(exp.Current())) { - has_shared_edges = true; - break; - } - edge_set.Add(exp.Current()); - } - } - } - - BRepOffsetAPI_Sewing sewing_builder; - sewing_builder.SetTolerance(getValue(GV_PRECISION)); - sewing_builder.SetMaxTolerance(getValue(GV_PRECISION)); - sewing_builder.SetMinTolerance(getValue(GV_PRECISION)); - - BRep_Builder builder; - TopoDS_Shell shell; - builder.MakeShell(shell); - - for (face_iterator.Initialize(face_list); face_iterator.More(); face_iterator.Next()) { - if (has_shared_edges) { - builder.Add(shell, face_iterator.Value()); - } else { - sewing_builder.Add(face_iterator.Value()); - } - } - - try { - if (has_shared_edges) { - ShapeFix_Shell fix; - fix.FixFaceOrientation(shell); - shape = fix.Shape(); - } else { - sewing_builder.Perform(); - shape = sewing_builder.SewedShape(); - } - - BRepCheck_Analyzer ana(shape); - valid_shell = ana.IsValid(); - - if (!valid_shell) { - ShapeFix_Shape sfs(shape); - sfs.Perform(); - shape = sfs.Shape(); - - BRepCheck_Analyzer reana(shape); - valid_shell = reana.IsValid(); - } - - valid_shell &= util::count(shape, TopAbs_SHELL) > 0; - } catch (const Standard_Failure& e) { - if (e.GetMessageString() && strlen(e.GetMessageString())) { - Logger::Error(e.GetMessageString()); - } else { - Logger::Error("Unknown error sewing shell"); - } - } catch (...) { - Logger::Error("Unknown error sewing shell"); - } - - if (valid_shell) { - - TopoDS_Shape complete_shape; - TopExp_Explorer exp(shape, TopAbs_SHELL); - - for (; exp.More(); exp.Next()) { - TopoDS_Shape result_shape = exp.Current(); - - try { - ShapeFix_Solid solid; - solid.SetMaxTolerance(getValue(GV_PRECISION)); - TopoDS_Solid solid_shape = solid.SolidFromShell(TopoDS::Shell(exp.Current())); - // @todo: BRepClass3d_SolidClassifier::PerformInfinitePoint() is done by SolidFromShell - // and this is done again, to be able to catch errors during this process. - // This is double work that should be avoided. - if (!solid_shape.IsNull()) { - try { - BRepClass3d_SolidClassifier classifier(solid_shape); - result_shape = solid_shape; - classifier.PerformInfinitePoint(getValue(GV_PRECISION)); - if (classifier.State() == TopAbs_IN) { - shape.Reverse(); - } - } catch (const Standard_Failure& e) { - if (e.GetMessageString() && strlen(e.GetMessageString())) { - Logger::Error(e.GetMessageString()); - } else { - Logger::Error("Unknown error classifying solid"); - } - } catch (...) { - Logger::Error("Unknown error classifying solid"); - } - } - } catch (const Standard_Failure& e) { - if (e.GetMessageString() && strlen(e.GetMessageString())) { - Logger::Error(e.GetMessageString()); - } else { - Logger::Error("Unknown error creating solid"); - } - } catch (...) { - Logger::Error("Unknown error creating solid"); - } - - if (complete_shape.IsNull()) { - complete_shape = result_shape; - } else { - BRep_Builder B; - if (complete_shape.ShapeType() != TopAbs_COMPOUND) { - TopoDS_Compound C; - B.MakeCompound(C); - B.Add(C, complete_shape); - complete_shape = C; - Logger::Warning("Multiple components in IfcConnectedFaceSet"); - } - B.Add(complete_shape, result_shape); - } - } - - TopExp_Explorer loose_faces(shape, TopAbs_FACE, TopAbs_SHELL); - - for (; loose_faces.More(); loose_faces.Next()) { - BRep_Builder B; - if (complete_shape.ShapeType() != TopAbs_COMPOUND) { - TopoDS_Compound C; - B.MakeCompound(C); - B.Add(C, complete_shape); - complete_shape = C; - Logger::Warning("Loose faces in IfcConnectedFaceSet"); - } - B.Add(complete_shape, loose_faces.Current()); - } - - shape = complete_shape; - - } else { - Logger::Error("Failed to sew faceset"); - } - - return valid_shell; -} - -bool IfcGeom::Kernel::is_compound(const TopoDS_Shape& shape) { - bool has_solids = TopExp_Explorer(shape,TopAbs_SOLID).More() != 0; - bool has_shells = TopExp_Explorer(shape,TopAbs_SHELL).More() != 0; - bool has_compounds = TopExp_Explorer(shape,TopAbs_COMPOUND).More() != 0; - bool has_faces = TopExp_Explorer(shape,TopAbs_FACE).More() != 0; - return has_compounds && has_faces && !has_solids && !has_shells; -} - -const TopoDS_Shape& IfcGeom::Kernel::ensure_fit_for_subtraction(const TopoDS_Shape& shape, TopoDS_Shape& solid) { - const bool is_comp = is_compound(shape); - if (!is_comp) { - return solid = shape; - } - - if (!create_solid_from_compound(shape, solid)) { - return solid = shape; - } - - // If the SEW_SHELLS option had been set this precision had been applied - // at the end of the generic convert_shape() call. - const double precision = getValue(GV_PRECISION); - apply_tolerance(solid, precision); - - return solid; -} - namespace { struct opening_sorter { bool operator()(const std::pair& a, const std::pair& b) const { @@ -510,11 +300,11 @@ bool IfcGeom::Kernel::convert_openings(const IfcSchema::IfcProduct* entity, cons for (unsigned int i = 0; i < opening_shapes.size(); ++i) { TopoDS_Shape opening_shape_solid; - const TopoDS_Shape& opening_shape_unlocated = ensure_fit_for_subtraction(opening_shapes[i].Shape(), opening_shape_solid); + const TopoDS_Shape& opening_shape_unlocated = util::ensure_fit_for_subtraction(opening_shapes[i].Shape(), opening_shape_solid, getValue(GV_PRECISION)); gp_GTrsf gtrsf = opening_shapes[i].Placement(); gtrsf.PreMultiply(opening_trsf); - TopoDS_Shape opening_shape = apply_transformation(opening_shape_unlocated, gtrsf); + TopoDS_Shape opening_shape = util::apply_transformation(opening_shape_unlocated, gtrsf); opening_vector.push_back(std::make_pair(util::min_edge_length(opening_shape), opening_shape)); } @@ -562,13 +352,13 @@ bool IfcGeom::Kernel::convert_openings(const IfcSchema::IfcProduct* entity, cons if (as_shell) { entity_shape_unlocated = entity_part; } else { - entity_shape_unlocated = ensure_fit_for_subtraction(entity_part, entity_shape_solid); + entity_shape_unlocated = util::ensure_fit_for_subtraction(entity_part, entity_shape_solid, getValue(GV_PRECISION)); } 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 entity_shape = util::apply_transformation(entity_shape_unlocated, entity_shape_gtrsf); TopoDS_Shape result = entity_shape; @@ -637,310 +427,6 @@ bool IfcGeom::Kernel::convert_openings(const IfcSchema::IfcProduct* entity, cons return true; } -double IfcGeom::Kernel::get_wire_intersection_tolerance(const TopoDS_Wire& wire) const { - return getValue(GV_NO_WIRE_INTERSECTION_TOLERANCE) > 0. - ? 0 - : faceset_helper_ - // eps is added to both ends of the parametric domain, so 3. is chosen to be on the safe side here. - ? (faceset_helper_->epsilon() / 3.) - // @todo re-evaluate 2. here for the reasons above: - : (std::min)(util::min_edge_length(wire) / 2., getValue(GV_PRECISION) * 10.); -} - -bool IfcGeom::Kernel::convert_wire_to_face(const TopoDS_Wire& w, TopoDS_Face& face) { - TopoDS_Wire wire = w; - - TopTools_ListOfShape results; - - if (getValue(GV_NO_WIRE_INTERSECTION_CHECK) < 0. && util::wire_intersections(wire, results, get_wire_intersection_tolerance(wire), getValue(GV_PRECISION))) { - Logger::Warning("Self-intersections with " + boost::lexical_cast(results.Extent()) + " cycles detected"); - util::select_largest(results, wire); - } - - bool is_2d = true; - TopExp_Explorer exp(wire, TopAbs_EDGE); - for (; exp.More(); exp.Next()) { - double a, b; - Handle(Geom_Curve) crv = BRep_Tool::Curve(TopoDS::Edge(exp.Current()), a, b); - if (crv->DynamicType() != STANDARD_TYPE(Geom_Line)) { - is_2d = false; - break; - } - Handle(Geom_Line) line = Handle(Geom_Line)::DownCast(crv); - if (line->Lin().Direction().Z() > ALMOST_ZERO) { - is_2d = false; - break; - } - } - - if (!is_2d) { - // For 2d wires (e.g. profiles) a higher tolerance for plane fitting is never required. - ShapeFix_ShapeTolerance FTol; - FTol.SetTolerance(wire, getValue(GV_PRECISION), TopAbs_WIRE); - } - - BRepBuilderAPI_MakeFace mf(wire, false); - BRepBuilderAPI_FaceError er = mf.Error(); - - if (er != BRepBuilderAPI_FaceDone) { - Logger::Error("Failed to create face."); - return false; - } - face = mf.Face(); - - return true; -} - -bool IfcGeom::Kernel::convert_wire_to_faces(const TopoDS_Wire& w, TopoDS_Compound& faces) { - bool is_2d = true; - TopExp_Explorer exp(w, TopAbs_EDGE); - for (; exp.More(); exp.Next()) { - double a, b; - Handle(Geom_Curve) crv = BRep_Tool::Curve(TopoDS::Edge(exp.Current()), a, b); - if (crv->DynamicType() != STANDARD_TYPE(Geom_Line)) { - is_2d = false; - break; - } - Handle(Geom_Line) line = Handle(Geom_Line)::DownCast(crv); - if (line->Lin().Direction().Z() > ALMOST_ZERO) { - is_2d = false; - break; - } - } - - TopTools_ListOfShape results; - if (getValue(GV_NO_WIRE_INTERSECTION_CHECK) < 0. && util::wire_intersections(w, results, get_wire_intersection_tolerance(w), getValue(GV_PRECISION))) { - Logger::Warning("Self-intersections with " + boost::lexical_cast(results.Extent()) + " cycles detected"); - } else { - results.Clear(); - results.Append(w); - } - - TopoDS_Compound C; - BRep_Builder B; - B.MakeCompound(faces); - - std::list> face_list; - double max_area = 0.; - - TopTools_ListIteratorOfListOfShape it(results); - for (; it.More(); it.Next()) { - const TopoDS_Wire& wire = TopoDS::Wire(it.Value()); - if (!is_2d) { - // For 2d wires (e.g. profiles) a higher tolerance for plane fitting is never required. - ShapeFix_ShapeTolerance FTol; - FTol.SetTolerance(wire, getValue(GV_PRECISION), TopAbs_WIRE); - } - - BRepBuilderAPI_MakeFace mf(wire, false); - BRepBuilderAPI_FaceError er = mf.Error(); - - if (er != BRepBuilderAPI_FaceDone) { - Logger::Error("Failed to create face."); - continue; - } - - TopoDS_Face face = mf.Face(); - const double m = face_area(face); - - face_list.push_back({ m, face }); - if (m > max_area) { - max_area = m; - } - } - - for (auto& p : face_list) { - if (p.first >= max_area / 10.) { - B.Add(faces, p.second); - } else { - Logger::Warning("Ignoring self-intersection loop with area " + boost::lexical_cast(p.first)); - } - } - - return true; -} - - -void IfcGeom::Kernel::assert_closed_wire(TopoDS_Wire& wire) { - if (wire.Closed() == 0) { - TopoDS_Vertex v0, v1; - TopExp::Vertices(wire, v0, v1); - - gp_Pnt p1 = BRep_Tool::Pnt(v0); - gp_Pnt p2 = BRep_Tool::Pnt(v1); - - if (p1.Distance(p2) > getValue(GV_PRECISION)) { - - BRepBuilderAPI_MakeWire mw; - mw.Add(wire); - mw.Add(BRepBuilderAPI_MakeEdge(v0, v1).Edge()); - wire = mw.Wire(); - - } - - Logger::Warning("Wire not closed:"); - } -} - -bool IfcGeom::Kernel::convert_curve_to_wire(const Handle(Geom_Curve)& curve, TopoDS_Wire& wire) { - try { - wire = BRepBuilderAPI_MakeWire(BRepBuilderAPI_MakeEdge(curve)); - return true; - } catch (const Standard_Failure& e) { - if (e.GetMessageString() && strlen(e.GetMessageString())) { - Logger::Error(e.GetMessageString()); - } else { - Logger::Error("Unknown error converting curve to wire"); - } - } catch (...) { - Logger::Error("Unknown error converting curve to wire"); - } - return false; -} - -bool IfcGeom::Kernel::profile_helper(int numVerts, double* verts, int numFillets, int* filletIndices, double* filletRadii, gp_Trsf2d trsf, TopoDS_Shape& face_shape) { - TopoDS_Vertex* vertices = new TopoDS_Vertex[numVerts]; - - for ( int i = 0; i < numVerts; i ++ ) { - gp_XY xy (verts[2*i],verts[2*i+1]); - trsf.Transforms(xy); - vertices[i] = BRepBuilderAPI_MakeVertex(gp_Pnt(xy.X(),xy.Y(),0.0f)); - } - - BRepBuilderAPI_MakeWire w; - for ( int i = 0; i < numVerts; i ++ ) - w.Add(BRepBuilderAPI_MakeEdge(vertices[i],vertices[(i+1)%numVerts])); - - TopoDS_Face face; - convert_wire_to_face(w.Wire(),face); - - if ( numFillets && *std::max_element(filletRadii, filletRadii + numFillets) > ALMOST_ZERO ) { - BRepFilletAPI_MakeFillet2d fillet (face); - for ( int i = 0; i < numFillets; i ++ ) { - const double radius = filletRadii[i]; - if ( radius <= ALMOST_ZERO ) continue; - fillet.AddFillet(vertices[filletIndices[i]],radius); - } - fillet.Build(); - if (fillet.IsDone()) { - face = TopoDS::Face(fillet.Shape()); - } else { - Logger::Error("Failed to process profile fillets"); - } - } - - face_shape = face; - - delete[] vertices; - return true; -} -double IfcGeom::Kernel::shape_volume(const TopoDS_Shape& s) { - GProp_GProps prop; - BRepGProp::VolumeProperties(s, prop); - return prop.Mass(); -} -double IfcGeom::Kernel::face_area(const TopoDS_Face& f) { - GProp_GProps prop; - BRepGProp::SurfaceProperties(f,prop); - return prop.Mass(); -} -bool IfcGeom::Kernel::is_convex(const TopoDS_Wire& wire) { - for ( TopExp_Explorer exp1(wire,TopAbs_VERTEX); exp1.More(); exp1.Next() ) { - TopoDS_Vertex V1 = TopoDS::Vertex(exp1.Current()); - gp_Pnt P1 = BRep_Tool::Pnt(V1); - // Store the neighboring points - std::vector neighbors; - for ( TopExp_Explorer exp3(wire,TopAbs_EDGE); exp3.More(); exp3.Next() ) { - TopoDS_Edge edge = TopoDS::Edge(exp3.Current()); - std::vector edge_points; - for ( TopExp_Explorer exp2(edge,TopAbs_VERTEX); exp2.More(); exp2.Next() ) { - TopoDS_Vertex V2 = TopoDS::Vertex(exp2.Current()); - gp_Pnt P2 = BRep_Tool::Pnt(V2); - edge_points.push_back(P2); - } - if ( edge_points.size() != 2 ) continue; - if ( edge_points[0].IsEqual(P1,getValue(GV_POINT_EQUALITY_TOLERANCE))) neighbors.push_back(edge_points[1]); - else if ( edge_points[1].IsEqual(P1, getValue(GV_POINT_EQUALITY_TOLERANCE))) neighbors.push_back(edge_points[0]); - } - // There should be two of these - if ( neighbors.size() != 2 ) return false; - // Now find the non neighboring points - std::vector non_neighbors; - for ( TopExp_Explorer exp2(wire,TopAbs_VERTEX); exp2.More(); exp2.Next() ) { - TopoDS_Vertex V2 = TopoDS::Vertex(exp2.Current()); - gp_Pnt P2 = BRep_Tool::Pnt(V2); - if ( P1.IsEqual(P2,getValue(GV_POINT_EQUALITY_TOLERANCE)) ) continue; - bool found = false; - for( std::vector::const_iterator it = neighbors.begin(); it != neighbors.end(); ++ it ) { - if ( (*it).IsEqual(P2,getValue(GV_POINT_EQUALITY_TOLERANCE)) ) { found = true; break; } - } - if ( ! found ) non_neighbors.push_back(P2); - } - // Calculate the angle between the two edges of the vertex - gp_Dir dir1(neighbors[0].XYZ() - P1.XYZ()); - gp_Dir dir2(neighbors[1].XYZ() - P1.XYZ()); - const double angle = acos(dir1.Dot(dir2)) + 0.0001; - // Now for the non-neighbors see whether a greater angle can be found with one of the edges - for ( std::vector::const_iterator it = non_neighbors.begin(); it != non_neighbors.end(); ++ it ) { - gp_Dir dir3((*it).XYZ() - P1.XYZ()); - const double angle2 = acos(dir3.Dot(dir1)); - const double angle3 = acos(dir3.Dot(dir2)); - if ( angle2 > angle || angle3 > angle ) return false; - } - } - return true; -} -TopoDS_Shape IfcGeom::Kernel::halfspace_from_plane(const gp_Pln& pln,const gp_Pnt& cent) { - TopoDS_Face face = BRepBuilderAPI_MakeFace(pln).Face(); - return BRepPrimAPI_MakeHalfSpace(face,cent).Solid(); -} -gp_Pln IfcGeom::Kernel::plane_from_face(const TopoDS_Face& face) { - BRepGProp_Face prop(face); - Standard_Real u1,u2,v1,v2; - prop.Bounds(u1,u2,v1,v2); - Standard_Real u = (u1+u2)/2.0; - Standard_Real v = (v1+v2)/2.0; - gp_Pnt p; - gp_Vec n; - prop.Normal(u,v,p,n); - return gp_Pln(p,n); -} -gp_Pnt IfcGeom::Kernel::point_above_plane(const gp_Pln& pln, bool agree) { - if ( agree ) { - return pln.Location().Translated(pln.Axis().Direction()); - } else { - return pln.Location().Translated(-pln.Axis().Direction()); - } -} - -void IfcGeom::Kernel::apply_tolerance(TopoDS_Shape& s, double t) { - /* - // This does not result in actionable error messages and has been disabled. - ShapeAnalysis_ShapeTolerance toler; - if (Logger::LOG_WARNING >= Logger::Verbosity()) { - if (toler.Tolerance(s, 0) > t * 10.) { - Handle_TopTools_HSequenceOfShape shapes = toler.OverTolerance(s, t * 10.); - for (int i = 1; i <= shapes->Length(); ++i) { - const TopoDS_Shape& sub = shapes->Value(i); - std::stringstream ss; - TopAbs::Print(sub.ShapeType(), ss); - Logger::Warning("Tolerance of " + boost::lexical_cast(toler.Tolerance(sub, 0)) + " on " + ss.str()); - } - } - } - */ - -#if OCC_VERSION_HEX < 0x60900 - // This tolerance hack is not required as the boolean ops use a fuzziness value - - ShapeFix_ShapeTolerance tol; - tol.LimitTolerance(s, t); -#else - (void)s; - (void)t; -#endif -} - void IfcGeom::Kernel::setValue(GeomValue var, double value) { switch (var) { case GV_DEFLECTION_TOLERANCE: @@ -1025,271 +511,6 @@ double IfcGeom::Kernel::getValue(GeomValue var) const { throw std::runtime_error("Invalid setting"); } -namespace { - - // Returns the vertex part of an TopoDS_Edge edge that is not TopoDS_Vertex vertex - TopoDS_Vertex find_other(const TopoDS_Edge& edge, const TopoDS_Vertex& vertex) { - TopExp_Explorer exp(edge, TopAbs_VERTEX); - while (exp.More()) { - if (!exp.Current().IsSame(vertex)) { - return TopoDS::Vertex(exp.Current()); - } - exp.Next(); - } - return TopoDS_Vertex(); - } - - TopoDS_Edge find_next(const TopTools_IndexedMapOfShape& edge_set, const TopTools_IndexedDataMapOfShapeListOfShape& vertex_to_edges, const TopoDS_Vertex& current, const TopoDS_Edge& previous_edge) { - const TopTools_ListOfShape& edges = vertex_to_edges.FindFromKey(current); - TopTools_ListIteratorOfListOfShape eit; - for (eit.Initialize(edges); eit.More(); eit.Next()) { - const TopoDS_Edge& edge = TopoDS::Edge(eit.Value()); - if (edge.IsSame(previous_edge)) continue; - if (edge_set.Contains(edge)) { - return edge; - } - } - return TopoDS_Edge(); - } - -} - -bool IfcGeom::Kernel::fill_nonmanifold_wires_with_planar_faces(TopoDS_Shape& shape) { - BRepOffsetAPI_Sewing sew; - sew.Add(shape); - - TopTools_IndexedDataMapOfShapeListOfShape edge_to_faces; - TopTools_IndexedDataMapOfShapeListOfShape vertex_to_edges; - std::set visited; - TopTools_IndexedMapOfShape edge_set; - - TopExp::MapShapesAndAncestors (shape, TopAbs_EDGE, TopAbs_FACE, edge_to_faces); - - const int num_edges = edge_to_faces.Extent(); - for (int i = 1; i <= num_edges; ++i) { - const TopTools_ListOfShape& faces = edge_to_faces.FindFromIndex(i); - const int count = faces.Extent(); - // Find only the non-manifold edges: Edges that are only part of a - // single face and therefore part of the wire(s) we want to fill. - if (count == 1) { - const TopoDS_Shape& edge = edge_to_faces.FindKey(i); - TopExp::MapShapesAndAncestors (edge, TopAbs_VERTEX, TopAbs_EDGE, vertex_to_edges); - edge_set.Add(edge); - } - } - - const int num_verts = vertex_to_edges.Extent(); - TopoDS_Vertex first, current; - TopoDS_Edge previous_edge; - - // Now loop over all the vertices that are part of the wire(s) to be filled - for (int i = 1; i <= num_verts; ++i) { - first = current = TopoDS::Vertex(vertex_to_edges.FindKey(i)); - // We keep track of the vertices we already used - if (visited.find(vertex_to_edges.FindIndex(current)) != visited.end()) { - continue; - } - // Given these vertices, try to find closed loops and create new - // wires out of them. - BRepBuilderAPI_MakeWire w; - for (;;) { - visited.insert(vertex_to_edges.FindIndex(current)); - // Find the edge that the current vertex is part of and points - // away from the previous vertex (null for the first vertex). - TopoDS_Edge edge = find_next(edge_set, vertex_to_edges, current, previous_edge); - if (edge.IsNull()) { - return false; - } - TopoDS_Vertex other = find_other(edge, current); - if (other.IsNull()) { - // Dealing with a conical edge probably, for some reason - // this works better than adding the edge directly. - double u1, u2; - Handle(Geom_Curve) crv = BRep_Tool::Curve(edge, u1, u2); - w.Add(BRepBuilderAPI_MakeEdge(crv, u1, u2)); - break; - } else { - w.Add(edge); - } - // See if the starting point of this loop has been reached. Note that - // additional wires after this one potentially will be created. - if (other.IsSame(first)) { - break; - } - previous_edge = edge; - current = other; - } - sew.Add(BRepBuilderAPI_MakeFace(w)); - previous_edge.Nullify(); - } - - sew.Perform(); - shape = sew.SewedShape(); - - try { - ShapeFix_Solid solid; - solid.LimitTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE)); - shape = solid.SolidFromShell(TopoDS::Shell(shape)); - } catch (const Standard_Failure& e) { - if (e.GetMessageString() && strlen(e.GetMessageString())) { - Logger::Error(e.GetMessageString()); - } else { - Logger::Error("Unknown error creating solid"); - } - } catch (...) { - Logger::Error("Unknown error creating solid"); - } - - return true; -} - -bool IfcGeom::Kernel::flatten_shape_list(const IfcGeom::IfcRepresentationShapeItems& shapes, TopoDS_Shape& result, bool fuse) { - TopoDS_Compound compound; - BRep_Builder builder; - builder.MakeCompound(compound); - - result = TopoDS_Shape(); - - for ( IfcGeom::IfcRepresentationShapeItems::const_iterator it = shapes.begin(); it != shapes.end(); ++ it ) { - TopoDS_Shape merged; - const TopoDS_Shape& s = it->Shape(); - if (fuse) { - ensure_fit_for_subtraction(s, merged); - } else { - merged = s; - } - const gp_GTrsf& trsf = it->Placement(); - const TopoDS_Shape moved_shape = apply_transformation(merged, trsf); - - if (shapes.size() == 1) { - result = moved_shape; - const double precision = getValue(GV_PRECISION); - apply_tolerance(result, precision); - return true; - } - - if (fuse) { - if (result.IsNull()) { - result = moved_shape; - } else { - BRepAlgoAPI_Fuse brep_fuse(result, moved_shape); - if ( brep_fuse.IsDone() ) { - TopoDS_Shape fused = brep_fuse; - - ShapeFix_Shape fix(result); - fix.Perform(); - result = fix.Shape(); - - bool is_valid = BRepCheck_Analyzer(result).IsValid() != 0; - if ( is_valid ) { - result = fused; - } - } - } - } else { - builder.Add(compound,moved_shape); - } - } - - if (!fuse) { - result = compound; - } - - const bool success = !result.IsNull(); - if (success) { - const double precision = getValue(GV_PRECISION); - apply_tolerance(result, precision); - } - - return success; -} - -void IfcGeom::Kernel::remove_duplicate_points_from_loop(TColgp_SequenceOfPnt& polygon, bool closed, double tol) { - if (tol <= 0.) tol = getValue(GV_PRECISION); - tol *= tol; - - for (;;) { - bool removed = false; - int n = polygon.Length() - (closed ? 0 : 1); - for (int i = 1; i <= n; ++i) { - // wrap around to the first point in case of a closed loop - int j = (i % polygon.Length()) + 1; - double dist = polygon.Value(i).SquareDistance(polygon.Value(j)); - if (dist < tol) { - // do not remove the first or last point to - // maintain connectivity with other wires - if ((closed && j == 1) || (!closed && j == n)) polygon.Remove(i); - else polygon.Remove(j); - removed = true; - break; - } - } - if (!removed) break; - } -} - -void IfcGeom::Kernel::remove_collinear_points_from_loop(TColgp_SequenceOfPnt& polygon, bool closed, double tol) { - if (tol <= 0.) tol = getValue(GV_PRECISION); - const int start = closed ? 1 : 2; - const int end = polygon.Length() - (closed ? 0 : 1); - std::vector to_remove(polygon.Length(), false); - for (int i = start; i <= end; ++i) { - const gp_Pnt& a = polygon.Value(((i - 2 + polygon.Length()) % polygon.Length()) + 1); - const gp_Pnt& b = polygon.Value(i); - const gp_Pnt& c = polygon.Value((i % polygon.Length()) + 1); - const gp_Vec d1 = c.XYZ() - a.XYZ(); - const gp_Vec d2 = b.XYZ() - a.XYZ(); - const double dt = d2.Dot(d1) / d1.Dot(d1); - const gp_Vec d3 = d1.Scaled(dt); - const gp_Pnt b2 = a.XYZ() + d3.XYZ(); - if (b.Distance(b2) < tol) { - to_remove[i-1] = true; - } - } - for (int i = (int) to_remove.size() - 1; i >= 0; --i) { - if (to_remove[i]) { - polygon.Remove(i+1); - } - } -} - -bool IfcGeom::Kernel::wire_to_sequence_of_point(const TopoDS_Wire& w, TColgp_SequenceOfPnt& p) { - TopExp_Explorer exp(w, TopAbs_EDGE); - for (; exp.More(); exp.Next()) { - double a, b; - Handle_Geom_Curve crv = BRep_Tool::Curve(TopoDS::Edge(exp.Current()), a, b); - if (crv->DynamicType() != STANDARD_TYPE(Geom_Line)) { - return false; - } - } - - exp.ReInit(); - - int i = 0; - for (; exp.More(); exp.Next(), ++i) { - TopoDS_Vertex v1, v2; - TopExp::Vertices(TopoDS::Edge(exp.Current()), v1, v2, true); - if (exp.More()) { - if (i == 0) { - p.Append(BRep_Tool::Pnt(v1)); - } - p.Append(BRep_Tool::Pnt(v2)); - } - } - - return true; -} - -void IfcGeom::Kernel::sequence_of_point_to_wire(const TColgp_SequenceOfPnt& p, TopoDS_Wire& w, bool close) { - BRepBuilderAPI_MakePolygon builder; - for (int i = 1; i <= p.Length(); ++i) { - builder.Add(p.Value(i)); - } - if (close) { - builder.Close(); - } - w = builder.Wire(); -} IfcSchema::IfcRelVoidsElement::list::ptr IfcGeom::Kernel::find_openings(IfcSchema::IfcProduct* product) { std::vector rs; @@ -1367,7 +588,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 (util::flatten_shape_list(shapes, merge, false, getValue(GV_PRECISION))) { if (util::count(merge, TopAbs_FACE) > 0) { std::vector thickness; std::vector layers; @@ -1389,12 +610,12 @@ IfcGeom::BRepElement* IfcGeom::Kernel::create_brep_for_representation_and_produc // If there's only a single layer there is no need to manipulate geometries. bool success = true; if (product->as() && fold_layers(product->as(), shapes, layers, thickness, folded_layers)) { - if (apply_folded_layerset(shapes, folded_layers, styles, shapes2)) { + if (util::apply_folded_layerset(shapes, folded_layers, styles, shapes2, getValue(GV_PRECISION))) { std::swap(shapes, shapes2); success = true; } } else { - if (apply_layerset(shapes, layers, styles, shapes2)) { + if (util::apply_layerset(shapes, layers, styles, shapes2, getValue(GV_PRECISION))) { std::swap(shapes, shapes2); success = true; } @@ -1850,7 +1071,7 @@ bool IfcGeom::Kernel::convert_layerset(const IfcSchema::IfcProduct* product, std } TopoDS_Shape axis_shape; - flatten_shape_list(axis_items, axis_shape, false); + util::flatten_shape_list(axis_items, axis_shape, false, getValue(GV_PRECISION)); TopExp_Explorer exp(axis_shape, TopAbs_EDGE); TopoDS_Edge axis_edge; @@ -1956,80 +1177,6 @@ bool IfcGeom::Kernel::convert_layerset(const IfcSchema::IfcProduct* product, std return true; } -const Handle_Geom_Curve IfcGeom::Kernel::intersect(const Handle_Geom_Surface& a, const Handle_Geom_Surface& b) { - GeomAPI_IntSS x(a, b, 1.e-7); - if (x.IsDone() && x.NbLines() == 1) { - return x.Line(1); - } else { - return Handle_Geom_Curve(); - } -} - -const Handle_Geom_Curve IfcGeom::Kernel::intersect(const Handle_Geom_Surface& a, const TopoDS_Face& b) { - return intersect(a, BRep_Tool::Surface(b)); -} - -const Handle_Geom_Curve IfcGeom::Kernel::intersect(const TopoDS_Face& a, const Handle_Geom_Surface& b) { - return intersect(BRep_Tool::Surface(a), b); -} - -bool IfcGeom::Kernel::intersect(const Handle_Geom_Curve& a, const Handle_Geom_Surface& b, gp_Pnt& p) { - GeomAPI_IntCS x(a, b); - if (x.IsDone() && x.NbPoints() == 1) { - p = x.Point(1); - return true; - } else { - return false; - } -} - -bool IfcGeom::Kernel::intersect(const Handle_Geom_Curve& a, const TopoDS_Face& b, gp_Pnt &c) { - return intersect(a, BRep_Tool::Surface(b), c); -} - -bool IfcGeom::Kernel::intersect(const Handle_Geom_Curve& a, const TopoDS_Shape& b, std::vector& out) { - TopExp_Explorer exp(b, TopAbs_FACE); - gp_Pnt p; - for (; exp.More(); exp.Next()) { - if (intersect(a, TopoDS::Face(exp.Current()), p)) { - out.push_back(p); - } - } - return !out.empty(); -} - -bool IfcGeom::Kernel::intersect(const Handle_Geom_Surface& a, const TopoDS_Shape& b, std::vector< std::pair >& out) { - TopExp_Explorer exp(b, TopAbs_FACE); - for (; exp.More(); exp.Next()) { - const TopoDS_Face& f = TopoDS::Face(exp.Current()); - const Handle_Geom_Surface& s = BRep_Tool::Surface(f); - Handle_Geom_Curve crv = intersect(a, s); - if (!crv.IsNull()) { - out.push_back(std::make_pair(s, crv)); - } - } - return !out.empty(); -} - -bool IfcGeom::Kernel::closest(const gp_Pnt& a, const std::vector& b, gp_Pnt& c) { - double minimal_distance = std::numeric_limits::infinity(); - for (std::vector::const_iterator it = b.begin(); it != b.end(); ++it) { - const double d = a.Distance(*it); - if (d < minimal_distance) { - minimal_distance = d; - c = *it; - } - } - return minimal_distance != std::numeric_limits::infinity(); -} - -bool IfcGeom::Kernel::project(const Handle_Geom_Curve& crv, const gp_Pnt& pt, gp_Pnt& p, double& u, double& d) { - ShapeAnalysis_Curve sac; - sac.Project(crv, pt, 1e-3, p, u, false); - d = pt.Distance(p); - return true; -} - bool IfcGeom::Kernel::find_wall_end_points(const IfcSchema::IfcWall* wall, gp_Pnt& start, gp_Pnt& end) { IfcSchema::IfcRepresentation* axis_representation = find_representation(wall, "Axis"); if (!axis_representation) { @@ -2244,7 +1391,7 @@ bool IfcGeom::Kernel::fold_layers(const IfcSchema::IfcWall* wall, const IfcRepre } TopoDS_Shape axis_shape; - flatten_shape_list(axis_items, axis_shape, false); + util::flatten_shape_list(axis_items, axis_shape, false, getValue(GV_PRECISION)); // local and other are IfcLocalPlacements and therefore have a unit // scale factor that can be applied by means of TopoDS_Shape::Move() @@ -2252,7 +1399,7 @@ bool IfcGeom::Kernel::fold_layers(const IfcSchema::IfcWall* wall, const IfcRepre axis_shape.Move(local); TopoDS_Shape body_shape; - flatten_shape_list(items, body_shape, false); + util::flatten_shape_list(items, body_shape, false, getValue(GV_PRECISION)); // Create a single paremetric range over a single curve // that represents the entire 1d domain of the other wall @@ -2285,7 +1432,7 @@ bool IfcGeom::Kernel::fold_layers(const IfcSchema::IfcWall* wall, const IfcRepre gp_Pnt p = BRep_Tool::Pnt(TopoDS::Vertex(exp2.Current())); gp_Pnt pp; double u, d; - if (project(other_axis_curve, p, pp, u, d)) { + if (util::project(other_axis_curve, p, pp, u, d)) { if (u < axis_u1) axis_u1 = u; if (u > axis_u2) axis_u2 = u; } @@ -2355,7 +1502,7 @@ bool IfcGeom::Kernel::fold_layers(const IfcSchema::IfcWall* wall, const IfcRepre // vertical edges at wall end point face. curves_on_surfaces_t layer_ends; - intersect(surface, body_shape, layer_ends); + util::intersect(surface, body_shape, layer_ends); Handle_Geom_Curve layer_body_intersection; Handle_Geom_Surface body_surface; @@ -2370,7 +1517,7 @@ bool IfcGeom::Kernel::fold_layers(const IfcSchema::IfcWall* wall, const IfcRepre continue; } // Find vertical wall end point edge closest to end point associated with semantic connection - if (project(kt->second, own_end_point, p, u, d)) { + if (util::project(kt->second, own_end_point, p, u, d)) { // In addition to closest, there is a length threshold based on thickness. // @todo ideally, first, the point closest to end-point is selected, and // after that the parallel check is performed. But threshold probably @@ -2439,344 +1586,6 @@ bool IfcGeom::Kernel::fold_layers(const IfcSchema::IfcWall* wall, const IfcRepre return folds_made; } -namespace { - - void subshapes(const TopoDS_Shape& in, std::list& out) { - TopoDS_Iterator sit(in); - for (; sit.More(); sit.Next()) { - out.push_back(sit.Value()); - } - } - -#if OCC_VERSION_HEX >= 0x70200 - bool split(IfcGeom::Kernel&, const TopoDS_Shape& input, const TopTools_ListOfShape& operands, double eps, std::vector& slices) { - if (operands.Extent() < 2) { - // Needs to have at least two cutting surfaces for the ordering based on surface containment to work. - return false; - } - - BRepAlgoAPI_Splitter split; - TopTools_ListOfShape input_list; - input_list.Append(input); - split.SetArguments(input_list); - split.SetTools(operands); - split.SetNonDestructive(true); - split.SetFuzzyValue(eps); - split.Build(); - - if (!split.IsDone()) { - return false; - } else { - - std::map surfaces; - - // NB 1, since first surface has been excluded - int i = 1; - for (TopTools_ListIteratorOfListOfShape it(operands); it.More(); it.Next(), ++i) { - TopExp_Explorer exp(it.Value(), TopAbs_FACE); - for (; exp.More(); exp.Next()) { - surfaces.insert(std::make_pair(BRep_Tool::Surface(TopoDS::Face(exp.Current())).get(), i)); - } - } - - auto result_shape = split.Shape(); - std::list subs; - subshapes(result_shape, subs); - - // Sometimes there is more nesting of compounds, so when we find a single compound we again try to explode it into a list. - if (subs.size() == 1 && (subs.front().ShapeType() == TopAbs_COMPSOLID || subs.front().ShapeType() == TopAbs_COMPOUND)) { - auto s = subs.front(); - subs.clear(); - subshapes(s, subs); - } - - // Initialize storage - slices.resize(subs.size()); - - for (auto& s : subs) { - - // Iterate over the faces of solid to find correspondence to original - // splitting surfaces. For the outmost slices, there will be a single - // corresponding surface, because the outmost surfaces that align with - // the body geometry have not been added as operands. For intermediate - // slices, two surface indices should be find that should be next to - // each other in the array of input surfaces. - - TopExp_Explorer exp(s, TopAbs_FACE); - int min = std::numeric_limits::max(); - int max = std::numeric_limits::min(); - for (; exp.More(); exp.Next()) { - auto ssrf = BRep_Tool::Surface(TopoDS::Face(exp.Current())); - auto it = surfaces.find(ssrf.get()); - if (it != surfaces.end()) { - if (it->second < min) { - min = it->second; - - } - if (it->second > max) { - max = it->second; - } - } - } - - int idx = std::numeric_limits::max(); - if (min != std::numeric_limits::max()) { - if (min == 1 && max == 1) { - idx = 0; - } else if (min + 1 == max || min == max) { - idx = min; - } - } - - if (idx < (int) slices.size()) { - if (slices[idx].IsNull()) { - slices[idx] = s; - continue; - } - } - - Logger::Error("Unable to map layer geometry to material index"); - return false; - } - } - - return true; - } -#else - bool split(IfcGeom::Kernel& k, const TopoDS_Shape& input, const TopTools_ListOfShape& operands, double, std::vector& slices) { - TopTools_ListIteratorOfListOfShape it(operands); - TopoDS_Shape i = input; - for (; it.More(); it.Next()) { - const TopoDS_Shape& s = it.Value(); - TopoDS_Shape a, b; - - Handle(Geom_Surface) surf; - if (s.ShapeType() == TopAbs_FACE) { - surf = BRep_Tool::Surface(TopoDS::Face(s)); - } - - if ((s.ShapeType() == TopAbs_FACE && k.split_solid_by_surface(i, surf, a, b)) || - (s.ShapeType() == TopAbs_SHELL && k.split_solid_by_shell(i, s, a, b))) - { - slices.push_back(b); - i = a; - } else { - return false; - } - } - slices.push_back(i); - return true; - } -#endif -} - -bool IfcGeom::Kernel::apply_folded_layerset(const IfcRepresentationShapeItems& items, const std::vector< std::vector >& surfaces, const std::vector>& styles, IfcRepresentationShapeItems& result) { - Bnd_Box bb; - TopoDS_Shape input; - flatten_shape_list(items, input, false); - - typedef std::vector< std::vector > folded_surfaces_t; - typedef std::vector< std::pair< TopoDS_Face, std::pair > > faces_with_mass_t; - - TopTools_ListOfShape shells; - - for (folded_surfaces_t::const_iterator it = surfaces.begin(); it != surfaces.end(); ++it) { - if (it->empty()) { - continue; - } else if (it->size() == 1) { - const Handle_Geom_Surface& surface = (*it)[0]; - double u1, v1, u2, v2; - if (!project(surface, input, u1, v1, u2, v2)) { - continue; - } - shells.Append(BRepBuilderAPI_MakeShell(surface, u1, v1, u2, v2).Shell()); - } else { - faces_with_mass_t solids; - for (folded_surfaces_t::value_type::const_iterator jt = it->begin(); jt != it->end(); ++jt) { - const Handle_Geom_Surface& surface = *jt; - double u1, v1, u2, v2; - if (!project(surface, input, u1, v1, u2, v2)) { - continue; - } - TopoDS_Face face = BRepBuilderAPI_MakeFace(surface, u1, u2, v1, v2, 1.e-7).Face(); - gp_Pnt p, p1, p2; gp_Vec vu, vv, n; - surface->D1((u1+u2)/2., (v1+v2)/2., p, vu, vv); - n = vu ^ vv; - p1 = p.Translated( n); - p2 = p.Translated(-n); - solids.push_back(std::make_pair(face, std::make_pair(p1, p2))); - } - - - if (solids.empty()) { - continue; - } - - faces_with_mass_t::iterator jt = solids.begin(); - TopoDS_Face& A = jt->first; - TopoDS_Shape An = BRepPrimAPI_MakeHalfSpace(A, jt->second.second).Solid(); - for (++jt; jt != solids.end(); ++jt) { - TopoDS_Face& B = jt->first; - TopoDS_Shape Bn = BRepPrimAPI_MakeHalfSpace(B, jt->second.second).Solid(); - - TopoDS_Shape a = BRepAlgoAPI_Cut(A, Bn); - if (util::count(a, TopAbs_FACE) == 1) { - A = TopoDS::Face(TopExp_Explorer(a, TopAbs_FACE).Current()); - } - - TopoDS_Shape b = BRepAlgoAPI_Cut(B, An); - if (util::count(b, TopAbs_FACE) == 1) { - B = TopoDS::Face(TopExp_Explorer(b, TopAbs_FACE).Current()); - } - } - - BRepOffsetAPI_Sewing builder; - for (faces_with_mass_t::const_iterator kt = solids.begin(); kt != solids.end(); ++kt) { - builder.Add(kt->first); - } - - builder.Perform(); - TopoDS_Shape s = builder.SewedShape(); - if (s.ShapeType() == TopAbs_SHELL) { - shells.Append(TopoDS::Shell(s)); - } else { - Logger::Error("Expected shell type in layerset processing"); - return false; - } - } - } - - if (shells.Extent() == 0) { - - return false; - - } else if (shells.Extent() == 1) { - - for (IfcRepresentationShapeItems::const_iterator it = items.begin(); it != items.end(); ++it) { - TopoDS_Shape a,b; - if (split_solid_by_shell(it->Shape(), shells.First(), a, b)) { - result.push_back(IfcRepresentationShapeItem(it->ItemId(), it->Placement(), b, !!styles[0] ? styles[0] : it->StylePtr())); - result.push_back(IfcRepresentationShapeItem(it->ItemId(), it->Placement(), a, !!styles[1] ? styles[1] : it->StylePtr())); - } else { - continue; - } - } - - return true; - - } else { - - for (IfcRepresentationShapeItems::const_iterator it = items.begin(); it != items.end(); ++it) { - - const TopoDS_Shape& s = it->Shape(); - TopoDS_Solid sld; - ensure_fit_for_subtraction(s, sld); - - std::vector slices; - if (split(*this, it->Shape(), shells, getValue(GV_PRECISION), slices) && slices.size() == styles.size()) { - for (size_t i = 0; i < slices.size(); ++i) { - result.push_back(IfcRepresentationShapeItem(it->ItemId(), it->Placement(), slices[i], !!styles[i] ? styles[i] : it->StylePtr())); - } - } else { - return false; - } - } - - return true; - - } - -} - -bool IfcGeom::Kernel::apply_layerset(const IfcRepresentationShapeItems& items, const std::vector& surfaces, const std::vector>& styles, IfcRepresentationShapeItems& result) { - if (surfaces.size() < 3) { - - return false; - - } else if (surfaces.size() == 3) { - - for (IfcRepresentationShapeItems::const_iterator it = items.begin(); it != items.end(); ++it) { - TopoDS_Shape a,b; - if (split_solid_by_surface(it->Shape(), surfaces[1], a, b)) { - result.push_back(IfcRepresentationShapeItem(it->ItemId(), it->Placement(), b, !!styles[0] ? styles[0] : it->StylePtr())); - result.push_back(IfcRepresentationShapeItem(it->ItemId(), it->Placement(), a, !!styles[1] ? styles[1] : it->StylePtr())); - } else { - continue; - } - } - - return true; - - } else { - - /* - // Determine whether sequence of surfaces is consistent with surface normal, so that - // layer operations are applied in the correct order. This seems to be always the case. - Bnd_Box bb; - for (IfcRepresentationShapeItems::const_iterator it = items.begin(); it != items.end(); ++it) { - BRepBndLib::Add(it->Shape(), bb); - } - - double x1, y1, z1, x2, y2, z2; - bb.Get(x1, y1, z1, x2, y2, z2); - gp_Pnt p1(x1, y1, z1); - gp_Pnt p2(x2, y2, z2); - gp_Pnt avg = (p1.XYZ() + p2.XYZ()) / 2.; - - ShapeAnalysis_Surface sas1(surfaces[0]); - ShapeAnalysis_Surface sas2(surfaces[1]); - const gp_Pnt2d uv = sas1.ValueOfUV(avg, 1e-3); - - gp_Pnt ps1, ps2, mass; - gp_Vec du1, dv1, du2, dv2; - surfaces[0]->D1(uv.X(), uv.Y(), ps1, du1, dv1); - const gp_Vec n1 = dv1.XYZ() ^ du1.XYZ(); - - const bool reversed = gp_Dir(ps2.XYZ() - ps1.XYZ()).Dot(n1) < 0.; - - surfaces[surfaces.size() - 1]->D0(uv.X(), uv.Y(), mass); - mass.ChangeCoord() += n1.XYZ(); - */ - - for (IfcRepresentationShapeItems::const_iterator it = items.begin(); it != items.end(); ++it) { - - const TopoDS_Shape& s = it->Shape(); - TopoDS_Solid sld; - ensure_fit_for_subtraction(s, sld); - - TopTools_ListOfShape operands; - for (unsigned i = 1; i < surfaces.size() - 1; ++i) { - double u1, v1, u2, v2; - if (!project(surfaces[i], sld, u1, v1, u2, v2)) { - return false; - } - - TopoDS_Face face = BRepBuilderAPI_MakeFace(surfaces[i], u1, u2, v1, v2, 1.e-7).Face(); - - operands.Append(face); - } - - /* - // enable this is you want to see how IfcOpenShell has placed the layer surfaces - for (auto& x : operands) { - result.push_back(IfcRepresentationShapeItem(it->ItemId(), it->Placement(), x, nullptr)); - } - */ - - std::vector slices; - if (split(*this, it->Shape(), operands, getValue(GV_PRECISION), slices) && slices.size() == styles.size()) { - for (size_t i = 0; i < slices.size(); ++i) { - result.push_back(IfcRepresentationShapeItem(it->ItemId(), it->Placement(), slices[i], !!styles[i] ? styles[i] : it->StylePtr())); - } - } else { - return false; - } - } - - return true; - } -} - IfcSchema::IfcRepresentation* IfcGeom::Kernel::find_representation(const IfcSchema::IfcProduct* product, const std::string& identifier) { if (!product->Representation()) return 0; IfcSchema::IfcProductRepresentation* prod_rep = product->Representation(); @@ -2789,177 +1598,6 @@ IfcSchema::IfcRepresentation* IfcGeom::Kernel::find_representation(const IfcSche return 0; } -bool IfcGeom::Kernel::split_solid_by_surface(const TopoDS_Shape& input, const Handle_Geom_Surface& surface, TopoDS_Shape& front, TopoDS_Shape& back) { - // Use an unbounded surface, that isolate part of the input shape, - // to split this shape into two parts. Make sure that the addition - // of the two result volumes matches that of the input. - - double u1, v1, u2, v2; - if (!project(surface, input, u1, v1, u2, v2)) { - return false; - } - - TopoDS_Face face = BRepBuilderAPI_MakeFace(surface, u1, u2, v1, v2, 1.e-7).Face(); - gp_Pnt p, p1, p2; gp_Vec vu, vv, n; - surface->D1((u1+u2)/2., (v1+v2)/2., p, vu, vv); - n = vu ^ vv; - p1 = p.Translated(-n); - TopoDS_Solid solid = BRepPrimAPI_MakeHalfSpace(face, p1).Solid(); - - const bool b = split_solid_by_shell(input, solid, front, back); - return b; -} - -bool IfcGeom::Kernel::split_solid_by_shell(const TopoDS_Shape& input, const TopoDS_Shape& shell, TopoDS_Shape& front, TopoDS_Shape& back) { - // Use a shell, typically one or more connected faces, that isolate part - // of the input shape, to split this shape into two parts. Make sure that - // the addition of the two result volumes matches that of the input. - - TopoDS_Solid solid; - if (shell.ShapeType() == TopAbs_SHELL) { - solid = BRepBuilderAPI_MakeSolid(TopoDS::Shell(shell)).Solid(); - } else if (shell.ShapeType() == TopAbs_SOLID) { - solid = TopoDS::Solid(shell); - } else { - return false; - } - apply_tolerance(solid, getValue(GV_PRECISION)); - -#if OCC_VERSION_HEX >= 0x70300 - TopTools_ListOfShape shapes; -#else - BOPCol_ListOfShape shapes; -#endif - shapes.Append(input); - shapes.Append(solid); - BOPAlgo_PaveFiller filler(new NCollection_IncAllocator); // TODO: Does this need to be freed? - filler.SetArguments(shapes); - filler.Perform(); - front = BRepAlgoAPI_Cut(input, solid, filler); - back = BRepAlgoAPI_Common(input, solid, filler); - - bool is_null[2]; - - for (int i = 0; i < 2; ++i) { - TopoDS_Shape& shape = i == 0 ? front : back; - const bool result_is_null = is_null[i] = shape.IsNull() != 0; - if (result_is_null) { - continue; - } - try { - ShapeFix_Shape fix(shape); - if (fix.Perform()) { - shape = fix.Shape(); - } - } catch (const Standard_Failure& e) { - if (e.GetMessageString() && strlen(e.GetMessageString())) { - Logger::Error(e.GetMessageString()); - } else { - Logger::Error("Unknown error performing fixes"); - } - } catch (...) { - Logger::Error("Unknown error performing fixes"); - } - BRepCheck_Analyzer analyser(shape); - bool is_valid = analyser.IsValid() != 0; - if (!is_valid) { - return false; - } - } - - if (is_null[0] || is_null[1]) { - Logger::Message(Logger::LOG_ERROR, "Null result obtained from layerset slicing"); - if (is_null[0] && is_null[1]) { - return false; - } - } - - const double ab = shape_volume(input); - const double a = shape_volume(front); - const double b = shape_volume(back); - - return ALMOST_THE_SAME(ab, a+b, 1.e-3); -} - -bool IfcGeom::Kernel::project(const Handle_Geom_Surface& srf, const TopoDS_Shape& shp, double& u1, double& v1, double& u2, double& v2, double widen) { - // @todo std::unique_ptr for C++11 - ShapeAnalysis_Surface* sas = 0; - Handle(Geom_Plane) pln; - - if (srf->DynamicType() == STANDARD_TYPE(Geom_Plane)) { - // Optimize projection for specific cases - pln = Handle(Geom_Plane)::DownCast(srf); - } else if (srf->DynamicType() == STANDARD_TYPE(Geom_OffsetSurface) && Handle(Geom_OffsetSurface)::DownCast(srf)->BasisSurface()->DynamicType() == STANDARD_TYPE(Geom_Plane)) { - // For an offset planar surface the projected UV coords are the same as the basis surface - pln = Handle(Geom_Plane)::DownCast(Handle(Geom_OffsetSurface)::DownCast(srf)->BasisSurface()); - } else { - sas = new ShapeAnalysis_Surface(srf); - } - - u1 = v1 = +std::numeric_limits::infinity(); - u2 = v2 = -std::numeric_limits::infinity(); - - gp_Pnt median; - int vertex_count = 0; - for (TopExp_Explorer exp(shp, TopAbs_VERTEX); exp.More(); exp.Next(), ++vertex_count) { - gp_Pnt p = BRep_Tool::Pnt(TopoDS::Vertex(exp.Current())); - median.ChangeCoord() += p.XYZ(); - - gp_Pnt2d uv; - if (sas) { - uv = sas->ValueOfUV(p, 1e-3); - } else { - gp_Vec d = p.XYZ() - pln->Position().Location().XYZ(); - uv.SetX(d.Dot(pln->Position().XDirection())); - uv.SetY(d.Dot(pln->Position().YDirection())); - } - - if (uv.X() < u1) u1 = uv.X(); - if (uv.Y() < v1) v1 = uv.Y(); - if (uv.X() > u2) u2 = uv.X(); - if (uv.Y() > v2) v2 = uv.Y(); - } - - if (vertex_count > 0) { - - // Add a little bit of resolution so that the median is shifted towards the mass - // of the curve. This helps to find the parameter ordering for conic surfaces. - for (TopExp_Explorer exp(shp, TopAbs_EDGE); exp.More(); exp.Next(), ++vertex_count) { - const TopoDS_Edge& e = TopoDS::Edge(exp.Current()); - - double a, b; - Handle_Geom_Curve crv = BRep_Tool::Curve(e, a, b); - gp_Pnt p; - crv->D0((a + b) / 2., p); - - median.ChangeCoord() += p.XYZ(); - } - - median.ChangeCoord().Divide(vertex_count); - gp_Pnt2d uv; - if (sas) { - uv = sas->ValueOfUV(median, 1e-3); - } else { - gp_Vec d = median.XYZ() - pln->Position().Location().XYZ(); - uv.SetX(d.Dot(pln->Position().XDirection())); - uv.SetY(d.Dot(pln->Position().YDirection())); - } - - if (uv.X() < u1 || uv.X() > u2) { - std::swap(u1, u2); - } - - u1 -= widen; - u2 += widen; - v1 -= widen; - v2 += widen; - - } - - delete sas; - return vertex_count > 0; -} - const IfcSchema::IfcRepresentationItem* IfcGeom::Kernel::find_item_carrying_style(const IfcSchema::IfcRepresentationItem* item) { if (item->StyledByItem()->size()) { return item; @@ -3020,125 +1658,6 @@ bool IfcGeom::Kernel::is_identity_transform(IfcUtil::IfcBaseInterface* l) { } } -TopoDS_Shape IfcGeom::Kernel::apply_transformation(const TopoDS_Shape& s, const gp_Trsf& t) { - if (t.Form() == gp_Identity) { - return s; - } else { - /// @todo set to 1. and exactly 1. or use epsilon? - if (t.ScaleFactor() != 1.) { - return BRepBuilderAPI_Transform(s, t, true); - } else { - return s.Moved(t); - } - } -} - -TopoDS_Shape IfcGeom::Kernel::apply_transformation(const TopoDS_Shape& s, const gp_GTrsf& t) { - if (t.Form() == gp_Other) { - return BRepBuilderAPI_GTransform(s, t, true); - } else { - - return apply_transformation(s, t.Trsf()); - } -} - -bool IfcGeom::Kernel::fit_halfspace(const TopoDS_Shape& a, const TopoDS_Shape& b, TopoDS_Shape& box, double& height) { - TopExp_Explorer exp(b, TopAbs_FACE); - if (!exp.More()) { - return false; - } - - TopoDS_Face face = TopoDS::Face(exp.Current()); - exp.Next(); - - if (exp.More()) { - return false; - } - - Handle(Geom_Surface) surf = BRep_Tool::Surface(face); - - // const gp_XYZ xyz = a.Location().Transformation().TranslationPart(); - // std::cout << "dz " << xyz.Z() << std::endl; - - if (surf->DynamicType() != STANDARD_TYPE(Geom_Plane)) { - return false; - } - - Bnd_Box bb; - BRepBndLib::Add(a, bb); - - if (bb.IsVoid()) { - return false; - } - - double xs[2], ys[2], zs[2]; - bb.Get(xs[0], ys[0], zs[0], xs[1], ys[1], zs[1]); - - gp_Pln pln = Handle(Geom_Plane)::DownCast(surf)->Pln(); - - gp_Pnt P = pln.Position().Location(); - gp_Vec z = pln.Position().Direction(); - gp_Vec x = pln.Position().XDirection(); - gp_Vec y = pln.Position().YDirection(); - - if (face.Orientation() != TopAbs_REVERSED) { - z.Reverse(); - } - - double D, Umin, Umax, Vmin, Vmax; - D = 0.; - Umin = Vmin = +std::numeric_limits::infinity(); - Umax = Vmax = -std::numeric_limits::infinity(); - - for (int i = 0; i < 2; ++i) { - for (int j = 0; j < 2; ++j) { - for (int k = 0; k < 2; ++k) { - gp_Pnt p(xs[i], ys[j], zs[k]); - - gp_Vec d = p.XYZ() - P.XYZ(); - const double u = d.Dot(x); - const double v = d.Dot(y); - const double w = d.Dot(z); - - if (w > D) { - D = w; - } - if (u < Umin) { - Umin = u; - } - if (u > Umax) { - Umax = u; - } - if (v < Vmin) { - Vmin = v; - } - if (v > Vmax) { - Vmax = v; - } - } - } - } - - const double eps = getValue(GV_PRECISION) * 1000.; - - BRepBuilderAPI_MakePolygon poly; - poly.Add(P.XYZ() + x.XYZ() * (Umin - eps) + y.XYZ() * (Vmin - eps)); - poly.Add(P.XYZ() + x.XYZ() * (Umax + eps) + y.XYZ() * (Vmin - eps)); - poly.Add(P.XYZ() + x.XYZ() * (Umax + eps) + y.XYZ() * (Vmax + eps)); - poly.Add(P.XYZ() + x.XYZ() * (Umin - eps) + y.XYZ() * (Vmax + eps)); - poly.Close(); - - BRepBuilderAPI_MakeFace mf(surf, poly.Wire(), true); - - gp_Vec vec = gp_Vec(z.XYZ() * (D + eps)); - - BRepPrimAPI_MakePrism mp(mf.Face(), vec); - box = mp.Shape(); - - height = D; - return true; -} - 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 719a0d07b3..86b7ec2a8d 100644 --- a/src/ifcgeom/IfcGeom.h +++ b/src/ifcgeom/IfcGeom.h @@ -259,68 +259,22 @@ public: void set_offset(const std::array& offset); void set_rotation(const std::array& rotation); double get_wire_intersection_tolerance(const TopoDS_Wire&) const; - - bool convert_wire_to_face(const TopoDS_Wire& wire, TopoDS_Face& face); - bool convert_wire_to_faces(const TopoDS_Wire& wire, TopoDS_Compound& face); - bool convert_curve_to_wire(const Handle(Geom_Curve)& curve, TopoDS_Wire& wire); + bool convert_shapes(const IfcUtil::IfcBaseInterface* L, IfcRepresentationShapeItems& result); IfcGeom::ShapeType shape_type(const IfcUtil::IfcBaseInterface* L); bool convert_shape(const IfcUtil::IfcBaseInterface* L, TopoDS_Shape& result); - bool flatten_shape_list(const IfcGeom::IfcRepresentationShapeItems& shapes, TopoDS_Shape& result, bool fuse); bool convert_wire(const IfcUtil::IfcBaseInterface* L, TopoDS_Wire& result); 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); - void assert_closed_wire(TopoDS_Wire& wire); bool convert_layerset(const IfcSchema::IfcProduct*, std::vector&, std::vector>&, std::vector&); - bool apply_layerset(const IfcRepresentationShapeItems&, const std::vector&, const std::vector>&, IfcRepresentationShapeItems&); - bool apply_folded_layerset(const IfcRepresentationShapeItems&, const std::vector< std::vector >&, const std::vector>&, IfcRepresentationShapeItems&); bool fold_layers(const IfcSchema::IfcWall*, const IfcRepresentationShapeItems&, const std::vector&, const std::vector&, std::vector< std::vector >&); - - 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&); - - 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&); - const Handle_Geom_Curve intersect(const Handle_Geom_Surface&, const TopoDS_Face&); - const Handle_Geom_Curve intersect(const TopoDS_Face&, const Handle_Geom_Surface&); - bool intersect(const Handle_Geom_Curve&, const Handle_Geom_Surface&, gp_Pnt&); - bool intersect(const Handle_Geom_Curve&, const TopoDS_Face&, gp_Pnt&); - bool intersect(const Handle_Geom_Curve&, const TopoDS_Shape&, std::vector&); - bool intersect(const Handle_Geom_Surface&, const TopoDS_Shape&, std::vector< std::pair >&); - bool closest(const gp_Pnt&, const std::vector&, gp_Pnt&); - bool project(const Handle_Geom_Curve&, const gp_Pnt&, gp_Pnt& p, double& u, double& d); - bool project(const Handle_Geom_Surface&, const TopoDS_Shape&, double& u1, double& v1, double& u2, double& v2, double widen=0.1); - bool find_wall_end_points(const IfcSchema::IfcWall*, gp_Pnt& start, gp_Pnt& end); IfcSchema::IfcSurfaceStyleShading* get_surface_style(IfcSchema::IfcRepresentationItem* item); const IfcSchema::IfcRepresentationItem* find_item_carrying_style(const IfcSchema::IfcRepresentationItem* item); - bool create_solid_from_compound(const TopoDS_Shape& compound, TopoDS_Shape& solid); - bool shape_to_face_list(const TopoDS_Shape& s, TopTools_ListOfShape& li); - bool create_solid_from_faces(const TopTools_ListOfShape& face_list, TopoDS_Shape& solid, bool force_sewing=false); - bool is_compound(const TopoDS_Shape& shape); - bool is_convex(const TopoDS_Wire& wire); - TopoDS_Shape halfspace_from_plane(const gp_Pln& pln,const gp_Pnt& cent); - gp_Pln plane_from_face(const TopoDS_Face& face); - gp_Pnt point_above_plane(const gp_Pln& pln, bool agree=true); - const TopoDS_Shape& ensure_fit_for_subtraction(const TopoDS_Shape& shape, TopoDS_Shape& solid); - bool profile_helper(int numVerts, double* verts, int numFillets, int* filletIndices, double* filletRadii, gp_Trsf2d trsf, TopoDS_Shape& face); - void apply_tolerance(TopoDS_Shape& s, double t); - bool fill_nonmanifold_wires_with_planar_faces(TopoDS_Shape& shape); - void remove_duplicate_points_from_loop(TColgp_SequenceOfPnt& polygon, bool closed, double tol=-1.); - void remove_collinear_points_from_loop(TColgp_SequenceOfPnt& polygon, bool closed, double tol=-1.); - bool wire_to_sequence_of_point(const TopoDS_Wire&, TColgp_SequenceOfPnt&); - void sequence_of_point_to_wire(const TColgp_SequenceOfPnt&, TopoDS_Wire&, bool closed); - static double shape_volume(const TopoDS_Shape& s); - static double face_area(const TopoDS_Face& f); - - static TopoDS_Shape apply_transformation(const TopoDS_Shape&, const gp_Trsf&); - static TopoDS_Shape apply_transformation(const TopoDS_Shape&, const gp_GTrsf&); - bool is_identity_transform(IfcUtil::IfcBaseInterface*); IfcSchema::IfcRelVoidsElement::list::ptr find_openings(IfcSchema::IfcProduct* product); diff --git a/src/ifcgeom/IfcGeometricSet.cpp b/src/ifcgeom/IfcGeometricSet.cpp index 77cd2e6464..010fe6acb7 100644 --- a/src/ifcgeom/IfcGeometricSet.cpp +++ b/src/ifcgeom/IfcGeometricSet.cpp @@ -39,7 +39,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcGeometricSet* l, IfcRepresenta TopoDS_Shape s; if (shape_type(element) == ST_SHAPELIST) { IfcRepresentationShapeItems items; - if (!(convert_shapes(element, items) && flatten_shape_list(items, s, false))) { + if (!(convert_shapes(element, items) && util::flatten_shape_list(items, s, false, getValue(GV_PRECISION)))) { continue; } } else if (shape_type(element) == ST_SHAPE && include_solids_and_surfaces) { diff --git a/src/ifcgeom/IfcIShapeProfileDef.cpp b/src/ifcgeom/IfcIShapeProfileDef.cpp index 37affbce91..8ce9b9a69c 100644 --- a/src/ifcgeom/IfcIShapeProfileDef.cpp +++ b/src/ifcgeom/IfcIShapeProfileDef.cpp @@ -19,6 +19,7 @@ #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/profile_helper.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -67,5 +68,5 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcIShapeProfileDef* l, TopoDS_Sh double coords[24] = {-x1,-y, x1,-y, x1,-y+dy1, d1,-y+dy1, d1,y-dy2, x2,y-dy2, x2,y, -x2,y, -x2,y-dy2, -d1,y-dy2, -d1,-y+dy1, -x1,-y+dy1}; int fillets[4] = {3,4,9,10}; double radii[4] = {f1,f2,f2,f1}; - return profile_helper(12,coords,(doFillet1||doFillet2) ? 4 : 0,fillets,radii,trsf2d,face); + return util::profile_helper(12,coords,(doFillet1||doFillet2) ? 4 : 0,fillets,radii,trsf2d,face); } diff --git a/src/ifcgeom/IfcLShapeProfileDef.cpp b/src/ifcgeom/IfcLShapeProfileDef.cpp index c006e41835..c1e4e103c6 100644 --- a/src/ifcgeom/IfcLShapeProfileDef.cpp +++ b/src/ifcgeom/IfcLShapeProfileDef.cpp @@ -19,6 +19,7 @@ #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/profile_helper.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -94,5 +95,5 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcLShapeProfileDef* l, TopoDS_Sh double coords[12] = {-x,-y, x,-y, x,-y+d-dy1, xx, xy, -x+d-dx1,y, -x,y}; int fillets[3] = {2,3,4}; double radii[3] = {f2,f1,f2}; - return profile_helper(6,coords,doFillet ? 3 : 0,fillets,radii,trsf2d,face); + return util::profile_helper(6,coords,doFillet ? 3 : 0,fillets,radii,trsf2d,face); } diff --git a/src/ifcgeom/IfcPolyLoop.cpp b/src/ifcgeom/IfcPolyLoop.cpp index dda1a98944..6c88a7a451 100644 --- a/src/ifcgeom/IfcPolyLoop.cpp +++ b/src/ifcgeom/IfcPolyLoop.cpp @@ -48,7 +48,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolyLoop* l, TopoDS_Wire& resu // Remove points that are too close to one another const double eps = getValue(GV_PRECISION) * 10; - remove_duplicate_points_from_loop(polygon, true, eps); + util::remove_duplicate_points_from_loop(polygon, true, eps); int count = polygon.Length(); if (original_count - count != 0) { @@ -70,7 +70,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolyLoop* l, TopoDS_Wire& resu result = w.Wire(); TopTools_ListOfShape results; - if (getValue(GV_NO_WIRE_INTERSECTION_CHECK) < 0. && util::wire_intersections(result, results, get_wire_intersection_tolerance(result), getValue(GV_PRECISION))) { + if (getValue(GV_NO_WIRE_INTERSECTION_CHECK) < 0. && util::wire_intersections(result, results, {getValue(GV_NO_WIRE_INTERSECTION_CHECK) < 0., getValue(GV_NO_WIRE_INTERSECTION_TOLERANCE) < 0., 0., getValue(GV_PRECISION)})) { Logger::Error("Self-intersections with " + boost::lexical_cast(results.Extent()) + " cycles detected", l); util::select_largest(results, result); } diff --git a/src/ifcgeom/IfcPolygonalBoundedHalfSpace.cpp b/src/ifcgeom/IfcPolygonalBoundedHalfSpace.cpp index 002e323bb5..59abf35fe0 100644 --- a/src/ifcgeom/IfcPolygonalBoundedHalfSpace.cpp +++ b/src/ifcgeom/IfcPolygonalBoundedHalfSpace.cpp @@ -24,6 +24,7 @@ #include #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/wire_utils.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -38,17 +39,17 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolygonalBoundedHalfSpace* l, if ( ! convert(l->Position(),trsf) ) return false; TColgp_SequenceOfPnt points; - if (wire_to_sequence_of_point(wire, points)) { + if (util::wire_to_sequence_of_point(wire, points)) { // Boolean subtractions not very robust for narrow operands, // increase minimal point spacing to eliminate such shapes. const double t = getValue(GV_PRECISION) * 10.; - remove_duplicate_points_from_loop(points, wire.Closed() != 0, t); // Note: wire always closed, as per if statement above - remove_collinear_points_from_loop(points, wire.Closed() != 0, t); + util::remove_duplicate_points_from_loop(points, wire.Closed() != 0, t); // Note: wire always closed, as per if statement above + util::remove_collinear_points_from_loop(points, wire.Closed() != 0, t); if (points.Length() < 3) { Logger::Message(Logger::LOG_ERROR, "Not enough points retained from:", l->PolygonalBoundary()); return false; } - sequence_of_point_to_wire(points, wire, wire.Closed() != 0); + util::sequence_of_point_to_wire(points, wire, wire.Closed() != 0); } TopoDS_Shape prism = BRepPrimAPI_MakePrism(BRepBuilderAPI_MakeFace(wire),gp_Vec(0,0,200)); diff --git a/src/ifcgeom/IfcPolygonalFaceSet.cpp b/src/ifcgeom/IfcPolygonalFaceSet.cpp index d00591f2f9..882884e035 100644 --- a/src/ifcgeom/IfcPolygonalFaceSet.cpp +++ b/src/ifcgeom/IfcPolygonalFaceSet.cpp @@ -23,6 +23,7 @@ #include #include "../ifcgeom/IfcGeom.h" #include "../ifcgeom_schema_agnostic/wire_utils.h" +#include "../ifcgeom_schema_agnostic/base_utils.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -117,7 +118,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolygonalFaceSet* pfs, TopoDS_ TopTools_ListIteratorOfListOfShape it(fs); for (; it.More(); it.Next()) { const TopoDS_Face& tri = TopoDS::Face(it.Value()); - if (face_area(tri) > getValue(GV_MINIMAL_FACE_AREA)) { + if (util::face_area(tri) > getValue(GV_MINIMAL_FACE_AREA)) { faces.Append(tri); } } @@ -127,7 +128,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolygonalFaceSet* pfs, TopoDS_ } } - if (faces.Extent() > getValue(GV_MAX_FACES_TO_ORIENT) || !create_solid_from_faces(faces, shape)) { + if (faces.Extent() > getValue(GV_MAX_FACES_TO_ORIENT) || !util::create_solid_from_faces(faces, shape, getValue(GV_PRECISION))) { TopoDS_Compound compound; BRep_Builder builder; builder.MakeCompound(compound); diff --git a/src/ifcgeom/IfcPolyline.cpp b/src/ifcgeom/IfcPolyline.cpp index 49dc05fcc4..e3cf08e7a9 100644 --- a/src/ifcgeom/IfcPolyline.cpp +++ b/src/ifcgeom/IfcPolyline.cpp @@ -21,6 +21,7 @@ #include #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/wire_utils.h" #define _USE_MATH_DEFINES #define Kernel MAKE_TYPE_NAME(Kernel) @@ -45,7 +46,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcPolyline* l, TopoDS_Wire& resu } // Remove points that are too close to one another - remove_duplicate_points_from_loop(polygon, closed_by_proximity, eps); + util::remove_duplicate_points_from_loop(polygon, closed_by_proximity, eps); if (polygon.Length() < 2) { // We somehow need to signal we fail this curve on purpose not to trigger an error. diff --git a/src/ifcgeom/IfcRectangleHollowProfileDef.cpp b/src/ifcgeom/IfcRectangleHollowProfileDef.cpp index 77f6084b97..a4e45bf078 100644 --- a/src/ifcgeom/IfcRectangleHollowProfileDef.cpp +++ b/src/ifcgeom/IfcRectangleHollowProfileDef.cpp @@ -25,6 +25,7 @@ #include #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/profile_helper.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -62,8 +63,8 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcRectangleHollowProfileDef* l, double radii2[4] = {r2,r2,r2,r2}; int fillets[4] = {0,1,2,3}; - bool s1 = profile_helper(4,coords1,fr1 ? 4 : 0,fillets,radii1,trsf2d,f1); - bool s2 = profile_helper(4,coords2,fr2 ? 4 : 0,fillets,radii2,trsf2d,f2); + bool s1 = util::profile_helper(4,coords1,fr1 ? 4 : 0,fillets,radii1,trsf2d,f1); + bool s2 = util::profile_helper(4,coords2,fr2 ? 4 : 0,fillets,radii2,trsf2d,f2); if (!s1 || !s2) return false; diff --git a/src/ifcgeom/IfcRectangleProfileDef.cpp b/src/ifcgeom/IfcRectangleProfileDef.cpp index 13b79df195..0226f419e6 100644 --- a/src/ifcgeom/IfcRectangleProfileDef.cpp +++ b/src/ifcgeom/IfcRectangleProfileDef.cpp @@ -19,6 +19,7 @@ #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/profile_helper.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -41,5 +42,5 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcRectangleProfileDef* l, TopoDS } double coords[8] = {-x,-y,x,-y,x,y,-x,y}; - return profile_helper(4,coords,0,0,0,trsf2d,face); + return util::profile_helper(4,coords,0,0,0,trsf2d,face); } diff --git a/src/ifcgeom/IfcRoundedRectangleProfileDef.cpp b/src/ifcgeom/IfcRoundedRectangleProfileDef.cpp index 61a3037fa0..e285fd66d1 100644 --- a/src/ifcgeom/IfcRoundedRectangleProfileDef.cpp +++ b/src/ifcgeom/IfcRoundedRectangleProfileDef.cpp @@ -19,6 +19,7 @@ #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/profile_helper.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -44,5 +45,5 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcRoundedRectangleProfileDef* l, double coords[8] = {-x,-y, x,-y, x,y, -x,y}; int fillets[4] = {0,1,2,3}; double radii[4] = {r,r,r,r}; - return profile_helper(4,coords,4,fillets,radii,trsf2d,face); + return util::profile_helper(4,coords,4,fillets,radii,trsf2d,face); } diff --git a/src/ifcgeom/IfcTShapeProfileDef.cpp b/src/ifcgeom/IfcTShapeProfileDef.cpp index 3143182863..f6b2d3570f 100644 --- a/src/ifcgeom/IfcTShapeProfileDef.cpp +++ b/src/ifcgeom/IfcTShapeProfileDef.cpp @@ -19,6 +19,7 @@ #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/profile_helper.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -108,5 +109,5 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcTShapeProfileDef* l, TopoDS_Sh double coords[16] = {d1/2.-dx2,-y, xx,xy, x,y-d2+dy2, x,y, -x,y, -x,y-d2+dy2, -xx,xy, -d1/2.+dx2,-y}; int fillets[6] = {0,1,2,5,6,7}; double radii[6] = {f2,f1,f3,f3,f1,f2}; - return profile_helper(8, coords, (doFillet || doWebEdgeFillet || doFlangeEdgeFillet) ? 6 : 0, fillets, radii, trsf2d, face); + return util::profile_helper(8, coords, (doFillet || doWebEdgeFillet || doFlangeEdgeFillet) ? 6 : 0, fillets, radii, trsf2d, face); } diff --git a/src/ifcgeom/IfcTrapeziumProfileDef.cpp b/src/ifcgeom/IfcTrapeziumProfileDef.cpp index 300cff3ef6..15ad070ced 100644 --- a/src/ifcgeom/IfcTrapeziumProfileDef.cpp +++ b/src/ifcgeom/IfcTrapeziumProfileDef.cpp @@ -19,6 +19,8 @@ #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/profile_helper.h" + #define Kernel MAKE_TYPE_NAME(Kernel) @@ -52,5 +54,5 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcTrapeziumProfileDef* l, TopoDS -x1 + dx + w - x_offset, y, -x1 + dx - x_offset,y }; - return profile_helper(4,coords,0,0,0,trsf2d,face); + return util::profile_helper(4,coords,0,0,0,trsf2d,face); } diff --git a/src/ifcgeom/IfcTriangulatedFaceSet.cpp b/src/ifcgeom/IfcTriangulatedFaceSet.cpp index cc1a7d0b62..e0e72394a8 100644 --- a/src/ifcgeom/IfcTriangulatedFaceSet.cpp +++ b/src/ifcgeom/IfcTriangulatedFaceSet.cpp @@ -21,6 +21,7 @@ #include #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/base_utils.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -48,7 +49,7 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcTriangulatedFaceSet* l, TopoDS } } - if (faces.Extent() > getValue(GV_MAX_FACES_TO_ORIENT) || !create_solid_from_faces(faces, shape)) { + if (faces.Extent() > getValue(GV_MAX_FACES_TO_ORIENT) || !util::create_solid_from_faces(faces, shape, getValue(GV_PRECISION))) { TopoDS_Compound compound; BRep_Builder builder; builder.MakeCompound(compound); diff --git a/src/ifcgeom/IfcUShapeProfileDef.cpp b/src/ifcgeom/IfcUShapeProfileDef.cpp index 64a15b6ced..b0b7b832e2 100644 --- a/src/ifcgeom/IfcUShapeProfileDef.cpp +++ b/src/ifcgeom/IfcUShapeProfileDef.cpp @@ -19,6 +19,7 @@ #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/profile_helper.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -67,5 +68,5 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcUShapeProfileDef* l, TopoDS_Sh double coords[16] = {-x,-y, x,-y, x,-y+d2-dy2, -x+d1,-y+d2+dy1, -x+d1,y-d2-dy1, x,y-d2+dy2, x,y, -x,y}; int fillets[4] = {2,3,4,5}; double radii[4] = {f2,f1,f1,f2}; - return profile_helper(8, coords, (doFillet || doEdgeFillet) ? 4 : 0, fillets, radii, trsf2d, face); + return util::profile_helper(8, coords, (doFillet || doEdgeFillet) ? 4 : 0, fillets, radii, trsf2d, face); } diff --git a/src/ifcgeom/IfcZShapeProfileDef.cpp b/src/ifcgeom/IfcZShapeProfileDef.cpp index d39f9083d1..0bdbf01054 100644 --- a/src/ifcgeom/IfcZShapeProfileDef.cpp +++ b/src/ifcgeom/IfcZShapeProfileDef.cpp @@ -19,6 +19,7 @@ #include #include "../ifcgeom/IfcGeom.h" +#include "../ifcgeom_schema_agnostic/profile_helper.h" #define Kernel MAKE_TYPE_NAME(Kernel) @@ -58,5 +59,5 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcZShapeProfileDef* l, TopoDS_Sh double coords[16] = {-dx,-y, x,-y, x,-y+dy, dx,-y+dy, dx,y, -x,y, -x,y-dy, -dx,y-dy}; int fillets[4] = {2,3,6,7}; double radii[4] = {f2,f1,f2,f1}; - return profile_helper(8,coords,(doFillet || doEdgeFillet) ? 4 : 0,fillets,radii,trsf2d,face); + return util::profile_helper(8,coords,(doFillet || doEdgeFillet) ? 4 : 0,fillets,radii,trsf2d,face); } diff --git a/src/ifcgeom/faceset_helper.cpp b/src/ifcgeom/faceset_helper.cpp index 56d69ba7ee..0ee89a30a7 100644 --- a/src/ifcgeom/faceset_helper.cpp +++ b/src/ifcgeom/faceset_helper.cpp @@ -275,7 +275,7 @@ bool IfcGeom::Kernel::faceset_helper::wires(const LP& loop, TopTools_Lis wire.Closed(true); TopTools_ListOfShape results; - if (kernel_->getValue(GV_NO_WIRE_INTERSECTION_CHECK) < 0. && util::wire_intersections(wire, results, kernel_->get_wire_intersection_tolerance(wire), kernel_->getValue(IfcGeom::Kernel::GV_PRECISION))) { + if (kernel_->getValue(GV_NO_WIRE_INTERSECTION_CHECK) < 0. && util::wire_intersections(wire, results, {kernel_->getValue(GV_NO_WIRE_INTERSECTION_CHECK) < 0., kernel_->getValue(GV_NO_WIRE_INTERSECTION_TOLERANCE) < 0., 0., kernel_->getValue(GV_PRECISION)})) { Logger::Warning("Self-intersections with " + boost::lexical_cast(results.Extent()) + " cycles detected"); non_manifold_ = true; wires = results; diff --git a/src/ifcgeom/mapping.cpp b/src/ifcgeom/mapping.cpp index 8e09756330..ee4d32bd07 100644 --- a/src/ifcgeom/mapping.cpp +++ b/src/ifcgeom/mapping.cpp @@ -19,6 +19,7 @@ #include "IfcGeom.h" #include "../ifcgeom_schema_agnostic/IfcGeomShapeType.h" +#include "../ifcgeom_schema_agnostic/wire_utils.h" #include @@ -68,7 +69,7 @@ bool IfcGeom::Kernel::convert_shape(const IfcBaseInterface* l, TopoDS_Shape& r) if (st == ST_SHAPELIST) { processed = true; IfcRepresentationShapeItems items; - success = convert_shapes(l, items) && flatten_shape_list(items, r, false); + success = convert_shapes(l, items) && util::flatten_shape_list(items, r, false, getValue(GV_PRECISION)); } else if (st == ST_SHAPE && include_solids_and_surfaces) { #include "mapping_shape.i" } else if (st == ST_FACE && include_solids_and_surfaces) { @@ -85,22 +86,20 @@ bool IfcGeom::Kernel::convert_shape(const IfcBaseInterface* l, TopoDS_Shape& r) processed = true; Handle(Geom_Curve) crv; TopoDS_Wire w; - success = convert_curve(l, crv) && convert_curve_to_wire(crv, w); + success = convert_curve(l, crv) && util::convert_curve_to_wire(crv, w); if (success) { r = w; } } if ( processed && success ) { - const double precision = getValue(GV_PRECISION); - apply_tolerance(r, precision); #ifndef NO_CACHE cache.Shape[id] = r; #endif if (Logger::LOG_DEBUG >= Logger::Verbosity()) { BRepCheck_Analyzer ana(r); - Logger::Notice("Valid: " + std::to_string(ana.IsValid()), l); + Logger::Notice("Valid: " + std::to_string((bool) ana.IsValid()), l); } } else if (!ignored) { const char* const msg = processed @@ -115,7 +114,7 @@ bool IfcGeom::Kernel::convert_wire(const IfcBaseInterface* l, TopoDS_Wire& r) { #include "mapping_wire.i" Handle(Geom_Curve) curve; if (IfcGeom::Kernel::convert_curve(l, curve)) { - return IfcGeom::Kernel::convert_curve_to_wire(curve, r); + return util::convert_curve_to_wire(curve, r); } Logger::Message(Logger::LOG_ERROR,"No operation defined for:",l); return false; diff --git a/src/ifcgeom_schema_agnostic/IfcRepresentationShapeItem.h b/src/ifcgeom_schema_agnostic/IfcRepresentationShapeItem.h index 3a2f0938dd..1f751a792c 100644 --- a/src/ifcgeom_schema_agnostic/IfcRepresentationShapeItem.h +++ b/src/ifcgeom_schema_agnostic/IfcRepresentationShapeItem.h @@ -20,12 +20,15 @@ #ifndef IFCSHAPELIST_H #define IFCSHAPELIST_H +#include "../ifcgeom_schema_agnostic/IfcGeomRenderStyles.h" + #include #include -#include "../ifcgeom_schema_agnostic/IfcGeomRenderStyles.h" +#include namespace IfcGeom { + class IFC_GEOM_API IfcRepresentationShapeItem { private: int id; @@ -51,6 +54,11 @@ namespace IfcGeom { void setStyle(std::shared_ptr newStyle) { style = newStyle; } int ItemId() const { return id; } }; + typedef std::vector IfcRepresentationShapeItems; + + namespace util { + bool flatten_shape_list(const IfcGeom::IfcRepresentationShapeItems& shapes, TopoDS_Shape& result, bool fuse, double tol); + } } #endif diff --git a/src/ifcgeom_schema_agnostic/base_utils.cpp b/src/ifcgeom_schema_agnostic/base_utils.cpp index 3a0cc41d1c..f656fb45a9 100644 --- a/src/ifcgeom_schema_agnostic/base_utils.cpp +++ b/src/ifcgeom_schema_agnostic/base_utils.cpp @@ -1,13 +1,47 @@ #include "base_utils.h" -#include +#include "../ifcparse/IfcLogger.h" + #include #include #include #include + #include #include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#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) { @@ -153,3 +187,555 @@ gp_Trsf IfcGeom::util::combine_offset_and_rotation(const gp_Vec & offset, const return rotation_transform * offset_transform; } + + +bool IfcGeom::util::project(const Handle_Geom_Surface& srf, const TopoDS_Shape& shp, double& u1, double& v1, double& u2, double& v2, double widen) { + // @todo std::unique_ptr for C++11 + ShapeAnalysis_Surface* sas = 0; + Handle(Geom_Plane) pln; + + if (srf->DynamicType() == STANDARD_TYPE(Geom_Plane)) { + // Optimize projection for specific cases + pln = Handle(Geom_Plane)::DownCast(srf); + } else if (srf->DynamicType() == STANDARD_TYPE(Geom_OffsetSurface) && Handle(Geom_OffsetSurface)::DownCast(srf)->BasisSurface()->DynamicType() == STANDARD_TYPE(Geom_Plane)) { + // For an offset planar surface the projected UV coords are the same as the basis surface + pln = Handle(Geom_Plane)::DownCast(Handle(Geom_OffsetSurface)::DownCast(srf)->BasisSurface()); + } else { + sas = new ShapeAnalysis_Surface(srf); + } + + u1 = v1 = +std::numeric_limits::infinity(); + u2 = v2 = -std::numeric_limits::infinity(); + + gp_Pnt median; + int vertex_count = 0; + for (TopExp_Explorer exp(shp, TopAbs_VERTEX); exp.More(); exp.Next(), ++vertex_count) { + gp_Pnt p = BRep_Tool::Pnt(TopoDS::Vertex(exp.Current())); + median.ChangeCoord() += p.XYZ(); + + gp_Pnt2d uv; + if (sas) { + uv = sas->ValueOfUV(p, 1e-3); + } else { + gp_Vec d = p.XYZ() - pln->Position().Location().XYZ(); + uv.SetX(d.Dot(pln->Position().XDirection())); + uv.SetY(d.Dot(pln->Position().YDirection())); + } + + if (uv.X() < u1) u1 = uv.X(); + if (uv.Y() < v1) v1 = uv.Y(); + if (uv.X() > u2) u2 = uv.X(); + if (uv.Y() > v2) v2 = uv.Y(); + } + + if (vertex_count > 0) { + + // Add a little bit of resolution so that the median is shifted towards the mass + // of the curve. This helps to find the parameter ordering for conic surfaces. + for (TopExp_Explorer exp(shp, TopAbs_EDGE); exp.More(); exp.Next(), ++vertex_count) { + const TopoDS_Edge& e = TopoDS::Edge(exp.Current()); + + double a, b; + Handle_Geom_Curve crv = BRep_Tool::Curve(e, a, b); + gp_Pnt p; + crv->D0((a + b) / 2., p); + + median.ChangeCoord() += p.XYZ(); + } + + median.ChangeCoord().Divide(vertex_count); + gp_Pnt2d uv; + if (sas) { + uv = sas->ValueOfUV(median, 1e-3); + } else { + gp_Vec d = median.XYZ() - pln->Position().Location().XYZ(); + uv.SetX(d.Dot(pln->Position().XDirection())); + uv.SetY(d.Dot(pln->Position().YDirection())); + } + + if (uv.X() < u1 || uv.X() > u2) { + std::swap(u1, u2); + } + + u1 -= widen; + u2 += widen; + v1 -= widen; + v2 += widen; + + } + + delete sas; + return vertex_count > 0; +} + + +TopoDS_Shape IfcGeom::util::apply_transformation(const TopoDS_Shape& s, const gp_Trsf& t) { + if (t.Form() == gp_Identity) { + return s; + } else { + /// @todo set to 1. and exactly 1. or use epsilon? + if (t.ScaleFactor() != 1.) { + return BRepBuilderAPI_Transform(s, t, true); + } else { + return s.Moved(t); + } + } +} + +TopoDS_Shape IfcGeom::util::apply_transformation(const TopoDS_Shape& s, const gp_GTrsf& t) { + if (t.Form() == gp_Other) { + return BRepBuilderAPI_GTransform(s, t, true); + } else { + + return apply_transformation(s, t.Trsf()); + } +} + +bool IfcGeom::util::fit_halfspace(const TopoDS_Shape& a, const TopoDS_Shape& b, TopoDS_Shape& box, double& height, double tol) { + TopExp_Explorer exp(b, TopAbs_FACE); + if (!exp.More()) { + return false; + } + + TopoDS_Face face = TopoDS::Face(exp.Current()); + exp.Next(); + + if (exp.More()) { + return false; + } + + Handle(Geom_Surface) surf = BRep_Tool::Surface(face); + + // const gp_XYZ xyz = a.Location().Transformation().TranslationPart(); + // std::cout << "dz " << xyz.Z() << std::endl; + + if (surf->DynamicType() != STANDARD_TYPE(Geom_Plane)) { + return false; + } + + Bnd_Box bb; + BRepBndLib::Add(a, bb); + + if (bb.IsVoid()) { + return false; + } + + double xs[2], ys[2], zs[2]; + bb.Get(xs[0], ys[0], zs[0], xs[1], ys[1], zs[1]); + + gp_Pln pln = Handle(Geom_Plane)::DownCast(surf)->Pln(); + + gp_Pnt P = pln.Position().Location(); + gp_Vec z = pln.Position().Direction(); + gp_Vec x = pln.Position().XDirection(); + gp_Vec y = pln.Position().YDirection(); + + if (face.Orientation() != TopAbs_REVERSED) { + z.Reverse(); + } + + double D, Umin, Umax, Vmin, Vmax; + D = 0.; + Umin = Vmin = +std::numeric_limits::infinity(); + Umax = Vmax = -std::numeric_limits::infinity(); + + for (int i = 0; i < 2; ++i) { + for (int j = 0; j < 2; ++j) { + for (int k = 0; k < 2; ++k) { + gp_Pnt p(xs[i], ys[j], zs[k]); + + gp_Vec d = p.XYZ() - P.XYZ(); + const double u = d.Dot(x); + const double v = d.Dot(y); + const double w = d.Dot(z); + + if (w > D) { + D = w; + } + if (u < Umin) { + Umin = u; + } + if (u > Umax) { + Umax = u; + } + if (v < Vmin) { + Vmin = v; + } + if (v > Vmax) { + Vmax = v; + } + } + } + } + + const double eps = tol * 1000.; + + BRepBuilderAPI_MakePolygon poly; + poly.Add(P.XYZ() + x.XYZ() * (Umin - eps) + y.XYZ() * (Vmin - eps)); + poly.Add(P.XYZ() + x.XYZ() * (Umax + eps) + y.XYZ() * (Vmin - eps)); + poly.Add(P.XYZ() + x.XYZ() * (Umax + eps) + y.XYZ() * (Vmax + eps)); + poly.Add(P.XYZ() + x.XYZ() * (Umin - eps) + y.XYZ() * (Vmax + eps)); + poly.Close(); + + BRepBuilderAPI_MakeFace mf(surf, poly.Wire(), true); + + gp_Vec vec = gp_Vec(z.XYZ() * (D + eps)); + + BRepPrimAPI_MakePrism mp(mf.Face(), vec); + box = mp.Shape(); + + height = D; + return true; +} + + +const Handle_Geom_Curve IfcGeom::util::intersect(const Handle_Geom_Surface& a, const Handle_Geom_Surface& b) { + GeomAPI_IntSS x(a, b, 1.e-7); + if (x.IsDone() && x.NbLines() == 1) { + return x.Line(1); + } else { + return Handle_Geom_Curve(); + } +} + +const Handle_Geom_Curve IfcGeom::util::intersect(const Handle_Geom_Surface& a, const TopoDS_Face& b) { + return intersect(a, BRep_Tool::Surface(b)); +} + +const Handle_Geom_Curve IfcGeom::util::intersect(const TopoDS_Face& a, const Handle_Geom_Surface& b) { + return intersect(BRep_Tool::Surface(a), b); +} + +bool IfcGeom::util::intersect(const Handle_Geom_Curve& a, const Handle_Geom_Surface& b, gp_Pnt& p) { + GeomAPI_IntCS x(a, b); + if (x.IsDone() && x.NbPoints() == 1) { + p = x.Point(1); + return true; + } else { + return false; + } +} + +bool IfcGeom::util::intersect(const Handle_Geom_Curve& a, const TopoDS_Face& b, gp_Pnt &c) { + return intersect(a, BRep_Tool::Surface(b), c); +} + +bool IfcGeom::util::intersect(const Handle_Geom_Curve& a, const TopoDS_Shape& b, std::vector& out) { + TopExp_Explorer exp(b, TopAbs_FACE); + gp_Pnt p; + for (; exp.More(); exp.Next()) { + if (intersect(a, TopoDS::Face(exp.Current()), p)) { + out.push_back(p); + } + } + return !out.empty(); +} + +bool IfcGeom::util::intersect(const Handle_Geom_Surface& a, const TopoDS_Shape& b, std::vector< std::pair >& out) { + TopExp_Explorer exp(b, TopAbs_FACE); + for (; exp.More(); exp.Next()) { + const TopoDS_Face& f = TopoDS::Face(exp.Current()); + const Handle_Geom_Surface& s = BRep_Tool::Surface(f); + Handle_Geom_Curve crv = intersect(a, s); + if (!crv.IsNull()) { + out.push_back(std::make_pair(s, crv)); + } + } + return !out.empty(); +} + +bool IfcGeom::util::closest(const gp_Pnt& a, const std::vector& b, gp_Pnt& c) { + double minimal_distance = std::numeric_limits::infinity(); + for (std::vector::const_iterator it = b.begin(); it != b.end(); ++it) { + const double d = a.Distance(*it); + if (d < minimal_distance) { + minimal_distance = d; + c = *it; + } + } + return minimal_distance != std::numeric_limits::infinity(); +} + +bool IfcGeom::util::project(const Handle_Geom_Curve& crv, const gp_Pnt& pt, gp_Pnt& p, double& u, double& d) { + ShapeAnalysis_Curve sac; + sac.Project(crv, pt, 1e-3, p, u, false); + d = pt.Distance(p); + return true; +} + +double IfcGeom::util::shape_volume(const TopoDS_Shape& s) { + GProp_GProps prop; + BRepGProp::VolumeProperties(s, prop); + return prop.Mass(); +} + +double IfcGeom::util::face_area(const TopoDS_Face& f) { + GProp_GProps prop; + BRepGProp::SurfaceProperties(f, prop); + return prop.Mass(); +} + +bool IfcGeom::util::is_convex(const TopoDS_Wire& wire, double tol) { + for (TopExp_Explorer exp1(wire, TopAbs_VERTEX); exp1.More(); exp1.Next()) { + TopoDS_Vertex V1 = TopoDS::Vertex(exp1.Current()); + gp_Pnt P1 = BRep_Tool::Pnt(V1); + // Store the neighboring points + std::vector neighbors; + for (TopExp_Explorer exp3(wire, TopAbs_EDGE); exp3.More(); exp3.Next()) { + TopoDS_Edge edge = TopoDS::Edge(exp3.Current()); + std::vector edge_points; + for (TopExp_Explorer exp2(edge, TopAbs_VERTEX); exp2.More(); exp2.Next()) { + TopoDS_Vertex V2 = TopoDS::Vertex(exp2.Current()); + gp_Pnt P2 = BRep_Tool::Pnt(V2); + edge_points.push_back(P2); + } + if (edge_points.size() != 2) continue; + if (edge_points[0].IsEqual(P1, tol)) neighbors.push_back(edge_points[1]); + else if (edge_points[1].IsEqual(P1, tol)) neighbors.push_back(edge_points[0]); + } + // There should be two of these + if (neighbors.size() != 2) return false; + // Now find the non neighboring points + std::vector non_neighbors; + for (TopExp_Explorer exp2(wire, TopAbs_VERTEX); exp2.More(); exp2.Next()) { + TopoDS_Vertex V2 = TopoDS::Vertex(exp2.Current()); + gp_Pnt P2 = BRep_Tool::Pnt(V2); + if (P1.IsEqual(P2, tol)) continue; + bool found = false; + for (std::vector::const_iterator it = neighbors.begin(); it != neighbors.end(); ++it) { + if ((*it).IsEqual(P2, tol)) { found = true; break; } + } + if (!found) non_neighbors.push_back(P2); + } + // Calculate the angle between the two edges of the vertex + gp_Dir dir1(neighbors[0].XYZ() - P1.XYZ()); + gp_Dir dir2(neighbors[1].XYZ() - P1.XYZ()); + const double angle = acos(dir1.Dot(dir2)) + 0.0001; + // Now for the non-neighbors see whether a greater angle can be found with one of the edges + for (std::vector::const_iterator it = non_neighbors.begin(); it != non_neighbors.end(); ++it) { + gp_Dir dir3((*it).XYZ() - P1.XYZ()); + const double angle2 = acos(dir3.Dot(dir1)); + const double angle3 = acos(dir3.Dot(dir2)); + if (angle2 > angle || angle3 > angle) return false; + } + } + return true; +} + +TopoDS_Shape IfcGeom::util::halfspace_from_plane(const gp_Pln& pln, const gp_Pnt& cent) { + TopoDS_Face face = BRepBuilderAPI_MakeFace(pln).Face(); + return BRepPrimAPI_MakeHalfSpace(face, cent).Solid(); +} + +gp_Pln IfcGeom::util::plane_from_face(const TopoDS_Face& face) { + BRepGProp_Face prop(face); + Standard_Real u1, u2, v1, v2; + prop.Bounds(u1, u2, v1, v2); + Standard_Real u = (u1 + u2) / 2.0; + Standard_Real v = (v1 + v2) / 2.0; + gp_Pnt p; + gp_Vec n; + prop.Normal(u, v, p, n); + return gp_Pln(p, n); +} + +gp_Pnt IfcGeom::util::point_above_plane(const gp_Pln& pln, bool agree) { + if (agree) { + return pln.Location().Translated(pln.Axis().Direction()); + } else { + return pln.Location().Translated(-pln.Axis().Direction()); + } +} + +bool IfcGeom::util::is_compound(const TopoDS_Shape& shape) { + bool has_solids = TopExp_Explorer(shape, TopAbs_SOLID).More() != 0; + bool has_shells = TopExp_Explorer(shape, TopAbs_SHELL).More() != 0; + bool has_compounds = TopExp_Explorer(shape, TopAbs_COMPOUND).More() != 0; + bool has_faces = TopExp_Explorer(shape, TopAbs_FACE).More() != 0; + return has_compounds && has_faces && !has_solids && !has_shells; +} + +bool IfcGeom::util::shape_to_face_list(const TopoDS_Shape& s, TopTools_ListOfShape& li) { + TopExp_Explorer exp(s, TopAbs_FACE); + for (; exp.More(); exp.Next()) { + TopoDS_Face face = TopoDS::Face(exp.Current()); + li.Append(face); + } + return true; +} + +bool IfcGeom::util::create_solid_from_compound(const TopoDS_Shape& compound, TopoDS_Shape& shape, double tol) { + TopTools_ListOfShape face_list; + shape_to_face_list(compound, face_list); + if (face_list.Extent() == 0) { + return false; + } + return create_solid_from_faces(face_list, shape, tol); +} + +bool IfcGeom::util::create_solid_from_faces(const TopTools_ListOfShape& face_list, TopoDS_Shape& shape, double tol, bool force_sewing) { + bool valid_shell = false; + + if (face_list.Extent() == 1) { + shape = face_list.First(); + // A bit dubious what to return here. + return true; + } else if (face_list.Extent() == 0) { + return false; + } + + TopTools_ListIteratorOfListOfShape face_iterator; + + bool has_shared_edges = false; + TopTools_MapOfShape edge_set; + + // In case there are wire interesections or failures in non-planar wire triangulations + // the idea is to let occt do an exhaustive search of edge partners. But we have not + // found a case where this actually improves boolean ops later on. + // if (!faceset_helper_ || !faceset_helper_->non_manifold()) { + + for (face_iterator.Initialize(face_list); !force_sewing && face_iterator.More(); face_iterator.Next()) { + // As soon as is detected one of the edges is shared, the assumption is made no + // additional sewing is necessary. + if (!has_shared_edges) { + TopExp_Explorer exp(face_iterator.Value(), TopAbs_EDGE); + for (; exp.More(); exp.Next()) { + if (edge_set.Contains(exp.Current())) { + has_shared_edges = true; + break; + } + edge_set.Add(exp.Current()); + } + } + } + + BRepOffsetAPI_Sewing sewing_builder; + sewing_builder.SetTolerance(tol); + sewing_builder.SetMaxTolerance(tol); + sewing_builder.SetMinTolerance(tol); + + BRep_Builder builder; + TopoDS_Shell shell; + builder.MakeShell(shell); + + for (face_iterator.Initialize(face_list); face_iterator.More(); face_iterator.Next()) { + if (has_shared_edges) { + builder.Add(shell, face_iterator.Value()); + } else { + sewing_builder.Add(face_iterator.Value()); + } + } + + try { + if (has_shared_edges) { + ShapeFix_Shell fix; + fix.FixFaceOrientation(shell); + shape = fix.Shape(); + } else { + sewing_builder.Perform(); + shape = sewing_builder.SewedShape(); + } + + BRepCheck_Analyzer ana(shape); + valid_shell = ana.IsValid(); + + if (!valid_shell) { + ShapeFix_Shape sfs(shape); + sfs.Perform(); + shape = sfs.Shape(); + + BRepCheck_Analyzer reana(shape); + valid_shell = reana.IsValid(); + } + + valid_shell &= util::count(shape, TopAbs_SHELL) > 0; + } catch (const Standard_Failure& e) { + if (e.GetMessageString() && strlen(e.GetMessageString())) { + Logger::Error(e.GetMessageString()); + } else { + Logger::Error("Unknown error sewing shell"); + } + } catch (...) { + Logger::Error("Unknown error sewing shell"); + } + + if (valid_shell) { + + TopoDS_Shape complete_shape; + TopExp_Explorer exp(shape, TopAbs_SHELL); + + for (; exp.More(); exp.Next()) { + TopoDS_Shape result_shape = exp.Current(); + + try { + ShapeFix_Solid solid; + solid.SetMaxTolerance(tol); + TopoDS_Solid solid_shape = solid.SolidFromShell(TopoDS::Shell(exp.Current())); + // @todo: BRepClass3d_SolidClassifier::PerformInfinitePoint() is done by SolidFromShell + // and this is done again, to be able to catch errors during this process. + // This is double work that should be avoided. + if (!solid_shape.IsNull()) { + try { + BRepClass3d_SolidClassifier classifier(solid_shape); + result_shape = solid_shape; + classifier.PerformInfinitePoint(tol); + if (classifier.State() == TopAbs_IN) { + shape.Reverse(); + } + } catch (const Standard_Failure& e) { + if (e.GetMessageString() && strlen(e.GetMessageString())) { + Logger::Error(e.GetMessageString()); + } else { + Logger::Error("Unknown error classifying solid"); + } + } catch (...) { + Logger::Error("Unknown error classifying solid"); + } + } + } catch (const Standard_Failure& e) { + if (e.GetMessageString() && strlen(e.GetMessageString())) { + Logger::Error(e.GetMessageString()); + } else { + Logger::Error("Unknown error creating solid"); + } + } catch (...) { + Logger::Error("Unknown error creating solid"); + } + + if (complete_shape.IsNull()) { + complete_shape = result_shape; + } else { + BRep_Builder B; + if (complete_shape.ShapeType() != TopAbs_COMPOUND) { + TopoDS_Compound C; + B.MakeCompound(C); + B.Add(C, complete_shape); + complete_shape = C; + Logger::Warning("Multiple components in IfcConnectedFaceSet"); + } + B.Add(complete_shape, result_shape); + } + } + + TopExp_Explorer loose_faces(shape, TopAbs_FACE, TopAbs_SHELL); + + for (; loose_faces.More(); loose_faces.Next()) { + BRep_Builder B; + if (complete_shape.ShapeType() != TopAbs_COMPOUND) { + TopoDS_Compound C; + B.MakeCompound(C); + B.Add(C, complete_shape); + complete_shape = C; + Logger::Warning("Loose faces in IfcConnectedFaceSet"); + } + B.Add(complete_shape, loose_faces.Current()); + } + + shape = complete_shape; + + } else { + Logger::Error("Failed to sew faceset"); + } + + return valid_shell; +} diff --git a/src/ifcgeom_schema_agnostic/base_utils.h b/src/ifcgeom_schema_agnostic/base_utils.h index b7c640624e..de46b3379c 100644 --- a/src/ifcgeom_schema_agnostic/base_utils.h +++ b/src/ifcgeom_schema_agnostic/base_utils.h @@ -1,8 +1,19 @@ #ifndef BASE_UTILS_H #define BASE_UTILS_H -#include #include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include namespace IfcGeom { namespace util { @@ -26,6 +37,34 @@ namespace IfcGeom { 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); + + bool create_solid_from_compound(const TopoDS_Shape& compound, TopoDS_Shape& solid, double tol); + bool shape_to_face_list(const TopoDS_Shape& s, TopTools_ListOfShape& li); + bool create_solid_from_faces(const TopTools_ListOfShape& face_list, TopoDS_Shape& solid, double tol, bool force_sewing = false); + bool is_compound(const TopoDS_Shape& shape); + bool is_convex(const TopoDS_Wire& wire, double tol); + TopoDS_Shape halfspace_from_plane(const gp_Pln& pln, const gp_Pnt& cent); + gp_Pln plane_from_face(const TopoDS_Face& face); + gp_Pnt point_above_plane(const gp_Pln& pln, bool agree = true); + + bool fit_halfspace(const TopoDS_Shape& a, const TopoDS_Shape& b, TopoDS_Shape& box, double& height, double tol); + const Handle_Geom_Curve intersect(const Handle_Geom_Surface&, const Handle_Geom_Surface&); + const Handle_Geom_Curve intersect(const Handle_Geom_Surface&, const TopoDS_Face&); + const Handle_Geom_Curve intersect(const TopoDS_Face&, const Handle_Geom_Surface&); + bool intersect(const Handle_Geom_Curve&, const Handle_Geom_Surface&, gp_Pnt&); + bool intersect(const Handle_Geom_Curve&, const TopoDS_Face&, gp_Pnt&); + bool intersect(const Handle_Geom_Curve&, const TopoDS_Shape&, std::vector&); + bool intersect(const Handle_Geom_Surface&, const TopoDS_Shape&, std::vector< std::pair >&); + bool closest(const gp_Pnt&, const std::vector&, gp_Pnt&); + bool project(const Handle_Geom_Curve&, const gp_Pnt&, gp_Pnt& p, double& u, double& d); + bool project(const Handle_Geom_Surface&, const TopoDS_Shape&, double& u1, double& v1, double& u2, double& v2, double widen = 0.1); + + + double shape_volume(const TopoDS_Shape& s); + double face_area(const TopoDS_Face& f); + + TopoDS_Shape apply_transformation(const TopoDS_Shape&, const gp_Trsf&); + TopoDS_Shape apply_transformation(const TopoDS_Shape&, const gp_GTrsf&); } } diff --git a/src/ifcgeom_schema_agnostic/boolean_utils.cpp b/src/ifcgeom_schema_agnostic/boolean_utils.cpp index b77122de3e..a5ecc20c55 100644 --- a/src/ifcgeom_schema_agnostic/boolean_utils.cpp +++ b/src/ifcgeom_schema_agnostic/boolean_utils.cpp @@ -1357,3 +1357,16 @@ bool IfcGeom::util::boolean_operation(const boolean_settings& settings, const To bs.Append(b); return boolean_operation(settings, a, bs, op, result, fuzziness); } + +const TopoDS_Shape& IfcGeom::util::ensure_fit_for_subtraction(const TopoDS_Shape& shape, TopoDS_Shape& solid, double tol) { + const bool is_comp = is_compound(shape); + if (!is_comp) { + return solid = shape; + } + + if (!create_solid_from_compound(shape, solid, tol)) { + return solid = shape; + } + + return solid; +} \ No newline at end of file diff --git a/src/ifcgeom_schema_agnostic/boolean_utils.h b/src/ifcgeom_schema_agnostic/boolean_utils.h index 3d0cce8b70..1e67dabed1 100644 --- a/src/ifcgeom_schema_agnostic/boolean_utils.h +++ b/src/ifcgeom_schema_agnostic/boolean_utils.h @@ -93,6 +93,8 @@ namespace IfcGeom { 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.); + + const TopoDS_Shape& ensure_fit_for_subtraction(const TopoDS_Shape& shape, TopoDS_Shape& solid, double tol); } } diff --git a/src/ifcgeom_schema_agnostic/wire_utils.cpp b/src/ifcgeom_schema_agnostic/wire_utils.cpp index b9484d48a3..ba53a7c05f 100644 --- a/src/ifcgeom_schema_agnostic/wire_utils.cpp +++ b/src/ifcgeom_schema_agnostic/wire_utils.cpp @@ -3,6 +3,7 @@ #include "../ifcparse/IfcLogger.h" #include "../ifcgeom_schema_agnostic/Kernel.h" #include "../ifcgeom_schema_agnostic/base_utils.h" +#include "../ifcgeom_schema_agnostic/boolean_utils.h" #include "../ifcgeom_schema_agnostic/IfcGeomTree.h" #include @@ -21,6 +22,9 @@ #include #include #include +#include +#include +#include #include #include @@ -350,7 +354,25 @@ namespace { }; } -bool IfcGeom::util::wire_intersections(const TopoDS_Wire& wire, TopTools_ListOfShape& wires, double eps, double eps_real) { +namespace { + double get_wire_intersection_tolerance(const IfcGeom::util::wire_tolerance_settings& settings, const TopoDS_Wire& wire) { + if (settings.use_wire_intersection_tolerance) { + // This corresponds to faceset_helper::epsilon + if (settings.vertex_clustering_epsilon > 0.) { + return settings.vertex_clustering_epsilon / 3.; + } else { + return (std::min)(IfcGeom::util::min_edge_length(wire) / 2., settings.precision * 10.); + } + } else { + return 0.; + } + } +} + +bool IfcGeom::util::wire_intersections(const TopoDS_Wire& wire, TopTools_ListOfShape& wires, const wire_tolerance_settings& settings) { + double eps = get_wire_intersection_tolerance(settings, wire); + double eps_real = settings.precision; + if (!wire.Closed()) { wires.Append(wire); return false; @@ -512,7 +534,7 @@ bool IfcGeom::util::wire_intersections(const TopoDS_Wire& wire, TopTools_ListOfS // @todo this is a change in behaviour with eps precomputed from the kernel // instead of adaptively calculated for the successive iterations. - wire_intersections(sfw.Wire(), wires, eps, eps_real); + wire_intersections(sfw.Wire(), wires, settings); } return true; @@ -567,4 +589,360 @@ void IfcGeom::util::select_largest(const TopTools_ListOfShape& shapes, TopoDS_Sh largest = it.Value(); } } +} + + +bool IfcGeom::util::wire_to_sequence_of_point(const TopoDS_Wire& w, TColgp_SequenceOfPnt& p) { + TopExp_Explorer exp(w, TopAbs_EDGE); + for (; exp.More(); exp.Next()) { + double a, b; + Handle_Geom_Curve crv = BRep_Tool::Curve(TopoDS::Edge(exp.Current()), a, b); + if (crv->DynamicType() != STANDARD_TYPE(Geom_Line)) { + return false; + } + } + + exp.ReInit(); + + int i = 0; + for (; exp.More(); exp.Next(), ++i) { + TopoDS_Vertex v1, v2; + TopExp::Vertices(TopoDS::Edge(exp.Current()), v1, v2, true); + if (exp.More()) { + if (i == 0) { + p.Append(BRep_Tool::Pnt(v1)); + } + p.Append(BRep_Tool::Pnt(v2)); + } + } + + return true; +} + +void IfcGeom::util::sequence_of_point_to_wire(const TColgp_SequenceOfPnt& p, TopoDS_Wire& w, bool close) { + BRepBuilderAPI_MakePolygon builder; + for (int i = 1; i <= p.Length(); ++i) { + builder.Add(p.Value(i)); + } + if (close) { + builder.Close(); + } + w = builder.Wire(); +} + +void IfcGeom::util::remove_collinear_points_from_loop(TColgp_SequenceOfPnt& polygon, bool closed, double tol) { + const int start = closed ? 1 : 2; + const int end = polygon.Length() - (closed ? 0 : 1); + std::vector to_remove(polygon.Length(), false); + for (int i = start; i <= end; ++i) { + const gp_Pnt& a = polygon.Value(((i - 2 + polygon.Length()) % polygon.Length()) + 1); + const gp_Pnt& b = polygon.Value(i); + const gp_Pnt& c = polygon.Value((i % polygon.Length()) + 1); + const gp_Vec d1 = c.XYZ() - a.XYZ(); + const gp_Vec d2 = b.XYZ() - a.XYZ(); + const double dt = d2.Dot(d1) / d1.Dot(d1); + const gp_Vec d3 = d1.Scaled(dt); + const gp_Pnt b2 = a.XYZ() + d3.XYZ(); + if (b.Distance(b2) < tol) { + to_remove[i - 1] = true; + } + } + for (int i = (int)to_remove.size() - 1; i >= 0; --i) { + if (to_remove[i]) { + polygon.Remove(i + 1); + } + } +} + +void IfcGeom::util::remove_duplicate_points_from_loop(TColgp_SequenceOfPnt& polygon, bool closed, double tol) { + tol *= tol; + + for (;;) { + bool removed = false; + int n = polygon.Length() - (closed ? 0 : 1); + for (int i = 1; i <= n; ++i) { + // wrap around to the first point in case of a closed loop + int j = (i % polygon.Length()) + 1; + double dist = polygon.Value(i).SquareDistance(polygon.Value(j)); + if (dist < tol) { + // do not remove the first or last point to + // maintain connectivity with other wires + if ((closed && j == 1) || (!closed && j == n)) polygon.Remove(i); + else polygon.Remove(j); + removed = true; + break; + } + } + if (!removed) break; + } +} + + +namespace { + + // Returns the vertex part of an TopoDS_Edge edge that is not TopoDS_Vertex vertex + TopoDS_Vertex find_other(const TopoDS_Edge& edge, const TopoDS_Vertex& vertex) { + TopExp_Explorer exp(edge, TopAbs_VERTEX); + while (exp.More()) { + if (!exp.Current().IsSame(vertex)) { + return TopoDS::Vertex(exp.Current()); + } + exp.Next(); + } + return TopoDS_Vertex(); + } + + TopoDS_Edge find_next(const TopTools_IndexedMapOfShape& edge_set, const TopTools_IndexedDataMapOfShapeListOfShape& vertex_to_edges, const TopoDS_Vertex& current, const TopoDS_Edge& previous_edge) { + const TopTools_ListOfShape& edges = vertex_to_edges.FindFromKey(current); + TopTools_ListIteratorOfListOfShape eit; + for (eit.Initialize(edges); eit.More(); eit.Next()) { + const TopoDS_Edge& edge = TopoDS::Edge(eit.Value()); + if (edge.IsSame(previous_edge)) continue; + if (edge_set.Contains(edge)) { + return edge; + } + } + return TopoDS_Edge(); + } + +} + +bool IfcGeom::util::fill_nonmanifold_wires_with_planar_faces(TopoDS_Shape& shape, double tol) { + BRepOffsetAPI_Sewing sew; + sew.Add(shape); + + TopTools_IndexedDataMapOfShapeListOfShape edge_to_faces; + TopTools_IndexedDataMapOfShapeListOfShape vertex_to_edges; + std::set visited; + TopTools_IndexedMapOfShape edge_set; + + TopExp::MapShapesAndAncestors(shape, TopAbs_EDGE, TopAbs_FACE, edge_to_faces); + + const int num_edges = edge_to_faces.Extent(); + for (int i = 1; i <= num_edges; ++i) { + const TopTools_ListOfShape& faces = edge_to_faces.FindFromIndex(i); + const int count = faces.Extent(); + // Find only the non-manifold edges: Edges that are only part of a + // single face and therefore part of the wire(s) we want to fill. + if (count == 1) { + const TopoDS_Shape& edge = edge_to_faces.FindKey(i); + TopExp::MapShapesAndAncestors(edge, TopAbs_VERTEX, TopAbs_EDGE, vertex_to_edges); + edge_set.Add(edge); + } + } + + const int num_verts = vertex_to_edges.Extent(); + TopoDS_Vertex first, current; + TopoDS_Edge previous_edge; + + // Now loop over all the vertices that are part of the wire(s) to be filled + for (int i = 1; i <= num_verts; ++i) { + first = current = TopoDS::Vertex(vertex_to_edges.FindKey(i)); + // We keep track of the vertices we already used + if (visited.find(vertex_to_edges.FindIndex(current)) != visited.end()) { + continue; + } + // Given these vertices, try to find closed loops and create new + // wires out of them. + BRepBuilderAPI_MakeWire w; + for (;;) { + visited.insert(vertex_to_edges.FindIndex(current)); + // Find the edge that the current vertex is part of and points + // away from the previous vertex (null for the first vertex). + TopoDS_Edge edge = find_next(edge_set, vertex_to_edges, current, previous_edge); + if (edge.IsNull()) { + return false; + } + TopoDS_Vertex other = find_other(edge, current); + if (other.IsNull()) { + // Dealing with a conical edge probably, for some reason + // this works better than adding the edge directly. + double u1, u2; + Handle(Geom_Curve) crv = BRep_Tool::Curve(edge, u1, u2); + w.Add(BRepBuilderAPI_MakeEdge(crv, u1, u2)); + break; + } else { + w.Add(edge); + } + // See if the starting point of this loop has been reached. Note that + // additional wires after this one potentially will be created. + if (other.IsSame(first)) { + break; + } + previous_edge = edge; + current = other; + } + sew.Add(BRepBuilderAPI_MakeFace(w)); + previous_edge.Nullify(); + } + + sew.Perform(); + shape = sew.SewedShape(); + + try { + ShapeFix_Solid solid; + solid.LimitTolerance(tol); + shape = solid.SolidFromShell(TopoDS::Shell(shape)); + } catch (const Standard_Failure& e) { + if (e.GetMessageString() && strlen(e.GetMessageString())) { + Logger::Error(e.GetMessageString()); + } else { + Logger::Error("Unknown error creating solid"); + } + } catch (...) { + Logger::Error("Unknown error creating solid"); + } + + return true; +} + + +bool IfcGeom::util::convert_curve_to_wire(const Handle(Geom_Curve)& curve, TopoDS_Wire& wire) { + try { + wire = BRepBuilderAPI_MakeWire(BRepBuilderAPI_MakeEdge(curve)); + return true; + } catch (const Standard_Failure& e) { + if (e.GetMessageString() && strlen(e.GetMessageString())) { + Logger::Error(e.GetMessageString()); + } else { + Logger::Error("Unknown error converting curve to wire"); + } + } catch (...) { + Logger::Error("Unknown error converting curve to wire"); + } + return false; +} + + +void IfcGeom::util::assert_closed_wire(TopoDS_Wire& wire, double tol) { + if (wire.Closed() == 0) { + TopoDS_Vertex v0, v1; + TopExp::Vertices(wire, v0, v1); + + gp_Pnt p1 = BRep_Tool::Pnt(v0); + gp_Pnt p2 = BRep_Tool::Pnt(v1); + + if (p1.Distance(p2) > tol) { + BRepBuilderAPI_MakeWire mw; + mw.Add(wire); + mw.Add(BRepBuilderAPI_MakeEdge(v0, v1).Edge()); + wire = mw.Wire(); + } + + Logger::Warning("Wire not closed"); + } +} + +bool IfcGeom::util::convert_wire_to_face(const TopoDS_Wire& w, TopoDS_Face& face, const IfcGeom::util::wire_tolerance_settings& settings) { + TopoDS_Wire wire = w; + + TopTools_ListOfShape results; + + if (settings.use_wire_intersection_check && util::wire_intersections(wire, results, settings)) { + Logger::Warning("Self-intersections with " + boost::lexical_cast(results.Extent()) + " cycles detected"); + util::select_largest(results, wire); + } + + bool is_2d = true; + TopExp_Explorer exp(wire, TopAbs_EDGE); + for (; exp.More(); exp.Next()) { + double a, b; + // @todo this does not handle fillets + Handle(Geom_Curve) crv = BRep_Tool::Curve(TopoDS::Edge(exp.Current()), a, b); + if (crv->DynamicType() != STANDARD_TYPE(Geom_Line)) { + is_2d = false; + break; + } + Handle(Geom_Line) line = Handle(Geom_Line)::DownCast(crv); + if (line->Lin().Direction().Z() > ALMOST_ZERO) { + is_2d = false; + break; + } + } + + if (!is_2d) { + // For 2d wires (e.g. profiles) a higher tolerance for plane fitting is never required. + ShapeFix_ShapeTolerance FTol; + FTol.SetTolerance(wire, settings.precision, TopAbs_WIRE); + } + + BRepBuilderAPI_MakeFace mf(wire, false); + BRepBuilderAPI_FaceError er = mf.Error(); + + if (er != BRepBuilderAPI_FaceDone) { + Logger::Error("Failed to create face."); + return false; + } + face = mf.Face(); + + return true; +} + +bool IfcGeom::util::convert_wire_to_faces(const TopoDS_Wire& w, TopoDS_Compound& faces, const IfcGeom::util::wire_tolerance_settings& settings) { + bool is_2d = true; + TopExp_Explorer exp(w, TopAbs_EDGE); + for (; exp.More(); exp.Next()) { + double a, b; + Handle(Geom_Curve) crv = BRep_Tool::Curve(TopoDS::Edge(exp.Current()), a, b); + if (crv->DynamicType() != STANDARD_TYPE(Geom_Line)) { + is_2d = false; + break; + } + Handle(Geom_Line) line = Handle(Geom_Line)::DownCast(crv); + if (line->Lin().Direction().Z() > ALMOST_ZERO) { + is_2d = false; + break; + } + } + + TopTools_ListOfShape results; + if (settings.use_wire_intersection_check && util::wire_intersections(w, results, settings)) { + Logger::Warning("Self-intersections with " + boost::lexical_cast(results.Extent()) + " cycles detected"); + } else { + results.Clear(); + results.Append(w); + } + + TopoDS_Compound C; + BRep_Builder B; + B.MakeCompound(faces); + + std::list> face_list; + double max_area = 0.; + + TopTools_ListIteratorOfListOfShape it(results); + for (; it.More(); it.Next()) { + const TopoDS_Wire& wire = TopoDS::Wire(it.Value()); + if (!is_2d) { + // For 2d wires (e.g. profiles) a higher tolerance for plane fitting is never required. + ShapeFix_ShapeTolerance FTol; + FTol.SetTolerance(wire, settings.precision, TopAbs_WIRE); + } + + BRepBuilderAPI_MakeFace mf(wire, false); + BRepBuilderAPI_FaceError er = mf.Error(); + + if (er != BRepBuilderAPI_FaceDone) { + Logger::Error("Failed to create face."); + continue; + } + + TopoDS_Face face = mf.Face(); + const double m = face_area(face); + + face_list.push_back({ m, face }); + if (m > max_area) { + max_area = m; + } + } + + for (auto& p : face_list) { + if (p.first >= max_area / 10.) { + B.Add(faces, p.second); + } else { + Logger::Warning("Ignoring self-intersection loop with area " + boost::lexical_cast(p.first)); + } + } + + return true; } \ No newline at end of file diff --git a/src/ifcgeom_schema_agnostic/wire_utils.h b/src/ifcgeom_schema_agnostic/wire_utils.h index 3d905a7338..582cc46a64 100644 --- a/src/ifcgeom_schema_agnostic/wire_utils.h +++ b/src/ifcgeom_schema_agnostic/wire_utils.h @@ -1,5 +1,16 @@ +#ifndef WIRE_UTILS_H +#define WIRE_UTILS_H + #include + +#include + #include +#include +#include +#include + +#include #include #include @@ -16,13 +27,34 @@ namespace IfcGeom { TRIANGULATE_WIRE_NON_MANIFOLD, }; + struct wire_tolerance_settings { + bool use_wire_intersection_check; + bool use_wire_intersection_tolerance; + double vertex_clustering_epsilon; + double precision; + }; + /// Triangulate the set of wires. The firstmost wire is assumed to be the outer wire. triangulate_wire_result triangulate_wire(const std::vector& wires, TopTools_ListOfShape& faces); - // eps: tolerance added to wire intersection checks, can be zero - // eps_real: tolerance used to construct new edge geometry around intersection points, cannot be zero - bool wire_intersections(const TopoDS_Wire& wire, TopTools_ListOfShape& wires, double eps, double eps_real); + bool wire_intersections(const TopoDS_Wire& wire, TopTools_ListOfShape& wires, const wire_tolerance_settings& settings); void select_largest(const TopTools_ListOfShape& shapes, TopoDS_Shape& largest); + + bool convert_wire_to_face(const TopoDS_Wire& wire, TopoDS_Face& face, const IfcGeom::util::wire_tolerance_settings& settings); + + bool convert_wire_to_faces(const TopoDS_Wire& wire, TopoDS_Compound& face, const IfcGeom::util::wire_tolerance_settings& settings); + + void assert_closed_wire(TopoDS_Wire& wire, double tol); + + bool fill_nonmanifold_wires_with_planar_faces(TopoDS_Shape& shape, double tol); + void remove_duplicate_points_from_loop(TColgp_SequenceOfPnt& polygon, bool closed, double tol); + void remove_collinear_points_from_loop(TColgp_SequenceOfPnt& polygon, bool closed, double tol); + bool wire_to_sequence_of_point(const TopoDS_Wire&, TColgp_SequenceOfPnt&); + void sequence_of_point_to_wire(const TColgp_SequenceOfPnt&, TopoDS_Wire&, bool closed); + + bool convert_curve_to_wire(const Handle(Geom_Curve)& curve, TopoDS_Wire& wire); } } + +#endif