From d4efcd40f993c85e6240965bdf3055d3a531d82e Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Wed, 3 Jul 2024 14:36:35 +0200 Subject: [PATCH] Advanced brep, sweeps and various fixes #4848 #4895 --- src/ifcgeom/AbstractKernel.h | 27 +- src/ifcgeom/kernels/cgal/CgalKernel.cpp | 11 +- .../kernels/opencascade/OpenCascadeKernel.h | 6 + src/ifcgeom/kernels/opencascade/extrusion.cpp | 2 +- src/ifcgeom/kernels/opencascade/face.cpp | 253 +++++++++++++++--- src/ifcgeom/kernels/opencascade/loop.cpp | 46 ++-- src/ifcgeom/kernels/opencascade/matrix4.cpp | 2 +- src/ifcgeom/kernels/opencascade/shell.cpp | 21 +- src/ifcgeom/mapping/IfcEdge.cpp | 8 +- src/ifcgeom/mapping/IfcFace.cpp | 7 + .../mapping/IfcSurfaceCurveSweptAreaSolid.cpp | 2 +- .../mapping/IfcSurfaceOfLinearExtrusion.cpp | 36 +-- .../mapping/IfcSurfaceOfRevolution.cpp | 37 +-- src/ifcgeom/mapping/IfcSweptDiskSolid.cpp | 4 +- src/ifcgeom/taxonomy.cpp | 72 ++--- src/ifcgeom/taxonomy.h | 209 +++++++++++---- src/ifcwrap/IfcGeomWrapper.i | 11 +- src/ifcwrap/utils/type_conversion.i | 1 + src/ifcwrap/utils/typemaps_out.i | 1 + 19 files changed, 537 insertions(+), 219 deletions(-) diff --git a/src/ifcgeom/AbstractKernel.h b/src/ifcgeom/AbstractKernel.h index fbc83438a8..26d6f090b8 100644 --- a/src/ifcgeom/AbstractKernel.h +++ b/src/ifcgeom/AbstractKernel.h @@ -54,8 +54,9 @@ namespace ifcopenshell { namespace geometry { namespace kernels { virtual bool convert_impl(const taxonomy::bspline_surface::ptr, IfcGeom::ConversionResults&) { throw std::runtime_error("Not implemented"); } virtual bool convert_impl(const taxonomy::cylinder::ptr, IfcGeom::ConversionResults&) { throw std::runtime_error("Not implemented"); } virtual bool convert_impl(const taxonomy::sphere::ptr, IfcGeom::ConversionResults&) { throw std::runtime_error("Not implemented"); } + virtual bool convert_impl(const taxonomy::torus::ptr, IfcGeom::ConversionResults&) { throw std::runtime_error("Not implemented"); } virtual bool convert_impl(const taxonomy::solid::ptr, IfcGeom::ConversionResults&) { throw std::runtime_error("Not implemented"); } - virtual bool convert_impl(const taxonomy::surface_curve_sweep::ptr, IfcGeom::ConversionResults&) { throw std::runtime_error("Not implemented"); } + virtual bool convert_impl(const taxonomy::sweep_along_curve::ptr, IfcGeom::ConversionResults&) { throw std::runtime_error("Not implemented"); } virtual bool convert_impl(const taxonomy::loft::ptr, IfcGeom::ConversionResults&) { throw std::runtime_error("Not implemented"); } virtual bool convert_impl(const taxonomy::collection::ptr, IfcGeom::ConversionResults&); virtual bool convert_impl(const taxonomy::piecewise_function::ptr item, IfcGeom::ConversionResults& cs); @@ -107,7 +108,7 @@ namespace { static bool dispatch(const ifcopenshell::geometry::taxonomy::ptr item, T& visitor) { // @todo it should be possible to eliminate this dynamic_cast when there is a static equivalent to kind() auto v = ifcopenshell::geometry::taxonomy::template dcast>(item); - if (v) { + if (v && item->kind() == v->kind()) { visitor(v); return true; } else { @@ -123,6 +124,28 @@ namespace { return false; } }; + + /* A compile-time for loop over the curve kinds */ + template + struct dispatch_surface_creation { + static bool dispatch(const ifcopenshell::geometry::taxonomy::ptr item, T& visitor) { + auto v = ifcopenshell::geometry::taxonomy::template dcast>(item); + if (v && item->kind() == v->kind()) { + visitor(v); + return true; + } else { + return dispatch_surface_creation::dispatch(item, visitor); + } + } + }; + + template + struct dispatch_surface_creation { + static bool dispatch(const ifcopenshell::geometry::taxonomy::ptr item, T&) { + Logger::Error("No conversion for " + std::to_string(item->kind())); + return false; + } + }; } #endif \ No newline at end of file diff --git a/src/ifcgeom/kernels/cgal/CgalKernel.cpp b/src/ifcgeom/kernels/cgal/CgalKernel.cpp index a9180ea75b..507fb77c50 100644 --- a/src/ifcgeom/kernels/cgal/CgalKernel.cpp +++ b/src/ifcgeom/kernels/cgal/CgalKernel.cpp @@ -354,6 +354,10 @@ namespace { double u; typedef void result_type; + void operator()(const boost::blank&) { + throw std::runtime_error("Unbounded curve not supported here"); + } + void operator()(const taxonomy::point3::ptr& p) { point_projection_visitor_ v{ *p }; dispatch_curve_creation::dispatch(curve, v); @@ -1121,7 +1125,7 @@ bool CgalKernel::convert(const taxonomy::extrusion::ptr extrusion, cgal_shape_t } std::list bottom_face; - if (!convert(extrusion->basis, bottom_face) || bottom_face.size() != 1) { + if (!convert(taxonomy::cast(taxonomy::cast(extrusion->basis)), bottom_face) || bottom_face.size() != 1) { return false; } @@ -1542,8 +1546,9 @@ bool CgalKernel::process_as_2d_polygon(const taxonomy::boolean_result::ptr br, s try { std::transform(extrusions.begin(), extrusions.end(), std::back_inserter(wires), [this](extrusion_pair& p) { auto ex = p.second; - if (ex->basis->children.size() == 1 && ex->basis->children[0]->kind() == taxonomy::LOOP) { - auto l = (taxonomy::loop::ptr) ex->basis->children[0]; + auto ex_basis = taxonomy::cast(ex->basis); + if (ex_basis->children.size() == 1 && ex_basis->children[0]->kind() == taxonomy::LOOP) { + auto l = (taxonomy::loop::ptr) ex_basis->children[0]; cgal_wire_t w; cgal_placement_t trsf; convert_placement(ex->matrix, trsf); diff --git a/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.h b/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.h index 106604123a..29becd032f 100644 --- a/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.h +++ b/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.h @@ -135,6 +135,7 @@ public: bool convert(const ifcopenshell::geometry::taxonomy::solid::ptr, TopoDS_Shape&); bool convert(const ifcopenshell::geometry::taxonomy::loft::ptr, TopoDS_Shape&); bool convert(const ifcopenshell::geometry::taxonomy::bspline_surface::ptr bs, Handle(Geom_Surface) surf); + bool convert(const ifcopenshell::geometry::taxonomy::sweep_along_curve::ptr, TopoDS_Shape&); virtual bool convert_impl(const ifcopenshell::geometry::taxonomy::edge::ptr, IfcGeom::ConversionResults&); virtual bool convert_impl(const ifcopenshell::geometry::taxonomy::loop::ptr, IfcGeom::ConversionResults&); @@ -144,10 +145,15 @@ public: virtual bool convert_impl(const ifcopenshell::geometry::taxonomy::extrusion::ptr, IfcGeom::ConversionResults&); virtual bool convert_impl(const ifcopenshell::geometry::taxonomy::boolean_result::ptr, IfcGeom::ConversionResults&); virtual bool convert_impl(const ifcopenshell::geometry::taxonomy::loft::ptr, IfcGeom::ConversionResults&); + virtual bool convert_impl(const ifcopenshell::geometry::taxonomy::sweep_along_curve::ptr, IfcGeom::ConversionResults&); virtual bool convert_openings(const IfcUtil::IfcBaseEntity* entity, const std::vector>& openings, const IfcGeom::ConversionResults& entity_shapes, const ifcopenshell::geometry::taxonomy::matrix4& entity_trsf, IfcGeom::ConversionResults& cut_shapes); + typedef boost::variant curve_creation_visitor_result_type; + curve_creation_visitor_result_type convert_curve(const ifcopenshell::geometry::taxonomy::ptr); + Handle(Geom_Surface) convert_surface(const ifcopenshell::geometry::taxonomy::ptr); + template static T convert_xyz(const U& u) { const auto& vs = u.ccomponents(); diff --git a/src/ifcgeom/kernels/opencascade/extrusion.cpp b/src/ifcgeom/kernels/opencascade/extrusion.cpp index bfeaa021eb..0ee6ff8e2a 100644 --- a/src/ifcgeom/kernels/opencascade/extrusion.cpp +++ b/src/ifcgeom/kernels/opencascade/extrusion.cpp @@ -15,7 +15,7 @@ bool OpenCascadeKernel::convert(const taxonomy::extrusion::ptr extrusion, TopoDS } TopoDS_Shape face; - if (!convert(extrusion->basis, face)) { + if (!convert(taxonomy::cast(extrusion->basis), face)) { return false; } diff --git a/src/ifcgeom/kernels/opencascade/face.cpp b/src/ifcgeom/kernels/opencascade/face.cpp index ee78c120fa..3f4aee36f3 100644 --- a/src/ifcgeom/kernels/opencascade/face.cpp +++ b/src/ifcgeom/kernels/opencascade/face.cpp @@ -37,6 +37,17 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "OpenCascadeKernel.h" #include "face_definition.h" @@ -48,6 +59,173 @@ using namespace ifcopenshell::geometry::kernels; using namespace IfcGeom; using namespace IfcGeom::util; + +namespace { + struct surface_creation_visitor { + OpenCascadeKernel* kernel; + Handle(Geom_Surface) result; + + Handle(Geom_Surface) operator()(const taxonomy::bspline_surface::ptr& bs) { + auto& cps = bs->control_points; + if (!cps.size() || !cps[0].size()) { + throw std::runtime_error("Empty control point array"); + } + + auto& uknots = bs->knots[0]; + auto& vknots = bs->knots[1]; + auto& umults = bs->multiplicities[0]; + auto& vmults = bs->multiplicities[1]; + + TColgp_Array2OfPnt Poles(0, (int)cps.size() - 1, 0, (int)cps[0].size() - 1); + TColStd_Array1OfReal UKnots(0, (int)uknots.size() - 1); + TColStd_Array1OfReal VKnots(0, (int)vknots.size() - 1); + TColStd_Array1OfInteger UMults(0, (int)umults.size() - 1); + TColStd_Array1OfInteger VMults(0, (int)vmults.size() - 1); + Standard_Integer UDegree = bs->degree[0]; + Standard_Integer VDegree = bs->degree[1]; + + int i = 0, j; + for (auto it = cps.begin(); it != cps.end(); ++it, ++i) { + j = 0; + for (auto jt = (*it).begin(); jt != (*it).end(); ++jt, ++j) { + Poles(i, j) = kernel->convert_xyz(**jt); + } + } + i = 0; + for (std::vector::const_iterator it = uknots.begin(); it != uknots.end(); ++it, ++i) { + UKnots(i) = *it; + } + i = 0; + for (std::vector::const_iterator it = vknots.begin(); it != vknots.end(); ++it, ++i) { + VKnots(i) = *it; + } + i = 0; + for (std::vector::const_iterator it = umults.begin(); it != umults.end(); ++it, ++i) { + UMults(i) = *it; + } + i = 0; + for (std::vector::const_iterator it = vmults.begin(); it != vmults.end(); ++it, ++i) { + VMults(i) = *it; + } + return result = Handle(Geom_Surface)(new Geom_BSplineSurface(Poles, UKnots, VKnots, UMults, VMults, UDegree, VDegree)); + } + + Handle(Geom_Surface) operator()(const taxonomy::plane::ptr& p) { + const auto& m = p->matrix->ccomponents(); + return result = Handle(Geom_Surface)(new Geom_Plane( + OpenCascadeKernel::convert_xyz2(m.col(3)), + OpenCascadeKernel::convert_xyz2(m.col(2)))); + } + + Handle(Geom_Surface) operator()(const taxonomy::cylinder::ptr& c) { + const auto& m = c->matrix->ccomponents(); + return result = Handle(Geom_Surface)(new Geom_CylindricalSurface(gp_Ax3( + OpenCascadeKernel::convert_xyz2(m.col(3)), + OpenCascadeKernel::convert_xyz2(m.col(2)), + OpenCascadeKernel::convert_xyz2(m.col(0)) + ), c->radius)); + } + + Handle(Geom_Surface) operator()(const taxonomy::sphere::ptr& s) { + const auto& m = s->matrix->ccomponents(); + return result = Handle(Geom_Surface)(new Geom_SphericalSurface(gp_Ax3( + OpenCascadeKernel::convert_xyz2(m.col(3)), + OpenCascadeKernel::convert_xyz2(m.col(2)), + OpenCascadeKernel::convert_xyz2(m.col(0)) + ), s->radius)); + } + + Handle(Geom_Surface) operator()(const taxonomy::torus::ptr& t) { + const auto& m = t->matrix->ccomponents(); + return result = Handle(Geom_Surface)(new Geom_ToroidalSurface(gp_Ax3( + OpenCascadeKernel::convert_xyz2(m.col(3)), + OpenCascadeKernel::convert_xyz2(m.col(2)), + OpenCascadeKernel::convert_xyz2(m.col(0)) + ), t->radius1, t->radius2)); + } + + TopoDS_Wire get_edges_as_wire(const taxonomy::item::ptr& i) { + // It's a bit more convenient to use high level BRepPrimAPI calls that operate on + // topology. On a single edge that will create a Geom_TrimmedCurve for us. + auto crv_or_wire = kernel->convert_curve(i); + if (crv_or_wire.which() == 1) { + const auto& w = boost::get(crv_or_wire); + return w; + } else { + throw std::runtime_error("Unexpected curve evaluation"); + } + } + + Handle(Geom_Curve) get_curve(const taxonomy::item::ptr& i) { + // @todo unify with trimmed curve handling + auto crv_or_wire = kernel->convert_curve(i); + if (crv_or_wire.which() == 0) { + return boost::get(crv_or_wire); + } else { + // @todo + const double precision_ = 1.e-5; + Logger::Warning("Approximating BasisCurve due to possible discontinuities", i->instance); + const auto& w = boost::get(crv_or_wire); +#if OCC_VERSION_HEX < 0x70600 + BRepAdaptor_CompCurve cc(w, true); + Handle(Adaptor3d_HCurve) hcc = Handle(Adaptor3d_HCurve)(new BRepAdaptor_HCompCurve(cc)); +#else + auto hcc = new BRepAdaptor_CompCurve(w, true); +#endif + // @todo, arbitrary numbers here, note they cannot be too high as contiguous memory is allocated based on them. + Approx_Curve3d approx(hcc, precision_, GeomAbs_C0, 10, 10); + return approx.Curve(); + } + } + + Handle(Geom_Surface) operator()(const taxonomy::extrusion::ptr& e) { + auto crv = get_curve(e->basis); + return result = Handle(Geom_Surface)(new Geom_SurfaceOfLinearExtrusion( + crv, + OpenCascadeKernel::convert_xyz(*e->direction) + )); + } + + Handle(Geom_Surface) operator()(const taxonomy::revolve::ptr& e) { + gp_Ax1 ax( + OpenCascadeKernel::convert_xyz(*e->axis_origin), + OpenCascadeKernel::convert_xyz(*e->direction)); + + if (e->basis && (e->basis->kind() == taxonomy::EDGE || (e->basis->kind() == taxonomy::LOOP && taxonomy::cast(e->basis)->children.size() == 1))) { + auto e_basis = e->basis; + if ((e->basis->kind() == taxonomy::LOOP)) { + e_basis = taxonomy::cast(e->basis)->children[0]; + } + auto w = get_edges_as_wire(e_basis); + TopoDS_Shape shp = BRepPrimAPI_MakeRevol(w, ax); + TopExp_Explorer exp(shp, TopAbs_FACE); + if (exp.More()) { + Handle(Geom_Surface) surf = BRep_Tool::Surface(TopoDS::Face(exp.Current())); + exp.Next(); + if (!exp.More()) { + return result = surf; + } + } + // fall back to approach below + } + + auto crv = get_curve(e->basis); + return result = Handle(Geom_Surface)(new Geom_SurfaceOfRevolution( + crv, ax + )); + } + }; +} + +Handle(Geom_Surface) OpenCascadeKernel::convert_surface(const taxonomy::ptr surface) { + surface_creation_visitor v{ this }; + if (dispatch_surface_creation::dispatch(surface, v)) { + return v.result; + } else { + throw std::runtime_error("No surface created"); + } +} + bool OpenCascadeKernel::convert(const taxonomy::face::ptr face, TopoDS_Shape& result) { #ifdef IFOPSH_DEBUG std::ostringstream oss; @@ -58,24 +236,9 @@ bool OpenCascadeKernel::convert(const taxonomy::face::ptr face, TopoDS_Shape& re face_definition fd; - const bool is_face_surface = false; /* todo */ - - /* - if (is_face_surface) { - IfcSchema::IfcFaceSurface* fs = (IfcSchema::IfcFaceSurface*) l; - fs->FaceSurface(); - // FIXME: Surfaces are interpreted as a TopoDS_Shape - TopoDS_Shape surface_shape; - if (!convert_shape(fs->FaceSurface(), surface_shape)) return false; - - // FIXME: Assert this obtains the only face - TopExp_Explorer exp(surface_shape, TopAbs_FACE); - if (!exp.More()) return false; - - TopoDS_Face surface = TopoDS::Face(exp.Current()); - fd.surface() = BRep_Tool::Surface(surface); + if (face->basis) { + fd.surface() = convert_surface(face->basis); } - */ const int num_bounds = face->children.size(); int num_outer_bounds = 0; @@ -198,11 +361,22 @@ bool OpenCascadeKernel::convert(const taxonomy::face::ptr face, TopoDS_Shape& re if (fd.all_outer()) { for (const auto& w : fd.wires()) { TopTools_ListOfShape fl; - triangulate_wire({ w }, fl); + auto r = triangulate_wire({ w }, fl); + if (r == util::TRIANGULATE_WIRE_FAIL) { + continue; + } face_list.Append(fl); + if (faceset_helper_ && r == util::TRIANGULATE_WIRE_NON_MANIFOLD) { + faceset_helper_->non_manifold() = true; + } } } else { - triangulate_wire(fd.wires(), face_list); + auto r = triangulate_wire(fd.wires(), face_list); + if (r != util::TRIANGULATE_WIRE_FAIL) { + if (faceset_helper_ && r == util::TRIANGULATE_WIRE_NON_MANIFOLD) { + faceset_helper_->non_manifold() = true; + } + } } } else if (!fd.all_outer()) { BRepBuilderAPI_MakeFace mf(fd.surface(), fd.outer_wire()); @@ -210,13 +384,20 @@ bool OpenCascadeKernel::convert(const taxonomy::face::ptr face, TopoDS_Shape& re if (mf.IsDone()) { // Is this necessary TopoDS_Face f = mf.Face(); - mf.Init(f); + if (std::distance(fd.inner_wires().first, fd.inner_wires().second)) { + mf.Init(f); - for (auto it = fd.inner_wires().first; it != fd.inner_wires().second; ++it) { - mf.Add(*it); + for (auto it = fd.inner_wires().first; it != fd.inner_wires().second; ++it) { + mf.Add(*it); + } + + face_list.Append(mf.Face()); + } else { + face_list.Append(f); } - - face_list.Append(mf.Face()); + } else { + Logger::Error("Internal error in face creation"); + return false; } } else { for (const auto& w : fd.wires()) { @@ -237,12 +418,24 @@ bool OpenCascadeKernel::convert(const taxonomy::face::ptr face, TopoDS_Shape& re // For planar faces, Open Cascade generates p-curves on the fly. for (TopTools_ListIteratorOfListOfShape it(face_list); it.More(); it.Next()) { - // Small chance there are multiple faces - const TopoDS_Face& occ_face = TopoDS::Face(it.Value()); - for (TopExp_Explorer exp2(occ_face, TopAbs_EDGE); exp2.More(); exp2.Next()) { - const TopoDS_Edge& edge = TopoDS::Edge(exp2.Current()); - ShapeFix_Edge fix_edge; - fix_edge.FixAddPCurve(edge, occ_face, false, precision_); + ShapeFix_Shape sfs(it.Value()); + + Handle(ShapeExtend_MsgRegistrator) msg; + msg = new ShapeExtend_MsgRegistrator; + sfs.SetMsgRegistrator(msg); + + sfs.Perform(); + it.Value() = sfs.Shape(); + + ShapeExtend_DataMapIteratorOfDataMapOfShapeListOfMsg jt(msg->MapShape()); + for (; jt.More(); jt.Next()) { + Message_ListIteratorOfListOfMsg kt(jt.Value()); + for (; kt.More(); kt.Next()) { + char* c = new char[kt.Value().Value().LengthOfCString() + 1]; + kt.Value().Value().ToUTF8CString(c); + Logger::Notice(c, face->instance); + delete[] c; + } } } } diff --git a/src/ifcgeom/kernels/opencascade/loop.cpp b/src/ifcgeom/kernels/opencascade/loop.cpp index ca50395072..1320ad2663 100644 --- a/src/ifcgeom/kernels/opencascade/loop.cpp +++ b/src/ifcgeom/kernels/opencascade/loop.cpp @@ -30,14 +30,11 @@ using namespace IfcGeom; using namespace IfcGeom::util; namespace { - typedef boost::variant curve_creation_visitor_result_type; - curve_creation_visitor_result_type convert_curve(OpenCascadeKernel* kernel, const taxonomy::ptr curve); - struct curve_creation_visitor { OpenCascadeKernel* kernel; - curve_creation_visitor_result_type result; + OpenCascadeKernel::curve_creation_visitor_result_type result; - curve_creation_visitor_result_type operator()(const taxonomy::bspline_curve::ptr& bc) { + OpenCascadeKernel::curve_creation_visitor_result_type operator()(const taxonomy::bspline_curve::ptr& bc) { const bool is_rational = !!bc->weights; @@ -81,28 +78,28 @@ namespace { } } - curve_creation_visitor_result_type operator()(const taxonomy::line::ptr& l) { + OpenCascadeKernel::curve_creation_visitor_result_type operator()(const taxonomy::line::ptr& l) { const auto& m = l->matrix->ccomponents(); return result = Handle(Geom_Curve)(new Geom_Line(OpenCascadeKernel::convert_xyz2(m.col(3)), OpenCascadeKernel::convert_xyz2(m.col(2)))); } - curve_creation_visitor_result_type operator()(const taxonomy::circle::ptr& c) { + OpenCascadeKernel::curve_creation_visitor_result_type operator()(const taxonomy::circle::ptr& c) { const auto& m = c->matrix->ccomponents(); return result = Handle(Geom_Curve)(new Geom_Circle(gp_Ax2(OpenCascadeKernel::convert_xyz2(m.col(3)), OpenCascadeKernel::convert_xyz2(m.col(2)), OpenCascadeKernel::convert_xyz2(m.col(0))), c->radius)); } - curve_creation_visitor_result_type operator()(const taxonomy::ellipse::ptr& e) { + OpenCascadeKernel::curve_creation_visitor_result_type operator()(const taxonomy::ellipse::ptr& e) { const auto& m = e->matrix->ccomponents(); return result = Handle(Geom_Curve)(new Geom_Ellipse(gp_Ax2(OpenCascadeKernel::convert_xyz2(m.col(3)), OpenCascadeKernel::convert_xyz2(m.col(2)), OpenCascadeKernel::convert_xyz2(m.col(0))), e->radius, e->radius2)); } - curve_creation_visitor_result_type operator()(const taxonomy::loop::ptr& l) { + OpenCascadeKernel::curve_creation_visitor_result_type operator()(const taxonomy::loop::ptr& l) { TopoDS_Wire wire; kernel->convert(l, wire); return result = wire; } - curve_creation_visitor_result_type operator()(const taxonomy::edge::ptr& e) { + OpenCascadeKernel::curve_creation_visitor_result_type operator()(const taxonomy::edge::ptr& e) { // @todo for polyloops/-lines we should probably construct edges based on correct oriented TopoDS_Vertex instead. if (e->start.which() != e->end.which()) { @@ -117,7 +114,7 @@ namespace { // to make sure we select the correct arc later on. e_basis = taxonomy::cast(e_basis)->basis; } - auto crv_or_wire = convert_curve(kernel, e_basis); + auto crv_or_wire = kernel->convert_curve(e_basis); Handle(Geom_Curve) curve; if (crv_or_wire.which() == 0) { curve = boost::get(crv_or_wire); @@ -149,15 +146,17 @@ namespace { // @todo, copy over logic from previous IfcTrimmedCurve handling if (e_start.which() == 0) { + E = BRepBuilderAPI_MakeEdge(curve).Edge(); + } else if (e_start.which() == 1) { auto p1 = OpenCascadeKernel::convert_xyz(*boost::get(e_start)); auto p2 = OpenCascadeKernel::convert_xyz(*boost::get(e_end)); E = BRepBuilderAPI_MakeEdge(curve, p1, p2).Edge(); - } else { + } else if (e_start.which() == 2) { auto v1 = boost::get(e_start); auto v2 = boost::get(e_end); - if (is_conic && ALMOST_THE_SAME(fmod(v2 - v1, M_PI*2.), 0.)) { + if (is_conic && ALMOST_THE_SAME(fmod(v2 - v1, M_PI * 2.), 0.)) { E = BRepBuilderAPI_MakeEdge(curve).Edge(); } else { E = BRepBuilderAPI_MakeEdge(curve, v1, v2).Edge(); @@ -176,7 +175,7 @@ namespace { E.Reverse(); } } else { - if (e->start.which() != 0) { + if (e->start.which() != 1) { throw std::runtime_error("Non-cartesian trim on edge without curve"); } auto p1 = OpenCascadeKernel::convert_xyz(*boost::get(e->start)); @@ -203,19 +202,20 @@ namespace { return result = W; } - curve_creation_visitor_result_type operator()(const taxonomy::offset_curve::ptr&) { + OpenCascadeKernel::curve_creation_visitor_result_type operator()(const taxonomy::offset_curve::ptr&) { // @todo throw std::runtime_error("Offset curves not supported as part of loop"); } }; - curve_creation_visitor_result_type convert_curve(OpenCascadeKernel* kernel, const taxonomy::ptr curve) { - curve_creation_visitor v{ kernel }; - if (dispatch_curve_creation::dispatch(curve, v)) { - return v.result; - } else { - throw std::runtime_error("No curve created"); - } +} + +OpenCascadeKernel::curve_creation_visitor_result_type OpenCascadeKernel::convert_curve(const taxonomy::ptr curve) { + curve_creation_visitor v{ this }; + if (dispatch_curve_creation::dispatch(curve, v)) { + return v.result; + } else { + throw std::runtime_error("No curve created"); } } @@ -367,7 +367,7 @@ bool OpenCascadeKernel::convert_impl(const taxonomy::loop::ptr loop, IfcGeom::Co } bool OpenCascadeKernel::convert_impl(const taxonomy::edge::ptr edge, IfcGeom::ConversionResults& results) { - TopoDS_Wire shape = boost::get(convert_curve(this, edge)); + TopoDS_Wire shape = boost::get(convert_curve(edge)); results.emplace_back(ConversionResult( edge->instance->data().id(), diff --git a/src/ifcgeom/kernels/opencascade/matrix4.cpp b/src/ifcgeom/kernels/opencascade/matrix4.cpp index 7559915f37..f7eed3065a 100644 --- a/src/ifcgeom/kernels/opencascade/matrix4.cpp +++ b/src/ifcgeom/kernels/opencascade/matrix4.cpp @@ -15,7 +15,7 @@ bool OpenCascadeKernel::convert(const taxonomy::matrix4::ptr matrix, gp_GTrsf& t ); if (matrix->instance && matrix->instance->declaration().name() == "IfcCartesianTransformationOperator3DnonUniform") { - std::wcout << "non uniform" << std::endl; + // std::wcout << "non uniform" << std::endl; } // @nb SetVectorialPart() sets gp_GTrsf.scale to 0.0, causing an non-invertable diff --git a/src/ifcgeom/kernels/opencascade/shell.cpp b/src/ifcgeom/kernels/opencascade/shell.cpp index 21777c531d..ef1115a49c 100644 --- a/src/ifcgeom/kernels/opencascade/shell.cpp +++ b/src/ifcgeom/kernels/opencascade/shell.cpp @@ -7,9 +7,28 @@ using namespace ifcopenshell::geometry::kernels; using namespace IfcGeom; using namespace IfcGeom::util; +namespace { + // @todo move into taxonomy; + bool shell_polyhedral(const taxonomy::shell::ptr& sh) { + for (auto& f : sh->children) { + for (auto& w : f->children) { + if (!w->is_polyhedron()) { + return false; + } + } + if (f->basis && f->basis->kind() != taxonomy::PLANE) { + return false; + } + } + return true; + } +} + bool OpenCascadeKernel::convert(const taxonomy::shell::ptr l, TopoDS_Shape& shape) { std::unique_ptr helper_scope; - helper_scope.reset(new faceset_helper(this, l)); + if (shell_polyhedral(l)) { + helper_scope.reset(new faceset_helper(this, l)); + } faceset_helper_ = helper_scope.get(); diff --git a/src/ifcgeom/mapping/IfcEdge.cpp b/src/ifcgeom/mapping/IfcEdge.cpp index fdb6538cd9..8fb2feebe6 100644 --- a/src/ifcgeom/mapping/IfcEdge.cpp +++ b/src/ifcgeom/mapping/IfcEdge.cpp @@ -40,7 +40,13 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcEdge* inst) { e->end = taxonomy::cast(map(pnt2)); if (inst->as()) { - e->basis = map(inst->as()->EdgeGeometry()); + auto basis = map(inst->as()->EdgeGeometry()); + auto loop = taxonomy::dcast(basis); + if (loop && loop->children.size() == 1) { + loop->calculate_linear_edge_curves(); + basis = loop->children[0]->basis; + } + e->basis = basis; e->curve_sense = inst->as()->SameSense(); } diff --git a/src/ifcgeom/mapping/IfcFace.cpp b/src/ifcgeom/mapping/IfcFace.cpp index 4cb049b681..60f08b3c88 100644 --- a/src/ifcgeom/mapping/IfcFace.cpp +++ b/src/ifcgeom/mapping/IfcFace.cpp @@ -41,6 +41,13 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcFace* inst) { face->children.push_back(r); } } + + auto face_surface = inst->as(); + + if (face_surface) { + face->basis = map(face_surface->FaceSurface()); + } + if (face->children.empty()) { #ifdef TAXONOMY_USE_NAKED_PTR delete face; diff --git a/src/ifcgeom/mapping/IfcSurfaceCurveSweptAreaSolid.cpp b/src/ifcgeom/mapping/IfcSurfaceCurveSweptAreaSolid.cpp index e99b5da83f..c379c15eeb 100644 --- a/src/ifcgeom/mapping/IfcSurfaceCurveSweptAreaSolid.cpp +++ b/src/ifcgeom/mapping/IfcSurfaceCurveSweptAreaSolid.cpp @@ -33,7 +33,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSurfaceCurveSweptAreaSolid* matrix = taxonomy::cast(map(inst->Position())); } - auto scs = taxonomy::make(matrix, f, map(inst->ReferenceSurface()), map(inst->Directrix())); + auto scs = taxonomy::make(matrix, f, map(inst->ReferenceSurface()), map(inst->Directrix())); scs->matrix = matrix; return scs; diff --git a/src/ifcgeom/mapping/IfcSurfaceOfLinearExtrusion.cpp b/src/ifcgeom/mapping/IfcSurfaceOfLinearExtrusion.cpp index b18c56b73d..f16df01ee8 100644 --- a/src/ifcgeom/mapping/IfcSurfaceOfLinearExtrusion.cpp +++ b/src/ifcgeom/mapping/IfcSurfaceOfLinearExtrusion.cpp @@ -23,37 +23,19 @@ using namespace ifcopenshell::geometry; taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSurfaceOfLinearExtrusion* inst) { return nullptr; - - /* - TopoDS_Wire wire; - if ( !convert_wire(inst->SweptCurve(), wire) ) { - TopoDS_Face face; - if ( !convert_face(inst->SweptCurve(),face) ) return false; - TopExp_Explorer exp(face, TopAbs_WIRE); - wire = TopoDS::Wire(exp.Current()); - } - const double height = inst->Depth() * length_unit_; - - gp_Trsf trsf; + taxonomy::matrix4::ptr matrix; bool has_position = true; -#ifdef SCHEMA_IfcSweptSurface_Position_IS_OPTIONAL +#ifdef SCHEMA_IfcSweptAreaSolid_Position_IS_OPTIONAL has_position = inst->Position() != nullptr; #endif if (has_position) { - IfcGeom::Kernel::convert(inst->Position(), trsf); + matrix = taxonomy::cast(map(inst->Position())); } - gp_Dir dir; - convert(inst->ExtrudedDirection(),dir); - - shape = BRepPrimAPI_MakePrism(wire, height*dir); - - if (has_position) { - // IfcSweptSurface.Position (trsf) is an IfcAxis2Placement3D - // and therefore has a unit scale factor - shape.Move(trsf); - } - - return !shape.IsNull(); - */ + return taxonomy::make( + matrix, + taxonomy::cast(map(inst->SweptCurve())), + taxonomy::cast(map(inst->ExtrudedDirection())), + std::numeric_limits::infinity() + ); } diff --git a/src/ifcgeom/mapping/IfcSurfaceOfRevolution.cpp b/src/ifcgeom/mapping/IfcSurfaceOfRevolution.cpp index b7043e9e4f..f83c4ecf03 100644 --- a/src/ifcgeom/mapping/IfcSurfaceOfRevolution.cpp +++ b/src/ifcgeom/mapping/IfcSurfaceOfRevolution.cpp @@ -22,37 +22,20 @@ using namespace ifcopenshell::geometry; taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSurfaceOfRevolution* inst) { - return nullptr; - - /* - TopoDS_Wire wire; - if ( !convert_wire(inst->SweptCurve(), wire) ) { - TopoDS_Face face; - if ( !convert_face(inst->SweptCurve(),face) ) return false; - TopExp_Explorer exp(face, TopAbs_WIRE); - wire = TopoDS::Wire(exp.Current()); - } - - gp_Ax1 ax1; - IfcGeom::Kernel::convert(inst->AxisPosition(), ax1); - - gp_Trsf trsf; + taxonomy::matrix4::ptr matrix; bool has_position = true; -#ifdef SCHEMA_IfcSweptSurface_Position_IS_OPTIONAL +#ifdef SCHEMA_IfcSweptAreaSolid_Position_IS_OPTIONAL has_position = inst->Position() != nullptr; #endif if (has_position) { - IfcGeom::Kernel::convert(inst->Position(), trsf); - } - - shape = BRepPrimAPI_MakeRevol(wire, ax1); - - if (has_position) { - // IfcSweptSurface.Position (trsf) is an IfcAxis2Placement3D - // and therefore has a unit scale factor - shape.Move(trsf); + matrix = taxonomy::cast(map(inst->Position())); } - return !shape.IsNull(); - */ + return taxonomy::make( + matrix, + taxonomy::cast(map(inst->SweptCurve())), + taxonomy::cast(map(inst->AxisPosition()->Location())), + taxonomy::cast(map(inst->AxisPosition()->Axis())), + boost::none + ); } diff --git a/src/ifcgeom/mapping/IfcSweptDiskSolid.cpp b/src/ifcgeom/mapping/IfcSweptDiskSolid.cpp index e70cfea746..1eec8c6bc7 100644 --- a/src/ifcgeom/mapping/IfcSweptDiskSolid.cpp +++ b/src/ifcgeom/mapping/IfcSweptDiskSolid.cpp @@ -94,6 +94,8 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSweptDiskSolid* inst) { e->basis = c; e->start = 0.; e->end = 2 * boost::math::constants::pi(); + // @todo allow identity by leaving unspecified? + c->matrix = taxonomy::make(); auto l = taxonomy::make(); l->children = { e }; @@ -103,7 +105,7 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcSweptDiskSolid* inst) { } } - return taxonomy::make(taxonomy::make(), f, nullptr, loop); + return taxonomy::make(taxonomy::make(), f, nullptr, loop); diff --git a/src/ifcgeom/taxonomy.cpp b/src/ifcgeom/taxonomy.cpp index 0783621ff9..6616785f7a 100644 --- a/src/ifcgeom/taxonomy.cpp +++ b/src/ifcgeom/taxonomy.cpp @@ -102,13 +102,14 @@ namespace { } } - int compare(const boost::variant& a, const boost::variant& b) { + int compare(const boost::variant& a, const boost::variant& b) { bool a_lt_b, b_lt_a; if (a.which() == 0) { + return 0; + } else if (a.which() == 1) { a_lt_b = compare(*boost::get(a), *boost::get(b)); b_lt_a = compare(*boost::get(b), *boost::get(a)); - } - else { + } else { a_lt_b = std::less()(boost::get(a), boost::get(b)); b_lt_a = std::less()(boost::get(b), boost::get(a)); } @@ -153,7 +154,11 @@ namespace { throw std::runtime_error("not implemented"); } - bool compare(const surface_curve_sweep&, const surface_curve_sweep&) { + bool compare(const torus&, const torus&) { + throw std::runtime_error("not implemented"); + } + + bool compare(const sweep_along_curve&, const sweep_along_curve&) { throw std::runtime_error("not implemented"); } @@ -552,7 +557,7 @@ const std::string& ifcopenshell::geometry::taxonomy::kind_to_string(kinds k) { using namespace std::string_literals; static std::string values[] = { - "matrix4"s, "point3"s, "direction3"s, "line"s, "circle"s, "ellipse"s, "bspline_curve"s, "offset_curve"s, "plane"s, "cylinder"s, "sphere"s, "bspline_surface"s, "edge"s, "loop"s, "face"s, "shell"s, "solid"s, "loft"s, "extrusion"s, "revolve"s, "surface_curve_sweep"s, "node"s, "collection"s, "boolean_result"s, "piecewise_function"s, "colour"s, "style"s, + "matrix4"s, "point3"s, "direction3"s, "line"s, "circle"s, "ellipse"s, "bspline_curve"s, "offset_curve"s, "plane"s, "cylinder"s, "sphere"s, "torus"s, "bspline_surface"s, "edge"s, "loop"s, "face"s, "shell"s, "solid"s, "loft"s, "extrusion"s, "revolve"s, "sweep_along_curve"s, "node"s, "collection"s, "boolean_result"s, "piecewise_function"s, "colour"s, "style"s, }; return values[k]; @@ -560,19 +565,18 @@ const std::string& ifcopenshell::geometry::taxonomy::kind_to_string(kinds k) { std::atomic_uint32_t item::counter_(0); -void ifcopenshell::geometry::taxonomy::piecewise_function::print(std::ostream& o, int) const { - o << "piecewise_function" << std::endl; +void ifcopenshell::geometry::taxonomy::item::print(std::ostream& o, int indent) const { + o << std::string(indent, ' ') << kind_to_string(kind()) << std::endl; } void ifcopenshell::geometry::taxonomy::matrix4::print(std::ostream& o, int indent) const { - print_impl(o, "matrix4", indent); + print_impl(o, kind_to_string(kind()), indent); } void ifcopenshell::geometry::taxonomy::colour::print(std::ostream& o, int indent) const { - print_impl(o, "colour", indent); + print_impl(o, kind_to_string(kind()), indent); } - void ifcopenshell::geometry::taxonomy::style::print(std::ostream& o, int indent) const { o << std::string(indent, ' ') << "style" << std::endl; o << std::string(indent, ' ') << " " << "name " << (name) << std::endl; @@ -588,31 +592,23 @@ void ifcopenshell::geometry::taxonomy::style::print(std::ostream& o, int indent) } void ifcopenshell::geometry::taxonomy::point3::print(std::ostream& o, int indent) const { - print_impl(o, "point3", indent); + print_impl(o, kind_to_string(kind()), indent); } void ifcopenshell::geometry::taxonomy::direction3::print(std::ostream& o, int indent) const { - print_impl(o, "direction3", indent); + print_impl(o, kind_to_string(kind()), indent); } void ifcopenshell::geometry::taxonomy::line::print(std::ostream& o, int indent) const { - print_impl(o, "line", indent); + print_impl(o, kind_to_string(kind()), indent); } void ifcopenshell::geometry::taxonomy::circle::print(std::ostream& o, int indent) const { - print_impl(o, "circle", indent); + print_impl(o, kind_to_string(kind()), indent); } void ifcopenshell::geometry::taxonomy::ellipse::print(std::ostream& o, int indent) const { - print_impl(o, "ellipse", indent); -} - -void ifcopenshell::geometry::taxonomy::bspline_curve::print(std::ostream& o, int indent) const { - o << std::string(indent, ' ') << "bspline curve" << std::endl; -} - -void ifcopenshell::geometry::taxonomy::offset_curve::print(std::ostream& o, int indent) const { - o << std::string(indent, ' ') << "offset_curve" << std::endl; + print_impl(o, kind_to_string(kind()), indent); } void ifcopenshell::geometry::taxonomy::trimmed_curve::print(std::ostream& o, int indent) const { @@ -632,12 +628,12 @@ void ifcopenshell::geometry::taxonomy::trimmed_curve::print(std::ostream& o, int basis->print(o, indent + 4); } - const boost::variant* const start_end[2] = { &start, &end }; + const boost::variant* const start_end[2] = { &start, &end }; for (int i = 0; i < 2; ++i) { o << std::string(indent + 4, ' ') << (i == 0 ? "start" : "end") << std::endl; - if (start_end[i]->which() == 0) { + if (start_end[i]->which() == 1) { boost::get(*start_end[i])->print(o, indent + 4); - } else if (start_end[i]->which() == 1) { + } else if (start_end[i]->which() == 2) { o << std::string(indent + 4, ' ') << "parameter " << boost::get(*start_end[i]) << std::endl; } } @@ -647,32 +643,8 @@ void ifcopenshell::geometry::taxonomy::trimmed_curve::print(std::ostream& o, int } } -void ifcopenshell::geometry::taxonomy::plane::print(std::ostream& o, int indent) const { - o << "not implemented"; -} - -void ifcopenshell::geometry::taxonomy::cylinder::print(std::ostream& o, int indent) const { - o << "not implemented"; -} - -void ifcopenshell::geometry::taxonomy::sphere::print(std::ostream& o, int indent) const { - o << "not implemented"; -} - -void ifcopenshell::geometry::taxonomy::bspline_surface::print(std::ostream& o, int indent) const { - o << "not implemented"; -} - void ifcopenshell::geometry::taxonomy::extrusion::print(std::ostream& o, int indent) const { o << std::string(indent, ' ') << "extrusion " << depth << std::endl; direction->print(o, indent + 4); basis->print(o, indent + 4); } - -void ifcopenshell::geometry::taxonomy::revolve::print(std::ostream& o, int indent) const { - o << std::string(indent, ' ') << "revolve" << std::endl; -} - -void ifcopenshell::geometry::taxonomy::surface_curve_sweep::print(std::ostream& o, int indent) const { - o << std::string(indent, ' ') << "surface_curve_sweep" << std::endl; -} \ No newline at end of file diff --git a/src/ifcgeom/taxonomy.h b/src/ifcgeom/taxonomy.h index e118aff996..2c8b36144f 100644 --- a/src/ifcgeom/taxonomy.h +++ b/src/ifcgeom/taxonomy.h @@ -30,6 +30,8 @@ // @todo don't do std::less but use hashing and cache hash values. +namespace boost { inline std::size_t hash_value(const blank&) { return 0; } } + namespace ifcopenshell { namespace geometry { @@ -91,7 +93,7 @@ typedef item const* ptr; topology_error(const char* const s) : std::runtime_error(s) {} }; - enum kinds { MATRIX4, POINT3, DIRECTION3, LINE, CIRCLE, ELLIPSE, BSPLINE_CURVE, OFFSET_CURVE, PLANE, CYLINDER, SPHERE, BSPLINE_SURFACE, EDGE, LOOP, FACE, SHELL, SOLID, LOFT, EXTRUSION, REVOLVE, SURFACE_CURVE_SWEEP, NODE, COLLECTION, BOOLEAN_RESULT, PIECEWISE_FUNCTION, COLOUR, STYLE }; + enum kinds { MATRIX4, POINT3, DIRECTION3, LINE, CIRCLE, ELLIPSE, BSPLINE_CURVE, OFFSET_CURVE, PLANE, CYLINDER, SPHERE, TORUS, BSPLINE_SURFACE, EDGE, LOOP, FACE, SHELL, SOLID, LOFT, EXTRUSION, REVOLVE, SURFACE_CURVE_SWEEP, NODE, COLLECTION, BOOLEAN_RESULT, PIECEWISE_FUNCTION, COLOUR, STYLE }; const std::string& kind_to_string(kinds k); @@ -109,7 +111,7 @@ typedef item const* ptr; virtual item* clone_() const = 0; virtual kinds kind() const = 0; - virtual void print(std::ostream&, int indent = 0) const = 0; + virtual void print(std::ostream&, int indent = 0) const; virtual void reverse() { throw taxonomy::topology_error(); } virtual size_t calc_hash() const = 0; virtual size_t hash() const { @@ -377,8 +379,6 @@ typedef item const* ptr; return *length_; } - void print(std::ostream& o, int = 0) const; - virtual piecewise_function* clone_() const { return new piecewise_function(*this); } virtual kinds kind() const { return PIECEWISE_FUNCTION; } @@ -608,8 +608,6 @@ typedef item const* ptr; std::vector knots; boost::optional> weights; int degree; - - void print(std::ostream& o, int indent = 0) const; }; struct offset_curve : public curve { @@ -626,8 +624,6 @@ typedef item const* ptr; auto v = std::make_tuple(static_cast(OFFSET_CURVE), reference->hash(), offset, basis ? basis->hash() : size_t(0)); return boost::hash{}(v); } - - void print(std::ostream& o, int indent = 0) const; }; struct trimmed_curve : public geom_item { @@ -635,7 +631,7 @@ typedef item const* ptr; // @todo The copy constructor of point3 within the variant fails on the avx instruction // on the default gcc in Ubuntu 18.04 and a recent AMD Ryzen. Probably due to allignment. - boost::variant start, end; + boost::variant start, end; // @todo somehow account for the fact that curve in IFC can be trimmed curve, polyline and composite curve as well. item::ptr basis; @@ -643,7 +639,7 @@ typedef item const* ptr; // @todo does this make sense? this is to accommodate for the fact that orientation is defined on both TrimmedCurve as well CompCurveSegment boost::optional curve_sense; - trimmed_curve() : basis(nullptr), curve_sense(true) {} + trimmed_curve() : basis(nullptr) {} trimmed_curve(const point3::ptr& a, const point3::ptr& b) : start(a), end(b), basis(nullptr) {} virtual void reverse() { @@ -704,6 +700,10 @@ typedef item const* ptr; } } + virtual void print_impl(std::ostream&, int) const { + // empty on purpose + } + void print(std::ostream& o, int indent = 0) const { o << std::string(indent, ' ') << kind_to_string(kind()) << std::endl; if (matrix && !matrix->is_identity()) { @@ -712,6 +712,7 @@ typedef item const* ptr; for (auto& c : children) { c->print(o, indent + 4); } + print_impl(o, indent + 4); } virtual ~collection_base() { @@ -762,6 +763,28 @@ typedef item const* ptr; return true; } + void calculate_linear_edge_curves() const { + for (auto& e : children) { + if (e->basis == nullptr) { + if (e->start.which() == 1 && e->end.which() == 1) { + auto ln = make(); + auto a = boost::get(e->start)->ccomponents(); + auto b = boost::get(e->end)->ccomponents(); + ln->matrix = make(a, b - a); + e->basis = ln; + } + } + } + } + + void remove_linear_edge_curves() const { + for (auto& e : children) { + if (e->basis != nullptr && e->basis->kind() == LINE) { + e->basis = nullptr; + } + } + } + virtual loop* clone_() const { return new loop(*this); } virtual kinds kind() const { return LOOP; } @@ -779,6 +802,13 @@ typedef item const* ptr; virtual face* clone_() const { return new face(*this); } virtual kinds kind() const { return FACE; } + virtual void print_impl(std::ostream& o, int indent) const { + if (basis) { + o << std::string(indent, ' ') << "basis" << std::endl; + basis->print(o, indent + 4); + } + } + virtual size_t calc_hash() const { auto v = std::make_tuple(static_cast(FACE), hash_elements(), basis ? basis->hash() : size_t(0)); return boost::hash{}(v); @@ -790,6 +820,11 @@ typedef item const* ptr; boost::optional closed; + virtual void print_impl(std::ostream& o, int indent) const { + using namespace std::string_literals; + o << std::string(indent, ' ') << "closed " << (closed ? *closed ? "yes"s : "no"s : "unknown"s) << std::endl; + } + virtual shell* clone_() const { return new shell(*this); } virtual kinds kind() const { return SHELL; } @@ -819,6 +854,11 @@ typedef item const* ptr; virtual loft* clone_() const { return new loft(*this); } virtual kinds kind() const { return LOFT; } + virtual void print_impl(std::ostream& o, int indent) const { + o << std::string(indent, ' ') << "axis" << std::endl; + axis->print(o, indent + 4); + } + virtual size_t calc_hash() const { auto v = std::make_tuple(static_cast(LOFT), hash_elements(), axis ? axis->hash() : size_t(0)); return boost::hash{}(v); @@ -833,8 +873,6 @@ typedef item const* ptr; virtual plane* clone_() const { return new plane(*this); } virtual kinds kind() const { return PLANE; } - void print(std::ostream& o, int) const; - virtual size_t calc_hash() const { auto v = std::make_tuple(static_cast(PLANE), matrix->hash_components()); return boost::hash{}(v); @@ -849,8 +887,6 @@ typedef item const* ptr; virtual cylinder* clone_() const { return new cylinder(*this); } virtual kinds kind() const { return CYLINDER; } - void print(std::ostream& o, int) const; - virtual size_t calc_hash() const { auto v = std::make_tuple(static_cast(CYLINDER), matrix->hash_components()); return boost::hash{}(v); @@ -865,14 +901,27 @@ typedef item const* ptr; virtual sphere* clone_() const { return new sphere(*this); } virtual kinds kind() const { return SPHERE; } - void print(std::ostream& o, int) const; - virtual size_t calc_hash() const { auto v = std::make_tuple(static_cast(SPHERE), matrix->hash_components()); return boost::hash{}(v); } }; + struct torus : public surface { + DECLARE_PTR(torus) + + double radius1; + double radius2; + + virtual torus* clone_() const { return new torus(*this); } + virtual kinds kind() const { return TORUS; } + + virtual size_t calc_hash() const { + auto v = std::make_tuple(static_cast(TORUS), matrix->hash_components()); + return boost::hash{}(v); + } + }; + struct bspline_surface : public surface { DECLARE_PTR(bspline_surface) @@ -914,17 +963,15 @@ typedef item const* ptr; std::array, 2> knots; boost::optional>> weights; std::array degree; - - void print(std::ostream& o, int) const; }; struct sweep : public geom_item { DECLARE_PTR(sweep) - face::ptr basis; + item::ptr basis; sweep(face::ptr b) : basis(b) {} - sweep(matrix4::ptr m, face::ptr b) : geom_item(m), basis(b) {} + sweep(matrix4::ptr m, item::ptr b) : geom_item(m), basis(b) {} }; struct extrusion : public sweep { @@ -936,7 +983,7 @@ typedef item const* ptr; virtual extrusion* clone_() const { return new extrusion(*this); } virtual kinds kind() const { return EXTRUSION; } - extrusion(matrix4::ptr m, face::ptr basis, direction3::ptr dir, double d) : sweep(m, basis), direction(dir), depth(d) {} + extrusion(matrix4::ptr m, item::ptr basis, direction3::ptr dir, double d) : sweep(m, basis), direction(dir), depth(d) {} void print(std::ostream& o, int indent = 0) const; @@ -956,9 +1003,7 @@ typedef item const* ptr; virtual revolve* clone_() const { return new revolve(*this); } virtual kinds kind() const { return REVOLVE; } - revolve(matrix4::ptr m, face::ptr basis, point3::ptr pnt, direction3::ptr dir, const boost::optional& a) : sweep(m, basis), axis_origin(pnt), direction(dir), angle(a) {} - - void print(std::ostream& o, int indent = 0) const; + revolve(matrix4::ptr m, item::ptr basis, point3::ptr pnt, direction3::ptr dir, const boost::optional& a) : sweep(m, basis), axis_origin(pnt), direction(dir), angle(a) {} virtual size_t calc_hash() const { auto v = std::make_tuple(static_cast(REVOLVE), matrix->hash_components(), basis->calc_hash(), axis_origin->hash_components(), direction->hash_components(), angle ? *angle : 1000.); @@ -966,18 +1011,16 @@ typedef item const* ptr; } }; - struct surface_curve_sweep : public sweep { - DECLARE_PTR(surface_curve_sweep) + struct sweep_along_curve : public sweep { + DECLARE_PTR(sweep_along_curve) item::ptr surface; item::ptr curve; - virtual surface_curve_sweep* clone_() const { return new surface_curve_sweep(*this); } + virtual sweep_along_curve* clone_() const { return new sweep_along_curve(*this); } virtual kinds kind() const { return SURFACE_CURVE_SWEEP; } - surface_curve_sweep(matrix4::ptr m, face::ptr basis, item::ptr surf, item::ptr crv) : sweep(m, basis), surface(surf), curve(crv) {} - - void print(std::ostream& o, int indent = 0) const; + sweep_along_curve(matrix4::ptr m, face::ptr basis, item::ptr surf, item::ptr crv) : sweep(m, basis), surface(surf), curve(crv) {} virtual size_t calc_hash() const { auto v = std::make_tuple(static_cast(SURFACE_CURVE_SWEEP), matrix->hash_components(), basis->calc_hash(), surface->calc_hash(), curve->calc_hash()); @@ -1025,9 +1068,9 @@ typedef item const* ptr; }; namespace impl { - typedef std::tuple KindsTuple; + typedef std::tuple KindsTuple; typedef std::tuple CurvesTuple; - typedef std::tuple SurfacesTuple; + typedef std::tuple SurfacesTuple; } struct type_by_kind { @@ -1093,16 +1136,25 @@ typedef item const* ptr; if constexpr (std::is_same_v) { auto circle = taxonomy::dcast(item); auto ellipse = taxonomy::dcast(item); - if (circle || ellipse) { + auto line = taxonomy::dcast(item); + auto bspline_curve = taxonomy::dcast(item); + if (circle || ellipse || line || bspline_curve) { edge_ = taxonomy::make(); if (circle) { (*edge_)->basis = circle; - } else { + } else if (ellipse) { (*edge_)->basis = ellipse; + } else if (line) { + (*edge_)->basis = line; + } else if (bspline_curve) { + (*edge_)->basis = bspline_curve; + } + + if (circle || ellipse) { + // @todo + (*edge_)->start = 0.; + (*edge_)->end = 2 * boost::math::constants::pi(); } - // @todo make trims optional to allow unbounded edges - (*edge_)->start = 0.; - (*edge_)->end = 2 * boost::math::constants::pi(); } } } @@ -1131,17 +1183,55 @@ typedef item const* ptr; if constexpr (std::is_same_v) { auto circle = taxonomy::dcast(item); auto ellipse = taxonomy::dcast(item); - if (circle || ellipse) { + auto line = taxonomy::dcast(item); + auto bspline_curve = taxonomy::dcast(item); + if (circle || ellipse || line || bspline_curve) { auto edge = taxonomy::make(); if (circle) { edge->basis = circle; - } else { + } else if (ellipse) { edge->basis = ellipse; + } else if (line) { + edge->basis = line; + } else if (bspline_curve) { + edge->basis = bspline_curve; } - // @todo make trims optional to allow unbounded edges - edge->start = 0.; - edge->end = 2 * boost::math::constants::pi(); + if (circle || ellipse) { + // @todo + edge->start = 0.; + edge->end = 2 * boost::math::constants::pi(); + } + + loop_ = taxonomy::make(); + (*loop_)->children.push_back(edge); + } + } + } + + operator bool() const { + return loop_.is_initialized(); + } + + operator typename T::ptr() const { + if constexpr (std::is_same_v) { + if (loop_) { + return *loop_; + } + } + return nullptr; + } + }; + + template + class edge_to_loop_upgrade { + private: + boost::optional loop_; + public: + edge_to_loop_upgrade(taxonomy::ptr item) { + if constexpr (std::is_same_v) { + auto edge = taxonomy::dcast(item); + if (edge) { loop_ = taxonomy::make(); (*loop_)->children.push_back(edge); } @@ -1172,16 +1262,25 @@ typedef item const* ptr; if constexpr (std::is_same_v) { auto circle = taxonomy::dcast(item); auto ellipse = taxonomy::dcast(item); - if (circle || ellipse) { + auto line = taxonomy::dcast(item); + auto bspline_curve = taxonomy::dcast(item); + if (circle || ellipse || line || bspline_curve) { auto edge = taxonomy::make(); if (circle) { edge->basis = circle; - } else { + } else if (ellipse) { edge->basis = ellipse; + } else if (line) { + edge->basis = line; + } else if (bspline_curve) { + edge->basis = bspline_curve; + } + + if (circle || ellipse) { + // @todo + edge->start = 0.; + edge->end = 2 * boost::math::constants::pi(); } - // @todo make trims optional to allow unbounded edges - edge->start = 0.; - edge->end = 2 * boost::math::constants::pi(); auto loop = taxonomy::make(); loop->children.push_back(edge); @@ -1278,6 +1377,12 @@ typedef item const* ptr; return upg; } } + { + edge_to_loop_upgrade upg(u); + if (upg) { + return upg; + } + } { curve_to_face_upgrade upg(u); if (upg) { @@ -1316,6 +1421,12 @@ typedef item const* ptr; return upg; } } + { + edge_to_loop_upgrade upg(u); + if (upg) { + return upg; + } + } { loop_to_face_upgrade upg(u); if (upg) { @@ -1450,10 +1561,10 @@ typedef item const* ptr; } else if (auto l = taxonomy::dcast(i)) { // @todo maybe make edge a collection then as well? - if (l->start.which() == 0) { + if (l->start.which() == 1) { fn(boost::get(l->start)); } - if (l->end.which() == 0) { + if (l->end.which() == 1) { fn(boost::get(l->end)); } } diff --git a/src/ifcwrap/IfcGeomWrapper.i b/src/ifcwrap/IfcGeomWrapper.i index b0f890fdf2..06026aa32c 100644 --- a/src/ifcwrap/IfcGeomWrapper.i +++ b/src/ifcwrap/IfcGeomWrapper.i @@ -20,6 +20,7 @@ %rename("buffer") stream_or_filename; %ignore stream_or_filename::stream; +%ignore boost::hash_value; // This is only used for RGB colours, hence the size of 3 %typemap(out) const double* { @@ -100,8 +101,11 @@ std::pair vector_to_buffer(const T& t) { %ignore ifcopenshell::geometry::taxonomy::item::print; -%typemap(out) boost::variant< ifcopenshell::geometry::taxonomy::point3::ptr,double > { +%typemap(out) boost::variant { if ($1.which() == 0) { + Py_INCREF(Py_None); + return Py_None; + } else if ($1.which() == 1) { return SWIG_NewPointerObj(SWIG_as_voidptr(new std::shared_ptr(boost::get($1))), SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__point3_t, 0 | SWIG_POINTER_OWN); } else { return PyFloat_FromDouble(boost::get($1)); @@ -137,6 +141,7 @@ std::pair vector_to_buffer(const T& t) { if (!$1) $1 = try_upcast($input, SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__shell_t); if (!$1) $1 = try_upcast($input, SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__solid_t); if (!$1) $1 = try_upcast($input, SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__sphere_t); + if (!$1) $1 = try_upcast($input, SWIGTYPE_p_std__shared_ptrT_ifcopenshell__geometry__taxonomy__torus_t); if (!$1) $1 = try_upcast