From ffb69c62adc7ea23153ac68859a3fcf08a8277d2 Mon Sep 17 00:00:00 2001 From: artem-ogre Date: Mon, 13 Nov 2023 14:18:25 +0100 Subject: [PATCH] Fix bug in handling hanging edges when triangulating pseudo-polygons Add a regression test --- CDT/include/Triangulation.h | 20 ++++---- CDT/include/Triangulation.hpp | 92 +++++++++++++++-------------------- CDT/tests/cdt.test.cpp | 9 ++++ CDT/tests/inputs/hanging3.txt | 9 ++++ 4 files changed, 67 insertions(+), 63 deletions(-) create mode 100644 CDT/tests/inputs/hanging3.txt diff --git a/CDT/include/Triangulation.h b/CDT/include/Triangulation.h index 3ee4c24..a7ce1bc 100644 --- a/CDT/include/Triangulation.h +++ b/CDT/include/Triangulation.h @@ -520,7 +520,7 @@ class CDT_EXPORT Triangulation /// State for an iteration of triangulate pseudo-polygon typedef tuple - TriangulatePseudopolygonTask; + TriangulatePseudoPolygonTask; /** * Insert an edge into constraint Delaunay triangulation @@ -538,7 +538,7 @@ class CDT_EXPORT Triangulation Edge edge, Edge originalEdge, EdgeVec& remaining, - std::vector& tppIterations); + std::vector& tppIterations); /** * Insert an edge or its part into constraint Delaunay triangulation @@ -556,7 +556,7 @@ class CDT_EXPORT Triangulation Edge edge, Edge originalEdge, EdgeVec& remaining, - std::vector& tppIterations); + std::vector& tppIterations); /// State for iteration of conforming to edge typedef tuple ConformToEdgeTask; @@ -629,16 +629,16 @@ class CDT_EXPORT Triangulation VertInd iVedge1, VertInd iVedge2, TriInd newNeighbor); - void triangulatePseudopolygon( + void triangulatePseudoPolygon( const std::vector& poly, - const std::vector& outerTris, + unordered_map& outerTris, TriInd iT, TriInd iN, - std::vector& iterations); - void triangulatePseudopolygonIteration( + std::vector& iterations); + void triangulatePseudoPolygonIteration( const std::vector& poly, - const std::vector& outerTris, - std::vector& iterations); + unordered_map& outerTris, + std::vector& iterations); IndexSizeType findDelaunayPoint( const std::vector& poly, IndexSizeType iA, @@ -859,7 +859,7 @@ void Triangulation::insertEdges( if(isFinalized()) throw FinalizedError(CDT_SOURCE_LOCATION); - std::vector tppIterations; + std::vector tppIterations; EdgeVec remaining; for(; first != last; ++first) { diff --git a/CDT/include/Triangulation.hpp b/CDT/include/Triangulation.hpp index 5fe0d2b..5dbcc61 100644 --- a/CDT/include/Triangulation.hpp +++ b/CDT/include/Triangulation.hpp @@ -521,7 +521,7 @@ void Triangulation::insertEdgeIteration( const Edge edge, const Edge originalEdge, EdgeVec& remaining, - std::vector& tppIterations) + std::vector& tppIterations) { const VertInd iA = edge.v1(); VertInd iB = edge.v2(); @@ -556,15 +556,15 @@ void Triangulation::insertEdgeIteration( Triangle t = triangles[iT]; std::vector intersected(1, iT); std::vector polyL, polyR; - std::vector outerTrisL, outerTrisR; polyL.reserve(2); polyL.push_back(iA); polyL.push_back(iVL); - outerTrisL.push_back(edgeNeighbor(t, iA, iVL)); polyR.reserve(2); polyR.push_back(iA); polyR.push_back(iVR); - outerTrisR.push_back(edgeNeighbor(t, iA, iVR)); + unordered_map outerTris; + outerTris[Edge(iA, iVL)] = edgeNeighbor(t, iA, iVL); + outerTris[Edge(iA, iVR)] = edgeNeighbor(t, iA, iVR); VertInd iV = iA; while(!t.containsVertex(iB)) @@ -616,14 +616,20 @@ void Triangulation::insertEdgeIteration( locatePointLine(vertices[iVopo], a, b, distanceTolerance); if(loc == PtLineLocation::Left) { - outerTrisL.push_back(edgeNeighbor(tOpo, polyL.back(), iVopo)); + const Edge e(polyL.back(), iVopo); + const TriInd outer = edgeNeighbor(tOpo, e.v1(), e.v2()); + if(!outerTris.insert(std::make_pair(e, outer)).second) + outerTris[e] = noNeighbor; // hanging edge detected polyL.push_back(iVopo); iV = iVL; iVL = iVopo; } else if(loc == PtLineLocation::Right) { - outerTrisR.push_back(edgeNeighbor(tOpo, polyR.back(), iVopo)); + const Edge e(polyR.back(), iVopo); + const TriInd outer = edgeNeighbor(tOpo, e.v1(), e.v2()); + if(!outerTris.insert(std::make_pair(e, outer)).second) + outerTris[e] = noNeighbor; // hanging edge detected polyR.push_back(iVopo); iV = iVR; iVR = iVopo; @@ -635,8 +641,8 @@ void Triangulation::insertEdgeIteration( iT = iTopo; t = triangles[iT]; } - outerTrisL.push_back(edgeNeighbor(t, polyL.back(), iB)); - outerTrisR.push_back(edgeNeighbor(t, polyR.back(), iB)); + outerTris[Edge(polyL.back(), iB)] = edgeNeighbor(t, polyL.back(), iB); + outerTris[Edge(polyR.back(), iB)] = edgeNeighbor(t, polyR.back(), iB); polyL.push_back(iB); polyR.push_back(iB); @@ -647,26 +653,16 @@ void Triangulation::insertEdgeIteration( pivotVertexTriangleCW(iA); if(m_vertTris[iB] == intersected.back()) pivotVertexTriangleCW(iB); - // Handle outer triangles for the cases of hanging edges - typedef std::vector::iterator TriIndIt; - std::sort(intersected.begin(), intersected.end()); - for(TriIndIt it = outerTrisR.begin(); it != outerTrisR.end(); ++it) - if(std::binary_search(intersected.begin(), intersected.end(), *it)) - *it = noNeighbor; - for(TriIndIt it = outerTrisL.begin(); it != outerTrisL.end(); ++it) - if(std::binary_search(intersected.begin(), intersected.end(), *it)) - *it = noNeighbor; // Remove intersected triangles typedef std::vector::const_iterator TriIndCit; for(TriIndCit it = intersected.begin(); it != intersected.end(); ++it) makeDummy(*it); { // Triangulate pseudo-polygons on both sides std::reverse(polyR.begin(), polyR.end()); - std::reverse(outerTrisR.begin(), outerTrisR.end()); const TriInd iTL = addTriangle(); const TriInd iTR = addTriangle(); - triangulatePseudopolygon(polyL, outerTrisL, iTL, iTR, tppIterations); - triangulatePseudopolygon(polyR, outerTrisR, iTR, iTL, tppIterations); + triangulatePseudoPolygon(polyL, outerTris, iTL, iTR, tppIterations); + triangulatePseudoPolygon(polyR, outerTris, iTR, iTL, tppIterations); } if(iB != edge.v2()) // encountered point on the edge @@ -688,7 +684,7 @@ void Triangulation::insertEdge( Edge edge, const Edge originalEdge, EdgeVec& remaining, - std::vector& tppIterations) + std::vector& tppIterations) { // use iteration over recursion to avoid stack overflows remaining.clear(); @@ -776,8 +772,7 @@ void Triangulation::conformToEdgeIteration( // don't count super-triangle vertices e1 = Edge(e1.v1() - m_nTargetVerts, e1.v2() - m_nTargetVerts); e2 = Edge(e2.v1() - m_nTargetVerts, e2.v2() - m_nTargetVerts); - throw IntersectingConstraintsError( - e1, e2, CDT_SOURCE_LOCATION); + throw IntersectingConstraintsError(e1, e2, CDT_SOURCE_LOCATION); } break; case IntersectingConstraintEdges::TryResolve: { @@ -1597,23 +1592,15 @@ void Triangulation::changeNeighbor( } template -void Triangulation::triangulatePseudopolygon( +void Triangulation::triangulatePseudoPolygon( const std::vector& poly, - const std::vector& outerTris, - const TriInd iT, - const TriInd iN, - std::vector& iterations) + unordered_map& outerTris, + TriInd iT, + TriInd iN, + std::vector& iterations) { assert(poly.size() > 2); - - // note: needed for proper linking with outer triangles - // during pseudo-polygon triangulation, vertex triangle - // will be set back, see asserts at the end - for(std::size_t i = 1; i < outerTris.size(); ++i) - if(outerTris[i] == noNeighbor) - m_vertTris[poly[i]] = noNeighbor; - - // note: uses interation instead of recursion to avoid stack overflows + // note: uses iteration instead of recursion to avoid stack overflows iterations.clear(); iterations.push_back(make_tuple( IndexSizeType(0), @@ -1623,19 +1610,15 @@ void Triangulation::triangulatePseudopolygon( Index(0))); while(!iterations.empty()) { - triangulatePseudopolygonIteration(poly, outerTris, iterations); + triangulatePseudoPolygonIteration(poly, outerTris, iterations); } - - // make sure adjacent triangles were restored - for(std::size_t i = 0; i < poly.size(); ++i) - assert(m_vertTris[poly[i]] != noNeighbor); } template -void Triangulation::triangulatePseudopolygonIteration( +void Triangulation::triangulatePseudoPolygonIteration( const std::vector& poly, - const std::vector& outerTris, - std::vector& iterations) + unordered_map& outerTris, + std::vector& iterations) { IndexSizeType iA, iB; TriInd iT, iParent; @@ -1651,11 +1634,10 @@ void Triangulation::triangulatePseudopolygonIteration( const VertInd a = poly[iA]; const VertInd b = poly[iB]; const VertInd c = poly[iC]; + // split pseudo-polygon in two parts and triangulate them - // - // note: first part needs to be pushed on stack last - // in order to be processed first - // + // note: second part needs to be pushed on stack first to be processed first + // second part: points after the Delaunay point if(iB - iC > 1) { @@ -1664,13 +1646,16 @@ void Triangulation::triangulatePseudopolygonIteration( } else // pseudo-poly is reduced to a single outer edge { - const TriInd outerTri = outerTris[iC]; + const Edge outerEdge(b, c); + const TriInd outerTri = outerTris.at(outerEdge); if(outerTri != noNeighbor) { assert(outerTri != iT); t.neighbors[1] = outerTri; changeNeighbor(outerTri, c, b, iT); } + else + outerTris.at(outerEdge) = iT; } // first part: points before the Delaunay point if(iC - iA > 1) @@ -1680,14 +1665,16 @@ void Triangulation::triangulatePseudopolygonIteration( } else { // pseudo-poly is reduced to a single outer edge - const TriInd outerTri = - outerTris[iA] != noNeighbor ? outerTris[iA] : m_vertTris[c]; + const Edge outerEdge(c, a); + const TriInd outerTri = outerTris.at(outerEdge); if(outerTri != noNeighbor) { assert(outerTri != iT); t.neighbors[2] = outerTri; changeNeighbor(outerTri, c, a, iT); } + else + outerTris.at(outerEdge) = iT; } // Finalize triangle // note: only when triangle is finalized to we add it as a neighbor to @@ -1695,7 +1682,6 @@ void Triangulation::triangulatePseudopolygonIteration( triangles[iParent].neighbors[iInParent] = iT; t.neighbors[0] = iParent; t.vertices = detail::arr3(a, b, c); - setAdjacentTriangle(a, iT); setAdjacentTriangle(c, iT); } diff --git a/CDT/tests/cdt.test.cpp b/CDT/tests/cdt.test.cpp index 9b0c9f3..e01eea2 100644 --- a/CDT/tests/cdt.test.cpp +++ b/CDT/tests/cdt.test.cpp @@ -936,3 +936,12 @@ TEST_CASE("Regression test issue #154 (3)", "") REQUIRE(topologyString(cdt) == topologyString(outFile)); } } + +TEST_CASE("Regression test: hanging edge in pseudo-poly", "") +{ + auto [vv, ee] = readInputFromFile("inputs/hanging3.txt"); + auto cdt = Triangulation{}; + cdt.insertVertices(vv); + cdt.insertEdges(ee); + REQUIRE(CDT::verifyTopology(cdt)); +} diff --git a/CDT/tests/inputs/hanging3.txt b/CDT/tests/inputs/hanging3.txt new file mode 100644 index 0000000..eb6bce4 --- /dev/null +++ b/CDT/tests/inputs/hanging3.txt @@ -0,0 +1,9 @@ +7 1 +1534.79 789.063 +-785.078 788.629 +789.094 533.067 +1034.16 789.067 +785.067 513.067 +784 664.004 +513.064 789.067 +0 1 \ No newline at end of file