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