From 36a0a0f2dd40d500e35b200f7b128ea84b58e0d7 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Tue, 8 Oct 2024 14:53:56 +0200 Subject: [PATCH] Work-around for tiny radii sweeps #5474 --- .../kernels/opencascade/OpenCascadeKernel.h | 2 +- src/ifcgeom/kernels/opencascade/face.cpp | 6 ++- src/ifcgeom/kernels/opencascade/loop.cpp | 17 +++++++- .../kernels/opencascade/sweep_along_curve.cpp | 41 ++++++++++++++++++- 4 files changed, 60 insertions(+), 6 deletions(-) diff --git a/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.h b/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.h index 8b98cbdf7a..b21e424934 100644 --- a/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.h +++ b/src/ifcgeom/kernels/opencascade/OpenCascadeKernel.h @@ -128,7 +128,7 @@ public: const IfcGeom::ConversionResults& entity_shapes, const ifcopenshell::geometry::taxonomy::matrix4& entity_trsf, IfcGeom::ConversionResults& cut_shapes); virtual bool unify_shapes(const IfcGeom::ConversionResults& input, IfcGeom::ConversionResults& output); - typedef boost::variant curve_creation_visitor_result_type; + 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); diff --git a/src/ifcgeom/kernels/opencascade/face.cpp b/src/ifcgeom/kernels/opencascade/face.cpp index e37c43d1bc..61c0e6e27a 100644 --- a/src/ifcgeom/kernels/opencascade/face.cpp +++ b/src/ifcgeom/kernels/opencascade/face.cpp @@ -151,7 +151,7 @@ namespace { // 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) { + if (crv_or_wire.which() == 2) { const auto& w = boost::get(crv_or_wire); return w; } else { @@ -163,8 +163,10 @@ namespace { // @todo unify with trimmed curve handling auto crv_or_wire = kernel->convert_curve(i); if (crv_or_wire.which() == 0) { + throw std::runtime_error("Failed to obtain curve"); + } else if (crv_or_wire.which() == 1) { return boost::get(crv_or_wire); - } else { + } else if (crv_or_wire.which() == 2) { // @todo const double precision_ = 1.e-5; Logger::Warning("Approximating BasisCurve due to possible discontinuities", i->instance); diff --git a/src/ifcgeom/kernels/opencascade/loop.cpp b/src/ifcgeom/kernels/opencascade/loop.cpp index 403d55835f..a03a044e82 100644 --- a/src/ifcgeom/kernels/opencascade/loop.cpp +++ b/src/ifcgeom/kernels/opencascade/loop.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #if OCC_VERSION_HEX < 0x70600 @@ -98,7 +99,7 @@ namespace { if (!kernel->convert(l, wire)) { throw std::runtime_error("Failed to convert loop to wire"); } - return result; + return result = wire; } OpenCascadeKernel::curve_creation_visitor_result_type operator()(const taxonomy::edge::ptr& e) { @@ -119,6 +120,9 @@ namespace { auto crv_or_wire = kernel->convert_curve(e_basis); Handle(Geom_Curve) curve; if (crv_or_wire.which() == 0) { + // raise exception + return result; + } else if (crv_or_wire.which() == 1) { curve = boost::get(crv_or_wire); } else { // @todo @@ -355,6 +359,17 @@ bool OpenCascadeKernel::convert(const taxonomy::loop::ptr loop, TopoDS_Wire& wir wire = mw.Wire(); } + if (loop->matrix && !loop->matrix->is_identity()) { + const auto& m = loop->matrix->ccomponents(); + gp_Trsf tr; + tr.SetValues( + m(0, 0), m(0, 1), m(0, 2), m(0, 3), + m(1, 0), m(1, 1), m(1, 2), m(1, 3), + m(2, 0), m(2, 1), m(2, 2), m(2, 3) + ); + wire = TopoDS::Wire(BRepBuilderAPI_Transform(wire, tr).Shape()); + } + return true; } diff --git a/src/ifcgeom/kernels/opencascade/sweep_along_curve.cpp b/src/ifcgeom/kernels/opencascade/sweep_along_curve.cpp index bb3fa7f190..a4f33371ea 100644 --- a/src/ifcgeom/kernels/opencascade/sweep_along_curve.cpp +++ b/src/ifcgeom/kernels/opencascade/sweep_along_curve.cpp @@ -34,8 +34,9 @@ using namespace IfcGeom::util; bool OpenCascadeKernel::convert(const taxonomy::sweep_along_curve::ptr scs, TopoDS_Shape& result) { auto w = convert_curve(scs->curve); - if (w.which() == 0) { + if (w.which() != 2) { Logger::Error("Unsupported directrix"); + return false; } TopoDS_Shape face; convert(taxonomy::cast(scs->basis), face); @@ -136,6 +137,9 @@ bool OpenCascadeKernel::convert(const taxonomy::sweep_along_curve::ptr scs, Topo builder.SetMode(surface_face); } builder.Build(); + if (!builder.IsDone()) { + return false; + } builder.MakeSolid(); result = builder.Shape(); @@ -144,12 +148,45 @@ bool OpenCascadeKernel::convert(const taxonomy::sweep_along_curve::ptr scs, Topo bool OpenCascadeKernel::convert_impl(const taxonomy::sweep_along_curve::ptr scs, IfcGeom::ConversionResults& results) { TopoDS_Shape shape; + // For tiny radii occt will fail building the sweep, in which case we enlarge the inputs to occt, and add a scale matrix to the output + bool enlarged = false; + static double enlarge_factor = 1000.; + if (scs->basis->kind() == taxonomy::FACE) { + auto w = std::static_pointer_cast(scs->basis)->children[0]; + if (w->children.size() == 1 && w->children[0]->basis && w->children[0]->basis->kind() == taxonomy::CIRCLE) { + auto circ = std::static_pointer_cast(w->children[0]->basis); + enlarged = circ->radius < 1.e-4; + if (enlarged) { + // @todo immutability + circ->radius *= enlarge_factor; + auto crv = std::static_pointer_cast(scs->curve); + if (crv->matrix) { + crv->matrix = taxonomy::make( + Eigen::Scaling(enlarge_factor) * + crv->matrix->ccomponents() + ); + } else { + crv->matrix = taxonomy::make(); + crv->matrix->components().topLeftCorner<3, 3>() = Eigen::Scaling(enlarge_factor, enlarge_factor, enlarge_factor).toDenseMatrix(); + } + } + } + } if (!convert(scs, shape)) { return false; } + taxonomy::matrix4::ptr m; + if (enlarged) { + m = taxonomy::make( + Eigen::Scaling(1. / enlarge_factor) * + scs->matrix->ccomponents() + ); + } else { + m = scs->matrix; + } results.emplace_back(ConversionResult( scs->instance->as()->id(), - scs->matrix, + m, new OpenCascadeShape(shape), scs->surface_style ));