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