From 884b3fe850f520a1d87ca810b440988b42824cf0 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Sat, 16 Apr 2016 14:47:09 +0200 Subject: [PATCH] Project wires onto a plane if non-planar. Fixes #57. --- src/ifcgeom/IfcGeom.h | 2 + src/ifcgeom/IfcGeomFaces.cpp | 15 ++++++- src/ifcgeom/IfcGeomFunctions.cpp | 74 ++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 2 deletions(-) diff --git a/src/ifcgeom/IfcGeom.h b/src/ifcgeom/IfcGeom.h index c593fbab95..1423da042b 100644 --- a/src/ifcgeom/IfcGeom.h +++ b/src/ifcgeom/IfcGeom.h @@ -128,6 +128,8 @@ public: 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); + bool approximate_plane_through_wire(const TopoDS_Wire&, gp_Pln&); + bool flatten_wire(TopoDS_Wire&); std::pair initializeUnits(IfcSchema::IfcUnitAssignment*); diff --git a/src/ifcgeom/IfcGeomFaces.cpp b/src/ifcgeom/IfcGeomFaces.cpp index e7fd6e03d0..9b7b99c2fd 100644 --- a/src/ifcgeom/IfcGeomFaces.cpp +++ b/src/ifcgeom/IfcGeomFaces.cpp @@ -202,7 +202,11 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& face) { ShapeFix_ShapeTolerance FTol; FTol.SetTolerance(wire, getValue(GV_PRECISION), TopAbs_WIRE); + bool flattened_wire = false; + if (!mf) { + process_wire: + if (face_surface.IsNull()) { mf = new BRepBuilderAPI_MakeFace(wire); } else { @@ -264,9 +268,16 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcFace* l, TopoDS_Shape& face) { success = true; } } else { - Logger::Message(Logger::LOG_ERROR, "Failed to process face boundary", bound->entity); + const bool non_planar = mf->Error() == BRepBuilderAPI_NotPlanar; delete mf; - return false; + if (!non_planar || flattened_wire || !flatten_wire(wire)) { + Logger::Message(Logger::LOG_ERROR, "Failed to process face boundary", bound->entity); + return false; + } else { + Logger::Message(Logger::LOG_ERROR, "Flattening face boundary", bound->entity); + flattened_wire = true; + goto process_wire; + } } } else { diff --git a/src/ifcgeom/IfcGeomFunctions.cpp b/src/ifcgeom/IfcGeomFunctions.cpp index 8123bcb041..e452fe4398 100644 --- a/src/ifcgeom/IfcGeomFunctions.cpp +++ b/src/ifcgeom/IfcGeomFunctions.cpp @@ -75,6 +75,8 @@ #include #include +#include + #include #include #include @@ -94,6 +96,7 @@ #include #include +#include #include #include @@ -1301,3 +1304,74 @@ const IfcSchema::IfcRepresentationItem* IfcGeom::Kernel::find_item_carrying_styl return item; } + +bool IfcGeom::Kernel::approximate_plane_through_wire(const TopoDS_Wire& wire, gp_Pln& plane) { + // Newell's Method is used for the normal calculation + // as a simple edge cross product can give opposite results + // for a concave face boundary. + // Reference: Graphics Gems III p. 231 + + double x = 0, y = 0, z = 0; + gp_Pnt current, previous, first; + gp_XYZ center; + int n = 0; + + BRepTools_WireExplorer exp(wire); + + for (;; exp.Next()) { + const bool has_more = exp.More(); + if (has_more) { + const TopoDS_Vertex& v = exp.CurrentVertex(); + current = BRep_Tool::Pnt(v); + center += current.XYZ(); + } else { + current = first; + } + if (n) { + const double& xn = previous.X(); + const double& yn = previous.Y(); + const double& zn = previous.Z(); + const double& xn1 = current.X(); + const double& yn1 = current.Y(); + const double& zn1 = current.Z(); + x += (yn - yn1)*(zn + zn1); + y += (xn + xn1)*(zn - zn1); + z += (xn - xn1)*(yn + yn1); + } else { + first = current; + } + if (!has_more) { + break; + } + previous = current; + ++n; + } + + if (n < 3) { + return false; + } + + plane = gp_Pln(center / n, gp_Dir(x, y, z)); + return true; +} + +bool IfcGeom::Kernel::flatten_wire(TopoDS_Wire& wire) { + gp_Pln pln; + if (!approximate_plane_through_wire(wire, pln)) { + return false; + } + TopoDS_Face face = BRepBuilderAPI_MakeFace(pln).Face(); + BRepAlgo_NormalProjection proj(face); + proj.Add(wire); + proj.Build(); + if (!proj.IsDone()) { + return false; + } + TopTools_ListOfShape list; + proj.BuildWire(list); + if (list.Extent() != 1) { + return false; + } + wire = TopoDS::Wire(list.First()); + return true; +} \ No newline at end of file