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, you may find one here:
18 * http://www.gnu.org/licenses/gpl-3.0.html
19 * or you may search the http://www.gnu.org website for the version 3 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 *
23 * Based on Uniform Plane Subdivision algorithm from Lamot, Marko, and Borut Žalik.
24 * "A fast polygon triangulation algorithm based on uniform plane subdivision."
25 * Computers & graphics 27, no. 2 (2003): 239-253.
26 *
27 * Code derived from:
28 * K-3D which is Copyright (c) 2005-2006, Romain Behar, GPL-2, license above
29 * earcut which is Copyright (c) 2016, Mapbox, ISC
30 *
31 * ISC License:
32 * Permission to use, copy, modify, and/or distribute this software for any purpose
33 * with or without fee is hereby granted, provided that the above copyright notice
34 * and this permission notice appear in all copies.
35 *
36 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
37 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
38 * FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
39 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
40 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
41 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
42 * THIS SOFTWARE.
43 *
44 */
45
46#ifndef __POLYGON_TRIANGULATION_H
47#define __POLYGON_TRIANGULATION_H
48
49#include <algorithm>
50#include <array>
51#include <deque>
52#include <cmath>
53#include <vector>
54
55#include <advanced_config.h>
58#include <geometry/vertex_set.h>
59#include <math/box2.h>
60#include <math/vector2d.h>
61
62#include <wx/log.h>
63
64// ADVANCED_CFG::GetCfg() cannot be used on msys2/mingw builds (link failure)
65// So we use the ADVANCED_CFG default values
66#if defined( __MINGW32__ )
67 #define TRIANGULATESIMPLIFICATIONLEVEL 50
68 #define TRIANGULATEMINIMUMAREA 1000
69#else
70 #define TRIANGULATESIMPLIFICATIONLEVEL ADVANCED_CFG::GetCfg().m_TriangulateSimplificationLevel
71 #define TRIANGULATEMINIMUMAREA ADVANCED_CFG::GetCfg().m_TriangulateMinimumArea
72#endif
73
74#define TRIANGULATE_TRACE "triangulate"
75
77{
78public:
83
90 {
91 if( aPolygon.empty() )
92 return true;
93
94 if( aPolygon.size() == 1 )
95 return TesselatePolygon( aPolygon[0], aHintData );
96
97 const SHAPE_LINE_CHAIN& outline = aPolygon[0];
98
99 m_bbox = outline.BBox();
100
101 for( size_t i = 1; i < aPolygon.size(); i++ )
102 m_bbox.Merge( aPolygon[i].BBox() );
103
104 m_result.Clear();
105
106 if( !m_bbox.GetWidth() || !m_bbox.GetHeight() )
107 return true;
108
109 for( const SHAPE_LINE_CHAIN& chain : aPolygon )
110 {
111 for( const VECTOR2I& pt : chain.CPoints() )
112 m_result.AddVertex( pt );
113 }
114
115 int baseIndex = 0;
116 VERTEX* outerRing = createRing( outline, baseIndex, true );
117 baseIndex += outline.PointCount();
118
119 if( !outerRing || outerRing->prev == outerRing->next )
120 return true;
121
122 std::vector<VERTEX*> holeRings;
123
124 for( size_t i = 1; i < aPolygon.size(); i++ )
125 {
126 VERTEX* holeRing = createRing( aPolygon[i], baseIndex, false );
127 baseIndex += aPolygon[i].PointCount();
128
129 // Reject rings collapsed below 3 vertices. Match the outer-ring guard at
130 // line 119 so degenerate holes (single point or two-vertex sliver) cannot
131 // reach eliminateHoles().
132 if( holeRing && holeRing->prev != holeRing->next )
133 holeRings.push_back( holeRing );
134 }
135
137
138 if( !holeRings.empty() )
139 {
140 outerRing = eliminateHoles( outerRing, holeRings );
141
142 if( !outerRing )
143 {
144 wxLogTrace( TRIANGULATE_TRACE, "Hole elimination failed" );
145 return false;
146 }
147 }
148
149 if( VERTEX* simplified = simplifyList( outerRing ) )
150 outerRing = simplified;
151
152 outerRing->updateList();
153
154 auto retval = earcutList( outerRing );
155
156 if( !retval )
157 {
158 wxLogTrace( TRIANGULATE_TRACE, "Tesselation with holes failed, logging remaining vertices" );
159 logRemaining();
160 }
161
162 m_vertices.clear();
163 return retval;
164 }
165
168 {
169 m_bbox = aPoly.BBox();
170 m_result.Clear();
171
172 if( !m_bbox.GetWidth() || !m_bbox.GetHeight() )
173 return true;
174
178 VERTEX* firstVertex = createList( aPoly );
179
180 for( const VECTOR2I& pt : aPoly.CPoints() )
181 m_result.AddVertex( pt );
182
183 if( !firstVertex || firstVertex->prev == firstVertex->next )
184 return true;
185
186 wxLogTrace( TRIANGULATE_TRACE, "Created list with %f area", firstVertex->area() );
187
189
190 if( VERTEX* simplified = simplifyList( firstVertex ) )
191 firstVertex = simplified;
192
193 firstVertex->updateList();
194
201 if( aHintData && aHintData->Vertices().size() == m_result.GetVertexCount() )
202 {
203 m_result.SetTriangles( aHintData->Triangles() );
204 return true;
205 }
206 else
207 {
208 auto retval = earcutList( firstVertex );
209
210 if( !retval )
211 {
212 wxLogTrace( TRIANGULATE_TRACE, "Tesselation failed, logging remaining vertices" );
213 logRemaining();
214 }
215
216 m_vertices.clear();
217 return retval;
218 }
219 }
220
221 std::vector<double> PartitionAreaFractionsForTesting( const SHAPE_LINE_CHAIN& aPoly,
222 size_t aTargetLeaves ) const
223 {
224 std::vector<SHAPE_LINE_CHAIN> partitions = partitionPolygonBalanced( aPoly, aTargetLeaves );
225 std::vector<double> fractions;
226 double totalArea = std::abs( aPoly.Area() );
227
228 if( totalArea <= 0.0 )
229 return fractions;
230
231 fractions.reserve( partitions.size() );
232
233 for( const SHAPE_LINE_CHAIN& part : partitions )
234 fractions.push_back( std::abs( part.Area() ) / totalArea );
235
236 return fractions;
237 }
238private:
240 friend class SHAPE_POLY_SET;
241
243 {
246 };
247
248 bool collectScanlineHits( const SHAPE_LINE_CHAIN& aPoly, bool aVertical, int aCut,
249 std::array<SCANLINE_HIT, 2>& aHits ) const
250 {
251 int count = 0;
252
253 for( int ii = 0; ii < aPoly.PointCount(); ++ii )
254 {
255 const VECTOR2I& a = aPoly.CPoint( ii );
256 const VECTOR2I& b = aPoly.CPoint( ( ii + 1 ) % aPoly.PointCount() );
257
258 if( aVertical )
259 {
260 if( a.x == b.x || aCut <= std::min( a.x, b.x ) || aCut >= std::max( a.x, b.x ) )
261 continue;
262
263 if( count >= 2 )
264 return false;
265
266 double t = static_cast<double>( aCut - a.x ) / static_cast<double>( b.x - a.x );
267 int y = static_cast<int>( std::lround( a.y + t * ( b.y - a.y ) ) );
268 aHits[count++] = { ii, VECTOR2I( aCut, y ) };
269 }
270 else
271 {
272 if( a.y == b.y || aCut <= std::min( a.y, b.y ) || aCut >= std::max( a.y, b.y ) )
273 continue;
274
275 if( count >= 2 )
276 return false;
277
278 double t = static_cast<double>( aCut - a.y ) / static_cast<double>( b.y - a.y );
279 int x = static_cast<int>( std::lround( a.x + t * ( b.x - a.x ) ) );
280 aHits[count++] = { ii, VECTOR2I( x, aCut ) };
281 }
282 }
283
284 return count == 2;
285 }
286
287 SHAPE_LINE_CHAIN createSplitChild( const SHAPE_LINE_CHAIN& aPoly, int aStart, int aEnd ) const
288 {
289 SHAPE_LINE_CHAIN child;
290 int idx = aStart;
291 int guard = 0;
292 const int count = aPoly.PointCount();
293
294 do
295 {
296 child.Append( aPoly.CPoint( idx ) );
297 idx = ( idx + 1 ) % count;
298 ++guard;
299 } while( idx != ( aEnd + 1 ) % count && guard <= count + 2 );
300
301 child.SetClosed( true );
302 child.Simplify2( true );
303 return child;
304 }
305
306 bool splitPolygonAtCoordinate( const SHAPE_LINE_CHAIN& aPoly, bool aVertical, int aCut,
307 std::array<SHAPE_LINE_CHAIN, 2>& aChildren, double& aAreaA,
308 double& aAreaB ) const
309 {
310 std::array<SCANLINE_HIT, 2> hits;
311
312 if( !collectScanlineHits( aPoly, aVertical, aCut, hits ) )
313 return false;
314
315 SHAPE_LINE_CHAIN augmented( aPoly );
316 augmented.Split( hits[0].point, true );
317 augmented.Split( hits[1].point, true );
318
319 int idxA = augmented.Find( hits[0].point );
320 int idxB = augmented.Find( hits[1].point );
321
322 if( idxA < 0 || idxB < 0 || idxA == idxB )
323 return false;
324
325 aChildren[0] = createSplitChild( augmented, idxA, idxB );
326 aChildren[1] = createSplitChild( augmented, idxB, idxA );
327
328 if( aChildren[0].PointCount() < 3 || aChildren[1].PointCount() < 3 )
329 return false;
330
331 aAreaA = std::abs( aChildren[0].Area() );
332 aAreaB = std::abs( aChildren[1].Area() );
333 return aAreaA > 0.0 && aAreaB > 0.0;
334 }
335
337 std::array<SHAPE_LINE_CHAIN, 2>& aChildren ) const
338 {
339 const BOX2I bbox = aPoly.BBox();
340 const bool verticalFirst = bbox.GetWidth() >= bbox.GetHeight();
341 const double totalArea = std::abs( aPoly.Area() );
342
343 if( totalArea <= 0.0 )
344 return false;
345
346 auto tryAxis =
347 [&]( bool aVertical ) -> bool
348 {
349 const int low = ( aVertical ? bbox.GetX() : bbox.GetY() ) + 1;
350 const int high = ( aVertical ? bbox.GetRight() : bbox.GetBottom() ) - 1;
351
352 if( high <= low )
353 return false;
354
355 double bestImbalance = std::numeric_limits<double>::infinity();
356 std::array<SHAPE_LINE_CHAIN, 2> bestChildren;
357
358 constexpr int kSamples = 15;
359
360 for( int ii = 1; ii <= kSamples; ++ii )
361 {
362 int cut = low + ( ( high - low ) * ii ) / ( kSamples + 1 );
363 std::array<SHAPE_LINE_CHAIN, 2> candidate;
364 double areaA = 0.0;
365 double areaB = 0.0;
366
367 if( !splitPolygonAtCoordinate( aPoly, aVertical, cut, candidate, areaA, areaB ) )
368 continue;
369
370 double imbalance = std::abs( areaA - areaB ) / totalArea;
371
372 if( imbalance < bestImbalance )
373 {
374 bestImbalance = imbalance;
375 bestChildren = std::move( candidate );
376 }
377 }
378
379 if( !std::isfinite( bestImbalance ) || bestImbalance > 0.35 )
380 return false;
381
382 aChildren = std::move( bestChildren );
383 return true;
384 };
385
386 return tryAxis( verticalFirst ) || tryAxis( !verticalFirst );
387 }
388
389 std::vector<SHAPE_LINE_CHAIN> partitionPolygonBalanced( const SHAPE_LINE_CHAIN& aPoly,
390 size_t aTargetLeaves ) const
391 {
392 std::vector<SHAPE_LINE_CHAIN> leaves = { aPoly };
393
394 if( aTargetLeaves < 2 )
395 return leaves;
396
397 while( leaves.size() < aTargetLeaves )
398 {
399 int bestLeaf = -1;
400 double bestArea = 0.0;
401
402 for( size_t ii = 0; ii < leaves.size(); ++ii )
403 {
404 double area = std::abs( leaves[ii].Area() );
405
406 if( area > bestArea )
407 {
408 bestArea = area;
409 bestLeaf = static_cast<int>( ii );
410 }
411 }
412
413 if( bestLeaf < 0 )
414 break;
415
416 std::array<SHAPE_LINE_CHAIN, 2> children;
417
418 if( !splitPolygonBalanced( leaves[bestLeaf], children ) )
419 break;
420
421 leaves[bestLeaf] = std::move( children[0] );
422 leaves.push_back( std::move( children[1] ) );
423 }
424
425 return leaves;
426 }
427
429 {
430 constexpr size_t kVerticesPerLeaf = 50000;
431 constexpr size_t kMaxLeaves = 8;
432 size_t leaves = 1;
433
434 while( leaves < kMaxLeaves
435 && static_cast<size_t>( aPoly.PointCount() ) / leaves > kVerticesPerLeaf )
436 {
437 leaves *= 2;
438 }
439
440 return leaves;
441 }
442
447 {
448 std::set<VERTEX*> seen;
449 wxLog::EnableLogging();
450 for( VERTEX& p : m_vertices )
451 {
452 if( !p.next || p.next == &p || seen.find( &p ) != seen.end() )
453 continue;
454
455 logVertices( &p, &seen );
456 }
457 }
458
459 void logVertices( VERTEX* aStart, std::set<VERTEX*>* aSeen )
460 {
461 if( aSeen && aSeen->count( aStart ) )
462 return;
463
464 if( aSeen )
465 aSeen->insert( aStart );
466
467 int count = 1;
468 VERTEX* p = aStart->next;
469 wxString msg = wxString::Format( "Vertices: %d,%d,", static_cast<int>( aStart->x ),
470 static_cast<int>( aStart->y ) );
471
472 do
473 {
474 msg += wxString::Format( "%d,%d,", static_cast<int>( p->x ), static_cast<int>( p->y ) );
475
476 if( aSeen )
477 aSeen->insert( p );
478
479 p = p->next;
480 count++;
481 } while( p != aStart );
482
483 if( count < 3 ) // Don't log anything that only has 2 or fewer points
484 return;
485
486 msg.RemoveLast();
487 wxLogTrace( TRIANGULATE_TRACE, msg );
488 }
489
495 {
496 if( !aStart || aStart->next == aStart->prev )
497 return aStart;
498
499 VERTEX* p = aStart;
500 VERTEX* next = p->next;
501 VERTEX* retval = aStart;
502 int count = 0;
503
504 double sq_dist = TRIANGULATESIMPLIFICATIONLEVEL;
505 sq_dist *= sq_dist;
506
507 do
508 {
509 VECTOR2D diff = VECTOR2D( next->x - p->x, next->y - p->y );
510
511 if( diff.SquaredEuclideanNorm() < sq_dist )
512 {
513 if( next == aStart )
514 {
515 retval = p;
516 aStart->remove();
517 count++;
518 break;
519 }
520
521 next = next->next;
522 p->next->remove();
523 count++;
524 retval = p;
525 }
526 else
527 {
528 p = next;
529 next = next->next;
530 }
531 } while( p != aStart && next && p );
532
533 wxLogTrace( TRIANGULATE_TRACE, "Removed %d points in simplifyList", count );
534
535 if( count )
536 return retval;
537
538 return nullptr;
539 }
540
549 {
550 VERTEX* retval = nullptr;
551 size_t count = 0;
552
553 if( ( retval = simplifyList( aStart ) ) )
554 aStart = retval;
555
556 wxASSERT( aStart->next && aStart->prev );
557
558 VERTEX* p = aStart->next;
559
560 while( p != aStart && p->next && p->prev )
561 {
562 // We make a dummy triangle that is actually part of the existing line segment
563 // and measure its area. This will not be exactly zero due to floating point
564 // errors. We then look for areas that are less than 4 times the area of the
565 // dummy triangle. For small triangles, this is a small number
566 VERTEX tmp( 0, 0.5 * ( p->prev->x + p->next->x ), 0.5 * ( p->prev->y + p->next->y ), this );
567 double null_area = 4.0 * std::abs( area( p->prev, &tmp, p->next ) );
568
569 if( *p == *( p->next ) || std::abs( area( p->prev, p, p->next ) ) <= null_area )
570 {
571 // This is a spike, remove it, leaving only one point
572 if( *( p->next ) == *( p->prev ) )
573 p->next->remove();
574
575 p = p->prev;
576 p->next->remove();
577 retval = p;
578 ++count;
579
580 if( p == p->next )
581 break;
582
583 // aStart was removed above, so we need to reset it
584 if( !aStart->next )
585 aStart = p->prev;
586
587 continue;
588 }
589
590 p = p->next;
591 };
592
594 if( !p->next || p->next == p || p->next == p->prev )
595 return p;
596
597 // We needed an end point above that wouldn't be removed, so
598 // here we do the final check for this as a Steiner point
599 VERTEX tmp( 0, 0.5 * ( p->prev->x + p->next->x ),
600 0.5 * ( p->prev->y + p->next->y ), this );
601 double null_area = 4.0 * std::abs( area( p->prev, &tmp, p->next ) );
602
603 if( std::abs( area( p->prev, p, p->next ) ) <= null_area )
604 {
605 retval = p->next;
606 p->remove();
607 ++count;
608 }
609
610 wxLogTrace( TRIANGULATE_TRACE, "Removed %zu NULL triangles", count );
611
612 return retval;
613 }
614
628 bool earcutList( VERTEX* aPoint, int pass = 0 )
629 {
630 constexpr int kMaxRecursion = 64;
631
632 if( pass >= kMaxRecursion )
633 {
634 wxLogTrace( TRIANGULATE_TRACE, "earcutList recursion limit reached; aborting triangulation", pass );
635 return false;
636 }
637
638 wxLogTrace( TRIANGULATE_TRACE, "earcutList starting at %p for pass %d", aPoint, pass );
639
640 if( !aPoint )
641 return true;
642
643 VERTEX* stop = aPoint;
644 VERTEX* prev;
645 VERTEX* next;
646 int internal_pass = 1;
647 constexpr int kEarLookahead = 2;
648
649 while( aPoint->prev != aPoint->next )
650 {
651 prev = aPoint->prev;
652 next = aPoint->next;
653
654 VERTEX* bestEar = nullptr;
655 double bestScore = -1.0;
656 int lookahead = 0;
657
658 for( VERTEX* candidate = aPoint; candidate && lookahead < kEarLookahead;
659 candidate = candidate->next, ++lookahead )
660 {
661 if( !candidate->isEar() || isTooSmall( candidate ) )
662 continue;
663
664 const double score = earScore( candidate->prev, candidate, candidate->next );
665
666 if( !bestEar || score > bestScore )
667 {
668 bestEar = candidate;
669 bestScore = score;
670 }
671 }
672
673 if( bestEar )
674 {
675 prev = bestEar->prev;
676 next = bestEar->next;
677 m_result.AddTriangle( prev->i, bestEar->i, next->i );
678 bestEar->remove();
679
680 // Skip one vertex as the triangle will account for the prev node
681 aPoint = next->next;
682 stop = next->next;
683 continue;
684 }
685
686 VERTEX* nextNext = next->next;
687
688 if( *prev != *nextNext && intersects( prev, aPoint, next, nextNext ) &&
689 locallyInside( prev, nextNext ) &&
690 locallyInside( nextNext, prev ) )
691 {
692 wxLogTrace( TRIANGULATE_TRACE,
693 "Local intersection detected. Merging minor triangle with area %f",
694 area( prev, aPoint, nextNext ) );
695 m_result.AddTriangle( prev->i, aPoint->i, nextNext->i );
696
697 // remove two nodes involved
698 next->remove();
699 aPoint->remove();
700
701 aPoint = nextNext;
702 stop = nextNext;
703
704 continue;
705 }
706
707 aPoint = next;
708
709 /*
710 * We've searched the entire polygon for available ears and there are still
711 * un-sliced nodes remaining.
712 */
713 if( aPoint == stop && aPoint->prev != aPoint->next )
714 {
715 VERTEX* newPoint;
716
717 // Removing null triangles will remove steiner points as well as colinear points
718 // that are three in a row. Because our next step is to subdivide the polygon,
719 // we need to allow it to add the subdivided points first. This is why we only
720 // run the RemoveNullTriangles function after the first pass.
721 if( ( internal_pass == 2 ) && ( newPoint = removeNullTriangles( aPoint ) ) )
722 {
723 // There are no remaining triangles in the list
724 if( newPoint->next == newPoint->prev )
725 break;
726
727 aPoint = newPoint;
728 stop = newPoint;
729 continue;
730 }
731
732 ++internal_pass;
733
734 // This will subdivide the polygon 2 times. The first pass will add enough points
735 // such that each edge is less than the average edge length. If this doesn't work
736 // The next pass will remove the null triangles (above) and subdivide the polygon
737 // again, this time adding one point to each long edge (and thereby changing the locations)
738 if( internal_pass < 4 )
739 {
740 wxLogTrace( TRIANGULATE_TRACE, "Subdividing polygon" );
741 subdividePolygon( aPoint, internal_pass );
742 continue;
743 }
744
745 // If we don't have any NULL triangles left, cut the polygon in two and try again
746 wxLogTrace( TRIANGULATE_TRACE, "Splitting polygon" );
747
748 if( !splitPolygon( aPoint, pass + 1 ) )
749 return false;
750
751 break;
752 }
753 }
754
755 // Check to see if we are left with only three points in the polygon
756 if( aPoint->next && aPoint->prev == aPoint->next->next )
757 {
758 // Three concave points will never be able to be triangulated because they were
759 // created by an intersecting polygon, so just drop them.
760 if( area( aPoint->prev, aPoint, aPoint->next ) >= 0 )
761 return true;
762 }
763
764 /*
765 * At this point, our polygon should be fully tessellated.
766 */
767 if( aPoint->prev != aPoint->next )
768 return std::abs( aPoint->area() ) > TRIANGULATEMINIMUMAREA;
769
770 return true;
771 }
772
773
777
778 bool isTooSmall( const VERTEX* aPoint ) const
779 {
780 double min_area = TRIANGULATEMINIMUMAREA;
781 double prev_sq_len = ( aPoint->prev->x - aPoint->x ) * ( aPoint->prev->x - aPoint->x ) +
782 ( aPoint->prev->y - aPoint->y ) * ( aPoint->prev->y - aPoint->y );
783 double next_sq_len = ( aPoint->next->x - aPoint->x ) * ( aPoint->next->x - aPoint->x ) +
784 ( aPoint->next->y - aPoint->y ) * ( aPoint->next->y - aPoint->y );
785 double opp_sq_len = ( aPoint->next->x - aPoint->prev->x ) * ( aPoint->next->x - aPoint->prev->x ) +
786 ( aPoint->next->y - aPoint->prev->y ) * ( aPoint->next->y - aPoint->prev->y );
787
788 return ( prev_sq_len < min_area || next_sq_len < min_area || opp_sq_len < min_area );
789 }
790
791 double earScore( const VERTEX* a, const VERTEX* b, const VERTEX* c ) const
792 {
793 const double ab_sq = ( a->x - b->x ) * ( a->x - b->x ) + ( a->y - b->y ) * ( a->y - b->y );
794 const double bc_sq = ( b->x - c->x ) * ( b->x - c->x ) + ( b->y - c->y ) * ( b->y - c->y );
795 const double ca_sq = ( c->x - a->x ) * ( c->x - a->x ) + ( c->y - a->y ) * ( c->y - a->y );
796 const double norm = ab_sq + bc_sq + ca_sq;
797
798 if( norm <= 0.0 )
799 return 0.0;
800
801 return std::abs( area( a, b, c ) ) / norm;
802 }
803
808 VERTEX* createRing( const SHAPE_LINE_CHAIN& aPoints, int aBaseIndex, bool aWantCCW )
809 {
810 VERTEX* tail = nullptr;
811 double sum = 0.0;
812 VECTOR2L last_pt;
813 bool first = true;
814
815 for( int i = 0; i < aPoints.PointCount(); i++ )
816 {
817 VECTOR2D p1 = aPoints.CPoint( i );
818 VECTOR2D p2 = aPoints.CPoint( ( i + 1 ) % aPoints.PointCount() );
819 sum += ( ( p2.x - p1.x ) * ( p2.y + p1.y ) );
820 }
821
822 bool isCW = sum > 0.0;
823 bool needReverse = ( aWantCCW == isCW );
824
825 auto addVertex = [&]( int i )
826 {
827 const VECTOR2I& pt = aPoints.CPoint( i );
828
829 if( first || pt.SquaredDistance( last_pt ) > m_simplificationLevel )
830 {
831 tail = insertVertex( aBaseIndex + i, pt, tail );
832 last_pt = pt;
833 first = false;
834 }
835 };
836
837 if( needReverse )
838 {
839 for( int i = aPoints.PointCount() - 1; i >= 0; i-- )
840 addVertex( i );
841 }
842 else
843 {
844 for( int i = 0; i < aPoints.PointCount(); i++ )
845 addVertex( i );
846 }
847
848 // Collapse a final duplicate, but never on a single-vertex ring. When the
849 // simplification pass leaves only one vertex, tail->next == tail and removing
850 // it would leave the caller holding a vertex with null next/prev pointers.
851 if( tail && tail->next != tail && ( *tail == *tail->next ) )
852 tail->next->remove();
853
854 return tail;
855 }
856
862 VERTEX* eliminateHoles( VERTEX* aOuterRing, std::vector<VERTEX*>& aHoleRings )
863 {
864 struct HoleInfo
865 {
866 VERTEX* leftmost;
867 double leftX;
868 };
869
870 std::vector<HoleInfo> holes;
871 holes.reserve( aHoleRings.size() );
872
873 for( VERTEX* hole : aHoleRings )
874 {
875 VERTEX* leftmost = hole;
876 VERTEX* p = hole->next;
877
878 while( p != hole )
879 {
880 if( p->x < leftmost->x || ( p->x == leftmost->x && p->y < leftmost->y ) )
881 leftmost = p;
882
883 p = p->next;
884 }
885
886 holes.push_back( { leftmost, leftmost->x } );
887 }
888
889 std::sort( holes.begin(), holes.end(),
890 []( const HoleInfo& a, const HoleInfo& b ) { return a.leftX < b.leftX; } );
891
892 for( const HoleInfo& hi : holes )
893 {
894 VERTEX* bridge = findHoleBridge( hi.leftmost, aOuterRing );
895
896 if( bridge )
897 {
898 VERTEX* bridgeReverse = bridge->split( hi.leftmost );
899 filterPoints( bridgeReverse, bridgeReverse->next );
900 aOuterRing = filterPoints( bridge, bridge->next );
901 }
902 else
903 {
904 wxLogTrace( TRIANGULATE_TRACE, "Failed to find bridge for hole at (%f, %f)",
905 hi.leftmost->x, hi.leftmost->y );
906 }
907 }
908
909 return aOuterRing;
910 }
911
915 VERTEX* filterPoints( VERTEX* aStart, VERTEX* aEnd = nullptr )
916 {
917 if( !aStart )
918 return aStart;
919
920 if( !aEnd )
921 aEnd = aStart;
922
923 VERTEX* p = aStart;
924 bool again;
925
926 do
927 {
928 again = false;
929
930 if( *p == *p->next )
931 {
932 VERTEX* toRemove = p->next;
933
934 if( toRemove == aEnd )
935 aEnd = p;
936
937 toRemove->remove();
938
939 if( p == p->next )
940 return p;
941
942 p = p->prev;
943 again = true;
944 }
945 else
946 {
947 p = p->next;
948 }
949 } while( again || p != aEnd );
950
951 return aEnd;
952 }
953
954
959 VERTEX* findHoleBridge( VERTEX* aHole, VERTEX* aOuterStart )
960 {
961 VERTEX* p = aOuterStart;
962 double hx = aHole->x;
963 double hy = aHole->y;
964 double qx = -std::numeric_limits<double>::infinity();
965 VERTEX* m = nullptr;
966
967 do
968 {
969 if( hy <= p->y && hy >= p->next->y && p->next->y != p->y )
970 {
971 double x = p->x + ( hy - p->y ) * ( p->next->x - p->x )
972 / ( p->next->y - p->y );
973
974 if( x <= hx && x > qx )
975 {
976 qx = x;
977
978 if( x == hx )
979 {
980 if( hy == p->y )
981 return p;
982
983 if( hy == p->next->y )
984 return p->next;
985 }
986
987 m = ( p->x < p->next->x ) ? p : p->next;
988 }
989 }
990
991 p = p->next;
992 } while( p != aOuterStart );
993
994 if( !m )
995 return nullptr;
996
997 if( hx == qx )
998 return m;
999
1000 // Pick the vertex inside the visibility triangle closest to the ray
1001 const VERTEX* stop = m;
1002 double mx = m->x;
1003 double my = m->y;
1004 double tanMin = std::numeric_limits<double>::infinity();
1005
1006 p = m;
1007
1008 do
1009 {
1010 if( hx >= p->x && p->x >= mx && hx != p->x )
1011 {
1012 bool inside;
1013
1014 if( hy < my )
1015 inside = triArea( hx, hy, mx, my, p->x, p->y ) >= 0
1016 && triArea( mx, my, qx, hy, p->x, p->y ) >= 0
1017 && triArea( qx, hy, hx, hy, p->x, p->y ) >= 0;
1018 else
1019 inside = triArea( qx, hy, mx, my, p->x, p->y ) >= 0
1020 && triArea( mx, my, hx, hy, p->x, p->y ) >= 0
1021 && triArea( hx, hy, qx, hy, p->x, p->y ) >= 0;
1022
1023 if( inside )
1024 {
1025 double t = std::abs( hy - p->y ) / ( hx - p->x );
1026
1027 if( locallyInside( p, aHole )
1028 && ( t < tanMin
1029 || ( t == tanMin
1030 && ( p->x > m->x
1031 || ( p->x == m->x
1032 && sectorContainsSector( m, p ) ) ) ) ) )
1033 {
1034 m = p;
1035 tanMin = t;
1036 }
1037 }
1038 }
1039
1040 p = p->next;
1041 } while( p != stop );
1042
1043 return m;
1044 }
1045
1049 static double triArea( double ax, double ay, double bx, double by,
1050 double cx, double cy )
1051 {
1052 return ( bx - ax ) * ( cy - ay ) - ( by - ay ) * ( cx - ax );
1053 }
1054
1059 bool sectorContainsSector( const VERTEX* m, const VERTEX* p ) const
1060 {
1061 return area( m->prev, m, p->prev ) < 0 && area( p->next, m, m->next ) < 0;
1062 }
1063
1067 void subdividePolygon( VERTEX* aStart, int pass = 0 )
1068 {
1069 VERTEX* p = aStart;
1070
1071 struct VertexComparator {
1072 bool operator()(const std::pair<VERTEX*,double>& a, const std::pair<VERTEX*,double>& b) const {
1073 return a.second > b.second;
1074 }
1075 };
1076
1077 std::set<std::pair<VERTEX*,double>, VertexComparator> longest;
1078 double avg = 0.0;
1079
1080 do
1081 {
1082 double len = ( p->x - p->next->x ) * ( p->x - p->next->x ) +
1083 ( p->y - p->next->y ) * ( p->y - p->next->y );
1084 longest.emplace( p, len );
1085
1086 avg += len;
1087 p = p->next;
1088 } while (p != aStart);
1089
1090 avg /= longest.size();
1091 wxLogTrace( TRIANGULATE_TRACE, "Average length: %f", avg );
1092
1093 constexpr double kSubdivideThresholdFactor = 1.1;
1094 const double subdivideThreshold = avg * kSubdivideThresholdFactor;
1095
1096 for( auto it = longest.begin(); it != longest.end() && it->second > subdivideThreshold;
1097 ++it )
1098 {
1099 wxLogTrace( TRIANGULATE_TRACE, "Subdividing edge with length %f", it->second );
1100 VERTEX* a = it->first;
1101 VERTEX* b = a->next;
1102 VERTEX* last = a;
1103
1104 // We adjust the number of divisions based on the pass in order to progressively
1105 // subdivide the polygon when triangulation fails
1106 int divisions = avg / it->second + 2 + pass;
1107 double step = 1.0 / divisions;
1108
1109 for( int i = 1; i < divisions; i++ )
1110 {
1111 double x = a->x * ( 1.0 - step * i ) + b->x * ( step * i );
1112 double y = a->y * ( 1.0 - step * i ) + b->y * ( step * i );
1113 last = insertTriVertex( VECTOR2I( x, y ), last );
1114 }
1115 }
1116
1117 // update z-order of the vertices
1118 aStart->updateList();
1119 }
1120
1127 bool splitPolygon( VERTEX* start, int aPass )
1128 {
1129 VERTEX* origPoly = start;
1130
1131 // If we have fewer than 4 points, we cannot split the polygon
1132 if( !start || !start->next || start->next == start->prev
1133 || start->next->next == start->prev )
1134 {
1135 return true;
1136 }
1137
1138 // Our first attempts to split the polygon will be at overlapping points.
1139 // These are natural split points and we only need to switch the loop directions
1140 // to generate two new loops. Since they are overlapping, we are do not
1141 // need to create a new segment to disconnect the two loops.
1142 do
1143 {
1144 std::vector<VERTEX*> overlapPoints;
1145 VERTEX* z_pt = origPoly;
1146
1147 while ( z_pt->prevZ && *z_pt->prevZ == *origPoly )
1148 z_pt = z_pt->prevZ;
1149
1150 overlapPoints.push_back( z_pt );
1151
1152 while( z_pt->nextZ && *z_pt->nextZ == *origPoly )
1153 {
1154 z_pt = z_pt->nextZ;
1155 overlapPoints.push_back( z_pt );
1156 }
1157
1158 if( overlapPoints.size() != 2 || overlapPoints[0]->next == overlapPoints[1]
1159 || overlapPoints[0]->prev == overlapPoints[1] )
1160 {
1161 origPoly = origPoly->next;
1162 continue;
1163 }
1164
1165 if( overlapPoints[0]->area( overlapPoints[1] ) < 0 || overlapPoints[1]->area( overlapPoints[0] ) < 0 )
1166 {
1167 wxLogTrace( TRIANGULATE_TRACE, "Split generated a hole, skipping" );
1168 origPoly = origPoly->next;
1169 continue;
1170 }
1171
1172 wxLogTrace( TRIANGULATE_TRACE, "Splitting at overlap point %f, %f", overlapPoints[0]->x, overlapPoints[0]->y );
1173 std::swap( overlapPoints[0]->next, overlapPoints[1]->next );
1174 overlapPoints[0]->next->prev = overlapPoints[0];
1175 overlapPoints[1]->next->prev = overlapPoints[1];
1176
1177 overlapPoints[0]->updateList();
1178 overlapPoints[1]->updateList();
1179 logVertices( overlapPoints[0], nullptr );
1180 logVertices( overlapPoints[1], nullptr );
1181 bool retval = earcutList( overlapPoints[0], aPass )
1182 && earcutList( overlapPoints[1], aPass );
1183
1184 wxLogTrace( TRIANGULATE_TRACE, "%s at first overlap split", retval ? "Success" : "Failed" );
1185 return retval;
1186
1187
1188 } while ( origPoly != start );
1189
1190 // If we've made it through the split algorithm and we still haven't found a
1191 // set of overlapping points, we need to create a new segment to split the polygon
1192 // into two separate polygons. We do this by finding the two vertices that form
1193 // a valid line (does not cross the existing polygon)
1194 do
1195 {
1196 VERTEX* marker = origPoly->next->next;
1197
1198 while( marker != origPoly->prev )
1199 {
1200 // Find a diagonal line that is wholly enclosed by the polygon interior
1201 if( origPoly->next && origPoly->i != marker->i && goodSplit( origPoly, marker ) )
1202 {
1203 VERTEX* newPoly = origPoly->split( marker );
1204
1205 origPoly->updateList();
1206 newPoly->updateList();
1207
1208 bool retval = earcutList( origPoly, aPass ) && earcutList( newPoly, aPass );
1209
1210 wxLogTrace( TRIANGULATE_TRACE, "%s at split", retval ? "Success" : "Failed" );
1211 return retval;
1212 }
1213
1214 marker = marker->next;
1215 }
1216
1217 origPoly = origPoly->next;
1218 } while( origPoly != start );
1219
1220 wxLogTrace( TRIANGULATE_TRACE, "Could not find a valid split point" );
1221 return false;
1222 }
1223
1233 bool goodSplit( const VERTEX* a, const VERTEX* b ) const
1234 {
1235 bool a_on_edge = ( a->nextZ && *a == *a->nextZ ) || ( a->prevZ && *a == *a->prevZ );
1236 bool b_on_edge = ( b->nextZ && *b == *b->nextZ ) || ( b->prevZ && *b == *b->prevZ );
1237 bool no_intersect = a->next->i != b->i && a->prev->i != b->i && !intersectsPolygon( a, b );
1238 bool local_split = locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b );
1239 bool same_dir = area( a->prev, a, b->prev ) != 0.0 || area( a, b->prev, b ) != 0.0;
1240 bool has_len = ( *a == *b ) && area( a->prev, a, a->next ) > 0 && area( b->prev, b, b->next ) > 0;
1241 bool pos_area = a->area( b ) > 0 && b->area( a ) > 0;
1242
1243 return no_intersect && local_split && ( same_dir || has_len ) && !a_on_edge && !b_on_edge && pos_area;
1244
1245 }
1246
1247
1248 constexpr int sign( double aVal ) const
1249 {
1250 return ( aVal > 0 ) - ( aVal < 0 );
1251 }
1252
1256 constexpr bool overlapping( const VERTEX* p, const VERTEX* q, const VERTEX* r ) const
1257 {
1258 return q->x <= std::max( p->x, r->x ) &&
1259 q->x >= std::min( p->x, r->x ) &&
1260 q->y <= std::max( p->y, r->y ) &&
1261 q->y >= std::min( p->y, r->y );
1262 }
1263
1269 bool intersects( const VERTEX* p1, const VERTEX* q1, const VERTEX* p2, const VERTEX* q2 ) const
1270 {
1271 int sign1 = sign( area( p1, q1, p2 ) );
1272 int sign2 = sign( area( p1, q1, q2 ) );
1273 int sign3 = sign( area( p2, q2, p1 ) );
1274 int sign4 = sign( area( p2, q2, q1 ) );
1275
1276 if( sign1 != sign2 && sign3 != sign4 )
1277 return true;
1278
1279 if( sign1 == 0 && overlapping( p1, p2, q1 ) )
1280 return true;
1281
1282 if( sign2 == 0 && overlapping( p1, q2, q1 ) )
1283 return true;
1284
1285 if( sign3 == 0 && overlapping( p2, p1, q2 ) )
1286 return true;
1287
1288 if( sign4 == 0 && overlapping( p2, q1, q2 ) )
1289 return true;
1290
1291
1292 return false;
1293 }
1294
1301 bool intersectsPolygon( const VERTEX* a, const VERTEX* b ) const
1302 {
1303 for( size_t ii = 0; ii < m_vertices_original_size; ii++ )
1304 {
1305 const VERTEX* p = &m_vertices[ii];
1306 const VERTEX* q = &m_vertices[( ii + 1 ) % m_vertices_original_size];
1307
1308 if( p->i == a->i || p->i == b->i || q->i == a->i || q->i == b->i )
1309 continue;
1310
1311 if( intersects( p, q, a, b ) )
1312 return true;
1313 }
1314
1315 return false;
1316 }
1317
1325 {
1326 m_result.AddVertex( pt );
1327 return insertVertex( m_result.GetVertexCount() - 1, pt, last );
1328 }
1329
1330private:
1333};
1334
1335#endif //__POLYGON_TRIANGULATION_H
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr coord_type GetY() const
Definition box2.h:208
constexpr size_type GetWidth() const
Definition box2.h:214
constexpr coord_type GetX() const
Definition box2.h:207
constexpr size_type GetHeight() const
Definition box2.h:215
constexpr coord_type GetRight() const
Definition box2.h:217
constexpr coord_type GetBottom() const
Definition box2.h:222
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).
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:307
constexpr extended_type SquaredDistance(const VECTOR2< T > &aVector) const
Compute the squared distance between two vectors.
Definition vector2d.h:561
std::deque< VERTEX > m_vertices
Definition vertex_set.h:343
friend class VERTEX
Definition vertex_set.h:255
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:342
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:258
VECTOR2I::extended_type m_simplificationLevel
Definition vertex_set.h:344
VERTEX * split(VERTEX *b)
Split the referenced polygon between the reference point and vertex b, assuming they are in the same ...
const double x
Definition vertex_set.h:235
VERTEX * next
Definition vertex_set.h:241
VERTEX * prevZ
Definition vertex_set.h:247
void updateList()
After inserting or changing nodes, this function should be called to remove duplicate vertices and en...
Definition vertex_set.h:121
VERTEX * nextZ
Definition vertex_set.h:248
VERTEX * prev
Definition vertex_set.h:240
const int i
Definition vertex_set.h:234
void remove()
Remove the node from the linked list and z-ordered linked list.
Definition vertex_set.h:84
double area(const VERTEX *aEnd=nullptr) const
Returns the signed area of the polygon connected to the current vertex, optionally ending at a specif...
Definition vertex_set.h:202
const double y
Definition vertex_set.h:236
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
#define TRIANGULATE_TRACE
#define TRIANGULATEMINIMUMAREA
#define TRIANGULATESIMPLIFICATIONLEVEL
CITER next(CITER it)
Definition ptree.cpp:124
const SHAPE_LINE_CHAIN chain
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687
VECTOR2< double > VECTOR2D
Definition vector2d.h:686
VECTOR2< int64_t > VECTOR2L
Definition vector2d.h:688