KiCad PCB EDA Suite
Loading...
Searching...
No Matches
convert_shape_list_to_polygon.cpp
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 (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2015 SoftPLC Corporation, Dick Hollenbeck <[email protected]>
6 * Copyright (C) 1992-2023 KiCad Developers, see AUTHORS.txt for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include <unordered_set>
27
28#include <trigo.h>
29#include <macros.h>
30
31#include <math/vector2d.h>
32#include <pcb_shape.h>
33#include <footprint.h>
34#include <pad.h>
35#include <base_units.h>
40#include <board.h>
41#include <collectors.h>
42
43#include <wx/log.h>
44
45
53const wxChar* traceBoardOutline = wxT( "KICAD_BOARD_OUTLINE" );
54
55
56class SCOPED_FLAGS_CLEANER : public std::unordered_set<EDA_ITEM*>
57{
59
60public:
61 SCOPED_FLAGS_CLEANER( const EDA_ITEM_FLAGS& aFlagsToClear ) : m_flagsToClear( aFlagsToClear ) {}
62
64 {
65 for( EDA_ITEM* item : *this )
67 }
68};
69
70
79static bool close_enough( VECTOR2I aLeft, VECTOR2I aRight, unsigned aLimit )
80{
81 return ( aLeft - aRight ).SquaredEuclideanNorm() <= SEG::Square( aLimit );
82}
83
84
93static bool closer_to_first( VECTOR2I aRef, VECTOR2I aFirst, VECTOR2I aSecond )
94{
95 return ( aRef - aFirst ).SquaredEuclideanNorm() < ( aRef - aSecond ).SquaredEuclideanNorm();
96}
97
98
108static PCB_SHAPE* findNext( PCB_SHAPE* aShape, const VECTOR2I& aPoint,
109 const std::vector<PCB_SHAPE*>& aList, unsigned aLimit )
110{
111 // Look for an unused, exact hit
112 for( PCB_SHAPE* graphic : aList )
113 {
114 if( graphic == aShape || ( graphic->GetFlags() & SKIP_STRUCT ) != 0 )
115 continue;
116
117 if( aPoint == graphic->GetStart() || aPoint == graphic->GetEnd() )
118 return graphic;
119 }
120
121 // Search again for anything that's close, even something already used. (The latter is
122 // important for error reporting.)
123 VECTOR2I pt( aPoint );
124 SEG::ecoord closest_dist_sq = SEG::Square( aLimit );
125 PCB_SHAPE* closest_graphic = nullptr;
126 SEG::ecoord d_sq;
127
128 for( PCB_SHAPE* graphic : aList )
129 {
130 if( graphic == aShape )
131 continue;
132
133 d_sq = ( pt - graphic->GetStart() ).SquaredEuclideanNorm();
134
135 if( d_sq < closest_dist_sq )
136 {
137 closest_dist_sq = d_sq;
138 closest_graphic = graphic;
139 }
140
141 d_sq = ( pt - graphic->GetEnd() ).SquaredEuclideanNorm();
142
143 if( d_sq < closest_dist_sq )
144 {
145 closest_dist_sq = d_sq;
146 closest_graphic = graphic;
147 }
148 }
149
150 return closest_graphic; // Note: will be nullptr if nothing within aLimit
151}
152
153
154static bool isCopperOutside( const FOOTPRINT* aFootprint, SHAPE_POLY_SET& aShape )
155{
156 bool padOutside = false;
157
158 for( PAD* pad : aFootprint->Pads() )
159 {
161
162 poly.ClearArcs();
163
164 poly.BooleanIntersection( *pad->GetEffectivePolygon( ERROR_INSIDE ),
166
167 if( poly.OutlineCount() == 0 )
168 {
169 VECTOR2I padPos = pad->GetPosition();
170 wxLogTrace( traceBoardOutline, wxT( "Tested pad (%d, %d): outside" ),
171 padPos.x, padPos.y );
172 padOutside = true;
173 break;
174 }
175
176 VECTOR2I padPos = pad->GetPosition();
177 wxLogTrace( traceBoardOutline, wxT( "Tested pad (%d, %d): not outside" ),
178 padPos.x, padPos.y );
179 }
180
181 return padOutside;
182}
183
184
185bool doConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aShapeList, SHAPE_POLY_SET& aPolygons,
186 int aErrorMax, int aChainingEpsilon, bool aAllowDisjoint,
187 OUTLINE_ERROR_HANDLER* aErrorHandler, bool aAllowUseArcsInPolygons,
188 SCOPED_FLAGS_CLEANER& aCleaner )
189{
190 if( aShapeList.size() == 0 )
191 return true;
192
193 bool selfIntersecting = false;
194
195 wxString msg;
196 PCB_SHAPE* graphic = nullptr;
197
198 std::set<PCB_SHAPE*> startCandidates( aShapeList.begin(), aShapeList.end() );
199
200 // Keep a list of where the various shapes came from so after doing our combined-polygon
201 // tests we can still report errors against the individual graphic items.
202 std::map<std::pair<VECTOR2I, VECTOR2I>, PCB_SHAPE*> shapeOwners;
203
204 auto fetchOwner =
205 [&]( const SEG& seg ) -> PCB_SHAPE*
206 {
207 auto it = shapeOwners.find( std::make_pair( seg.A, seg.B ) );
208 return it == shapeOwners.end() ? nullptr : it->second;
209 };
210
211 PCB_SHAPE* prevGraphic = nullptr;
212 VECTOR2I prevPt;
213
214 std::vector<SHAPE_LINE_CHAIN> contours;
215
216 for( PCB_SHAPE* shape : startCandidates )
217 shape->ClearFlags( SKIP_STRUCT );
218
219 while( startCandidates.size() )
220 {
221 graphic = (PCB_SHAPE*) *startCandidates.begin();
222 graphic->SetFlags( SKIP_STRUCT );
223 aCleaner.insert( graphic );
224 startCandidates.erase( startCandidates.begin() );
225
226 contours.emplace_back();
227
228 SHAPE_LINE_CHAIN& currContour = contours.back();
229 currContour.SetWidth( graphic->GetWidth() );
230 bool firstPt = true;
231
232 // Circles, rects and polygons are closed shapes unto themselves (and do not combine
233 // with other shapes), so process them separately.
234 if( graphic->GetShape() == SHAPE_T::POLY )
235 {
236 for( auto it = graphic->GetPolyShape().CIterate(); it; it++ )
237 {
238 VECTOR2I pt = *it;
239
240 currContour.Append( pt );
241
242 if( firstPt )
243 firstPt = false;
244 else
245 shapeOwners[ std::make_pair( prevPt, pt ) ] = graphic;
246
247 prevPt = pt;
248 }
249
250 currContour.SetClosed( true );
251 }
252 else if( graphic->GetShape() == SHAPE_T::CIRCLE )
253 {
254 VECTOR2I center = graphic->GetCenter();
255 int radius = graphic->GetRadius();
256 VECTOR2I start = center;
257 start.x += radius;
258
259 // Add 360 deg Arc in currContour
260 SHAPE_ARC arc360( center, start, ANGLE_360, 0 );
261 currContour.Append( arc360, aErrorMax );
262 currContour.SetClosed( true );
263
264 // set shapeOwners for currContour points created by appending the arc360:
265 for( int ii = 1; ii < currContour.PointCount(); ++ii )
266 {
267 shapeOwners[ std::make_pair( currContour.CPoint( ii-1 ),
268 currContour.CPoint( ii ) ) ] = graphic;
269 }
270
271 if( !aAllowUseArcsInPolygons )
272 currContour.ClearArcs();
273 }
274 else if( graphic->GetShape() == SHAPE_T::RECTANGLE )
275 {
276 std::vector<VECTOR2I> pts = graphic->GetRectCorners();
277
278 for( const VECTOR2I& pt : pts )
279 {
280 currContour.Append( pt );
281
282 if( firstPt )
283 firstPt = false;
284 else
285 shapeOwners[ std::make_pair( prevPt, pt ) ] = graphic;
286
287 prevPt = pt;
288 }
289
290 currContour.SetClosed( true );
291 }
292 else
293 {
294 // Polygon start point. Arbitrarily chosen end of the segment and build the poly
295 // from here.
296 VECTOR2I startPt = graphic->GetEnd();
297 prevPt = startPt;
298 currContour.Append( prevPt );
299
300 // do not append the other end point yet, this first 'graphic' might be an arc
301 for(;;)
302 {
303 switch( graphic->GetShape() )
304 {
306 case SHAPE_T::CIRCLE:
307 {
308 // As a non-first item, closed shapes can't be anything but self-intersecting
309 if( aErrorHandler )
310 {
311 wxASSERT( prevGraphic );
312 (*aErrorHandler)( _( "(self-intersecting)" ), prevGraphic, graphic,
313 prevPt );
314 }
315
316 selfIntersecting = true;
317
318 // A closed shape will finish where it started, so no point in updating prevPt
319 break;
320 }
321
322 case SHAPE_T::SEGMENT:
323 {
324 VECTOR2I nextPt;
325
326 // Use the line segment end point furthest away from prevPt as we assume
327 // the other end to be ON prevPt or very close to it.
328 if( closer_to_first( prevPt, graphic->GetStart(), graphic->GetEnd()) )
329 nextPt = graphic->GetEnd();
330 else
331 nextPt = graphic->GetStart();
332
333 currContour.Append( nextPt );
334 shapeOwners[ std::make_pair( prevPt, nextPt ) ] = graphic;
335 prevPt = nextPt;
336 }
337 break;
338
339 case SHAPE_T::ARC:
340 {
341 VECTOR2I pstart = graphic->GetStart();
342 VECTOR2I pmid = graphic->GetArcMid();
343 VECTOR2I pend = graphic->GetEnd();
344
345 if( !close_enough( prevPt, pstart, aChainingEpsilon ) )
346 {
347 wxASSERT( close_enough( prevPt, graphic->GetEnd(), aChainingEpsilon ) );
348
349 std::swap( pstart, pend );
350 }
351
352 SHAPE_ARC sarc( pstart, pmid, pend, 0 );
353
354 SHAPE_LINE_CHAIN arcChain;
355 arcChain.Append( sarc, aErrorMax );
356
357 // if this arc is after another object, pop off the first point
358 // the previous point from the last object should be already close enough as part of chaining
359 if( prevGraphic != nullptr )
360 arcChain.Remove( 0 );
361
362 if( !aAllowUseArcsInPolygons )
363 arcChain.ClearArcs();
364
365 // set shapeOwners for arcChain points created by appending the sarc:
366 for( int ii = 1; ii < arcChain.PointCount(); ++ii )
367 {
368 shapeOwners[std::make_pair( arcChain.CPoint( ii - 1 ),
369 arcChain.CPoint( ii ) )] = graphic;
370 }
371
372 currContour.Append( arcChain );
373
374 prevPt = pend;
375 }
376 break;
377
378 case SHAPE_T::BEZIER:
379 {
380 // We do not support Bezier curves in polygons, so approximate with a series
381 // of short lines and put those line segments into the !same! PATH.
382 VECTOR2I nextPt;
383 bool reverse = false;
384
385 // Use the end point furthest away from prevPt as we assume the other
386 // end to be ON prevPt or very close to it.
387 if( closer_to_first( prevPt, graphic->GetStart(), graphic->GetEnd()) )
388 {
389 nextPt = graphic->GetEnd();
390 }
391 else
392 {
393 nextPt = graphic->GetStart();
394 reverse = true;
395 }
396
397 // Ensure the approximated Bezier shape is built
399
400 if( reverse )
401 {
402 for( int jj = graphic->GetBezierPoints().size()-1; jj >= 0; jj-- )
403 {
404 const VECTOR2I& pt = graphic->GetBezierPoints()[jj];
405
406 if( prevPt == pt )
407 continue;
408
409 currContour.Append( pt );
410 shapeOwners[ std::make_pair( prevPt, pt ) ] = graphic;
411 prevPt = pt;
412 }
413 }
414 else
415 {
416 for( const VECTOR2I& pt : graphic->GetBezierPoints() )
417 {
418 if( prevPt == pt )
419 continue;
420
421 currContour.Append( pt );
422 shapeOwners[ std::make_pair( prevPt, pt ) ] = graphic;
423 prevPt = pt;
424 }
425 }
426
427 prevPt = nextPt;
428 }
429 break;
430
431 default:
433 return false;
434 }
435
436 // Get next closest segment.
437 PCB_SHAPE* nextGraphic = findNext( graphic, prevPt, aShapeList, aChainingEpsilon );
438
439 if( nextGraphic && !( nextGraphic->GetFlags() & SKIP_STRUCT ) )
440 {
441 prevGraphic = graphic;
442 graphic = nextGraphic;
443 graphic->SetFlags( SKIP_STRUCT );
444 aCleaner.insert( graphic );
445 startCandidates.erase( graphic );
446 continue;
447 }
448
449 // Finished, or ran into trouble...
450 if( close_enough( startPt, prevPt, aChainingEpsilon ) )
451 {
452 currContour.SetClosed( true );
453 break;
454 }
455 else if( nextGraphic ) // encountered already-used segment, but not at the start
456 {
457 if( aErrorHandler )
458 (*aErrorHandler)( _( "(self-intersecting)" ), graphic, nextGraphic,
459 prevPt );
460
461 break;
462 }
463 else // encountered discontinuity
464 {
465 if( aErrorHandler )
466 (*aErrorHandler)( _( "(not a closed shape)" ), graphic, nullptr, prevPt );
467
468 break;
469 }
470 }
471 }
472 }
473
474 for( const SHAPE_LINE_CHAIN& contour : contours )
475 {
476 if( !contour.IsClosed() )
477 return false;
478 }
479
480 // First, collect the parents of each contour
481 std::map<int, std::vector<int>> contourToParentIndexesMap;
482
483 for( size_t ii = 0; ii < contours.size(); ++ii )
484 {
485 VECTOR2I firstPt = contours[ii].GetPoint( 0 );
486 std::vector<int> parents;
487
488 for( size_t jj = 0; jj < contours.size(); ++jj )
489 {
490 if( jj == ii )
491 continue;
492
493 const SHAPE_LINE_CHAIN& parentCandidate = contours[jj];
494
495 if( parentCandidate.PointInside( firstPt ) )
496 parents.push_back( jj );
497 }
498
499 contourToParentIndexesMap[ii] = std::move( parents );
500 }
501
502 // Next add those that are top-level outlines to the SHAPE_POLY_SET
503 std::map<int, int> contourToOutlineIdxMap;
504
505 for( const auto& [ contourIndex, parentIndexes ] : contourToParentIndexesMap )
506 {
507 if( parentIndexes.size() %2 == 0 )
508 {
509 // Even number of parents; top-level outline
510 if( !aAllowDisjoint && !aPolygons.IsEmpty() )
511 {
512 if( aErrorHandler )
513 {
514 BOARD_ITEM* a = fetchOwner( aPolygons.Outline( 0 ).GetSegment( 0 ) );
515 BOARD_ITEM* b = fetchOwner( contours[ contourIndex ].GetSegment( 0 ) );
516
517 if( a && b )
518 {
519 (*aErrorHandler)( _( "(multiple board outlines not supported)" ), a, b,
520 contours[ contourIndex ].GetPoint( 0 ) );
521
522 return false;
523 }
524 }
525 }
526
527 aPolygons.AddOutline( contours[ contourIndex ] );
528 contourToOutlineIdxMap[ contourIndex ] = aPolygons.OutlineCount() - 1;
529 }
530 }
531
532 // And finally add the holes
533 for( const auto& [ contourIndex, parentIndexes ] : contourToParentIndexesMap )
534 {
535 if( parentIndexes.size() %2 == 1 )
536 {
537 // Odd number of parents; we're a hole in the parent which has one fewer parents
538 // than we have.
539 const SHAPE_LINE_CHAIN& hole = contours[ contourIndex ];
540
541 for( int parentContourIdx : parentIndexes )
542 {
543 if( contourToParentIndexesMap[ parentContourIdx ].size() == parentIndexes.size() - 1 )
544 {
545 int outlineIdx = contourToOutlineIdxMap[ parentContourIdx ];
546 aPolygons.AddHole( hole, outlineIdx );
547 break;
548 }
549 }
550 }
551 }
552
553 // All of the silliness that follows is to work around the segment iterator while checking
554 // for collisions.
555 // TODO: Implement proper segment and point iterators that follow std
556 for( auto seg1 = aPolygons.IterateSegmentsWithHoles(); seg1; seg1++ )
557 {
558 auto seg2 = seg1;
559
560 for( ++seg2; seg2; seg2++ )
561 {
562 // Check for exact overlapping segments.
563 if( *seg1 == *seg2 || ( ( *seg1 ).A == ( *seg2 ).B && ( *seg1 ).B == ( *seg2 ).A ) )
564 {
565 if( aErrorHandler )
566 {
567 BOARD_ITEM* a = fetchOwner( *seg1 );
568 BOARD_ITEM* b = fetchOwner( *seg2 );
569 (*aErrorHandler)( _( "(self-intersecting)" ), a, b, ( *seg1 ).A );
570 }
571
572 selfIntersecting = true;
573 }
574
575 if( OPT_VECTOR2I pt = seg1.Get().Intersect( seg2.Get(), true ) )
576 {
577 if( aErrorHandler )
578 {
579 BOARD_ITEM* a = fetchOwner( *seg1 );
580 BOARD_ITEM* b = fetchOwner( *seg2 );
581 (*aErrorHandler)( _( "(self-intersecting)" ), a, b, *pt );
582 }
583
584 selfIntersecting = true;
585 }
586 }
587 }
588
589 return !selfIntersecting;
590}
591
592
593bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aShapeList, SHAPE_POLY_SET& aPolygons,
594 int aErrorMax, int aChainingEpsilon, bool aAllowDisjoint,
595 OUTLINE_ERROR_HANDLER* aErrorHandler, bool aAllowUseArcsInPolygons )
596{
598
599 return doConvertOutlineToPolygon( aShapeList, aPolygons, aErrorMax, aChainingEpsilon,
600 aAllowDisjoint, aErrorHandler, aAllowUseArcsInPolygons,
601 cleaner );
602}
603
604
605bool TestBoardOutlinesGraphicItems( BOARD* aBoard, int aMinDist,
606 OUTLINE_ERROR_HANDLER* aErrorHandler )
607{
608 bool success = true;
609 PCB_TYPE_COLLECTOR items;
610 int min_dist = std::max( 0, aMinDist );
611
612 // Get all the shapes into 'items', then keep only those on layer == Edge_Cuts.
613 items.Collect( aBoard, { PCB_SHAPE_T } );
614
615 std::vector<PCB_SHAPE*> segList;
616
617 for( int ii = 0; ii < items.GetCount(); ii++ )
618 {
619 PCB_SHAPE* seg = static_cast<PCB_SHAPE*>( items[ii] );
620
621 if( seg->GetLayer() == Edge_Cuts )
622 segList.push_back( seg );
623 }
624
625 for( FOOTPRINT* fp : aBoard->Footprints() )
626 {
627 PCB_TYPE_COLLECTOR fpItems;
628 fpItems.Collect( fp, { PCB_SHAPE_T } );
629
630 for( int ii = 0; ii < fpItems.GetCount(); ii++ )
631 {
632 PCB_SHAPE* fpSeg = static_cast<PCB_SHAPE*>( fpItems[ii] );
633
634 if( fpSeg->GetLayer() == Edge_Cuts )
635 segList.push_back( fpSeg );
636 }
637 }
638
639 // Now Test validity of collected items
640 for( PCB_SHAPE* graphic : segList )
641 {
642 switch( graphic->GetShape() )
643 {
645 {
646 VECTOR2I seg = graphic->GetEnd() - graphic->GetStart();
647 int dim = seg.EuclideanNorm();
648
649 if( dim <= min_dist )
650 {
651 success = false;
652
653 if( aErrorHandler )
654 {
655 (*aErrorHandler)( wxString::Format( _( "(rectangle has null or very small "
656 "size: %d nm)" ), dim ),
657 graphic, nullptr, graphic->GetStart() );
658 }
659 }
660 break;
661 }
662
663 case SHAPE_T::CIRCLE:
664 {
665 int r = graphic->GetRadius();
666
667 if( r <= min_dist )
668 {
669 success = false;
670
671 if( aErrorHandler )
672 {
673 (*aErrorHandler)( wxString::Format( _( "(circle has null or very small "
674 "radius: %d nm)" ), r ),
675 graphic, nullptr, graphic->GetStart() );
676 }
677 }
678 break;
679 }
680
681 case SHAPE_T::SEGMENT:
682 {
683 VECTOR2I seg = graphic->GetEnd() - graphic->GetStart();
684 int dim = seg.EuclideanNorm();
685
686 if( dim <= min_dist )
687 {
688 success = false;
689
690 if( aErrorHandler )
691 {
692 (*aErrorHandler)( wxString::Format( _( "(segment has null or very small "
693 "length: %d nm)" ), dim ),
694 graphic, nullptr, graphic->GetStart() );
695 }
696 }
697 break;
698 }
699
700 case SHAPE_T::ARC:
701 {
702 // Arc size can be evaluated from the distance between arc middle point and arc ends
703 // We do not need a precise value, just an idea of its size
704 VECTOR2I arcMiddle = graphic->GetArcMid();
705 VECTOR2I seg1 = arcMiddle - graphic->GetStart();
706 VECTOR2I seg2 = graphic->GetEnd() - arcMiddle;
707 int dim = seg1.EuclideanNorm() + seg2.EuclideanNorm();
708
709 if( dim <= min_dist )
710 {
711 success = false;
712
713 if( aErrorHandler )
714 {
715 (*aErrorHandler)( wxString::Format( _( "(arc has null or very small size: "
716 "%d nm)" ), dim ),
717 graphic, nullptr, graphic->GetStart() );
718 }
719 }
720 break;
721 }
722
723 case SHAPE_T::POLY:
724 break;
725
726 case SHAPE_T::BEZIER:
727 break;
728
729 default:
730 UNIMPLEMENTED_FOR( graphic->SHAPE_T_asString() );
731 return false;
732 }
733 }
734
735 return success;
736}
737
738
739bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, int aErrorMax,
740 int aChainingEpsilon, OUTLINE_ERROR_HANDLER* aErrorHandler,
741 bool aAllowUseArcsInPolygons )
742{
743 PCB_TYPE_COLLECTOR items;
744 SHAPE_POLY_SET fpHoles;
745 bool success = false;
746
748
749 // Get all the shapes into 'items', then keep only those on layer == Edge_Cuts.
750 items.Collect( aBoard, { PCB_SHAPE_T } );
751
752 for( int ii = 0; ii < items.GetCount(); ++ii )
753 items[ii]->ClearFlags( SKIP_STRUCT );
754
755 for( FOOTPRINT* fp : aBoard->Footprints() )
756 {
757 PCB_TYPE_COLLECTOR fpItems;
758 fpItems.Collect( fp, { PCB_SHAPE_T } );
759
760 std::vector<PCB_SHAPE*> fpSegList;
761
762 for( int ii = 0; ii < fpItems.GetCount(); ii++ )
763 {
764 PCB_SHAPE* fpSeg = static_cast<PCB_SHAPE*>( fpItems[ii] );
765
766 if( fpSeg->GetLayer() == Edge_Cuts )
767 fpSegList.push_back( fpSeg );
768 }
769
770 if( !fpSegList.empty() )
771 {
772 SHAPE_POLY_SET fpOutlines;
773 success = doConvertOutlineToPolygon( fpSegList, fpOutlines, aErrorMax, aChainingEpsilon,
774 false,
775 // don't report errors here; the second pass also
776 // gets an opportunity to use these segments
777 nullptr, aAllowUseArcsInPolygons, cleaner );
778
779 // Test to see if we should make holes or outlines. Holes are made if the footprint
780 // has copper outside of a single, closed outline. If there are multiple outlines,
781 // we assume that the footprint edges represent holes as we do not support multiple
782 // boards. Similarly, if any of the footprint pads are located outside of the edges,
783 // then the edges are holes
784 if( success && ( isCopperOutside( fp, fpOutlines ) || fpOutlines.OutlineCount() > 1 ) )
785 {
786 fpHoles.Append( fpOutlines );
787 }
788 else
789 {
790 // If it wasn't a closed area, or wasn't a hole, the we want to keep the fpSegs
791 // in contention for the board outline builds.
792 for( int ii = 0; ii < fpItems.GetCount(); ++ii )
793 fpItems[ii]->ClearFlags( SKIP_STRUCT );
794 }
795 }
796 }
797
798 // Make a working copy of aSegList, because the list is modified during calculations
799 std::vector<PCB_SHAPE*> segList;
800
801 for( int ii = 0; ii < items.GetCount(); ii++ )
802 {
803 PCB_SHAPE* seg = static_cast<PCB_SHAPE*>( items[ii] );
804
805 // Skip anything already used to generate footprint holes (above)
806 if( seg->GetFlags() & SKIP_STRUCT )
807 continue;
808
809 if( seg->GetLayer() == Edge_Cuts )
810 segList.push_back( seg );
811 }
812
813 if( segList.size() )
814 {
815 success = doConvertOutlineToPolygon( segList, aOutlines, aErrorMax, aChainingEpsilon, true,
816 aErrorHandler, aAllowUseArcsInPolygons, cleaner );
817 }
818
819 if( !success || !aOutlines.OutlineCount() )
820 {
821 // Couldn't create a valid polygon outline. Use the board edge cuts bounding box to
822 // create a rectangular outline, or, failing that, the bounding box of the items on
823 // the board.
824 BOX2I bbbox = aBoard->GetBoardEdgesBoundingBox();
825
826 // If null area, uses the global bounding box.
827 if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
828 bbbox = aBoard->ComputeBoundingBox( false, false );
829
830 // Ensure non null area. If happen, gives a minimal size.
831 if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
832 bbbox.Inflate( pcbIUScale.mmToIU( 1.0 ) );
833
834 aOutlines.RemoveAllContours();
835 aOutlines.NewOutline();
836
837 VECTOR2I corner;
838 aOutlines.Append( bbbox.GetOrigin() );
839
840 corner.x = bbbox.GetOrigin().x;
841 corner.y = bbbox.GetEnd().y;
842 aOutlines.Append( corner );
843
844 aOutlines.Append( bbbox.GetEnd() );
845
846 corner.x = bbbox.GetEnd().x;
847 corner.y = bbbox.GetOrigin().y;
848 aOutlines.Append( corner );
849 }
850
851 for( int ii = 0; ii < fpHoles.OutlineCount(); ++ii )
852 {
853 const VECTOR2I holePt = fpHoles.Outline( ii ).CPoint( 0 );
854
855 for( int jj = 0; jj < aOutlines.OutlineCount(); ++jj )
856 {
857 if( aOutlines.Outline( jj ).PointInside( holePt ) )
858 {
859 aOutlines.AddHole( fpHoles.Outline( ii ), jj );
860 break;
861 }
862 }
863 }
864
865 return success;
866}
867
868
881void buildBoardBoundingBoxPoly( const BOARD* aBoard, SHAPE_POLY_SET& aOutline )
882{
883 BOX2I bbbox = aBoard->GetBoundingBox();
884 SHAPE_LINE_CHAIN chain;
885
886 // If null area, uses the global bounding box.
887 if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
888 bbbox = aBoard->ComputeBoundingBox( false, false );
889
890 // Ensure non null area. If happen, gives a minimal size.
891 if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
892 bbbox.Inflate( pcbIUScale.mmToIU( 1.0 ) );
893
894 // Inflate slightly (by 1/10th the size of the box)
895 bbbox.Inflate( bbbox.GetWidth() / 10, bbbox.GetHeight() / 10 );
896
897 chain.Append( bbbox.GetOrigin() );
898 chain.Append( bbbox.GetOrigin().x, bbbox.GetEnd().y );
899 chain.Append( bbbox.GetEnd() );
900 chain.Append( bbbox.GetEnd().x, bbbox.GetOrigin().y );
901 chain.SetClosed( true );
902
903 aOutline.RemoveAllContours();
904 aOutline.AddOutline( chain );
905}
906
907
908VECTOR2I projectPointOnSegment( const VECTOR2I& aEndPoint, const SHAPE_POLY_SET& aOutline,
909 int aOutlineNum = 0 )
910{
911 int minDistance = -1;
912 VECTOR2I projPoint;
913
914 for( auto it = aOutline.CIterateSegments( aOutlineNum ); it; it++ )
915 {
916 auto seg = it.Get();
917 int dis = seg.Distance( aEndPoint );
918
919 if( minDistance < 0 || ( dis < minDistance ) )
920 {
921 minDistance = dis;
922 projPoint = seg.NearestPoint( aEndPoint );
923 }
924 }
925
926 return projPoint;
927}
928
929
930int findEndSegments( SHAPE_LINE_CHAIN& aChain, SEG& aStartSeg, SEG& aEndSeg )
931{
932 int foundSegs = 0;
933
934 for( int i = 0; i < aChain.SegmentCount(); i++ )
935 {
936 SEG seg = aChain.Segment( i );
937
938 bool foundA = false;
939 bool foundB = false;
940
941 for( int j = 0; j < aChain.SegmentCount(); j++ )
942 {
943 // Don't test the segment against itself
944 if( i == j )
945 continue;
946
947 SEG testSeg = aChain.Segment( j );
948
949 if( testSeg.Contains( seg.A ) )
950 foundA = true;
951
952 if( testSeg.Contains( seg.B ) )
953 foundB = true;
954 }
955
956 // This segment isn't a start or end
957 if( foundA && foundB )
958 continue;
959
960 if( foundSegs == 0 )
961 {
962 // The first segment we encounter is the "start" segment
963 wxLogTrace( traceBoardOutline, wxT( "Found start segment: (%d, %d)-(%d, %d)" ),
964 seg.A.x, seg.A.y, seg.B.x, seg.B.y );
965 aStartSeg = seg;
966 foundSegs++;
967 }
968 else
969 {
970 // Once we find both start and end, we can stop
971 wxLogTrace( traceBoardOutline, wxT( "Found end segment: (%d, %d)-(%d, %d)" ),
972 seg.A.x, seg.A.y, seg.B.x, seg.B.y );
973 aEndSeg = seg;
974 foundSegs++;
975 break;
976 }
977 }
978
979 return foundSegs;
980}
981
982
983bool BuildFootprintPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, int aErrorMax,
984 int aChainingEpsilon, OUTLINE_ERROR_HANDLER* aErrorHandler )
985
986{
987 FOOTPRINT* footprint = aBoard->GetFirstFootprint();
988
989 // No footprint loaded
990 if( !footprint )
991 {
992 wxLogTrace( traceBoardOutline, wxT( "No footprint found on board" ) );
993 return false;
994 }
995
996 PCB_TYPE_COLLECTOR items;
997 SHAPE_POLY_SET outlines;
998 bool success = false;
999
1001
1002 // Get all the SHAPEs into 'items', then keep only those on layer == Edge_Cuts.
1003 items.Collect( aBoard, { PCB_SHAPE_T } );
1004
1005 // Make a working copy of aSegList, because the list is modified during calculations
1006 std::vector<PCB_SHAPE*> segList;
1007
1008 for( int ii = 0; ii < items.GetCount(); ii++ )
1009 {
1010 if( items[ii]->GetLayer() == Edge_Cuts )
1011 segList.push_back( static_cast<PCB_SHAPE*>( items[ii] ) );
1012 }
1013
1014 if( !segList.empty() )
1015 {
1016 success = doConvertOutlineToPolygon( segList, outlines, aErrorMax, aChainingEpsilon, true,
1017 aErrorHandler, false, cleaner );
1018 }
1019
1020 // A closed outline was found on Edge_Cuts
1021 if( success )
1022 {
1023 wxLogTrace( traceBoardOutline, wxT( "Closed outline found" ) );
1024
1025 // If copper is outside a closed polygon, treat it as a hole
1026 // If there are multiple outlines in the footprint, they are also holes
1027 if( isCopperOutside( footprint, outlines ) || outlines.OutlineCount() > 1 )
1028 {
1029 wxLogTrace( traceBoardOutline, wxT( "Treating outline as a hole" ) );
1030
1031 buildBoardBoundingBoxPoly( aBoard, aOutlines );
1032
1033 // Copy all outlines from the conversion as holes into the new outline
1034 for( int i = 0; i < outlines.OutlineCount(); i++ )
1035 {
1036 SHAPE_LINE_CHAIN& out = outlines.Outline( i );
1037
1038 if( out.IsClosed() )
1039 aOutlines.AddHole( out, -1 );
1040
1041 for( int j = 0; j < outlines.HoleCount( i ); j++ )
1042 {
1043 SHAPE_LINE_CHAIN& hole = outlines.Hole( i, j );
1044
1045 if( hole.IsClosed() )
1046 aOutlines.AddHole( hole, -1 );
1047 }
1048 }
1049 }
1050 // If all copper is inside, then the computed outline is the board outline
1051 else
1052 {
1053 wxLogTrace( traceBoardOutline, wxT( "Treating outline as board edge" ) );
1054 aOutlines = outlines;
1055 }
1056
1057 return true;
1058 }
1059 // No board outlines were found, so use the bounding box
1060 else if( outlines.OutlineCount() == 0 )
1061 {
1062 wxLogTrace( traceBoardOutline, wxT( "Using footprint bounding box" ) );
1063 buildBoardBoundingBoxPoly( aBoard, aOutlines );
1064
1065 return true;
1066 }
1067 // There is an outline present, but it is not closed
1068 else
1069 {
1070 wxLogTrace( traceBoardOutline, wxT( "Trying to build outline" ) );
1071
1072 std::vector<SHAPE_LINE_CHAIN> closedChains;
1073 std::vector<SHAPE_LINE_CHAIN> openChains;
1074
1075 // The ConvertOutlineToPolygon function returns only one main outline and the rest as
1076 // holes, so we promote the holes and process them
1077 openChains.push_back( outlines.Outline( 0 ) );
1078
1079 for( int j = 0; j < outlines.HoleCount( 0 ); j++ )
1080 {
1081 SHAPE_LINE_CHAIN hole = outlines.Hole( 0, j );
1082
1083 if( hole.IsClosed() )
1084 {
1085 wxLogTrace( traceBoardOutline, wxT( "Found closed hole" ) );
1086 closedChains.push_back( hole );
1087 }
1088 else
1089 {
1090 wxLogTrace( traceBoardOutline, wxT( "Found open hole" ) );
1091 openChains.push_back( hole );
1092 }
1093 }
1094
1095 SHAPE_POLY_SET bbox;
1096 buildBoardBoundingBoxPoly( aBoard, bbox );
1097
1098 // Treat the open polys as the board edge
1099 SHAPE_LINE_CHAIN chain = openChains[0];
1100 SHAPE_LINE_CHAIN rect = bbox.Outline( 0 );
1101
1102 // We know the outline chain is open, so set to non-closed to get better segment count
1103 chain.SetClosed( false );
1104
1105 SEG startSeg;
1106 SEG endSeg;
1107
1108 // The two possible board outlines
1109 SHAPE_LINE_CHAIN upper;
1110 SHAPE_LINE_CHAIN lower;
1111
1112 findEndSegments( chain, startSeg, endSeg );
1113
1114 if( chain.SegmentCount() == 0 )
1115 {
1116 // Something is wrong, bail out with the overall footprint bounding box
1117 wxLogTrace( traceBoardOutline, wxT( "No line segments in provided outline" ) );
1118 aOutlines = bbox;
1119 return true;
1120 }
1121 else if( chain.SegmentCount() == 1 )
1122 {
1123 // This case means there is only 1 line segment making up the edge cuts of the
1124 // footprint, so we just need to use it to cut the bounding box in half.
1125 wxLogTrace( traceBoardOutline, wxT( "Only 1 line segment in provided outline" ) );
1126
1127 startSeg = chain.Segment( 0 );
1128
1129 // Intersect with all the sides of the rectangle
1130 OPT_VECTOR2I inter0 = startSeg.IntersectLines( rect.Segment( 0 ) );
1131 OPT_VECTOR2I inter1 = startSeg.IntersectLines( rect.Segment( 1 ) );
1132 OPT_VECTOR2I inter2 = startSeg.IntersectLines( rect.Segment( 2 ) );
1133 OPT_VECTOR2I inter3 = startSeg.IntersectLines( rect.Segment( 3 ) );
1134
1135 if( inter0 && inter2 && !inter1 && !inter3 )
1136 {
1137 // Intersects the vertical rectangle sides only
1138 wxLogTrace( traceBoardOutline, wxT( "Segment intersects only vertical bbox "
1139 "sides" ) );
1140
1141 // The upper half
1142 upper.Append( *inter0 );
1143 upper.Append( rect.GetPoint( 1 ) );
1144 upper.Append( rect.GetPoint( 2 ) );
1145 upper.Append( *inter2 );
1146 upper.SetClosed( true );
1147
1148 // The lower half
1149 lower.Append( *inter0 );
1150 lower.Append( rect.GetPoint( 0 ) );
1151 lower.Append( rect.GetPoint( 3 ) );
1152 lower.Append( *inter2 );
1153 lower.SetClosed( true );
1154 }
1155 else if( inter1 && inter3 && !inter0 && !inter2 )
1156 {
1157 // Intersects the horizontal rectangle sides only
1158 wxLogTrace( traceBoardOutline, wxT( "Segment intersects only horizontal bbox "
1159 "sides" ) );
1160
1161 // The left half
1162 upper.Append( *inter1 );
1163 upper.Append( rect.GetPoint( 1 ) );
1164 upper.Append( rect.GetPoint( 0 ) );
1165 upper.Append( *inter3 );
1166 upper.SetClosed( true );
1167
1168 // The right half
1169 lower.Append( *inter1 );
1170 lower.Append( rect.GetPoint( 2 ) );
1171 lower.Append( rect.GetPoint( 3 ) );
1172 lower.Append( *inter3 );
1173 lower.SetClosed( true );
1174 }
1175 else
1176 {
1177 // Angled line segment that cuts across a corner
1178 wxLogTrace( traceBoardOutline, wxT( "Segment intersects two perpendicular bbox "
1179 "sides" ) );
1180
1181 // Figure out which actual lines are intersected, since IntersectLines assumes
1182 // an infinite line
1183 bool hit0 = rect.Segment( 0 ).Contains( *inter0 );
1184 bool hit1 = rect.Segment( 1 ).Contains( *inter1 );
1185 bool hit2 = rect.Segment( 2 ).Contains( *inter2 );
1186 bool hit3 = rect.Segment( 3 ).Contains( *inter3 );
1187
1188 if( hit0 && hit1 )
1189 {
1190 // Cut across the upper left corner
1191 wxLogTrace( traceBoardOutline, wxT( "Segment cuts upper left corner" ) );
1192
1193 // The upper half
1194 upper.Append( *inter0 );
1195 upper.Append( rect.GetPoint( 1 ) );
1196 upper.Append( *inter1 );
1197 upper.SetClosed( true );
1198
1199 // The lower half
1200 lower.Append( *inter0 );
1201 lower.Append( rect.GetPoint( 0 ) );
1202 lower.Append( rect.GetPoint( 3 ) );
1203 lower.Append( rect.GetPoint( 2 ) );
1204 lower.Append( *inter1 );
1205 lower.SetClosed( true );
1206 }
1207 else if( hit1 && hit2 )
1208 {
1209 // Cut across the upper right corner
1210 wxLogTrace( traceBoardOutline, wxT( "Segment cuts upper right corner" ) );
1211
1212 // The upper half
1213 upper.Append( *inter1 );
1214 upper.Append( rect.GetPoint( 2 ) );
1215 upper.Append( *inter2 );
1216 upper.SetClosed( true );
1217
1218 // The lower half
1219 lower.Append( *inter1 );
1220 lower.Append( rect.GetPoint( 1 ) );
1221 lower.Append( rect.GetPoint( 0 ) );
1222 lower.Append( rect.GetPoint( 3 ) );
1223 lower.Append( *inter2 );
1224 lower.SetClosed( true );
1225 }
1226 else if( hit2 && hit3 )
1227 {
1228 // Cut across the lower right corner
1229 wxLogTrace( traceBoardOutline, wxT( "Segment cuts lower right corner" ) );
1230
1231 // The upper half
1232 upper.Append( *inter2 );
1233 upper.Append( rect.GetPoint( 2 ) );
1234 upper.Append( rect.GetPoint( 1 ) );
1235 upper.Append( rect.GetPoint( 0 ) );
1236 upper.Append( *inter3 );
1237 upper.SetClosed( true );
1238
1239 // The bottom half
1240 lower.Append( *inter2 );
1241 lower.Append( rect.GetPoint( 3 ) );
1242 lower.Append( *inter3 );
1243 lower.SetClosed( true );
1244 }
1245 else
1246 {
1247 // Cut across the lower left corner
1248 wxLogTrace( traceBoardOutline, wxT( "Segment cuts upper left corner" ) );
1249
1250 // The upper half
1251 upper.Append( *inter0 );
1252 upper.Append( rect.GetPoint( 1 ) );
1253 upper.Append( rect.GetPoint( 2 ) );
1254 upper.Append( rect.GetPoint( 3 ) );
1255 upper.Append( *inter3 );
1256 upper.SetClosed( true );
1257
1258 // The bottom half
1259 lower.Append( *inter0 );
1260 lower.Append( rect.GetPoint( 0 ) );
1261 lower.Append( *inter3 );
1262 lower.SetClosed( true );
1263 }
1264 }
1265 }
1266 else
1267 {
1268 // More than 1 segment
1269 wxLogTrace( traceBoardOutline, wxT( "Multiple segments in outline" ) );
1270
1271 // Just a temporary thing
1272 aOutlines = bbox;
1273 return true;
1274 }
1275
1276 // Figure out which is the correct outline
1277 SHAPE_POLY_SET poly1;
1278 SHAPE_POLY_SET poly2;
1279
1280 poly1.NewOutline();
1281 poly1.Append( upper );
1282
1283 poly2.NewOutline();
1284 poly2.Append( lower );
1285
1286 if( isCopperOutside( footprint, poly1 ) )
1287 {
1288 wxLogTrace( traceBoardOutline, wxT( "Using lower shape" ) );
1289 aOutlines = poly2;
1290 }
1291 else
1292 {
1293 wxLogTrace( traceBoardOutline, wxT( "Using upper shape" ) );
1294 aOutlines = poly1;
1295 }
1296
1297 // Add all closed polys as holes to the main outline
1298 for( SHAPE_LINE_CHAIN& closedChain : closedChains )
1299 {
1300 wxLogTrace( traceBoardOutline, wxT( "Adding hole to main outline" ) );
1301 aOutlines.AddHole( closedChain, -1 );
1302 }
1303
1304 return true;
1305 }
1306
1307 // We really shouldn't reach this point
1308 return false;
1309}
constexpr int ARC_HIGH_DEF
Definition: base_units.h:120
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:79
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:289
const BOX2I GetBoardEdgesBoundingBox() const
Return the board bounding box calculated using exclusively the board edges (graphics on Edge....
Definition: board.h:922
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: board.h:908
BOX2I ComputeBoundingBox(bool aBoardEdgesOnly=false, bool aIncludeHiddenText=false) const
Calculate the bounding box containing all board items (or board edge segments).
Definition: board.cpp:1652
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition: board.h:447
const FOOTPRINTS & Footprints() const
Definition: board.h:330
const Vec & GetOrigin() const
Definition: box2.h:200
size_type GetHeight() const
Definition: box2.h:205
size_type GetWidth() const
Definition: box2.h:204
const Vec GetEnd() const
Definition: box2.h:202
BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:541
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:81
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:89
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:127
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition: eda_item.h:129
EDA_ITEM_FLAGS GetFlags() const
Definition: eda_item.h:130
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:279
int GetRadius() const
Definition: eda_shape.cpp:612
SHAPE_T GetShape() const
Definition: eda_shape.h:125
void RebuildBezierToSegmentsPointsList(int aMaxError)
Rebuild the m_bezierPoints vertex list that approximate the Bezier curve by a list of segments.
Definition: eda_shape.cpp:511
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:167
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:130
std::vector< VECTOR2I > GetRectCorners() const
Definition: eda_shape.cpp:1171
const std::vector< VECTOR2I > & GetBezierPoints() const
Definition: eda_shape.h:262
wxString SHAPE_T_asString() const
Definition: eda_shape.cpp:89
VECTOR2I GetArcMid() const
Definition: eda_shape.cpp:582
PADS & Pads()
Definition: footprint.h:195
Definition: pad.h:54
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_shape.h:75
int GetWidth() const override
Definition: pcb_shape.cpp:369
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: pcb_shape.h:70
Collect all BOARD_ITEM objects of a given set of KICAD_T type(s).
Definition: collectors.h:524
void Collect(BOARD_ITEM *aBoard, const std::vector< KICAD_T > &aTypes)
Collect BOARD_ITEM objects using this class's Inspector method, which does the collection.
Definition: collectors.cpp:518
SCOPED_FLAGS_CLEANER(const EDA_ITEM_FLAGS &aFlagsToClear)
Definition: seg.h:42
VECTOR2I A
Definition: seg.h:49
VECTOR2I::extended_type ecoord
Definition: seg.h:44
VECTOR2I B
Definition: seg.h:50
static SEG::ecoord Square(int a)
Definition: seg.h:123
OPT_VECTOR2I IntersectLines(const SEG &aSeg) const
Compute the intersection point of lines passing through ends of (this) and aSeg.
Definition: seg.h:220
bool Contains(const SEG &aSeg) const
Definition: seg.h:317
bool PointInside(const VECTOR2I &aPt, int aAccuracy=0, bool aUseBBoxCache=false) const override
Check if point aP lies inside a closed shape.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
bool IsClosed() const override
virtual const VECTOR2I GetPoint(int aIndex) const override
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.
void ClearArcs()
Remove all arc references in the line chain, resulting in a chain formed only of straight segments.
SEG Segment(int aIndex) const
Return a copy of the aIndex-th segment in the line chain.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
virtual const SEG GetSegment(int aIndex) const override
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
int SegmentCount() const
Return the number of segments in this line chain.
void Remove(int aStartIndex, int aEndIndex)
Remove the range of points [start_index, end_index] from the line chain.
void SetWidth(int aWidth)
Set the width of all segments in the chain.
Represent a set of closed polygons.
void RemoveAllContours()
Remove all outlines & holes (clears) the polygon set.
void ClearArcs()
Removes all arc references from all the outlines and holes in the polyset.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
CONST_ITERATOR CIterate(int aFirst, int aLast, bool aIterateHoles=false) const
void BooleanIntersection(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset intersection For aFastMode meaning, see function booleanOp.
int HoleCount(int aOutline) const
Returns the number of holes in a given outline.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
int AddHole(const SHAPE_LINE_CHAIN &aHole, int aOutline=-1)
Adds a new hole to the given outline (default: last) and returns its index.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Return the reference to aHole-th hole in the aIndex-th outline.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
CONST_SEGMENT_ITERATOR CIterateSegments(int aFirst, int aLast, bool aIterateHoles=false) const
Return an iterator object, for iterating between aFirst and aLast outline, with or without holes (def...
int OutlineCount() const
Return the number of outlines in the set.
SHAPE_POLY_SET CloneDropTriangulation() const
SEGMENT_ITERATOR IterateSegmentsWithHoles()
Returns an iterator object, for all outlines in the set (with holes)
double Distance(const VECTOR2< extended_type > &aVector) const
Compute the distance between two vectors.
Definition: vector2d.h:550
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:278
VECTOR2I projectPointOnSegment(const VECTOR2I &aEndPoint, const SHAPE_POLY_SET &aOutline, int aOutlineNum=0)
static PCB_SHAPE * findNext(PCB_SHAPE *aShape, const VECTOR2I &aPoint, const std::vector< PCB_SHAPE * > &aList, unsigned aLimit)
Search for a PCB_SHAPE matching a given end point or start point in a list.
static bool isCopperOutside(const FOOTPRINT *aFootprint, SHAPE_POLY_SET &aShape)
bool ConvertOutlineToPolygon(std::vector< PCB_SHAPE * > &aShapeList, SHAPE_POLY_SET &aPolygons, int aErrorMax, int aChainingEpsilon, bool aAllowDisjoint, OUTLINE_ERROR_HANDLER *aErrorHandler, bool aAllowUseArcsInPolygons)
Build a polygon set with holes from a PCB_SHAPE list.
bool TestBoardOutlinesGraphicItems(BOARD *aBoard, int aMinDist, OUTLINE_ERROR_HANDLER *aErrorHandler)
Test a board graphic items on edge cut layer for validity.
void buildBoardBoundingBoxPoly(const BOARD *aBoard, SHAPE_POLY_SET &aOutline)
Get the complete bounding box of the board (including all items).
int findEndSegments(SHAPE_LINE_CHAIN &aChain, SEG &aStartSeg, SEG &aEndSeg)
bool BuildBoardPolygonOutlines(BOARD *aBoard, SHAPE_POLY_SET &aOutlines, int aErrorMax, int aChainingEpsilon, OUTLINE_ERROR_HANDLER *aErrorHandler, bool aAllowUseArcsInPolygons)
Extract the board outlines and build a closed polygon from lines, arcs and circle items on edge cut l...
static bool close_enough(VECTOR2I aLeft, VECTOR2I aRight, unsigned aLimit)
Local and tunable method of qualifying the proximity of two points.
static bool closer_to_first(VECTOR2I aRef, VECTOR2I aFirst, VECTOR2I aSecond)
Local method which qualifies whether the start or end point of a segment is closest to a point.
bool BuildFootprintPolygonOutlines(BOARD *aBoard, SHAPE_POLY_SET &aOutlines, int aErrorMax, int aChainingEpsilon, OUTLINE_ERROR_HANDLER *aErrorHandler)
Extract a board outline for a footprint view.
bool doConvertOutlineToPolygon(std::vector< PCB_SHAPE * > &aShapeList, SHAPE_POLY_SET &aPolygons, int aErrorMax, int aChainingEpsilon, bool aAllowDisjoint, OUTLINE_ERROR_HANDLER *aErrorHandler, bool aAllowUseArcsInPolygons, SCOPED_FLAGS_CLEANER &aCleaner)
const std::function< void(const wxString &msg, BOARD_ITEM *itemA, BOARD_ITEM *itemB, const VECTOR2I &pt)> OUTLINE_ERROR_HANDLER
#define _(s)
static constexpr EDA_ANGLE ANGLE_360
Definition: eda_angle.h:407
#define SKIP_STRUCT
flag indicating that the structure should be ignored
std::uint32_t EDA_ITEM_FLAGS
@ ARC
use RECTANGLE instead of RECT to avoid collision in a Windows header
a few functions useful in geometry calculations.
@ ERROR_INSIDE
const wxChar * traceBoardOutline
Flag to enable debug tracing for the board outline creation.
@ Edge_Cuts
Definition: layer_ids.h:113
This file contains miscellaneous commonly used macros and functions.
#define UNIMPLEMENTED_FOR(type)
Definition: macros.h:96
std::optional< VECTOR2I > OPT_VECTOR2I
Definition: seg.h:39
constexpr int mmToIU(double mm) const
Definition: base_units.h:88
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88