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 while( p != hole )
1059 {
1060 if( p->x < leftmost->x || ( p->x == leftmost->x && p->y < leftmost->y ) )
1061 leftmost = p;
1062
1063 p = p->next;
1064 }
1065
1066 holes.push_back( { leftmost, leftmost->x } );
1067 }
1068
1069 std::sort( holes.begin(), holes.end(),
1070 []( const HoleInfo& a, const HoleInfo& b ) { return a.leftX < b.leftX; } );
1071
1072 for( const HoleInfo& hi : holes )
1073 {
1074 VERTEX* bridge = findHoleBridge( hi.leftmost, aOuterRing );
1075
1076 if( bridge )
1077 {
1078 VERTEX* bridgeReverse = bridge->split( hi.leftmost );
1079 filterPoints( bridgeReverse, bridgeReverse->next );
1080 aOuterRing = filterPoints( bridge, bridge->next );
1081 }
1082 else
1083 {
1084 wxLogTrace( TRIANGULATE_TRACE, "Failed to find bridge for hole at (%f, %f)",
1085 hi.leftmost->x, hi.leftmost->y );
1086 }
1087 }
1088
1089 return aOuterRing;
1090 }
1091
1095 VERTEX* filterPoints( VERTEX* aStart, VERTEX* aEnd = nullptr )
1096 {
1097 if( !aStart )
1098 return aStart;
1099
1100 if( !aEnd )
1101 aEnd = aStart;
1102
1103 VERTEX* p = aStart;
1104 bool again;
1105
1106 do
1107 {
1108 again = false;
1109
1110 if( *p == *p->next )
1111 {
1112 VERTEX* toRemove = p->next;
1113
1114 if( toRemove == aEnd )
1115 aEnd = p;
1116
1117 toRemove->remove();
1118
1119 if( p == p->next )
1120 return p;
1121
1122 p = p->prev;
1123 again = true;
1124 }
1125 else
1126 {
1127 p = p->next;
1128 }
1129 } while( again || p != aEnd );
1130
1131 return aEnd;
1132 }
1133
1134
1139 VERTEX* findHoleBridge( VERTEX* aHole, VERTEX* aOuterStart )
1140 {
1141 VERTEX* p = aOuterStart;
1142 double hx = aHole->x;
1143 double hy = aHole->y;
1144 double qx = -std::numeric_limits<double>::infinity();
1145 VERTEX* m = nullptr;
1146
1147 do
1148 {
1149 if( hy <= p->y && hy >= p->next->y && p->next->y != p->y )
1150 {
1151 double x = p->x + ( hy - p->y ) * ( p->next->x - p->x )
1152 / ( p->next->y - p->y );
1153
1154 if( x <= hx && x > qx )
1155 {
1156 qx = x;
1157
1158 if( x == hx )
1159 {
1160 if( hy == p->y )
1161 return p;
1162
1163 if( hy == p->next->y )
1164 return p->next;
1165 }
1166
1167 m = ( p->x < p->next->x ) ? p : p->next;
1168 }
1169 }
1170
1171 p = p->next;
1172 } while( p != aOuterStart );
1173
1174 if( !m )
1175 return nullptr;
1176
1177 if( hx == qx )
1178 return m;
1179
1180 // Pick the vertex inside the visibility triangle closest to the ray
1181 const VERTEX* stop = m;
1182 double mx = m->x;
1183 double my = m->y;
1184 double tanMin = std::numeric_limits<double>::infinity();
1185
1186 p = m;
1187
1188 do
1189 {
1190 if( hx >= p->x && p->x >= mx && hx != p->x )
1191 {
1192 bool inside;
1193
1194 if( hy < my )
1195 inside = triArea( hx, hy, mx, my, p->x, p->y ) >= 0
1196 && triArea( mx, my, qx, hy, p->x, p->y ) >= 0
1197 && triArea( qx, hy, hx, hy, p->x, p->y ) >= 0;
1198 else
1199 inside = triArea( qx, hy, mx, my, p->x, p->y ) >= 0
1200 && triArea( mx, my, hx, hy, p->x, p->y ) >= 0
1201 && triArea( hx, hy, qx, hy, p->x, p->y ) >= 0;
1202
1203 if( inside )
1204 {
1205 double t = std::abs( hy - p->y ) / ( hx - p->x );
1206
1207 if( locallyInside( p, aHole )
1208 && ( t < tanMin
1209 || ( t == tanMin
1210 && ( p->x > m->x
1211 || ( p->x == m->x
1212 && sectorContainsSector( m, p ) ) ) ) ) )
1213 {
1214 m = p;
1215 tanMin = t;
1216 }
1217 }
1218 }
1219
1220 p = p->next;
1221 } while( p != stop );
1222
1223 return m;
1224 }
1225
1229 static double triArea( double ax, double ay, double bx, double by,
1230 double cx, double cy )
1231 {
1232 return ( bx - ax ) * ( cy - ay ) - ( by - ay ) * ( cx - ax );
1233 }
1234
1239 bool sectorContainsSector( const VERTEX* m, const VERTEX* p ) const
1240 {
1241 return area( m->prev, m, p->prev ) < 0 && area( p->next, m, m->next ) < 0;
1242 }
1243
1247 void subdividePolygon( VERTEX* aStart, int pass = 0 )
1248 {
1249 VERTEX* p = aStart;
1250
1251 struct VertexComparator {
1252 bool operator()(const std::pair<VERTEX*,double>& a, const std::pair<VERTEX*,double>& b) const {
1253 return a.second > b.second;
1254 }
1255 };
1256
1257 std::set<std::pair<VERTEX*,double>, VertexComparator> longest;
1258 double avg = 0.0;
1259
1260 do
1261 {
1262 double len = ( p->x - p->next->x ) * ( p->x - p->next->x ) +
1263 ( p->y - p->next->y ) * ( p->y - p->next->y );
1264 longest.emplace( p, len );
1265
1266 avg += len;
1267 p = p->next;
1268 } while (p != aStart);
1269
1270 avg /= longest.size();
1271 wxLogTrace( TRIANGULATE_TRACE, "Average length: %f", avg );
1272
1273 constexpr double kSubdivideThresholdFactor = 1.1;
1274 const double subdivideThreshold = avg * kSubdivideThresholdFactor;
1275
1276 for( auto it = longest.begin(); it != longest.end() && it->second > subdivideThreshold;
1277 ++it )
1278 {
1279 wxLogTrace( TRIANGULATE_TRACE, "Subdividing edge with length %f", it->second );
1280 VERTEX* a = it->first;
1281 VERTEX* b = a->next;
1282 VERTEX* last = a;
1283
1284 // We adjust the number of divisions based on the pass in order to progressively
1285 // subdivide the polygon when triangulation fails
1286 int divisions = avg / it->second + 2 + pass;
1287 double step = 1.0 / divisions;
1288
1289 for( int i = 1; i < divisions; i++ )
1290 {
1291 double x = a->x * ( 1.0 - step * i ) + b->x * ( step * i );
1292 double y = a->y * ( 1.0 - step * i ) + b->y * ( step * i );
1293 last = insertTriVertex( VECTOR2I( x, y ), last );
1294 }
1295 }
1296
1297 // update z-order of the vertices
1298 aStart->updateList();
1299 }
1300
1307 bool splitPolygon( VERTEX* start, int aPass )
1308 {
1309 VERTEX* origPoly = start;
1310
1311 // If we have fewer than 4 points, we cannot split the polygon
1312 if( !start || !start->next || start->next == start->prev
1313 || start->next->next == start->prev )
1314 {
1315 return true;
1316 }
1317
1318 // Our first attempts to split the polygon will be at overlapping points.
1319 // These are natural split points and we only need to switch the loop directions
1320 // to generate two new loops. Since they are overlapping, we are do not
1321 // need to create a new segment to disconnect the two loops.
1322 do
1323 {
1324 std::vector<VERTEX*> overlapPoints;
1325 VERTEX* z_pt = origPoly;
1326
1327 while ( z_pt->prevZ && *z_pt->prevZ == *origPoly )
1328 z_pt = z_pt->prevZ;
1329
1330 overlapPoints.push_back( z_pt );
1331
1332 while( z_pt->nextZ && *z_pt->nextZ == *origPoly )
1333 {
1334 z_pt = z_pt->nextZ;
1335 overlapPoints.push_back( z_pt );
1336 }
1337
1338 if( overlapPoints.size() != 2 || overlapPoints[0]->next == overlapPoints[1]
1339 || overlapPoints[0]->prev == overlapPoints[1] )
1340 {
1341 origPoly = origPoly->next;
1342 continue;
1343 }
1344
1345 if( overlapPoints[0]->area( overlapPoints[1] ) < 0 || overlapPoints[1]->area( overlapPoints[0] ) < 0 )
1346 {
1347 wxLogTrace( TRIANGULATE_TRACE, "Split generated a hole, skipping" );
1348 origPoly = origPoly->next;
1349 continue;
1350 }
1351
1352 wxLogTrace( TRIANGULATE_TRACE, "Splitting at overlap point %f, %f", overlapPoints[0]->x, overlapPoints[0]->y );
1353 std::swap( overlapPoints[0]->next, overlapPoints[1]->next );
1354 overlapPoints[0]->next->prev = overlapPoints[0];
1355 overlapPoints[1]->next->prev = overlapPoints[1];
1356
1357 overlapPoints[0]->updateList();
1358 overlapPoints[1]->updateList();
1359 logVertices( overlapPoints[0], nullptr );
1360 logVertices( overlapPoints[1], nullptr );
1361 bool retval = earcutList( overlapPoints[0], aPass )
1362 && earcutList( overlapPoints[1], aPass );
1363
1364 wxLogTrace( TRIANGULATE_TRACE, "%s at first overlap split", retval ? "Success" : "Failed" );
1365 return retval;
1366
1367
1368 } while ( origPoly != start );
1369
1370 // If we've made it through the split algorithm and we still haven't found a
1371 // set of overlapping points, we need to create a new segment to split the polygon
1372 // into two separate polygons. We do this by finding the two vertices that form
1373 // a valid line (does not cross the existing polygon)
1374 do
1375 {
1376 VERTEX* marker = origPoly->next->next;
1377
1378 while( marker != origPoly->prev )
1379 {
1380 // Find a diagonal line that is wholly enclosed by the polygon interior
1381 if( origPoly->next && origPoly->i != marker->i && goodSplit( origPoly, marker ) )
1382 {
1383 VERTEX* newPoly = origPoly->split( marker );
1384
1385 origPoly->updateList();
1386 newPoly->updateList();
1387
1388 bool retval = earcutList( origPoly, aPass ) && earcutList( newPoly, aPass );
1389
1390 wxLogTrace( TRIANGULATE_TRACE, "%s at split", retval ? "Success" : "Failed" );
1391 return retval;
1392 }
1393
1394 marker = marker->next;
1395 }
1396
1397 origPoly = origPoly->next;
1398 } while( origPoly != start );
1399
1400 wxLogTrace( TRIANGULATE_TRACE, "Could not find a valid split point" );
1401 return false;
1402 }
1403
1413 bool goodSplit( const VERTEX* a, const VERTEX* b ) const
1414 {
1415 bool a_on_edge = ( a->nextZ && *a == *a->nextZ ) || ( a->prevZ && *a == *a->prevZ );
1416 bool b_on_edge = ( b->nextZ && *b == *b->nextZ ) || ( b->prevZ && *b == *b->prevZ );
1417 bool no_intersect = a->next->i != b->i && a->prev->i != b->i && !intersectsPolygon( a, b );
1418 bool local_split = locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b );
1419 bool same_dir = area( a->prev, a, b->prev ) != 0.0 || area( a, b->prev, b ) != 0.0;
1420 bool has_len = ( *a == *b ) && area( a->prev, a, a->next ) > 0 && area( b->prev, b, b->next ) > 0;
1421 bool pos_area = a->area( b ) > 0 && b->area( a ) > 0;
1422
1423 return no_intersect && local_split && ( same_dir || has_len ) && !a_on_edge && !b_on_edge && pos_area;
1424
1425 }
1426
1427
1428 constexpr int sign( double aVal ) const
1429 {
1430 return ( aVal > 0 ) - ( aVal < 0 );
1431 }
1432
1436 constexpr bool overlapping( const VERTEX* p, const VERTEX* q, const VERTEX* r ) const
1437 {
1438 return q->x <= std::max( p->x, r->x ) &&
1439 q->x >= std::min( p->x, r->x ) &&
1440 q->y <= std::max( p->y, r->y ) &&
1441 q->y >= std::min( p->y, r->y );
1442 }
1443
1449 bool intersects( const VERTEX* p1, const VERTEX* q1, const VERTEX* p2, const VERTEX* q2 ) const
1450 {
1451 int sign1 = sign( area( p1, q1, p2 ) );
1452 int sign2 = sign( area( p1, q1, q2 ) );
1453 int sign3 = sign( area( p2, q2, p1 ) );
1454 int sign4 = sign( area( p2, q2, q1 ) );
1455
1456 if( sign1 != sign2 && sign3 != sign4 )
1457 return true;
1458
1459 if( sign1 == 0 && overlapping( p1, p2, q1 ) )
1460 return true;
1461
1462 if( sign2 == 0 && overlapping( p1, q2, q1 ) )
1463 return true;
1464
1465 if( sign3 == 0 && overlapping( p2, p1, q2 ) )
1466 return true;
1467
1468 if( sign4 == 0 && overlapping( p2, q1, q2 ) )
1469 return true;
1470
1471
1472 return false;
1473 }
1474
1481 bool intersectsPolygon( const VERTEX* a, const VERTEX* b ) const
1482 {
1483 for( size_t ii = 0; ii < m_vertices_original_size; ii++ )
1484 {
1485 const VERTEX* p = &m_vertices[ii];
1486 const VERTEX* q = &m_vertices[( ii + 1 ) % m_vertices_original_size];
1487
1488 if( p->i == a->i || p->i == b->i || q->i == a->i || q->i == b->i )
1489 continue;
1490
1491 if( intersects( p, q, a, b ) )
1492 return true;
1493 }
1494
1495 return false;
1496 }
1497
1505 {
1506 m_result.AddVertex( pt );
1507 return insertVertex( m_result.GetVertexCount() - 1, pt, last );
1508 }
1509
1510private:
1513};
1514
1515#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