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 constexpr int kMaxRecursion = 64;
811
812 if( pass >= kMaxRecursion )
813 {
814 wxLogTrace( TRIANGULATE_TRACE, "earcutList recursion limit reached; aborting triangulation", pass );
815 return false;
816 }
817
818 wxLogTrace( TRIANGULATE_TRACE, "earcutList starting at %p for pass %d", aPoint, pass );
819
820 if( !aPoint )
821 return true;
822
823 VERTEX* stop = aPoint;
824 VERTEX* prev;
825 VERTEX* next;
826 int internal_pass = 1;
827 constexpr int kEarLookahead = 2;
828
829 while( aPoint->prev != aPoint->next )
830 {
831 prev = aPoint->prev;
832 next = aPoint->next;
833
834 VERTEX* bestEar = nullptr;
835 double bestScore = -1.0;
836 int lookahead = 0;
837
838 for( VERTEX* candidate = aPoint; candidate && lookahead < kEarLookahead;
839 candidate = candidate->next, ++lookahead )
840 {
841 if( !candidate->isEar() || isTooSmall( candidate ) )
842 continue;
843
844 const double score = earScore( candidate->prev, candidate, candidate->next );
845
846 if( !bestEar || score > bestScore )
847 {
848 bestEar = candidate;
849 bestScore = score;
850 }
851 }
852
853 if( bestEar )
854 {
855 prev = bestEar->prev;
856 next = bestEar->next;
857 m_result.AddTriangle( prev->i, bestEar->i, next->i );
858 bestEar->remove();
859
860 // Skip one vertex as the triangle will account for the prev node
861 aPoint = next->next;
862 stop = next->next;
863 continue;
864 }
865
866 VERTEX* nextNext = next->next;
867
868 if( *prev != *nextNext && intersects( prev, aPoint, next, nextNext ) &&
869 locallyInside( prev, nextNext ) &&
870 locallyInside( nextNext, prev ) )
871 {
872 wxLogTrace( TRIANGULATE_TRACE,
873 "Local intersection detected. Merging minor triangle with area %f",
874 area( prev, aPoint, nextNext ) );
875 m_result.AddTriangle( prev->i, aPoint->i, nextNext->i );
876
877 // remove two nodes involved
878 next->remove();
879 aPoint->remove();
880
881 aPoint = nextNext;
882 stop = nextNext;
883
884 continue;
885 }
886
887 aPoint = next;
888
889 /*
890 * We've searched the entire polygon for available ears and there are still
891 * un-sliced nodes remaining.
892 */
893 if( aPoint == stop && aPoint->prev != aPoint->next )
894 {
895 VERTEX* newPoint;
896
897 // Removing null triangles will remove steiner points as well as colinear points
898 // that are three in a row. Because our next step is to subdivide the polygon,
899 // we need to allow it to add the subdivided points first. This is why we only
900 // run the RemoveNullTriangles function after the first pass.
901 if( ( internal_pass == 2 ) && ( newPoint = removeNullTriangles( aPoint ) ) )
902 {
903 // There are no remaining triangles in the list
904 if( newPoint->next == newPoint->prev )
905 break;
906
907 aPoint = newPoint;
908 stop = newPoint;
909 continue;
910 }
911
912 ++internal_pass;
913
914 // This will subdivide the polygon 2 times. The first pass will add enough points
915 // such that each edge is less than the average edge length. If this doesn't work
916 // The next pass will remove the null triangles (above) and subdivide the polygon
917 // again, this time adding one point to each long edge (and thereby changing the locations)
918 if( internal_pass < 4 )
919 {
920 wxLogTrace( TRIANGULATE_TRACE, "Subdividing polygon" );
921 subdividePolygon( aPoint, internal_pass );
922 continue;
923 }
924
925 // If we don't have any NULL triangles left, cut the polygon in two and try again
926 wxLogTrace( TRIANGULATE_TRACE, "Splitting polygon" );
927
928 if( !splitPolygon( aPoint, pass + 1 ) )
929 return false;
930
931 break;
932 }
933 }
934
935 // Check to see if we are left with only three points in the polygon
936 if( aPoint->next && aPoint->prev == aPoint->next->next )
937 {
938 // Three concave points will never be able to be triangulated because they were
939 // created by an intersecting polygon, so just drop them.
940 if( area( aPoint->prev, aPoint, aPoint->next ) >= 0 )
941 return true;
942 }
943
944 /*
945 * At this point, our polygon should be fully tessellated.
946 */
947 if( aPoint->prev != aPoint->next )
948 return std::abs( aPoint->area() ) > TRIANGULATEMINIMUMAREA;
949
950 return true;
951 }
952
953
957
958 bool isTooSmall( const VERTEX* aPoint ) const
959 {
960 double min_area = TRIANGULATEMINIMUMAREA;
961 double prev_sq_len = ( aPoint->prev->x - aPoint->x ) * ( aPoint->prev->x - aPoint->x ) +
962 ( aPoint->prev->y - aPoint->y ) * ( aPoint->prev->y - aPoint->y );
963 double next_sq_len = ( aPoint->next->x - aPoint->x ) * ( aPoint->next->x - aPoint->x ) +
964 ( aPoint->next->y - aPoint->y ) * ( aPoint->next->y - aPoint->y );
965 double opp_sq_len = ( aPoint->next->x - aPoint->prev->x ) * ( aPoint->next->x - aPoint->prev->x ) +
966 ( aPoint->next->y - aPoint->prev->y ) * ( aPoint->next->y - aPoint->prev->y );
967
968 return ( prev_sq_len < min_area || next_sq_len < min_area || opp_sq_len < min_area );
969 }
970
971 double earScore( const VERTEX* a, const VERTEX* b, const VERTEX* c ) const
972 {
973 const double ab_sq = ( a->x - b->x ) * ( a->x - b->x ) + ( a->y - b->y ) * ( a->y - b->y );
974 const double bc_sq = ( b->x - c->x ) * ( b->x - c->x ) + ( b->y - c->y ) * ( b->y - c->y );
975 const double ca_sq = ( c->x - a->x ) * ( c->x - a->x ) + ( c->y - a->y ) * ( c->y - a->y );
976 const double norm = ab_sq + bc_sq + ca_sq;
977
978 if( norm <= 0.0 )
979 return 0.0;
980
981 return std::abs( area( a, b, c ) ) / norm;
982 }
983
988 VERTEX* createRing( const SHAPE_LINE_CHAIN& aPoints, int aBaseIndex, bool aWantCCW )
989 {
990 VERTEX* tail = nullptr;
991 double sum = 0.0;
992 VECTOR2L last_pt;
993 bool first = true;
994
995 for( int i = 0; i < aPoints.PointCount(); i++ )
996 {
997 VECTOR2D p1 = aPoints.CPoint( i );
998 VECTOR2D p2 = aPoints.CPoint( ( i + 1 ) % aPoints.PointCount() );
999 sum += ( ( p2.x - p1.x ) * ( p2.y + p1.y ) );
1000 }
1001
1002 bool isCW = sum > 0.0;
1003 bool needReverse = ( aWantCCW == isCW );
1004
1005 auto addVertex = [&]( int i )
1006 {
1007 const VECTOR2I& pt = aPoints.CPoint( i );
1008
1009 if( first || pt.SquaredDistance( last_pt ) > m_simplificationLevel )
1010 {
1011 tail = insertVertex( aBaseIndex + i, pt, tail );
1012 last_pt = pt;
1013 first = false;
1014 }
1015 };
1016
1017 if( needReverse )
1018 {
1019 for( int i = aPoints.PointCount() - 1; i >= 0; i-- )
1020 addVertex( i );
1021 }
1022 else
1023 {
1024 for( int i = 0; i < aPoints.PointCount(); i++ )
1025 addVertex( i );
1026 }
1027
1028 // Collapse a final duplicate, but never on a single-vertex ring. When the
1029 // simplification pass leaves only one vertex, tail->next == tail and removing
1030 // it would leave the caller holding a vertex with null next/prev pointers.
1031 if( tail && tail->next != tail && ( *tail == *tail->next ) )
1032 tail->next->remove();
1033
1034 return tail;
1035 }
1036
1042 VERTEX* eliminateHoles( VERTEX* aOuterRing, std::vector<VERTEX*>& aHoleRings )
1043 {
1044 struct HoleInfo
1045 {
1046 VERTEX* leftmost;
1047 double leftX;
1048 };
1049
1050 std::vector<HoleInfo> holes;
1051 holes.reserve( aHoleRings.size() );
1052
1053 for( VERTEX* hole : aHoleRings )
1054 {
1055 VERTEX* leftmost = hole;
1056 VERTEX* p = hole->next;
1057
1058 if( !p )
1059 {
1060 wxFAIL;
1061 break;
1062 }
1063
1064 while( p != hole )
1065 {
1066 if( p->x < leftmost->x || ( p->x == leftmost->x && p->y < leftmost->y ) )
1067 leftmost = p;
1068
1069 p = p->next;
1070
1071 if( !p )
1072 {
1073 wxFAIL;
1074 break;
1075 }
1076 }
1077
1078 holes.push_back( { leftmost, leftmost->x } );
1079 }
1080
1081 std::sort( holes.begin(), holes.end(),
1082 []( const HoleInfo& a, const HoleInfo& b ) { return a.leftX < b.leftX; } );
1083
1084 for( const HoleInfo& hi : holes )
1085 {
1086 VERTEX* bridge = findHoleBridge( hi.leftmost, aOuterRing );
1087
1088 if( bridge )
1089 {
1090 VERTEX* bridgeReverse = bridge->split( hi.leftmost );
1091 filterPoints( bridgeReverse, bridgeReverse->next );
1092 aOuterRing = filterPoints( bridge, bridge->next );
1093 }
1094 else
1095 {
1096 wxLogTrace( TRIANGULATE_TRACE, "Failed to find bridge for hole at (%f, %f)",
1097 hi.leftmost->x, hi.leftmost->y );
1098 }
1099 }
1100
1101 return aOuterRing;
1102 }
1103
1107 VERTEX* filterPoints( VERTEX* aStart, VERTEX* aEnd = nullptr )
1108 {
1109 if( !aStart )
1110 return aStart;
1111
1112 if( !aEnd )
1113 aEnd = aStart;
1114
1115 VERTEX* p = aStart;
1116 bool again;
1117
1118 do
1119 {
1120 again = false;
1121
1122 if( *p == *p->next )
1123 {
1124 VERTEX* toRemove = p->next;
1125
1126 if( toRemove == aEnd )
1127 aEnd = p;
1128
1129 toRemove->remove();
1130
1131 if( p == p->next )
1132 return p;
1133
1134 p = p->prev;
1135 again = true;
1136 }
1137 else
1138 {
1139 p = p->next;
1140 }
1141 } while( again || p != aEnd );
1142
1143 return aEnd;
1144 }
1145
1146
1151 VERTEX* findHoleBridge( VERTEX* aHole, VERTEX* aOuterStart )
1152 {
1153 VERTEX* p = aOuterStart;
1154 double hx = aHole->x;
1155 double hy = aHole->y;
1156 double qx = -std::numeric_limits<double>::infinity();
1157 VERTEX* m = nullptr;
1158
1159 do
1160 {
1161 if( hy <= p->y && hy >= p->next->y && p->next->y != p->y )
1162 {
1163 double x = p->x + ( hy - p->y ) * ( p->next->x - p->x )
1164 / ( p->next->y - p->y );
1165
1166 if( x <= hx && x > qx )
1167 {
1168 qx = x;
1169
1170 if( x == hx )
1171 {
1172 if( hy == p->y )
1173 return p;
1174
1175 if( hy == p->next->y )
1176 return p->next;
1177 }
1178
1179 m = ( p->x < p->next->x ) ? p : p->next;
1180 }
1181 }
1182
1183 p = p->next;
1184 } while( p != aOuterStart );
1185
1186 if( !m )
1187 return nullptr;
1188
1189 if( hx == qx )
1190 return m;
1191
1192 // Pick the vertex inside the visibility triangle closest to the ray
1193 const VERTEX* stop = m;
1194 double mx = m->x;
1195 double my = m->y;
1196 double tanMin = std::numeric_limits<double>::infinity();
1197
1198 p = m;
1199
1200 do
1201 {
1202 if( hx >= p->x && p->x >= mx && hx != p->x )
1203 {
1204 bool inside;
1205
1206 if( hy < my )
1207 inside = triArea( hx, hy, mx, my, p->x, p->y ) >= 0
1208 && triArea( mx, my, qx, hy, p->x, p->y ) >= 0
1209 && triArea( qx, hy, hx, hy, p->x, p->y ) >= 0;
1210 else
1211 inside = triArea( qx, hy, mx, my, p->x, p->y ) >= 0
1212 && triArea( mx, my, hx, hy, p->x, p->y ) >= 0
1213 && triArea( hx, hy, qx, hy, p->x, p->y ) >= 0;
1214
1215 if( inside )
1216 {
1217 double t = std::abs( hy - p->y ) / ( hx - p->x );
1218
1219 if( locallyInside( p, aHole )
1220 && ( t < tanMin
1221 || ( t == tanMin
1222 && ( p->x > m->x
1223 || ( p->x == m->x
1224 && sectorContainsSector( m, p ) ) ) ) ) )
1225 {
1226 m = p;
1227 tanMin = t;
1228 }
1229 }
1230 }
1231
1232 p = p->next;
1233 } while( p != stop );
1234
1235 return m;
1236 }
1237
1241 static double triArea( double ax, double ay, double bx, double by,
1242 double cx, double cy )
1243 {
1244 return ( bx - ax ) * ( cy - ay ) - ( by - ay ) * ( cx - ax );
1245 }
1246
1251 bool sectorContainsSector( const VERTEX* m, const VERTEX* p ) const
1252 {
1253 return area( m->prev, m, p->prev ) < 0 && area( p->next, m, m->next ) < 0;
1254 }
1255
1259 void subdividePolygon( VERTEX* aStart, int pass = 0 )
1260 {
1261 VERTEX* p = aStart;
1262
1263 struct VertexComparator {
1264 bool operator()(const std::pair<VERTEX*,double>& a, const std::pair<VERTEX*,double>& b) const {
1265 return a.second > b.second;
1266 }
1267 };
1268
1269 std::set<std::pair<VERTEX*,double>, VertexComparator> longest;
1270 double avg = 0.0;
1271
1272 do
1273 {
1274 double len = ( p->x - p->next->x ) * ( p->x - p->next->x ) +
1275 ( p->y - p->next->y ) * ( p->y - p->next->y );
1276 longest.emplace( p, len );
1277
1278 avg += len;
1279 p = p->next;
1280 } while (p != aStart);
1281
1282 avg /= longest.size();
1283 wxLogTrace( TRIANGULATE_TRACE, "Average length: %f", avg );
1284
1285 constexpr double kSubdivideThresholdFactor = 1.1;
1286 const double subdivideThreshold = avg * kSubdivideThresholdFactor;
1287
1288 for( auto it = longest.begin(); it != longest.end() && it->second > subdivideThreshold;
1289 ++it )
1290 {
1291 wxLogTrace( TRIANGULATE_TRACE, "Subdividing edge with length %f", it->second );
1292 VERTEX* a = it->first;
1293 VERTEX* b = a->next;
1294 VERTEX* last = a;
1295
1296 // We adjust the number of divisions based on the pass in order to progressively
1297 // subdivide the polygon when triangulation fails
1298 int divisions = avg / it->second + 2 + pass;
1299 double step = 1.0 / divisions;
1300
1301 for( int i = 1; i < divisions; i++ )
1302 {
1303 double x = a->x * ( 1.0 - step * i ) + b->x * ( step * i );
1304 double y = a->y * ( 1.0 - step * i ) + b->y * ( step * i );
1305 last = insertTriVertex( VECTOR2I( x, y ), last );
1306 }
1307 }
1308
1309 // update z-order of the vertices
1310 aStart->updateList();
1311 }
1312
1319 bool splitPolygon( VERTEX* start, int aPass )
1320 {
1321 VERTEX* origPoly = start;
1322
1323 // If we have fewer than 4 points, we cannot split the polygon
1324 if( !start || !start->next || start->next == start->prev
1325 || start->next->next == start->prev )
1326 {
1327 return true;
1328 }
1329
1330 // Our first attempts to split the polygon will be at overlapping points.
1331 // These are natural split points and we only need to switch the loop directions
1332 // to generate two new loops. Since they are overlapping, we are do not
1333 // need to create a new segment to disconnect the two loops.
1334 do
1335 {
1336 std::vector<VERTEX*> overlapPoints;
1337 VERTEX* z_pt = origPoly;
1338
1339 while ( z_pt->prevZ && *z_pt->prevZ == *origPoly )
1340 z_pt = z_pt->prevZ;
1341
1342 overlapPoints.push_back( z_pt );
1343
1344 while( z_pt->nextZ && *z_pt->nextZ == *origPoly )
1345 {
1346 z_pt = z_pt->nextZ;
1347 overlapPoints.push_back( z_pt );
1348 }
1349
1350 if( overlapPoints.size() != 2 || overlapPoints[0]->next == overlapPoints[1]
1351 || overlapPoints[0]->prev == overlapPoints[1] )
1352 {
1353 origPoly = origPoly->next;
1354 continue;
1355 }
1356
1357 if( overlapPoints[0]->area( overlapPoints[1] ) < 0 || overlapPoints[1]->area( overlapPoints[0] ) < 0 )
1358 {
1359 wxLogTrace( TRIANGULATE_TRACE, "Split generated a hole, skipping" );
1360 origPoly = origPoly->next;
1361 continue;
1362 }
1363
1364 wxLogTrace( TRIANGULATE_TRACE, "Splitting at overlap point %f, %f", overlapPoints[0]->x, overlapPoints[0]->y );
1365 std::swap( overlapPoints[0]->next, overlapPoints[1]->next );
1366 overlapPoints[0]->next->prev = overlapPoints[0];
1367 overlapPoints[1]->next->prev = overlapPoints[1];
1368
1369 overlapPoints[0]->updateList();
1370 overlapPoints[1]->updateList();
1371 logVertices( overlapPoints[0], nullptr );
1372 logVertices( overlapPoints[1], nullptr );
1373 bool retval = earcutList( overlapPoints[0], aPass )
1374 && earcutList( overlapPoints[1], aPass );
1375
1376 wxLogTrace( TRIANGULATE_TRACE, "%s at first overlap split", retval ? "Success" : "Failed" );
1377 return retval;
1378
1379
1380 } while ( origPoly != start );
1381
1382 // If we've made it through the split algorithm and we still haven't found a
1383 // set of overlapping points, we need to create a new segment to split the polygon
1384 // into two separate polygons. We do this by finding the two vertices that form
1385 // a valid line (does not cross the existing polygon)
1386 do
1387 {
1388 VERTEX* marker = origPoly->next->next;
1389
1390 while( marker != origPoly->prev )
1391 {
1392 // Find a diagonal line that is wholly enclosed by the polygon interior
1393 if( origPoly->next && origPoly->i != marker->i && goodSplit( origPoly, marker ) )
1394 {
1395 VERTEX* newPoly = origPoly->split( marker );
1396
1397 origPoly->updateList();
1398 newPoly->updateList();
1399
1400 bool retval = earcutList( origPoly, aPass ) && earcutList( newPoly, aPass );
1401
1402 wxLogTrace( TRIANGULATE_TRACE, "%s at split", retval ? "Success" : "Failed" );
1403 return retval;
1404 }
1405
1406 marker = marker->next;
1407 }
1408
1409 origPoly = origPoly->next;
1410 } while( origPoly != start );
1411
1412 wxLogTrace( TRIANGULATE_TRACE, "Could not find a valid split point" );
1413 return false;
1414 }
1415
1425 bool goodSplit( const VERTEX* a, const VERTEX* b ) const
1426 {
1427 bool a_on_edge = ( a->nextZ && *a == *a->nextZ ) || ( a->prevZ && *a == *a->prevZ );
1428 bool b_on_edge = ( b->nextZ && *b == *b->nextZ ) || ( b->prevZ && *b == *b->prevZ );
1429 bool no_intersect = a->next->i != b->i && a->prev->i != b->i && !intersectsPolygon( a, b );
1430 bool local_split = locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b );
1431 bool same_dir = area( a->prev, a, b->prev ) != 0.0 || area( a, b->prev, b ) != 0.0;
1432 bool has_len = ( *a == *b ) && area( a->prev, a, a->next ) > 0 && area( b->prev, b, b->next ) > 0;
1433 bool pos_area = a->area( b ) > 0 && b->area( a ) > 0;
1434
1435 return no_intersect && local_split && ( same_dir || has_len ) && !a_on_edge && !b_on_edge && pos_area;
1436
1437 }
1438
1439
1440 constexpr int sign( double aVal ) const
1441 {
1442 return ( aVal > 0 ) - ( aVal < 0 );
1443 }
1444
1448 constexpr bool overlapping( const VERTEX* p, const VERTEX* q, const VERTEX* r ) const
1449 {
1450 return q->x <= std::max( p->x, r->x ) &&
1451 q->x >= std::min( p->x, r->x ) &&
1452 q->y <= std::max( p->y, r->y ) &&
1453 q->y >= std::min( p->y, r->y );
1454 }
1455
1461 bool intersects( const VERTEX* p1, const VERTEX* q1, const VERTEX* p2, const VERTEX* q2 ) const
1462 {
1463 int sign1 = sign( area( p1, q1, p2 ) );
1464 int sign2 = sign( area( p1, q1, q2 ) );
1465 int sign3 = sign( area( p2, q2, p1 ) );
1466 int sign4 = sign( area( p2, q2, q1 ) );
1467
1468 if( sign1 != sign2 && sign3 != sign4 )
1469 return true;
1470
1471 if( sign1 == 0 && overlapping( p1, p2, q1 ) )
1472 return true;
1473
1474 if( sign2 == 0 && overlapping( p1, q2, q1 ) )
1475 return true;
1476
1477 if( sign3 == 0 && overlapping( p2, p1, q2 ) )
1478 return true;
1479
1480 if( sign4 == 0 && overlapping( p2, q1, q2 ) )
1481 return true;
1482
1483
1484 return false;
1485 }
1486
1493 bool intersectsPolygon( const VERTEX* a, const VERTEX* b ) const
1494 {
1495 for( size_t ii = 0; ii < m_vertices_original_size; ii++ )
1496 {
1497 const VERTEX* p = &m_vertices[ii];
1498 const VERTEX* q = &m_vertices[( ii + 1 ) % m_vertices_original_size];
1499
1500 if( p->i == a->i || p->i == b->i || q->i == a->i || q->i == b->i )
1501 continue;
1502
1503 if( intersects( p, q, a, b ) )
1504 return true;
1505 }
1506
1507 return false;
1508 }
1509
1517 {
1518 m_result.AddVertex( pt );
1519 return insertVertex( m_result.GetVertexCount() - 1, pt, last );
1520 }
1521
1522private:
1525};
1526
1527#endif //__POLYGON_TRIANGULATION_H
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
constexpr coord_type GetY() const
Definition box2.h:204
constexpr size_type GetWidth() const
Definition box2.h:210
constexpr coord_type GetX() const
Definition box2.h:203
constexpr size_type GetHeight() const
Definition box2.h:211
constexpr coord_type GetRight() const
Definition box2.h:213
constexpr coord_type GetBottom() const
Definition box2.h:218
SHAPE_POLY_SET::TRIANGULATED_POLYGON & m_result
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 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...
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:303
constexpr extended_type SquaredDistance(const VECTOR2< T > &aVector) const
Compute the squared distance between two vectors.
Definition vector2d.h:557
std::deque< VERTEX > m_vertices
Definition vertex_set.h:339
friend class VERTEX
Definition vertex_set.h:251
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:338
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:254
VECTOR2I::extended_type m_simplificationLevel
Definition vertex_set.h:340
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 ...
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:231
VERTEX * next
Definition vertex_set.h:237
VERTEX * prevZ
Definition vertex_set.h:243
void updateList()
After inserting or changing nodes, this function should be called to remove duplicate vertices and en...
Definition vertex_set.h:117
bool inTriangle(const VERTEX &a, const VERTEX &b, const VERTEX &c)
Check to see if triangle surrounds our current vertex.
Definition vertex_set.h:187
VERTEX * nextZ
Definition vertex_set.h:244
VERTEX * prev
Definition vertex_set.h:236
const int i
Definition vertex_set.h:230
void remove()
Remove the node from the linked list and z-ordered linked list.
Definition vertex_set.h:80
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:198
uint32_t z
Definition vertex_set.h:240
const double y
Definition vertex_set.h:232
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
#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:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682
VECTOR2< int64_t > VECTOR2L
Definition vector2d.h:684