From 21d39d8a2333d513b9180495cdf2c20385bc196e Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Fri, 18 Apr 2025 10:08:57 +0200 Subject: [PATCH] Use exact topology points at parametric range bounds to guarantee welding #4487 --- src/ifcgeom/IfcGeomRepresentation.cpp | 5 +++- .../OpenCascadeConversionResult.cpp | 28 ++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/ifcgeom/IfcGeomRepresentation.cpp b/src/ifcgeom/IfcGeomRepresentation.cpp index c888efba2a..2c1983108e 100644 --- a/src/ifcgeom/IfcGeomRepresentation.cpp +++ b/src/ifcgeom/IfcGeomRepresentation.cpp @@ -397,7 +397,10 @@ int IfcGeom::Representation::Triangulation::addVertex(int item_id, int material_ if (settings().get().get()) { const VertexKey key = std::make_tuple(item_id, material_index, X, Y, Z); typename VertexKeyMap::const_iterator it = welds.find(key); - if (it != welds.end()) return it->second; + if (it != welds.end()) { + // Return index for previously encountered point + return it->second; + } i = (int)(welds.size() + weld_offset_); welds[key] = i; } diff --git a/src/ifcgeom/kernels/opencascade/OpenCascadeConversionResult.cpp b/src/ifcgeom/kernels/opencascade/OpenCascadeConversionResult.cpp index 32f90904a1..1382f1fdc9 100644 --- a/src/ifcgeom/kernels/opencascade/OpenCascadeConversionResult.cpp +++ b/src/ifcgeom/kernels/opencascade/OpenCascadeConversionResult.cpp @@ -248,8 +248,34 @@ void ifcopenshell::geometry::OpenCascadeShape::Triangulate(ifcopenshell::geometr int previous = -1; const bool reversed = texp.Value().Orientation() == TopAbs_REVERSED; bool first = true; + + gp_Pnt p0, p1; + double u0 = std::numeric_limits::quiet_NaN(), u1 = std::numeric_limits::quiet_NaN(); + if (auto crv = BRep_Tool::Curve(TopoDS::Edge(texp.Value()), u0, u1)) { + TopoDS_Vertex v0, v1; + TopExp::Vertices(TopoDS::Edge(texp.Value()), v0, v1, false); + if (!v0.IsNull() && !v1.IsNull()) { + p0 = BRep_Tool::Pnt(v0); + p1 = BRep_Tool::Pnt(v1); + } else { + u0 = u1 = std::numeric_limits::quiet_NaN(); + } + } + for (int i = (reversed ? n : 1); reversed ? (i >= 1) : (i <= n); i += reversed ? -1 : 1) { - gp_XYZ p = tessellater.Value(i).XYZ(); + gp_XYZ p; + if (std::fabs(tessellater.Parameter(i) - u0) < 1.e-7) { + // Use the exact points from the topology when parameter is close to the begin or end of the parametric range + // This guarantees points are properly welded, because the GCPnts_QuasiUniformDeflection could otherwise introduce + // minor differences between the approximated points from shared vertices. + // @todo Using GCPnts_QuasiUniformDeflection on linear edges is pure lazyness + p = p0.XYZ(); + } else if (std::fabs(tessellater.Parameter(i) - u1) < 1.e-7) { + p = p1.XYZ(); + } else { + p = tessellater.Value(i).XYZ(); + } + auto p_local = p; taxonomy_transform(place.components_, p);