KiCad PCB EDA Suite
Loading...
Searching...
No Matches
polygon_triangulation.h
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.TXT for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 3
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 *
19 * Based on Uniform Plane Subdivision algorithm from Lamot, Marko, and Borut Žalik.
20 * "A fast polygon triangulation algorithm based on uniform plane subdivision."
21 * Computers & graphics 27, no. 2 (2003): 239-253.
22 *
23 * Code derived from:
24 * K-3D which is Copyright (c) 2005-2006, Romain Behar, GPL-2, license above
25 * earcut which is Copyright (c) 2016, Mapbox, ISC
26 *
27 * ISC License:
28 * Permission to use, copy, modify, and/or distribute this software for any purpose
29 * with or without fee is hereby granted, provided that the above copyright notice
30 * and this permission notice appear in all copies.
31 *
32 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
33 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
34 * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
35 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
36 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
37 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
38 * THIS SOFTWARE.
39 *
40 */
41
42#ifndef __POLYGON_TRIANGULATION_H
43#define __POLYGON_TRIANGULATION_H
44
45#include <algorithm>
46#include <array>
47#include <cstdlib>
48#include <deque>
49#include <cmath>
50#include <vector>
51
52#include <advanced_config.h>
55#include <geometry/vertex_set.h>
56#include <math/box2.h>
57#include <math/vector2d.h>
58
59#include <wx/log.h>
60
61// ADVANCED_CFG::GetCfg() cannot be used on msys2/mingw builds (link failure)
62// So we use the ADVANCED_CFG default values
63#if defined( __MINGW32__ )
64 #define TRIANGULATESIMPLIFICATIONLEVEL 50
65 #define TRIANGULATEMINIMUMAREA 1000
66 #define TRIANGULATEDELAUNAYREFINE true
67#else
68 #define TRIANGULATESIMPLIFICATIONLEVEL ADVANCED_CFG::GetCfg().m_TriangulateSimplificationLevel
69 #define TRIANGULATEMINIMUMAREA ADVANCED_CFG::GetCfg().m_TriangulateMinimumArea
70 #define TRIANGULATEDELAUNAYREFINE ADVANCED_CFG::GetCfg().m_TriangulateDelaunayRefine
71#endif
72
73#define TRIANGULATE_TRACE "triangulate"
74
75
77static inline bool triangulationRefineEnabled()
78{
80}
81
82
84{
85public:
90
97 {
98 if( aPolygon.empty() )
99 return true;
100
101 if( aPolygon.size() == 1 )
102 return TesselatePolygon( aPolygon[0], aHintData );
103
104 const SHAPE_LINE_CHAIN& outline = aPolygon[0];
105
106 m_bbox = outline.BBox();
107
108 for( size_t i = 1; i < aPolygon.size(); i++ )
109 m_bbox.Merge( aPolygon[i].BBox() );
110
111 m_result.Clear();
112
113 if( !m_bbox.GetWidth() || !m_bbox.GetHeight() )
114 return true;
115
116 for( const SHAPE_LINE_CHAIN& chain : aPolygon )
117 {
118 for( const VECTOR2I& pt : chain.CPoints() )
119 m_result.AddVertex( pt );
120 }
121
122 int baseIndex = 0;
123 VERTEX* outerRing = createRing( outline, baseIndex, true );
124 baseIndex += outline.PointCount();
125
126 if( !outerRing || outerRing->prev == outerRing->next )
127 return true;
128
129 std::vector<VERTEX*> holeRings;
130
131 for( size_t i = 1; i < aPolygon.size(); i++ )
132 {
133 VERTEX* holeRing = createRing( aPolygon[i], baseIndex, false );
134 baseIndex += aPolygon[i].PointCount();
135
136 // Reject rings collapsed below 3 vertices. Match the outer-ring guard at
137 // line 119 so degenerate holes (single point or two-vertex sliver) cannot
138 // reach eliminateHoles().
139 if( holeRing && holeRing->prev != holeRing->next )
140 holeRings.push_back( holeRing );
141 }
142
144
145 if( !holeRings.empty() )
146 {
147 outerRing = eliminateHoles( outerRing, holeRings );
148
149 if( !outerRing )
150 {
151 wxLogTrace( TRIANGULATE_TRACE, "Hole elimination failed" );
152 return false;
153 }
154 }
155
156 if( VERTEX* simplified = simplifyList( outerRing ) )
157 outerRing = simplified;
158
159 outerRing->updateList();
160
161 if( VERTEX* decimated = decimateList( outerRing ) )
162 outerRing = decimated;
163
164 auto retval = earcutList( outerRing );
165
166 if( !retval )
167 {
168 wxLogTrace( TRIANGULATE_TRACE, "Tesselation with holes failed, logging remaining vertices" );
169 logRemaining();
170 }
171 else if( triangulationRefineEnabled() )
172 {
173 m_result.Refine();
174 }
175
176 m_vertices.clear();
177 return retval;
178 }
179
182 {
183 m_bbox = aPoly.BBox();
184 m_result.Clear();
185
186 if( !m_bbox.GetWidth() || !m_bbox.GetHeight() )
187 return true;
188
192 VERTEX* firstVertex = createList( aPoly );
193
194 for( const VECTOR2I& pt : aPoly.CPoints() )
195 m_result.AddVertex( pt );
196
197 if( !firstVertex || firstVertex->prev == firstVertex->next )
198 return true;
199
200 wxLogTrace( TRIANGULATE_TRACE, "Created list with %f area", firstVertex->area() );
201
203
204 if( VERTEX* simplified = simplifyList( firstVertex ) )
205 firstVertex = simplified;
206
207 firstVertex->updateList();
208
215 if( aHintData && aHintData->Vertices().size() == m_result.GetVertexCount() )
216 {
217 m_result.SetTriangles( aHintData->Triangles() );
218 return true;
219 }
220 else
221 {
222 if( VERTEX* decimated = decimateList( firstVertex ) )
223 firstVertex = decimated;
224
225 auto retval = earcutList( firstVertex );
226
227 if( !retval )
228 {
229 wxLogTrace( TRIANGULATE_TRACE, "Tesselation failed, logging remaining vertices" );
230 logRemaining();
231 }
232 else if( triangulationRefineEnabled() )
233 {
234 m_result.Refine();
235 }
236
237 m_vertices.clear();
238 return retval;
239 }
240 }
241
242 std::vector<double> PartitionAreaFractionsForTesting( const SHAPE_LINE_CHAIN& aPoly,
243 size_t aTargetLeaves ) const
244 {
245 std::vector<SHAPE_LINE_CHAIN> partitions = partitionPolygonBalanced( aPoly, aTargetLeaves );
246 std::vector<double> fractions;
247 double totalArea = std::abs( aPoly.Area() );
248
249 if( totalArea <= 0.0 )
250 return fractions;
251
252 fractions.reserve( partitions.size() );
253
254 for( const SHAPE_LINE_CHAIN& part : partitions )
255 fractions.push_back( std::abs( part.Area() ) / totalArea );
256
257 return fractions;
258 }
259private:
261 friend class SHAPE_POLY_SET;
262
264 {
267 };
268
269 bool collectScanlineHits( const SHAPE_LINE_CHAIN& aPoly, bool aVertical, int aCut,
270 std::array<SCANLINE_HIT, 2>& aHits ) const
271 {
272 int count = 0;
273
274 for( int ii = 0; ii < aPoly.PointCount(); ++ii )
275 {
276 const VECTOR2I& a = aPoly.CPoint( ii );
277 const VECTOR2I& b = aPoly.CPoint( ( ii + 1 ) % aPoly.PointCount() );
278
279 if( aVertical )
280 {
281 if( a.x == b.x || aCut <= std::min( a.x, b.x ) || aCut >= std::max( a.x, b.x ) )
282 continue;
283
284 if( count >= 2 )
285 return false;
286
287 double t = static_cast<double>( aCut - a.x ) / static_cast<double>( b.x - a.x );
288 int y = static_cast<int>( std::lround( a.y + t * ( b.y - a.y ) ) );
289 aHits[count++] = { ii, VECTOR2I( aCut, y ) };
290 }
291 else
292 {
293 if( a.y == b.y || aCut <= std::min( a.y, b.y ) || aCut >= std::max( a.y, b.y ) )
294 continue;
295
296 if( count >= 2 )
297 return false;
298
299 double t = static_cast<double>( aCut - a.y ) / static_cast<double>( b.y - a.y );
300 int x = static_cast<int>( std::lround( a.x + t * ( b.x - a.x ) ) );
301 aHits[count++] = { ii, VECTOR2I( x, aCut ) };
302 }
303 }
304
305 return count == 2;
306 }
307
308 SHAPE_LINE_CHAIN createSplitChild( const SHAPE_LINE_CHAIN& aPoly, int aStart, int aEnd ) const
309 {
310 SHAPE_LINE_CHAIN child;
311 int idx = aStart;
312 int guard = 0;
313 const int count = aPoly.PointCount();
314
315 do
316 {
317 child.Append( aPoly.CPoint( idx ) );
318 idx = ( idx + 1 ) % count;
319 ++guard;
320 } while( idx != ( aEnd + 1 ) % count && guard <= count + 2 );
321
322 child.SetClosed( true );
323 child.Simplify2( true );
324 return child;
325 }
326
327 bool splitPolygonAtCoordinate( const SHAPE_LINE_CHAIN& aPoly, bool aVertical, int aCut,
328 std::array<SHAPE_LINE_CHAIN, 2>& aChildren, double& aAreaA,
329 double& aAreaB ) const
330 {
331 std::array<SCANLINE_HIT, 2> hits;
332
333 if( !collectScanlineHits( aPoly, aVertical, aCut, hits ) )
334 return false;
335
336 SHAPE_LINE_CHAIN augmented( aPoly );
337 augmented.Split( hits[0].point, true );
338 augmented.Split( hits[1].point, true );
339
340 int idxA = augmented.Find( hits[0].point );
341 int idxB = augmented.Find( hits[1].point );
342
343 if( idxA < 0 || idxB < 0 || idxA == idxB )
344 return false;
345
346 aChildren[0] = createSplitChild( augmented, idxA, idxB );
347 aChildren[1] = createSplitChild( augmented, idxB, idxA );
348
349 if( aChildren[0].PointCount() < 3 || aChildren[1].PointCount() < 3 )
350 return false;
351
352 aAreaA = std::abs( aChildren[0].Area() );
353 aAreaB = std::abs( aChildren[1].Area() );
354 return aAreaA > 0.0 && aAreaB > 0.0;
355 }
356
358 std::array<SHAPE_LINE_CHAIN, 2>& aChildren ) const
359 {
360 const BOX2I bbox = aPoly.BBox();
361 const bool verticalFirst = bbox.GetWidth() >= bbox.GetHeight();
362 const double totalArea = std::abs( aPoly.Area() );
363
364 if( totalArea <= 0.0 )
365 return false;
366
367 auto tryAxis =
368 [&]( bool aVertical ) -> bool
369 {
370 const int low = ( aVertical ? bbox.GetX() : bbox.GetY() ) + 1;
371 const int high = ( aVertical ? bbox.GetRight() : bbox.GetBottom() ) - 1;
372
373 if( high <= low )
374 return false;
375
376 double bestImbalance = std::numeric_limits<double>::infinity();
377 std::array<SHAPE_LINE_CHAIN, 2> bestChildren;
378
379 constexpr int kSamples = 15;
380
381 for( int ii = 1; ii <= kSamples; ++ii )
382 {
383 int cut = low + ( ( high - low ) * ii ) / ( kSamples + 1 );
384 std::array<SHAPE_LINE_CHAIN, 2> candidate;
385 double areaA = 0.0;
386 double areaB = 0.0;
387
388 if( !splitPolygonAtCoordinate( aPoly, aVertical, cut, candidate, areaA, areaB ) )
389 continue;
390
391 double imbalance = std::abs( areaA - areaB ) / totalArea;
392
393 if( imbalance < bestImbalance )
394 {
395 bestImbalance = imbalance;
396 bestChildren = std::move( candidate );
397 }
398 }
399
400 if( !std::isfinite( bestImbalance ) || bestImbalance > 0.35 )
401 return false;
402
403 aChildren = std::move( bestChildren );
404 return true;
405 };
406
407 return tryAxis( verticalFirst ) || tryAxis( !verticalFirst );
408 }
409
410 std::vector<SHAPE_LINE_CHAIN> partitionPolygonBalanced( const SHAPE_LINE_CHAIN& aPoly,
411 size_t aTargetLeaves ) const
412 {
413 std::vector<SHAPE_LINE_CHAIN> leaves = { aPoly };
414
415 if( aTargetLeaves < 2 )
416 return leaves;
417
418 while( leaves.size() < aTargetLeaves )
419 {
420 int bestLeaf = -1;
421 double bestArea = 0.0;
422
423 for( size_t ii = 0; ii < leaves.size(); ++ii )
424 {
425 double area = std::abs( leaves[ii].Area() );
426
427 if( area > bestArea )
428 {
429 bestArea = area;
430 bestLeaf = static_cast<int>( ii );
431 }
432 }
433
434 if( bestLeaf < 0 )
435 break;
436
437 std::array<SHAPE_LINE_CHAIN, 2> children;
438
439 if( !splitPolygonBalanced( leaves[bestLeaf], children ) )
440 break;
441
442 leaves[bestLeaf] = std::move( children[0] );
443 leaves.push_back( std::move( children[1] ) );
444 }
445
446 return leaves;
447 }
448
450 {
451 constexpr size_t kVerticesPerLeaf = 50000;
452 constexpr size_t kMaxLeaves = 8;
453 size_t leaves = 1;
454
455 while( leaves < kMaxLeaves
456 && static_cast<size_t>( aPoly.PointCount() ) / leaves > kVerticesPerLeaf )
457 {
458 leaves *= 2;
459 }
460
461 return leaves;
462 }
463
468 {
469 std::set<VERTEX*> seen;
470 wxLog::EnableLogging();
471 for( VERTEX& p : m_vertices )
472 {
473 if( !p.next || p.next == &p || seen.find( &p ) != seen.end() )
474 continue;
475
476 logVertices( &p, &seen );
477 }
478 }
479
480 void logVertices( VERTEX* aStart, std::set<VERTEX*>* aSeen )
481 {
482 if( aSeen && aSeen->count( aStart ) )
483 return;
484
485 if( aSeen )
486 aSeen->insert( aStart );
487
488 int count = 1;
489 VERTEX* p = aStart->next;
490 wxString msg = wxString::Format( "Vertices: %d,%d,", static_cast<int>( aStart->x ),
491 static_cast<int>( aStart->y ) );
492
493 do
494 {
495 msg += wxString::Format( "%d,%d,", static_cast<int>( p->x ), static_cast<int>( p->y ) );
496
497 if( aSeen )
498 aSeen->insert( p );
499
500 p = p->next;
501 count++;
502 } while( p != aStart );
503
504 if( count < 3 ) // Don't log anything that only has 2 or fewer points
505 return;
506
507 msg.RemoveLast();
508 wxLogTrace( TRIANGULATE_TRACE, msg );
509 }
510
516 {
517 if( !aStart || aStart->next == aStart->prev )
518 return aStart;
519
520 VERTEX* p = aStart;
521 VERTEX* next = p->next;
522 VERTEX* retval = aStart;
523 int count = 0;
524
525 double sq_dist = TRIANGULATESIMPLIFICATIONLEVEL;
526 sq_dist *= sq_dist;
527
528 do
529 {
530 VECTOR2D diff = VECTOR2D( next->x - p->x, next->y - p->y );
531
532 if( diff.SquaredEuclideanNorm() < sq_dist )
533 {
534 if( next == aStart )
535 {
536 retval = p;
537 aStart->remove();
538 count++;
539 break;
540 }
541
542 next = next->next;
543 p->next->remove();
544 count++;
545 retval = p;
546 }
547 else
548 {
549 p = next;
550 next = next->next;
551 }
552 } while( p != aStart && next && p );
553
554 wxLogTrace( TRIANGULATE_TRACE, "Removed %d points in simplifyList", count );
555
556 if( count )
557 return retval;
558
559 return nullptr;
560 }
561
573 {
574 if( !aStart || aStart->next == aStart->prev )
575 return nullptr;
576
577 const double eps = TRIANGULATESIMPLIFICATIONLEVEL;
578 const double epsSq = eps * eps;
579
580 // Caps the quadratic chord re-validation as a run grows.
581 constexpr size_t kMaxRun = 256;
582
583 size_t ringSize = 1;
584 VERTEX* head = aStart;
585
586 // The sentinel is never absorbed; anchor it at the lexicographic max, a hull corner
587 // no valid decimation would remove.
588 for( VERTEX* v = head->next; v != head; v = v->next )
589 {
590 ++ringSize;
591
592 if( v->x > aStart->x || ( v->x == aStart->x && v->y > aStart->y ) )
593 aStart = v;
594 }
595
596 if( ringSize < 8 )
597 return nullptr;
598
599 // Cumulative signed-area budget bounds net drift even when every removal cuts the same
600 // way. Absolute term caps large rings to the QA coverage tolerance; relative term
601 // keeps small rings honest.
602 const double areaBudget = std::min( std::abs( aStart->area() ) * 2e-4, 2.5e8 );
603 double areaUsed = 0.0;
604
605 auto inBand =
606 []( const VERTEX* p, const VERTEX* a, const VERTEX* b, double aEpsSq )
607 {
608 double dx = b->x - a->x;
609 double dy = b->y - a->y;
610 double vx = p->x - a->x;
611 double vy = p->y - a->y;
612 double lenSq = dx * dx + dy * dy;
613 double dot = dx * vx + dy * vy;
614
615 if( lenSq <= 0.0 )
616 return vx * vx + vy * vy <= aEpsSq;
617
618 if( dot < 0.0 || dot > lenSq )
619 return false;
620
621 double cross = dx * vy - dy * vx;
622
623 return cross * cross <= aEpsSq * lenSq;
624 };
625
626 // Boundary-inclusive, mirroring isEar(): fracture-bridge feet land on the triangle
627 // boundary and reject the removal.
628 auto triangleIsEmpty =
629 [this]( VERTEX* aA, VERTEX* aB, VERTEX* aC )
630 {
631 VERTEX* a = aA;
632 VERTEX* c = aC;
633
634 // inTriangle() assumes the negative-area winding isEar() queries with.
635 if( area( aA, aB, aC ) > 0 )
636 std::swap( a, c );
637
638 const double minTX = std::min( a->x, std::min( aB->x, c->x ) );
639 const double minTY = std::min( a->y, std::min( aB->y, c->y ) );
640 const double maxTX = std::max( a->x, std::max( aB->x, c->x ) );
641 const double maxTY = std::max( a->y, std::max( aB->y, c->y ) );
642
643 const uint32_t minZ = zOrder( minTX, minTY );
644 const uint32_t maxZ = zOrder( maxTX, maxTY );
645
646 for( VERTEX* p = aB->nextZ; p && p->z <= maxZ; p = p->nextZ )
647 {
648 if( p != aA && p != aC && p->inTriangle( *a, *aB, *c ) )
649 return false;
650 }
651
652 for( VERTEX* p = aB->prevZ; p && p->z >= minZ; p = p->prevZ )
653 {
654 if( p != aA && p != aC && p->inTriangle( *a, *aB, *c ) )
655 return false;
656 }
657
658 return true;
659 };
660
661 std::vector<const VERTEX*> absorbed;
662 absorbed.reserve( kMaxRun );
663
664 size_t removed = 0;
665 VERTEX* anchor = aStart;
666
667 do
668 {
669 VERTEX* end = anchor->next;
670 absorbed.clear();
671
672 while( end != aStart && absorbed.size() < kMaxRun && ringSize - removed >= 4 )
673 {
674 VERTEX* tryEnd = end->next;
675
676 // A degenerate chord would leave coordinate-duplicate neighbors behind.
677 if( *anchor == *tryEnd )
678 break;
679
680 if( !triangleIsEmpty( anchor, end, tryEnd ) )
681 break;
682
683 double delta = 0.5 * area( anchor, end, tryEnd );
684
685 if( std::abs( areaUsed + delta ) > areaBudget )
686 break;
687
688 // Re-validate the whole run against the grown chord, so acceptance never
689 // depends on removal order.
690 bool ok = inBand( end, anchor, tryEnd, epsSq );
691
692 for( const VERTEX* v : absorbed )
693 {
694 if( !ok )
695 break;
696
697 ok = inBand( v, anchor, tryEnd, epsSq );
698 }
699
700 if( !ok )
701 break;
702
703 areaUsed += delta;
704 absorbed.push_back( end );
705 end->remove();
706 ++removed;
707 end = tryEnd;
708 }
709
710 anchor = end;
711 } while( anchor != aStart );
712
713 wxLogTrace( TRIANGULATE_TRACE, "Removed %zu points in decimateList", removed );
714
715 if( removed )
716 return aStart;
717
718 return nullptr;
719 }
720
729 {
730 VERTEX* retval = nullptr;
731 size_t count = 0;
732
733 if( ( retval = simplifyList( aStart ) ) )
734 aStart = retval;
735
736 wxASSERT( aStart->next && aStart->prev );
737
738 VERTEX* p = aStart->next;
739
740 while( p != aStart && p->next && p->prev )
741 {
742 // We make a dummy triangle that is actually part of the existing line segment
743 // and measure its area. This will not be exactly zero due to floating point
744 // errors. We then look for areas that are less than 4 times the area of the
745 // dummy triangle. For small triangles, this is a small number
746 VERTEX tmp( 0, 0.5 * ( p->prev->x + p->next->x ), 0.5 * ( p->prev->y + p->next->y ), this );
747 double null_area = 4.0 * std::abs( area( p->prev, &tmp, p->next ) );
748
749 if( *p == *( p->next ) || std::abs( area( p->prev, p, p->next ) ) <= null_area )
750 {
751 // This is a spike, remove it, leaving only one point
752 if( *( p->next ) == *( p->prev ) )
753 p->next->remove();
754
755 p = p->prev;
756 p->next->remove();
757 retval = p;
758 ++count;
759
760 if( p == p->next )
761 break;
762
763 // aStart was removed above, so we need to reset it
764 if( !aStart->next )
765 aStart = p->prev;
766
767 continue;
768 }
769
770 p = p->next;
771 };
772
774 if( !p->next || p->next == p || p->next == p->prev )
775 return p;
776
777 // We needed an end point above that wouldn't be removed, so
778 // here we do the final check for this as a Steiner point
779 VERTEX tmp( 0, 0.5 * ( p->prev->x + p->next->x ),
780 0.5 * ( p->prev->y + p->next->y ), this );
781 double null_area = 4.0 * std::abs( area( p->prev, &tmp, p->next ) );
782
783 if( std::abs( area( p->prev, p, p->next ) ) <= null_area )
784 {
785 retval = p->next;
786 p->remove();
787 ++count;
788 }
789
790 wxLogTrace( TRIANGULATE_TRACE, "Removed %zu NULL triangles", count );
791
792 return retval;
793 }
794
808 bool earcutList( VERTEX* aPoint, int pass = 0 )
809 {
810 bool retval = earcutRing( aPoint, pass );
811
812 // Other isEar() callers must not scan an index built for a ring they do not walk
813 m_reflexIndexValid = false;
814 return retval;
815 }
816
817 bool earcutRing( VERTEX* aPoint, int pass )
818 {
819 constexpr int kMaxRecursion = 64;
820
821 if( pass >= kMaxRecursion )
822 {
823 wxLogTrace( TRIANGULATE_TRACE, "earcutList recursion limit reached; aborting triangulation", pass );
824 return false;
825 }
826
827 wxLogTrace( TRIANGULATE_TRACE, "earcutList starting at %p for pass %d", aPoint, pass );
828
829 if( !aPoint )
830 return true;
831
832 VERTEX* stop = aPoint;
833 VERTEX* prev;
834 VERTEX* next;
835 int internal_pass = 1;
836 constexpr int kEarLookahead = 2;
837
838 // Below this size, building the index costs more than the ear tests it saves
839 constexpr size_t kReflexIndexMinVertices = 8192;
840 size_t ringSize = 0;
841 const VERTEX* walk = aPoint;
842
843 do
844 {
845 ringSize++;
846 walk = walk->next;
847 } while( walk != aPoint && ringSize < kReflexIndexMinVertices );
848
849 const bool useReflexIndex = ringSize >= kReflexIndexMinVertices;
850
851 while( aPoint->prev != aPoint->next )
852 {
853 if( useReflexIndex && !m_reflexIndexValid )
854 buildReflexIndex( aPoint );
855
856 prev = aPoint->prev;
857 next = aPoint->next;
858
859 VERTEX* bestEar = nullptr;
860 double bestScore = -1.0;
861 int lookahead = 0;
862
863 for( VERTEX* candidate = aPoint; candidate && lookahead < kEarLookahead;
864 candidate = candidate->next, ++lookahead )
865 {
866 if( !candidate->isEar() || isTooSmall( candidate ) )
867 continue;
868
869 const double score = earScore( candidate->prev, candidate, candidate->next );
870
871 if( !bestEar || score > bestScore )
872 {
873 bestEar = candidate;
874 bestScore = score;
875 }
876 }
877
878 if( bestEar )
879 {
880 prev = bestEar->prev;
881 next = bestEar->next;
882 m_result.AddTriangle( prev->i, bestEar->i, next->i );
883 bestEar->remove();
884
885 // A degenerate ear can leave a neighbor reflex that the index never recorded
886 if( isUnindexedReflex( prev ) || isUnindexedReflex( next ) )
887 m_reflexIndexValid = false;
888
889 // Skip one vertex as the triangle will account for the prev node
890 aPoint = next->next;
891 stop = next->next;
892 continue;
893 }
894
895 VERTEX* nextNext = next->next;
896
897 if( *prev != *nextNext && intersects( prev, aPoint, next, nextNext ) &&
898 locallyInside( prev, nextNext ) &&
899 locallyInside( nextNext, prev ) )
900 {
901 wxLogTrace( TRIANGULATE_TRACE,
902 "Local intersection detected. Merging minor triangle with area %f",
903 area( prev, aPoint, nextNext ) );
904 m_result.AddTriangle( prev->i, aPoint->i, nextNext->i );
905
906 // remove two nodes involved
907 next->remove();
908 aPoint->remove();
909 m_reflexIndexValid = false;
910
911 aPoint = nextNext;
912 stop = nextNext;
913
914 continue;
915 }
916
917 aPoint = next;
918
919 /*
920 * We've searched the entire polygon for available ears and there are still
921 * un-sliced nodes remaining.
922 */
923 if( aPoint == stop && aPoint->prev != aPoint->next )
924 {
925 VERTEX* newPoint;
926
927 // Every recovery step below edits the ring
928 m_reflexIndexValid = false;
929
930 // Removing null triangles will remove steiner points as well as colinear points
931 // that are three in a row. Because our next step is to subdivide the polygon,
932 // we need to allow it to add the subdivided points first. This is why we only
933 // run the RemoveNullTriangles function after the first pass.
934 if( ( internal_pass == 2 ) && ( newPoint = removeNullTriangles( aPoint ) ) )
935 {
936 // There are no remaining triangles in the list
937 if( newPoint->next == newPoint->prev )
938 break;
939
940 aPoint = newPoint;
941 stop = newPoint;
942 continue;
943 }
944
945 ++internal_pass;
946
947 // This will subdivide the polygon 2 times. The first pass will add enough points
948 // such that each edge is less than the average edge length. If this doesn't work
949 // The next pass will remove the null triangles (above) and subdivide the polygon
950 // again, this time adding one point to each long edge (and thereby changing the locations)
951 if( internal_pass < 4 )
952 {
953 wxLogTrace( TRIANGULATE_TRACE, "Subdividing polygon" );
954 subdividePolygon( aPoint, internal_pass );
955 continue;
956 }
957
958 // If we don't have any NULL triangles left, cut the polygon in two and try again
959 wxLogTrace( TRIANGULATE_TRACE, "Splitting polygon" );
960
961 if( !splitPolygon( aPoint, pass + 1 ) )
962 return false;
963
964 break;
965 }
966 }
967
968 // Check to see if we are left with only three points in the polygon
969 if( aPoint->next && aPoint->prev == aPoint->next->next )
970 {
971 // Three concave points will never be able to be triangulated because they were
972 // created by an intersecting polygon, so just drop them.
973 if( area( aPoint->prev, aPoint, aPoint->next ) >= 0 )
974 return true;
975 }
976
977 /*
978 * At this point, our polygon should be fully tessellated.
979 */
980 if( aPoint->prev != aPoint->next )
981 return std::abs( aPoint->area() ) > TRIANGULATEMINIMUMAREA;
982
983 return true;
984 }
985
986
987 bool isUnindexedReflex( const VERTEX* aPoint ) const
988 {
989 return !aPoint->m_inReflexIndex && area( aPoint->prev, aPoint, aPoint->next ) >= 0;
990 }
991
995
996 bool isTooSmall( const VERTEX* aPoint ) const
997 {
998 double min_area = TRIANGULATEMINIMUMAREA;
999 double prev_sq_len = ( aPoint->prev->x - aPoint->x ) * ( aPoint->prev->x - aPoint->x ) +
1000 ( aPoint->prev->y - aPoint->y ) * ( aPoint->prev->y - aPoint->y );
1001 double next_sq_len = ( aPoint->next->x - aPoint->x ) * ( aPoint->next->x - aPoint->x ) +
1002 ( aPoint->next->y - aPoint->y ) * ( aPoint->next->y - aPoint->y );
1003 double opp_sq_len = ( aPoint->next->x - aPoint->prev->x ) * ( aPoint->next->x - aPoint->prev->x ) +
1004 ( aPoint->next->y - aPoint->prev->y ) * ( aPoint->next->y - aPoint->prev->y );
1005
1006 return ( prev_sq_len < min_area || next_sq_len < min_area || opp_sq_len < min_area );
1007 }
1008
1009 double earScore( const VERTEX* a, const VERTEX* b, const VERTEX* c ) const
1010 {
1011 const double ab_sq = ( a->x - b->x ) * ( a->x - b->x ) + ( a->y - b->y ) * ( a->y - b->y );
1012 const double bc_sq = ( b->x - c->x ) * ( b->x - c->x ) + ( b->y - c->y ) * ( b->y - c->y );
1013 const double ca_sq = ( c->x - a->x ) * ( c->x - a->x ) + ( c->y - a->y ) * ( c->y - a->y );
1014 const double norm = ab_sq + bc_sq + ca_sq;
1015
1016 if( norm <= 0.0 )
1017 return 0.0;
1018
1019 return std::abs( area( a, b, c ) ) / norm;
1020 }
1021
1026 VERTEX* createRing( const SHAPE_LINE_CHAIN& aPoints, int aBaseIndex, bool aWantCCW )
1027 {
1028 VERTEX* tail = nullptr;
1029 double sum = 0.0;
1030 VECTOR2L last_pt;
1031 bool first = true;
1032
1033 for( int i = 0; i < aPoints.PointCount(); i++ )
1034 {
1035 VECTOR2D p1 = aPoints.CPoint( i );
1036 VECTOR2D p2 = aPoints.CPoint( ( i + 1 ) % aPoints.PointCount() );
1037 sum += ( ( p2.x - p1.x ) * ( p2.y + p1.y ) );
1038 }
1039
1040 bool isCW = sum > 0.0;
1041 bool needReverse = ( aWantCCW == isCW );
1042
1043 auto addVertex = [&]( int i )
1044 {
1045 const VECTOR2I& pt = aPoints.CPoint( i );
1046
1047 if( first || pt.SquaredDistance( last_pt ) > m_simplificationLevel )
1048 {
1049 tail = insertVertex( aBaseIndex + i, pt, tail );
1050 last_pt = pt;
1051 first = false;
1052 }
1053 };
1054
1055 if( needReverse )
1056 {
1057 for( int i = aPoints.PointCount() - 1; i >= 0; i-- )
1058 addVertex( i );
1059 }
1060 else
1061 {
1062 for( int i = 0; i < aPoints.PointCount(); i++ )
1063 addVertex( i );
1064 }
1065
1066 // Collapse a final duplicate, but never on a single-vertex ring. When the
1067 // simplification pass leaves only one vertex, tail->next == tail and removing
1068 // it would leave the caller holding a vertex with null next/prev pointers.
1069 if( tail && tail->next != tail && ( *tail == *tail->next ) )
1070 tail->next->remove();
1071
1072 return tail;
1073 }
1074
1080 VERTEX* eliminateHoles( VERTEX* aOuterRing, std::vector<VERTEX*>& aHoleRings )
1081 {
1082 struct HoleInfo
1083 {
1084 VERTEX* leftmost;
1085 double leftX;
1086 };
1087
1088 std::vector<HoleInfo> holes;
1089 holes.reserve( aHoleRings.size() );
1090
1091 for( VERTEX* hole : aHoleRings )
1092 {
1093 VERTEX* leftmost = hole;
1094 VERTEX* p = hole->next;
1095
1096 if( !p )
1097 {
1098 wxFAIL;
1099 break;
1100 }
1101
1102 while( p != hole )
1103 {
1104 if( p->x < leftmost->x || ( p->x == leftmost->x && p->y < leftmost->y ) )
1105 leftmost = p;
1106
1107 p = p->next;
1108
1109 if( !p )
1110 {
1111 wxFAIL;
1112 break;
1113 }
1114 }
1115
1116 holes.push_back( { leftmost, leftmost->x } );
1117 }
1118
1119 std::sort( holes.begin(), holes.end(),
1120 []( const HoleInfo& a, const HoleInfo& b ) { return a.leftX < b.leftX; } );
1121
1122 for( const HoleInfo& hi : holes )
1123 {
1124 VERTEX* bridge = findHoleBridge( hi.leftmost, aOuterRing );
1125
1126 if( bridge )
1127 {
1128 VERTEX* bridgeReverse = bridge->split( hi.leftmost );
1129 filterPoints( bridgeReverse, bridgeReverse->next );
1130 aOuterRing = filterPoints( bridge, bridge->next );
1131 }
1132 else
1133 {
1134 wxLogTrace( TRIANGULATE_TRACE, "Failed to find bridge for hole at (%f, %f)",
1135 hi.leftmost->x, hi.leftmost->y );
1136 }
1137 }
1138
1139 return aOuterRing;
1140 }
1141
1145 VERTEX* filterPoints( VERTEX* aStart, VERTEX* aEnd = nullptr )
1146 {
1147 if( !aStart )
1148 return aStart;
1149
1150 if( !aEnd )
1151 aEnd = aStart;
1152
1153 VERTEX* p = aStart;
1154 bool again;
1155
1156 do
1157 {
1158 again = false;
1159
1160 if( *p == *p->next )
1161 {
1162 VERTEX* toRemove = p->next;
1163
1164 if( toRemove == aEnd )
1165 aEnd = p;
1166
1167 toRemove->remove();
1168
1169 if( p == p->next )
1170 return p;
1171
1172 p = p->prev;
1173 again = true;
1174 }
1175 else
1176 {
1177 p = p->next;
1178 }
1179 } while( again || p != aEnd );
1180
1181 return aEnd;
1182 }
1183
1184
1189 VERTEX* findHoleBridge( VERTEX* aHole, VERTEX* aOuterStart )
1190 {
1191 VERTEX* p = aOuterStart;
1192 double hx = aHole->x;
1193 double hy = aHole->y;
1194 double qx = -std::numeric_limits<double>::infinity();
1195 VERTEX* m = nullptr;
1196
1197 do
1198 {
1199 if( hy <= p->y && hy >= p->next->y && p->next->y != p->y )
1200 {
1201 double x = p->x + ( hy - p->y ) * ( p->next->x - p->x )
1202 / ( p->next->y - p->y );
1203
1204 if( x <= hx && x > qx )
1205 {
1206 qx = x;
1207
1208 if( x == hx )
1209 {
1210 if( hy == p->y )
1211 return p;
1212
1213 if( hy == p->next->y )
1214 return p->next;
1215 }
1216
1217 m = ( p->x < p->next->x ) ? p : p->next;
1218 }
1219 }
1220
1221 p = p->next;
1222 } while( p != aOuterStart );
1223
1224 if( !m )
1225 return nullptr;
1226
1227 if( hx == qx )
1228 return m;
1229
1230 // Pick the vertex inside the visibility triangle closest to the ray
1231 const VERTEX* stop = m;
1232 double mx = m->x;
1233 double my = m->y;
1234 double tanMin = std::numeric_limits<double>::infinity();
1235
1236 p = m;
1237
1238 do
1239 {
1240 if( hx >= p->x && p->x >= mx && hx != p->x )
1241 {
1242 bool inside;
1243
1244 if( hy < my )
1245 inside = triArea( hx, hy, mx, my, p->x, p->y ) >= 0
1246 && triArea( mx, my, qx, hy, p->x, p->y ) >= 0
1247 && triArea( qx, hy, hx, hy, p->x, p->y ) >= 0;
1248 else
1249 inside = triArea( qx, hy, mx, my, p->x, p->y ) >= 0
1250 && triArea( mx, my, hx, hy, p->x, p->y ) >= 0
1251 && triArea( hx, hy, qx, hy, p->x, p->y ) >= 0;
1252
1253 if( inside )
1254 {
1255 double t = std::abs( hy - p->y ) / ( hx - p->x );
1256
1257 if( locallyInside( p, aHole )
1258 && ( t < tanMin
1259 || ( t == tanMin
1260 && ( p->x > m->x
1261 || ( p->x == m->x
1262 && sectorContainsSector( m, p ) ) ) ) ) )
1263 {
1264 m = p;
1265 tanMin = t;
1266 }
1267 }
1268 }
1269
1270 p = p->next;
1271 } while( p != stop );
1272
1273 return m;
1274 }
1275
1279 static double triArea( double ax, double ay, double bx, double by,
1280 double cx, double cy )
1281 {
1282 return ( bx - ax ) * ( cy - ay ) - ( by - ay ) * ( cx - ax );
1283 }
1284
1289 bool sectorContainsSector( const VERTEX* m, const VERTEX* p ) const
1290 {
1291 return area( m->prev, m, p->prev ) < 0 && area( p->next, m, m->next ) < 0;
1292 }
1293
1297 void subdividePolygon( VERTEX* aStart, int pass = 0 )
1298 {
1299 VERTEX* p = aStart;
1300
1301 struct VertexComparator {
1302 bool operator()(const std::pair<VERTEX*,double>& a, const std::pair<VERTEX*,double>& b) const {
1303 return a.second > b.second;
1304 }
1305 };
1306
1307 std::set<std::pair<VERTEX*,double>, VertexComparator> longest;
1308 double avg = 0.0;
1309
1310 do
1311 {
1312 double len = ( p->x - p->next->x ) * ( p->x - p->next->x ) +
1313 ( p->y - p->next->y ) * ( p->y - p->next->y );
1314 longest.emplace( p, len );
1315
1316 avg += len;
1317 p = p->next;
1318 } while (p != aStart);
1319
1320 avg /= longest.size();
1321 wxLogTrace( TRIANGULATE_TRACE, "Average length: %f", avg );
1322
1323 constexpr double kSubdivideThresholdFactor = 1.1;
1324 const double subdivideThreshold = avg * kSubdivideThresholdFactor;
1325
1326 for( auto it = longest.begin(); it != longest.end() && it->second > subdivideThreshold;
1327 ++it )
1328 {
1329 wxLogTrace( TRIANGULATE_TRACE, "Subdividing edge with length %f", it->second );
1330 VERTEX* a = it->first;
1331 VERTEX* b = a->next;
1332 VERTEX* last = a;
1333
1334 // We adjust the number of divisions based on the pass in order to progressively
1335 // subdivide the polygon when triangulation fails
1336 int divisions = avg / it->second + 2 + pass;
1337 double step = 1.0 / divisions;
1338
1339 for( int i = 1; i < divisions; i++ )
1340 {
1341 double x = a->x * ( 1.0 - step * i ) + b->x * ( step * i );
1342 double y = a->y * ( 1.0 - step * i ) + b->y * ( step * i );
1343 last = insertTriVertex( VECTOR2I( x, y ), last );
1344 }
1345 }
1346
1347 // update z-order of the vertices
1348 aStart->updateList();
1349 }
1350
1357 bool splitPolygon( VERTEX* start, int aPass )
1358 {
1359 VERTEX* origPoly = start;
1360
1361 // If we have fewer than 4 points, we cannot split the polygon
1362 if( !start || !start->next || start->next == start->prev
1363 || start->next->next == start->prev )
1364 {
1365 return true;
1366 }
1367
1368 // Our first attempts to split the polygon will be at overlapping points.
1369 // These are natural split points and we only need to switch the loop directions
1370 // to generate two new loops. Since they are overlapping, we are do not
1371 // need to create a new segment to disconnect the two loops.
1372 do
1373 {
1374 std::vector<VERTEX*> overlapPoints;
1375 VERTEX* z_pt = origPoly;
1376
1377 while ( z_pt->prevZ && *z_pt->prevZ == *origPoly )
1378 z_pt = z_pt->prevZ;
1379
1380 overlapPoints.push_back( z_pt );
1381
1382 while( z_pt->nextZ && *z_pt->nextZ == *origPoly )
1383 {
1384 z_pt = z_pt->nextZ;
1385 overlapPoints.push_back( z_pt );
1386 }
1387
1388 if( overlapPoints.size() != 2 || overlapPoints[0]->next == overlapPoints[1]
1389 || overlapPoints[0]->prev == overlapPoints[1] )
1390 {
1391 origPoly = origPoly->next;
1392 continue;
1393 }
1394
1395 if( overlapPoints[0]->area( overlapPoints[1] ) < 0 || overlapPoints[1]->area( overlapPoints[0] ) < 0 )
1396 {
1397 wxLogTrace( TRIANGULATE_TRACE, "Split generated a hole, skipping" );
1398 origPoly = origPoly->next;
1399 continue;
1400 }
1401
1402 wxLogTrace( TRIANGULATE_TRACE, "Splitting at overlap point %f, %f", overlapPoints[0]->x, overlapPoints[0]->y );
1403 std::swap( overlapPoints[0]->next, overlapPoints[1]->next );
1404 overlapPoints[0]->next->prev = overlapPoints[0];
1405 overlapPoints[1]->next->prev = overlapPoints[1];
1406
1407 overlapPoints[0]->updateList();
1408 overlapPoints[1]->updateList();
1409 logVertices( overlapPoints[0], nullptr );
1410 logVertices( overlapPoints[1], nullptr );
1411 bool retval = earcutList( overlapPoints[0], aPass )
1412 && earcutList( overlapPoints[1], aPass );
1413
1414 wxLogTrace( TRIANGULATE_TRACE, "%s at first overlap split", retval ? "Success" : "Failed" );
1415 return retval;
1416
1417
1418 } while ( origPoly != start );
1419
1420 // If we've made it through the split algorithm and we still haven't found a
1421 // set of overlapping points, we need to create a new segment to split the polygon
1422 // into two separate polygons. We do this by finding the two vertices that form
1423 // a valid line (does not cross the existing polygon)
1424 do
1425 {
1426 VERTEX* marker = origPoly->next->next;
1427
1428 while( marker != origPoly->prev )
1429 {
1430 // Find a diagonal line that is wholly enclosed by the polygon interior
1431 if( origPoly->next && origPoly->i != marker->i && goodSplit( origPoly, marker ) )
1432 {
1433 VERTEX* newPoly = origPoly->split( marker );
1434
1435 origPoly->updateList();
1436 newPoly->updateList();
1437
1438 bool retval = earcutList( origPoly, aPass ) && earcutList( newPoly, aPass );
1439
1440 wxLogTrace( TRIANGULATE_TRACE, "%s at split", retval ? "Success" : "Failed" );
1441 return retval;
1442 }
1443
1444 marker = marker->next;
1445 }
1446
1447 origPoly = origPoly->next;
1448 } while( origPoly != start );
1449
1450 wxLogTrace( TRIANGULATE_TRACE, "Could not find a valid split point" );
1451 return clipRemainingEars( start );
1452 }
1453
1460 {
1461 VERTEX* p = aStart;
1462 VERTEX* stop = aStart;
1463
1464 while( p->prev != p->next )
1465 {
1466 if( p->isEar() )
1467 {
1468 VERTEX* next = p->next;
1469
1470 m_result.AddTriangle( p->prev->i, p->i, next->i );
1471 p->remove();
1472 p = next;
1473 stop = next;
1474 continue;
1475 }
1476
1477 p = p->next;
1478
1479 if( p == stop )
1480 return false;
1481 }
1482
1483 return true;
1484 }
1485
1495 bool goodSplit( const VERTEX* a, const VERTEX* b ) const
1496 {
1497 bool a_on_edge = ( a->nextZ && *a == *a->nextZ ) || ( a->prevZ && *a == *a->prevZ );
1498 bool b_on_edge = ( b->nextZ && *b == *b->nextZ ) || ( b->prevZ && *b == *b->prevZ );
1499 bool no_intersect = a->next->i != b->i && a->prev->i != b->i && !intersectsPolygon( a, b );
1500 bool local_split = locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b );
1501 bool same_dir = area( a->prev, a, b->prev ) != 0.0 || area( a, b->prev, b ) != 0.0;
1502 bool has_len = ( *a == *b ) && area( a->prev, a, a->next ) > 0 && area( b->prev, b, b->next ) > 0;
1503 bool pos_area = a->area( b ) > 0 && b->area( a ) > 0;
1504
1505 return no_intersect && local_split && ( same_dir || has_len ) && !a_on_edge && !b_on_edge && pos_area;
1506
1507 }
1508
1509
1510 constexpr int sign( double aVal ) const
1511 {
1512 return ( aVal > 0 ) - ( aVal < 0 );
1513 }
1514
1518 constexpr bool overlapping( const VERTEX* p, const VERTEX* q, const VERTEX* r ) const
1519 {
1520 return q->x <= std::max( p->x, r->x ) &&
1521 q->x >= std::min( p->x, r->x ) &&
1522 q->y <= std::max( p->y, r->y ) &&
1523 q->y >= std::min( p->y, r->y );
1524 }
1525
1531 bool intersects( const VERTEX* p1, const VERTEX* q1, const VERTEX* p2, const VERTEX* q2 ) const
1532 {
1533 int sign1 = sign( area( p1, q1, p2 ) );
1534 int sign2 = sign( area( p1, q1, q2 ) );
1535 int sign3 = sign( area( p2, q2, p1 ) );
1536 int sign4 = sign( area( p2, q2, q1 ) );
1537
1538 if( sign1 != sign2 && sign3 != sign4 )
1539 return true;
1540
1541 if( sign1 == 0 && overlapping( p1, p2, q1 ) )
1542 return true;
1543
1544 if( sign2 == 0 && overlapping( p1, q2, q1 ) )
1545 return true;
1546
1547 if( sign3 == 0 && overlapping( p2, p1, q2 ) )
1548 return true;
1549
1550 if( sign4 == 0 && overlapping( p2, q1, q2 ) )
1551 return true;
1552
1553
1554 return false;
1555 }
1556
1563 bool intersectsPolygon( const VERTEX* a, const VERTEX* b ) const
1564 {
1565 for( size_t ii = 0; ii < m_vertices_original_size; ii++ )
1566 {
1567 const VERTEX* p = &m_vertices[ii];
1568 const VERTEX* q = &m_vertices[( ii + 1 ) % m_vertices_original_size];
1569
1570 if( p->i == a->i || p->i == b->i || q->i == a->i || q->i == b->i )
1571 continue;
1572
1573 if( intersects( p, q, a, b ) )
1574 return true;
1575 }
1576
1577 return false;
1578 }
1579
1587 {
1588 m_result.AddVertex( pt );
1589 return insertVertex( m_result.GetVertexCount() - 1, pt, last );
1590 }
1591
1592private:
1595};
1596
1597#endif //__POLYGON_TRIANGULATION_H
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
constexpr coord_type GetY() const
Definition box2.h:205
constexpr size_type GetWidth() const
Definition box2.h:211
constexpr coord_type GetX() const
Definition box2.h:204
constexpr size_type GetHeight() const
Definition box2.h:212
constexpr coord_type GetRight() const
Definition box2.h:214
constexpr coord_type GetBottom() const
Definition box2.h:219
SHAPE_POLY_SET::TRIANGULATED_POLYGON & m_result
bool earcutRing(VERTEX *aPoint, int pass)
VERTEX * createRing(const SHAPE_LINE_CHAIN &aPoints, int aBaseIndex, bool aWantCCW)
Create a VERTEX linked list from a SHAPE_LINE_CHAIN with a global index offset.
bool isUnindexedReflex(const VERTEX *aPoint) const
bool intersects(const VERTEX *p1, const VERTEX *q1, const VERTEX *p2, const VERTEX *q2) const
Check for intersection between two segments, end points included.
VERTEX * eliminateHoles(VERTEX *aOuterRing, std::vector< VERTEX * > &aHoleRings)
Bridge all hole rings into the outer ring by sorting holes left-to-right and connecting each hole's l...
friend struct POLYGON_TRIANGULATION_TEST_ACCESS
constexpr int sign(double aVal) const
static double triArea(double ax, double ay, double bx, double by, double cx, double cy)
Signed area of triangle (ax,ay), (bx,by), (cx,cy).
VERTEX * decimateList(VERTEX *aStart)
Replace near-collinear runs with a single chord.
bool splitPolygon(VERTEX *start, int aPass)
If we cannot find an ear to slice in the current polygon list, we use this to split the polygon into ...
void logVertices(VERTEX *aStart, std::set< VERTEX * > *aSeen)
bool TesselatePolygon(const SHAPE_POLY_SET::POLYGON &aPolygon, SHAPE_POLY_SET::TRIANGULATED_POLYGON *aHintData)
Triangulate a polygon with holes by bridging holes directly into the outer ring's VERTEX linked list,...
VERTEX * insertTriVertex(const VECTOR2I &pt, VERTEX *last)
Create an entry in the vertices lookup and optionally inserts the newly created vertex into an existi...
bool goodSplit(const VERTEX *a, const VERTEX *b) const
Check if a segment joining two vertices lies fully inside the polygon.
double earScore(const VERTEX *a, const VERTEX *b, const VERTEX *c) const
std::vector< SHAPE_LINE_CHAIN > partitionPolygonBalanced(const SHAPE_LINE_CHAIN &aPoly, size_t aTargetLeaves) const
VERTEX * simplifyList(VERTEX *aStart)
Simplify the line chain by removing points that are too close to each other.
POLYGON_TRIANGULATION(SHAPE_POLY_SET::TRIANGULATED_POLYGON &aResult)
bool collectScanlineHits(const SHAPE_LINE_CHAIN &aPoly, bool aVertical, int aCut, std::array< SCANLINE_HIT, 2 > &aHits) const
constexpr bool overlapping(const VERTEX *p, const VERTEX *q, const VERTEX *r) const
If p, q, and r are collinear and r lies between p and q, then return true.
bool splitPolygonBalanced(const SHAPE_LINE_CHAIN &aPoly, std::array< SHAPE_LINE_CHAIN, 2 > &aChildren) const
void logRemaining()
Outputs a list of vertices that have not yet been triangulated.
VERTEX * removeNullTriangles(VERTEX *aStart)
Iterate through the list to remove NULL triangles if they exist.
bool intersectsPolygon(const VERTEX *a, const VERTEX *b) const
Check whether the segment from vertex a -> vertex b crosses any of the segments of the polygon of whi...
bool isTooSmall(const VERTEX *aPoint) const
Check whether a given vertex is too small to matter.
bool earcutList(VERTEX *aPoint, int pass=0)
Walk through a circular linked list starting at aPoint.
bool splitPolygonAtCoordinate(const SHAPE_LINE_CHAIN &aPoly, bool aVertical, int aCut, std::array< SHAPE_LINE_CHAIN, 2 > &aChildren, double &aAreaA, double &aAreaB) const
size_t suggestedPartitionLeafCount(const SHAPE_LINE_CHAIN &aPoly) const
std::vector< double > PartitionAreaFractionsForTesting(const SHAPE_LINE_CHAIN &aPoly, size_t aTargetLeaves) const
bool sectorContainsSector(const VERTEX *m, const VERTEX *p) const
Whether sector in vertex m contains sector in vertex p in the same coordinate frame.
SHAPE_LINE_CHAIN createSplitChild(const SHAPE_LINE_CHAIN &aPoly, int aStart, int aEnd) const
void subdividePolygon(VERTEX *aStart, int pass=0)
Inserts a new vertex halfway between each existing pair of vertices.
VERTEX * filterPoints(VERTEX *aStart, VERTEX *aEnd=nullptr)
Remove consecutive duplicate vertices from the linked list.
bool TesselatePolygon(const SHAPE_LINE_CHAIN &aPoly, SHAPE_POLY_SET::TRIANGULATED_POLYGON *aHintData)
VERTEX * findHoleBridge(VERTEX *aHole, VERTEX *aOuterStart)
Find a vertex on the outer ring visible from the hole's leftmost vertex by casting a horizontal ray t...
bool clipRemainingEars(VERTEX *aStart)
Last resort for a ring that can be neither ear clipped nor split, typically a hatch sliver whose ears...
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
int Split(const VECTOR2I &aP, bool aExact=false)
Insert the point aP belonging to one of the our segments, splitting the adjacent segment in two.
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
int PointCount() const
Return the number of points (vertices) in this line chain.
double Area(bool aAbsolute=true) const
Return the area of this chain.
SHAPE_LINE_CHAIN & Simplify2(bool aRemoveColinear=true)
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
int Find(const VECTOR2I &aP, int aThreshold=0) const
Search for point aP.
const std::vector< VECTOR2I > & CPoints() const
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
const std::deque< VECTOR2I > & Vertices() const
const std::deque< TRI > & Triangles() const
std::vector< SHAPE_LINE_CHAIN > POLYGON
represents a single polygon outline with holes.
constexpr extended_type SquaredEuclideanNorm() const
Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
Definition vector2d.h:312
constexpr extended_type SquaredDistance(const VECTOR2< T > &aVector) const
Compute the squared distance between two vectors.
Definition vector2d.h:582
std::deque< VERTEX > m_vertices
Definition vertex_set.h:366
friend class VERTEX
Definition vertex_set.h:261
bool middleInside(const VERTEX *a, const VERTEX *b) const
Check if the middle of the segment from a to b is inside the polygon.
VERTEX * createList(const SHAPE_LINE_CHAIN &points, VERTEX *aTail=nullptr, void *aUserData=nullptr)
Create a list of vertices from a line chain.
bool locallyInside(const VERTEX *a, const VERTEX *b) const
Check whether the segment from vertex a -> vertex b is inside the polygon around the immediate area o...
BOX2I m_bbox
Definition vertex_set.h:365
void buildReflexIndex(VERTEX *aStart)
Index the reflex vertices of the ring containing aStart in z-order.
double area(const VERTEX *p, const VERTEX *q, const VERTEX *r) const
Return the twice the signed area of the triangle formed by vertices p, q, and r.
VERTEX * insertVertex(int aIndex, const VECTOR2I &pt, VERTEX *last, void *aUserData=nullptr)
Insert a vertex into the vertex set.
VERTEX_SET(int aSimplificationLevel)
Definition vertex_set.h:264
VECTOR2I::extended_type m_simplificationLevel
Definition vertex_set.h:367
uint32_t zOrder(const double aX, const double aY) const
Note that while the inputs are doubles, these are scaled by the size of the bounding box to fit into ...
bool m_reflexIndexValid
Definition vertex_set.h:369
VERTEX * split(VERTEX *b)
Split the referenced polygon between the reference point and vertex b, assuming they are in the same ...
const double x
Definition vertex_set.h:235
VERTEX * next
Definition vertex_set.h:241
VERTEX * prevZ
Definition vertex_set.h:247
void updateList()
After inserting or changing nodes, this function should be called to remove duplicate vertices and en...
Definition vertex_set.h:118
bool inTriangle(const VERTEX &a, const VERTEX &b, const VERTEX &c)
Check to see if triangle surrounds our current vertex.
Definition vertex_set.h:191
VERTEX * nextZ
Definition vertex_set.h:248
VERTEX * prev
Definition vertex_set.h:240
const int i
Definition vertex_set.h:234
void remove()
Remove the node from the linked list and z-ordered linked list.
Definition vertex_set.h:81
bool m_inReflexIndex
Definition vertex_set.h:256
double area(const VERTEX *aEnd=nullptr) const
Returns the signed area of the polygon connected to the current vertex, optionally ending at a specif...
Definition vertex_set.h:202
uint32_t z
Definition vertex_set.h:244
bool isEar(bool aMatchUserData=false) const
Check whether the given vertex is in the middle of an ear.
const double y
Definition vertex_set.h:236
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
#define TRIANGULATE_TRACE
#define TRIANGULATEMINIMUMAREA
#define TRIANGULATEDELAUNAYREFINE
#define TRIANGULATESIMPLIFICATIONLEVEL
static bool triangulationRefineEnabled()
Whether the sliver-minimizing flip post-pass runs, from the TriangulateDelaunayRefine setting.
CITER next(CITER it)
Definition ptree.cpp:120
const SHAPE_LINE_CHAIN chain
VECTOR2I end
int delta
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707
VECTOR2< int64_t > VECTOR2L
Definition vector2d.h:709