CDT  2.0.2
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 {
1686 encroachedEdges.push(triEdge);
1687 }
1688 }
1689 iT = t.next(iSplitVert).first;
1690 } while(iT != start);
1691 }
1692 return badTriangles;
1693}
1694
1695template <typename T, typename TNearPointLocator>
1696OptionalVertInd Triangulation<T, TNearPointLocator>::splitEncroachedEdge(
1697 const Edge edge,
1698 const VertInd steinerVerticesOffset,
1699 TriIndUSet* const toEraseOrNull,
1700 Unrefined& unrefined)
1701{
1702 const V2d<T>& start = vertices[edge.v1()];
1703 const V2d<T>& end = vertices[edge.v2()];
1704
1705 TriInd iT, iTopo;
1706 tie(iT, iTopo) = edgeTriangles(edge.v1(), edge.v2());
1707 assert(iT != noNeighbor && iTopo != noNeighbor);
1708
1709 T split = T(0.5);
1710 // Edge sorts its vertices and Steiner ones are appended after the input
1711 // ones, so only v1 can be an input vertex here
1712 if(edge.v1() < steinerVerticesOffset &&
1713 edge.v2() >= steinerVerticesOffset &&
1714 hasAnotherFixedEdgeAtSmallAngle(edge.v1(), edge))
1715 {
1716 // In Ruppert's paper, he used D(0.01) factor to divide edge length, but
1717 // that introduces FP rounding errors, so it's avoided.
1718 const T len = distance(start, end);
1719 const T d = len / T(2);
1720 // Find the splitting distance
1721 T nearestPowerOfTwo = T(1);
1722 while(d > nearestPowerOfTwo)
1723 {
1724 nearestPowerOfTwo *= T(2);
1725 }
1726 while(d < T(0.75) * nearestPowerOfTwo)
1727 {
1728 nearestPowerOfTwo *= T(0.5);
1729 }
1730 split = nearestPowerOfTwo / len;
1731 }
1732
1733 const V2d<T> mid = V2d<T>(
1734 detail::lerp(start.x, end.x, split),
1735 detail::lerp(start.y, end.y, split));
1736
1737 const OptionalVertInd iMid = splitFixedEdgeAt(
1738 edge, mid, iT, iTopo, AddVertexType::RefinementEdgeSplit);
1739 if(!iMid.hasValue())
1740 {
1741 ++unrefined.splitVertexInvalid;
1742 }
1743 else if(toEraseOrNull)
1744 {
1745 // splitting reuses iT/iTopo for two of the four resulting triangles and
1746 // appends the other two: propagate erasure marks to the new triangles
1747 if(toEraseOrNull->count(iT))
1748 toEraseOrNull->insert(TriInd(triangles.size() - 2));
1749 if(toEraseOrNull->count(iTopo))
1750 toEraseOrNull->insert(TriInd(triangles.size() - 1));
1751 }
1752 return iMid;
1753}
1754
1755/* Flip edge between T and Topo:
1756 *
1757 * v4 | - old edge
1758 * /|\ ~ - new edge
1759 * / | \
1760 * n3 / T' \ n4
1761 * / | \
1762 * / | \
1763 * T -> v1 ~~~~~~~~ v3 <- Topo
1764 * \ | /
1765 * \ | /
1766 * n1 \Topo'/ n2
1767 * \ | /
1768 * \|/
1769 * v2
1770 */
1771template <typename T, typename TNearPointLocator>
1773 const TriInd iT,
1774 const TriInd iTopo,
1775 const VertInd v1,
1776 const VertInd v2,
1777 const VertInd v3,
1778 const VertInd v4,
1779 const TriInd n1,
1780 const TriInd n2,
1781 const TriInd n3,
1782 const TriInd n4)
1783{
1784#ifdef CDT_ENABLE_CALLBACK_HANDLER
1785 if(m_callbackHandler)
1786 {
1787 m_callbackHandler->onFlipEdge(iT, iTopo);
1788 }
1789#endif
1790
1791 // change vertices and neighbors
1792 triangles[iT] = Triangle(arr3(v4, v1, v3), arr3(n3, iTopo, n4));
1793 triangles[iTopo] = Triangle(arr3(v2, v3, v1), arr3(n2, iT, n1));
1794 // adjust neighboring triangles and vertices
1795 changeNeighbor(n1, iT, iTopo);
1796 changeNeighbor(n4, iTopo, iT);
1797 // only adjust adjacent triangles if triangulation is not finalized:
1798 // can happen when called from outside on an already finalized
1799 // triangulation
1800 if(!isFinalized())
1801 {
1802 setAdjacentTriangle(v4, iT);
1803 setAdjacentTriangle(v2, iTopo);
1804 }
1805}
1806
1807/* Insert point into triangle: split into 3 triangles:
1808 * - create 2 new triangles
1809 * - re-use old triangle for the 3rd
1810 * v3
1811 * / | \
1812 * / | \ <-- original triangle (t)
1813 * / | \
1814 * n3 / | \ n2
1815 * /newT2|newT1\
1816 * / v \
1817 * / __/ \__ \
1818 * / __/ \__ \
1819 * / _/ t' \_ \
1820 * v1 ___________________ v2
1821 * n1
1822 */
1823template <typename T, typename TNearPointLocator>
1824std::stack<TriInd>
1825Triangulation<T, TNearPointLocator>::insertVertexInsideTriangle(
1826 VertInd v,
1827 TriInd iT)
1828{
1829 const TriInd iNewT1 = addTriangle();
1830 const TriInd iNewT2 = addTriangle();
1831
1832#ifdef CDT_ENABLE_CALLBACK_HANDLER
1833 if(m_callbackHandler)
1834 {
1835 m_callbackHandler->onInsertVertexInsideTriangle(iT, iNewT1, iNewT2);
1836 }
1837#endif
1838
1839 Triangle& t = triangles[iT];
1840 const array<VertInd, 3> vv = t.vertices;
1841 const array<TriInd, 3> nn = t.neighbors;
1842 const VertInd v1 = vv[0], v2 = vv[1], v3 = vv[2];
1843 const TriInd n1 = nn[0], n2 = nn[1], n3 = nn[2];
1844 // make two new triangles and convert current triangle to 3rd new
1845 // triangle
1846 triangles[iNewT1] = Triangle(arr3(v2, v3, v), arr3(n2, iNewT2, iT));
1847 triangles[iNewT2] = Triangle(arr3(v3, v1, v), arr3(n3, iT, iNewT1));
1848 t = Triangle(arr3(v1, v2, v), arr3(n1, iNewT1, iNewT2));
1849 // adjust adjacent triangles
1850 setAdjacentTriangle(v, iT);
1851 setAdjacentTriangle(v3, iNewT1);
1852 // change triangle neighbor's neighbors to new triangles
1853 changeNeighbor(n2, iT, iNewT1);
1854 changeNeighbor(n3, iT, iNewT2);
1855 // return newly added triangles
1856 std::stack<TriInd> newTriangles;
1857 newTriangles.push(iT);
1858 newTriangles.push(iNewT1);
1859 newTriangles.push(iNewT2);
1860 return newTriangles;
1861}
1862
1863/* Inserting a point on the edge between two triangles
1864 * T1 (top) v1
1865 * /|\
1866 * n1 / | \ n4
1867 * / | \
1868 * / T1' | Tnew1\
1869 * v2-------v-------v4
1870 * \ T2' | Tnew2/
1871 * \ | /
1872 * n2 \ | / n3
1873 * \|/
1874 * T2 (bottom) v3
1875 */
1876template <typename T, typename TNearPointLocator>
1877std::stack<TriInd> Triangulation<T, TNearPointLocator>::insertVertexOnEdge(
1878 VertInd v,
1879 TriInd iT1,
1880 TriInd iT2,
1881 const bool doHandleFixedSplitEdge)
1882{
1883 const TriInd iTnew1 = addTriangle();
1884 const TriInd iTnew2 = addTriangle();
1885
1886#ifdef CDT_ENABLE_CALLBACK_HANDLER
1887 if(m_callbackHandler)
1888 {
1889 m_callbackHandler->onInsertVertexOnEdge(iT1, iT2, iTnew1, iTnew2);
1890 }
1891#endif
1892
1893 Triangle& t1 = triangles[iT1];
1894 Triangle& t2 = triangles[iT2];
1895 Index i = opposedVertexInd(t1.neighbors, iT2);
1896 const VertInd v1 = t1.vertices[i];
1897 const VertInd v2 = t1.vertices[ccw(i)];
1898 const TriInd n1 = t1.neighbors[i];
1899 const TriInd n4 = t1.neighbors[cw(i)];
1900 i = opposedVertexInd(t2.neighbors, iT1);
1901 const VertInd v3 = t2.vertices[i];
1902 const VertInd v4 = t2.vertices[ccw(i)];
1903 const TriInd n3 = t2.neighbors[i];
1904 const TriInd n2 = t2.neighbors[cw(i)];
1905 // add new triangles and change existing ones
1906 t1 = Triangle(arr3(v, v1, v2), arr3(iTnew1, n1, iT2));
1907 t2 = Triangle(arr3(v, v2, v3), arr3(iT1, n2, iTnew2));
1908 triangles[iTnew1] = Triangle(arr3(v, v4, v1), arr3(iTnew2, n4, iT1));
1909 triangles[iTnew2] = Triangle(arr3(v, v3, v4), arr3(iT2, n3, iTnew1));
1910 // adjust adjacent triangles
1911 setAdjacentTriangle(v, iT1);
1912 setAdjacentTriangle(v4, iTnew1);
1913 // adjust neighboring triangles and vertices
1914 changeNeighbor(n4, iT1, iTnew1);
1915 changeNeighbor(n3, iT2, iTnew2);
1916 // properly handle the case when the split edge is a fixed edge
1917 if(doHandleFixedSplitEdge)
1918 {
1919 const Edge sharedEdge(v2, v4);
1920 if(fixedEdges.count(sharedEdge))
1921 splitFixedEdge(sharedEdge, v);
1922 }
1923 // return newly added triangles
1924 std::stack<TriInd> newTriangles;
1925 newTriangles.push(iT1);
1926 newTriangles.push(iTnew2);
1927 newTriangles.push(iT2);
1928 newTriangles.push(iTnew1);
1929 return newTriangles;
1930}
1931
1932template <typename T, typename TNearPointLocator>
1933array<TriInd, 2>
1934Triangulation<T, TNearPointLocator>::trianglesAt(const V2d<T>& pos) const
1935{
1936 array<TriInd, 2> out = {noNeighbor, noNeighbor};
1937 for(TriInd i = TriInd(0); i < TriInd(triangles.size()); ++i)
1938 {
1939 const Triangle& t = triangles[i];
1940 const V2d<T>& v1 = vertices[t.vertices[0]];
1941 const V2d<T>& v2 = vertices[t.vertices[1]];
1942 const V2d<T>& v3 = vertices[t.vertices[2]];
1943 const PtTriLocation::Enum loc = locatePointTriangle(pos, v1, v2, v3);
1944 if(loc == PtTriLocation::Outside)
1945 continue;
1946 out[0] = i;
1947 if(isOnEdge(loc))
1948 out[1] = t.neighbors[edgeNeighbor(loc)];
1949 return out;
1950 }
1951 handleException(
1952 Error("No triangle was found at position", CDT_SOURCE_LOCATION));
1953 return out;
1954}
1955
1956template <typename T, typename TNearPointLocator>
1957OptionalTriInd Triangulation<T, TNearPointLocator>::walkTriangles(
1958 const VertInd startVertex,
1959 const V2d<T>& pos) const
1960{
1961 // begin walk in search of triangle at pos
1962 TriInd currTri = m_vertTris[startVertex];
1963 bool found = false;
1964 bool isOutside = false;
1965 detail::SplitMix64RandGen prng;
1966 while(!found)
1967 {
1968 const Triangle& t = triangles[currTri];
1969 found = true;
1970 isOutside = false;
1971 // stochastic offset to randomize which edge we check first
1972 const Index offset(prng() % 3);
1973 for(Index i_(0); i_ < Index(3); ++i_)
1974 {
1975 const Index i((i_ + offset) % 3);
1976 const V2d<T>& vStart = vertices[t.vertices[i]];
1977 const V2d<T>& vEnd = vertices[t.vertices[ccw(i)]];
1978 const PtLineLocation::Enum edgeCheck =
1979 locatePointLine(pos, vStart, vEnd);
1980 const TriInd iN = t.neighbors[i];
1981 if(edgeCheck == PtLineLocation::Right)
1982 {
1983 if(iN != noNeighbor)
1984 {
1985 found = false;
1986 currTri = iN;
1987 break;
1988 }
1989 // crossing this edge would leave the triangulated area:
1990 // other edges can still lead to a triangle containing pos
1991 isOutside = true;
1992 }
1993 }
1994 }
1995 return isOutside ? OptionalTriInd(noNeighbor) : OptionalTriInd(currTri);
1996}
1997
1998template <typename T, typename TNearPointLocator>
1999array<TriInd, 2> Triangulation<T, TNearPointLocator>::walkingSearchTrianglesAt(
2000 const VertInd iV,
2001 const VertInd startVertex) const
2002{
2003 const V2d<T> v = vertices[iV];
2004 array<TriInd, 2> out = {noNeighbor, noNeighbor};
2005 const OptionalTriInd walkResult = walkTriangles(startVertex, v);
2006 if(!walkResult.hasValue())
2007 {
2008 handleException(
2009 Error("No triangle was found at position", CDT_SOURCE_LOCATION));
2010 }
2011 const TriInd iT = walkResult.value();
2012 // Finished walk, locate point in current triangle
2013 const Triangle& t = triangles[iT];
2014 const V2d<T>& v1 = vertices[t.vertices[0]];
2015 const V2d<T>& v2 = vertices[t.vertices[1]];
2016 const V2d<T>& v3 = vertices[t.vertices[2]];
2017 const PtTriLocation::Enum loc = locatePointTriangle(v, v1, v2, v3);
2018
2019 if(loc == PtTriLocation::Outside)
2020 {
2021 handleException(
2022 Error("No triangle was found at position", CDT_SOURCE_LOCATION));
2023 }
2024 if(loc == PtTriLocation::OnVertex)
2025 {
2026 const VertInd iDupe = v1 == v ? t.vertices[0]
2027 : v2 == v ? t.vertices[1]
2028 : t.vertices[2];
2029 handleException(DuplicateVertexError(
2030 VertInd(iV - nSuperTriVerts),
2031 VertInd(iDupe - nSuperTriVerts),
2032 CDT_SOURCE_LOCATION));
2033 }
2034
2035 out[0] = iT;
2036 if(isOnEdge(loc))
2037 out[1] = t.neighbors[edgeNeighbor(loc)];
2038 return out;
2039}
2040
2041template <typename T, typename TNearPointLocator>
2042void Triangulation<T, TNearPointLocator>::changeNeighbor(
2043 const TriInd iT,
2044 const TriInd oldNeighbor,
2045 const TriInd newNeighbor)
2046{
2047 if(iT == noNeighbor)
2048 return;
2049 NeighborsArr3& nn = triangles[iT].neighbors;
2050 assert(
2051 nn[0] == oldNeighbor || nn[1] == oldNeighbor || nn[2] == oldNeighbor);
2052 if(nn[0] == oldNeighbor)
2053 nn[0] = newNeighbor;
2054 else if(nn[1] == oldNeighbor)
2055 nn[1] = newNeighbor;
2056 else
2057 nn[2] = newNeighbor;
2058}
2059
2060template <typename T, typename TNearPointLocator>
2061void Triangulation<T, TNearPointLocator>::changeNeighbor(
2062 const TriInd iT,
2063 const VertInd iVedge1,
2064 const VertInd iVedge2,
2065 const TriInd newNeighbor)
2066{
2067 assert(iT != noNeighbor);
2068 Triangle& t = triangles[iT];
2069 t.neighbors[edgeNeighborInd(t.vertices, iVedge1, iVedge2)] = newNeighbor;
2070}
2071
2072template <typename T, typename TNearPointLocator>
2073void Triangulation<T, TNearPointLocator>::triangulatePseudoPolygon(
2074 const std::vector<VertInd>& poly,
2075 unordered_map<Edge, TriInd>& outerTris,
2076 TriInd iT,
2077 TriInd iN,
2078 std::vector<TriInd>& trianglesToReuse,
2079 std::vector<TriangulatePseudoPolygonTask>& iterations)
2080{
2081 assert(poly.size() > 2);
2082 // note: uses iteration instead of recursion to avoid stack overflows
2083 iterations.clear();
2084 iterations.push_back(make_tuple(
2085 IndexSizeType(0),
2086 static_cast<IndexSizeType>(poly.size() - 1),
2087 iT,
2088 iN,
2089 Index(0)));
2090 while(!iterations.empty())
2091 {
2092 triangulatePseudoPolygonIteration(
2093 poly, outerTris, trianglesToReuse, iterations);
2094 }
2095}
2096
2097template <typename T, typename TNearPointLocator>
2098void Triangulation<T, TNearPointLocator>::triangulatePseudoPolygonIteration(
2099 const std::vector<VertInd>& poly,
2100 unordered_map<Edge, TriInd>& outerTris,
2101 std::vector<TriInd>& trianglesToReuse,
2102 std::vector<TriangulatePseudoPolygonTask>& iterations)
2103{
2104 IndexSizeType iA, iB;
2105 TriInd iT, iParent;
2106 Index iInParent;
2107 assert(!iterations.empty());
2108 tie(iA, iB, iT, iParent, iInParent) = iterations.back();
2109 iterations.pop_back();
2110 assert(iB - iA > 1 && iT != noNeighbor && iParent != noNeighbor);
2111 Triangle& t = triangles[iT];
2112 // find Delaunay point
2113 const IndexSizeType iC = findDelaunayPoint(poly, iA, iB);
2114
2115 const VertInd a = poly[iA];
2116 const VertInd b = poly[iB];
2117 const VertInd c = poly[iC];
2118
2119 // split pseudo-polygon in two parts and triangulate them
2120 // note: second part needs to be pushed on stack first to be processed first
2121
2122 // second part: points after the Delaunay point
2123 if(iB - iC > 1)
2124 {
2125 assert(!trianglesToReuse.empty());
2126 const TriInd iNext = trianglesToReuse.back();
2127 trianglesToReuse.pop_back();
2128 iterations.push_back(make_tuple(iC, iB, iNext, iT, Index(1)));
2129 }
2130 else // pseudo-poly is reduced to a single outer edge
2131 {
2132 const Edge outerEdge(b, c);
2133 const TriInd outerTri = outerTris.at(outerEdge);
2134 if(outerTri != noNeighbor)
2135 {
2136 assert(outerTri != iT);
2137 t.neighbors[1] = outerTri;
2138 changeNeighbor(outerTri, c, b, iT);
2139 }
2140 else
2141 outerTris.at(outerEdge) = iT;
2142 }
2143 // first part: points before the Delaunay point
2144 if(iC - iA > 1)
2145 { // add next triangle and add another iteration
2146 assert(!trianglesToReuse.empty());
2147 const TriInd iNext = trianglesToReuse.back();
2148 trianglesToReuse.pop_back();
2149 iterations.push_back(make_tuple(iA, iC, iNext, iT, Index(2)));
2150 }
2151 else
2152 { // pseudo-poly is reduced to a single outer edge
2153 const Edge outerEdge(c, a);
2154 const TriInd outerTri = outerTris.at(outerEdge);
2155 if(outerTri != noNeighbor)
2156 {
2157 assert(outerTri != iT);
2158 t.neighbors[2] = outerTri;
2159 changeNeighbor(outerTri, c, a, iT);
2160 }
2161 else
2162 outerTris.at(outerEdge) = iT;
2163 }
2164 // Finalize triangle
2165 // note: only when triangle is finalized to we add it as a neighbor to
2166 // parent to maintain triangulation topology consistency
2167 triangles[iParent].neighbors[iInParent] = iT;
2168 t.neighbors[0] = iParent;
2169 t.vertices = arr3(a, b, c);
2170 setAdjacentTriangle(c, iT);
2171}
2172
2173template <typename T, typename TNearPointLocator>
2174IndexSizeType Triangulation<T, TNearPointLocator>::findDelaunayPoint(
2175 const std::vector<VertInd>& poly,
2176 const IndexSizeType iA,
2177 const IndexSizeType iB) const
2178{
2179 assert(iB - iA > 1);
2180 const V2d<T>& a = vertices[poly[iA]];
2181 const V2d<T>& b = vertices[poly[iB]];
2182 IndexSizeType out = iA + 1;
2183 const V2d<T>* c = &vertices[poly[out]]; // caching for better performance
2184 for(IndexSizeType i = iA + 1; i < iB; ++i)
2185 {
2186 const V2d<T>& v = vertices[poly[i]];
2187 if(isInCircumcircle(v, a, b, *c))
2188 {
2189 out = i;
2190 c = &v;
2191 }
2192 }
2193 assert(out > iA && out < iB); // point is between ends
2194 return out;
2195}
2196
2197template <typename T, typename TNearPointLocator>
2199 const std::vector<V2d<T> >& newVertices)
2200{
2201 return insertVertices(
2202 newVertices.begin(), newVertices.end(), getX_V2d<T>, getY_V2d<T>);
2203}
2204
2205template <typename T, typename TNearPointLocator>
2207{
2208 return m_vertTris.empty() && !vertices.empty();
2209}
2210
2211template <typename T, typename TNearPointLocator>
2212unordered_map<TriInd, LayerDepth>
2213Triangulation<T, TNearPointLocator>::peelLayer(
2214 std::stack<TriInd> seeds,
2215 const LayerDepth layerDepth,
2216 std::vector<LayerDepth>& triDepths) const
2217{
2218 unordered_map<TriInd, LayerDepth> behindBoundary;
2219 while(!seeds.empty())
2220 {
2221 const TriInd iT = seeds.top();
2222 seeds.pop();
2223 triDepths[iT] = std::min(triDepths[iT], layerDepth);
2224 behindBoundary.erase(iT);
2225 const Triangle& t = triangles[iT];
2226 for(Index i(0); i < Index(3); ++i)
2227 {
2228 const Edge opEdge(t.vertices[ccw(i)], t.vertices[cw(i)]);
2229 const TriInd iN = t.neighbors[opoNbr(i)];
2230 if(iN == noNeighbor || triDepths[iN] <= layerDepth)
2231 continue;
2232 if(fixedEdges.count(opEdge))
2233 {
2234 const unordered_map<Edge, LayerDepth>::const_iterator cit =
2235 overlapCount.find(opEdge);
2236 const LayerDepth triDepth = cit == overlapCount.end()
2237 ? layerDepth + 1
2238 : layerDepth + cit->second + 1;
2239 behindBoundary[iN] = triDepth;
2240 continue;
2241 }
2242 seeds.push(iN);
2243 }
2244 }
2245 return behindBoundary;
2246}
2247
2248template <typename T, typename TNearPointLocator>
2249std::vector<LayerDepth>
2251{
2252 std::vector<LayerDepth> triDepths(
2253 triangles.size(), std::numeric_limits<LayerDepth>::max());
2254 std::stack<TriInd> seeds(TriDeque(1, m_vertTris[0]));
2255 LayerDepth layerDepth = 0;
2256 LayerDepth deepestSeedDepth = 0;
2257
2258 unordered_map<LayerDepth, TriIndUSet> seedsByDepth;
2259 do
2260 {
2261 const unordered_map<TriInd, LayerDepth>& newSeeds =
2262 peelLayer(seeds, layerDepth, triDepths);
2263
2264 seedsByDepth.erase(layerDepth);
2265 typedef unordered_map<TriInd, LayerDepth>::const_iterator Iter;
2266 for(Iter it = newSeeds.begin(); it != newSeeds.end(); ++it)
2267 {
2268 deepestSeedDepth = std::max(deepestSeedDepth, it->second);
2269 seedsByDepth[it->second].insert(it->first);
2270 }
2271 const TriIndUSet& nextLayerSeeds = seedsByDepth[layerDepth + 1];
2272 seeds = std::stack<TriInd>(
2273 TriDeque(nextLayerSeeds.begin(), nextLayerSeeds.end()));
2274 ++layerDepth;
2275 } while(!seeds.empty() || deepestSeedDepth > layerDepth);
2276
2277 return triDepths;
2278}
2279
2280#ifdef CDT_ENABLE_CALLBACK_HANDLER
2281template <typename T, typename TNearPointLocator>
2283 ICallbackHandler* callbackHandler)
2284{
2285 m_callbackHandler = callbackHandler;
2286}
2287#endif
2288
2289template <typename T, typename TNearPointLocator>
2290void Triangulation<T, TNearPointLocator>::insertVertices_AsProvided(
2291 VertInd superGeomVertCount)
2292{
2293 for(VertInd iV = superGeomVertCount; iV < vertices.size(); ++iV)
2294 {
2295#ifdef CDT_ENABLE_CALLBACK_HANDLER
2296 if(m_callbackHandler && m_callbackHandler->isAbortCalculation())
2297 {
2298 return;
2299 }
2300#endif
2301 insertVertex(iV);
2302 }
2303}
2304
2305template <typename T, typename TNearPointLocator>
2306void Triangulation<T, TNearPointLocator>::insertVertices_Randomized(
2307 VertInd superGeomVertCount)
2308{
2309 std::size_t vertexCount = vertices.size() - superGeomVertCount;
2310 std::vector<VertInd> ii(vertexCount);
2311 detail::iota(ii.begin(), ii.end(), superGeomVertCount);
2312 detail::random_shuffle(ii.begin(), ii.end());
2313 for(std::vector<VertInd>::iterator it = ii.begin(); it != ii.end(); ++it)
2314 {
2315#ifdef CDT_ENABLE_CALLBACK_HANDLER
2316 if(m_callbackHandler && m_callbackHandler->isAbortCalculation())
2317 {
2318 return;
2319 }
2320#endif
2321 insertVertex(*it);
2322 }
2323}
2324
2325namespace detail
2326{
2327
2328// log2 implementation backwards compatible with pre c++11
2329template <typename T>
2330inline double log2_bc(T x)
2331{
2332#ifdef CDT_CXX11_IS_SUPPORTED
2333 return std::log2(x);
2334#else
2335 static double log2_constant = std::log(2.0);
2336 return std::log(static_cast<double>(x)) / log2_constant;
2337#endif
2338}
2339
2344inline std::size_t maxQueueLengthBFSKDTree(const std::size_t vertexCount)
2345{
2346 const int filledLayerPow2 =
2347 static_cast<int>(std::floor(log2_bc(vertexCount)) - 1);
2348 const std::size_t nodesInFilledTree =
2349 static_cast<std::size_t>(std::pow(2., filledLayerPow2 + 1) - 1);
2350 const std::size_t nodesInLastFilledLayer =
2351 static_cast<std::size_t>(std::pow(2., filledLayerPow2));
2352 const std::size_t nodesInLastLayer = vertexCount - nodesInFilledTree;
2353 return nodesInLastLayer >= nodesInLastFilledLayer
2354 ? nodesInLastFilledLayer + nodesInLastLayer -
2355 nodesInLastFilledLayer
2356 : nodesInLastFilledLayer;
2357}
2358
2359template <typename T>
2360class FixedCapacityQueue
2361{
2362public:
2363 FixedCapacityQueue(const std::size_t capacity)
2364 : m_vec(capacity)
2365 , m_front(m_vec.begin())
2366 , m_back(m_vec.begin())
2367 , m_size(0)
2368 {}
2369 bool empty() const
2370 {
2371 return m_size == 0;
2372 }
2373 const T& front() const
2374 {
2375 return *m_front;
2376 }
2377 void pop()
2378 {
2379 assert(m_size > 0);
2380 ++m_front;
2381 if(m_front == m_vec.end())
2382 m_front = m_vec.begin();
2383 --m_size;
2384 }
2385 void push(const T& t)
2386 {
2387 assert(m_size < m_vec.size());
2388 *m_back = t;
2389 ++m_back;
2390 if(m_back == m_vec.end())
2391 m_back = m_vec.begin();
2392 ++m_size;
2393 }
2394#ifdef CDT_CXX11_IS_SUPPORTED
2395 void push(const T&& t)
2396 {
2397 assert(m_size < m_vec.size());
2398 *m_back = t;
2399 ++m_back;
2400 if(m_back == m_vec.end())
2401 m_back = m_vec.begin();
2402 ++m_size;
2403 }
2404#endif
2405private:
2406 std::vector<T> m_vec;
2407 typename std::vector<T>::iterator m_front;
2408 typename std::vector<T>::iterator m_back;
2409 std::size_t m_size;
2410};
2411
2412template <typename T>
2413class less_than_x
2414{
2415 const std::vector<V2d<T> >& m_vertices;
2416
2417public:
2418 less_than_x(const std::vector<V2d<T> >& vertices)
2419 : m_vertices(vertices)
2420 {}
2421 bool operator()(const VertInd a, const VertInd b) const
2422 {
2423 return m_vertices[a].x < m_vertices[b].x;
2424 }
2425};
2426
2427template <typename T>
2428class less_than_y
2429{
2430 const std::vector<V2d<T> >& m_vertices;
2431
2432public:
2433 less_than_y(const std::vector<V2d<T> >& vertices)
2434 : m_vertices(vertices)
2435 {}
2436 bool operator()(const VertInd a, const VertInd b) const
2437 {
2438 return m_vertices[a].y < m_vertices[b].y;
2439 }
2440};
2441
2442} // namespace detail
2443
2444template <typename T, typename TNearPointLocator>
2445void Triangulation<T, TNearPointLocator>::insertVertices_KDTreeBFS(
2446 VertInd superGeomVertCount,
2447 Box2d<T> box)
2448{
2449 // calculate original indices
2450 const VertInd vertexCount(verticesCount() - superGeomVertCount);
2451 if(vertexCount <= VertInd(0))
2452 return;
2453 std::vector<VertInd> ii(vertexCount);
2454 detail::iota(ii.begin(), ii.end(), superGeomVertCount);
2455
2456 typedef std::vector<VertInd>::iterator It;
2458 detail::maxQueueLengthBFSKDTree(vertexCount));
2459 queue.push(make_tuple(ii.begin(), ii.end(), box.min, box.max, VertInd(0)));
2460
2461 It first, last;
2462 V2d<T> newBoxMin, newBoxMax;
2463 VertInd parent, mid;
2464
2465 const detail::less_than_x<T> cmpX(vertices);
2466 const detail::less_than_y<T> cmpY(vertices);
2467
2468 while(!queue.empty())
2469 {
2470#ifdef CDT_ENABLE_CALLBACK_HANDLER
2471 if(m_callbackHandler && m_callbackHandler->isAbortCalculation())
2472 {
2473 return;
2474 }
2475#endif
2476 tie(first, last, box.min, box.max, parent) = queue.front();
2477 queue.pop();
2478 assert(first != last);
2479
2480 const std::ptrdiff_t len = std::distance(first, last);
2481 if(len == 1)
2482 {
2483 insertVertex(*first, parent);
2484 continue;
2485 }
2486 const It midIt = first + len / 2;
2487 if(box.max.x - box.min.x >= box.max.y - box.min.y)
2488 {
2489 detail::portable_nth_element(first, midIt, last, cmpX);
2490 mid = *midIt;
2491 const T split = vertices[mid].x;
2492 newBoxMin.x = split;
2493 newBoxMin.y = box.min.y;
2494 newBoxMax.x = split;
2495 newBoxMax.y = box.max.y;
2496 }
2497 else
2498 {
2499 detail::portable_nth_element(first, midIt, last, cmpY);
2500 mid = *midIt;
2501 const T split = vertices[mid].y;
2502 newBoxMin.x = box.min.x;
2503 newBoxMin.y = split;
2504 newBoxMax.x = box.max.x;
2505 newBoxMax.y = split;
2506 }
2507 insertVertex(mid, parent);
2508 if(first != midIt)
2509 {
2510 queue.push(make_tuple(first, midIt, box.min, newBoxMax, mid));
2511 }
2512 if(midIt + 1 != last)
2513 {
2514 queue.push(make_tuple(midIt + 1, last, newBoxMin, box.max, mid));
2515 }
2516 }
2517}
2518
2519template <typename T, typename TNearPointLocator>
2520std::pair<TriInd, TriInd> Triangulation<T, TNearPointLocator>::edgeTriangles(
2521 const VertInd a,
2522 const VertInd b) const
2523{
2524 const TriInd triStart = m_vertTris[a];
2525 assert(triStart != noNeighbor);
2526 TriInd iT = triStart, iTNext = triStart;
2527 VertInd iV = noVertex;
2528 do
2529 {
2530 const Triangle& t = triangles[iT];
2531 tie(iTNext, iV) = t.next(a);
2532 assert(iTNext != noNeighbor);
2533 if(iV == b)
2534 {
2535 return std::make_pair(iT, iTNext);
2536 }
2537 iT = iTNext;
2538 } while(iT != triStart);
2539 return std::make_pair(noNeighbor, noNeighbor);
2540}
2541
2542template <typename T, typename TNearPointLocator>
2543bool Triangulation<T, TNearPointLocator>::hasEdge(
2544 const VertInd a,
2545 const VertInd b) const
2546{
2547 return edgeTriangles(a, b).first != invalidIndexSizeType;
2548}
2549
2550template <typename T, typename TNearPointLocator>
2551VertInd Triangulation<T, TNearPointLocator>::opposedVertexOnEdge(
2552 const Edge& edge,
2553 const T tolerance) const
2554{
2555 if(fixedEdges.count(edge))
2556 return noVertex;
2557 const V2d<T>& a = vertices[edge.v1()];
2558 const V2d<T>& b = vertices[edge.v2()];
2559 const std::pair<TriInd, TriInd> tris = edgeTriangles(edge.v1(), edge.v2());
2560 const VertInd opposed[] = {
2561 opposedVertex(triangles[tris.first], tris.second),
2562 opposedVertex(triangles[tris.second], tris.first)};
2563 for(int i = 0; i < 2; ++i)
2564 {
2565 const V2d<T>& v = vertices[opposed[i]];
2566 // near line ab and inside its diametral circle: both parts are
2567 // shorter than ab, so splitting terminates
2568 if(locatePointLine(v, a, b, tolerance) == PtLineLocation::OnLine &&
2569 detail::isEncroachingOnEdge(v, a, b))
2570 return opposed[i];
2571 }
2572 return noVertex;
2573}
2574
2578template <typename T, typename TNearPointLocator>
2579bool Triangulation<T, TNearPointLocator>::hasAnotherFixedEdgeAtSmallAngle(
2580 const VertInd v,
2581 const Edge& excludeEdge) const
2582{
2583 // splits stay on the original segment: its direction from v is unchanged
2584 const VertInd iVOther =
2585 excludeEdge.v1() == v ? excludeEdge.v2() : excludeEdge.v1();
2586 const TriInd triStart = m_vertTris[v];
2587 assert(triStart != noNeighbor);
2588 TriInd iT = triStart;
2589 do
2590 {
2591 const Triangle& t = triangles[iT];
2592 TriInd iTNext;
2593 VertInd iV;
2594 tie(iTNext, iV) = t.next(v);
2595 const Edge candidate(v, iV);
2596 if(candidate != excludeEdge && fixedEdges.count(candidate) &&
2597 detail::isAngleAtApexSmall(
2598 vertices[v], vertices[iVOther], vertices[iV]))
2599 {
2600 return true;
2601 }
2602 iT = iTNext;
2603 } while(iT != triStart);
2604 return false;
2605}
2606
2607template <typename T, typename TNearPointLocator>
2608void Triangulation<T, TNearPointLocator>::setAdjacentTriangle(
2609 const VertInd v,
2610 const TriInd t)
2611{
2612 assert(t != noNeighbor);
2613 m_vertTris[v] = t;
2614 assert(
2615 triangles[t].vertices[0] == v || triangles[t].vertices[1] == v ||
2616 triangles[t].vertices[2] == v);
2617}
2618
2619template <typename T, typename TNearPointLocator>
2620void Triangulation<T, TNearPointLocator>::pivotVertexTriangleCW(const VertInd v)
2621{
2622 assert(m_vertTris[v] != noNeighbor);
2623 m_vertTris[v] = triangles[m_vertTris[v]].next(v).first;
2624 assert(m_vertTris[v] != noNeighbor);
2625 assert(
2626 triangles[m_vertTris[v]].vertices[0] == v ||
2627 triangles[m_vertTris[v]].vertices[1] == v ||
2628 triangles[m_vertTris[v]].vertices[2] == v);
2629}
2630
2631template <typename T, typename TNearPointLocator>
2632void Triangulation<T, TNearPointLocator>::tryAddVertexToLocator(const VertInd v)
2633{
2634 if(!m_nearPtLocator.empty()) // only if locator is initialized already
2635 m_nearPtLocator.addPoint(v, vertices);
2636}
2637
2638template <typename T, typename TNearPointLocator>
2639void Triangulation<T, TNearPointLocator>::tryInitNearestPointLocator()
2640{
2641 if(!vertices.empty() && m_nearPtLocator.empty())
2642 {
2643 m_nearPtLocator.initialize(vertices);
2644 }
2645}
2646
2647template <typename T, typename TNearPointLocator>
2649 const VertInd maxVerticesToInsert,
2650 const RefinementCriterion::Enum refinementCriterion,
2651 const T refinementThreshold,
2652 TriIndUSet* const toEraseOrNull,
2653 const T minEdgeLength)
2654{
2655 if(isFinalized())
2656 handleException(FinalizedError(CDT_SOURCE_LOCATION));
2657
2658 tryInitNearestPointLocator();
2659
2660 Unrefined unrefined;
2661 VertInd remainingVertexBudget = maxVerticesToInsert;
2662 const VertInd steinerVerticesOffset = VertInd(vertices.size());
2663
2664 // split all the encroached constrained (fixed) edges
2665 resolveEncroachedEdges(
2667 remainingVertexBudget,
2668 steinerVerticesOffset,
2669 NULL, // no circumcenter yet: only edge-vs-edge encroachment matters
2670 refinementCriterion,
2671 refinementThreshold,
2672 toEraseOrNull,
2673 minEdgeLength,
2674 unrefined);
2675
2676 // refine triangulation by inserting bad-quality triangles' circumcenters
2677 // keep vertices to detect triangles modified after enqueueing
2678 std::queue<std::pair<TriInd, VerticesArr3> > badTriangles;
2679 for(TriInd iT(0), n(triangles.size()); iT < n; ++iT)
2680 {
2681 const Triangle& t = triangles[iT];
2682 if(!touchesSuperTriangle(t) &&
2683 !(toEraseOrNull && toEraseOrNull->count(iT)) &&
2684 isRefinementNeeded(t, refinementCriterion, refinementThreshold))
2685 {
2686 badTriangles.push(std::make_pair(iT, t.vertices));
2687 }
2688 }
2689
2690 while(!badTriangles.empty() && remainingVertexBudget > 0)
2691 {
2692 const TriInd iT = badTriangles.front().first;
2693 const VerticesArr3 vv = badTriangles.front().second;
2694 const Triangle& t = triangles[iT];
2695 badTriangles.pop();
2696 if(toEraseOrNull && toEraseOrNull->count(iT))
2697 continue;
2698 if(vv != t.vertices)
2699 continue; // skip triangle that got modified since put in queue
2700 if(!isRefinementNeeded(t, refinementCriterion, refinementThreshold) ||
2701 (refinementCriterion == RefinementCriterion::SmallestAngle &&
2702 isSmallestAngleFixed(t)))
2703 {
2704 // fixed-edge sharp angle is impossible to refine
2705 continue;
2706 }
2707 // copy: resolveEncroachedEdges below can re-allocate 'triangles'
2708 const V2d<T>& v0 = vertices[vv[0]];
2709 const V2d<T>& v1 = vertices[vv[1]];
2710 const V2d<T>& v2 = vertices[vv[2]];
2711 const T shortestEdge = std::min(
2712 distance(v0, v1), std::min(distance(v1, v2), distance(v2, v0)));
2713 if(shortestEdge <= minEdgeLength)
2714 {
2715 // same minEdgeLength give-up
2716 ++unrefined.shortEdgeTriangles;
2717 continue;
2718 }
2719 const V2d<T> circumcenterPos = circumcenter(v0, v1, v2);
2720 const OptionalTriInd triAtCircumcenter = walkTriangles(
2721 m_nearPtLocator.nearPoint(circumcenterPos, vertices),
2722 circumcenterPos);
2723 if(!triAtCircumcenter.hasValue())
2724 {
2725 // circumcenter falls outside triangulated area
2726 ++unrefined.circumcenterOutside;
2727 continue;
2728 }
2729
2730 const VertInd budgetBeforeSplits = remainingVertexBudget;
2731 const EdgeVec encroachedEdges =
2732 edgesEncroachedBy(circumcenterPos, triAtCircumcenter.value());
2733 const TriIndVec badTris = resolveEncroachedEdges(
2734 detail::toQueue(encroachedEdges),
2735 remainingVertexBudget,
2736 steinerVerticesOffset,
2737 &circumcenterPos,
2738 refinementCriterion,
2739 refinementThreshold,
2740 toEraseOrNull,
2741 minEdgeLength,
2742 unrefined);
2743 if(!remainingVertexBudget)
2744 break;
2745
2746 // a circumcenter encroaching on fixed edges:
2747 // split edges instead and re-visit the triangle later
2748 if(remainingVertexBudget != budgetBeforeSplits || !badTris.empty())
2749 {
2750 typedef TriIndVec::const_iterator It;
2751 for(It it = badTris.begin(); it != badTris.end(); ++it)
2752 {
2753 badTriangles.push(std::make_pair(*it, triangles[*it].vertices));
2754 }
2755 badTriangles.push(std::make_pair(iT, triangles[iT].vertices));
2756 continue;
2757 }
2758 // splitting was given up on: inserting would encroach anyway
2759 if(!encroachedEdges.empty())
2760 continue;
2761
2762 const TriInd iCircumcenterTri = triAtCircumcenter.value();
2763 const Triangle& circumcenterTri = triangles[iCircumcenterTri];
2765 circumcenterPos,
2766 vertices[circumcenterTri.vertices[0]],
2767 vertices[circumcenterTri.vertices[1]],
2768 vertices[circumcenterTri.vertices[2]]);
2769 if(loc == PtTriLocation::OnVertex)
2770 {
2771 // circumcenter coincides with an existing vertex
2772 ++unrefined.circumcenterOnVertex;
2773 continue;
2774 }
2775 const array<TriInd, 2> trisAt = {
2776 iCircumcenterTri,
2777 isOnEdge(loc) ? circumcenterTri.neighbors[edgeNeighbor(loc)]
2778 : noNeighbor};
2779 // Skip adding a Steiner point if the triangle will be removed anyway.
2780 if(toEraseOrNull &&
2781 (toEraseOrNull->count(trisAt[0]) || toEraseOrNull->count(trisAt[1])))
2782 {
2783 // circumcenter falls into a triangle that will be removed
2784 ++unrefined.circumcenterOutside;
2785 continue;
2786 }
2787
2788 --remainingVertexBudget;
2789 const VertInd iVert = static_cast<VertInd>(vertices.size());
2790 addNewVertex(circumcenterPos, noNeighbor);
2791#ifdef CDT_ENABLE_CALLBACK_HANDLER
2792 if(m_callbackHandler)
2793 {
2794 m_callbackHandler->onAddVertexStart(
2796 }
2797#endif
2798 std::stack<TriInd> triStack =
2799 trisAt[1] == noNeighbor
2800 ? insertVertexInsideTriangle(iVert, trisAt[0])
2801 : insertVertexOnEdge(iVert, trisAt[0], trisAt[1], true);
2802 ensureDelaunayByEdgeFlips(iVert, triStack);
2803 tryAddVertexToLocator(iVert);
2804
2805 TriInd start = m_vertTris[iVert];
2806 TriInd currTri = start;
2807 do
2808 {
2809 const Triangle& t = triangles[currTri];
2810 if(!(toEraseOrNull && toEraseOrNull->count(currTri)) &&
2812 isRefinementNeeded(t, refinementCriterion, refinementThreshold))
2813 {
2814 badTriangles.push(std::make_pair(currTri, t.vertices));
2815 }
2816 currTri = t.next(iVert).first;
2817 } while(currTri != start);
2818 }
2819
2820 // sharp corners that come from the input are impossible to refine: report
2821 // the triangles that are left too sharp because of them
2822 if(refinementCriterion == RefinementCriterion::SmallestAngle)
2823 {
2824 for(TriInd iT(0), n(triangles.size()); iT < n; ++iT)
2825 {
2826 const Triangle& t = triangles[iT];
2827 if(touchesSuperTriangle(t) ||
2828 (toEraseOrNull && toEraseOrNull->count(iT)))
2829 {
2830 continue;
2831 }
2832 if(isRefinementNeeded(
2833 t, refinementCriterion, refinementThreshold) &&
2834 isSmallestAngleFixed(t))
2835 {
2836 ++unrefined.sharpFixedCorner;
2837 }
2838 }
2839 }
2840 return unrefined;
2841}
2842
2843} // namespace CDT
2844
2845CDT_RESTORE_MATH_SETTINGS_FOR_CONSTRUCTIONS
2846
2847#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
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.