CDT  2.0.1
C++ library for constrained Delaunay triangulation
Loading...
Searching...
No Matches
Triangulation.hpp
Go to the documentation of this file.
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
9#ifndef CDT_pDqrlveWIOrIWeUCkPqX
10#define CDT_pDqrlveWIOrIWeUCkPqX
11
12#include "Triangulation.h"
13#include "portable_nth_element.hpp"
14
15#include <algorithm>
16#include <cassert>
17#include <cmath>
18#include <deque>
19#include <stdexcept>
20
21CDT_ENSURE_PRECISE_MATH_FOR_CONSTRUCTIONS
22
23namespace CDT
24{
25
26typedef std::deque<TriInd> TriDeque;
27
28namespace detail
29{
30
32inline EdgeQueue toQueue(const EdgeVec& edges)
33{
34 return EdgeQueue(EdgeQueue::container_type(edges.begin(), edges.end()));
35}
36
38template <typename TVec>
39void sortUnique(TVec& v)
40{
41 std::sort(v.begin(), v.end());
42 v.erase(std::unique(v.begin(), v.end()), v.end());
43}
44
45namespace defaults
46{
47
48const VertexInsertionOrder::Enum vertexInsertionOrder =
50const IntersectingConstraintEdges::Enum intersectingEdgesStrategy =
52const float minDistToConstraintEdge(0);
53
54} // namespace defaults
55
56} // namespace detail
57
58CDT_INLINE_IF_HEADER_ONLY Unrefined::Unrefined()
63 , shortEdges(0)
65{}
66
67template <typename T, typename TNearPointLocator>
69 : m_vertexInsertionOrder(detail::defaults::vertexInsertionOrder)
70 , m_intersectingEdgesStrategy(detail::defaults::intersectingEdgesStrategy)
71 , m_minDistToConstraintEdge(detail::defaults::minDistToConstraintEdge)
72#ifdef CDT_ENABLE_CALLBACK_HANDLER
73 , m_callbackHandler(NULL)
74#endif
75{}
76
77template <typename T, typename TNearPointLocator>
79 const VertexInsertionOrder::Enum vertexInsertionOrder)
80 : m_vertexInsertionOrder(vertexInsertionOrder)
81 , m_intersectingEdgesStrategy(detail::defaults::intersectingEdgesStrategy)
82 , m_minDistToConstraintEdge(detail::defaults::minDistToConstraintEdge)
83#ifdef CDT_ENABLE_CALLBACK_HANDLER
84 , m_callbackHandler(NULL)
85#endif
86{}
87
88template <typename T, typename TNearPointLocator>
90 const VertexInsertionOrder::Enum vertexInsertionOrder,
91 const IntersectingConstraintEdges::Enum intersectingEdgesStrategy,
92 const T minDistToConstraintEdge)
93 : m_vertexInsertionOrder(vertexInsertionOrder)
94 , m_intersectingEdgesStrategy(intersectingEdgesStrategy)
95 , m_minDistToConstraintEdge(minDistToConstraintEdge)
96#ifdef CDT_ENABLE_CALLBACK_HANDLER
97 , m_callbackHandler(NULL)
98#endif
99{}
100
101template <typename T, typename TNearPointLocator>
103 const VertexInsertionOrder::Enum vertexInsertionOrder,
104 const TNearPointLocator& nearPtLocator,
105 const IntersectingConstraintEdges::Enum intersectingEdgesStrategy,
106 const T minDistToConstraintEdge)
107 : m_nearPtLocator(nearPtLocator)
108 , m_vertexInsertionOrder(vertexInsertionOrder)
109 , m_intersectingEdgesStrategy(intersectingEdgesStrategy)
110 , m_minDistToConstraintEdge(minDistToConstraintEdge)
111#ifdef CDT_ENABLE_CALLBACK_HANDLER
112 , m_callbackHandler(NULL)
113#endif
114{}
115
116template <typename T, typename TNearPointLocator>
118{
119 finalizeTriangulation(collectSuperTriangle());
120}
121
122template <typename T, typename TNearPointLocator>
127
128template <typename T, typename TNearPointLocator>
133
134template <typename T, typename TNearPointLocator>
136{
137 if(isFinalized())
138 handleException(FinalizedError(CDT_SOURCE_LOCATION));
139
140 // find triangles adjacent to super-triangle's vertices
141 TriIndUSet toErase;
142 for(TriInd iT(0); iT < TriInd(triangles.size()); ++iT)
143 {
145 toErase.insert(iT);
146 }
147 return toErase;
148}
149
150template <typename T, typename TNearPointLocator>
152{
153 if(isFinalized())
154 handleException(FinalizedError(CDT_SOURCE_LOCATION));
155
156 assert(m_vertTris[0] != noNeighbor);
157 const std::stack<TriInd> seed(std::deque<TriInd>(1, m_vertTris[0]));
158 return growToBoundary(seed);
159}
160
161template <typename T, typename TNearPointLocator>
164{
165 if(isFinalized())
166 handleException(FinalizedError(CDT_SOURCE_LOCATION));
167
168 const std::vector<LayerDepth> triDepths = calculateTriangleDepths();
169 TriIndUSet toErase;
170 toErase.reserve(triangles.size());
171 for(std::size_t iT = 0; iT != triangles.size(); ++iT)
172 {
173 if(triDepths[iT] % 2 == 0)
174 toErase.insert(static_cast<TriInd>(iT));
175 }
176 return toErase;
177}
178
181{
182 return Edge(
183 VertInd(e.v1() - nSuperTriVerts), VertInd(e.v2() - nSuperTriVerts));
184}
185
186template <typename T, typename TNearPointLocator>
188 const TriIndUSet& removedTriangles)
189{
190 if(removedTriangles.empty())
191 return;
192 // remove triangles and calculate triangle index mapping
193 TriIndUMap triIndMap;
194 for(TriInd iT(0), iTnew(0); iT < TriInd(triangles.size()); ++iT)
195 {
196 if(removedTriangles.count(iT))
197 continue;
198 triIndMap[iT] = iTnew;
199 triangles[iTnew] = triangles[iT];
200 iTnew++;
201 }
202 triangles.erase(triangles.end() - removedTriangles.size(), triangles.end());
203 // adjust triangles' neighbors
204 for(TriInd iT(0); iT < triangles.size(); ++iT)
205 {
206 Triangle& t = triangles[iT];
207 // update neighbors to account for removed triangles
208 NeighborsArr3& nn = t.neighbors;
209 for(NeighborsArr3::iterator n = nn.begin(); n != nn.end(); ++n)
210 {
211 if(removedTriangles.count(*n))
212 {
213 *n = noNeighbor;
214 }
215 else if(*n != noNeighbor)
216 {
217 *n = triIndMap[*n];
218 }
219 }
220 }
221}
222
223template <typename T, typename TNearPointLocator>
228
229template <typename T, typename TNearPointLocator>
231{
232 return m_vertTris;
233}
234
235template <typename T, typename TNearPointLocator>
237 const TriIndUSet& removedTriangles)
238{
239 if(isFinalized())
240 handleException(FinalizedError(CDT_SOURCE_LOCATION));
241
242 m_vertTris = TriIndVec();
243 // remove super-triangle
244 vertices.erase(vertices.begin(), vertices.begin() + nSuperTriVerts);
245 // Edge re-mapping
246 { // fixed edges
247 EdgeUSet updatedFixedEdges;
248 typedef CDT::EdgeUSet::const_iterator It;
249 for(It e = fixedEdges.begin(); e != fixedEdges.end(); ++e)
250 {
251 updatedFixedEdges.insert(RemapNoSuperTriangle(*e));
252 }
253 fixedEdges = updatedFixedEdges;
254 }
255 { // overlap count
256 unordered_map<Edge, BoundaryOverlapCount> updatedOverlapCount;
257 typedef unordered_map<Edge, BoundaryOverlapCount>::const_iterator It;
258 for(It it = overlapCount.begin(); it != overlapCount.end(); ++it)
259 {
260 updatedOverlapCount.insert(
261 std::make_pair(RemapNoSuperTriangle(it->first), it->second));
262 }
263 overlapCount = updatedOverlapCount;
264 }
265 { // split edges mapping
266 unordered_map<Edge, EdgeVec> updatedPieceToOriginals;
267 typedef unordered_map<Edge, EdgeVec>::const_iterator It;
268 for(It it = pieceToOriginals.begin(); it != pieceToOriginals.end();
269 ++it)
270 {
271 EdgeVec ee = it->second;
272 for(EdgeVec::iterator eeIt = ee.begin(); eeIt != ee.end(); ++eeIt)
273 {
274 *eeIt = RemapNoSuperTriangle(*eeIt);
275 }
276 updatedPieceToOriginals.insert(
277 std::make_pair(RemapNoSuperTriangle(it->first), ee));
278 }
279 pieceToOriginals = updatedPieceToOriginals;
280 }
281 // remove other triangles
282 removeTriangles(removedTriangles);
283 // adjust triangle vertices: account for removed super-triangle
284 for(TriangleVec::iterator t = triangles.begin(); t != triangles.end(); ++t)
285 {
286 VerticesArr3& vv = t->vertices;
287 for(VerticesArr3::iterator v = vv.begin(); v != vv.end(); ++v)
288 {
289 *v -= nSuperTriVerts;
290 }
291 }
292}
293
294template <typename T, typename TNearPointLocator>
295TriIndUSet Triangulation<T, TNearPointLocator>::growToBoundary(
296 std::stack<TriInd> seeds) const
297{
298 TriIndUSet traversed;
299 while(!seeds.empty())
300 {
301 const TriInd iT = seeds.top();
302 seeds.pop();
303 traversed.insert(iT);
304 const Triangle& t = triangles[iT];
305 for(Index i(0); i < Index(3); ++i)
306 {
307 const Edge opEdge(t.vertices[ccw(i)], t.vertices[cw(i)]);
308 if(fixedEdges.count(opEdge))
309 continue;
310 const TriInd iN = t.neighbors[opoNbr(i)];
311 if(iN != noNeighbor && traversed.count(iN) == 0)
312 seeds.push(iN);
313 }
314 }
315 return traversed;
316}
317
318template <typename T, typename TNearPointLocator>
319TriInd Triangulation<T, TNearPointLocator>::addTriangle(const Triangle& t)
320{
321 const TriInd iT = trianglesCount();
322 triangles.push_back(t);
323 return iT;
324}
325
326template <typename T, typename TNearPointLocator>
327TriInd Triangulation<T, TNearPointLocator>::addTriangle()
328{
329 return addTriangle(Triangle());
330}
331
332template <typename T, typename TNearPointLocator>
333VertInd Triangulation<T, TNearPointLocator>::verticesCount() const
334{
335 return static_cast<VertInd>(vertices.size());
336}
337
338template <typename T, typename TNearPointLocator>
339TriInd Triangulation<T, TNearPointLocator>::trianglesCount() const
340{
341 return static_cast<TriInd>(triangles.size());
342}
343
344template <typename T, typename TNearPointLocator>
346 const std::vector<Edge>& edges)
347{
348 insertEdges(edges.begin(), edges.end(), edge_get_v1, edge_get_v2);
349}
350
351template <typename T, typename TNearPointLocator>
353 const std::vector<Edge>& edges)
354{
355 conformToEdges(edges.begin(), edges.end(), edge_get_v1, edge_get_v2);
356}
357
358template <typename T, typename TNearPointLocator>
359void Triangulation<T, TNearPointLocator>::fixEdge(const Edge& edge)
360{
361 if(!fixedEdges.insert(edge).second)
362 {
363 ++overlapCount[edge]; // if edge is already fixed increment the counter
364 }
365}
366
367namespace detail
368{
369
370// add element to 'to' if not already in 'to'
371template <typename T, typename Allocator1>
372void insert_unique(std::vector<T, Allocator1>& to, const T& elem)
373{
374 if(std::find(to.begin(), to.end(), elem) == to.end())
375 {
376 to.push_back(elem);
377 }
378}
379
380// add elements of 'from' that are not present in 'to' to 'to'
381template <typename T, typename Allocator1, typename Allocator2>
382void insert_unique(
383 std::vector<T, Allocator1>& to,
384 const std::vector<T, Allocator2>& from)
385{
386 typedef typename std::vector<T, Allocator2>::const_iterator Cit;
387 to.reserve(to.size() + from.size());
388 for(Cit cit = from.begin(); cit != from.end(); ++cit)
389 {
390 insert_unique(to, *cit);
391 }
392}
393
394} // namespace detail
395
396template <typename T, typename TNearPointLocator>
397void Triangulation<T, TNearPointLocator>::splitFixedEdge(
398 const Edge& edge,
399 const VertInd iSplitVert)
400{
401 // split constraint (fixed) edge that already exists in triangulation
402 const Edge half1(edge.v1(), iSplitVert);
403 const Edge half2(iSplitVert, edge.v2());
404 // remove the edge that and add its halves
405 fixedEdges.erase(edge);
406 fixEdge(half1);
407 fixEdge(half2);
408 // maintain overlaps
409 typedef unordered_map<Edge, BoundaryOverlapCount>::const_iterator It;
410 const It overlapIt = overlapCount.find(edge);
411 if(overlapIt != overlapCount.end())
412 {
413 overlapCount[half1] += overlapIt->second;
414 overlapCount[half2] += overlapIt->second;
415 overlapCount.erase(overlapIt);
416 }
417 // maintain piece-to-original mapping
418 EdgeVec newOriginals(1, edge);
419 const unordered_map<Edge, EdgeVec>::const_iterator originalsIt =
420 pieceToOriginals.find(edge);
421 if(originalsIt != pieceToOriginals.end())
422 { // edge being split was split before: pass-through originals
423 newOriginals = originalsIt->second;
424 pieceToOriginals.erase(originalsIt);
425 }
426 detail::insert_unique(pieceToOriginals[half1], newOriginals);
427 detail::insert_unique(pieceToOriginals[half2], newOriginals);
428}
429
430template <typename T, typename TNearPointLocator>
431VertInd Triangulation<T, TNearPointLocator>::addSplitEdgeVertex(
432 const Edge& edge,
433 const V2d<T>& splitVert,
434 const TriInd iT,
435 const TriInd iTopo,
436 const AddVertexType::Enum vertexType)
437{
438 // add a new point on the edge that splits an edge in two
439 const VertInd iSplitVert = verticesCount();
440 addNewVertex(splitVert, noNeighbor);
441
442#ifdef CDT_ENABLE_CALLBACK_HANDLER
443 if(m_callbackHandler)
444 {
445 m_callbackHandler->onAddVertexStart(iSplitVert, vertexType);
446 }
447#else
448 (void)vertexType;
449#endif
450
451 std::stack<TriInd> triStack = insertVertexOnEdge(iSplitVert, iT, iTopo);
452 // before the flips: isFlipNeeded needs the halves to be fixed
453 splitFixedEdge(edge, iSplitVert);
454 tryAddVertexToLocator(iSplitVert);
455 ensureDelaunayByEdgeFlips(iSplitVert, triStack);
456 return iSplitVert;
457}
458
459template <typename T, typename TNearPointLocator>
460OptionalVertInd Triangulation<T, TNearPointLocator>::splitFixedEdgeAt(
461 const Edge& edge,
462 const V2d<T>& splitVert,
463 const TriInd iT,
464 const TriInd iTopo,
465 const AddVertexType::Enum vertexType)
466{
467 if(!isEdgeSplitVertexValid(splitVert, iT, iTopo))
468 return OptionalVertInd(noVertex);
469 const VertInd iSplitVert =
470 addSplitEdgeVertex(edge, splitVert, iT, iTopo, vertexType);
471 return OptionalVertInd(iSplitVert);
472}
473
474template <typename T, typename TNearPointLocator>
475bool Triangulation<T, TNearPointLocator>::isEdgeSplitVertexValid(
476 const V2d<T>& splitVert,
477 const TriInd iT,
478 const TriInd iTopo) const
479{
480 // quadrilateral of the two triangles, CCW: v2 and v4 end the split edge
481 const Triangle& t1 = triangles[iT];
482 const Index i = opposedVertexInd(t1.neighbors, iTopo);
483 const V2d<T>& v1 = vertices[t1.vertices[i]];
484 const V2d<T>& v2 = vertices[t1.vertices[ccw(i)]];
485 const V2d<T>& v4 = vertices[t1.vertices[cw(i)]];
486 const Triangle& t2 = triangles[iTopo];
487 const V2d<T>& v3 =
488 vertices[t2.vertices[opposedVertexInd(t2.neighbors, iT)]];
489 return locatePointLine(splitVert, v1, v2) == PtLineLocation::Left &&
490 locatePointLine(splitVert, v2, v3) == PtLineLocation::Left &&
491 locatePointLine(splitVert, v3, v4) == PtLineLocation::Left &&
492 locatePointLine(splitVert, v4, v1) == PtLineLocation::Left;
493}
494
495template <typename T, typename TNearPointLocator>
496Edge Triangulation<T, TNearPointLocator>::originalInputEdge(const Edge& e) const
497{
498 const Edge orig =
499 pieceToOriginals.count(e) ? pieceToOriginals.at(e).front() : e;
500 return Edge(
501 VertInd(orig.v1() - nSuperTriVerts),
502 VertInd(orig.v2() - nSuperTriVerts));
503}
504
505template <typename T, typename TNearPointLocator>
506const Triangle&
507Triangulation<T, TNearPointLocator>::triangleAt(const TriInd iT) const
508{
509 if(iT >= TriInd(triangles.size()))
510 handleException(Error(
511 iT == noNeighbor
512 ? "Attempted reading no-neighbor sentinel value triangle"
513 : "Triangle index " + CDT::to_string(iT) + " out of range " +
514 CDT::to_string(triangles.size()),
515 CDT_SOURCE_LOCATION));
516 return triangles[iT];
517}
518
519template <typename T, typename TNearPointLocator>
520void Triangulation<T, TNearPointLocator>::fixEdge(
521 const Edge& edge,
522 const Edge& originalEdge)
523{
524 fixEdge(edge);
525 if(edge != originalEdge)
526 detail::insert_unique(pieceToOriginals[edge], originalEdge);
527}
528
529namespace detail
530{
531
532template <typename T>
533T lerp(const T& a, const T& b, const T t)
534{
535 return (T(1) - t) * a + t * b;
536}
537
539template <typename T>
540bool isAngleAtApexSmall(const V2d<T>& apex, const V2d<T>& a, const V2d<T>& b)
541{
542 const T ux = a.x - apex.x, uy = a.y - apex.y;
543 const T wx = b.x - apex.x, wy = b.y - apex.y;
544 const T dot = ux * wx + uy * wy;
545 // cos(60°) = 0.5
546 return dot > T(0) && T(2) * dot > distance(apex, a) * distance(apex, b);
547}
548
549// Precondition: ab and cd intersect normally
550template <typename T>
551V2d<T> intersectionPosition(
552 const V2d<T>& a,
553 const V2d<T>& b,
554 const V2d<T>& c,
555 const V2d<T>& d)
556{
557 // note: for better accuracy we interpolate x and y separately
558 // on a segment with the shortest x/y-projection correspondingly
559 const T a_cd = predicates::orient2d(c.x, c.y, d.x, d.y, a.x, a.y);
560 const T b_cd = predicates::orient2d(c.x, c.y, d.x, d.y, b.x, b.y);
561 const T t_ab = a_cd / (a_cd - b_cd);
562
563 const T c_ab = predicates::orient2d(a.x, a.y, b.x, b.y, c.x, c.y);
564 const T d_ab = predicates::orient2d(a.x, a.y, b.x, b.y, d.x, d.y);
565 const T t_cd = c_ab / (c_ab - d_ab);
566
567 return V2d<T>(
568 std::fabs(a.x - b.x) < std::fabs(c.x - d.x) ? lerp(a.x, b.x, t_ab)
569 : lerp(c.x, d.x, t_cd),
570 std::fabs(a.y - b.y) < std::fabs(c.y - d.y) ? lerp(a.y, b.y, t_ab)
571 : lerp(c.y, d.y, t_cd));
572}
573
574} // namespace detail
575
576template <typename T, typename TNearPointLocator>
577void Triangulation<T, TNearPointLocator>::insertEdgeIteration(
578 const Edge edge,
579 const Edge originalEdge,
580 EdgeVec& remaining,
581 std::vector<TriangulatePseudoPolygonTask>& tppIterations)
582{
583 const VertInd iA = edge.v1();
584 VertInd iB = edge.v2();
585 if(iA == iB) // edge connects a vertex to itself
586 return;
587
588 const V2d<T>& a = vertices[iA];
589 const V2d<T>& b = vertices[iB];
590 const T distanceTolerance =
591 m_minDistToConstraintEdge == T(0)
592 ? T(0)
593 : m_minDistToConstraintEdge * distance(a, b);
594
595 if(hasEdge(iA, iB))
596 {
597 const VertInd iV = opposedVertexOnEdge(edge, distanceTolerance);
598 if(iV != noVertex)
599 {
600 remaining.push_back(Edge(iV, iB));
601 remaining.push_back(Edge(iA, iV));
602 return;
603 }
604 fixEdge(edge, originalEdge);
605 return;
606 }
607
608 TriInd iT;
609 // Note: 'L' is left and 'R' is right of the inserted constraint edge
610 VertInd iVL, iVR;
611 tie(iT, iVL, iVR) = intersectedTriangle(iA, a, b, distanceTolerance);
612 // if one of the triangle vertices is on the edge, split edge at it
613 if(iT == noNeighbor)
614 {
615 remaining.push_back(Edge(iVL, iB));
616 remaining.push_back(Edge(iA, iVL));
617 return;
618 }
619 Triangle t = triangles[iT];
620 std::vector<TriInd> intersected(1, iT);
621 std::vector<VertInd> polyL, polyR;
622 polyL.reserve(2);
623 polyL.push_back(iA);
624 polyL.push_back(iVL);
625 polyR.reserve(2);
626 polyR.push_back(iA);
627 polyR.push_back(iVR);
628 unordered_map<Edge, TriInd> outerTris;
629 outerTris[Edge(iA, iVL)] = edgeNeighbor(t, iA, iVL);
630 outerTris[Edge(iA, iVR)] = edgeNeighbor(t, iA, iVR);
631 VertInd iV = iA;
632
633 while(!t.containsVertex(iB))
634 {
635 const TriInd iTopo = opposedTriangle(t, iV);
636 const Triangle& tOpo = triangleAt(iTopo);
637 const VertInd iVopo = opposedVertex(tOpo, iT);
638
639 switch(m_intersectingEdgesStrategy)
640 {
642 if(fixedEdges.count(Edge(iVL, iVR)))
643 handleException(IntersectingConstraintsError(
644 originalInputEdge(originalEdge),
645 originalInputEdge(Edge(iVL, iVR)),
646 CDT_SOURCE_LOCATION));
647 break;
649 {
650 if(!fixedEdges.count(Edge(iVL, iVR)))
651 break;
652 // split edge at the intersection of two constraint edges
653 const V2d<T> newV = detail::intersectionPosition(
654 vertices[iA], vertices[iB], vertices[iVL], vertices[iVR]);
655 const OptionalVertInd splitVert = splitFixedEdgeAt(
656 Edge(iVL, iVR),
657 newV,
658 iT,
659 iTopo,
661 if(!splitVert.hasValue())
662 handleException(InvalidEdgeSplitVertex(
663 originalInputEdge(originalEdge),
664 originalInputEdge(Edge(iVL, iVR)),
665 CDT_SOURCE_LOCATION));
666 const VertInd iNewVert = splitVert.value();
667 // TODO: is it's possible to re-use pseudo-polygons
668 // for inserting [iA, iNewVert] edge half?
669 remaining.push_back(Edge(iA, iNewVert));
670 remaining.push_back(Edge(iNewVert, iB));
671 return;
672 }
674 assert(!fixedEdges.count(Edge(iVL, iVR)));
675 break;
676 }
677
678 const PtLineLocation::Enum loc =
679 locatePointLine(vertices[iVopo], a, b, distanceTolerance);
680 if(loc == PtLineLocation::Left)
681 {
682 const Edge e(polyL.back(), iVopo);
683 const TriInd outer = edgeNeighbor(tOpo, e.v1(), e.v2());
684 if(!outerTris.insert(std::make_pair(e, outer)).second)
685 outerTris.at(e) = noNeighbor; // hanging edge detected
686 polyL.push_back(iVopo);
687 iV = iVL;
688 iVL = iVopo;
689 }
690 else if(loc == PtLineLocation::Right)
691 {
692 const Edge e(polyR.back(), iVopo);
693 const TriInd outer = edgeNeighbor(tOpo, e.v1(), e.v2());
694 if(!outerTris.insert(std::make_pair(e, outer)).second)
695 outerTris.at(e) = noNeighbor; // hanging edge detected
696 polyR.push_back(iVopo);
697 iV = iVR;
698 iVR = iVopo;
699 }
700 else // encountered point on the edge
701 iB = iVopo;
702
703 intersected.push_back(iTopo);
704 iT = iTopo;
705 t = triangles[iT];
706 }
707 outerTris[Edge(polyL.back(), iB)] = edgeNeighbor(t, polyL.back(), iB);
708 outerTris[Edge(polyR.back(), iB)] = edgeNeighbor(t, polyR.back(), iB);
709 polyL.push_back(iB);
710 polyR.push_back(iB);
711
712 assert(!intersected.empty());
713 // make sure start/end vertices have a valid adjacent triangle
714 // that is not intersected by an edge
715 if(m_vertTris[iA] == intersected.front())
716 pivotVertexTriangleCW(iA);
717 if(m_vertTris[iB] == intersected.back())
718 pivotVertexTriangleCW(iB);
719
720 {
721#ifdef CDT_ENABLE_CALLBACK_HANDLER
722 if(m_callbackHandler)
723 {
724 m_callbackHandler->onReTriangulatePolygon(intersected);
725 }
726#endif
727
728 // Triangulate pseudo-polygons on both sides
729 std::reverse(polyR.begin(), polyR.end());
730
731 // note: intersected triangles are re-used for new triangles
732 // every triangulation of an n-gon has n − 2 triangles
733 // even if outer polygon has hanging edges it holds
734 assert(intersected.size() >= 2);
735 const TriInd iTL = intersected.back();
736 intersected.pop_back();
737 const TriInd iTR = intersected.back();
738 intersected.pop_back();
739
740 triangulatePseudoPolygon(
741 polyL, outerTris, iTL, iTR, intersected, tppIterations);
742 triangulatePseudoPolygon(
743 polyR, outerTris, iTR, iTL, intersected, tppIterations);
744 assert(intersected.empty());
745 }
746
747 if(iB != edge.v2()) // encountered point on the edge
748 {
749 // fix edge part
750 const Edge edgePart(iA, iB);
751 fixEdge(edgePart, originalEdge);
752 remaining.push_back(Edge(iB, edge.v2()));
753 return;
754 }
755 else
756 {
757 fixEdge(edge, originalEdge);
758 }
759}
760
761template <typename T, typename TNearPointLocator>
762void Triangulation<T, TNearPointLocator>::insertEdge(
763 Edge edge,
764 const Edge originalEdge,
765 EdgeVec& remaining,
766 std::vector<TriangulatePseudoPolygonTask>& tppIterations)
767{
768#ifdef CDT_ENABLE_CALLBACK_HANDLER
769 if(m_callbackHandler)
770 {
771 m_callbackHandler->onAddEdgeStart(edge);
772 }
773#endif
774
775 // use iteration over recursion to avoid stack overflows
776 remaining.clear();
777 remaining.push_back(edge);
778 while(!remaining.empty())
779 {
780 edge = remaining.back();
781 remaining.pop_back();
782 insertEdgeIteration(edge, originalEdge, remaining, tppIterations);
783 }
784}
785
786template <typename T, typename TNearPointLocator>
787void Triangulation<T, TNearPointLocator>::conformToEdgeIteration(
788 Edge edge,
789 const EdgeVec& originals,
790 BoundaryOverlapCount overlaps,
791 std::vector<ConformToEdgeTask>& remaining)
792{
793 const VertInd iA = edge.v1();
794 VertInd iB = edge.v2();
795 if(iA == iB) // edge connects a vertex to itself
796 return;
797
798 const V2d<T>& a = vertices[iA];
799 const V2d<T>& b = vertices[iB];
800 const T distanceTolerance =
801 m_minDistToConstraintEdge == T(0)
802 ? T(0)
803 : m_minDistToConstraintEdge * distance(a, b);
804
805 if(hasEdge(iA, iB))
806 {
807 const VertInd iV = opposedVertexOnEdge(edge, distanceTolerance);
808 if(iV != noVertex)
809 {
810#ifdef CDT_CXX11_IS_SUPPORTED
811 remaining.emplace_back(Edge(iV, iB), originals, overlaps);
812 remaining.emplace_back(Edge(iA, iV), originals, overlaps);
813#else
814 remaining.push_back(make_tuple(Edge(iV, iB), originals, overlaps));
815 remaining.push_back(make_tuple(Edge(iA, iV), originals, overlaps));
816#endif
817 return;
818 }
819 fixEdge(edge);
820 if(overlaps > 0)
821 overlapCount[edge] = overlaps;
822 // avoid marking edge as a part of itself
823 if(!originals.empty() && edge != originals.front())
824 {
825 detail::insert_unique(pieceToOriginals[edge], originals);
826 }
827 return;
828 }
829
830 TriInd iT;
831 VertInd iVleft, iVright;
832 tie(iT, iVleft, iVright) = intersectedTriangle(iA, a, b, distanceTolerance);
833 // if one of the triangle vertices is on the edge, split edge at it
834 if(iT == noNeighbor)
835 {
836#ifdef CDT_CXX11_IS_SUPPORTED
837 remaining.emplace_back(Edge(iVleft, iB), originals, overlaps);
838 remaining.emplace_back(Edge(iA, iVleft), originals, overlaps);
839#else
840 remaining.push_back(make_tuple(Edge(iVleft, iB), originals, overlaps));
841 remaining.push_back(make_tuple(Edge(iA, iVleft), originals, overlaps));
842#endif
843 return;
844 }
845
846 VertInd iV = iA;
847 Triangle t = triangles[iT];
848 while(std::find(t.vertices.begin(), t.vertices.end(), iB) ==
849 t.vertices.end())
850 {
851 const TriInd iTopo = opposedTriangle(t, iV);
852 const Triangle& tOpo = triangleAt(iTopo);
853 const VertInd iVopo = opposedVertex(tOpo, iT);
854 const V2d<T> vOpo = vertices[iVopo];
855
856 switch(m_intersectingEdgesStrategy)
857 {
859 if(fixedEdges.count(Edge(iVleft, iVright)))
860 handleException(IntersectingConstraintsError(
861 originalInputEdge(edge),
862 originalInputEdge(Edge(iVleft, iVright)),
863 CDT_SOURCE_LOCATION));
864 break;
866 {
867 if(!fixedEdges.count(Edge(iVleft, iVright)))
868 break;
869 // split edge at the intersection of two constraint edges
870 const V2d<T> newV = detail::intersectionPosition(
871 vertices[iA],
872 vertices[iB],
873 vertices[iVleft],
874 vertices[iVright]);
875 const OptionalVertInd splitVert = splitFixedEdgeAt(
876 Edge(iVleft, iVright),
877 newV,
878 iT,
879 iTopo,
881 if(!splitVert.hasValue())
882 handleException(InvalidEdgeSplitVertex(
883 originalInputEdge(edge),
884 originalInputEdge(Edge(iVleft, iVright)),
885 CDT_SOURCE_LOCATION));
886 const VertInd iNewVert = splitVert.value();
887#ifdef CDT_CXX11_IS_SUPPORTED
888 remaining.emplace_back(Edge(iNewVert, iB), originals, overlaps);
889 remaining.emplace_back(Edge(iA, iNewVert), originals, overlaps);
890#else
891 remaining.push_back(
892 make_tuple(Edge(iNewVert, iB), originals, overlaps));
893 remaining.push_back(
894 make_tuple(Edge(iA, iNewVert), originals, overlaps));
895#endif
896 return;
897 }
899 assert(!fixedEdges.count(Edge(iVleft, iVright)));
900 break;
901 }
902
903 iT = iTopo;
904 t = triangles[iT];
905
906 const PtLineLocation::Enum loc =
907 locatePointLine(vOpo, a, b, distanceTolerance);
908 if(loc == PtLineLocation::Left)
909 {
910 iV = iVleft;
911 iVleft = iVopo;
912 }
913 else if(loc == PtLineLocation::Right)
914 {
915 iV = iVright;
916 iVright = iVopo;
917 }
918 else // encountered point on the edge
919 iB = iVopo;
920 }
921
922 // encountered one or more points on the edge: add remaining edge part
923 if(iB != edge.v2())
924 {
925#ifdef CDT_CXX11_IS_SUPPORTED
926 remaining.emplace_back(Edge(iB, edge.v2()), originals, overlaps);
927#else
928 remaining.push_back(
929 make_tuple(Edge(iB, edge.v2()), originals, overlaps));
930#endif
931 }
932
933 // add mid-point to triangulation
934 const VertInd iMid = verticesCount();
935 const V2d<T>& start = vertices[iA];
936 const V2d<T>& end = vertices[iB];
937 addNewVertex(
938 V2d<T>((start.x + end.x) / T(2), (start.y + end.y) / T(2)), noNeighbor);
939
940#ifdef CDT_ENABLE_CALLBACK_HANDLER
941 if(m_callbackHandler)
942 {
943 m_callbackHandler->onAddVertexStart(
945 }
946#endif
947
948 const std::vector<Edge> flippedFixedEdges =
949 insertVertex_FlipFixedEdges(iMid);
950
951#ifdef CDT_CXX11_IS_SUPPORTED
952 remaining.emplace_back(Edge(iMid, iB), originals, overlaps);
953 remaining.emplace_back(Edge(iA, iMid), originals, overlaps);
954#else
955 remaining.push_back(make_tuple(Edge(iMid, iB), originals, overlaps));
956 remaining.push_back(make_tuple(Edge(iA, iMid), originals, overlaps));
957#endif
958
959 // re-introduce fixed edges that were flipped
960 // and make sure overlap count is preserved
961 for(std::vector<Edge>::const_iterator it = flippedFixedEdges.begin();
962 it != flippedFixedEdges.end();
963 ++it)
964 {
965 const Edge& flippedFixedEdge = *it;
966 fixedEdges.erase(flippedFixedEdge);
967
968 BoundaryOverlapCount prevOverlaps = 0;
969 const unordered_map<Edge, BoundaryOverlapCount>::const_iterator
970 overlapsIt = overlapCount.find(flippedFixedEdge);
971 if(overlapsIt != overlapCount.end())
972 {
973 prevOverlaps = overlapsIt->second;
974 overlapCount.erase(overlapsIt);
975 }
976 // override overlapping boundaries count when re-inserting an edge
977 EdgeVec prevOriginals(1, flippedFixedEdge);
978 const unordered_map<Edge, EdgeVec>::const_iterator originalsIt =
979 pieceToOriginals.find(flippedFixedEdge);
980 if(originalsIt != pieceToOriginals.end())
981 {
982 prevOriginals = originalsIt->second;
983 }
984#ifdef CDT_CXX11_IS_SUPPORTED
985 remaining.emplace_back(flippedFixedEdge, prevOriginals, prevOverlaps);
986#else
987 remaining.push_back(
988 make_tuple(flippedFixedEdge, prevOriginals, prevOverlaps));
989#endif
990 }
991}
992
993template <typename T, typename TNearPointLocator>
994void Triangulation<T, TNearPointLocator>::conformToEdge(
995 Edge edge,
996 EdgeVec originals,
997 BoundaryOverlapCount overlaps,
998 std::vector<ConformToEdgeTask>& remaining)
999{
1000#ifdef CDT_ENABLE_CALLBACK_HANDLER
1001 if(m_callbackHandler)
1002 {
1003 m_callbackHandler->onAddEdgeStart(edge);
1004 }
1005#endif
1006
1007 // use iteration over recursion to avoid stack overflows
1008 remaining.clear();
1009#ifdef CDT_CXX11_IS_SUPPORTED
1010 remaining.emplace_back(edge, originals, overlaps);
1011#else
1012 remaining.push_back(make_tuple(edge, originals, overlaps));
1013#endif
1014 while(!remaining.empty())
1015 {
1016 tie(edge, originals, overlaps) = remaining.back();
1017 remaining.pop_back();
1018 conformToEdgeIteration(edge, originals, overlaps, remaining);
1019 }
1020}
1021
1032template <typename T, typename TNearPointLocator>
1033tuple<TriInd, VertInd, VertInd>
1034Triangulation<T, TNearPointLocator>::intersectedTriangle(
1035 const VertInd iA,
1036 const V2d<T>& a,
1037 const V2d<T>& b,
1038 const T orientationTolerance) const
1039{
1040 const TriInd startTri = m_vertTris[iA];
1041 TriInd iT = startTri;
1042 do
1043 {
1044 const Triangle t = triangles[iT];
1045 const Index i = vertexInd(t.vertices, iA);
1046 const VertInd iP2 = t.vertices[ccw(i)];
1047 const T orientP2 = orient2D(vertices[iP2], a, b);
1048 const PtLineLocation::Enum locP2 = classifyOrientation(orientP2);
1049 if(locP2 == PtLineLocation::Right)
1050 {
1051 const VertInd iP1 = t.vertices[cw(i)];
1052 const T orientP1 = orient2D(vertices[iP1], a, b);
1053 const PtLineLocation::Enum locP1 = classifyOrientation(orientP1);
1054 if(locP1 == PtLineLocation::OnLine)
1055 {
1056 return make_tuple(noNeighbor, iP1, iP1);
1057 }
1058 if(locP1 == PtLineLocation::Left)
1059 {
1060 if(orientationTolerance)
1061 {
1062 T closestOrient;
1063 VertInd iClosestP;
1064 if(std::abs(orientP1) <= std::abs(orientP2))
1065 {
1066 closestOrient = orientP1;
1067 iClosestP = iP1;
1068 }
1069 else
1070 {
1071 closestOrient = orientP2;
1072 iClosestP = iP2;
1073 }
1075 closestOrient, orientationTolerance) ==
1076 PtLineLocation::OnLine)
1077 {
1078 return make_tuple(noNeighbor, iClosestP, iClosestP);
1079 }
1080 }
1081 return make_tuple(iT, iP1, iP2);
1082 }
1083 }
1084 iT = t.next(iA).first;
1085 } while(iT != startTri);
1086
1087 handleException(Error(
1088 "Could not find vertex triangle intersected by an edge.",
1089 CDT_SOURCE_LOCATION));
1090 return make_tuple(noNeighbor, noVertex, noVertex);
1091}
1092
1093template <typename T, typename TNearPointLocator>
1094void Triangulation<T, TNearPointLocator>::addSuperTriangle(const Box2d<T>& box)
1095{
1096 const V2d<T> center(
1097 (box.min.x + box.max.x) / T(2), (box.min.y + box.max.y) / T(2));
1098 const T w = box.max.x - box.min.x;
1099 const T h = box.max.y - box.min.y;
1100 T r = std::max(w, h); // incircle radius upper bound
1101
1102 // Note: make sure radius is big enough. Constants chosen experimentally.
1103 // - for tiny bounding boxes: use 1.0 as the smallest radius
1104 // - multiply radius by 2.0 for extra safety margin
1105 r = std::max(T(2) * r, T(1));
1106
1107 // Note: for very large floating point numbers rounding can lead to wrong
1108 // super-triangle coordinates. This is a very rare corner-case so the
1109 // handling is very primitive.
1110 { // note: '<=' means '==' but avoids the warning
1111 while(center.y <= center.y - r)
1112 r *= T(2);
1113 }
1114
1115 const T R = T(2) * r; // excircle radius
1116 const T cos_30_deg = T(0.8660254037844386); // note: (std::sqrt(3.0) / 2.0)
1117 const T shiftX = R * cos_30_deg;
1118 const V2d<T> posV1(center.x - shiftX, center.y - r);
1119 const V2d<T> posV2(center.x + shiftX, center.y - r);
1120 const V2d<T> posV3(center.x, center.y + R);
1121 addNewVertex(posV1, TriInd(0));
1122 addNewVertex(posV2, TriInd(0));
1123 addNewVertex(posV3, TriInd(0));
1124
1125#ifdef CDT_ENABLE_CALLBACK_HANDLER
1126 if(m_callbackHandler)
1127 {
1128 m_callbackHandler->onAddSuperTriangle();
1129 }
1130#endif
1131
1132 addTriangle(
1133 Triangle(arr3(VertInd(0), VertInd(1), VertInd(2)), arr3(noNeighbor)));
1134
1135 if(m_vertexInsertionOrder != VertexInsertionOrder::Auto)
1136 {
1137 m_nearPtLocator.initialize(vertices);
1138 }
1139}
1140
1141template <typename T, typename TNearPointLocator>
1142void Triangulation<T, TNearPointLocator>::addNewVertex(
1143 const V2d<T>& pos,
1144 const TriInd iT)
1145{
1146 vertices.push_back(pos);
1147 m_vertTris.push_back(iT);
1148}
1149
1150template <typename T, typename TNearPointLocator>
1151std::vector<Edge>
1152Triangulation<T, TNearPointLocator>::insertVertex_FlipFixedEdges(
1153 const VertInd iV1)
1154{
1155 std::vector<Edge> flippedFixedEdges;
1156
1157 const V2d<T>& v1 = vertices[iV1];
1158 const VertInd startVertex = m_nearPtLocator.nearPoint(v1, vertices);
1159 array<TriInd, 2> trisAt = walkingSearchTrianglesAt(iV1, startVertex);
1160 std::stack<TriInd> triStack =
1161 trisAt[1] == noNeighbor ? insertVertexInsideTriangle(iV1, trisAt[0])
1162 : insertVertexOnEdge(iV1, trisAt[0], trisAt[1]);
1163
1164 TriInd iTopo, n1, n2, n3, n4;
1165 VertInd iV2, iV3, iV4;
1166 while(!triStack.empty())
1167 {
1168 const TriInd iT = triStack.top();
1169 triStack.pop();
1170
1171 edgeFlipInfo(iT, iV1, iTopo, iV2, iV3, iV4, n1, n2, n3, n4);
1172 if(iTopo != noNeighbor && isFlipNeeded(iV1, iV2, iV3, iV4, true))
1173 {
1174 // if flipped edge is fixed, remember it
1175 const Edge flippedEdge(iV2, iV4);
1176 if(!fixedEdges.empty() &&
1177 fixedEdges.find(flippedEdge) != fixedEdges.end())
1178 {
1179 flippedFixedEdges.push_back(flippedEdge);
1180 }
1181
1182 flipEdge(iT, iTopo, iV1, iV2, iV3, iV4, n1, n2, n3, n4);
1183 triStack.push(iT);
1184 triStack.push(iTopo);
1185 }
1186 }
1187
1188 tryAddVertexToLocator(iV1);
1189 return flippedFixedEdges;
1190}
1191
1192template <typename T, typename TNearPointLocator>
1193void Triangulation<T, TNearPointLocator>::insertVertex(
1194 const VertInd iVert,
1195 const VertInd walkStart)
1196{
1197#ifdef CDT_ENABLE_CALLBACK_HANDLER
1198 if(m_callbackHandler)
1199 {
1200 m_callbackHandler->onAddVertexStart(iVert, AddVertexType::UserInput);
1201 }
1202#endif
1203
1204 const array<TriInd, 2> trisAt = walkingSearchTrianglesAt(iVert, walkStart);
1205 std::stack<TriInd> triStack =
1206 trisAt[1] == noNeighbor
1207 ? insertVertexInsideTriangle(iVert, trisAt[0])
1208 : insertVertexOnEdge(iVert, trisAt[0], trisAt[1], true);
1209 ensureDelaunayByEdgeFlips(iVert, triStack);
1210}
1211
1212template <typename T, typename TNearPointLocator>
1213void Triangulation<T, TNearPointLocator>::insertVertex(const VertInd iVert)
1214{
1215 const V2d<T>& v = vertices[iVert];
1216 const VertInd walkStart = m_nearPtLocator.nearPoint(v, vertices);
1217 insertVertex(iVert, walkStart);
1218 tryAddVertexToLocator(iVert);
1219}
1220
1221template <typename T, typename TNearPointLocator>
1222void Triangulation<T, TNearPointLocator>::ensureDelaunayByEdgeFlips(
1223 const VertInd iV1,
1224 std::stack<TriInd>& triStack)
1225{
1226 TriInd iTopo, n1, n2, n3, n4;
1227 VertInd iV2, iV3, iV4;
1228 while(!triStack.empty())
1229 {
1230 const TriInd iT = triStack.top();
1231 triStack.pop();
1232
1233 edgeFlipInfo(iT, iV1, iTopo, iV2, iV3, iV4, n1, n2, n3, n4);
1234 if(iTopo != noNeighbor && isFlipNeeded(iV1, iV2, iV3, iV4))
1235 {
1236 flipEdge(iT, iTopo, iV1, iV2, iV3, iV4, n1, n2, n3, n4);
1237 triStack.push(iT);
1238 triStack.push(iTopo);
1239 }
1240 }
1241}
1242
1243/*
1244 * v4 original edge: (v1, v3)
1245 * /|\ flip-candidate edge: (v, v2)
1246 * / | \
1247 * n3 / | \ n4
1248 * / | \
1249 * new vertex--> v1 T | Topo v3
1250 * \ | /
1251 * n1 \ | / n2
1252 * \ | /
1253 * \|/
1254 * v2
1255 */
1256template <typename T, typename TNearPointLocator>
1257void Triangulation<T, TNearPointLocator>::edgeFlipInfo(
1258 const TriInd iT,
1259 const VertInd iV1,
1260 TriInd& iTopo,
1261 VertInd& iV2,
1262 VertInd& iV3,
1263 VertInd& iV4,
1264 TriInd& n1,
1265 TriInd& n2,
1266 TriInd& n3,
1267 TriInd& n4)
1268{
1269 /* v[2]
1270 / \
1271 n[2]/ \n[1]
1272 /_____\
1273 v[0] n[0] v[1] */
1274 const Triangle& t = triangles[iT];
1275 if(t.vertices[0] == iV1)
1276 {
1277 iV2 = t.vertices[1];
1278 iV4 = t.vertices[2];
1279 n1 = t.neighbors[0];
1280 n3 = t.neighbors[2];
1281 iTopo = t.neighbors[1];
1282 }
1283 else if(t.vertices[1] == iV1)
1284 {
1285 iV2 = t.vertices[2];
1286 iV4 = t.vertices[0];
1287 n1 = t.neighbors[1];
1288 n3 = t.neighbors[0];
1289 iTopo = t.neighbors[2];
1290 }
1291 else
1292 {
1293 iV2 = t.vertices[0];
1294 iV4 = t.vertices[1];
1295 n1 = t.neighbors[2];
1296 n3 = t.neighbors[1];
1297 iTopo = t.neighbors[0];
1298 }
1299 if(iTopo == noNeighbor)
1300 return;
1301 const Triangle& tOpo = triangles[iTopo];
1302 if(tOpo.neighbors[0] == iT)
1303 {
1304 iV3 = tOpo.vertices[2];
1305 n2 = tOpo.neighbors[1];
1306 n4 = tOpo.neighbors[2];
1307 }
1308 else if(tOpo.neighbors[1] == iT)
1309 {
1310 iV3 = tOpo.vertices[0];
1311 n2 = tOpo.neighbors[2];
1312 n4 = tOpo.neighbors[0];
1313 }
1314 else
1315 {
1316 iV3 = tOpo.vertices[1];
1317 n2 = tOpo.neighbors[0];
1318 n4 = tOpo.neighbors[1];
1319 }
1320}
1321
1322template <typename T, typename TNearPointLocator>
1323bool Triangulation<T, TNearPointLocator>::isSameOriginalEdge(
1324 const Edge& e1,
1325 const Edge& e2) const
1326{
1327 if(!fixedEdges.count(e1) || !fixedEdges.count(e2))
1328 return false;
1329 typedef unordered_map<Edge, EdgeVec>::const_iterator It;
1330 const It it1 = pieceToOriginals.find(e1);
1331 const It it2 = pieceToOriginals.find(e2);
1332 return (it1 == pieceToOriginals.end() ? e1 : it1->second.front()) ==
1333 (it2 == pieceToOriginals.end() ? e2 : it2->second.front());
1334}
1335
1346/*
1347 * v4 original edge: (v2, v4)
1348 * /|\ flip-candidate edge: (v1, v3)
1349 * / | \
1350 * / | \
1351 * / | \
1352 * new vertex--> v1 | v3
1353 * \ | /
1354 * \ | /
1355 * \ | /
1356 * \|/
1357 * v2
1358 */
1359template <typename T, typename TNearPointLocator>
1360bool Triangulation<T, TNearPointLocator>::isFlipNeeded(
1361 const VertInd iV1,
1362 const VertInd iV2,
1363 const VertInd iV3,
1364 const VertInd iV4,
1365 const bool doFlipFixedEdges) const
1366{
1367 if(!doFlipFixedEdges && fixedEdges.count(Edge(iV2, iV4)))
1368 return false; // flip not needed if the original edge is fixed
1369 // the flip would make a triangle out of two pieces of one input edge
1370 if(!fixedEdges.empty() &&
1371 (isSameOriginalEdge(Edge(iV1, iV2), Edge(iV2, iV3)) ||
1372 isSameOriginalEdge(Edge(iV3, iV4), Edge(iV4, iV1))))
1373 return false;
1374 const V2d<T>& v1 = vertices[iV1];
1375 const V2d<T>& v2 = vertices[iV2];
1376 const V2d<T>& v3 = vertices[iV3];
1377 const V2d<T>& v4 = vertices[iV4];
1378 // If flip-candidate edge touches super-triangle in-circumference
1379 // test has to be replaced with orient2d test against the line
1380 // formed by two non-artificial vertices (that don't belong to
1381 // super-triangle)
1382 if(iV1 < nSuperTriVerts) // flip-candidate edge touches super-triangle
1383 {
1384 // does original edge also touch super-triangle?
1385 if(iV2 < nSuperTriVerts)
1386 return locatePointLine(v2, v3, v4) == locatePointLine(v1, v3, v4);
1387 if(iV4 < nSuperTriVerts)
1388 return locatePointLine(v4, v2, v3) == locatePointLine(v1, v2, v3);
1389 return false; // original edge does not touch super-triangle
1390 }
1391 if(iV3 < nSuperTriVerts) // flip-candidate edge touches super-triangle
1392 {
1393 // does original edge also touch super-triangle?
1394 if(iV2 < nSuperTriVerts)
1395 {
1396 return locatePointLine(v2, v1, v4) == locatePointLine(v3, v1, v4);
1397 }
1398 if(iV4 < nSuperTriVerts)
1399 {
1400 return locatePointLine(v4, v2, v1) == locatePointLine(v3, v2, v1);
1401 }
1402 return false; // original edge does not touch super-triangle
1403 }
1404 // flip-candidate edge does not touch super-triangle
1405 if(iV2 < nSuperTriVerts)
1406 return locatePointLine(v2, v3, v4) == locatePointLine(v1, v3, v4);
1407 if(iV4 < nSuperTriVerts)
1408 return locatePointLine(v4, v2, v3) == locatePointLine(v1, v2, v3);
1409 return isInCircumcircle(v1, v2, v3, v4);
1410}
1411
1412/* Flip edge between T and Topo:
1413 *
1414 * v4 | - old edge
1415 * /|\ ~ - new edge
1416 * / | \
1417 * n3 / T' \ n4
1418 * / | \
1419 * / | \
1420 * T -> v1~~~~~~~~~v3 <- Topo
1421 * \ | /
1422 * \ | /
1423 * n1 \Topo'/ n2
1424 * \ | /
1425 * \|/
1426 * v2
1427 */
1428template <typename T, typename TNearPointLocator>
1430 const TriInd iT,
1431 const TriInd iTopo)
1432{
1433#ifdef CDT_ENABLE_CALLBACK_HANDLER
1434 if(m_callbackHandler)
1435 {
1436 m_callbackHandler->onFlipEdge(iT, iTopo);
1437 }
1438#endif
1439
1440 Triangle& t = triangles[iT];
1441 Triangle& tOpo = triangles[iTopo];
1442 const array<TriInd, 3>& triNs = t.neighbors;
1443 const array<TriInd, 3>& triOpoNs = tOpo.neighbors;
1444 const array<VertInd, 3>& triVs = t.vertices;
1445 const array<VertInd, 3>& triOpoVs = tOpo.vertices;
1446 // find vertices and neighbors
1447 Index i = opposedVertexInd(t.neighbors, iTopo);
1448 const VertInd v1 = triVs[i];
1449 const VertInd v2 = triVs[ccw(i)];
1450 const TriInd n1 = triNs[i];
1451 const TriInd n3 = triNs[cw(i)];
1452 i = opposedVertexInd(tOpo.neighbors, iT);
1453 const VertInd v3 = triOpoVs[i];
1454 const VertInd v4 = triOpoVs[ccw(i)];
1455 const TriInd n4 = triOpoNs[i];
1456 const TriInd n2 = triOpoNs[cw(i)];
1457 // change vertices and neighbors
1458 t = Triangle(arr3(v4, v1, v3), arr3(n3, iTopo, n4));
1459 tOpo = Triangle(arr3(v2, v3, v1), arr3(n2, iT, n1));
1460 // adjust neighboring triangles and vertices
1461 changeNeighbor(n1, iT, iTopo);
1462 changeNeighbor(n4, iTopo, iT);
1463 // only adjust adjacent triangles if triangulation is not finalized:
1464 // can happen when called from outside on an already finalized
1465 // triangulation
1466 if(!isFinalized())
1467 {
1468 setAdjacentTriangle(v4, iT);
1469 setAdjacentTriangle(v2, iTopo);
1470 }
1471}
1472
1473template <typename T, typename TNearPointLocator>
1474bool Triangulation<T, TNearPointLocator>::isRefinementNeeded(
1475 const Triangle& tri,
1476 const RefinementCriterion::Enum refinementCriterion,
1477 const T refinementThreshold) const
1478{
1479 const V2d<T>& a = vertices[tri.vertices[0]];
1480 const V2d<T>& b = vertices[tri.vertices[1]];
1481 const V2d<T>& c = vertices[tri.vertices[2]];
1482 switch(refinementCriterion)
1483 {
1485 return smallestAngle(a, b, c) < refinementThreshold;
1487 return area(a, b, c) > refinementThreshold;
1488 }
1489 assert(false); // unreachable code
1490 return false;
1491}
1492
1493template <typename T, typename TNearPointLocator>
1494bool Triangulation<T, TNearPointLocator>::isSmallestAngleFixed(
1495 const Triangle& tri) const
1496{
1497 Index iApex(0);
1498 T shortestSqLen = distanceSquared(
1499 vertices[tri.vertices[ccw(Index(0))]],
1500 vertices[tri.vertices[cw(Index(0))]]);
1501 for(Index i(1); i < Index(3); ++i)
1502 {
1503 const T sqLen = distanceSquared(
1504 vertices[tri.vertices[ccw(i)]], vertices[tri.vertices[cw(i)]]);
1505 if(sqLen < shortestSqLen)
1506 {
1507 shortestSqLen = sqLen;
1508 iApex = i;
1509 }
1510 }
1511 const VertInd apex = tri.vertices[iApex];
1512 return fixedEdges.count(Edge(apex, tri.vertices[ccw(iApex)])) &&
1513 fixedEdges.count(Edge(apex, tri.vertices[cw(iApex)]));
1514}
1515
1516template <typename T, typename TNearPointLocator>
1517bool Triangulation<T, TNearPointLocator>::isEdgeEncroached(
1518 const Edge& edge) const
1519{
1520 TriInd iT, iTopo;
1521 tie(iT, iTopo) = edgeTriangles(edge.v1(), edge.v2());
1522 assert(iT != noNeighbor && iTopo != noNeighbor);
1523 const VertInd v1 = opposedVertex(triangles[iT], iTopo);
1524 const VertInd v2 = opposedVertex(triangles[iTopo], iT);
1525 const V2d<T>& edgeStart = vertices[edge.v1()];
1526 const V2d<T>& edgeEnd = vertices[edge.v2()];
1527 return detail::isEncroachingOnEdge(vertices[v1], edgeStart, edgeEnd) ||
1528 detail::isEncroachingOnEdge(vertices[v2], edgeStart, edgeEnd);
1529}
1530
1531template <typename T, typename TNearPointLocator>
1532bool Triangulation<T, TNearPointLocator>::isEdgeEncroachedBy(
1533 const Edge& edge,
1534 const V2d<T>& v) const
1535{
1536 return detail::isEncroachingOnEdge(
1537 v, vertices[edge.v1()], vertices[edge.v2()]);
1538}
1539
1540template <typename T, typename TNearPointLocator>
1542{
1543 if(isFinalized())
1544 handleException(FinalizedError(CDT_SOURCE_LOCATION));
1545
1546 // Search in all fixed edges to find encroached edges
1547 EdgeVec encroachedEdges;
1548 typedef EdgeUSet::const_iterator Iter;
1549 for(Iter it = fixedEdges.begin(); it != fixedEdges.end(); ++it)
1550 {
1551 const Edge edge = *it;
1552 if(isEdgeEncroached(edge))
1553 {
1554 encroachedEdges.push_back(edge);
1555 }
1556 }
1557 // fixedEdges is a hash set: its iteration order is platform-dependent,
1558 // so sort to keep refinement output deterministic
1559 std::sort(encroachedEdges.begin(), encroachedEdges.end());
1560 return encroachedEdges;
1561}
1562
1563template <typename T, typename TNearPointLocator>
1565 const RefinementCriterion::Enum refinementCriterion,
1566 const T refinementThreshold) const
1567{
1568 const bool hasSuperTriangle = !isFinalized();
1569 TriIndVec unrefinedTriangles;
1570 for(TriInd iT(0), n(triangles.size()); iT < n; ++iT)
1571 {
1572 const Triangle& t = triangles[iT];
1573 if(hasSuperTriangle && touchesSuperTriangle(t))
1574 continue;
1575 if(isRefinementNeeded(t, refinementCriterion, refinementThreshold))
1576 unrefinedTriangles.push_back(iT);
1577 }
1578 return unrefinedTriangles;
1579}
1580
1581template <typename T, typename TNearPointLocator>
1582EdgeVec Triangulation<T, TNearPointLocator>::edgesEncroachedBy(
1583 const V2d<T>& v,
1584 const TriInd iT) const
1585{
1586 /*
1587 * A fixed edge not yet encroached by an existing vertex can only be
1588 * encroached by v if v is inside the circumscribed circle of the edge's
1589 * triangle on v's side: the circumscribed circles of an edge's two
1590 * triangles cover the edge's diametral circle. Such triangles are visible
1591 * from v, so growing from the triangle at v without crossing fixed edges
1592 * reaches all of them.
1593 */
1594 EdgeVec encroachedEdges;
1595 TriIndUSet traversed;
1596 std::stack<TriInd> toTraverse;
1597 toTraverse.push(iT);
1598 traversed.insert(iT);
1599 while(!toTraverse.empty())
1600 {
1601 const Triangle& t = triangles[toTraverse.top()];
1602 toTraverse.pop();
1603 for(Index i(0); i < Index(3); ++i)
1604 {
1605 const Edge opEdge(t.vertices[ccw(i)], t.vertices[cw(i)]);
1606 if(fixedEdges.count(opEdge))
1607 {
1608 // both edge's triangles can be reached: avoid duplicate edges
1609 if(isEdgeEncroachedBy(opEdge, v))
1610 detail::insert_unique(encroachedEdges, opEdge);
1611 continue;
1612 }
1613 const TriInd iN = t.neighbors[opoNbr(i)];
1614 if(iN == noNeighbor || traversed.count(iN))
1615 continue;
1616 const Triangle& n = triangles[iN];
1617 if(!isInCircumcircle(
1618 v,
1619 vertices[n.vertices[0]],
1620 vertices[n.vertices[1]],
1621 vertices[n.vertices[2]]))
1622 continue;
1623 traversed.insert(iN);
1624 toTraverse.push(iN);
1625 }
1626 }
1627 return encroachedEdges;
1628}
1629
1630template <typename T, typename TNearPointLocator>
1631TriIndVec Triangulation<T, TNearPointLocator>::resolveEncroachedEdges(
1632 EdgeQueue encroachedEdges,
1633 VertInd& remainingVertexBudget,
1634 const VertInd steinerVerticesOffset,
1635 const V2d<T>* const circumcenterOrNull,
1636 const RefinementCriterion::Enum refinementCriterion,
1637 const T badTriangleThreshold,
1638 TriIndUSet* const toEraseOrNull,
1639 const T minEdgeLength,
1640 Unrefined& unrefined)
1641{
1642 std::vector<TriInd> badTriangles;
1643
1644 while(!encroachedEdges.empty() && remainingVertexBudget > 0)
1645 {
1646 const Edge edge = encroachedEdges.front();
1647 encroachedEdges.pop();
1648 if(fixedEdges.find(edge) == fixedEdges.end())
1649 {
1650 continue;
1651 }
1652 // give up on already-too-short edges rather than split forever
1653 if(distance(vertices[edge.v1()], vertices[edge.v2()]) <= minEdgeLength)
1654 {
1655 ++unrefined.shortEdges;
1656 continue;
1657 }
1658 // split encroached edge
1659 const OptionalVertInd splitVert = splitEncroachedEdge(
1660 edge, steinerVerticesOffset, toEraseOrNull, unrefined);
1661 if(!splitVert.hasValue())
1662 continue;
1663 const VertInd iSplitVert = splitVert.value();
1664 --remainingVertexBudget;
1665
1666 const TriInd start = m_vertTris[iSplitVert];
1667 TriInd iT = start;
1668 do
1669 {
1670 const Triangle& t = triangles[iT];
1671 const bool isMarkedForErasure =
1672 toEraseOrNull && toEraseOrNull->count(iT);
1673 if(circumcenterOrNull && !isMarkedForErasure &&
1675 isRefinementNeeded(t, refinementCriterion, badTriangleThreshold))
1676 {
1677 badTriangles.push_back(iT);
1678 }
1679 for(Index i(0); i < Index(3); ++i)
1680 {
1681 const Edge triEdge(t.vertices[i], t.vertices[cw(i)]);
1682 if(fixedEdges.find(triEdge) == fixedEdges.end())
1683 continue;
1684 if(isEdgeEncroached(triEdge) ||
1685 (circumcenterOrNull &&
1686 isEdgeEncroachedBy(triEdge, *circumcenterOrNull)))
1687 {
1688 encroachedEdges.push(triEdge);
1689 }
1690 }
1691 iT = t.next(iSplitVert).first;
1692 } while(iT != start);
1693 }
1694 return badTriangles;
1695}
1696
1697template <typename T, typename TNearPointLocator>
1698OptionalVertInd Triangulation<T, TNearPointLocator>::splitEncroachedEdge(
1699 const Edge edge,
1700 const VertInd steinerVerticesOffset,
1701 TriIndUSet* const toEraseOrNull,
1702 Unrefined& unrefined)
1703{
1704 const V2d<T>& start = vertices[edge.v1()];
1705 const V2d<T>& end = vertices[edge.v2()];
1706
1707 TriInd iT, iTopo;
1708 tie(iT, iTopo) = edgeTriangles(edge.v1(), edge.v2());
1709 assert(iT != noNeighbor && iTopo != noNeighbor);
1710
1711 T split = T(0.5);
1712 // Edge sorts its vertices and Steiner ones are appended after the input
1713 // ones, so only v1 can be an input vertex here
1714 if(edge.v1() < steinerVerticesOffset &&
1715 edge.v2() >= steinerVerticesOffset &&
1716 hasAnotherFixedEdgeAtSmallAngle(edge.v1(), edge))
1717 {
1718 // In Ruppert's paper, he used D(0.01) factor to divide edge length, but
1719 // that introduces FP rounding errors, so it's avoided.
1720 const T len = distance(start, end);
1721 const T d = len / T(2);
1722 // Find the splitting distance
1723 T nearestPowerOfTwo = T(1);
1724 while(d > nearestPowerOfTwo)
1725 {
1726 nearestPowerOfTwo *= T(2);
1727 }
1728 while(d < T(0.75) * nearestPowerOfTwo)
1729 {
1730 nearestPowerOfTwo *= T(0.5);
1731 }
1732 split = nearestPowerOfTwo / len;
1733 }
1734
1735 const V2d<T> mid = V2d<T>(
1736 detail::lerp(start.x, end.x, split),
1737 detail::lerp(start.y, end.y, split));
1738
1739 const OptionalVertInd iMid = splitFixedEdgeAt(
1740 edge, mid, iT, iTopo, AddVertexType::RefinementEdgeSplit);
1741 if(!iMid.hasValue())
1742 {
1743 ++unrefined.splitVertexInvalid;
1744 }
1745 else if(toEraseOrNull)
1746 {
1747 // splitting reuses iT/iTopo for two of the four resulting triangles and
1748 // appends the other two: propagate erasure marks to the new triangles
1749 if(toEraseOrNull->count(iT))
1750 toEraseOrNull->insert(TriInd(triangles.size() - 2));
1751 if(toEraseOrNull->count(iTopo))
1752 toEraseOrNull->insert(TriInd(triangles.size() - 1));
1753 }
1754 return iMid;
1755}
1756
1757/* Flip edge between T and Topo:
1758 *
1759 * v4 | - old edge
1760 * /|\ ~ - new edge
1761 * / | \
1762 * n3 / T' \ n4
1763 * / | \
1764 * / | \
1765 * T -> v1 ~~~~~~~~ v3 <- Topo
1766 * \ | /
1767 * \ | /
1768 * n1 \Topo'/ n2
1769 * \ | /
1770 * \|/
1771 * v2
1772 */
1773template <typename T, typename TNearPointLocator>
1775 const TriInd iT,
1776 const TriInd iTopo,
1777 const VertInd v1,
1778 const VertInd v2,
1779 const VertInd v3,
1780 const VertInd v4,
1781 const TriInd n1,
1782 const TriInd n2,
1783 const TriInd n3,
1784 const TriInd n4)
1785{
1786#ifdef CDT_ENABLE_CALLBACK_HANDLER
1787 if(m_callbackHandler)
1788 {
1789 m_callbackHandler->onFlipEdge(iT, iTopo);
1790 }
1791#endif
1792
1793 // change vertices and neighbors
1794 triangles[iT] = Triangle(arr3(v4, v1, v3), arr3(n3, iTopo, n4));
1795 triangles[iTopo] = Triangle(arr3(v2, v3, v1), arr3(n2, iT, n1));
1796 // adjust neighboring triangles and vertices
1797 changeNeighbor(n1, iT, iTopo);
1798 changeNeighbor(n4, iTopo, iT);
1799 // only adjust adjacent triangles if triangulation is not finalized:
1800 // can happen when called from outside on an already finalized
1801 // triangulation
1802 if(!isFinalized())
1803 {
1804 setAdjacentTriangle(v4, iT);
1805 setAdjacentTriangle(v2, iTopo);
1806 }
1807}
1808
1809/* Insert point into triangle: split into 3 triangles:
1810 * - create 2 new triangles
1811 * - re-use old triangle for the 3rd
1812 * v3
1813 * / | \
1814 * / | \ <-- original triangle (t)
1815 * / | \
1816 * n3 / | \ n2
1817 * /newT2|newT1\
1818 * / v \
1819 * / __/ \__ \
1820 * / __/ \__ \
1821 * / _/ t' \_ \
1822 * v1 ___________________ v2
1823 * n1
1824 */
1825template <typename T, typename TNearPointLocator>
1826std::stack<TriInd>
1827Triangulation<T, TNearPointLocator>::insertVertexInsideTriangle(
1828 VertInd v,
1829 TriInd iT)
1830{
1831 const TriInd iNewT1 = addTriangle();
1832 const TriInd iNewT2 = addTriangle();
1833
1834#ifdef CDT_ENABLE_CALLBACK_HANDLER
1835 if(m_callbackHandler)
1836 {
1837 m_callbackHandler->onInsertVertexInsideTriangle(iT, iNewT1, iNewT2);
1838 }
1839#endif
1840
1841 Triangle& t = triangles[iT];
1842 const array<VertInd, 3> vv = t.vertices;
1843 const array<TriInd, 3> nn = t.neighbors;
1844 const VertInd v1 = vv[0], v2 = vv[1], v3 = vv[2];
1845 const TriInd n1 = nn[0], n2 = nn[1], n3 = nn[2];
1846 // make two new triangles and convert current triangle to 3rd new
1847 // triangle
1848 triangles[iNewT1] = Triangle(arr3(v2, v3, v), arr3(n2, iNewT2, iT));
1849 triangles[iNewT2] = Triangle(arr3(v3, v1, v), arr3(n3, iT, iNewT1));
1850 t = Triangle(arr3(v1, v2, v), arr3(n1, iNewT1, iNewT2));
1851 // adjust adjacent triangles
1852 setAdjacentTriangle(v, iT);
1853 setAdjacentTriangle(v3, iNewT1);
1854 // change triangle neighbor's neighbors to new triangles
1855 changeNeighbor(n2, iT, iNewT1);
1856 changeNeighbor(n3, iT, iNewT2);
1857 // return newly added triangles
1858 std::stack<TriInd> newTriangles;
1859 newTriangles.push(iT);
1860 newTriangles.push(iNewT1);
1861 newTriangles.push(iNewT2);
1862 return newTriangles;
1863}
1864
1865/* Inserting a point on the edge between two triangles
1866 * T1 (top) v1
1867 * /|\
1868 * n1 / | \ n4
1869 * / | \
1870 * / T1' | Tnew1\
1871 * v2-------v-------v4
1872 * \ T2' | Tnew2/
1873 * \ | /
1874 * n2 \ | / n3
1875 * \|/
1876 * T2 (bottom) v3
1877 */
1878template <typename T, typename TNearPointLocator>
1879std::stack<TriInd> Triangulation<T, TNearPointLocator>::insertVertexOnEdge(
1880 VertInd v,
1881 TriInd iT1,
1882 TriInd iT2,
1883 const bool doHandleFixedSplitEdge)
1884{
1885 const TriInd iTnew1 = addTriangle();
1886 const TriInd iTnew2 = addTriangle();
1887
1888#ifdef CDT_ENABLE_CALLBACK_HANDLER
1889 if(m_callbackHandler)
1890 {
1891 m_callbackHandler->onInsertVertexOnEdge(iT1, iT2, iTnew1, iTnew2);
1892 }
1893#endif
1894
1895 Triangle& t1 = triangles[iT1];
1896 Triangle& t2 = triangles[iT2];
1897 Index i = opposedVertexInd(t1.neighbors, iT2);
1898 const VertInd v1 = t1.vertices[i];
1899 const VertInd v2 = t1.vertices[ccw(i)];
1900 const TriInd n1 = t1.neighbors[i];
1901 const TriInd n4 = t1.neighbors[cw(i)];
1902 i = opposedVertexInd(t2.neighbors, iT1);
1903 const VertInd v3 = t2.vertices[i];
1904 const VertInd v4 = t2.vertices[ccw(i)];
1905 const TriInd n3 = t2.neighbors[i];
1906 const TriInd n2 = t2.neighbors[cw(i)];
1907 // add new triangles and change existing ones
1908 t1 = Triangle(arr3(v, v1, v2), arr3(iTnew1, n1, iT2));
1909 t2 = Triangle(arr3(v, v2, v3), arr3(iT1, n2, iTnew2));
1910 triangles[iTnew1] = Triangle(arr3(v, v4, v1), arr3(iTnew2, n4, iT1));
1911 triangles[iTnew2] = Triangle(arr3(v, v3, v4), arr3(iT2, n3, iTnew1));
1912 // adjust adjacent triangles
1913 setAdjacentTriangle(v, iT1);
1914 setAdjacentTriangle(v4, iTnew1);
1915 // adjust neighboring triangles and vertices
1916 changeNeighbor(n4, iT1, iTnew1);
1917 changeNeighbor(n3, iT2, iTnew2);
1918 // properly handle the case when the split edge is a fixed edge
1919 if(doHandleFixedSplitEdge)
1920 {
1921 const Edge sharedEdge(v2, v4);
1922 if(fixedEdges.count(sharedEdge))
1923 splitFixedEdge(sharedEdge, v);
1924 }
1925 // return newly added triangles
1926 std::stack<TriInd> newTriangles;
1927 newTriangles.push(iT1);
1928 newTriangles.push(iTnew2);
1929 newTriangles.push(iT2);
1930 newTriangles.push(iTnew1);
1931 return newTriangles;
1932}
1933
1934template <typename T, typename TNearPointLocator>
1935array<TriInd, 2>
1936Triangulation<T, TNearPointLocator>::trianglesAt(const V2d<T>& pos) const
1937{
1938 array<TriInd, 2> out = {noNeighbor, noNeighbor};
1939 for(TriInd i = TriInd(0); i < TriInd(triangles.size()); ++i)
1940 {
1941 const Triangle& t = triangles[i];
1942 const V2d<T>& v1 = vertices[t.vertices[0]];
1943 const V2d<T>& v2 = vertices[t.vertices[1]];
1944 const V2d<T>& v3 = vertices[t.vertices[2]];
1945 const PtTriLocation::Enum loc = locatePointTriangle(pos, v1, v2, v3);
1946 if(loc == PtTriLocation::Outside)
1947 continue;
1948 out[0] = i;
1949 if(isOnEdge(loc))
1950 out[1] = t.neighbors[edgeNeighbor(loc)];
1951 return out;
1952 }
1953 handleException(
1954 Error("No triangle was found at position", CDT_SOURCE_LOCATION));
1955 return out;
1956}
1957
1958template <typename T, typename TNearPointLocator>
1959OptionalTriInd Triangulation<T, TNearPointLocator>::walkTriangles(
1960 const VertInd startVertex,
1961 const V2d<T>& pos) const
1962{
1963 // begin walk in search of triangle at pos
1964 TriInd currTri = m_vertTris[startVertex];
1965 bool found = false;
1966 bool isOutside = false;
1967 detail::SplitMix64RandGen prng;
1968 while(!found)
1969 {
1970 const Triangle& t = triangles[currTri];
1971 found = true;
1972 isOutside = false;
1973 // stochastic offset to randomize which edge we check first
1974 const Index offset(prng() % 3);
1975 for(Index i_(0); i_ < Index(3); ++i_)
1976 {
1977 const Index i((i_ + offset) % 3);
1978 const V2d<T>& vStart = vertices[t.vertices[i]];
1979 const V2d<T>& vEnd = vertices[t.vertices[ccw(i)]];
1980 const PtLineLocation::Enum edgeCheck =
1981 locatePointLine(pos, vStart, vEnd);
1982 const TriInd iN = t.neighbors[i];
1983 if(edgeCheck == PtLineLocation::Right)
1984 {
1985 if(iN != noNeighbor)
1986 {
1987 found = false;
1988 currTri = iN;
1989 break;
1990 }
1991 // crossing this edge would leave the triangulated area:
1992 // other edges can still lead to a triangle containing pos
1993 isOutside = true;
1994 }
1995 }
1996 }
1997 return isOutside ? OptionalTriInd(noNeighbor) : OptionalTriInd(currTri);
1998}
1999
2000template <typename T, typename TNearPointLocator>
2001array<TriInd, 2> Triangulation<T, TNearPointLocator>::walkingSearchTrianglesAt(
2002 const VertInd iV,
2003 const VertInd startVertex) const
2004{
2005 const V2d<T> v = vertices[iV];
2006 array<TriInd, 2> out = {noNeighbor, noNeighbor};
2007 const OptionalTriInd walkResult = walkTriangles(startVertex, v);
2008 if(!walkResult.hasValue())
2009 {
2010 handleException(
2011 Error("No triangle was found at position", CDT_SOURCE_LOCATION));
2012 }
2013 const TriInd iT = walkResult.value();
2014 // Finished walk, locate point in current triangle
2015 const Triangle& t = triangles[iT];
2016 const V2d<T>& v1 = vertices[t.vertices[0]];
2017 const V2d<T>& v2 = vertices[t.vertices[1]];
2018 const V2d<T>& v3 = vertices[t.vertices[2]];
2019 const PtTriLocation::Enum loc = locatePointTriangle(v, v1, v2, v3);
2020
2021 if(loc == PtTriLocation::Outside)
2022 {
2023 handleException(
2024 Error("No triangle was found at position", CDT_SOURCE_LOCATION));
2025 }
2026 if(loc == PtTriLocation::OnVertex)
2027 {
2028 const VertInd iDupe = v1 == v ? t.vertices[0]
2029 : v2 == v ? t.vertices[1]
2030 : t.vertices[2];
2031 handleException(DuplicateVertexError(
2032 VertInd(iV - nSuperTriVerts),
2033 VertInd(iDupe - nSuperTriVerts),
2034 CDT_SOURCE_LOCATION));
2035 }
2036
2037 out[0] = iT;
2038 if(isOnEdge(loc))
2039 out[1] = t.neighbors[edgeNeighbor(loc)];
2040 return out;
2041}
2042
2043template <typename T, typename TNearPointLocator>
2044void Triangulation<T, TNearPointLocator>::changeNeighbor(
2045 const TriInd iT,
2046 const TriInd oldNeighbor,
2047 const TriInd newNeighbor)
2048{
2049 if(iT == noNeighbor)
2050 return;
2051 NeighborsArr3& nn = triangles[iT].neighbors;
2052 assert(
2053 nn[0] == oldNeighbor || nn[1] == oldNeighbor || nn[2] == oldNeighbor);
2054 if(nn[0] == oldNeighbor)
2055 nn[0] = newNeighbor;
2056 else if(nn[1] == oldNeighbor)
2057 nn[1] = newNeighbor;
2058 else
2059 nn[2] = newNeighbor;
2060}
2061
2062template <typename T, typename TNearPointLocator>
2063void Triangulation<T, TNearPointLocator>::changeNeighbor(
2064 const TriInd iT,
2065 const VertInd iVedge1,
2066 const VertInd iVedge2,
2067 const TriInd newNeighbor)
2068{
2069 assert(iT != noNeighbor);
2070 Triangle& t = triangles[iT];
2071 t.neighbors[edgeNeighborInd(t.vertices, iVedge1, iVedge2)] = newNeighbor;
2072}
2073
2074template <typename T, typename TNearPointLocator>
2075void Triangulation<T, TNearPointLocator>::triangulatePseudoPolygon(
2076 const std::vector<VertInd>& poly,
2077 unordered_map<Edge, TriInd>& outerTris,
2078 TriInd iT,
2079 TriInd iN,
2080 std::vector<TriInd>& trianglesToReuse,
2081 std::vector<TriangulatePseudoPolygonTask>& iterations)
2082{
2083 assert(poly.size() > 2);
2084 // note: uses iteration instead of recursion to avoid stack overflows
2085 iterations.clear();
2086 iterations.push_back(make_tuple(
2087 IndexSizeType(0),
2088 static_cast<IndexSizeType>(poly.size() - 1),
2089 iT,
2090 iN,
2091 Index(0)));
2092 while(!iterations.empty())
2093 {
2094 triangulatePseudoPolygonIteration(
2095 poly, outerTris, trianglesToReuse, iterations);
2096 }
2097}
2098
2099template <typename T, typename TNearPointLocator>
2100void Triangulation<T, TNearPointLocator>::triangulatePseudoPolygonIteration(
2101 const std::vector<VertInd>& poly,
2102 unordered_map<Edge, TriInd>& outerTris,
2103 std::vector<TriInd>& trianglesToReuse,
2104 std::vector<TriangulatePseudoPolygonTask>& iterations)
2105{
2106 IndexSizeType iA, iB;
2107 TriInd iT, iParent;
2108 Index iInParent;
2109 assert(!iterations.empty());
2110 tie(iA, iB, iT, iParent, iInParent) = iterations.back();
2111 iterations.pop_back();
2112 assert(iB - iA > 1 && iT != noNeighbor && iParent != noNeighbor);
2113 Triangle& t = triangles[iT];
2114 // find Delaunay point
2115 const IndexSizeType iC = findDelaunayPoint(poly, iA, iB);
2116
2117 const VertInd a = poly[iA];
2118 const VertInd b = poly[iB];
2119 const VertInd c = poly[iC];
2120
2121 // split pseudo-polygon in two parts and triangulate them
2122 // note: second part needs to be pushed on stack first to be processed first
2123
2124 // second part: points after the Delaunay point
2125 if(iB - iC > 1)
2126 {
2127 assert(!trianglesToReuse.empty());
2128 const TriInd iNext = trianglesToReuse.back();
2129 trianglesToReuse.pop_back();
2130 iterations.push_back(make_tuple(iC, iB, iNext, iT, Index(1)));
2131 }
2132 else // pseudo-poly is reduced to a single outer edge
2133 {
2134 const Edge outerEdge(b, c);
2135 const TriInd outerTri = outerTris.at(outerEdge);
2136 if(outerTri != noNeighbor)
2137 {
2138 assert(outerTri != iT);
2139 t.neighbors[1] = outerTri;
2140 changeNeighbor(outerTri, c, b, iT);
2141 }
2142 else
2143 outerTris.at(outerEdge) = iT;
2144 }
2145 // first part: points before the Delaunay point
2146 if(iC - iA > 1)
2147 { // add next triangle and add another iteration
2148 assert(!trianglesToReuse.empty());
2149 const TriInd iNext = trianglesToReuse.back();
2150 trianglesToReuse.pop_back();
2151 iterations.push_back(make_tuple(iA, iC, iNext, iT, Index(2)));
2152 }
2153 else
2154 { // pseudo-poly is reduced to a single outer edge
2155 const Edge outerEdge(c, a);
2156 const TriInd outerTri = outerTris.at(outerEdge);
2157 if(outerTri != noNeighbor)
2158 {
2159 assert(outerTri != iT);
2160 t.neighbors[2] = outerTri;
2161 changeNeighbor(outerTri, c, a, iT);
2162 }
2163 else
2164 outerTris.at(outerEdge) = iT;
2165 }
2166 // Finalize triangle
2167 // note: only when triangle is finalized to we add it as a neighbor to
2168 // parent to maintain triangulation topology consistency
2169 triangles[iParent].neighbors[iInParent] = iT;
2170 t.neighbors[0] = iParent;
2171 t.vertices = arr3(a, b, c);
2172 setAdjacentTriangle(c, iT);
2173}
2174
2175template <typename T, typename TNearPointLocator>
2176IndexSizeType Triangulation<T, TNearPointLocator>::findDelaunayPoint(
2177 const std::vector<VertInd>& poly,
2178 const IndexSizeType iA,
2179 const IndexSizeType iB) const
2180{
2181 assert(iB - iA > 1);
2182 const V2d<T>& a = vertices[poly[iA]];
2183 const V2d<T>& b = vertices[poly[iB]];
2184 IndexSizeType out = iA + 1;
2185 const V2d<T>* c = &vertices[poly[out]]; // caching for better performance
2186 for(IndexSizeType i = iA + 1; i < iB; ++i)
2187 {
2188 const V2d<T>& v = vertices[poly[i]];
2189 if(isInCircumcircle(v, a, b, *c))
2190 {
2191 out = i;
2192 c = &v;
2193 }
2194 }
2195 assert(out > iA && out < iB); // point is between ends
2196 return out;
2197}
2198
2199template <typename T, typename TNearPointLocator>
2201 const std::vector<V2d<T> >& newVertices)
2202{
2203 return insertVertices(
2204 newVertices.begin(), newVertices.end(), getX_V2d<T>, getY_V2d<T>);
2205}
2206
2207template <typename T, typename TNearPointLocator>
2209{
2210 return m_vertTris.empty() && !vertices.empty();
2211}
2212
2213template <typename T, typename TNearPointLocator>
2214unordered_map<TriInd, LayerDepth>
2215Triangulation<T, TNearPointLocator>::peelLayer(
2216 std::stack<TriInd> seeds,
2217 const LayerDepth layerDepth,
2218 std::vector<LayerDepth>& triDepths) const
2219{
2220 unordered_map<TriInd, LayerDepth> behindBoundary;
2221 while(!seeds.empty())
2222 {
2223 const TriInd iT = seeds.top();
2224 seeds.pop();
2225 triDepths[iT] = std::min(triDepths[iT], layerDepth);
2226 behindBoundary.erase(iT);
2227 const Triangle& t = triangles[iT];
2228 for(Index i(0); i < Index(3); ++i)
2229 {
2230 const Edge opEdge(t.vertices[ccw(i)], t.vertices[cw(i)]);
2231 const TriInd iN = t.neighbors[opoNbr(i)];
2232 if(iN == noNeighbor || triDepths[iN] <= layerDepth)
2233 continue;
2234 if(fixedEdges.count(opEdge))
2235 {
2236 const unordered_map<Edge, LayerDepth>::const_iterator cit =
2237 overlapCount.find(opEdge);
2238 const LayerDepth triDepth = cit == overlapCount.end()
2239 ? layerDepth + 1
2240 : layerDepth + cit->second + 1;
2241 behindBoundary[iN] = triDepth;
2242 continue;
2243 }
2244 seeds.push(iN);
2245 }
2246 }
2247 return behindBoundary;
2248}
2249
2250template <typename T, typename TNearPointLocator>
2251std::vector<LayerDepth>
2253{
2254 std::vector<LayerDepth> triDepths(
2255 triangles.size(), std::numeric_limits<LayerDepth>::max());
2256 std::stack<TriInd> seeds(TriDeque(1, m_vertTris[0]));
2257 LayerDepth layerDepth = 0;
2258 LayerDepth deepestSeedDepth = 0;
2259
2260 unordered_map<LayerDepth, TriIndUSet> seedsByDepth;
2261 do
2262 {
2263 const unordered_map<TriInd, LayerDepth>& newSeeds =
2264 peelLayer(seeds, layerDepth, triDepths);
2265
2266 seedsByDepth.erase(layerDepth);
2267 typedef unordered_map<TriInd, LayerDepth>::const_iterator Iter;
2268 for(Iter it = newSeeds.begin(); it != newSeeds.end(); ++it)
2269 {
2270 deepestSeedDepth = std::max(deepestSeedDepth, it->second);
2271 seedsByDepth[it->second].insert(it->first);
2272 }
2273 const TriIndUSet& nextLayerSeeds = seedsByDepth[layerDepth + 1];
2274 seeds = std::stack<TriInd>(
2275 TriDeque(nextLayerSeeds.begin(), nextLayerSeeds.end()));
2276 ++layerDepth;
2277 } while(!seeds.empty() || deepestSeedDepth > layerDepth);
2278
2279 return triDepths;
2280}
2281
2282#ifdef CDT_ENABLE_CALLBACK_HANDLER
2283template <typename T, typename TNearPointLocator>
2285 ICallbackHandler* callbackHandler)
2286{
2287 m_callbackHandler = callbackHandler;
2288}
2289#endif
2290
2291template <typename T, typename TNearPointLocator>
2292void Triangulation<T, TNearPointLocator>::insertVertices_AsProvided(
2293 VertInd superGeomVertCount)
2294{
2295 for(VertInd iV = superGeomVertCount; iV < vertices.size(); ++iV)
2296 {
2297#ifdef CDT_ENABLE_CALLBACK_HANDLER
2298 if(m_callbackHandler && m_callbackHandler->isAbortCalculation())
2299 {
2300 return;
2301 }
2302#endif
2303 insertVertex(iV);
2304 }
2305}
2306
2307template <typename T, typename TNearPointLocator>
2308void Triangulation<T, TNearPointLocator>::insertVertices_Randomized(
2309 VertInd superGeomVertCount)
2310{
2311 std::size_t vertexCount = vertices.size() - superGeomVertCount;
2312 std::vector<VertInd> ii(vertexCount);
2313 detail::iota(ii.begin(), ii.end(), superGeomVertCount);
2314 detail::random_shuffle(ii.begin(), ii.end());
2315 for(std::vector<VertInd>::iterator it = ii.begin(); it != ii.end(); ++it)
2316 {
2317#ifdef CDT_ENABLE_CALLBACK_HANDLER
2318 if(m_callbackHandler && m_callbackHandler->isAbortCalculation())
2319 {
2320 return;
2321 }
2322#endif
2323 insertVertex(*it);
2324 }
2325}
2326
2327namespace detail
2328{
2329
2330// log2 implementation backwards compatible with pre c++11
2331template <typename T>
2332inline double log2_bc(T x)
2333{
2334#ifdef CDT_CXX11_IS_SUPPORTED
2335 return std::log2(x);
2336#else
2337 static double log2_constant = std::log(2.0);
2338 return std::log(static_cast<double>(x)) / log2_constant;
2339#endif
2340}
2341
2346inline std::size_t maxQueueLengthBFSKDTree(const std::size_t vertexCount)
2347{
2348 const int filledLayerPow2 =
2349 static_cast<int>(std::floor(log2_bc(vertexCount)) - 1);
2350 const std::size_t nodesInFilledTree =
2351 static_cast<std::size_t>(std::pow(2., filledLayerPow2 + 1) - 1);
2352 const std::size_t nodesInLastFilledLayer =
2353 static_cast<std::size_t>(std::pow(2., filledLayerPow2));
2354 const std::size_t nodesInLastLayer = vertexCount - nodesInFilledTree;
2355 return nodesInLastLayer >= nodesInLastFilledLayer
2356 ? nodesInLastFilledLayer + nodesInLastLayer -
2357 nodesInLastFilledLayer
2358 : nodesInLastFilledLayer;
2359}
2360
2361template <typename T>
2362class FixedCapacityQueue
2363{
2364public:
2365 FixedCapacityQueue(const std::size_t capacity)
2366 : m_vec(capacity)
2367 , m_front(m_vec.begin())
2368 , m_back(m_vec.begin())
2369 , m_size(0)
2370 {}
2371 bool empty() const
2372 {
2373 return m_size == 0;
2374 }
2375 const T& front() const
2376 {
2377 return *m_front;
2378 }
2379 void pop()
2380 {
2381 assert(m_size > 0);
2382 ++m_front;
2383 if(m_front == m_vec.end())
2384 m_front = m_vec.begin();
2385 --m_size;
2386 }
2387 void push(const T& t)
2388 {
2389 assert(m_size < m_vec.size());
2390 *m_back = t;
2391 ++m_back;
2392 if(m_back == m_vec.end())
2393 m_back = m_vec.begin();
2394 ++m_size;
2395 }
2396#ifdef CDT_CXX11_IS_SUPPORTED
2397 void push(const T&& t)
2398 {
2399 assert(m_size < m_vec.size());
2400 *m_back = t;
2401 ++m_back;
2402 if(m_back == m_vec.end())
2403 m_back = m_vec.begin();
2404 ++m_size;
2405 }
2406#endif
2407private:
2408 std::vector<T> m_vec;
2409 typename std::vector<T>::iterator m_front;
2410 typename std::vector<T>::iterator m_back;
2411 std::size_t m_size;
2412};
2413
2414template <typename T>
2415class less_than_x
2416{
2417 const std::vector<V2d<T> >& m_vertices;
2418
2419public:
2420 less_than_x(const std::vector<V2d<T> >& vertices)
2421 : m_vertices(vertices)
2422 {}
2423 bool operator()(const VertInd a, const VertInd b) const
2424 {
2425 return m_vertices[a].x < m_vertices[b].x;
2426 }
2427};
2428
2429template <typename T>
2430class less_than_y
2431{
2432 const std::vector<V2d<T> >& m_vertices;
2433
2434public:
2435 less_than_y(const std::vector<V2d<T> >& vertices)
2436 : m_vertices(vertices)
2437 {}
2438 bool operator()(const VertInd a, const VertInd b) const
2439 {
2440 return m_vertices[a].y < m_vertices[b].y;
2441 }
2442};
2443
2444} // namespace detail
2445
2446template <typename T, typename TNearPointLocator>
2447void Triangulation<T, TNearPointLocator>::insertVertices_KDTreeBFS(
2448 VertInd superGeomVertCount,
2449 Box2d<T> box)
2450{
2451 // calculate original indices
2452 const VertInd vertexCount(verticesCount() - superGeomVertCount);
2453 if(vertexCount <= VertInd(0))
2454 return;
2455 std::vector<VertInd> ii(vertexCount);
2456 detail::iota(ii.begin(), ii.end(), superGeomVertCount);
2457
2458 typedef std::vector<VertInd>::iterator It;
2460 detail::maxQueueLengthBFSKDTree(vertexCount));
2461 queue.push(make_tuple(ii.begin(), ii.end(), box.min, box.max, VertInd(0)));
2462
2463 It first, last;
2464 V2d<T> newBoxMin, newBoxMax;
2465 VertInd parent, mid;
2466
2467 const detail::less_than_x<T> cmpX(vertices);
2468 const detail::less_than_y<T> cmpY(vertices);
2469
2470 while(!queue.empty())
2471 {
2472#ifdef CDT_ENABLE_CALLBACK_HANDLER
2473 if(m_callbackHandler && m_callbackHandler->isAbortCalculation())
2474 {
2475 return;
2476 }
2477#endif
2478 tie(first, last, box.min, box.max, parent) = queue.front();
2479 queue.pop();
2480 assert(first != last);
2481
2482 const std::ptrdiff_t len = std::distance(first, last);
2483 if(len == 1)
2484 {
2485 insertVertex(*first, parent);
2486 continue;
2487 }
2488 const It midIt = first + len / 2;
2489 if(box.max.x - box.min.x >= box.max.y - box.min.y)
2490 {
2491 detail::portable_nth_element(first, midIt, last, cmpX);
2492 mid = *midIt;
2493 const T split = vertices[mid].x;
2494 newBoxMin.x = split;
2495 newBoxMin.y = box.min.y;
2496 newBoxMax.x = split;
2497 newBoxMax.y = box.max.y;
2498 }
2499 else
2500 {
2501 detail::portable_nth_element(first, midIt, last, cmpY);
2502 mid = *midIt;
2503 const T split = vertices[mid].y;
2504 newBoxMin.x = box.min.x;
2505 newBoxMin.y = split;
2506 newBoxMax.x = box.max.x;
2507 newBoxMax.y = split;
2508 }
2509 insertVertex(mid, parent);
2510 if(first != midIt)
2511 {
2512 queue.push(make_tuple(first, midIt, box.min, newBoxMax, mid));
2513 }
2514 if(midIt + 1 != last)
2515 {
2516 queue.push(make_tuple(midIt + 1, last, newBoxMin, box.max, mid));
2517 }
2518 }
2519}
2520
2521template <typename T, typename TNearPointLocator>
2522std::pair<TriInd, TriInd> Triangulation<T, TNearPointLocator>::edgeTriangles(
2523 const VertInd a,
2524 const VertInd b) const
2525{
2526 const TriInd triStart = m_vertTris[a];
2527 assert(triStart != noNeighbor);
2528 TriInd iT = triStart, iTNext = triStart;
2529 VertInd iV = noVertex;
2530 do
2531 {
2532 const Triangle& t = triangles[iT];
2533 tie(iTNext, iV) = t.next(a);
2534 assert(iTNext != noNeighbor);
2535 if(iV == b)
2536 {
2537 return std::make_pair(iT, iTNext);
2538 }
2539 iT = iTNext;
2540 } while(iT != triStart);
2541 return std::make_pair(noNeighbor, noNeighbor);
2542}
2543
2544template <typename T, typename TNearPointLocator>
2545bool Triangulation<T, TNearPointLocator>::hasEdge(
2546 const VertInd a,
2547 const VertInd b) const
2548{
2549 return edgeTriangles(a, b).first != invalidIndexSizeType;
2550}
2551
2552template <typename T, typename TNearPointLocator>
2553VertInd Triangulation<T, TNearPointLocator>::opposedVertexOnEdge(
2554 const Edge& edge,
2555 const T tolerance) const
2556{
2557 if(fixedEdges.count(edge))
2558 return noVertex;
2559 const V2d<T>& a = vertices[edge.v1()];
2560 const V2d<T>& b = vertices[edge.v2()];
2561 const std::pair<TriInd, TriInd> tris = edgeTriangles(edge.v1(), edge.v2());
2562 const VertInd opposed[] = {
2563 opposedVertex(triangles[tris.first], tris.second),
2564 opposedVertex(triangles[tris.second], tris.first)};
2565 for(int i = 0; i < 2; ++i)
2566 {
2567 const V2d<T>& v = vertices[opposed[i]];
2568 // near line ab and inside its diametral circle: both parts are
2569 // shorter than ab, so splitting terminates
2570 if(locatePointLine(v, a, b, tolerance) == PtLineLocation::OnLine &&
2571 detail::isEncroachingOnEdge(v, a, b))
2572 return opposed[i];
2573 }
2574 return noVertex;
2575}
2576
2580template <typename T, typename TNearPointLocator>
2581bool Triangulation<T, TNearPointLocator>::hasAnotherFixedEdgeAtSmallAngle(
2582 const VertInd v,
2583 const Edge& excludeEdge) const
2584{
2585 // splits stay on the original segment: its direction from v is unchanged
2586 const VertInd iVOther =
2587 excludeEdge.v1() == v ? excludeEdge.v2() : excludeEdge.v1();
2588 const TriInd triStart = m_vertTris[v];
2589 assert(triStart != noNeighbor);
2590 TriInd iT = triStart;
2591 do
2592 {
2593 const Triangle& t = triangles[iT];
2594 TriInd iTNext;
2595 VertInd iV;
2596 tie(iTNext, iV) = t.next(v);
2597 const Edge candidate(v, iV);
2598 if(candidate != excludeEdge && fixedEdges.count(candidate) &&
2599 detail::isAngleAtApexSmall(
2600 vertices[v], vertices[iVOther], vertices[iV]))
2601 {
2602 return true;
2603 }
2604 iT = iTNext;
2605 } while(iT != triStart);
2606 return false;
2607}
2608
2609template <typename T, typename TNearPointLocator>
2610void Triangulation<T, TNearPointLocator>::setAdjacentTriangle(
2611 const VertInd v,
2612 const TriInd t)
2613{
2614 assert(t != noNeighbor);
2615 m_vertTris[v] = t;
2616 assert(
2617 triangles[t].vertices[0] == v || triangles[t].vertices[1] == v ||
2618 triangles[t].vertices[2] == v);
2619}
2620
2621template <typename T, typename TNearPointLocator>
2622void Triangulation<T, TNearPointLocator>::pivotVertexTriangleCW(const VertInd v)
2623{
2624 assert(m_vertTris[v] != noNeighbor);
2625 m_vertTris[v] = triangles[m_vertTris[v]].next(v).first;
2626 assert(m_vertTris[v] != noNeighbor);
2627 assert(
2628 triangles[m_vertTris[v]].vertices[0] == v ||
2629 triangles[m_vertTris[v]].vertices[1] == v ||
2630 triangles[m_vertTris[v]].vertices[2] == v);
2631}
2632
2633template <typename T, typename TNearPointLocator>
2634void Triangulation<T, TNearPointLocator>::tryAddVertexToLocator(const VertInd v)
2635{
2636 if(!m_nearPtLocator.empty()) // only if locator is initialized already
2637 m_nearPtLocator.addPoint(v, vertices);
2638}
2639
2640template <typename T, typename TNearPointLocator>
2641void Triangulation<T, TNearPointLocator>::tryInitNearestPointLocator()
2642{
2643 if(!vertices.empty() && m_nearPtLocator.empty())
2644 {
2645 m_nearPtLocator.initialize(vertices);
2646 }
2647}
2648
2649template <typename T, typename TNearPointLocator>
2651 const VertInd maxVerticesToInsert,
2652 const RefinementCriterion::Enum refinementCriterion,
2653 const T refinementThreshold,
2654 TriIndUSet* const toEraseOrNull,
2655 const T minEdgeLength)
2656{
2657 if(isFinalized())
2658 handleException(FinalizedError(CDT_SOURCE_LOCATION));
2659
2660 tryInitNearestPointLocator();
2661
2662 Unrefined unrefined;
2663 VertInd remainingVertexBudget = maxVerticesToInsert;
2664 const VertInd steinerVerticesOffset = VertInd(vertices.size());
2665
2666 // split all the encroached constrained (fixed) edges
2667 resolveEncroachedEdges(
2669 remainingVertexBudget,
2670 steinerVerticesOffset,
2671 NULL, // no circumcenter yet: only edge-vs-edge encroachment matters
2672 refinementCriterion,
2673 refinementThreshold,
2674 toEraseOrNull,
2675 minEdgeLength,
2676 unrefined);
2677
2678 // refine triangulation by inserting bad-quality triangles' circumcenters
2679 TriIndQueue badTriangles;
2680 for(TriInd iT(0), n(triangles.size()); iT < n; ++iT)
2681 {
2682 const Triangle& t = triangles[iT];
2683 if(!touchesSuperTriangle(t) &&
2684 !(toEraseOrNull && toEraseOrNull->count(iT)) &&
2685 isRefinementNeeded(t, refinementCriterion, refinementThreshold))
2686 {
2687 badTriangles.push(iT);
2688 }
2689 }
2690
2691 while(!badTriangles.empty() && remainingVertexBudget > 0)
2692 {
2693 const TriInd iT = badTriangles.front();
2694 badTriangles.pop();
2695 if(toEraseOrNull && toEraseOrNull->count(iT))
2696 continue;
2697 if(!isRefinementNeeded(
2698 triangles[iT], refinementCriterion, refinementThreshold) ||
2699 (refinementCriterion == RefinementCriterion::SmallestAngle &&
2700 isSmallestAngleFixed(triangles[iT])))
2701 {
2702 // fixed-edge sharp angle is impossible to refine
2703 continue;
2704 }
2705 // copy: resolveEncroachedEdges below can re-allocate 'triangles'
2706 const VerticesArr3 badTVerts = triangles[iT].vertices;
2707 const V2d<T>& v0 = vertices[badTVerts[0]];
2708 const V2d<T>& v1 = vertices[badTVerts[1]];
2709 const V2d<T>& v2 = vertices[badTVerts[2]];
2710 const T shortestEdge = std::min(
2711 distance(v0, v1), std::min(distance(v1, v2), distance(v2, v0)));
2712 if(shortestEdge <= minEdgeLength)
2713 {
2714 // same minEdgeLength give-up
2715 ++unrefined.shortEdgeTriangles;
2716 continue;
2717 }
2718 const V2d<T> circumcenterPos = circumcenter(v0, v1, v2);
2719 const OptionalTriInd triAtCircumcenter = walkTriangles(
2720 m_nearPtLocator.nearPoint(circumcenterPos, vertices),
2721 circumcenterPos);
2722 if(!triAtCircumcenter.hasValue())
2723 {
2724 // circumcenter falls outside triangulated area
2725 ++unrefined.circumcenterOutside;
2726 continue;
2727 }
2728
2729 const VertInd budgetBeforeSplits = remainingVertexBudget;
2730 const EdgeVec encroachedEdges =
2731 edgesEncroachedBy(circumcenterPos, triAtCircumcenter.value());
2732 const TriIndVec badTris = resolveEncroachedEdges(
2733 detail::toQueue(encroachedEdges),
2734 remainingVertexBudget,
2735 steinerVerticesOffset,
2736 &circumcenterPos,
2737 refinementCriterion,
2738 refinementThreshold,
2739 toEraseOrNull,
2740 minEdgeLength,
2741 unrefined);
2742 if(!remainingVertexBudget)
2743 break;
2744
2745 // a circumcenter encroaching on fixed edges:
2746 // split edges instead and re-visit the triangle later
2747 if(remainingVertexBudget != budgetBeforeSplits || !badTris.empty())
2748 {
2749 typedef TriIndVec::const_iterator It;
2750 for(It it = badTris.begin(); it != badTris.end(); ++it)
2751 {
2752 badTriangles.push(*it);
2753 }
2754 badTriangles.push(iT);
2755 continue;
2756 }
2757 // splitting was given up on: inserting would encroach anyway
2758 if(!encroachedEdges.empty())
2759 continue;
2760
2761 const TriInd iCircumcenterTri = triAtCircumcenter.value();
2762 const Triangle& circumcenterTri = triangles[iCircumcenterTri];
2764 circumcenterPos,
2765 vertices[circumcenterTri.vertices[0]],
2766 vertices[circumcenterTri.vertices[1]],
2767 vertices[circumcenterTri.vertices[2]]);
2768 if(loc == PtTriLocation::OnVertex)
2769 {
2770 // circumcenter coincides with an existing vertex
2771 ++unrefined.circumcenterOnVertex;
2772 continue;
2773 }
2774 const array<TriInd, 2> trisAt = {
2775 iCircumcenterTri,
2776 isOnEdge(loc) ? circumcenterTri.neighbors[edgeNeighbor(loc)]
2777 : noNeighbor};
2778 // Skip adding a Steiner point if the triangle will be removed anyway.
2779 if(toEraseOrNull &&
2780 (toEraseOrNull->count(trisAt[0]) || toEraseOrNull->count(trisAt[1])))
2781 {
2782 // circumcenter falls into a triangle that will be removed
2783 ++unrefined.circumcenterOutside;
2784 continue;
2785 }
2786
2787 --remainingVertexBudget;
2788 const VertInd iVert = static_cast<VertInd>(vertices.size());
2789 addNewVertex(circumcenterPos, noNeighbor);
2790#ifdef CDT_ENABLE_CALLBACK_HANDLER
2791 if(m_callbackHandler)
2792 {
2793 m_callbackHandler->onAddVertexStart(
2795 }
2796#endif
2797 std::stack<TriInd> triStack =
2798 trisAt[1] == noNeighbor
2799 ? insertVertexInsideTriangle(iVert, trisAt[0])
2800 : insertVertexOnEdge(iVert, trisAt[0], trisAt[1], true);
2801 ensureDelaunayByEdgeFlips(iVert, triStack);
2802 tryAddVertexToLocator(iVert);
2803
2804 TriInd start = m_vertTris[iVert];
2805 TriInd currTri = start;
2806 do
2807 {
2808 const Triangle& t = triangles[currTri];
2809 if(!(toEraseOrNull && toEraseOrNull->count(currTri)) &&
2811 isRefinementNeeded(t, refinementCriterion, refinementThreshold))
2812 {
2813 badTriangles.push(currTri);
2814 }
2815 currTri = t.next(iVert).first;
2816 } while(currTri != start);
2817 }
2818
2819 // sharp corners that come from the input are impossible to refine: report
2820 // the triangles that are left too sharp because of them
2821 if(refinementCriterion == RefinementCriterion::SmallestAngle)
2822 {
2823 for(TriInd iT(0), n(triangles.size()); iT < n; ++iT)
2824 {
2825 const Triangle& t = triangles[iT];
2826 if(touchesSuperTriangle(t) ||
2827 (toEraseOrNull && toEraseOrNull->count(iT)))
2828 {
2829 continue;
2830 }
2831 if(isRefinementNeeded(
2832 t, refinementCriterion, refinementThreshold) &&
2833 isSmallestAngleFixed(t))
2834 {
2835 ++unrefined.sharpFixedCorner;
2836 }
2837 }
2838 }
2839 return unrefined;
2840}
2841
2842} // namespace CDT
2843
2844CDT_RESTORE_MATH_SETTINGS_FOR_CONSTRUCTIONS
2845
2846#endif // header-guard
Triangulation class.
void iota(ForwardIt first, ForwardIt last, T value)
backport from c++11
std::size_t maxQueueLengthBFSKDTree(const std::size_t vertexCount)
Since KD-tree bulk load builds a balanced tree the maximum length of a queue can be pre-calculated: i...
bool isAngleAtApexSmall(const V2d< T > &apex, const V2d< T > &a, const V2d< T > &b)
Whether the angle at apex between apex->a and apex->b is smaller than 60°
EdgeQueue toQueue(const EdgeVec &edges)
Turn a vector into a queue, preserving order.
void sortUnique(TVec &v)
Sort a vector and remove its duplicate elements.
Base class for errors.
Error thrown when triangulation modification is attempted after it was finalized.
Interface for the callback handler that user can derive from and inject into the triangulation to mon...
Error thrown when intersecting constraint edges are detected, but triangulation is not configured to ...
Error thrown when resolving intersecting constraints fails: floating-point rounding places the comput...
EdgeUSet fixedEdges
triangulation's constraints (fixed edges)
void eraseOuterTriangles()
Erase triangles outside of constrained boundary using growing.
void conformToEdges(TEdgeIter first, TEdgeIter last, TGetEdgeVertexStart getStart, TGetEdgeVertexEnd getEnd)
Insert constraint edges into triangulation for Conforming Delaunay Triangulation (for example see fig...
std::vector< LayerDepth > calculateTriangleDepths() const
Calculate depth of each triangle in constraint triangulation.
TriIndUSet collectSuperTriangle() const
Collect triangles adjacent to super-triangle: same triangles that eraseSuperTriangle would remove.
bool isFinalized() const
Check if the triangulation was finalized with erase... method and super-triangle was removed.
TriIndUSet collectOuterTrianglesAndHoles() const
Collect triangles outside of constrained boundary and auto-detected holes: same triangles that eraseO...
TriIndVec findUnrefinedTriangles(RefinementCriterion::Enum refinementCriterion=RefinementCriterion::SmallestAngle, T refinementThreshold=degToRad(T(20))) const
Find triangles that don't fulfill the refinement criterion.
void finalizeTriangulation(const TriIndUSet &removedTriangles)
Remove super-triangle and triangles with specified indices.
void eraseOuterTrianglesAndHoles()
Erase triangles outside of constrained boundary and auto-detected holes.
V2dVec vertices
triangulation's vertices
TriIndUSet collectOuterTriangles() const
Collect triangles outside of constrained boundary: same triangles that eraseOuterTriangles would remo...
Unrefined refineTriangles(VertInd maxVerticesToInsert, RefinementCriterion::Enum refinementCriterion=RefinementCriterion::SmallestAngle, T refinementThreshold=degToRad(T(20)), TriIndUSet *toEraseOrNull=NULL, T minEdgeLength=T(1e-6))
Triangles refinement by splitting bad triangles.
void insertEdges(TEdgeIter first, TEdgeIter last, TGetEdgeVertexStart getStart, TGetEdgeVertexEnd getEnd)
Insert constraint edges into triangulation for Constrained Delaunay Triangulation (for example see fi...
void insertVertices(TVertexIter first, TVertexIter last, TGetVertexCoordX getX, TGetVertexCoordY getY)
Insert custom point-types specified by iterator range and X/Y-getters.
void eraseSuperTriangle()
Erase triangles adjacent to super triangle.
unordered_map< Edge, EdgeVec > pieceToOriginals
Stores list of original edges represented by a given fixed edge.
unordered_map< Edge, BoundaryOverlapCount > overlapCount
Stores count of overlapping boundaries for a fixed edge.
TriangleVec triangles
triangulation's triangles
Triangulation()
Default constructor.
EdgeVec findEncroachedFixedEdges() const
Find all fixed edges encroached by their opposed vertices.
void setCallbackHandler(ICallbackHandler *callbackHandler)
Set user-provided callback handler.
unsigned short LayerDepth
Type used for storing layer depths for triangles.
Definition CDT.h:39
OptionalIndex< VertInd > OptionalVertInd
Optional vertex index.
OptionalIndex< TriInd > OptionalTriInd
Optional triangle index.
void flipEdge(TriInd iT, TriInd iTopo)
Flip an edge between two triangle.
TriIndVec & VertTrisInternal()
Access internal vertex adjacent triangles.
void removeTriangles(const TriIndUSet &removedTriangles)
Remove triangles with specified indices.
Namespace containing triangulation functionality.
std::vector< Edge > EdgeVec
Vector of edges.
Definition CDTUtils.h:400
CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY VertInd opposedVertex(const Triangle &tri, TriInd iTopo)
Given two triangles, return vertex of first triangle opposed to the second.
Definition CDTUtils.hpp:204
unordered_set< Edge > EdgeUSet
Hash table of edges.
Definition CDTUtils.h:403
std::queue< TriInd > TriIndQueue
Queue of triangles.
Definition CDTUtils.h:402
VertInd edge_get_v2(const Edge &e)
Get edge second vertex.
Definition CDTUtils.h:389
CDT_EXPORT T area(const V2d< T > &a, const V2d< T > &b, const V2d< T > &c)
Surface area of a triangle ABC.
Definition CDTUtils.hpp:303
std::vector< TriInd > TriIndVec
Vector of triangle indices.
Definition CDTUtils.h:272
CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index edgeNeighborInd(const VerticesArr3 &vv, VertInd iVedge1, VertInd iVedge2)
Index of triangle's neighbor opposed to an edge.
Definition CDTUtils.hpp:142
VertInd edge_get_v1(const Edge &e)
Get edge first vertex.
Definition CDTUtils.h:383
CDT_EXPORT PtLineLocation::Enum classifyOrientation(T orientation, T orientationTolerance=T(0))
Classify value of orient2d predicate.
Definition CDTUtils.hpp:64
array< TriInd, 3 > NeighborsArr3
array of three neighbors
Definition CDTUtils.h:274
CDT_EXPORT T distance(const V2d< T > &a, const V2d< T > &b)
Distance between two 2D points.
Definition CDTUtils.hpp:251
IndexSizeType VertInd
Vertex index.
Definition CDTUtils.h:254
CDT_EXPORT Index cw(Index i)
Advance vertex or neighbor index clockwise.
Definition CDTUtils.hpp:28
array< VertInd, 3 > VerticesArr3
array of three vertex indices
Definition CDTUtils.h:273
CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY bool touchesSuperTriangle(const Triangle &t)
Check if any of triangle's vertices belongs to a super-triangle.
Definition CDTUtils.hpp:262
array< T, 3 > arr3(const T &v0, const T &v1, const T &v2)
Needed for c++03 compatibility (no uniform initialization available)
Definition CDTUtils.h:175
CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index opoNbr(Index vertIndex)
Opposed neighbor index from vertex index.
Definition CDTUtils.hpp:105
CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index vertexInd(const VerticesArr3 &vv, VertInd iV)
If triangle has a given vertex return vertex-index.
Definition CDTUtils.hpp:187
CDT_EXPORT T orient2D(const V2d< T > &p, const V2d< T > &v1, const V2d< T > &v2)
Orient p against line v1-v2 2D: robust geometric predicate.
Definition CDTUtils.hpp:47
CDT_EXPORT PtLineLocation::Enum locatePointLine(const V2d< T > &p, const V2d< T > &v1, const V2d< T > &v2, T orientationTolerance=T(0))
Check if point lies to the left of, to the right of, or on a line.
Definition CDTUtils.hpp:53
CDT_EXPORT T distanceSquared(const V2d< T > &a, const V2d< T > &b)
Squared distance between two 2D points.
Definition CDTUtils.hpp:257
unordered_set< TriInd > TriIndUSet
Hash table of triangles.
Definition CDTUtils.h:404
CDT_EXPORT Index ccw(Index i)
Advance vertex or neighbor index counter-clockwise.
Definition CDTUtils.hpp:23
CDT_EXPORT V2d< T > circumcenter(V2d< T > a, V2d< T > b, V2d< T > c)
Position of ABC triangle circumcenter.
Definition CDTUtils.hpp:309
CDT_EXPORT Index edgeNeighbor(PtTriLocation::Enum location)
Neighbor index from a on-edge location.
Definition CDTUtils.hpp:40
unordered_map< TriInd, TriInd > TriIndUMap
Triangle hash map.
Definition CDTUtils.h:405
const T & getX_V2d(const V2d< T > &v)
X- coordinate getter for V2d.
Definition CDTUtils.h:211
unsigned char Index
Index in triangle.
Definition CDTUtils.h:252
CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY TriInd opposedTriangle(const Triangle &tri, VertInd iVert)
Given triangle and a vertex find opposed triangle.
Definition CDTUtils.hpp:198
CDT_EXPORT T smallestAngle(const V2d< T > &a, const V2d< T > &b, const V2d< T > &c)
Smallest angle of triangle ABC in radians.
Definition CDTUtils.hpp:322
CDT_EXPORT bool isInCircumcircle(const V2d< T > &p, const V2d< T > &v1, const V2d< T > &v2, const V2d< T > &v3)
Test if point lies in a circumscribed circle of a triangle.
Definition CDTUtils.hpp:217
Edge RemapNoSuperTriangle(const Edge &e)
Remap removing super-triangle: subtract 3 from vertices.
CDT_EXPORT bool isOnEdge(PtTriLocation::Enum location)
Check if location is classified as on any of three edges.
Definition CDTUtils.hpp:33
IndexSizeType TriInd
Triangle index.
Definition CDTUtils.h:256
CDT_EXPORT PtTriLocation::Enum locatePointTriangle(const V2d< T > &p, const V2d< T > &v1, const V2d< T > &v2, const V2d< T > &v3)
Check if point a lies inside of, outside of, or on an edge of a triangle.
Definition CDTUtils.hpp:74
std::queue< Edge > EdgeQueue
Queue of edges.
Definition CDTUtils.h:401
CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index opposedVertexInd(const NeighborsArr3 &nn, TriInd iTopo)
Index of triangle's vertex opposed to a triangle.
Definition CDTUtils.hpp:176
const T & getY_V2d(const V2d< T > &v)
Y-coordinate getter for V2d.
Definition CDTUtils.h:218
Enum
The Enum itself.
@ RefinementCircumcenter
Refinement: circumcenter of a poor-quality triangle.
@ FixedEdgeMidpoint
During conforming triangulation edge mid-point is added.
@ FixedEdgesIntersection
Resolving fixed/constraint edges' intersection.
2D bounding box
Definition CDTUtils.h:279
V2d< T > max
max box corner
Definition CDTUtils.h:281
V2d< T > min
min box corner
Definition CDTUtils.h:280
Edge connecting two vertices: vertex with smaller index is always first.
Definition CDTUtils.h:334
VertInd v1() const
V1 getter.
Definition CDTUtils.h:361
VertInd v2() const
V2 getter.
Definition CDTUtils.h:367
@ TryResolve
attempt to resolve constraint edge intersections
@ NotAllowed
constraint edge intersections are not allowed
@ DontCheck
No checks: slightly faster but less safe.
@ LargestArea
constraint maximum triangles area
@ SmallestAngle
constraint minimum triangles angle
Triangulation triangle (counter-clockwise winding)
Definition CDTUtils.h:416
VerticesArr3 vertices
triangle's three vertices
Definition CDTUtils.h:417
NeighborsArr3 neighbors
triangle's three neighbors
Definition CDTUtils.h:418
std::pair< TriInd, VertInd > next(const VertInd i) const
Next triangle adjacent to a vertex (clockwise)
Definition CDTUtils.h:434
Counts of the refinements that Triangulation::refineTriangles was not able to perform.
std::size_t circumcenterOnVertex
triangles whose circumcenter coincides with an existing vertex
std::size_t sharpFixedCorner
triangles whose smallest angle is enclosed by two fixed edges: such an angle comes from the input and...
std::size_t splitVertexInvalid
fixed edges whose split vertex can not be placed: inserting it would break the triangulation's topolo...
std::size_t circumcenterOutside
triangles whose circumcenter is outside the triangulated area
std::size_t shortEdges
fixed edges that are shorter than the threshold
std::size_t shortEdgeTriangles
triangles whose shortest edge is shorter than the threshold
Unrefined()
Constructor: all the counts start at zero.
2D vector
Definition CDTUtils.h:192
T y
Y-coordinate.
Definition CDTUtils.h:194
T x
X-coordinate.
Definition CDTUtils.h:193
@ Auto
Automatic insertion order optimized for better performance.