diff --git a/src/ifcgeom/IfcGeom.h b/src/ifcgeom/IfcGeom.h index 30fd6b0627..ec98989770 100644 --- a/src/ifcgeom/IfcGeom.h +++ b/src/ifcgeom/IfcGeom.h @@ -289,6 +289,7 @@ public: void set_rotation(const std::array& rotation); 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::IfcBaseClass* L, IfcRepresentationShapeItems& result); IfcGeom::ShapeType shape_type(const IfcUtil::IfcBaseClass* L); diff --git a/src/ifcgeom/IfcGeomFaces.cpp b/src/ifcgeom/IfcGeomFaces.cpp index 512a749ac5..332b1321b0 100644 --- a/src/ifcgeom/IfcGeomFaces.cpp +++ b/src/ifcgeom/IfcGeomFaces.cpp @@ -447,8 +447,8 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcArbitraryClosedProfileDef* l, assert_closed_wire(wire); - TopoDS_Face f; - bool success = convert_wire_to_face(wire, f); + TopoDS_Compound f; + bool success = convert_wire_to_faces(wire, f); if (success) { face = f; } diff --git a/src/ifcgeom/IfcGeomFunctions.cpp b/src/ifcgeom/IfcGeomFunctions.cpp index 57a89e32c0..2cae09bceb 100644 --- a/src/ifcgeom/IfcGeomFunctions.cpp +++ b/src/ifcgeom/IfcGeomFunctions.cpp @@ -1139,6 +1139,76 @@ bool IfcGeom::Kernel::convert_wire_to_face(const TopoDS_Wire& w, TopoDS_Face& fa 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 (wire_intersections(w, results)) { + 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;