@@ -103,27 +103,47 @@ void Triangulation<T, TNearPointLocator>::eraseSuperTriangle()
103103{
104104 if (m_superGeomType != SuperGeometryType::SuperTriangle)
105105 return ;
106+ finalizeTriangulation (collectSuperTriangle ());
107+ }
108+
109+ template <typename T, typename TNearPointLocator>
110+ void Triangulation<T, TNearPointLocator>::eraseOuterTriangles()
111+ {
112+ finalizeTriangulation (collectOuterTriangles ());
113+ }
114+
115+ template <typename T, typename TNearPointLocator>
116+ void Triangulation<T, TNearPointLocator>::eraseOuterTrianglesAndHoles()
117+ {
118+ finalizeTriangulation (collectOuterTrianglesAndHoles ());
119+ }
120+
121+ template <typename T, typename TNearPointLocator>
122+ TriIndUSet Triangulation<T, TNearPointLocator>::collectSuperTriangle() const
123+ {
106124 // find triangles adjacent to super-triangle's vertices
107125 TriIndUSet toErase;
126+ if (m_superGeomType != SuperGeometryType::SuperTriangle)
127+ return toErase;
108128 for (TriInd iT (0 ); iT < TriInd (triangles.size ()); ++iT)
109129 {
110130 if (touchesSuperTriangle (triangles[iT]))
111131 toErase.insert (iT);
112132 }
113- finalizeTriangulation ( toErase) ;
133+ return toErase;
114134}
115135
116136template <typename T, typename TNearPointLocator>
117- void Triangulation<T, TNearPointLocator>::eraseOuterTriangles()
137+ TriIndUSet Triangulation<T, TNearPointLocator>::collectOuterTriangles() const
118138{
119139 assert (m_vertTris[0 ] != noNeighbor);
120140 const std::stack<TriInd> seed (std::deque<TriInd>(1 , m_vertTris[0 ]));
121- const TriIndUSet toErase = growToBoundary (seed);
122- finalizeTriangulation (toErase);
141+ return growToBoundary (seed);
123142}
124143
125144template <typename T, typename TNearPointLocator>
126- void Triangulation<T, TNearPointLocator>::eraseOuterTrianglesAndHoles()
145+ TriIndUSet
146+ Triangulation<T, TNearPointLocator>::collectOuterTrianglesAndHoles() const
127147{
128148 const std::vector<LayerDepth> triDepths = calculateTriangleDepths ();
129149 TriIndUSet toErase;
@@ -133,7 +153,7 @@ void Triangulation<T, TNearPointLocator>::eraseOuterTrianglesAndHoles()
133153 if (triDepths[iT] % 2 == 0 )
134154 toErase.insert (static_cast <TriInd>(iT));
135155 }
136- finalizeTriangulation ( toErase) ;
156+ return toErase;
137157}
138158
139159// / Remap removing super-triangle: subtract 3 from vertices
@@ -1403,9 +1423,9 @@ bool Triangulation<T, TNearPointLocator>::isRefinementNeeded(
14031423 switch (refinementCriterion)
14041424 {
14051425 case RefinementCriterion::SmallestAngle:
1406- return smallestAngle (a, b, c) <= refinementThreshold;
1426+ return smallestAngle (a, b, c) < refinementThreshold;
14071427 case RefinementCriterion::LargestArea:
1408- return area (a, b, c) >= refinementThreshold;
1428+ return area (a, b, c) > refinementThreshold;
14091429 }
14101430 assert (false ); // unreachable code
14111431 return false ;
@@ -1477,7 +1497,8 @@ TriIndVec Triangulation<T, TNearPointLocator>::resolveEncroachedEdges(
14771497 const VertInd steinerVerticesOffset,
14781498 const V2d<T>* const circumcenterOrNull,
14791499 const RefinementCriterion::Enum refinementCriterion,
1480- const T badTriangleThreshold)
1500+ const T badTriangleThreshold,
1501+ TriIndUSet* const toEraseOrNull)
14811502{
14821503 std::vector<TriInd> badTriangles;
14831504
@@ -1490,16 +1511,18 @@ TriIndVec Triangulation<T, TNearPointLocator>::resolveEncroachedEdges(
14901511 continue ;
14911512 }
14921513 // split encroached edge
1493- const VertInd iSplitVert =
1494- splitEncroachedEdge ( edge, steinerVerticesOffset);
1514+ const VertInd iSplitVert = splitEncroachedEdge (
1515+ edge, steinerVerticesOffset, toEraseOrNull );
14951516 --remainingVertexBudget;
14961517
14971518 const TriInd start = m_vertTris[iSplitVert];
14981519 TriInd iT = start;
14991520 do
15001521 {
15011522 const Triangle& t = triangles[iT];
1502- if (circumcenterOrNull &&
1523+ const bool isMarkedForErasure =
1524+ toEraseOrNull && toEraseOrNull->count (iT);
1525+ if (circumcenterOrNull && !isMarkedForErasure &&
15031526 isRefinementNeeded (t, refinementCriterion, badTriangleThreshold))
15041527 {
15051528 badTriangles.push_back (iT);
@@ -1525,13 +1548,32 @@ TriIndVec Triangulation<T, TNearPointLocator>::resolveEncroachedEdges(
15251548template <typename T, typename TNearPointLocator>
15261549VertInd Triangulation<T, TNearPointLocator>::splitEncroachedEdge(
15271550 const Edge edge,
1528- const VertInd steinerVerticesOffset)
1551+ const VertInd steinerVerticesOffset,
1552+ TriIndUSet* const toEraseOrNull)
15291553{
15301554 const V2d<T>& start = vertices[edge.v1 ()];
15311555 const V2d<T>& end = vertices[edge.v2 ()];
1556+
1557+ TriInd iT, iTopo;
1558+ std::tie (iT, iTopo) = edgeTriangles (edge.v1 (), edge.v2 ());
1559+ assert (iT != invalidIndex && iTopo != invalidIndex);
1560+
15321561 T split = T (0.5 );
1533- // check if any of the split edge vertices are Steiner vertices
1534- if (edge.v1 () >= steinerVerticesOffset || edge.v2 () >= steinerVerticesOffset)
1562+ // Use the concentric-shell splitting rule only when the edge is a
1563+ // subsegment terminating at a Steiner vertex that is itself an endpoint
1564+ // of another fixed (sub)segment meeting it at a small angle: splitting
1565+ // exactly in half in that case can lead to non-termination as the two
1566+ // segments keep encroaching on each other's ever-shrinking halves.
1567+ const VertInd v3 = opposedVertex (triangles[iT], iTopo);
1568+ const VertInd v4 = opposedVertex (triangles[iTopo], iT);
1569+ if ((edge.v1 () < steinerVerticesOffset &&
1570+ edge.v2 () >= steinerVerticesOffset &&
1571+ (fixedEdges.find (Edge (v3, edge.v1 ())) != fixedEdges.end () ||
1572+ fixedEdges.find (Edge (v4, edge.v1 ())) != fixedEdges.end ())) ||
1573+ (edge.v2 () < steinerVerticesOffset &&
1574+ edge.v1 () >= steinerVerticesOffset &&
1575+ (fixedEdges.find (Edge (v3, edge.v2 ())) != fixedEdges.end () ||
1576+ fixedEdges.find (Edge (v4, edge.v2 ())) != fixedEdges.end ())))
15351577 {
15361578 // In Ruppert's paper, he used D(0.01) factor to divide edge length, but
15371579 // that introduces FP rounding errors, so it's avoided.
@@ -1547,7 +1589,6 @@ VertInd Triangulation<T, TNearPointLocator>::splitEncroachedEdge(
15471589 {
15481590 nearestPowerOfTwo *= T (0.5 );
15491591 }
1550- assert (abs (nearestPowerOfTwo - pow (2 , round (log (d) / log (2.0 )))) < 1e6 );
15511592 split = nearestPowerOfTwo / len;
15521593 if (edge.v1 () >= steinerVerticesOffset)
15531594 split = T (1 ) - split;
@@ -1556,15 +1597,21 @@ VertInd Triangulation<T, TNearPointLocator>::splitEncroachedEdge(
15561597 const V2d<T> mid = V2d<T>(
15571598 detail::lerp (start.x , end.x , split),
15581599 detail::lerp (start.y , end.y , split));
1559- TriInd iT, iTopo;
1560- std::tie (iT, iTopo) = edgeTriangles (edge.v1 (), edge.v2 ());
1561- assert (iT != invalidIndex && iTopo != invalidIndex);
15621600
15631601 const VertInd iMid = addSplitEdgeVertex (mid, iT, iTopo);
15641602 if (fixedEdges.find (edge) != fixedEdges.end ())
15651603 {
15661604 splitFixedEdge (edge, iMid);
15671605 }
1606+ // splitting reuses iT/iTopo for two of the four resulting triangles and
1607+ // appends the other two: propagate erasure marks to the new triangles
1608+ if (toEraseOrNull)
1609+ {
1610+ if (toEraseOrNull->count (iT))
1611+ toEraseOrNull->insert (TriInd (triangles.size () - 2 ));
1612+ if (toEraseOrNull->count (iTopo))
1613+ toEraseOrNull->insert (TriInd (triangles.size () - 1 ));
1614+ }
15681615 return iMid;
15691616}
15701617
@@ -2393,7 +2440,8 @@ template <typename T, typename TNearPointLocator>
23932440void Triangulation<T, TNearPointLocator>::refineTriangles(
23942441 const VertInd maxVerticesToInsert,
23952442 const RefinementCriterion::Enum refinementCriterion,
2396- const T refinementThreshold)
2443+ const T refinementThreshold,
2444+ TriIndUSet* const toEraseOrNull)
23972445{
23982446 if (isFinalized ())
23992447 {
@@ -2412,8 +2460,8 @@ void Triangulation<T, TNearPointLocator>::refineTriangles(
24122460 {
24132461 const Edge edge = encroachedEdges.front ();
24142462 encroachedEdges.pop ();
2415- const VertInd iSplitVert =
2416- splitEncroachedEdge ( edge, steinerVerticesOffset);
2463+ const VertInd iSplitVert = splitEncroachedEdge (
2464+ edge, steinerVerticesOffset, toEraseOrNull );
24172465 // if resulting halves are encroached, add them to the queue
24182466 const Edge half1 (edge.v1 (), iSplitVert);
24192467 if (isEdgeEncroached (half1))
@@ -2436,6 +2484,7 @@ void Triangulation<T, TNearPointLocator>::refineTriangles(
24362484 {
24372485 const Triangle& t = triangles[iT];
24382486 if (!touchesSuperTriangle (t) &&
2487+ !(toEraseOrNull && toEraseOrNull->count (iT)) &&
24392488 isRefinementNeeded (t, refinementCriterion, refinementThreshold))
24402489 {
24412490 badTriangles.push (iT);
@@ -2446,6 +2495,8 @@ void Triangulation<T, TNearPointLocator>::refineTriangles(
24462495 {
24472496 const TriInd iT = badTriangles.front ();
24482497 badTriangles.pop ();
2498+ if (toEraseOrNull && toEraseOrNull->count (iT))
2499+ continue ;
24492500 const Triangle& badT = triangles[iT];
24502501 if (!isRefinementNeeded (badT, refinementCriterion, refinementThreshold))
24512502 {
@@ -2468,7 +2519,8 @@ void Triangulation<T, TNearPointLocator>::refineTriangles(
24682519 steinerVerticesOffset,
24692520 &triCircumenter,
24702521 refinementCriterion,
2471- refinementThreshold);
2522+ refinementThreshold,
2523+ toEraseOrNull);
24722524 if (!remainingVertexBudget)
24732525 break ;
24742526 if (!badTris.empty ())
@@ -2482,17 +2534,45 @@ void Triangulation<T, TNearPointLocator>::refineTriangles(
24822534 continue ;
24832535 }
24842536
2485- --remainingVertexBudget;
2537+ // locate the triangle(s) the circumcenter falls into before spending
2538+ // the vertex budget: if it lands in a triangle that will be erased
2539+ // anyway (e.g. a hole or outer triangle) skip it instead of adding a
2540+ // pointless Steiner point there
24862541 const VertInd iVert = static_cast <VertInd>(vertices.size ());
24872542 addNewVertex (triCircumenter, noNeighbor);
2488- insertVertex (iVert);
2543+ const VertInd walkStart =
2544+ m_nearPtLocator.nearPoint (triCircumenter, vertices);
2545+ const array<TriInd, 2 > trisAt =
2546+ walkingSearchTrianglesAt (iVert, walkStart);
2547+ if (toEraseOrNull &&
2548+ (toEraseOrNull->count (trisAt[0 ]) || toEraseOrNull->count (trisAt[1 ])))
2549+ {
2550+ vertices.pop_back ();
2551+ m_vertTris.pop_back ();
2552+ continue ;
2553+ }
2554+
2555+ --remainingVertexBudget;
2556+ // re-does the same walk as above internally, but reuses proven
2557+ // insertion logic (fixed-edge split handling, callbacks) rather than
2558+ // duplicating it here
2559+ insertVertex (iVert, walkStart);
2560+ tryAddVertexToLocator (iVert);
2561+ if (toEraseOrNull)
2562+ {
2563+ if (toEraseOrNull->count (trisAt[0 ]))
2564+ toEraseOrNull->insert (TriInd (triangles.size () - 2 ));
2565+ if (toEraseOrNull->count (trisAt[1 ]))
2566+ toEraseOrNull->insert (TriInd (triangles.size () - 1 ));
2567+ }
24892568
24902569 TriInd start = m_vertTris[iVert];
24912570 TriInd currTri = start;
24922571 do
24932572 {
24942573 const Triangle& t = triangles[currTri];
2495- if (isRefinementNeeded (t, refinementCriterion, refinementThreshold))
2574+ if (!(toEraseOrNull && toEraseOrNull->count (currTri)) &&
2575+ isRefinementNeeded (t, refinementCriterion, refinementThreshold))
24962576 {
24972577 badTriangles.push (currTri);
24982578 }
0 commit comments