diff --git a/src/ifcgeom/kernels/opencascade/sweep_along_curve.cpp b/src/ifcgeom/kernels/opencascade/sweep_along_curve.cpp index 0cfcf7a355..c971291868 100644 --- a/src/ifcgeom/kernels/opencascade/sweep_along_curve.cpp +++ b/src/ifcgeom/kernels/opencascade/sweep_along_curve.cpp @@ -84,6 +84,50 @@ namespace { } bool OpenCascadeKernel::convert(const taxonomy::sweep_along_curve::ptr scs, TopoDS_Shape& result) { + using namespace ifcopenshell::geometry; + + bool applied_temporary_offset = false; + Eigen::Vector3d mean; + + auto curve = scs->curve; + + // Apply temporary offset if the geometry is far away from origin + // Re: https://github.com/IfcOpenShell/IfcOpenShell/issues/7408 + // The norm2 that used as a treshold is actually really small though + // is this a coincedence that it solves the problem in this one test case? + if (curve->kind() == taxonomy::LOOP && std::dynamic_pointer_cast(curve)->is_polyhedron()) { + Eigen::Vector3d sum = Eigen::Vector3d::Zero(); + size_t count = 0; + + visit_2(std::dynamic_pointer_cast(curve), [&](const taxonomy::point3::ptr& p) { + const Eigen::Vector3d& coords = p->ccomponents(); + + sum += coords; + ++count; + }); + + mean = (count > 0) ? (sum / static_cast(count)).eval() : Eigen::Vector3d::Zero().eval(); + + if (mean.norm() > 1.e2) { + curve = taxonomy::loop::ptr((taxonomy::loop*)scs->curve->clone_()); + applied_temporary_offset = true; + std::set unique_points; + for (auto& e : std::dynamic_pointer_cast(curve)->children) { + auto* a = boost::get(&e->start); + auto* b = boost::get(&e->end); + if (a) { + unique_points.insert(*a); + } + if (b) { + unique_points.insert(*b); + } + } + for (auto& p : unique_points) { + p->components() -= mean; + } + } + } + auto w = convert_curve(scs->curve); if (w.which() != 2) { Logger::Error("Unsupported directrix"); @@ -207,13 +251,13 @@ bool OpenCascadeKernel::convert(const taxonomy::sweep_along_curve::ptr scs, Topo for (int i = 0; i < 2; ++i) { for (TopExp_Explorer exp(face, TopAbs_WIRE); exp.More(); exp.Next()) { const auto& section = TopoDS::Wire(exp.Current()); - if (section.IsSame(outer) != i == 0) { + if (section.IsSame(outer) != (i == 0)) { continue; } BRepOffsetAPI_MakePipeShell builder(wire); builder.Add(section); - builder.SetTransitionMode(contains_circular_segments(wire) && wire_is_c1_continuous(wire, 1.e-2) ? BRepBuilderAPI_Transformed : BRepBuilderAPI_RightCorner); + builder.SetTransitionMode(contains_circular_segments(wire) && wire_is_c1_continuous(wire, 1.e-2) ? BRepBuilderAPI_Transformed : BRepBuilderAPI_RightCorner); if (directrix_on_plane) { builder.SetMode(pln.Axis().Direction()); } else if (!is_plane) { @@ -238,8 +282,8 @@ bool OpenCascadeKernel::convert(const taxonomy::sweep_along_curve::ptr scs, Topo mf1.reset(new BRepBuilderAPI_MakeFace(f1)); } - for (TopExp_Explorer exp(builder.Shape(), TopAbs_FACE); exp.More(); exp.Next()) { - BB.Add(comp, exp.Current()); + for (TopExp_Explorer exp2(builder.Shape(), TopAbs_FACE); exp2.More(); exp2.Next()) { + BB.Add(comp, exp2.Current()); } } } @@ -254,6 +298,12 @@ bool OpenCascadeKernel::convert(const taxonomy::sweep_along_curve::ptr scs, Topo result = BRepBuilderAPI_MakeSolid(comp).Solid(); + if (applied_temporary_offset) { + gp_Trsf trsf; + trsf.SetTranslation(gp_Vec(-mean.x(), -mean.y(), -mean.z())); + result.Move(trsf); + } + return true; } diff --git a/src/ifcgeom/taxonomy.h b/src/ifcgeom/taxonomy.h index bba587654e..d3ec5c5e17 100644 --- a/src/ifcgeom/taxonomy.h +++ b/src/ifcgeom/taxonomy.h @@ -1649,42 +1649,34 @@ typedef item const* ptr; } template - void visit_2(const typename U::ptr& c, const Fn& fn) { + void visit_2(const typename U::ptr& collection, const Fn& fn) { static_assert(std::is_same::value, "@todo Only implemented for point3"); - for (auto& i : c->children) { + for (auto& child : collection->children) { // @todo Sad... now that we have templated collection members, // we can't generally use collection_base anymore as a cast target. - if (auto s = taxonomy::dcast(i)) { - visit_2(s, fn); - } - else if (auto s = taxonomy::dcast(i)) { - visit_2(s, fn); - } - else if (auto s = taxonomy::dcast(i)) { - visit_2(s, fn); - } - else if (auto s = taxonomy::dcast(i)) { - visit_2(s, fn); - } - else if (auto s = taxonomy::dcast(i)) { - visit_2(s, fn); - } - else if (auto s = taxonomy::dcast(i)) { - visit_2(s, fn); - } - else if (auto s = taxonomy::dcast(i)) { - visit_2(s, fn); - } - else if (auto pt = taxonomy::dcast(i)) { + if (auto col = std::dynamic_pointer_cast(child)) { + visit_2(col, fn); + } else if (auto loop = std::dynamic_pointer_cast(child)) { + visit_2(loop, fn); + } else if (auto face = std::dynamic_pointer_cast(child)) { + visit_2(face, fn); + } else if (auto shell = std::dynamic_pointer_cast(child)) { + visit_2(shell, fn); + } else if (auto solid = std::dynamic_pointer_cast(child)) { + visit_2(solid, fn); + } else if (auto loft = std::dynamic_pointer_cast(child)) { + visit_2(loft, fn); + } else if (auto bl = std::dynamic_pointer_cast(child)) { + visit_2(bl, fn); + } else if (auto pt = std::dynamic_pointer_cast(child)) { fn(pt); - } - else if (auto l = taxonomy::dcast(i)) { + } else if (auto ed = std::dynamic_pointer_cast(child)) { // @todo maybe make edge a collection then as well? - if (l->start.which() == 1) { - fn(boost::get(l->start)); + if (ed->start.which() == 1) { + fn(boost::get(ed->start)); } - if (l->end.which() == 1) { - fn(boost::get(l->end)); + if (ed->end.which() == 1) { + fn(boost::get(ed->end)); } } }