CDT  1.4.5
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
21namespace CDT
22{
23
24typedef std::deque<TriInd> TriDeque;
25
26namespace detail
27{
28
29namespace defaults
30{
31
32const std::size_t nTargetVerts = 0;
34const VertexInsertionOrder::Enum vertexInsertionOrder =
36const IntersectingConstraintEdges::Enum intersectingEdgesStrategy =
38const float minDistToConstraintEdge(0);
39
40} // namespace defaults
41
42} // namespace detail
43
44template <typename T, typename TNearPointLocator>
46 : m_nTargetVerts(detail::defaults::nTargetVerts)
47 , m_superGeomType(detail::defaults::superGeomType)
48 , m_vertexInsertionOrder(detail::defaults::vertexInsertionOrder)
49 , m_intersectingEdgesStrategy(detail::defaults::intersectingEdgesStrategy)
50 , m_minDistToConstraintEdge(detail::defaults::minDistToConstraintEdge)
51#ifdef CDT_ENABLE_CALLBACK_HANDLER
52 , m_callbackHandler(NULL)
53#endif
54{}
55
56template <typename T, typename TNearPointLocator>
58 const VertexInsertionOrder::Enum vertexInsertionOrder)
59 : m_nTargetVerts(detail::defaults::nTargetVerts)
60 , m_superGeomType(detail::defaults::superGeomType)
61 , m_vertexInsertionOrder(vertexInsertionOrder)
62 , m_intersectingEdgesStrategy(detail::defaults::intersectingEdgesStrategy)
63 , m_minDistToConstraintEdge(detail::defaults::minDistToConstraintEdge)
64#ifdef CDT_ENABLE_CALLBACK_HANDLER
65 , m_callbackHandler(NULL)
66#endif
67{}
68
69template <typename T, typename TNearPointLocator>
71 const VertexInsertionOrder::Enum vertexInsertionOrder,
72 const IntersectingConstraintEdges::Enum intersectingEdgesStrategy,
73 const T minDistToConstraintEdge)
74 : m_nTargetVerts(detail::defaults::nTargetVerts)
75 , m_superGeomType(detail::defaults::superGeomType)
76 , m_vertexInsertionOrder(vertexInsertionOrder)
77 , m_intersectingEdgesStrategy(intersectingEdgesStrategy)
78 , m_minDistToConstraintEdge(minDistToConstraintEdge)
79#ifdef CDT_ENABLE_CALLBACK_HANDLER
80 , m_callbackHandler(NULL)
81#endif
82{}
83
84template <typename T, typename TNearPointLocator>
86 const VertexInsertionOrder::Enum vertexInsertionOrder,
87 const TNearPointLocator& nearPtLocator,
88 const IntersectingConstraintEdges::Enum intersectingEdgesStrategy,
89 const T minDistToConstraintEdge)
90 : m_nearPtLocator(nearPtLocator)
91 , m_nTargetVerts(detail::defaults::nTargetVerts)
92 , m_superGeomType(detail::defaults::superGeomType)
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{
104 if(m_superGeomType != SuperGeometryType::SuperTriangle)
105 return;
106 // find triangles adjacent to super-triangle's vertices
107 TriIndUSet toErase;
108 for(TriInd iT(0); iT < TriInd(triangles.size()); ++iT)
109 {
110 if(touchesSuperTriangle(triangles[iT]))
111 toErase.insert(iT);
112 }
113 finalizeTriangulation(toErase);
114}
115
116template <typename T, typename TNearPointLocator>
118{
119 assert(m_vertTris[0] != noNeighbor);
120 const std::stack<TriInd> seed(std::deque<TriInd>(1, m_vertTris[0]));
121 const TriIndUSet toErase = growToBoundary(seed);
122 finalizeTriangulation(toErase);
123}
124
125template <typename T, typename TNearPointLocator>
127{
128 const std::vector<LayerDepth> triDepths = calculateTriangleDepths();
129 TriIndUSet toErase;
130 toErase.reserve(triangles.size());
131 for(std::size_t iT = 0; iT != triangles.size(); ++iT)
132 {
133 if(triDepths[iT] % 2 == 0)
134 toErase.insert(static_cast<TriInd>(iT));
135 }
136 finalizeTriangulation(toErase);
137}
138
141{
142 return Edge(VertInd(e.v1() - 3), VertInd(e.v2() - 3));
143}
144
145template <typename T, typename TNearPointLocator>
147 const TriIndUSet& removedTriangles)
148{
149 if(removedTriangles.empty())
150 return;
151 // remove triangles and calculate triangle index mapping
152 TriIndUMap triIndMap;
153 for(TriInd iT(0), iTnew(0); iT < TriInd(triangles.size()); ++iT)
154 {
155 if(removedTriangles.count(iT))
156 continue;
157 triIndMap[iT] = iTnew;
158 triangles[iTnew] = triangles[iT];
159 iTnew++;
160 }
161 triangles.erase(triangles.end() - removedTriangles.size(), triangles.end());
162 // adjust triangles' neighbors
163 for(TriInd iT(0); iT < triangles.size(); ++iT)
164 {
165 Triangle& t = triangles[iT];
166 // update neighbors to account for removed triangles
167 NeighborsArr3& nn = t.neighbors;
168 for(NeighborsArr3::iterator n = nn.begin(); n != nn.end(); ++n)
169 {
170 if(removedTriangles.count(*n))
171 {
172 *n = noNeighbor;
173 }
174 else if(*n != noNeighbor)
175 {
176 *n = triIndMap[*n];
177 }
178 }
179 }
180}
181
182template <typename T, typename TNearPointLocator>
187
188template <typename T, typename TNearPointLocator>
190{
191 return m_vertTris;
192}
193
194template <typename T, typename TNearPointLocator>
195void Triangulation<T, TNearPointLocator>::finalizeTriangulation(
196 const TriIndUSet& removedTriangles)
197{
198 m_vertTris = TriIndVec();
199 // remove super-triangle
200 if(m_superGeomType == SuperGeometryType::SuperTriangle)
201 {
202 vertices.erase(
203 vertices.begin(), vertices.begin() + nSuperTriangleVertices);
204 // Edge re-mapping
205 { // fixed edges
206 EdgeUSet updatedFixedEdges;
207 typedef CDT::EdgeUSet::const_iterator It;
208 for(It e = fixedEdges.begin(); e != fixedEdges.end(); ++e)
209 {
210 updatedFixedEdges.insert(RemapNoSuperTriangle(*e));
211 }
212 fixedEdges = updatedFixedEdges;
213 }
214 { // overlap count
215 unordered_map<Edge, BoundaryOverlapCount> updatedOverlapCount;
216 typedef unordered_map<Edge, BoundaryOverlapCount>::const_iterator
217 It;
218 for(It it = overlapCount.begin(); it != overlapCount.end(); ++it)
219 {
220 updatedOverlapCount.insert(
221 std::make_pair(
222 RemapNoSuperTriangle(it->first), it->second));
223 }
224 overlapCount = updatedOverlapCount;
225 }
226 { // split edges mapping
227 unordered_map<Edge, EdgeVec> updatedPieceToOriginals;
228 typedef unordered_map<Edge, EdgeVec>::const_iterator It;
229 for(It it = pieceToOriginals.begin(); it != pieceToOriginals.end();
230 ++it)
231 {
232 EdgeVec ee = it->second;
233 for(EdgeVec::iterator eeIt = ee.begin(); eeIt != ee.end();
234 ++eeIt)
235 {
236 *eeIt = RemapNoSuperTriangle(*eeIt);
237 }
238 updatedPieceToOriginals.insert(
239 std::make_pair(RemapNoSuperTriangle(it->first), ee));
240 }
241 pieceToOriginals = updatedPieceToOriginals;
242 }
243 }
244 // remove other triangles
245 removeTriangles(removedTriangles);
246 // adjust triangle vertices: account for removed super-triangle
247 if(m_superGeomType == SuperGeometryType::SuperTriangle)
248 {
249 for(TriangleVec::iterator t = triangles.begin(); t != triangles.end();
250 ++t)
251 {
252 VerticesArr3& vv = t->vertices;
253 for(VerticesArr3::iterator v = vv.begin(); v != vv.end(); ++v)
254 {
255 *v -= nSuperTriangleVertices;
256 }
257 }
258 }
259}
260
261template <typename T, typename TNearPointLocator>
263{
264 m_nearPtLocator.initialize(vertices);
265 m_nTargetVerts = static_cast<IndexSizeType>(vertices.size());
266 m_superGeomType = SuperGeometryType::Custom;
267}
268
269template <typename T, typename TNearPointLocator>
270TriIndUSet Triangulation<T, TNearPointLocator>::growToBoundary(
271 std::stack<TriInd> seeds) const
272{
273 TriIndUSet traversed;
274 while(!seeds.empty())
275 {
276 const TriInd iT = seeds.top();
277 seeds.pop();
278 traversed.insert(iT);
279 const Triangle& t = triangles[iT];
280 for(Index i(0); i < Index(3); ++i)
281 {
282 const Edge opEdge(t.vertices[ccw(i)], t.vertices[cw(i)]);
283 if(fixedEdges.count(opEdge))
284 continue;
285 const TriInd iN = t.neighbors[opoNbr(i)];
286 if(iN != noNeighbor && traversed.count(iN) == 0)
287 seeds.push(iN);
288 }
289 }
290 return traversed;
291}
292
293template <typename T, typename TNearPointLocator>
294TriInd Triangulation<T, TNearPointLocator>::addTriangle(const Triangle& t)
295{
296 const TriInd iT(triangles.size());
297 triangles.push_back(t);
298 return iT;
299}
300
301template <typename T, typename TNearPointLocator>
302TriInd Triangulation<T, TNearPointLocator>::addTriangle()
303{
304 return addTriangle(Triangle());
305}
306
307template <typename T, typename TNearPointLocator>
309 const std::vector<Edge>& edges)
310{
311 insertEdges(edges.begin(), edges.end(), edge_get_v1, edge_get_v2);
312}
313
314template <typename T, typename TNearPointLocator>
316 const std::vector<Edge>& edges)
317{
318 conformToEdges(edges.begin(), edges.end(), edge_get_v1, edge_get_v2);
319}
320
321template <typename T, typename TNearPointLocator>
322void Triangulation<T, TNearPointLocator>::fixEdge(const Edge& edge)
323{
324 if(!fixedEdges.insert(edge).second)
325 {
326 ++overlapCount[edge]; // if edge is already fixed increment the counter
327 }
328}
329
330namespace detail
331{
332
333// add element to 'to' if not already in 'to'
334template <typename T, typename Allocator1>
335void insert_unique(std::vector<T, Allocator1>& to, const T& elem)
336{
337 if(std::find(to.begin(), to.end(), elem) == to.end())
338 {
339 to.push_back(elem);
340 }
341}
342
343// add elements of 'from' that are not present in 'to' to 'to'
344template <typename T, typename Allocator1, typename Allocator2>
345void insert_unique(
346 std::vector<T, Allocator1>& to,
347 const std::vector<T, Allocator2>& from)
348{
349 typedef typename std::vector<T, Allocator2>::const_iterator Cit;
350 to.reserve(to.size() + from.size());
351 for(Cit cit = from.begin(); cit != from.end(); ++cit)
352 {
353 insert_unique(to, *cit);
354 }
355}
356
357} // namespace detail
358
359template <typename T, typename TNearPointLocator>
360void Triangulation<T, TNearPointLocator>::splitFixedEdge(
361 const Edge& edge,
362 const VertInd iSplitVert)
363{
364 // split constraint (fixed) edge that already exists in triangulation
365 const Edge half1(edge.v1(), iSplitVert);
366 const Edge half2(iSplitVert, edge.v2());
367 // remove the edge that and add its halves
368 fixedEdges.erase(edge);
369 fixEdge(half1);
370 fixEdge(half2);
371 // maintain overlaps
372 typedef unordered_map<Edge, BoundaryOverlapCount>::const_iterator It;
373 const It overlapIt = overlapCount.find(edge);
374 if(overlapIt != overlapCount.end())
375 {
376 overlapCount[half1] += overlapIt->second;
377 overlapCount[half2] += overlapIt->second;
378 overlapCount.erase(overlapIt);
379 }
380 // maintain piece-to-original mapping
381 EdgeVec newOriginals(1, edge);
382 const unordered_map<Edge, EdgeVec>::const_iterator originalsIt =
383 pieceToOriginals.find(edge);
384 if(originalsIt != pieceToOriginals.end())
385 { // edge being split was split before: pass-through originals
386 newOriginals = originalsIt->second;
387 pieceToOriginals.erase(originalsIt);
388 }
389 detail::insert_unique(pieceToOriginals[half1], newOriginals);
390 detail::insert_unique(pieceToOriginals[half2], newOriginals);
391}
392
393template <typename T, typename TNearPointLocator>
394VertInd Triangulation<T, TNearPointLocator>::addSplitEdgeVertex(
395 const V2d<T>& splitVert,
396 const TriInd iT,
397 const TriInd iTopo)
398{
399 // add a new point on the edge that splits an edge in two
400 const VertInd iSplitVert = static_cast<VertInd>(vertices.size());
401 addNewVertex(splitVert, noNeighbor);
402
403#ifdef CDT_ENABLE_CALLBACK_HANDLER
404 if(m_callbackHandler)
405 {
406 m_callbackHandler->onAddVertexStart(
408 }
409#endif
410
411 std::stack<TriInd> triStack = insertVertexOnEdge(iSplitVert, iT, iTopo);
412 tryAddVertexToLocator(iSplitVert);
413 ensureDelaunayByEdgeFlips(iSplitVert, triStack);
414 return iSplitVert;
415}
416
417template <typename T, typename TNearPointLocator>
418VertInd Triangulation<T, TNearPointLocator>::splitFixedEdgeAt(
419 const Edge& edge,
420 const V2d<T>& splitVert,
421 const TriInd iT,
422 const TriInd iTopo)
423{
424 const VertInd iSplitVert = addSplitEdgeVertex(splitVert, iT, iTopo);
425 splitFixedEdge(edge, iSplitVert);
426 return iSplitVert;
427}
428
429template <typename T, typename TNearPointLocator>
430bool Triangulation<T, TNearPointLocator>::isEdgeSplitVertexValid(
431 const V2d<T>& splitVert,
432 const TriInd iT,
433 const TriInd iTopo,
434 const VertInd iVL,
435 const VertInd iVR) const
436{
437 // Orient the split edge as it appears (counter-clockwise) in iT. Locating
438 // the (floating-point-rounded) split vertex against it both tells whether
439 // the split is safe and which of the two triangles sharing the edge must
440 // contain the vertex: 'Left' is iT's interior side, 'Right' is iTopo's.
441 const Triangle& tL = triangles[iT];
442 const Index sL = edgeNeighborInd(tL.vertices, iVL, iVR);
444 splitVert, vertices[tL.vertices[sL]], vertices[tL.vertices[ccw(sL)]]);
445 if(side == PtLineLocation::OnLine)
446 {
447 return splitVert != vertices[iVL] && splitVert != vertices[iVR];
448 }
449 const Triangle& t = side == PtLineLocation::Left ? tL : triangles[iTopo];
450
451 // The split vertex must not fall outside that triangle. Its relation to the
452 // split edge is already established by 'side', so only the two edges
453 // meeting at the apex (opposite the split edge) are tested. A point to the
454 // right of a counter-clockwise triangle's edge lies outside it.
455 const Index s = edgeNeighborInd(t.vertices, iVL, iVR);
456 const V2d<T>& from = vertices[t.vertices[s]]; // split edge tail (CCW)
457 const V2d<T>& to = vertices[t.vertices[ccw(s)]]; // split edge head (CCW)
458 const V2d<T>& apex = vertices[t.vertices[cw(s)]]; // opposite the split edge
459 return locatePointLine(splitVert, to, apex) != PtLineLocation::Right &&
460 locatePointLine(splitVert, apex, from) != PtLineLocation::Right;
461}
462
463template <typename T, typename TNearPointLocator>
464Edge Triangulation<T, TNearPointLocator>::originalInputEdge(const Edge& e) const
465{
466 const Edge orig =
467 pieceToOriginals.count(e) ? pieceToOriginals.at(e).front() : e;
468 return Edge(
469 VertInd(orig.v1() - m_nTargetVerts),
470 VertInd(orig.v2() - m_nTargetVerts));
471}
472
473template <typename T, typename TNearPointLocator>
474const Triangle&
475Triangulation<T, TNearPointLocator>::triangleAt(const TriInd iT) const
476{
477 if(iT >= triangles.size())
478 handleException(Error(
479 iT == noNeighbor
480 ? "Attempted reading no-neighbor sentinel value triangle"
481 : "Triangle index " + CDT::to_string(iT) + " out of range " +
482 CDT::to_string(triangles.size()),
483 CDT_SOURCE_LOCATION));
484 return triangles[iT];
485}
486
487template <typename T, typename TNearPointLocator>
488void Triangulation<T, TNearPointLocator>::fixEdge(
489 const Edge& edge,
490 const Edge& originalEdge)
491{
492 fixEdge(edge);
493 if(edge != originalEdge)
494 detail::insert_unique(pieceToOriginals[edge], originalEdge);
495}
496
497namespace detail
498{
499
500template <typename T>
501T lerp(const T& a, const T& b, const T t)
502{
503 return (T(1) - t) * a + t * b;
504}
505
506// Precondition: ab and cd intersect normally
507template <typename T>
508V2d<T> intersectionPosition(
509 const V2d<T>& a,
510 const V2d<T>& b,
511 const V2d<T>& c,
512 const V2d<T>& d)
513{
514 using namespace predicates::adaptive;
515
516 // note: for better accuracy we interpolate x and y separately
517 // on a segment with the shortest x/y-projection correspondingly
518 const T a_cd = orient2d(c.x, c.y, d.x, d.y, a.x, a.y);
519 const T b_cd = orient2d(c.x, c.y, d.x, d.y, b.x, b.y);
520 const T t_ab = a_cd / (a_cd - b_cd);
521
522 const T c_ab = orient2d(a.x, a.y, b.x, b.y, c.x, c.y);
523 const T d_ab = orient2d(a.x, a.y, b.x, b.y, d.x, d.y);
524 const T t_cd = c_ab / (c_ab - d_ab);
525
526 return V2d<T>(
527 std::fabs(a.x - b.x) < std::fabs(c.x - d.x) ? lerp(a.x, b.x, t_ab)
528 : lerp(c.x, d.x, t_cd),
529 std::fabs(a.y - b.y) < std::fabs(c.y - d.y) ? lerp(a.y, b.y, t_ab)
530 : lerp(c.y, d.y, t_cd));
531}
532
533} // namespace detail
534
535template <typename T, typename TNearPointLocator>
536void Triangulation<T, TNearPointLocator>::insertEdgeIteration(
537 const Edge edge,
538 const Edge originalEdge,
539 EdgeVec& remaining,
540 std::vector<TriangulatePseudoPolygonTask>& tppIterations)
541{
542 const VertInd iA = edge.v1();
543 VertInd iB = edge.v2();
544 if(iA == iB) // edge connects a vertex to itself
545 return;
546
547 if(hasEdge(iA, iB))
548 {
549 fixEdge(edge, originalEdge);
550 return;
551 }
552
553 const V2d<T>& a = vertices[iA];
554 const V2d<T>& b = vertices[iB];
555 const T distanceTolerance =
556 m_minDistToConstraintEdge == T(0)
557 ? T(0)
558 : m_minDistToConstraintEdge * distance(a, b);
559
560 TriInd iT;
561 // Note: 'L' is left and 'R' is right of the inserted constraint edge
562 VertInd iVL, iVR;
563 tie(iT, iVL, iVR) = intersectedTriangle(iA, a, b, distanceTolerance);
564 // if one of the triangle vertices is on the edge, move edge start
565 if(iT == noNeighbor)
566 {
567 const Edge edgePart(iA, iVL);
568 fixEdge(edgePart, originalEdge);
569 remaining.push_back(Edge(iVL, iB));
570 return;
571 }
572 Triangle t = triangles[iT];
573 std::vector<TriInd> intersected(1, iT);
574 std::vector<VertInd> polyL, polyR;
575 polyL.reserve(2);
576 polyL.push_back(iA);
577 polyL.push_back(iVL);
578 polyR.reserve(2);
579 polyR.push_back(iA);
580 polyR.push_back(iVR);
581 unordered_map<Edge, TriInd> outerTris;
582 outerTris[Edge(iA, iVL)] = edgeNeighbor(t, iA, iVL);
583 outerTris[Edge(iA, iVR)] = edgeNeighbor(t, iA, iVR);
584 VertInd iV = iA;
585
586 while(!t.containsVertex(iB))
587 {
588 const TriInd iTopo = opposedTriangle(t, iV);
589 const Triangle& tOpo = triangleAt(iTopo);
590 const VertInd iVopo = opposedVertex(tOpo, iT);
591
592 switch(m_intersectingEdgesStrategy)
593 {
595 if(fixedEdges.count(Edge(iVL, iVR)))
596 handleException(IntersectingConstraintsError(
597 originalInputEdge(originalEdge),
598 originalInputEdge(Edge(iVL, iVR)),
599 CDT_SOURCE_LOCATION));
600 break;
602 {
603 if(!fixedEdges.count(Edge(iVL, iVR)))
604 break;
605 // split edge at the intersection of two constraint edges
606 const V2d<T> newV = detail::intersectionPosition(
607 vertices[iA], vertices[iB], vertices[iVL], vertices[iVR]);
608 if(!isEdgeSplitVertexValid(newV, iT, iTopo, iVL, iVR))
609 handleException(InvalidEdgeSplitVertex(
610 originalInputEdge(originalEdge),
611 originalInputEdge(Edge(iVL, iVR)),
612 CDT_SOURCE_LOCATION));
613 const VertInd iNewVert =
614 splitFixedEdgeAt(Edge(iVL, iVR), newV, iT, iTopo);
615 // TODO: is it's possible to re-use pseudo-polygons
616 // for inserting [iA, iNewVert] edge half?
617 remaining.push_back(Edge(iA, iNewVert));
618 remaining.push_back(Edge(iNewVert, iB));
619 return;
620 }
622 assert(!fixedEdges.count(Edge(iVL, iVR)));
623 break;
624 }
625
626 const PtLineLocation::Enum loc =
627 locatePointLine(vertices[iVopo], a, b, distanceTolerance);
628 if(loc == PtLineLocation::Left)
629 {
630 const Edge e(polyL.back(), iVopo);
631 const TriInd outer = edgeNeighbor(tOpo, e.v1(), e.v2());
632 if(!outerTris.insert(std::make_pair(e, outer)).second)
633 outerTris.at(e) = noNeighbor; // hanging edge detected
634 polyL.push_back(iVopo);
635 iV = iVL;
636 iVL = iVopo;
637 }
638 else if(loc == PtLineLocation::Right)
639 {
640 const Edge e(polyR.back(), iVopo);
641 const TriInd outer = edgeNeighbor(tOpo, e.v1(), e.v2());
642 if(!outerTris.insert(std::make_pair(e, outer)).second)
643 outerTris.at(e) = noNeighbor; // hanging edge detected
644 polyR.push_back(iVopo);
645 iV = iVR;
646 iVR = iVopo;
647 }
648 else // encountered point on the edge
649 iB = iVopo;
650
651 intersected.push_back(iTopo);
652 iT = iTopo;
653 t = triangles[iT];
654 }
655 outerTris[Edge(polyL.back(), iB)] = edgeNeighbor(t, polyL.back(), iB);
656 outerTris[Edge(polyR.back(), iB)] = edgeNeighbor(t, polyR.back(), iB);
657 polyL.push_back(iB);
658 polyR.push_back(iB);
659
660 assert(!intersected.empty());
661 // make sure start/end vertices have a valid adjacent triangle
662 // that is not intersected by an edge
663 if(m_vertTris[iA] == intersected.front())
664 pivotVertexTriangleCW(iA);
665 if(m_vertTris[iB] == intersected.back())
666 pivotVertexTriangleCW(iB);
667
668 {
669#ifdef CDT_ENABLE_CALLBACK_HANDLER
670 if(m_callbackHandler)
671 {
672 m_callbackHandler->onReTriangulatePolygon(intersected);
673 }
674#endif
675
676 // Triangulate pseudo-polygons on both sides
677 std::reverse(polyR.begin(), polyR.end());
678
679 // note: intersected triangles are re-used for new triangles
680 // every triangulation of an n-gon has n − 2 triangles
681 // even if outer polygon has hanging edges it holds
682 assert(intersected.size() >= 2);
683 const TriInd iTL = intersected.back();
684 intersected.pop_back();
685 const TriInd iTR = intersected.back();
686 intersected.pop_back();
687
688 triangulatePseudoPolygon(
689 polyL, outerTris, iTL, iTR, intersected, tppIterations);
690 triangulatePseudoPolygon(
691 polyR, outerTris, iTR, iTL, intersected, tppIterations);
692 assert(intersected.empty());
693 }
694
695 if(iB != edge.v2()) // encountered point on the edge
696 {
697 // fix edge part
698 const Edge edgePart(iA, iB);
699 fixEdge(edgePart, originalEdge);
700 remaining.push_back(Edge(iB, edge.v2()));
701 return;
702 }
703 else
704 {
705 fixEdge(edge, originalEdge);
706 }
707}
708
709template <typename T, typename TNearPointLocator>
710void Triangulation<T, TNearPointLocator>::insertEdge(
711 Edge edge,
712 const Edge originalEdge,
713 EdgeVec& remaining,
714 std::vector<TriangulatePseudoPolygonTask>& tppIterations)
715{
716#ifdef CDT_ENABLE_CALLBACK_HANDLER
717 if(m_callbackHandler)
718 {
719 m_callbackHandler->onAddEdgeStart(edge);
720 }
721#endif
722
723 // use iteration over recursion to avoid stack overflows
724 remaining.clear();
725 remaining.push_back(edge);
726 while(!remaining.empty())
727 {
728 edge = remaining.back();
729 remaining.pop_back();
730 insertEdgeIteration(edge, originalEdge, remaining, tppIterations);
731 }
732}
733
734template <typename T, typename TNearPointLocator>
735void Triangulation<T, TNearPointLocator>::conformToEdgeIteration(
736 Edge edge,
737 const EdgeVec& originals,
738 BoundaryOverlapCount overlaps,
739 std::vector<ConformToEdgeTask>& remaining)
740{
741 const VertInd iA = edge.v1();
742 VertInd iB = edge.v2();
743 if(iA == iB) // edge connects a vertex to itself
744 return;
745
746 if(hasEdge(iA, iB))
747 {
748 fixEdge(edge);
749 if(overlaps > 0)
750 overlapCount[edge] = overlaps;
751 // avoid marking edge as a part of itself
752 if(!originals.empty() && edge != originals.front())
753 {
754 detail::insert_unique(pieceToOriginals[edge], originals);
755 }
756 return;
757 }
758
759 const V2d<T>& a = vertices[iA];
760 const V2d<T>& b = vertices[iB];
761 const T distanceTolerance =
762 m_minDistToConstraintEdge == T(0)
763 ? T(0)
764 : m_minDistToConstraintEdge * distance(a, b);
765 TriInd iT;
766 VertInd iVleft, iVright;
767 tie(iT, iVleft, iVright) = intersectedTriangle(iA, a, b, distanceTolerance);
768 // if one of the triangle vertices is on the edge, move edge start
769 if(iT == noNeighbor)
770 {
771 const Edge edgePart(iA, iVleft);
772 fixEdge(edgePart);
773 if(overlaps > 0)
774 overlapCount[edgePart] = overlaps;
775 detail::insert_unique(pieceToOriginals[edgePart], originals);
776#ifdef CDT_CXX11_IS_SUPPORTED
777 remaining.emplace_back(Edge(iVleft, iB), originals, overlaps);
778#else
779 remaining.push_back(make_tuple(Edge(iVleft, iB), originals, overlaps));
780#endif
781 return;
782 }
783
784 VertInd iV = iA;
785 Triangle t = triangles[iT];
786 while(std::find(t.vertices.begin(), t.vertices.end(), iB) ==
787 t.vertices.end())
788 {
789 const TriInd iTopo = opposedTriangle(t, iV);
790 const Triangle& tOpo = triangleAt(iTopo);
791 const VertInd iVopo = opposedVertex(tOpo, iT);
792 const V2d<T> vOpo = vertices[iVopo];
793
794 switch(m_intersectingEdgesStrategy)
795 {
797 if(fixedEdges.count(Edge(iVleft, iVright)))
798 handleException(IntersectingConstraintsError(
799 originalInputEdge(edge),
800 originalInputEdge(Edge(iVleft, iVright)),
801 CDT_SOURCE_LOCATION));
802 break;
804 {
805 if(!fixedEdges.count(Edge(iVleft, iVright)))
806 break;
807 // split edge at the intersection of two constraint edges
808 const V2d<T> newV = detail::intersectionPosition(
809 vertices[iA],
810 vertices[iB],
811 vertices[iVleft],
812 vertices[iVright]);
813 if(!isEdgeSplitVertexValid(newV, iT, iTopo, iVleft, iVright))
814 handleException(InvalidEdgeSplitVertex(
815 originalInputEdge(edge),
816 originalInputEdge(Edge(iVleft, iVright)),
817 CDT_SOURCE_LOCATION));
818 const VertInd iNewVert =
819 splitFixedEdgeAt(Edge(iVleft, iVright), newV, iT, iTopo);
820#ifdef CDT_CXX11_IS_SUPPORTED
821 remaining.emplace_back(Edge(iNewVert, iB), originals, overlaps);
822 remaining.emplace_back(Edge(iA, iNewVert), originals, overlaps);
823#else
824 remaining.push_back(
825 make_tuple(Edge(iNewVert, iB), originals, overlaps));
826 remaining.push_back(
827 make_tuple(Edge(iA, iNewVert), originals, overlaps));
828#endif
829 return;
830 }
832 assert(!fixedEdges.count(Edge(iVleft, iVright)));
833 break;
834 }
835
836 iT = iTopo;
837 t = triangles[iT];
838
839 const PtLineLocation::Enum loc =
840 locatePointLine(vOpo, a, b, distanceTolerance);
841 if(loc == PtLineLocation::Left)
842 {
843 iV = iVleft;
844 iVleft = iVopo;
845 }
846 else if(loc == PtLineLocation::Right)
847 {
848 iV = iVright;
849 iVright = iVopo;
850 }
851 else // encountered point on the edge
852 iB = iVopo;
853 }
854
855 // encountered one or more points on the edge: add remaining edge part
856 if(iB != edge.v2())
857 {
858#ifdef CDT_CXX11_IS_SUPPORTED
859 remaining.emplace_back(Edge(iB, edge.v2()), originals, overlaps);
860#else
861 remaining.push_back(
862 make_tuple(Edge(iB, edge.v2()), originals, overlaps));
863#endif
864 }
865
866 // add mid-point to triangulation
867 const VertInd iMid = static_cast<VertInd>(vertices.size());
868 const V2d<T>& start = vertices[iA];
869 const V2d<T>& end = vertices[iB];
870 addNewVertex(
871 V2d<T>((start.x + end.x) / T(2), (start.y + end.y) / T(2)), noNeighbor);
872
873#ifdef CDT_ENABLE_CALLBACK_HANDLER
874 if(m_callbackHandler)
875 {
876 m_callbackHandler->onAddVertexStart(
878 }
879#endif
880
881 const std::vector<Edge> flippedFixedEdges =
882 insertVertex_FlipFixedEdges(iMid);
883
884#ifdef CDT_CXX11_IS_SUPPORTED
885 remaining.emplace_back(Edge(iMid, iB), originals, overlaps);
886 remaining.emplace_back(Edge(iA, iMid), originals, overlaps);
887#else
888 remaining.push_back(make_tuple(Edge(iMid, iB), originals, overlaps));
889 remaining.push_back(make_tuple(Edge(iA, iMid), originals, overlaps));
890#endif
891
892 // re-introduce fixed edges that were flipped
893 // and make sure overlap count is preserved
894 for(std::vector<Edge>::const_iterator it = flippedFixedEdges.begin();
895 it != flippedFixedEdges.end();
896 ++it)
897 {
898 const Edge& flippedFixedEdge = *it;
899 fixedEdges.erase(flippedFixedEdge);
900
901 BoundaryOverlapCount prevOverlaps = 0;
902 const unordered_map<Edge, BoundaryOverlapCount>::const_iterator
903 overlapsIt = overlapCount.find(flippedFixedEdge);
904 if(overlapsIt != overlapCount.end())
905 {
906 prevOverlaps = overlapsIt->second;
907 overlapCount.erase(overlapsIt);
908 }
909 // override overlapping boundaries count when re-inserting an edge
910 EdgeVec prevOriginals(1, flippedFixedEdge);
911 const unordered_map<Edge, EdgeVec>::const_iterator originalsIt =
912 pieceToOriginals.find(flippedFixedEdge);
913 if(originalsIt != pieceToOriginals.end())
914 {
915 prevOriginals = originalsIt->second;
916 }
917#ifdef CDT_CXX11_IS_SUPPORTED
918 remaining.emplace_back(flippedFixedEdge, prevOriginals, prevOverlaps);
919#else
920 remaining.push_back(
921 make_tuple(flippedFixedEdge, prevOriginals, prevOverlaps));
922#endif
923 }
924}
925
926template <typename T, typename TNearPointLocator>
927void Triangulation<T, TNearPointLocator>::conformToEdge(
928 Edge edge,
929 EdgeVec originals,
930 BoundaryOverlapCount overlaps,
931 std::vector<ConformToEdgeTask>& remaining)
932{
933#ifdef CDT_ENABLE_CALLBACK_HANDLER
934 if(m_callbackHandler)
935 {
936 m_callbackHandler->onAddEdgeStart(edge);
937 }
938#endif
939
940 // use iteration over recursion to avoid stack overflows
941 remaining.clear();
942#ifdef CDT_CXX11_IS_SUPPORTED
943 remaining.emplace_back(edge, originals, overlaps);
944#else
945 remaining.push_back(make_tuple(edge, originals, overlaps));
946#endif
947 while(!remaining.empty())
948 {
949 tie(edge, originals, overlaps) = remaining.back();
950 remaining.pop_back();
951 conformToEdgeIteration(edge, originals, overlaps, remaining);
952 }
953}
954
965template <typename T, typename TNearPointLocator>
966tuple<TriInd, VertInd, VertInd>
967Triangulation<T, TNearPointLocator>::intersectedTriangle(
968 const VertInd iA,
969 const V2d<T>& a,
970 const V2d<T>& b,
971 const T orientationTolerance) const
972{
973 const TriInd startTri = m_vertTris[iA];
974 TriInd iT = startTri;
975 do
976 {
977 const Triangle t = triangles[iT];
978 const Index i = vertexInd(t.vertices, iA);
979 const VertInd iP2 = t.vertices[ccw(i)];
980 const T orientP2 = orient2D(vertices[iP2], a, b);
981 const PtLineLocation::Enum locP2 = classifyOrientation(orientP2);
982 if(locP2 == PtLineLocation::Right)
983 {
984 const VertInd iP1 = t.vertices[cw(i)];
985 const T orientP1 = orient2D(vertices[iP1], a, b);
986 const PtLineLocation::Enum locP1 = classifyOrientation(orientP1);
987 if(locP1 == PtLineLocation::OnLine)
988 {
989 return make_tuple(noNeighbor, iP1, iP1);
990 }
991 if(locP1 == PtLineLocation::Left)
992 {
993 if(orientationTolerance)
994 {
995 T closestOrient;
996 VertInd iClosestP;
997 if(std::abs(orientP1) <= std::abs(orientP2))
998 {
999 closestOrient = orientP1;
1000 iClosestP = iP1;
1001 }
1002 else
1003 {
1004 closestOrient = orientP2;
1005 iClosestP = iP2;
1006 }
1008 closestOrient, orientationTolerance) ==
1009 PtLineLocation::OnLine)
1010 {
1011 return make_tuple(noNeighbor, iClosestP, iClosestP);
1012 }
1013 }
1014 return make_tuple(iT, iP1, iP2);
1015 }
1016 }
1017 iT = t.next(iA).first;
1018 } while(iT != startTri);
1019
1020 handleException(Error(
1021 "Could not find vertex triangle intersected by an edge.",
1022 CDT_SOURCE_LOCATION));
1023 return make_tuple(noNeighbor, noVertex, noVertex);
1024}
1025
1026template <typename T, typename TNearPointLocator>
1027void Triangulation<T, TNearPointLocator>::addSuperTriangle(const Box2d<T>& box)
1028{
1029 m_nTargetVerts = nSuperTriangleVertices;
1030 m_superGeomType = SuperGeometryType::SuperTriangle;
1031
1032 const V2d<T> center(
1033 (box.min.x + box.max.x) / T(2), (box.min.y + box.max.y) / T(2));
1034 const T w = box.max.x - box.min.x;
1035 const T h = box.max.y - box.min.y;
1036 T r = std::max(w, h); // incircle radius upper bound
1037
1038 // Note: make sure radius is big enough. Constants chosen experimentally.
1039 // - for tiny bounding boxes: use 1.0 as the smallest radius
1040 // - multiply radius by 2.0 for extra safety margin
1041 r = std::max(T(2) * r, T(1));
1042
1043 // Note: for very large floating point numbers rounding can lead to wrong
1044 // super-triangle coordinates. This is a very rare corner-case so the
1045 // handling is very primitive.
1046 { // note: '<=' means '==' but avoids the warning
1047 while(center.y <= center.y - r)
1048 r *= T(2);
1049 }
1050
1051 const T R = T(2) * r; // excircle radius
1052 const T cos_30_deg = T(0.8660254037844386); // note: (std::sqrt(3.0) / 2.0)
1053 const T shiftX = R * cos_30_deg;
1054 const V2d<T> posV1(center.x - shiftX, center.y - r);
1055 const V2d<T> posV2(center.x + shiftX, center.y - r);
1056 const V2d<T> posV3(center.x, center.y + R);
1057 addNewVertex(posV1, TriInd(0));
1058 addNewVertex(posV2, TriInd(0));
1059 addNewVertex(posV3, TriInd(0));
1060
1061#ifdef CDT_ENABLE_CALLBACK_HANDLER
1062 if(m_callbackHandler)
1063 {
1064 m_callbackHandler->onAddSuperTriangle();
1065 }
1066#endif
1067
1068 addTriangle(
1069 Triangle(arr3(VertInd(0), VertInd(1), VertInd(2)), arr3(noNeighbor)));
1070
1071 if(m_vertexInsertionOrder != VertexInsertionOrder::Auto)
1072 {
1073 m_nearPtLocator.initialize(vertices);
1074 }
1075}
1076
1077template <typename T, typename TNearPointLocator>
1078void Triangulation<T, TNearPointLocator>::addNewVertex(
1079 const V2d<T>& pos,
1080 const TriInd iT)
1081{
1082 vertices.push_back(pos);
1083 m_vertTris.push_back(iT);
1084}
1085
1086template <typename T, typename TNearPointLocator>
1087std::vector<Edge>
1088Triangulation<T, TNearPointLocator>::insertVertex_FlipFixedEdges(
1089 const VertInd iV1)
1090{
1091 std::vector<Edge> flippedFixedEdges;
1092
1093 const V2d<T>& v1 = vertices[iV1];
1094 const VertInd startVertex = m_nearPtLocator.nearPoint(v1, vertices);
1095 array<TriInd, 2> trisAt = walkingSearchTrianglesAt(iV1, startVertex);
1096 std::stack<TriInd> triStack =
1097 trisAt[1] == noNeighbor ? insertVertexInsideTriangle(iV1, trisAt[0])
1098 : insertVertexOnEdge(iV1, trisAt[0], trisAt[1]);
1099
1100 TriInd iTopo, n1, n2, n3, n4;
1101 VertInd iV2, iV3, iV4;
1102 while(!triStack.empty())
1103 {
1104 const TriInd iT = triStack.top();
1105 triStack.pop();
1106
1107 edgeFlipInfo(iT, iV1, iTopo, iV2, iV3, iV4, n1, n2, n3, n4);
1108 if(iTopo != noNeighbor && isFlipNeeded(iV1, iV2, iV3, iV4))
1109 {
1110 // if flipped edge is fixed, remember it
1111 const Edge flippedEdge(iV2, iV4);
1112 if(!fixedEdges.empty() &&
1113 fixedEdges.find(flippedEdge) != fixedEdges.end())
1114 {
1115 flippedFixedEdges.push_back(flippedEdge);
1116 }
1117
1118 flipEdge(iT, iTopo, iV1, iV2, iV3, iV4, n1, n2, n3, n4);
1119 triStack.push(iT);
1120 triStack.push(iTopo);
1121 }
1122 }
1123
1124 tryAddVertexToLocator(iV1);
1125 return flippedFixedEdges;
1126}
1127
1128template <typename T, typename TNearPointLocator>
1129void Triangulation<T, TNearPointLocator>::insertVertex(
1130 const VertInd iVert,
1131 const VertInd walkStart)
1132{
1133#ifdef CDT_ENABLE_CALLBACK_HANDLER
1134 if(m_callbackHandler)
1135 {
1136 m_callbackHandler->onAddVertexStart(iVert, AddVertexType::UserInput);
1137 }
1138#endif
1139
1140 const array<TriInd, 2> trisAt = walkingSearchTrianglesAt(iVert, walkStart);
1141 std::stack<TriInd> triStack =
1142 trisAt[1] == noNeighbor
1143 ? insertVertexInsideTriangle(iVert, trisAt[0])
1144 : insertVertexOnEdge(iVert, trisAt[0], trisAt[1], true);
1145 ensureDelaunayByEdgeFlips(iVert, triStack);
1146}
1147
1148template <typename T, typename TNearPointLocator>
1149void Triangulation<T, TNearPointLocator>::insertVertex(const VertInd iVert)
1150{
1151 const V2d<T>& v = vertices[iVert];
1152 const VertInd walkStart = m_nearPtLocator.nearPoint(v, vertices);
1153 insertVertex(iVert, walkStart);
1154 tryAddVertexToLocator(iVert);
1155}
1156
1157template <typename T, typename TNearPointLocator>
1158void Triangulation<T, TNearPointLocator>::ensureDelaunayByEdgeFlips(
1159 const VertInd iV1,
1160 std::stack<TriInd>& triStack)
1161{
1162 TriInd iTopo, n1, n2, n3, n4;
1163 VertInd iV2, iV3, iV4;
1164 while(!triStack.empty())
1165 {
1166 const TriInd iT = triStack.top();
1167 triStack.pop();
1168
1169 edgeFlipInfo(iT, iV1, iTopo, iV2, iV3, iV4, n1, n2, n3, n4);
1170 if(iTopo != noNeighbor && isFlipNeeded(iV1, iV2, iV3, iV4))
1171 {
1172 flipEdge(iT, iTopo, iV1, iV2, iV3, iV4, n1, n2, n3, n4);
1173 triStack.push(iT);
1174 triStack.push(iTopo);
1175 }
1176 }
1177}
1178
1179/*
1180 * v4 original edge: (v1, v3)
1181 * /|\ flip-candidate edge: (v, v2)
1182 * / | \
1183 * n3 / | \ n4
1184 * / | \
1185 * new vertex--> v1 T | Topo v3
1186 * \ | /
1187 * n1 \ | / n2
1188 * \ | /
1189 * \|/
1190 * v2
1191 */
1192template <typename T, typename TNearPointLocator>
1193void Triangulation<T, TNearPointLocator>::edgeFlipInfo(
1194 const TriInd iT,
1195 const VertInd iV1,
1196 TriInd& iTopo,
1197 VertInd& iV2,
1198 VertInd& iV3,
1199 VertInd& iV4,
1200 TriInd& n1,
1201 TriInd& n2,
1202 TriInd& n3,
1203 TriInd& n4)
1204{
1205 /* v[2]
1206 / \
1207 n[2]/ \n[1]
1208 /_____\
1209 v[0] n[0] v[1] */
1210 const Triangle& t = triangles[iT];
1211 if(t.vertices[0] == iV1)
1212 {
1213 iV2 = t.vertices[1];
1214 iV4 = t.vertices[2];
1215 n1 = t.neighbors[0];
1216 n3 = t.neighbors[2];
1217 iTopo = t.neighbors[1];
1218 }
1219 else if(t.vertices[1] == iV1)
1220 {
1221 iV2 = t.vertices[2];
1222 iV4 = t.vertices[0];
1223 n1 = t.neighbors[1];
1224 n3 = t.neighbors[0];
1225 iTopo = t.neighbors[2];
1226 }
1227 else
1228 {
1229 iV2 = t.vertices[0];
1230 iV4 = t.vertices[1];
1231 n1 = t.neighbors[2];
1232 n3 = t.neighbors[1];
1233 iTopo = t.neighbors[0];
1234 }
1235 if(iTopo == noNeighbor)
1236 return;
1237 const Triangle& tOpo = triangles[iTopo];
1238 if(tOpo.neighbors[0] == iT)
1239 {
1240 iV3 = tOpo.vertices[2];
1241 n2 = tOpo.neighbors[1];
1242 n4 = tOpo.neighbors[2];
1243 }
1244 else if(tOpo.neighbors[1] == iT)
1245 {
1246 iV3 = tOpo.vertices[0];
1247 n2 = tOpo.neighbors[2];
1248 n4 = tOpo.neighbors[0];
1249 }
1250 else
1251 {
1252 iV3 = tOpo.vertices[1];
1253 n2 = tOpo.neighbors[0];
1254 n4 = tOpo.neighbors[1];
1255 }
1256}
1257
1268/*
1269 * v4 original edge: (v2, v4)
1270 * /|\ flip-candidate edge: (v1, v3)
1271 * / | \
1272 * / | \
1273 * / | \
1274 * new vertex--> v1 | v3
1275 * \ | /
1276 * \ | /
1277 * \ | /
1278 * \|/
1279 * v2
1280 */
1281template <typename T, typename TNearPointLocator>
1282bool Triangulation<T, TNearPointLocator>::isFlipNeeded(
1283 const VertInd iV1,
1284 const VertInd iV2,
1285 const VertInd iV3,
1286 const VertInd iV4) const
1287{
1288 if(fixedEdges.count(Edge(iV2, iV4)))
1289 return false; // flip not needed if the original edge is fixed
1290 const V2d<T>& v1 = vertices[iV1];
1291 const V2d<T>& v2 = vertices[iV2];
1292 const V2d<T>& v3 = vertices[iV3];
1293 const V2d<T>& v4 = vertices[iV4];
1294 if(m_superGeomType == SuperGeometryType::SuperTriangle)
1295 {
1296 // If flip-candidate edge touches super-triangle in-circumference
1297 // test has to be replaced with orient2d test against the line
1298 // formed by two non-artificial vertices (that don't belong to
1299 // super-triangle)
1300 if(iV1 < 3) // flip-candidate edge touches super-triangle
1301 {
1302 // does original edge also touch super-triangle?
1303 if(iV2 < 3)
1304 return locatePointLine(v2, v3, v4) ==
1305 locatePointLine(v1, v3, v4);
1306 if(iV4 < 3)
1307 return locatePointLine(v4, v2, v3) ==
1308 locatePointLine(v1, v2, v3);
1309 return false; // original edge does not touch super-triangle
1310 }
1311 if(iV3 < 3) // flip-candidate edge touches super-triangle
1312 {
1313 // does original edge also touch super-triangle?
1314 if(iV2 < 3)
1315 {
1316 return locatePointLine(v2, v1, v4) ==
1317 locatePointLine(v3, v1, v4);
1318 }
1319 if(iV4 < 3)
1320 {
1321 return locatePointLine(v4, v2, v1) ==
1322 locatePointLine(v3, v2, v1);
1323 }
1324 return false; // original edge does not touch super-triangle
1325 }
1326 // flip-candidate edge does not touch super-triangle
1327 if(iV2 < 3)
1328 return locatePointLine(v2, v3, v4) == locatePointLine(v1, v3, v4);
1329 if(iV4 < 3)
1330 return locatePointLine(v4, v2, v3) == locatePointLine(v1, v2, v3);
1331 }
1332 return isInCircumcircle(v1, v2, v3, v4);
1333}
1334
1335/* Flip edge between T and Topo:
1336 *
1337 * v4 | - old edge
1338 * /|\ ~ - new edge
1339 * / | \
1340 * n3 / T' \ n4
1341 * / | \
1342 * / | \
1343 * T -> v1~~~~~~~~~v3 <- Topo
1344 * \ | /
1345 * \ | /
1346 * n1 \Topo'/ n2
1347 * \ | /
1348 * \|/
1349 * v2
1350 */
1351template <typename T, typename TNearPointLocator>
1353 const TriInd iT,
1354 const TriInd iTopo)
1355{
1356#ifdef CDT_ENABLE_CALLBACK_HANDLER
1357 if(m_callbackHandler)
1358 {
1359 m_callbackHandler->onFlipEdge(iT, iTopo);
1360 }
1361#endif
1362
1363 Triangle& t = triangles[iT];
1364 Triangle& tOpo = triangles[iTopo];
1365 const array<TriInd, 3>& triNs = t.neighbors;
1366 const array<TriInd, 3>& triOpoNs = tOpo.neighbors;
1367 const array<VertInd, 3>& triVs = t.vertices;
1368 const array<VertInd, 3>& triOpoVs = tOpo.vertices;
1369 // find vertices and neighbors
1370 Index i = opposedVertexInd(t.neighbors, iTopo);
1371 const VertInd v1 = triVs[i];
1372 const VertInd v2 = triVs[ccw(i)];
1373 const TriInd n1 = triNs[i];
1374 const TriInd n3 = triNs[cw(i)];
1375 i = opposedVertexInd(tOpo.neighbors, iT);
1376 const VertInd v3 = triOpoVs[i];
1377 const VertInd v4 = triOpoVs[ccw(i)];
1378 const TriInd n4 = triOpoNs[i];
1379 const TriInd n2 = triOpoNs[cw(i)];
1380 // change vertices and neighbors
1381 t = Triangle(arr3(v4, v1, v3), arr3(n3, iTopo, n4));
1382 tOpo = Triangle(arr3(v2, v3, v1), arr3(n2, iT, n1));
1383 // adjust neighboring triangles and vertices
1384 changeNeighbor(n1, iT, iTopo);
1385 changeNeighbor(n4, iTopo, iT);
1386 // only adjust adjacent triangles if triangulation is not finalized:
1387 // can happen when called from outside on an already finalized
1388 // triangulation
1389 if(!isFinalized())
1390 {
1391 setAdjacentTriangle(v4, iT);
1392 setAdjacentTriangle(v2, iTopo);
1393 }
1394}
1395
1396/* Flip edge between T and Topo:
1397 *
1398 * v4 | - old edge
1399 * /|\ ~ - new edge
1400 * / | \
1401 * n3 / T' \ n4
1402 * / | \
1403 * / | \
1404 * T -> v1 ~~~~~~~~ v3 <- Topo
1405 * \ | /
1406 * \ | /
1407 * n1 \Topo'/ n2
1408 * \ | /
1409 * \|/
1410 * v2
1411 */
1412template <typename T, typename TNearPointLocator>
1414 const TriInd iT,
1415 const TriInd iTopo,
1416 const VertInd v1,
1417 const VertInd v2,
1418 const VertInd v3,
1419 const VertInd v4,
1420 const TriInd n1,
1421 const TriInd n2,
1422 const TriInd n3,
1423 const TriInd n4)
1424{
1425#ifdef CDT_ENABLE_CALLBACK_HANDLER
1426 if(m_callbackHandler)
1427 {
1428 m_callbackHandler->onFlipEdge(iT, iTopo);
1429 }
1430#endif
1431
1432 // change vertices and neighbors
1433 triangles[iT] = Triangle(arr3(v4, v1, v3), arr3(n3, iTopo, n4));
1434 triangles[iTopo] = Triangle(arr3(v2, v3, v1), arr3(n2, iT, n1));
1435 // adjust neighboring triangles and vertices
1436 changeNeighbor(n1, iT, iTopo);
1437 changeNeighbor(n4, iTopo, iT);
1438 // only adjust adjacent triangles if triangulation is not finalized:
1439 // can happen when called from outside on an already finalized
1440 // triangulation
1441 if(!isFinalized())
1442 {
1443 setAdjacentTriangle(v4, iT);
1444 setAdjacentTriangle(v2, iTopo);
1445 }
1446}
1447
1448/* Insert point into triangle: split into 3 triangles:
1449 * - create 2 new triangles
1450 * - re-use old triangle for the 3rd
1451 * v3
1452 * / | \
1453 * / | \ <-- original triangle (t)
1454 * / | \
1455 * n3 / | \ n2
1456 * /newT2|newT1\
1457 * / v \
1458 * / __/ \__ \
1459 * / __/ \__ \
1460 * / _/ t' \_ \
1461 * v1 ___________________ v2
1462 * n1
1463 */
1464template <typename T, typename TNearPointLocator>
1465std::stack<TriInd>
1466Triangulation<T, TNearPointLocator>::insertVertexInsideTriangle(
1467 VertInd v,
1468 TriInd iT)
1469{
1470 const TriInd iNewT1 = addTriangle();
1471 const TriInd iNewT2 = addTriangle();
1472
1473#ifdef CDT_ENABLE_CALLBACK_HANDLER
1474 if(m_callbackHandler)
1475 {
1476 m_callbackHandler->onInsertVertexInsideTriangle(iT, iNewT1, iNewT2);
1477 }
1478#endif
1479
1480 Triangle& t = triangles[iT];
1481 const array<VertInd, 3> vv = t.vertices;
1482 const array<TriInd, 3> nn = t.neighbors;
1483 const VertInd v1 = vv[0], v2 = vv[1], v3 = vv[2];
1484 const TriInd n1 = nn[0], n2 = nn[1], n3 = nn[2];
1485 // make two new triangles and convert current triangle to 3rd new
1486 // triangle
1487 triangles[iNewT1] = Triangle(arr3(v2, v3, v), arr3(n2, iNewT2, iT));
1488 triangles[iNewT2] = Triangle(arr3(v3, v1, v), arr3(n3, iT, iNewT1));
1489 t = Triangle(arr3(v1, v2, v), arr3(n1, iNewT1, iNewT2));
1490 // adjust adjacent triangles
1491 setAdjacentTriangle(v, iT);
1492 setAdjacentTriangle(v3, iNewT1);
1493 // change triangle neighbor's neighbors to new triangles
1494 changeNeighbor(n2, iT, iNewT1);
1495 changeNeighbor(n3, iT, iNewT2);
1496 // return newly added triangles
1497 std::stack<TriInd> newTriangles;
1498 newTriangles.push(iT);
1499 newTriangles.push(iNewT1);
1500 newTriangles.push(iNewT2);
1501 return newTriangles;
1502}
1503
1504/* Inserting a point on the edge between two triangles
1505 * T1 (top) v1
1506 * /|\
1507 * n1 / | \ n4
1508 * / | \
1509 * / T1' | Tnew1\
1510 * v2-------v-------v4
1511 * \ T2' | Tnew2/
1512 * \ | /
1513 * n2 \ | / n3
1514 * \|/
1515 * T2 (bottom) v3
1516 */
1517template <typename T, typename TNearPointLocator>
1518std::stack<TriInd> Triangulation<T, TNearPointLocator>::insertVertexOnEdge(
1519 VertInd v,
1520 TriInd iT1,
1521 TriInd iT2,
1522 const bool doHandleFixedSplitEdge)
1523{
1524 const TriInd iTnew1 = addTriangle();
1525 const TriInd iTnew2 = addTriangle();
1526
1527#ifdef CDT_ENABLE_CALLBACK_HANDLER
1528 if(m_callbackHandler)
1529 {
1530 m_callbackHandler->onInsertVertexOnEdge(iT1, iT2, iTnew1, iTnew2);
1531 }
1532#endif
1533
1534 Triangle& t1 = triangles[iT1];
1535 Triangle& t2 = triangles[iT2];
1536 Index i = opposedVertexInd(t1.neighbors, iT2);
1537 const VertInd v1 = t1.vertices[i];
1538 const VertInd v2 = t1.vertices[ccw(i)];
1539 const TriInd n1 = t1.neighbors[i];
1540 const TriInd n4 = t1.neighbors[cw(i)];
1541 i = opposedVertexInd(t2.neighbors, iT1);
1542 const VertInd v3 = t2.vertices[i];
1543 const VertInd v4 = t2.vertices[ccw(i)];
1544 const TriInd n3 = t2.neighbors[i];
1545 const TriInd n2 = t2.neighbors[cw(i)];
1546 // add new triangles and change existing ones
1547 t1 = Triangle(arr3(v, v1, v2), arr3(iTnew1, n1, iT2));
1548 t2 = Triangle(arr3(v, v2, v3), arr3(iT1, n2, iTnew2));
1549 triangles[iTnew1] = Triangle(arr3(v, v4, v1), arr3(iTnew2, n4, iT1));
1550 triangles[iTnew2] = Triangle(arr3(v, v3, v4), arr3(iT2, n3, iTnew1));
1551 // adjust adjacent triangles
1552 setAdjacentTriangle(v, iT1);
1553 setAdjacentTriangle(v4, iTnew1);
1554 // adjust neighboring triangles and vertices
1555 changeNeighbor(n4, iT1, iTnew1);
1556 changeNeighbor(n3, iT2, iTnew2);
1557 // properly handle the case when the split edge is a fixed edge
1558 if(doHandleFixedSplitEdge)
1559 {
1560 const Edge sharedEdge(v2, v4);
1561 if(fixedEdges.count(sharedEdge))
1562 splitFixedEdge(sharedEdge, v);
1563 }
1564 // return newly added triangles
1565 std::stack<TriInd> newTriangles;
1566 newTriangles.push(iT1);
1567 newTriangles.push(iTnew2);
1568 newTriangles.push(iT2);
1569 newTriangles.push(iTnew1);
1570 return newTriangles;
1571}
1572
1573template <typename T, typename TNearPointLocator>
1574array<TriInd, 2>
1575Triangulation<T, TNearPointLocator>::trianglesAt(const V2d<T>& pos) const
1576{
1577 array<TriInd, 2> out = {noNeighbor, noNeighbor};
1578 for(TriInd i = TriInd(0); i < TriInd(triangles.size()); ++i)
1579 {
1580 const Triangle& t = triangles[i];
1581 const V2d<T>& v1 = vertices[t.vertices[0]];
1582 const V2d<T>& v2 = vertices[t.vertices[1]];
1583 const V2d<T>& v3 = vertices[t.vertices[2]];
1584 const PtTriLocation::Enum loc = locatePointTriangle(pos, v1, v2, v3);
1585 if(loc == PtTriLocation::Outside)
1586 continue;
1587 out[0] = i;
1588 if(isOnEdge(loc))
1589 out[1] = t.neighbors[edgeNeighbor(loc)];
1590 return out;
1591 }
1592 handleException(
1593 Error("No triangle was found at position", CDT_SOURCE_LOCATION));
1594 return out;
1595}
1596
1597template <typename T, typename TNearPointLocator>
1598TriInd Triangulation<T, TNearPointLocator>::walkTriangles(
1599 const VertInd startVertex,
1600 const V2d<T>& pos) const
1601{
1602 // begin walk in search of triangle at pos
1603 TriInd currTri = m_vertTris[startVertex];
1604 bool found = false;
1605 detail::SplitMix64RandGen prng;
1606 while(!found)
1607 {
1608 const Triangle& t = triangles[currTri];
1609 found = true;
1610 // stochastic offset to randomize which edge we check first
1611 const Index offset(prng() % 3);
1612 for(Index i_(0); i_ < Index(3); ++i_)
1613 {
1614 const Index i((i_ + offset) % 3);
1615 const V2d<T>& vStart = vertices[t.vertices[i]];
1616 const V2d<T>& vEnd = vertices[t.vertices[ccw(i)]];
1617 const PtLineLocation::Enum edgeCheck =
1618 locatePointLine(pos, vStart, vEnd);
1619 const TriInd iN = t.neighbors[i];
1620 if(edgeCheck == PtLineLocation::Right && iN != noNeighbor)
1621 {
1622 found = false;
1623 currTri = iN;
1624 break;
1625 }
1626 }
1627 }
1628 return currTri;
1629}
1630
1631template <typename T, typename TNearPointLocator>
1632array<TriInd, 2> Triangulation<T, TNearPointLocator>::walkingSearchTrianglesAt(
1633 const VertInd iV,
1634 const VertInd startVertex) const
1635{
1636 const V2d<T> v = vertices[iV];
1637 array<TriInd, 2> out = {noNeighbor, noNeighbor};
1638 const TriInd iT = walkTriangles(startVertex, v);
1639 // Finished walk, locate point in current triangle
1640 const Triangle& t = triangles[iT];
1641 const V2d<T>& v1 = vertices[t.vertices[0]];
1642 const V2d<T>& v2 = vertices[t.vertices[1]];
1643 const V2d<T>& v3 = vertices[t.vertices[2]];
1644 const PtTriLocation::Enum loc = locatePointTriangle(v, v1, v2, v3);
1645
1646 if(loc == PtTriLocation::Outside)
1647 {
1648 handleException(
1649 Error("No triangle was found at position", CDT_SOURCE_LOCATION));
1650 }
1651 if(loc == PtTriLocation::OnVertex)
1652 {
1653 const VertInd iDupe = v1 == v ? t.vertices[0]
1654 : v2 == v ? t.vertices[1]
1655 : t.vertices[2];
1656 handleException(DuplicateVertexError(
1657 VertInd(iV - m_nTargetVerts),
1658 VertInd(iDupe - m_nTargetVerts),
1659 CDT_SOURCE_LOCATION));
1660 }
1661
1662 out[0] = iT;
1663 if(isOnEdge(loc))
1664 out[1] = t.neighbors[edgeNeighbor(loc)];
1665 return out;
1666}
1667
1668template <typename T, typename TNearPointLocator>
1669void Triangulation<T, TNearPointLocator>::changeNeighbor(
1670 const TriInd iT,
1671 const TriInd oldNeighbor,
1672 const TriInd newNeighbor)
1673{
1674 if(iT == noNeighbor)
1675 return;
1676 NeighborsArr3& nn = triangles[iT].neighbors;
1677 assert(
1678 nn[0] == oldNeighbor || nn[1] == oldNeighbor || nn[2] == oldNeighbor);
1679 if(nn[0] == oldNeighbor)
1680 nn[0] = newNeighbor;
1681 else if(nn[1] == oldNeighbor)
1682 nn[1] = newNeighbor;
1683 else
1684 nn[2] = newNeighbor;
1685}
1686
1687template <typename T, typename TNearPointLocator>
1688void Triangulation<T, TNearPointLocator>::changeNeighbor(
1689 const TriInd iT,
1690 const VertInd iVedge1,
1691 const VertInd iVedge2,
1692 const TriInd newNeighbor)
1693{
1694 assert(iT != noNeighbor);
1695 Triangle& t = triangles[iT];
1696 t.neighbors[edgeNeighborInd(t.vertices, iVedge1, iVedge2)] = newNeighbor;
1697}
1698
1699template <typename T, typename TNearPointLocator>
1700void Triangulation<T, TNearPointLocator>::triangulatePseudoPolygon(
1701 const std::vector<VertInd>& poly,
1702 unordered_map<Edge, TriInd>& outerTris,
1703 TriInd iT,
1704 TriInd iN,
1705 std::vector<TriInd>& trianglesToReuse,
1706 std::vector<TriangulatePseudoPolygonTask>& iterations)
1707{
1708 assert(poly.size() > 2);
1709 // note: uses iteration instead of recursion to avoid stack overflows
1710 iterations.clear();
1711 iterations.push_back(make_tuple(
1712 IndexSizeType(0),
1713 static_cast<IndexSizeType>(poly.size() - 1),
1714 iT,
1715 iN,
1716 Index(0)));
1717 while(!iterations.empty())
1718 {
1719 triangulatePseudoPolygonIteration(
1720 poly, outerTris, trianglesToReuse, iterations);
1721 }
1722}
1723
1724template <typename T, typename TNearPointLocator>
1725void Triangulation<T, TNearPointLocator>::triangulatePseudoPolygonIteration(
1726 const std::vector<VertInd>& poly,
1727 unordered_map<Edge, TriInd>& outerTris,
1728 std::vector<TriInd>& trianglesToReuse,
1729 std::vector<TriangulatePseudoPolygonTask>& iterations)
1730{
1731 IndexSizeType iA, iB;
1732 TriInd iT, iParent;
1733 Index iInParent;
1734 assert(!iterations.empty());
1735 tie(iA, iB, iT, iParent, iInParent) = iterations.back();
1736 iterations.pop_back();
1737 assert(iB - iA > 1 && iT != noNeighbor && iParent != noNeighbor);
1738 Triangle& t = triangles[iT];
1739 // find Delaunay point
1740 const IndexSizeType iC = findDelaunayPoint(poly, iA, iB);
1741
1742 const VertInd a = poly[iA];
1743 const VertInd b = poly[iB];
1744 const VertInd c = poly[iC];
1745
1746 // split pseudo-polygon in two parts and triangulate them
1747 // note: second part needs to be pushed on stack first to be processed first
1748
1749 // second part: points after the Delaunay point
1750 if(iB - iC > 1)
1751 {
1752 assert(!trianglesToReuse.empty());
1753 const TriInd iNext = trianglesToReuse.back();
1754 trianglesToReuse.pop_back();
1755 iterations.push_back(make_tuple(iC, iB, iNext, iT, Index(1)));
1756 }
1757 else // pseudo-poly is reduced to a single outer edge
1758 {
1759 const Edge outerEdge(b, c);
1760 const TriInd outerTri = outerTris.at(outerEdge);
1761 if(outerTri != noNeighbor)
1762 {
1763 assert(outerTri != iT);
1764 t.neighbors[1] = outerTri;
1765 changeNeighbor(outerTri, c, b, iT);
1766 }
1767 else
1768 outerTris.at(outerEdge) = iT;
1769 }
1770 // first part: points before the Delaunay point
1771 if(iC - iA > 1)
1772 { // add next triangle and add another iteration
1773 assert(!trianglesToReuse.empty());
1774 const TriInd iNext = trianglesToReuse.back();
1775 trianglesToReuse.pop_back();
1776 iterations.push_back(make_tuple(iA, iC, iNext, iT, Index(2)));
1777 }
1778 else
1779 { // pseudo-poly is reduced to a single outer edge
1780 const Edge outerEdge(c, a);
1781 const TriInd outerTri = outerTris.at(outerEdge);
1782 if(outerTri != noNeighbor)
1783 {
1784 assert(outerTri != iT);
1785 t.neighbors[2] = outerTri;
1786 changeNeighbor(outerTri, c, a, iT);
1787 }
1788 else
1789 outerTris.at(outerEdge) = iT;
1790 }
1791 // Finalize triangle
1792 // note: only when triangle is finalized to we add it as a neighbor to
1793 // parent to maintain triangulation topology consistency
1794 triangles[iParent].neighbors[iInParent] = iT;
1795 t.neighbors[0] = iParent;
1796 t.vertices = arr3(a, b, c);
1797 setAdjacentTriangle(c, iT);
1798}
1799
1800template <typename T, typename TNearPointLocator>
1801IndexSizeType Triangulation<T, TNearPointLocator>::findDelaunayPoint(
1802 const std::vector<VertInd>& poly,
1803 const IndexSizeType iA,
1804 const IndexSizeType iB) const
1805{
1806 assert(iB - iA > 1);
1807 const V2d<T>& a = vertices[poly[iA]];
1808 const V2d<T>& b = vertices[poly[iB]];
1809 IndexSizeType out = iA + 1;
1810 const V2d<T>* c = &vertices[poly[out]]; // caching for better performance
1811 for(IndexSizeType i = iA + 1; i < iB; ++i)
1812 {
1813 const V2d<T>& v = vertices[poly[i]];
1814 if(isInCircumcircle(v, a, b, *c))
1815 {
1816 out = i;
1817 c = &v;
1818 }
1819 }
1820 assert(out > iA && out < iB); // point is between ends
1821 return out;
1822}
1823
1824template <typename T, typename TNearPointLocator>
1826 const std::vector<V2d<T> >& newVertices)
1827{
1828 return insertVertices(
1829 newVertices.begin(), newVertices.end(), getX_V2d<T>, getY_V2d<T>);
1830}
1831
1832template <typename T, typename TNearPointLocator>
1834{
1835 return m_vertTris.empty() && !vertices.empty();
1836}
1837
1838template <typename T, typename TNearPointLocator>
1839unordered_map<TriInd, LayerDepth>
1840Triangulation<T, TNearPointLocator>::peelLayer(
1841 std::stack<TriInd> seeds,
1842 const LayerDepth layerDepth,
1843 std::vector<LayerDepth>& triDepths) const
1844{
1845 unordered_map<TriInd, LayerDepth> behindBoundary;
1846 while(!seeds.empty())
1847 {
1848 const TriInd iT = seeds.top();
1849 seeds.pop();
1850 triDepths[iT] = std::min(triDepths[iT], layerDepth);
1851 behindBoundary.erase(iT);
1852 const Triangle& t = triangles[iT];
1853 for(Index i(0); i < Index(3); ++i)
1854 {
1855 const Edge opEdge(t.vertices[ccw(i)], t.vertices[cw(i)]);
1856 const TriInd iN = t.neighbors[opoNbr(i)];
1857 if(iN == noNeighbor || triDepths[iN] <= layerDepth)
1858 continue;
1859 if(fixedEdges.count(opEdge))
1860 {
1861 const unordered_map<Edge, LayerDepth>::const_iterator cit =
1862 overlapCount.find(opEdge);
1863 const LayerDepth triDepth = cit == overlapCount.end()
1864 ? layerDepth + 1
1865 : layerDepth + cit->second + 1;
1866 behindBoundary[iN] = triDepth;
1867 continue;
1868 }
1869 seeds.push(iN);
1870 }
1871 }
1872 return behindBoundary;
1873}
1874
1875template <typename T, typename TNearPointLocator>
1876std::vector<LayerDepth>
1878{
1879 std::vector<LayerDepth> triDepths(
1880 triangles.size(), std::numeric_limits<LayerDepth>::max());
1881 std::stack<TriInd> seeds(TriDeque(1, m_vertTris[0]));
1882 LayerDepth layerDepth = 0;
1883 LayerDepth deepestSeedDepth = 0;
1884
1885 unordered_map<LayerDepth, TriIndUSet> seedsByDepth;
1886 do
1887 {
1888 const unordered_map<TriInd, LayerDepth>& newSeeds =
1889 peelLayer(seeds, layerDepth, triDepths);
1890
1891 seedsByDepth.erase(layerDepth);
1892 typedef unordered_map<TriInd, LayerDepth>::const_iterator Iter;
1893 for(Iter it = newSeeds.begin(); it != newSeeds.end(); ++it)
1894 {
1895 deepestSeedDepth = std::max(deepestSeedDepth, it->second);
1896 seedsByDepth[it->second].insert(it->first);
1897 }
1898 const TriIndUSet& nextLayerSeeds = seedsByDepth[layerDepth + 1];
1899 seeds = std::stack<TriInd>(
1900 TriDeque(nextLayerSeeds.begin(), nextLayerSeeds.end()));
1901 ++layerDepth;
1902 } while(!seeds.empty() || deepestSeedDepth > layerDepth);
1903
1904 return triDepths;
1905}
1906
1907#ifdef CDT_ENABLE_CALLBACK_HANDLER
1908template <typename T, typename TNearPointLocator>
1910 ICallbackHandler* callbackHandler)
1911{
1912 m_callbackHandler = callbackHandler;
1913}
1914#endif
1915
1916template <typename T, typename TNearPointLocator>
1917void Triangulation<T, TNearPointLocator>::insertVertices_AsProvided(
1918 VertInd superGeomVertCount)
1919{
1920 for(VertInd iV = superGeomVertCount; iV < vertices.size(); ++iV)
1921 {
1922#ifdef CDT_ENABLE_CALLBACK_HANDLER
1923 if(m_callbackHandler && m_callbackHandler->isAbortCalculation())
1924 {
1925 return;
1926 }
1927#endif
1928 insertVertex(iV);
1929 }
1930}
1931
1932template <typename T, typename TNearPointLocator>
1933void Triangulation<T, TNearPointLocator>::insertVertices_Randomized(
1934 VertInd superGeomVertCount)
1935{
1936 std::size_t vertexCount = vertices.size() - superGeomVertCount;
1937 std::vector<VertInd> ii(vertexCount);
1938 detail::iota(ii.begin(), ii.end(), superGeomVertCount);
1939 detail::random_shuffle(ii.begin(), ii.end());
1940 for(std::vector<VertInd>::iterator it = ii.begin(); it != ii.end(); ++it)
1941 {
1942#ifdef CDT_ENABLE_CALLBACK_HANDLER
1943 if(m_callbackHandler && m_callbackHandler->isAbortCalculation())
1944 {
1945 return;
1946 }
1947#endif
1948 insertVertex(*it);
1949 }
1950}
1951
1952namespace detail
1953{
1954
1955// log2 implementation backwards compatible with pre c++11
1956template <typename T>
1957inline double log2_bc(T x)
1958{
1959#ifdef CDT_CXX11_IS_SUPPORTED
1960 return std::log2(x);
1961#else
1962 static double log2_constant = std::log(2.0);
1963 return std::log(static_cast<double>(x)) / log2_constant;
1964#endif
1965}
1966
1971inline std::size_t maxQueueLengthBFSKDTree(const std::size_t vertexCount)
1972{
1973 const int filledLayerPow2 =
1974 static_cast<int>(std::floor(log2_bc(vertexCount)) - 1);
1975 const std::size_t nodesInFilledTree =
1976 static_cast<std::size_t>(std::pow(2., filledLayerPow2 + 1) - 1);
1977 const std::size_t nodesInLastFilledLayer =
1978 static_cast<std::size_t>(std::pow(2., filledLayerPow2));
1979 const std::size_t nodesInLastLayer = vertexCount - nodesInFilledTree;
1980 return nodesInLastLayer >= nodesInLastFilledLayer
1981 ? nodesInLastFilledLayer + nodesInLastLayer -
1982 nodesInLastFilledLayer
1983 : nodesInLastFilledLayer;
1984}
1985
1986template <typename T>
1987class FixedCapacityQueue
1988{
1989public:
1990 FixedCapacityQueue(const std::size_t capacity)
1991 : m_vec(capacity)
1992 , m_front(m_vec.begin())
1993 , m_back(m_vec.begin())
1994 , m_size(0)
1995 {}
1996 bool empty() const
1997 {
1998 return m_size == 0;
1999 }
2000 const T& front() const
2001 {
2002 return *m_front;
2003 }
2004 void pop()
2005 {
2006 assert(m_size > 0);
2007 ++m_front;
2008 if(m_front == m_vec.end())
2009 m_front = m_vec.begin();
2010 --m_size;
2011 }
2012 void push(const T& t)
2013 {
2014 assert(m_size < m_vec.size());
2015 *m_back = t;
2016 ++m_back;
2017 if(m_back == m_vec.end())
2018 m_back = m_vec.begin();
2019 ++m_size;
2020 }
2021#ifdef CDT_CXX11_IS_SUPPORTED
2022 void push(const T&& t)
2023 {
2024 assert(m_size < m_vec.size());
2025 *m_back = t;
2026 ++m_back;
2027 if(m_back == m_vec.end())
2028 m_back = m_vec.begin();
2029 ++m_size;
2030 }
2031#endif
2032private:
2033 std::vector<T> m_vec;
2034 typename std::vector<T>::iterator m_front;
2035 typename std::vector<T>::iterator m_back;
2036 std::size_t m_size;
2037};
2038
2039template <typename T>
2040class less_than_x
2041{
2042 const std::vector<V2d<T> >& m_vertices;
2043
2044public:
2045 less_than_x(const std::vector<V2d<T> >& vertices)
2046 : m_vertices(vertices)
2047 {}
2048 bool operator()(const VertInd a, const VertInd b) const
2049 {
2050 return m_vertices[a].x < m_vertices[b].x;
2051 }
2052};
2053
2054template <typename T>
2055class less_than_y
2056{
2057 const std::vector<V2d<T> >& m_vertices;
2058
2059public:
2060 less_than_y(const std::vector<V2d<T> >& vertices)
2061 : m_vertices(vertices)
2062 {}
2063 bool operator()(const VertInd a, const VertInd b) const
2064 {
2065 return m_vertices[a].y < m_vertices[b].y;
2066 }
2067};
2068
2069} // namespace detail
2070
2071template <typename T, typename TNearPointLocator>
2072void Triangulation<T, TNearPointLocator>::insertVertices_KDTreeBFS(
2073 VertInd superGeomVertCount,
2074 Box2d<T> box)
2075{
2076 // calculate original indices
2077 const VertInd vertexCount(vertices.size() - superGeomVertCount);
2078 if(vertexCount <= VertInd(0))
2079 return;
2080 std::vector<VertInd> ii(vertexCount);
2081 detail::iota(ii.begin(), ii.end(), superGeomVertCount);
2082
2083 typedef std::vector<VertInd>::iterator It;
2085 detail::maxQueueLengthBFSKDTree(vertexCount));
2086 queue.push(make_tuple(ii.begin(), ii.end(), box.min, box.max, VertInd(0)));
2087
2088 It first, last;
2089 V2d<T> newBoxMin, newBoxMax;
2090 VertInd parent, mid;
2091
2092 const detail::less_than_x<T> cmpX(vertices);
2093 const detail::less_than_y<T> cmpY(vertices);
2094
2095 while(!queue.empty())
2096 {
2097#ifdef CDT_ENABLE_CALLBACK_HANDLER
2098 if(m_callbackHandler && m_callbackHandler->isAbortCalculation())
2099 {
2100 return;
2101 }
2102#endif
2103 tie(first, last, box.min, box.max, parent) = queue.front();
2104 queue.pop();
2105 assert(first != last);
2106
2107 const std::ptrdiff_t len = std::distance(first, last);
2108 if(len == 1)
2109 {
2110 insertVertex(*first, parent);
2111 continue;
2112 }
2113 const It midIt = first + len / 2;
2114 if(box.max.x - box.min.x >= box.max.y - box.min.y)
2115 {
2116 detail::portable_nth_element(first, midIt, last, cmpX);
2117 mid = *midIt;
2118 const T split = vertices[mid].x;
2119 newBoxMin.x = split;
2120 newBoxMin.y = box.min.y;
2121 newBoxMax.x = split;
2122 newBoxMax.y = box.max.y;
2123 }
2124 else
2125 {
2126 detail::portable_nth_element(first, midIt, last, cmpY);
2127 mid = *midIt;
2128 const T split = vertices[mid].y;
2129 newBoxMin.x = box.min.x;
2130 newBoxMin.y = split;
2131 newBoxMax.x = box.max.x;
2132 newBoxMax.y = split;
2133 }
2134 insertVertex(mid, parent);
2135 if(first != midIt)
2136 {
2137 queue.push(make_tuple(first, midIt, box.min, newBoxMax, mid));
2138 }
2139 if(midIt + 1 != last)
2140 {
2141 queue.push(make_tuple(midIt + 1, last, newBoxMin, box.max, mid));
2142 }
2143 }
2144}
2145
2146template <typename T, typename TNearPointLocator>
2147std::pair<TriInd, TriInd> Triangulation<T, TNearPointLocator>::edgeTriangles(
2148 const VertInd a,
2149 const VertInd b) const
2150{
2151 const TriInd triStart = m_vertTris[a];
2152 assert(triStart != noNeighbor);
2153 TriInd iT = triStart, iTNext = triStart;
2154 VertInd iV = noVertex;
2155 do
2156 {
2157 const Triangle& t = triangles[iT];
2158 tie(iTNext, iV) = t.next(a);
2159 assert(iTNext != noNeighbor);
2160 if(iV == b)
2161 {
2162 return std::make_pair(iT, iTNext);
2163 }
2164 iT = iTNext;
2165 } while(iT != triStart);
2166 return std::make_pair(noNeighbor, noNeighbor);
2167}
2168
2169template <typename T, typename TNearPointLocator>
2170bool Triangulation<T, TNearPointLocator>::hasEdge(
2171 const VertInd a,
2172 const VertInd b) const
2173{
2174 return edgeTriangles(a, b).first != invalidIndexSizeType;
2175}
2176
2177template <typename T, typename TNearPointLocator>
2178void Triangulation<T, TNearPointLocator>::setAdjacentTriangle(
2179 const VertInd v,
2180 const TriInd t)
2181{
2182 assert(t != noNeighbor);
2183 m_vertTris[v] = t;
2184 assert(
2185 triangles[t].vertices[0] == v || triangles[t].vertices[1] == v ||
2186 triangles[t].vertices[2] == v);
2187}
2188
2189template <typename T, typename TNearPointLocator>
2190void Triangulation<T, TNearPointLocator>::pivotVertexTriangleCW(const VertInd v)
2191{
2192 assert(m_vertTris[v] != noNeighbor);
2193 m_vertTris[v] = triangles[m_vertTris[v]].next(v).first;
2194 assert(m_vertTris[v] != noNeighbor);
2195 assert(
2196 triangles[m_vertTris[v]].vertices[0] == v ||
2197 triangles[m_vertTris[v]].vertices[1] == v ||
2198 triangles[m_vertTris[v]].vertices[2] == v);
2199}
2200
2201template <typename T, typename TNearPointLocator>
2202void Triangulation<T, TNearPointLocator>::tryAddVertexToLocator(const VertInd v)
2203{
2204 if(!m_nearPtLocator.empty()) // only if locator is initialized already
2205 m_nearPtLocator.addPoint(v, vertices);
2206}
2207
2208template <typename T, typename TNearPointLocator>
2209void Triangulation<T, TNearPointLocator>::tryInitNearestPointLocator()
2210{
2211 if(!vertices.empty() && m_nearPtLocator.empty())
2212 {
2213 m_nearPtLocator.initialize(vertices);
2214 }
2215}
2216
2217} // namespace CDT
2218
2219#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...
Base class for errors.
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...
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.
bool isFinalized() const
Check if the triangulation was finalized with erase... method and super-triangle was removed.
void eraseOuterTrianglesAndHoles()
Erase triangles outside of constrained boundary and auto-detected holes.
V2dVec vertices
triangulation's vertices
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.
TriangleVec triangles
triangulation's triangles
Triangulation()
Default constructor.
void setCallbackHandler(ICallbackHandler *callbackHandler)
Set user-provided callback handler.
void initializedWithCustomSuperGeometry()
Call this method after directly setting custom super-geometry via vertices and triangles members.
unsigned short LayerDepth
Type used for storing layer depths for triangles.
Definition CDT.h:40
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:346
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:203
unordered_set< Edge > EdgeUSet
Hash table of edges.
Definition CDTUtils.h:347
VertInd edge_get_v2(const Edge &e)
Get edge second vertex.
Definition CDTUtils.h:335
std::vector< TriInd > TriIndVec
Vector of triangle indices.
Definition CDTUtils.h:225
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:141
VertInd edge_get_v1(const Edge &e)
Get edge first vertex.
Definition CDTUtils.h:329
CDT_EXPORT PtLineLocation::Enum classifyOrientation(T orientation, T orientationTolerance=T(0))
Classify value of orient2d predicate.
Definition CDTUtils.hpp:62
array< TriInd, 3 > NeighborsArr3
array of three neighbors
Definition CDTUtils.h:227
CDT_EXPORT T distance(const V2d< T > &a, const V2d< T > &b)
Distance between two 2D points.
Definition CDTUtils.hpp:250
IndexSizeType VertInd
Vertex index.
Definition CDTUtils.h:207
CDT_EXPORT Index cw(Index i)
Advance vertex or neighbor index clockwise.
Definition CDTUtils.hpp:26
array< VertInd, 3 > VerticesArr3
array of three vertex indices
Definition CDTUtils.h:226
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:261
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:128
CDT_EXPORT CDT_INLINE_IF_HEADER_ONLY Index opoNbr(Index vertIndex)
Opposed neighbor index from vertex index.
Definition CDTUtils.hpp:104
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:186
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:45
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:51
unordered_set< TriInd > TriIndUSet
Hash table of triangles.
Definition CDTUtils.h:348
CDT_EXPORT Index ccw(Index i)
Advance vertex or neighbor index counter-clockwise.
Definition CDTUtils.hpp:21
CDT_EXPORT Index edgeNeighbor(PtTriLocation::Enum location)
Neighbor index from a on-edge location.
Definition CDTUtils.hpp:38
unordered_map< TriInd, TriInd > TriIndUMap
Triangle hash map.
Definition CDTUtils.h:349
const T & getX_V2d(const V2d< T > &v)
X- coordinate getter for V2d.
Definition CDTUtils.h:164
unsigned char Index
Index in triangle.
Definition CDTUtils.h:205
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:197
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:216
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:31
IndexSizeType TriInd
Triangle index.
Definition CDTUtils.h:209
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:72
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:175
const T & getY_V2d(const V2d< T > &v)
Y-coordinate getter for V2d.
Definition CDTUtils.h:171
@ FixedEdgeMidpoint
During conforming triangulation edge mid-point is added.
@ FixedEdgesIntersection
Resolving fixed/constraint edges' intersection.
2D bounding box
Definition CDTUtils.h:232
V2d< T > max
max box corner
Definition CDTUtils.h:234
V2d< T > min
min box corner
Definition CDTUtils.h:233
Edge connecting two vertices: vertex with smaller index is always first.
Definition CDTUtils.h:287
VertInd v1() const
V1 getter.
Definition CDTUtils.h:307
VertInd v2() const
V2 getter.
Definition CDTUtils.h:313
@ TryResolve
attempt to resolve constraint edge intersections
@ NotAllowed
constraint edge intersections are not allowed
@ DontCheck
No checks: slightly faster but less safe.
Enum
The Enum itself.
@ SuperTriangle
conventional super-triangle
@ Custom
user-specified custom geometry (e.g., grid)
Triangulation triangle (counter-clockwise winding)
Definition CDTUtils.h:360
VerticesArr3 vertices
triangle's three vertices
Definition CDTUtils.h:361
NeighborsArr3 neighbors
triangle's three neighbors
Definition CDTUtils.h:362
std::pair< TriInd, VertInd > next(const VertInd i) const
Next triangle adjacent to a vertex (clockwise)
Definition CDTUtils.h:378
2D vector
Definition CDTUtils.h:145
T y
Y-coordinate.
Definition CDTUtils.h:147
T x
X-coordinate.
Definition CDTUtils.h:146
@ Auto
Automatic insertion order optimized for better performance.