/******************************************************************************** * * * This file is part of IfcOpenShell. * * * * IfcOpenShell is free software: you can redistribute it and/or modify * * it under the terms of the Lesser GNU General Public License as published by * * the Free Software Foundation, either version 3.0 of the License, or * * (at your option) any later version. * * * * IfcOpenShell is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * Lesser GNU General Public License for more details. * * * * You should have received a copy of the Lesser GNU General Public License * * along with this program. If not, see . * * * ********************************************************************************/ #ifndef OPENCASCADEKERNEL_H #define OPENCASCADEKERNEL_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../../ifcgeom/kernel_agnostic/AbstractKernel.h" #include "../../../ifcgeom/schema_agnostic/IfcGeomElement.h" #include "../../../ifcgeom/schema_agnostic/IfcGeomRepresentation.h" #include "../../../ifcgeom/schema_agnostic/ConversionResult.h" #include "../../../ifcgeom/kernels/opencascade/OpenCascadeConversionResult.h" #include "../../../ifcgeom/schema_agnostic/ifc_geom_api.h" #include "../../../ifcgeom/taxonomy.h" // Define this in case you want to conserve memory usage at all cost. This has been // benchmarked extensively: https://github.com/IfcOpenShell/IfcOpenShell/pull/47 // #define NO_CACHE #ifdef NO_CACHE #define IN_CACHE(T,E,t,e) #define CACHE(T,E,e) #else #define IN_CACHE(T,E,t,e) std::map::const_iterator it = cache.T.find(E->data().id());\ if ( it != cache.T.end() ) { e = it->second; return true; } #define CACHE(T,E,e) cache.T[E->data().id()] = e; #endif namespace ifcopenshell { namespace geometry { namespace kernels { class IFC_GEOM_API geometry_exception : public std::exception { protected: std::string message; public: geometry_exception(const std::string& m) : message(m) {} virtual ~geometry_exception() throw () {} virtual const char* what() const throw() { return message.c_str(); } }; class IFC_GEOM_API too_many_faces_exception : public geometry_exception { public: too_many_faces_exception() : geometry_exception("Too many faces for operation") {} }; /* class IFC_GEOM_API POSTFIX_SCHEMA(Cache) { public: #include "IfcRegisterCreateCache.h" std::map Shape; }; */ class IFC_GEOM_API OpenCascadeKernel : public AbstractKernel { private: // faceset_helper traverses the forward instance references of IfcConnectedFaceSet and then provides a mapping // M of (IfcCartesianPoint, IfcCartesianPoint) -> TopoDS_Edge, where M(a, b) is a partner of M(b, a), ie share // the same underlying edge but with orientation reversed. This then later speeds op the process of creating a // manifold Shell / Solid from this set of faces. Only IfcPolyLoop instances are used. Points within the tolerance // threshiold are merged, so consider points a, b, c, distance(a, b) < eps then M(a, b) = Null, M(a, b) = M(a, c). class faceset_helper { private: OpenCascadeKernel* kernel_; std::set duplicates_; std::map vertex_mapping_; std::map, TopoDS_Edge> edges_; double eps_; bool non_manifold_; template void loop_(const taxonomy::loop* ps, const Fn& callback) { if (ps->children.size() < 3) { return; } auto a = boost::get(((taxonomy::edge*) ps->children.back())->start).instance; auto A = a->data().id(); for (auto& b : ps->children) { auto B = boost::get(((taxonomy::edge*) b)->start).instance->data().id(); auto C = vertex_mapping_[A], D = vertex_mapping_[B]; bool fwd = C < D; if (!fwd) { std::swap(C, D); } if (C != D) { callback(C, D, fwd); A = B; } } } public: faceset_helper(OpenCascadeKernel* kernel, const taxonomy::shell* l); ~faceset_helper(); bool non_manifold() const { return non_manifold_; } bool& non_manifold() { return non_manifold_; } bool edge(const taxonomy::point3& a, const taxonomy::point3& b, TopoDS_Edge& e) { int A = vertex_mapping_[a.instance->data().id()]; int B = vertex_mapping_[b.instance->data().id()]; if (A == B) { return false; } return edge(A, B, e); } bool edge(int A, int B, TopoDS_Edge& e) { auto it = edges_.find({ A, B }); if (it == edges_.end()) { return false; } e = it->second; return true; } bool wire(const taxonomy::loop* loop, TopoDS_Wire& wire) { if (duplicates_.find(loop->instance->data().id()) != duplicates_.end()) { return false; } BRep_Builder builder; builder.MakeWire(wire); int count = 0; loop_(loop, [this, &builder, &wire, &count](int A, int B, bool fwd) { TopoDS_Edge e; if (edge(A, B, e)) { if (!fwd) { e.Reverse(); } builder.Add(wire, e); count += 1; } }); if (count >= 3) { wire.Closed(true); /* @todo TopTools_ListOfShape results; if (kernel_->wire_intersections(wire, results)) { Logger::Warning("Self-intersections with " + boost::lexical_cast(results.Extent()) + " cycles detected", loop); kernel_->select_largest(results, wire); non_manifold_ = true; } */ return true; } else { return false; } } double epsilon() const { return eps_; } }; /* #ifndef NO_CACHE POSTFIX_SCHEMA(Cache) cache; #endif */ faceset_helper* faceset_helper_; double precision_; public: OpenCascadeKernel() : AbstractKernel("opencascade") , faceset_helper_(nullptr) // @todo , precision_(1.e-5) {} OpenCascadeKernel(const OpenCascadeKernel& other) : AbstractKernel("opencascade") { *this = other; } static double shape_volume(const TopoDS_Shape&); static double face_area(const TopoDS_Face&); static int count(const TopoDS_Shape& s, TopAbs_ShapeEnum t, bool unique = false); bool create_solid_from_compound(const TopoDS_Shape& compound, TopoDS_Shape& shape); bool create_solid_from_faces(const TopTools_ListOfShape& face_list, TopoDS_Shape& shape); bool convert(const taxonomy::extrusion*, TopoDS_Shape&); bool convert(const taxonomy::face*, TopoDS_Shape&); bool convert(const taxonomy::loop*, TopoDS_Wire&); bool convert(const taxonomy::matrix4*, gp_GTrsf&); bool convert(const taxonomy::shell*, TopoDS_Shape&); bool approximate_plane_through_wire(const TopoDS_Wire& wire, gp_Pln& plane, double eps = -1.); bool triangulate_wire(const std::vector& wires, TopTools_ListOfShape& faces); bool boolean_operation(const TopoDS_Shape& a_, const TopTools_ListOfShape& b__, BOPAlgo_Operation op, TopoDS_Shape& result, double fuzziness = -1.); const TopoDS_Shape& ensure_fit_for_subtraction(const TopoDS_Shape& shape, TopoDS_Shape& solid); bool flatten_shape_list(const ifcopenshell::geometry::ConversionResults& shapes, TopoDS_Shape& result, bool fuse); bool is_compound(const TopoDS_Shape& shape); TopoDS_Shape apply_transformation(const TopoDS_Shape& s, const taxonomy::matrix4& t); TopoDS_Shape apply_transformation(const TopoDS_Shape& s, const gp_GTrsf& t); TopoDS_Shape apply_transformation(const TopoDS_Shape& s, const gp_Trsf& t); virtual bool convert_impl(const taxonomy::face*, ifcopenshell::geometry::ConversionResults&); virtual bool convert_impl(const taxonomy::shell*, ifcopenshell::geometry::ConversionResults&); virtual bool convert_impl(const taxonomy::extrusion*, ifcopenshell::geometry::ConversionResults&); virtual bool convert_impl(const taxonomy::boolean_result*, ifcopenshell::geometry::ConversionResults&); }; /* IfcUtil::IfcBaseClass* POSTFIX_SCHEMA(tesselate_)(const TopoDS_Shape& shape, double deflection); IfcUtil::IfcBaseClass* POSTFIX_SCHEMA(serialise_)(const TopoDS_Shape& shape, bool advanced); */ } } } #endif