diff --git a/src/ifcgeom/AbstractKernel.cpp b/src/ifcgeom/AbstractKernel.cpp index a86a542895..f785114420 100644 --- a/src/ifcgeom/AbstractKernel.cpp +++ b/src/ifcgeom/AbstractKernel.cpp @@ -30,8 +30,15 @@ bool ifcopenshell::geometry::kernels::AbstractKernel::convert(const taxonomy::pt try { return dispatch_conversion<0>::dispatch(this, item->kind(), item, results); } catch (std::exception& e) { - Logger::Error(e, item->instance); - return false; + std::string prev_exception = std::string(e.what()); + try { + return dispatch_with_upgrade<0>::dispatch(this, item, results); + } catch (std::exception& e) { + Logger::Error(prev_exception + " Conversion for upgraded element failed with: " + std::string(e.what()), item->instance); + return false; + } catch (...) { + return false; + } } catch (...) { // @todo we can't log OCCT exceptions here, can we do some reraising to solve this? return false; diff --git a/src/ifcgeom/AbstractKernel.h b/src/ifcgeom/AbstractKernel.h index 87a6e4d623..d06fe6bf4a 100644 --- a/src/ifcgeom/AbstractKernel.h +++ b/src/ifcgeom/AbstractKernel.h @@ -86,7 +86,7 @@ namespace { struct dispatch_conversion { static bool dispatch(ifcopenshell::geometry::kernels::AbstractKernel* kernel, ifcopenshell::geometry::taxonomy::kinds item_kind, const ifcopenshell::geometry::taxonomy::ptr item, IfcGeom::ConversionResults& results) { if (N == item_kind) { - auto concrete_item = ifcopenshell::geometry::taxonomy::template cast>(item); + auto concrete_item = std::static_pointer_cast>(item); return kernel->convert_impl(concrete_item, results); } else { return dispatch_conversion::dispatch(kernel, item_kind, item, results); @@ -102,6 +102,26 @@ namespace { } }; + template + struct dispatch_with_upgrade { + static bool dispatch(ifcopenshell::geometry::kernels::AbstractKernel* kernel, const ifcopenshell::geometry::taxonomy::ptr item, IfcGeom::ConversionResults& results) { + auto concrete_item = ifcopenshell::geometry::taxonomy::template dcast>(item); + if (concrete_item) { + return kernel->convert_impl(concrete_item, results); + } else { + return dispatch_with_upgrade::dispatch(kernel, item, results); + } + } + }; + + template <> + struct dispatch_with_upgrade { + static bool dispatch(ifcopenshell::geometry::kernels::AbstractKernel*, const ifcopenshell::geometry::taxonomy::ptr item, IfcGeom::ConversionResults&) { + Logger::Error("No conversion with upgrade for " + std::to_string(item->kind())); + return false; + } + }; + template struct TupleTypeIndex; diff --git a/src/ifcgeom/mapping/IfcCircle.cpp b/src/ifcgeom/mapping/IfcCircle.cpp index 6693cd18ca..754c62df62 100644 --- a/src/ifcgeom/mapping/IfcCircle.cpp +++ b/src/ifcgeom/mapping/IfcCircle.cpp @@ -34,5 +34,6 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCircle* inst) { auto c = taxonomy::make(); c->radius = r; c->matrix = taxonomy::cast(map(placement)); + return c; } diff --git a/src/ifcgeom/taxonomy.cpp b/src/ifcgeom/taxonomy.cpp index 2feacfae15..6f5e6249c7 100644 --- a/src/ifcgeom/taxonomy.cpp +++ b/src/ifcgeom/taxonomy.cpp @@ -594,3 +594,160 @@ void ifcopenshell::geometry::taxonomy::extrusion::print(std::ostream& o, int ind direction->print(o, indent + 4); basis->print(o, indent + 4); } + +boost::optional ifcopenshell::geometry::taxonomy::loop_to_face_upgrade_impl(ptr item) { + boost::optional face_; + auto loop_ = dcast(item); + if (loop_) { + loop_->external = true; + + face_ = make(); + (*face_)->instance = loop_->instance; + (*face_)->matrix = loop_->matrix; + (*face_)->children = { clone(loop_) }; + } + return face_; +} + +boost::optional ifcopenshell::geometry::taxonomy::curve_to_edge_upgrade_impl(ptr item) { + boost::optional edge_; + auto circle_ = dcast(item); + auto ellipse_ = dcast(item); + auto line_ = dcast(item); + auto bspline_curve_ = dcast(item); + if (circle_ || ellipse_ || line_ || bspline_curve_) { + edge_ = make(); + if (circle_) { + (*edge_)->basis = circle_; + (*edge_)->instance = circle_->instance; + } else if (ellipse_) { + (*edge_)->basis = ellipse_; + (*edge_)->instance = ellipse_->instance; + } else if (line_) { + (*edge_)->basis = line_; + (*edge_)->instance = line_->instance; + } else if (bspline_curve_) { + (*edge_)->basis = bspline_curve_; + (*edge_)->instance = bspline_curve_->instance; + } + + if (circle_ || ellipse_) { + // @todo + (*edge_)->start = 0.; + (*edge_)->end = 2 * boost::math::constants::pi(); + } + } + return edge_; +} + +boost::optional ifcopenshell::geometry::taxonomy::curve_to_loop_upgrade_impl(ptr item) { + boost::optional loop_; + auto circle_ = dcast(item); + auto ellipse_ = dcast(item); + auto line_ = dcast(item); + auto bspline_curve_ = dcast(item); + if (circle_ || ellipse_ || line_ || bspline_curve_) { + auto edge_ = make(); + if (circle_) { + edge_->basis = circle_; + } 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(); + } + + loop_ = make(); + (*loop_)->children.push_back(edge_); + } + return loop_; +} + +boost::optional ifcopenshell::geometry::taxonomy::edge_to_loop_upgrade_impl(ptr item) { + boost::optional loop_; + auto edge_ = dcast(item); + if (edge_) { + loop_ = make(); + (*loop_)->children.push_back(edge_); + } + return loop_; +} + +boost::optional ifcopenshell::geometry::taxonomy::curve_to_face_upgrade_impl(ptr item) { + boost::optional face_; + auto circle_ = dcast(item); + auto ellipse_ = dcast(item); + auto line_ = dcast(item); + auto bspline_curve_ = dcast(item); + + if (circle_ || ellipse_ || line_ || bspline_curve_) { + auto edge_ = make(); + if (circle_) { + edge_->basis = circle_; + } 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(); + } + + auto loop_ = make(); + loop_->children.push_back(edge_); + + face_ = make(); + (*face_)->instance = loop_->instance; + (*face_)->matrix = loop_->matrix; + (*face_)->children = { clone(loop_) }; + } + return face_; +} + + +boost::optional ifcopenshell::geometry::taxonomy::loop_to_piecewise_function_upgrade_impl(ptr item) { + boost::optional pwf_; + auto loop_ = dcast(item); + if (loop_) { + if (loop_->pwf.is_initialized()) { + pwf_ = loop_->pwf; + } else { + piecewise_function::spans_t spans; + spans.reserve(loop_->children.size()); + for (auto& edge_ : loop_->children) { + // the edge could be an arc or trimmed circle in the case of IfcIndexPolyCurve - support for this isn't implemented yet + if (edge_->basis) { + Logger::Message(Logger::Severity::LOG_NOTICE, "Shape of basis curve ignored - edge is treated as a straight line edge"); + } + + const auto& s = boost::get(edge_->start)->ccomponents(); + const auto& e = boost::get(edge_->end)->ccomponents(); + Eigen::Vector3d v = e - s; + auto l = v.norm(); // the norm of a vector is a measure of its length + v.normalize(); // normalize the vector so that it is a unit direction vector + std::function fn = [s, v](double u) { + Eigen::Vector3d o(s + u * v), axis(0, 0, 1), refDirection(v); + auto Y = axis.cross(refDirection).normalized(); + axis = refDirection.cross(Y).normalized(); + return make(o, axis, refDirection)->components(); + }; + spans.emplace_back(l, fn); + } + pwf_ = make(0.0,spans); + loop_->pwf = pwf_; + } + } + return pwf_; +} diff --git a/src/ifcgeom/taxonomy.h b/src/ifcgeom/taxonomy.h index c1ced08786..13164daebf 100644 --- a/src/ifcgeom/taxonomy.h +++ b/src/ifcgeom/taxonomy.h @@ -1035,6 +1035,7 @@ typedef item const* ptr; typedef std::tuple KindsTuple; typedef std::tuple CurvesTuple; typedef std::tuple SurfacesTuple; + typedef std::tuple UpgradesTuple; } struct type_by_kind { @@ -1058,6 +1059,14 @@ typedef item const* ptr; static const size_t max = std::tuple_size::value; }; + struct upgrades { + template + using type = typename std::tuple_element::type; + + static const size_t max = std::tuple_size::value; + }; + + boost::optional loop_to_face_upgrade_impl(ptr item); template class loop_to_face_upgrade { private: @@ -1065,15 +1074,7 @@ typedef item const* ptr; public: loop_to_face_upgrade(taxonomy::ptr item) { if constexpr (std::is_same_v) { - auto loop = taxonomy::dcast(item); - if (loop) { - loop->external = true; - - face_ = taxonomy::make(); - (*face_)->instance = loop->instance; - (*face_)->matrix = loop->matrix; - (*face_)->children = { taxonomy::clone(loop) }; - } + face_ = loop_to_face_upgrade_impl(item); } } @@ -1091,6 +1092,7 @@ typedef item const* ptr; } }; + boost::optional curve_to_edge_upgrade_impl(ptr item); template class curve_to_edge_upgrade { private: @@ -1098,28 +1100,7 @@ typedef item const* ptr; public: curve_to_edge_upgrade(taxonomy::ptr item) { if constexpr (std::is_same_v) { - auto circle = taxonomy::dcast(item); - auto ellipse = taxonomy::dcast(item); - 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 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(); - } - } + edge_ = taxonomy::curve_to_edge_upgrade_impl(item); } } @@ -1137,7 +1118,7 @@ typedef item const* ptr; } }; - + boost::optional curve_to_loop_upgrade_impl(ptr item); template class curve_to_loop_upgrade { private: @@ -1145,31 +1126,7 @@ typedef item const* ptr; public: curve_to_loop_upgrade(taxonomy::ptr item) { if constexpr (std::is_same_v) { - auto circle = taxonomy::dcast(item); - auto ellipse = taxonomy::dcast(item); - 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 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(); - } - - loop_ = taxonomy::make(); - (*loop_)->children.push_back(edge); - } + loop_ = curve_to_loop_upgrade_impl(item); } } @@ -1187,6 +1144,7 @@ typedef item const* ptr; } }; + boost::optional edge_to_loop_upgrade_impl(ptr item); template class edge_to_loop_upgrade { private: @@ -1194,11 +1152,7 @@ typedef item const* ptr; 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); - } + loop_ = edge_to_loop_upgrade_impl(item); } } @@ -1216,7 +1170,7 @@ typedef item const* ptr; } }; - + boost::optional curve_to_face_upgrade_impl(ptr item); template class curve_to_face_upgrade { private: @@ -1224,36 +1178,7 @@ typedef item const* ptr; public: curve_to_face_upgrade(taxonomy::ptr item) { if constexpr (std::is_same_v) { - auto circle = taxonomy::dcast(item); - auto ellipse = taxonomy::dcast(item); - 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 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(); - } - - auto loop = taxonomy::make(); - loop->children.push_back(edge); - - face_ = taxonomy::make(); - (*face_)->instance = loop->instance; - (*face_)->matrix = loop->matrix; - (*face_)->children = { taxonomy::clone(loop) }; - } + face_ = curve_to_face_upgrade_impl(item); } } @@ -1271,6 +1196,7 @@ typedef item const* ptr; } }; + boost::optional loop_to_piecewise_function_upgrade_impl(ptr item); template class loop_to_piecewise_function_upgrade { private: @@ -1279,36 +1205,7 @@ typedef item const* ptr; public: loop_to_piecewise_function_upgrade(taxonomy::ptr item) { if constexpr (std::is_same_v) { - auto loop = taxonomy::dcast(item); - if (loop) { - if (loop->pwf.is_initialized()) { - pwf_ = loop->pwf; - } else { - taxonomy::piecewise_function::spans_t spans; - spans.reserve(loop->children.size()); - for (auto& edge : loop->children) { - // the edge could be an arc or trimmed circle in the case of IfcIndexPolyCurve - support for this isn't implemented yet - if (edge->basis) { - Logger::Message(Logger::Severity::LOG_NOTICE, "Shape of basis curve ignored - edge is treated as a straight line edge"); - } - - const auto& s = boost::get(edge->start)->ccomponents(); - const auto& e = boost::get(edge->end)->ccomponents(); - Eigen::Vector3d v = e - s; - auto l = v.norm(); // the norm of a vector is a measure of its length - v.normalize(); // normalize the vector so that it is a unit direction vector - std::function fn = [s, v](double u) { - Eigen::Vector3d o(s + u * v), axis(0, 0, 1), refDirection(v); - auto Y = axis.cross(refDirection).normalized(); - axis = refDirection.cross(Y).normalized(); - return taxonomy::make(o, axis, refDirection)->components(); - }; - spans.emplace_back(l, fn); - } - pwf_ = taxonomy::make(0.0,spans); - loop->pwf = pwf_; - } - } + pwf_ = loop_to_piecewise_function_upgrade_impl(item); } } @@ -1342,13 +1239,13 @@ typedef item const* ptr; } } { - edge_to_loop_upgrade upg(u); + curve_to_face_upgrade upg(u); if (upg) { return upg; } } { - curve_to_face_upgrade upg(u); + edge_to_loop_upgrade upg(u); if (upg) { return upg; } @@ -1379,6 +1276,12 @@ typedef item const* ptr; return upg; } } + { + curve_to_loop_upgrade upg(u); + if (upg) { + return upg; + } + } { curve_to_face_upgrade upg(u); if (upg) { diff --git a/src/ifcopenshell-python/ifcopenshell/util/shape_builder.py b/src/ifcopenshell-python/ifcopenshell/util/shape_builder.py index 2d059c1a7c..05cae93995 100644 --- a/src/ifcopenshell-python/ifcopenshell/util/shape_builder.py +++ b/src/ifcopenshell-python/ifcopenshell/util/shape_builder.py @@ -228,10 +228,8 @@ class ShapeBuilder: :return: IfcCircle :rtype: ifcopenshell.entity_instance """ - ifc_center = self.file.createIfcAxis2Placement2D(self.file.createIfcCartesianPoint(center)) + ifc_center = self.create_axis2_placement_2d(center) ifc_curve = self.file.createIfcCircle(ifc_center, radius) - - # self.file_file.createIfcAxis2Placement2D(tool.Ifc.get().createIfcCartesianPoint(center[0:2])) return ifc_curve def plane( @@ -324,9 +322,7 @@ class ShapeBuilder: for further extrusion. """ direction = self.file.createIfcDirection(ref_x_direction) - ifc_position = self.file.createIfcAxis2Placement2D( - self.file.createIfcCartesianPoint(position), RefDirection=direction - ) + ifc_position = self.create_axis2_placement_2d(position, direction) ifc_ellipse = self.file.createIfcEllipse( Position=ifc_position, SemiAxis1=x_axis_radius, SemiAxis2=y_axis_radius ) @@ -373,6 +369,9 @@ class ShapeBuilder: "OuterCurve": outer_curve, } + if self.file.schema == "IFC2X3": + kwargs["Position"] = self.create_axis2_placement_2d() + if inner_curves: if not isinstance(inner_curves, collections.abc.Iterable): inner_curves = [inner_curves] @@ -580,6 +579,17 @@ class ShapeBuilder: position=matrix[:, 3][:3].tolist(), z_axis=matrix[:, 2][:3].tolist(), x_axis=matrix[:, 0][:3].tolist() ) + def create_axis2_placement_2d( + self, position: VectorTuple = (0.0, 0.0), x_direction: Optional[VectorTuple] = None + ) -> ifcopenshell.entity_instance: + """Create IfcAxis2Placement2D.""" + ref_direction = self.file.create_entity("IfcDirection", x_direction) if x_direction else None + return self.file.create_entity( + "IfcAxis2Placement2D", + Location=self.file.create_entity("IfcCartesianPoint", position), + RefDirection=ref_direction, + ) + def mirror( self, curve_or_item: Union[ifcopenshell.entity_instance, list[ifcopenshell.entity_instance]], diff --git a/src/ifcopenshell-python/test/geom/geometry_tests.py b/src/ifcopenshell-python/test/geom/geometry_tests.py new file mode 100644 index 0000000000..0d77f505ed --- /dev/null +++ b/src/ifcopenshell-python/test/geom/geometry_tests.py @@ -0,0 +1,72 @@ +# IfcOpenShell - IFC toolkit and geometry engine +# Copyright (C) 2021 Dion Moult +# +# This file is part of IfcOpenShell. +# +# IfcOpenShell is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation, either version 3 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 +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with IfcOpenShell. If not, see . + +import ifcopenshell +import ifcopenshell.geom +import test.bootstrap +from ifcopenshell.util.shape_builder import ShapeBuilder, V + + +class TestCreatingShapes(test.bootstrap.IFC4): + def test_create_IfcCirle(self): + builder = ShapeBuilder(self.file) + item = builder.circle(radius=1.0) + settings = ifcopenshell.geom.settings() + shape = ifcopenshell.geom.create_shape(settings, item) + assert shape.verts + + def test_create_IfcEllipse(self): + builder = ShapeBuilder(self.file) + item = self.file.create_entity( + "IfcEllipse", Position=builder.create_axis2_placement_2d(), SemiAxis1=1.0, SemiAxis2=0.5 + ) + settings = ifcopenshell.geom.settings() + shape = ifcopenshell.geom.create_shape(settings, item) + assert shape.verts + + def test_create_IfcLine(self): + item = self.file.create_entity( + "IfcLine", + Pnt=self.file.create_entity("IfcCartesianPoint", (0.0, 0.0)), + Dir=self.file.create_entity( + "IfcVector", Orientation=self.file.create_entity("IfcDirection", (1.0, 0.0)), Magnitude=1.0 + ), + ) + settings = ifcopenshell.geom.settings() + shape = ifcopenshell.geom.create_shape(settings, item) + assert shape.verts + + def test_create_IfcBSplineCurve(self): + points = [ + (6.4062, 19.4575766757, 13.352536093), + (5.8477429418, 19.9080686796, 14.1328111323), + (3.6139147091, 20.3256826827, 14.8561398035), + (1.1790137971e-05, 20.2853841412, 14.7863406821), + (1.1790137971e-05, 18.5102818197, 11.7117732726), + (1.16078600148, 18.0665062393, 10.9431314203), + ] + item = self.file.create_entity( + "IfcBSplineCurveWithKnots", + Degree=3, + ControlPointsList=[self.file.create_entity("IfcCartesianPoint", p) for p in points], + KnotMultiplicities=(4, 1, 1, 4), + Knots=(0.0, 1 / 3, 2 / 3, 1.0), + ) + settings = ifcopenshell.geom.settings() + shape = ifcopenshell.geom.create_shape(settings, item) + assert shape.verts