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