From 4842891fd75fa4023e08f9304e10c85c0dcb6478 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Sun, 18 Sep 2022 15:59:24 +0200 Subject: [PATCH] #2322 Implement IfcSweptDiskSolidPolygonal.FilletRadius --- src/ifcgeom/IfcSweptDiskSolid.cpp | 155 +++++++++++++++++++++++++++++- 1 file changed, 154 insertions(+), 1 deletion(-) diff --git a/src/ifcgeom/IfcSweptDiskSolid.cpp b/src/ifcgeom/IfcSweptDiskSolid.cpp index 533dff7381..1ae3aaffe5 100644 --- a/src/ifcgeom/IfcSweptDiskSolid.cpp +++ b/src/ifcgeom/IfcSweptDiskSolid.cpp @@ -1,4 +1,4 @@ -/******************************************************************************** +/******************************************************************************** * * * This file is part of IfcOpenShell. * * * @@ -27,12 +27,41 @@ #include #include #include +#include +#include +#include +#include + #include "../ifcgeom/IfcGeom.h" #include "../ifcgeom_schema_agnostic/sweep_utils.h" +#include "../ifcgeom_schema_agnostic/wire_utils.h" +#include "../ifcgeom_schema_agnostic/face_definition.h" #define Kernel MAKE_TYPE_NAME(Kernel) +namespace { + std::string format_edge(const TopoDS_Edge& e) { + std::ostringstream oss; + double _, __; + TopoDS_Vertex v0, v1; + + auto crv = BRep_Tool::Curve(e, _, __); + auto nm = crv->DynamicType()->Name(); + TopExp::Vertices(e, v0, v1, true); + + auto p0 = BRep_Tool::Pnt(v0); + auto p1 = BRep_Tool::Pnt(v1); + + oss << "Edge" << std::endl << std::setprecision(2) + << " (" << p0.X() << " " << p0.Y() << " " << p0.Z() << ")" << std::endl + << " (" << p1.X() << " " << p1.Y() << " " << p1.Z() << ")" << std::endl + << nm << std::endl; + + return oss.str(); + } +} + bool IfcGeom::Kernel::convert(const IfcSchema::IfcSweptDiskSolid* l, TopoDS_Shape& shape) { TopoDS_Wire wire, section1, section2; @@ -75,6 +104,130 @@ bool IfcGeom::Kernel::convert(const IfcSchema::IfcSweptDiskSolid* l, TopoDS_Shap } } + double fillet = 0.; + +#ifdef SCHEMA_HAS_IfcSweptDiskSolidPolygonal + if (l->as()) { + auto fr = l->as()->FilletRadius(); + if (fr) { + fillet = *fr; + } + } +#endif + + if (fillet > getValue(GV_PRECISION)) { + + if (util::is_polyhedron(wire)) { + BRep_Builder BB; + TopoDS_Vertex V; + std::vector sorted_edges; + util::sort_edges(wire, sorted_edges); + + if (sorted_edges.size() >= 2) { + size_t i = 0, j = 1; + + // If the wire is closed we need to cycle from end back to begin + while (j < (sorted_edges.size() + (wire.Closed() ? 1 : 0))) { + const TopoDS_Edge& a = sorted_edges[i]; + const TopoDS_Edge& b = sorted_edges[j % sorted_edges.size()]; + + /* + std::wcout << "INPUT:" << std::endl; + std::wcout << "a " << format_edge(a).c_str() << std::endl; + std::wcout << "b " << format_edge(b).c_str() << std::endl; + */ + + // @todo this code is duplicated with code from IfcFace, refactor + // Help Open Cascade by finding the plane more efficiently + // We have already asserted the wire is a polyhedron and edges are linear + double _, __; + Handle(Geom_Line) c1 = Handle(Geom_Line)::DownCast(BRep_Tool::Curve(a, _, __)); + Handle(Geom_Line) c2 = Handle(Geom_Line)::DownCast(BRep_Tool::Curve(b, _, __)); + + const gp_Vec ab = c1->Position().Direction(); + const gp_Vec ac = c2->Position().Direction(); + const gp_Vec cross = ab.Crossed(ac); + + if (cross.SquareMagnitude() > ALMOST_ZERO) { + const gp_Dir n = cross; + gp_Pln plane(c1->Position().Location(), n); + auto face = BRepBuilderAPI_MakeFace(plane).Face(); + + TopoDS_Wire wire; + BB.MakeWire(wire); + BB.Add(wire, a); + BB.Add(wire, b); + BB.Add(face, wire); + TopExp::CommonVertex(a, b, V); + + BRepFilletAPI_MakeFillet2d mf2d(face); + mf2d.AddFillet(V, fillet); + mf2d.Build(); + + if (mf2d.IsDone()) { + + auto new_wire = TopoDS::Wire(TopoDS_Iterator(mf2d.Shape()).Value()); + std::vector new_sorted_edges; + util::sort_edges(new_wire, new_sorted_edges); + + // B C B2 C + // o───────────────o o─────────────o + // │ / + // │ B1 o + // │ │ + // │ │ + // o o + // A A + + if (new_sorted_edges.size() == 3) { + // Delete AB + sorted_edges.erase(sorted_edges.begin() + i); + // Delete BC + sorted_edges.erase(sorted_edges.begin() + i); + + /* + std::wcout << "OUTPUT:" << std::endl; + for (auto& e : new_sorted_edges) { + std::wcout << " " << format_edge(e).c_str() << std::endl; + } + */ + + // Insert AB1 B1B2 B2C + sorted_edges.insert(sorted_edges.begin() + i, new_sorted_edges.begin(), new_sorted_edges.end()); + + // Number of edges increased, so an additional increment + i += 1; + j += 1; + } else { + Logger::Error("Unexpected amount of fillet edges generated"); + } + } else { + Logger::Error("Unable to build fillet, probably edge too short"); + } + } else { + Logger::Error("Colinear edges, not applying fillet"); + } + i++; + j++; + } + } else { + Logger::Error("Not enough edges for applying fillet"); + } + + TopoDS_Wire new_wire; + BB.MakeWire(new_wire); + + for (size_t i = 0; i < sorted_edges.size(); ++i) { + const auto& e = sorted_edges[i]; + BB.Add(new_wire, e); + } + + wire = new_wire; + } else { + Logger::Error("Directrix is not polyhedral, ignoring FilletRadius"); + } + } + // NB: Note that StartParam and EndParam param are ignored and the assumption is // made that the parametric range over which to be swept matches the IfcCurve in // its entirety.