Merge pull request #5362 from IfcOpenShell/fix-ifccircle-ifcellipse-processing

Fix error processing IfcCircle/IfcEllipse #5352
This commit is contained in:
Thomas Krijnen
2024-09-12 20:19:17 +02:00
committed by GitHub
7 changed files with 304 additions and 134 deletions
+9 -2
View File
@@ -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;
+21 -1
View File
@@ -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<ifcopenshell::geometry::taxonomy::type_by_kind::type<N>>(item);
auto concrete_item = std::static_pointer_cast<ifcopenshell::geometry::taxonomy::type_by_kind::type<N>>(item);
return kernel->convert_impl(concrete_item, results);
} else {
return dispatch_conversion<N + 1>::dispatch(kernel, item_kind, item, results);
@@ -102,6 +102,26 @@ namespace {
}
};
template <size_t N>
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<ifcopenshell::geometry::taxonomy::upgrades::type<N>>(item);
if (concrete_item) {
return kernel->convert_impl(concrete_item, results);
} else {
return dispatch_with_upgrade<N + 1>::dispatch(kernel, item, results);
}
}
};
template <>
struct dispatch_with_upgrade<ifcopenshell::geometry::taxonomy::upgrades::max> {
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 <class T, class Tuple>
struct TupleTypeIndex;
+1
View File
@@ -34,5 +34,6 @@ taxonomy::ptr mapping::map_impl(const IfcSchema::IfcCircle* inst) {
auto c = taxonomy::make<taxonomy::circle>();
c->radius = r;
c->matrix = taxonomy::cast<taxonomy::matrix4>(map(placement));
return c;
}
+157
View File
@@ -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<face::ptr> ifcopenshell::geometry::taxonomy::loop_to_face_upgrade_impl(ptr item) {
boost::optional<face::ptr> face_;
auto loop_ = dcast<loop>(item);
if (loop_) {
loop_->external = true;
face_ = make<face>();
(*face_)->instance = loop_->instance;
(*face_)->matrix = loop_->matrix;
(*face_)->children = { clone(loop_) };
}
return face_;
}
boost::optional<edge::ptr> ifcopenshell::geometry::taxonomy::curve_to_edge_upgrade_impl(ptr item) {
boost::optional<edge::ptr> edge_;
auto circle_ = dcast<circle>(item);
auto ellipse_ = dcast<ellipse>(item);
auto line_ = dcast<line>(item);
auto bspline_curve_ = dcast<bspline_curve>(item);
if (circle_ || ellipse_ || line_ || bspline_curve_) {
edge_ = make<edge>();
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<double>();
}
}
return edge_;
}
boost::optional<loop::ptr> ifcopenshell::geometry::taxonomy::curve_to_loop_upgrade_impl(ptr item) {
boost::optional<loop::ptr> loop_;
auto circle_ = dcast<circle>(item);
auto ellipse_ = dcast<ellipse>(item);
auto line_ = dcast<line>(item);
auto bspline_curve_ = dcast<bspline_curve>(item);
if (circle_ || ellipse_ || line_ || bspline_curve_) {
auto edge_ = make<edge>();
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<double>();
}
loop_ = make<loop>();
(*loop_)->children.push_back(edge_);
}
return loop_;
}
boost::optional<loop::ptr> ifcopenshell::geometry::taxonomy::edge_to_loop_upgrade_impl(ptr item) {
boost::optional<loop::ptr> loop_;
auto edge_ = dcast<edge>(item);
if (edge_) {
loop_ = make<loop>();
(*loop_)->children.push_back(edge_);
}
return loop_;
}
boost::optional<face::ptr> ifcopenshell::geometry::taxonomy::curve_to_face_upgrade_impl(ptr item) {
boost::optional<face::ptr> face_;
auto circle_ = dcast<circle>(item);
auto ellipse_ = dcast<ellipse>(item);
auto line_ = dcast<line>(item);
auto bspline_curve_ = dcast<bspline_curve>(item);
if (circle_ || ellipse_ || line_ || bspline_curve_) {
auto edge_ = make<edge>();
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<double>();
}
auto loop_ = make<loop>();
loop_->children.push_back(edge_);
face_ = make<face>();
(*face_)->instance = loop_->instance;
(*face_)->matrix = loop_->matrix;
(*face_)->children = { clone(loop_) };
}
return face_;
}
boost::optional<piecewise_function::ptr> ifcopenshell::geometry::taxonomy::loop_to_piecewise_function_upgrade_impl(ptr item) {
boost::optional<piecewise_function::ptr> pwf_;
auto loop_ = dcast<loop>(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<point3::ptr>(edge_->start)->ccomponents();
const auto& e = boost::get<point3::ptr>(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<Eigen::Matrix4d(double)> 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<matrix4>(o, axis, refDirection)->components();
};
spans.emplace_back(l, fn);
}
pwf_ = make<piecewise_function>(0.0,spans);
loop_->pwf = pwf_;
}
}
return pwf_;
}
+28 -125
View File
@@ -1035,6 +1035,7 @@ typedef item const* ptr;
typedef std::tuple<matrix4, point3, direction3, line, circle, ellipse, bspline_curve, offset_curve, plane, cylinder, sphere, torus, bspline_surface, edge, loop, face, shell, solid, loft, extrusion, revolve, sweep_along_curve, node, collection, boolean_result, piecewise_function> KindsTuple;
typedef std::tuple<line, circle, ellipse, bspline_curve, offset_curve, loop, edge> CurvesTuple;
typedef std::tuple<plane, cylinder, sphere, torus, bspline_surface, extrusion, revolve> SurfacesTuple;
typedef std::tuple<edge, loop, face, piecewise_function> UpgradesTuple;
}
struct type_by_kind {
@@ -1058,6 +1059,14 @@ typedef item const* ptr;
static const size_t max = std::tuple_size<impl::SurfacesTuple>::value;
};
struct upgrades {
template <std::size_t N>
using type = typename std::tuple_element<N, impl::UpgradesTuple>::type;
static const size_t max = std::tuple_size<impl::UpgradesTuple>::value;
};
boost::optional<face::ptr> loop_to_face_upgrade_impl(ptr item);
template <typename T>
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<T, face>) {
auto loop = taxonomy::dcast<taxonomy::loop>(item);
if (loop) {
loop->external = true;
face_ = taxonomy::make<taxonomy::face>();
(*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<edge::ptr> curve_to_edge_upgrade_impl(ptr item);
template <typename T>
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<T, edge>) {
auto circle = taxonomy::dcast<taxonomy::circle>(item);
auto ellipse = taxonomy::dcast<taxonomy::ellipse>(item);
auto line = taxonomy::dcast<taxonomy::line>(item);
auto bspline_curve = taxonomy::dcast<taxonomy::bspline_curve>(item);
if (circle || ellipse || line || bspline_curve) {
edge_ = taxonomy::make<taxonomy::edge>();
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<double>();
}
}
edge_ = taxonomy::curve_to_edge_upgrade_impl(item);
}
}
@@ -1137,7 +1118,7 @@ typedef item const* ptr;
}
};
boost::optional<loop::ptr> curve_to_loop_upgrade_impl(ptr item);
template <typename T>
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<T, loop>) {
auto circle = taxonomy::dcast<taxonomy::circle>(item);
auto ellipse = taxonomy::dcast<taxonomy::ellipse>(item);
auto line = taxonomy::dcast<taxonomy::line>(item);
auto bspline_curve = taxonomy::dcast<taxonomy::bspline_curve>(item);
if (circle || ellipse || line || bspline_curve) {
auto edge = taxonomy::make<taxonomy::edge>();
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<double>();
}
loop_ = taxonomy::make<taxonomy::loop>();
(*loop_)->children.push_back(edge);
}
loop_ = curve_to_loop_upgrade_impl(item);
}
}
@@ -1187,6 +1144,7 @@ typedef item const* ptr;
}
};
boost::optional<loop::ptr> edge_to_loop_upgrade_impl(ptr item);
template <typename T>
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<T, loop>) {
auto edge = taxonomy::dcast<taxonomy::edge>(item);
if (edge) {
loop_ = taxonomy::make<taxonomy::loop>();
(*loop_)->children.push_back(edge);
}
loop_ = edge_to_loop_upgrade_impl(item);
}
}
@@ -1216,7 +1170,7 @@ typedef item const* ptr;
}
};
boost::optional<face::ptr> curve_to_face_upgrade_impl(ptr item);
template <typename T>
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<T, edge>) {
auto circle = taxonomy::dcast<taxonomy::circle>(item);
auto ellipse = taxonomy::dcast<taxonomy::ellipse>(item);
auto line = taxonomy::dcast<taxonomy::line>(item);
auto bspline_curve = taxonomy::dcast<taxonomy::bspline_curve>(item);
if (circle || ellipse || line || bspline_curve) {
auto edge = taxonomy::make<taxonomy::edge>();
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<double>();
}
auto loop = taxonomy::make<taxonomy::loop>();
loop->children.push_back(edge);
face_ = taxonomy::make<taxonomy::face>();
(*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<piecewise_function::ptr> loop_to_piecewise_function_upgrade_impl(ptr item);
template <typename T>
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<T, piecewise_function>) {
auto loop = taxonomy::dcast<taxonomy::loop>(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<taxonomy::point3::ptr>(edge->start)->ccomponents();
const auto& e = boost::get<taxonomy::point3::ptr>(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<Eigen::Matrix4d(double)> 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<taxonomy::matrix4>(o, axis, refDirection)->components();
};
spans.emplace_back(l, fn);
}
pwf_ = taxonomy::make<taxonomy::piecewise_function>(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<T> upg(u);
curve_to_face_upgrade<T> upg(u);
if (upg) {
return upg;
}
}
{
curve_to_face_upgrade<T> upg(u);
edge_to_loop_upgrade<T> upg(u);
if (upg) {
return upg;
}
@@ -1379,6 +1276,12 @@ typedef item const* ptr;
return upg;
}
}
{
curve_to_loop_upgrade<T> upg(u);
if (upg) {
return upg;
}
}
{
curve_to_face_upgrade<T> upg(u);
if (upg) {
@@ -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]],
@@ -0,0 +1,72 @@
# IfcOpenShell - IFC toolkit and geometry engine
# Copyright (C) 2021 Dion Moult <dion@thinkmoult.com>
#
# 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 <http://www.gnu.org/licenses/>.
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