From 4354361a13caf6d00491b1db7a6bd1c6e609f7d2 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Fri, 9 Dec 2016 16:40:14 +0100 Subject: [PATCH] More elaborate wire ordering check and shell sewing code (#147) --- src/ifcgeom/IfcGeom.h | 1 + src/ifcgeom/IfcGeomFaces.cpp | 59 +++++++++++++++------ src/ifcgeom/IfcGeomFunctions.cpp | 90 ++++++++++++++++++++++++++------ src/ifcgeom/IfcGeomShapes.cpp | 38 ++------------ 4 files changed, 123 insertions(+), 65 deletions(-) diff --git a/src/ifcgeom/IfcGeom.h b/src/ifcgeom/IfcGeom.h index c555d68495..b2abcdcb5c 100644 --- a/src/ifcgeom/IfcGeom.h +++ b/src/ifcgeom/IfcGeom.h @@ -197,6 +197,7 @@ public: 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 create_solid_from_faces(const TopTools_ListOfShape& face_list, TopoDS_Shape& solid); 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); diff --git a/src/ifcgeom/IfcGeomFaces.cpp b/src/ifcgeom/IfcGeomFaces.cpp index f45042c506..d937848433 100644 --- a/src/ifcgeom/IfcGeomFaces.cpp +++ b/src/ifcgeom/IfcGeomFaces.cpp @@ -91,6 +91,9 @@ #include +#include +#include + #ifdef USE_IFC4 #include #include @@ -143,6 +146,8 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& face) { if (num_outer_bounds > 1) { builder.MakeCompound(compound); } + + TopTools_DataMapOfShapeInteger wire_senses; // The builder is initialized on the heap because of the various different moments // of initialization depending on the configuration of surfaces and boundaries. @@ -200,6 +205,8 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& face) { wire.Reverse(); } + wire_senses.Bind(wire.Oriented(TopAbs_FORWARD), same_sense ? TopAbs_FORWARD : TopAbs_REVERSED); + bool flattened_wire = false; if (!mf) { @@ -245,25 +252,9 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& face) { } ShapeFix_Face fix(mf->Face()); fix.FixOrientation(); - fix.Perform(); outer_face_bound = fix.Face(); } - // If the wires are reversed the face needs to be reversed as well in order - // to maintain the counter-clock-wise ordering of the bounding wire's vertices. - bool all_reversed = true; - TopoDS_Iterator jt(outer_face_bound, false); - for (; jt.More(); jt.Next()) { - const TopoDS_Wire& w = TopoDS::Wire(jt.Value()); - if ((w.Orientation() != TopAbs_REVERSED) == same_sense) { - all_reversed = false; - } - } - - if (all_reversed) { - outer_face_bound.Reverse(); - } - if (num_outer_bounds > 1) { builder.Add(compound, outer_face_bound); delete mf; mf = 0; @@ -307,11 +298,47 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& face) { if (success) { face = mf->Face(); } + + ShapeFix_Face sfs(TopoDS::Face(face)); + TopTools_DataMapOfShapeListOfShape wire_map; + sfs.FixOrientation(wire_map); + + TopoDS_Iterator jt(face, false); + for (; jt.More(); jt.Next()) { + const TopoDS_Wire& w = TopoDS::Wire(jt.Value()); + if (wire_map.IsBound(w)) { + const TopTools_ListOfShape& shapes = wire_map.Find(w); + TopTools_ListIteratorOfListOfShape it(shapes); + for (; it.More(); it.Next()) { + // Apparently the wire got reversed, so register it with opposite orientation in the map + wire_senses.Bind(it.Value(), wire_senses.Find(w) == TopAbs_FORWARD ? TopAbs_REVERSED : TopAbs_FORWARD); + } + } + } + + face = TopoDS::Face(sfs.Face()); } } } if (success) { + // If the wires are reversed the face needs to be reversed as well in order + // to maintain the counter-clock-wise ordering of the bounding wire's vertices. + if (num_bounds == 1 || true) { + bool all_reversed = true; + TopoDS_Iterator jt(face, false); + for (; jt.More(); jt.Next()) { + const TopoDS_Wire& w = TopoDS::Wire(jt.Value()); + if (!wire_senses.IsBound(w.Oriented(TopAbs_FORWARD)) || (w.Orientation() == wire_senses.Find(w.Oriented(TopAbs_FORWARD)))) { + all_reversed = false; + } + } + + if (all_reversed) { + face.Reverse(); + } + } + ShapeFix_ShapeTolerance FTol; FTol.SetTolerance(face, getValue(GV_PRECISION), TopAbs_FACE); } diff --git a/src/ifcgeom/IfcGeomFunctions.cpp b/src/ifcgeom/IfcGeomFunctions.cpp index bba83010ce..58db91d02e 100644 --- a/src/ifcgeom/IfcGeomFunctions.cpp +++ b/src/ifcgeom/IfcGeomFunctions.cpp @@ -126,6 +126,8 @@ #include +#include + #include #include "../ifcparse/IfcSIPrefix.h" @@ -141,26 +143,81 @@ #endif bool IfcGeom::Kernel::create_solid_from_compound(const TopoDS_Shape& compound, TopoDS_Shape& shape) { + TopTools_ListOfShape face_list; + TopExp_Explorer exp(compound, TopAbs_FACE); + for (; exp.More(); exp.Next()) { + TopoDS_Face face = TopoDS::Face(exp.Current()); + face_list.Append(face); + } + + 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 valid_shell = false; + + TopTools_ListIteratorOfListOfShape face_iterator; + BRepOffsetAPI_Sewing builder; builder.SetTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE)); builder.SetMaxTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE)); builder.SetMinTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE)); - TopExp_Explorer exp(compound,TopAbs_FACE); - if ( ! exp.More() ) return false; - for ( ; exp.More(); exp.Next() ) { - TopoDS_Face face = TopoDS::Face(exp.Current()); - builder.Add(face); + for (face_iterator.Initialize(face_list); face_iterator.More(); face_iterator.Next()) { + builder.Add(face_iterator.Value()); } - builder.Perform(); - shape = builder.SewedShape(); - if (shape.ShapeType() == TopAbs_SHELL) { - try { - ShapeFix_Solid sf_solid; - sf_solid.LimitTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE)); - shape = sf_solid.SolidFromShell(TopoDS::Shell(shape)); - } catch(...) {} + + try { + builder.Perform(); + shape = builder.SewedShape(); + valid_shell = BRepCheck_Analyzer(shape).IsValid() != 0; + } catch (...) {} + + 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.LimitTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE)); + TopoDS_Solid solid_shape = solid.SolidFromShell(TopoDS::Shell(exp.Current())); + 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 (...) {} + } + } catch (...) {} + + 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::Message(Logger::LOG_WARNING, "Multiple components in IfcConnectedFaceSet"); + } + B.Add(complete_shape, result_shape); + } + } + shape = complete_shape; + } else { + Logger::Message(Logger::LOG_WARNING, "Failed to sew faceset"); } - return true; + + return valid_shell; } bool IfcGeom::Kernel::is_compound(const TopoDS_Shape& shape) { @@ -176,7 +233,10 @@ const TopoDS_Shape& IfcGeom::Kernel::ensure_fit_for_subtraction(const TopoDS_Sha if (!is_comp) { return solid = shape; } - create_solid_from_compound(shape, solid); + + 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. diff --git a/src/ifcgeom/IfcGeomShapes.cpp b/src/ifcgeom/IfcGeomShapes.cpp index 70977a4143..d4abfa3ff9 100644 --- a/src/ifcgeom/IfcGeomShapes.cpp +++ b/src/ifcgeom/IfcGeomShapes.cpp @@ -643,49 +643,19 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcConnectedFaceSet* l, TopoDS_Sh if (face_list.Extent() == 0) { return false; } - - bool valid_shell = false; - TopTools_ListIteratorOfListOfShape face_iterator; - - if ( face_list.Extent() < getValue(GV_MAX_FACES_TO_SEW) ) { - BRepOffsetAPI_Sewing builder; - builder.SetTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE)); - builder.SetMaxTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE)); - builder.SetMinTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE)); - for (face_iterator.Initialize(face_list); face_iterator.More(); face_iterator.Next()) { - builder.Add(face_iterator.Value()); - } - try { - builder.Perform(); - shape = builder.SewedShape(); - valid_shell = BRepCheck_Analyzer(shape).IsValid() != 0; - } catch(...) {} - if (valid_shell) { - try { - ShapeFix_Solid solid; - solid.LimitTolerance(getValue(GV_POINT_EQUALITY_TOLERANCE)); - TopoDS_Solid solid_shape = solid.SolidFromShell(TopoDS::Shell(shape)); - if (!solid_shape.IsNull()) { - try { - BRepClass3d_SolidClassifier classifier(solid_shape); - shape = solid_shape; - } catch (...) {} - } - } catch(...) {} - } else { - Logger::Message(Logger::LOG_WARNING,"Failed to sew faceset:",l->entity); - } - } - if (!valid_shell) { + if (face_list.Extent() > getValue(GV_MAX_FACES_TO_SEW) || !create_solid_from_faces(face_list, shape)) { 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()); } shape = compound; } + return true; }