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