From 3100f8610fd2591337c285b2d6aca7ae2a0d396a Mon Sep 17 00:00:00 2001 From: Jason Cantarella Date: Tue, 2 Jun 2026 10:37:53 -0400 Subject: [PATCH] delaunayRefine: don't insert a circumcenter coincident with an existing vertex Constrained delaunayRefine (edges fixed via setMarkedEdges) could trace a circumcenter onto an existing vertex -- it ends as a Face point on a triangle corner -- creating a zero-area "needle" triangle that poisons downstream cotan-Laplacian / BFF solves. After insertVertex, if the new vertex has a near-zero incident edge (minInc <= 1e-9 * maxInc) it landed on existing geometry: undo the insertion and return Vertex(). delaunayRefine already treats a Vertex() return as "insertion failed" and continues, leaving the unrefinable constrained corner as a healthy (non-degenerate) triangle. Upstream report: https://github.com/nmwsharp/geometry-central/issues/246 --- src/surface/intrinsic_triangulation.cpp | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/surface/intrinsic_triangulation.cpp b/src/surface/intrinsic_triangulation.cpp index bf9d73dd..c2838f3c 100644 --- a/src/surface/intrinsic_triangulation.cpp +++ b/src/surface/intrinsic_triangulation.cpp @@ -282,7 +282,30 @@ Vertex IntrinsicTriangulation::insertCircumcenter(Face f) { } // === Phase 3: Add the new vertex - return insertVertex(newPositionOnIntrinsic); + Vertex newV = insertVertex(newPositionOnIntrinsic); + + // Proximity guard: a traced circumcenter can land coincident with an existing + // vertex (it ends as a Face/Edge point on a triangle corner). This happens on + // *constrained* triangulations near small input angles, where a bad triangle's + // circumcircle is effectively centered on an existing vertex. Inserting such a + // point creates a zero-area (needle) triangle that poisons downstream solves. + // Detect the resulting near-zero incident edge and undo the insertion; + // delaunayRefine() treats a Vertex() return as "insertion failed" and simply + // leaves that triangle unrefined (which is the correct behavior near an + // unrefinable constrained corner). + if (newV != Vertex()) { + double minInc = std::numeric_limits::infinity(); + double maxInc = 0.; + for (Edge e : newV.adjacentEdges()) { + minInc = std::min(minInc, edgeLengths[e]); + maxInc = std::max(maxInc, edgeLengths[e]); + } + if (minInc <= 1e-9 * maxInc) { + removeInsertedVertex(newV); + return Vertex(); + } + } + return newV; } Vertex IntrinsicTriangulation::insertBarycenter(Face f) {