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 // Snap the arc start point to avoid potential self-intersections
358 pstart = prevPt;
359
360 SHAPE_ARC sarc( pstart, pmid, pend, 0 );
361
362 SHAPE_LINE_CHAIN arcChain;
363 arcChain.Append( sarc, aErrorMax );
364
365 if( !aAllowUseArcsInPolygons )
366 arcChain.ClearArcs();
367
368 // set shapeOwners for arcChain points created by appending the sarc:
369 for( int ii = 1; ii < arcChain.PointCount(); ++ii )
370 {
371 shapeOwners[std::make_pair( arcChain.CPoint( ii - 1 ),
372 arcChain.CPoint( ii ) )] = graphic;
373 }
374
375 currContour.Append( arcChain );
376
377 prevPt = pend;
378 }
379 break;
380
381 case SHAPE_T::BEZIER:
382 {
383 // We do not support Bezier curves in polygons, so approximate with a series
384 // of short lines and put those line segments into the !same! PATH.
385 VECTOR2I nextPt;
386 bool reverse = false;
387
388 // Use the end point furthest away from prevPt as we assume the other
389 // end to be ON prevPt or very close to it.
390 if( closer_to_first( prevPt, graphic->GetStart(), graphic->GetEnd()) )
391 {
392 nextPt = graphic->GetEnd();
393 }
394 else
395 {
396 nextPt = graphic->GetStart();
397 reverse = true;
398 }
399
400 // Ensure the approximated Bezier shape is built
402
403 if( reverse )
404 {
405 for( int jj = graphic->GetBezierPoints().size()-1; jj >= 0; jj-- )
406 {
407 const VECTOR2I& pt = graphic->GetBezierPoints()[jj];
408
409 if( prevPt == pt )
410 continue;
411
412 currContour.Append( pt );
413 shapeOwners[ std::make_pair( prevPt, pt ) ] = graphic;
414 prevPt = pt;
415 }
416 }
417 else
418 {
419 for( const VECTOR2I& pt : graphic->GetBezierPoints() )
420 {
421 if( prevPt == pt )
422 continue;
423
424 currContour.Append( pt );
425 shapeOwners[ std::make_pair( prevPt, pt ) ] = graphic;
426 prevPt = pt;
427 }
428 }
429
430 prevPt = nextPt;
431 }
432 break;
433
434 default:
436 return false;
437 }
438
439 // Get next closest segment.
440 PCB_SHAPE* nextGraphic = findNext( graphic, prevPt, aShapeList, aChainingEpsilon );
441
442 if( nextGraphic && !( nextGraphic->GetFlags() & SKIP_STRUCT ) )
443 {
444 prevGraphic = graphic;
445 graphic = nextGraphic;
446 graphic->SetFlags( SKIP_STRUCT );
447 aCleaner.insert( graphic );
448 startCandidates.erase( graphic );
449 continue;
450 }
451
452 // Finished, or ran into trouble...
453 if( close_enough( startPt, prevPt, aChainingEpsilon ) )
454 {
455 if( startPt != prevPt && currContour.PointCount() > 2 )
456 {
457 // Snap the last shape's endpoint to the outline startpoint
458 PCB_SHAPE* owner = fetchOwner( currContour.CSegment( -1 ) );
459
460 if( currContour.IsArcEnd( currContour.PointCount() - 1 ) )
461 {
462 SHAPE_ARC arc = currContour.Arc(
463 currContour.ArcIndex( currContour.PointCount() - 1 ) );
464
465 // Snap the arc endpoint
466 SHAPE_ARC sarc( arc.GetP0(), arc.GetArcMid(), startPt, 0 );
467
468 SHAPE_LINE_CHAIN arcChain;
469 arcChain.Append( sarc, aErrorMax );
470
471 if( !aAllowUseArcsInPolygons )
472 arcChain.ClearArcs();
473
474 // Set shapeOwners for arcChain points created by appending the sarc:
475 for( int ii = 1; ii < arcChain.PointCount(); ++ii )
476 {
477 shapeOwners[std::make_pair( arcChain.CPoint( ii - 1 ),
478 arcChain.CPoint( ii ) )] = owner;
479 }
480
481 currContour.RemoveShape( currContour.PointCount() - 1 );
482 currContour.Append( arcChain );
483 }
484 else
485 {
486 // Snap the segment endpoint
487 currContour.SetPoint( -1, startPt );
488
489 shapeOwners[std::make_pair( currContour.CPoint( -2 ),
490 currContour.CPoint( -1 ) )] = owner;
491 }
492
493 prevPt = startPt;
494 }
495
496 currContour.SetClosed( true );
497 break;
498 }
499 else if( nextGraphic ) // encountered already-used segment, but not at the start
500 {
501 if( aErrorHandler )
502 (*aErrorHandler)( _( "(self-intersecting)" ), graphic, nextGraphic,
503 prevPt );
504
505 break;
506 }
507 else // encountered discontinuity
508 {
509 if( aErrorHandler )
510 (*aErrorHandler)( _( "(not a closed shape)" ), graphic, nullptr, prevPt );
511
512 break;
513 }
514 }
515 }
516 }
517
518 for( const SHAPE_LINE_CHAIN& contour : contours )
519 {
520 if( !contour.IsClosed() )
521 return false;
522 }
523
524 // First, collect the parents of each contour
525 std::map<int, std::vector<int>> contourToParentIndexesMap;
526
527 for( size_t ii = 0; ii < contours.size(); ++ii )
528 {
529 VECTOR2I firstPt = contours[ii].GetPoint( 0 );
530 std::vector<int> parents;
531
532 for( size_t jj = 0; jj < contours.size(); ++jj )
533 {
534 if( jj == ii )
535 continue;
536
537 const SHAPE_LINE_CHAIN& parentCandidate = contours[jj];
538
539 if( parentCandidate.PointInside( firstPt ) )
540 parents.push_back( jj );
541 }
542
543 contourToParentIndexesMap[ii] = std::move( parents );
544 }
545
546 // Next add those that are top-level outlines to the SHAPE_POLY_SET
547 std::map<int, int> contourToOutlineIdxMap;
548
549 for( const auto& [ contourIndex, parentIndexes ] : contourToParentIndexesMap )
550 {
551 if( parentIndexes.size() %2 == 0 )
552 {
553 // Even number of parents; top-level outline
554 if( !aAllowDisjoint && !aPolygons.IsEmpty() )
555 {
556 if( aErrorHandler )
557 {
558 BOARD_ITEM* a = fetchOwner( aPolygons.Outline( 0 ).GetSegment( 0 ) );
559 BOARD_ITEM* b = fetchOwner( contours[ contourIndex ].GetSegment( 0 ) );
560
561 if( a && b )
562 {
563 (*aErrorHandler)( _( "(multiple board outlines not supported)" ), a, b,
564 contours[ contourIndex ].GetPoint( 0 ) );
565
566 return false;
567 }
568 }
569 }
570
571 aPolygons.AddOutline( contours[ contourIndex ] );
572 contourToOutlineIdxMap[ contourIndex ] = aPolygons.OutlineCount() - 1;
573 }
574 }
575
576 // And finally add the holes
577 for( const auto& [ contourIndex, parentIndexes ] : contourToParentIndexesMap )
578 {
579 if( parentIndexes.size() %2 == 1 )
580 {
581 // Odd number of parents; we're a hole in the parent which has one fewer parents
582 // than we have.
583 const SHAPE_LINE_CHAIN& hole = contours[ contourIndex ];
584
585 for( int parentContourIdx : parentIndexes )
586 {
587 if( contourToParentIndexesMap[ parentContourIdx ].size() == parentIndexes.size() - 1 )
588 {
589 int outlineIdx = contourToOutlineIdxMap[ parentContourIdx ];
590 aPolygons.AddHole( hole, outlineIdx );
591 break;
592 }
593 }
594 }
595 }
596
597 // All of the silliness that follows is to work around the segment iterator while checking
598 // for collisions.
599 // TODO: Implement proper segment and point iterators that follow std
600 for( auto seg1 = aPolygons.IterateSegmentsWithHoles(); seg1; seg1++ )
601 {
602 auto seg2 = seg1;
603
604 for( ++seg2; seg2; seg2++ )
605 {
606 // Check for exact overlapping segments.
607 if( *seg1 == *seg2 || ( ( *seg1 ).A == ( *seg2 ).B && ( *seg1 ).B == ( *seg2 ).A ) )
608 {
609 if( aErrorHandler )
610 {
611 BOARD_ITEM* a = fetchOwner( *seg1 );
612 BOARD_ITEM* b = fetchOwner( *seg2 );
613 (*aErrorHandler)( _( "(self-intersecting)" ), a, b, ( *seg1 ).A );
614 }
615
616 selfIntersecting = true;
617 }
618
619 if( OPT_VECTOR2I pt = seg1.Get().Intersect( seg2.Get(), true ) )
620 {
621 if( aErrorHandler )
622 {
623 BOARD_ITEM* a = fetchOwner( *seg1 );
624 BOARD_ITEM* b = fetchOwner( *seg2 );
625 (*aErrorHandler)( _( "(self-intersecting)" ), a, b, *pt );
626 }
627
628 selfIntersecting = true;
629 }
630 }
631 }
632
633 return !selfIntersecting;
634}
635
636
637bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aShapeList, SHAPE_POLY_SET& aPolygons,
638 int aErrorMax, int aChainingEpsilon, bool aAllowDisjoint,
639 OUTLINE_ERROR_HANDLER* aErrorHandler, bool aAllowUseArcsInPolygons )
640{
642
643 return doConvertOutlineToPolygon( aShapeList, aPolygons, aErrorMax, aChainingEpsilon,
644 aAllowDisjoint, aErrorHandler, aAllowUseArcsInPolygons,
645 cleaner );
646}
647
648
649bool TestBoardOutlinesGraphicItems( BOARD* aBoard, int aMinDist,
650 OUTLINE_ERROR_HANDLER* aErrorHandler )
651{
652 bool success = true;
653 PCB_TYPE_COLLECTOR items;
654 int min_dist = std::max( 0, aMinDist );
655
656 // Get all the shapes into 'items', then keep only those on layer == Edge_Cuts.
657 items.Collect( aBoard, { PCB_SHAPE_T } );
658
659 std::vector<PCB_SHAPE*> shapeList;
660
661 for( int ii = 0; ii < items.GetCount(); ii++ )
662 {
663 PCB_SHAPE* seg = static_cast<PCB_SHAPE*>( items[ii] );
664
665 if( seg->GetLayer() == Edge_Cuts )
666 shapeList.push_back( seg );
667 }
668
669 // Now Test validity of collected items
670 for( PCB_SHAPE* shape : shapeList )
671 {
672 switch( shape->GetShape() )
673 {
675 {
676 VECTOR2I seg = shape->GetEnd() - shape->GetStart();
677 int dim = seg.EuclideanNorm();
678
679 if( dim <= min_dist )
680 {
681 success = false;
682
683 if( aErrorHandler )
684 {
685 (*aErrorHandler)( wxString::Format( _( "(rectangle has null or very small "
686 "size: %d nm)" ), dim ),
687 shape, nullptr, shape->GetStart() );
688 }
689 }
690 break;
691 }
692
693 case SHAPE_T::CIRCLE:
694 {
695 int r = shape->GetRadius();
696
697 if( r <= min_dist )
698 {
699 success = false;
700
701 if( aErrorHandler )
702 {
703 (*aErrorHandler)( wxString::Format( _( "(circle has null or very small "
704 "radius: %d nm)" ), r ),
705 shape, nullptr, shape->GetStart() );
706 }
707 }
708 break;
709 }
710
711 case SHAPE_T::SEGMENT:
712 {
713 VECTOR2I seg = shape->GetEnd() - shape->GetStart();
714 int dim = seg.EuclideanNorm();
715
716 if( dim <= min_dist )
717 {
718 success = false;
719
720 if( aErrorHandler )
721 {
722 (*aErrorHandler)( wxString::Format( _( "(segment has null or very small "
723 "length: %d nm)" ), dim ),
724 shape, nullptr, shape->GetStart() );
725 }
726 }
727 break;
728 }
729
730 case SHAPE_T::ARC:
731 {
732 // Arc size can be evaluated from the distance between arc middle point and arc ends
733 // We do not need a precise value, just an idea of its size
734 VECTOR2I arcMiddle = shape->GetArcMid();
735 VECTOR2I seg1 = arcMiddle - shape->GetStart();
736 VECTOR2I seg2 = shape->GetEnd() - arcMiddle;
737 int dim = seg1.EuclideanNorm() + seg2.EuclideanNorm();
738
739 if( dim <= min_dist )
740 {
741 success = false;
742
743 if( aErrorHandler )
744 {
745 (*aErrorHandler)( wxString::Format( _( "(arc has null or very small size: "
746 "%d nm)" ), dim ),
747 shape, nullptr, shape->GetStart() );
748 }
749 }
750 break;
751 }
752
753 case SHAPE_T::POLY:
754 break;
755
756 case SHAPE_T::BEZIER:
757 break;
758
759 default:
760 UNIMPLEMENTED_FOR( shape->SHAPE_T_asString() );
761 return false;
762 }
763 }
764
765 return success;
766}
767
768
769bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, int aErrorMax,
770 int aChainingEpsilon, OUTLINE_ERROR_HANDLER* aErrorHandler,
771 bool aAllowUseArcsInPolygons )
772{
773 PCB_TYPE_COLLECTOR items;
774 SHAPE_POLY_SET fpHoles;
775 bool success = false;
776
778
779 // Get all the shapes into 'items', then keep only those on layer == Edge_Cuts.
780 items.Collect( aBoard, { PCB_SHAPE_T } );
781
782 for( int ii = 0; ii < items.GetCount(); ++ii )
783 items[ii]->ClearFlags( SKIP_STRUCT );
784
785 for( FOOTPRINT* fp : aBoard->Footprints() )
786 {
787 PCB_TYPE_COLLECTOR fpItems;
788 fpItems.Collect( fp, { PCB_SHAPE_T } );
789
790 std::vector<PCB_SHAPE*> fpSegList;
791
792 for( int ii = 0; ii < fpItems.GetCount(); ii++ )
793 {
794 PCB_SHAPE* fpSeg = static_cast<PCB_SHAPE*>( fpItems[ii] );
795
796 if( fpSeg->GetLayer() == Edge_Cuts )
797 fpSegList.push_back( fpSeg );
798 }
799
800 if( !fpSegList.empty() )
801 {
802 SHAPE_POLY_SET fpOutlines;
803 success = doConvertOutlineToPolygon( fpSegList, fpOutlines, aErrorMax, aChainingEpsilon,
804 false,
805 // don't report errors here; the second pass also
806 // gets an opportunity to use these segments
807 nullptr, aAllowUseArcsInPolygons, cleaner );
808
809 // Test to see if we should make holes or outlines. Holes are made if the footprint
810 // has copper outside of a single, closed outline. If there are multiple outlines,
811 // we assume that the footprint edges represent holes as we do not support multiple
812 // boards. Similarly, if any of the footprint pads are located outside of the edges,
813 // then the edges are holes
814 if( success && ( isCopperOutside( fp, fpOutlines ) || fpOutlines.OutlineCount() > 1 ) )
815 {
816 fpHoles.Append( fpOutlines );
817 }
818 else
819 {
820 // If it wasn't a closed area, or wasn't a hole, the we want to keep the fpSegs
821 // in contention for the board outline builds.
822 for( int ii = 0; ii < fpItems.GetCount(); ++ii )
823 fpItems[ii]->ClearFlags( SKIP_STRUCT );
824 }
825 }
826 }
827
828 // Make a working copy of aSegList, because the list is modified during calculations
829 std::vector<PCB_SHAPE*> segList;
830
831 for( int ii = 0; ii < items.GetCount(); ii++ )
832 {
833 PCB_SHAPE* seg = static_cast<PCB_SHAPE*>( items[ii] );
834
835 // Skip anything already used to generate footprint holes (above)
836 if( seg->GetFlags() & SKIP_STRUCT )
837 continue;
838
839 if( seg->GetLayer() == Edge_Cuts )
840 segList.push_back( seg );
841 }
842
843 if( segList.size() )
844 {
845 success = doConvertOutlineToPolygon( segList, aOutlines, aErrorMax, aChainingEpsilon, true,
846 aErrorHandler, aAllowUseArcsInPolygons, cleaner );
847 }
848
849 if( !success || !aOutlines.OutlineCount() )
850 {
851 // Couldn't create a valid polygon outline. Use the board edge cuts bounding box to
852 // create a rectangular outline, or, failing that, the bounding box of the items on
853 // the board.
854 BOX2I bbbox = aBoard->GetBoardEdgesBoundingBox();
855
856 // If null area, uses the global bounding box.
857 if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
858 bbbox = aBoard->ComputeBoundingBox( false );
859
860 // Ensure non null area. If happen, gives a minimal size.
861 if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
862 bbbox.Inflate( pcbIUScale.mmToIU( 1.0 ) );
863
864 aOutlines.RemoveAllContours();
865 aOutlines.NewOutline();
866
867 VECTOR2I corner;
868 aOutlines.Append( bbbox.GetOrigin() );
869
870 corner.x = bbbox.GetOrigin().x;
871 corner.y = bbbox.GetEnd().y;
872 aOutlines.Append( corner );
873
874 aOutlines.Append( bbbox.GetEnd() );
875
876 corner.x = bbbox.GetEnd().x;
877 corner.y = bbbox.GetOrigin().y;
878 aOutlines.Append( corner );
879 }
880
881 for( int ii = 0; ii < fpHoles.OutlineCount(); ++ii )
882 {
883 const VECTOR2I holePt = fpHoles.Outline( ii ).CPoint( 0 );
884
885 for( int jj = 0; jj < aOutlines.OutlineCount(); ++jj )
886 {
887 if( aOutlines.Outline( jj ).PointInside( holePt ) )
888 {
889 aOutlines.AddHole( fpHoles.Outline( ii ), jj );
890 break;
891 }
892 }
893 }
894
895 return success;
896}
897
898
911void buildBoardBoundingBoxPoly( const BOARD* aBoard, SHAPE_POLY_SET& aOutline )
912{
913 BOX2I bbbox = aBoard->GetBoundingBox();
915
916 // If null area, uses the global bounding box.
917 if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
918 bbbox = aBoard->ComputeBoundingBox( false );
919
920 // Ensure non null area. If happen, gives a minimal size.
921 if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
922 bbbox.Inflate( pcbIUScale.mmToIU( 1.0 ) );
923
924 // Inflate slightly (by 1/10th the size of the box)
925 bbbox.Inflate( bbbox.GetWidth() / 10, bbbox.GetHeight() / 10 );
926
927 chain.Append( bbbox.GetOrigin() );
928 chain.Append( bbbox.GetOrigin().x, bbbox.GetEnd().y );
929 chain.Append( bbbox.GetEnd() );
930 chain.Append( bbbox.GetEnd().x, bbbox.GetOrigin().y );
931 chain.SetClosed( true );
932
933 aOutline.RemoveAllContours();
934 aOutline.AddOutline( chain );
935}
936
937
938VECTOR2I projectPointOnSegment( const VECTOR2I& aEndPoint, const SHAPE_POLY_SET& aOutline,
939 int aOutlineNum = 0 )
940{
941 int minDistance = -1;
942 VECTOR2I projPoint;
943
944 for( auto it = aOutline.CIterateSegments( aOutlineNum ); it; it++ )
945 {
946 auto seg = it.Get();
947 int dis = seg.Distance( aEndPoint );
948
949 if( minDistance < 0 || ( dis < minDistance ) )
950 {
951 minDistance = dis;
952 projPoint = seg.NearestPoint( aEndPoint );
953 }
954 }
955
956 return projPoint;
957}
958
959
960int findEndSegments( SHAPE_LINE_CHAIN& aChain, SEG& aStartSeg, SEG& aEndSeg )
961{
962 int foundSegs = 0;
963
964 for( int i = 0; i < aChain.SegmentCount(); i++ )
965 {
966 SEG seg = aChain.Segment( i );
967
968 bool foundA = false;
969 bool foundB = false;
970
971 for( int j = 0; j < aChain.SegmentCount(); j++ )
972 {
973 // Don't test the segment against itself
974 if( i == j )
975 continue;
976
977 SEG testSeg = aChain.Segment( j );
978
979 if( testSeg.Contains( seg.A ) )
980 foundA = true;
981
982 if( testSeg.Contains( seg.B ) )
983 foundB = true;
984 }
985
986 // This segment isn't a start or end
987 if( foundA && foundB )
988 continue;
989
990 if( foundSegs == 0 )
991 {
992 // The first segment we encounter is the "start" segment
993 wxLogTrace( traceBoardOutline, wxT( "Found start segment: (%d, %d)-(%d, %d)" ),
994 seg.A.x, seg.A.y, seg.B.x, seg.B.y );
995 aStartSeg = seg;
996 foundSegs++;
997 }
998 else
999 {
1000 // Once we find both start and end, we can stop
1001 wxLogTrace( traceBoardOutline, wxT( "Found end segment: (%d, %d)-(%d, %d)" ),
1002 seg.A.x, seg.A.y, seg.B.x, seg.B.y );
1003 aEndSeg = seg;
1004 foundSegs++;
1005 break;
1006 }
1007 }
1008
1009 return foundSegs;
1010}
1011
1012
1013bool BuildFootprintPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, int aErrorMax,
1014 int aChainingEpsilon, OUTLINE_ERROR_HANDLER* aErrorHandler )
1015
1016{
1017 FOOTPRINT* footprint = aBoard->GetFirstFootprint();
1018
1019 // No footprint loaded
1020 if( !footprint )
1021 {
1022 wxLogTrace( traceBoardOutline, wxT( "No footprint found on board" ) );
1023 return false;
1024 }
1025
1026 PCB_TYPE_COLLECTOR items;
1027 SHAPE_POLY_SET outlines;
1028 bool success = false;
1029
1031
1032 // Get all the SHAPEs into 'items', then keep only those on layer == Edge_Cuts.
1033 items.Collect( aBoard, { PCB_SHAPE_T } );
1034
1035 // Make a working copy of aSegList, because the list is modified during calculations
1036 std::vector<PCB_SHAPE*> segList;
1037
1038 for( int ii = 0; ii < items.GetCount(); ii++ )
1039 {
1040 if( items[ii]->GetLayer() == Edge_Cuts )
1041 segList.push_back( static_cast<PCB_SHAPE*>( items[ii] ) );
1042 }
1043
1044 if( !segList.empty() )
1045 {
1046 success = doConvertOutlineToPolygon( segList, outlines, aErrorMax, aChainingEpsilon, true,
1047 aErrorHandler, false, cleaner );
1048 }
1049
1050 // A closed outline was found on Edge_Cuts
1051 if( success )
1052 {
1053 wxLogTrace( traceBoardOutline, wxT( "Closed outline found" ) );
1054
1055 // If copper is outside a closed polygon, treat it as a hole
1056 // If there are multiple outlines in the footprint, they are also holes
1057 if( isCopperOutside( footprint, outlines ) || outlines.OutlineCount() > 1 )
1058 {
1059 wxLogTrace( traceBoardOutline, wxT( "Treating outline as a hole" ) );
1060
1061 buildBoardBoundingBoxPoly( aBoard, aOutlines );
1062
1063 // Copy all outlines from the conversion as holes into the new outline
1064 for( int i = 0; i < outlines.OutlineCount(); i++ )
1065 {
1066 SHAPE_LINE_CHAIN& out = outlines.Outline( i );
1067
1068 if( out.IsClosed() )
1069 aOutlines.AddHole( out, -1 );
1070
1071 for( int j = 0; j < outlines.HoleCount( i ); j++ )
1072 {
1073 SHAPE_LINE_CHAIN& hole = outlines.Hole( i, j );
1074
1075 if( hole.IsClosed() )
1076 aOutlines.AddHole( hole, -1 );
1077 }
1078 }
1079 }
1080 // If all copper is inside, then the computed outline is the board outline
1081 else
1082 {
1083 wxLogTrace( traceBoardOutline, wxT( "Treating outline as board edge" ) );
1084 aOutlines = outlines;
1085 }
1086
1087 return true;
1088 }
1089 // No board outlines were found, so use the bounding box
1090 else if( outlines.OutlineCount() == 0 )
1091 {
1092 wxLogTrace( traceBoardOutline, wxT( "Using footprint bounding box" ) );
1093 buildBoardBoundingBoxPoly( aBoard, aOutlines );
1094
1095 return true;
1096 }
1097 // There is an outline present, but it is not closed
1098 else
1099 {
1100 wxLogTrace( traceBoardOutline, wxT( "Trying to build outline" ) );
1101
1102 std::vector<SHAPE_LINE_CHAIN> closedChains;
1103 std::vector<SHAPE_LINE_CHAIN> openChains;
1104
1105 // The ConvertOutlineToPolygon function returns only one main outline and the rest as
1106 // holes, so we promote the holes and process them
1107 openChains.push_back( outlines.Outline( 0 ) );
1108
1109 for( int j = 0; j < outlines.HoleCount( 0 ); j++ )
1110 {
1111 SHAPE_LINE_CHAIN hole = outlines.Hole( 0, j );
1112
1113 if( hole.IsClosed() )
1114 {
1115 wxLogTrace( traceBoardOutline, wxT( "Found closed hole" ) );
1116 closedChains.push_back( hole );
1117 }
1118 else
1119 {
1120 wxLogTrace( traceBoardOutline, wxT( "Found open hole" ) );
1121 openChains.push_back( hole );
1122 }
1123 }
1124
1125 SHAPE_POLY_SET bbox;
1126 buildBoardBoundingBoxPoly( aBoard, bbox );
1127
1128 // Treat the open polys as the board edge
1129 SHAPE_LINE_CHAIN chain = openChains[0];
1130 SHAPE_LINE_CHAIN rect = bbox.Outline( 0 );
1131
1132 // We know the outline chain is open, so set to non-closed to get better segment count
1133 chain.SetClosed( false );
1134
1135 SEG startSeg;
1136 SEG endSeg;
1137
1138 // The two possible board outlines
1139 SHAPE_LINE_CHAIN upper;
1140 SHAPE_LINE_CHAIN lower;
1141
1142 findEndSegments( chain, startSeg, endSeg );
1143
1144 if( chain.SegmentCount() == 0 )
1145 {
1146 // Something is wrong, bail out with the overall footprint bounding box
1147 wxLogTrace( traceBoardOutline, wxT( "No line segments in provided outline" ) );
1148 aOutlines = bbox;
1149 return true;
1150 }
1151 else if( chain.SegmentCount() == 1 )
1152 {
1153 // This case means there is only 1 line segment making up the edge cuts of the
1154 // footprint, so we just need to use it to cut the bounding box in half.
1155 wxLogTrace( traceBoardOutline, wxT( "Only 1 line segment in provided outline" ) );
1156
1157 startSeg = chain.Segment( 0 );
1158
1159 // Intersect with all the sides of the rectangle
1160 OPT_VECTOR2I inter0 = startSeg.IntersectLines( rect.Segment( 0 ) );
1161 OPT_VECTOR2I inter1 = startSeg.IntersectLines( rect.Segment( 1 ) );
1162 OPT_VECTOR2I inter2 = startSeg.IntersectLines( rect.Segment( 2 ) );
1163 OPT_VECTOR2I inter3 = startSeg.IntersectLines( rect.Segment( 3 ) );
1164
1165 if( inter0 && inter2 && !inter1 && !inter3 )
1166 {
1167 // Intersects the vertical rectangle sides only
1168 wxLogTrace( traceBoardOutline, wxT( "Segment intersects only vertical bbox "
1169 "sides" ) );
1170
1171 // The upper half
1172 upper.Append( *inter0 );
1173 upper.Append( rect.GetPoint( 1 ) );
1174 upper.Append( rect.GetPoint( 2 ) );
1175 upper.Append( *inter2 );
1176 upper.SetClosed( true );
1177
1178 // The lower half
1179 lower.Append( *inter0 );
1180 lower.Append( rect.GetPoint( 0 ) );
1181 lower.Append( rect.GetPoint( 3 ) );
1182 lower.Append( *inter2 );
1183 lower.SetClosed( true );
1184 }
1185 else if( inter1 && inter3 && !inter0 && !inter2 )
1186 {
1187 // Intersects the horizontal rectangle sides only
1188 wxLogTrace( traceBoardOutline, wxT( "Segment intersects only horizontal bbox "
1189 "sides" ) );
1190
1191 // The left half
1192 upper.Append( *inter1 );
1193 upper.Append( rect.GetPoint( 1 ) );
1194 upper.Append( rect.GetPoint( 0 ) );
1195 upper.Append( *inter3 );
1196 upper.SetClosed( true );
1197
1198 // The right half
1199 lower.Append( *inter1 );
1200 lower.Append( rect.GetPoint( 2 ) );
1201 lower.Append( rect.GetPoint( 3 ) );
1202 lower.Append( *inter3 );
1203 lower.SetClosed( true );
1204 }
1205 else
1206 {
1207 // Angled line segment that cuts across a corner
1208 wxLogTrace( traceBoardOutline, wxT( "Segment intersects two perpendicular bbox "
1209 "sides" ) );
1210
1211 // Figure out which actual lines are intersected, since IntersectLines assumes
1212 // an infinite line
1213 bool hit0 = rect.Segment( 0 ).Contains( *inter0 );
1214 bool hit1 = rect.Segment( 1 ).Contains( *inter1 );
1215 bool hit2 = rect.Segment( 2 ).Contains( *inter2 );
1216 bool hit3 = rect.Segment( 3 ).Contains( *inter3 );
1217
1218 if( hit0 && hit1 )
1219 {
1220 // Cut across the upper left corner
1221 wxLogTrace( traceBoardOutline, wxT( "Segment cuts upper left corner" ) );
1222
1223 // The upper half
1224 upper.Append( *inter0 );
1225 upper.Append( rect.GetPoint( 1 ) );
1226 upper.Append( *inter1 );
1227 upper.SetClosed( true );
1228
1229 // The lower half
1230 lower.Append( *inter0 );
1231 lower.Append( rect.GetPoint( 0 ) );
1232 lower.Append( rect.GetPoint( 3 ) );
1233 lower.Append( rect.GetPoint( 2 ) );
1234 lower.Append( *inter1 );
1235 lower.SetClosed( true );
1236 }
1237 else if( hit1 && hit2 )
1238 {
1239 // Cut across the upper right corner
1240 wxLogTrace( traceBoardOutline, wxT( "Segment cuts upper right corner" ) );
1241
1242 // The upper half
1243 upper.Append( *inter1 );
1244 upper.Append( rect.GetPoint( 2 ) );
1245 upper.Append( *inter2 );
1246 upper.SetClosed( true );
1247
1248 // The lower half
1249 lower.Append( *inter1 );
1250 lower.Append( rect.GetPoint( 1 ) );
1251 lower.Append( rect.GetPoint( 0 ) );
1252 lower.Append( rect.GetPoint( 3 ) );
1253 lower.Append( *inter2 );
1254 lower.SetClosed( true );
1255 }
1256 else if( hit2 && hit3 )
1257 {
1258 // Cut across the lower right corner
1259 wxLogTrace( traceBoardOutline, wxT( "Segment cuts lower right corner" ) );
1260
1261 // The upper half
1262 upper.Append( *inter2 );
1263 upper.Append( rect.GetPoint( 2 ) );
1264 upper.Append( rect.GetPoint( 1 ) );
1265 upper.Append( rect.GetPoint( 0 ) );
1266 upper.Append( *inter3 );
1267 upper.SetClosed( true );
1268
1269 // The bottom half
1270 lower.Append( *inter2 );
1271 lower.Append( rect.GetPoint( 3 ) );
1272 lower.Append( *inter3 );
1273 lower.SetClosed( true );
1274 }
1275 else
1276 {
1277 // Cut across the lower left corner
1278 wxLogTrace( traceBoardOutline, wxT( "Segment cuts upper left corner" ) );
1279
1280 // The upper half
1281 upper.Append( *inter0 );
1282 upper.Append( rect.GetPoint( 1 ) );
1283 upper.Append( rect.GetPoint( 2 ) );
1284 upper.Append( rect.GetPoint( 3 ) );
1285 upper.Append( *inter3 );
1286 upper.SetClosed( true );
1287
1288 // The bottom half
1289 lower.Append( *inter0 );
1290 lower.Append( rect.GetPoint( 0 ) );
1291 lower.Append( *inter3 );
1292 lower.SetClosed( true );
1293 }
1294 }
1295 }
1296 else
1297 {
1298 // More than 1 segment
1299 wxLogTrace( traceBoardOutline, wxT( "Multiple segments in outline" ) );
1300
1301 // Just a temporary thing
1302 aOutlines = bbox;
1303 return true;
1304 }
1305
1306 // Figure out which is the correct outline
1307 SHAPE_POLY_SET poly1;
1308 SHAPE_POLY_SET poly2;
1309
1310 poly1.NewOutline();
1311 poly1.Append( upper );
1312
1313 poly2.NewOutline();
1314 poly2.Append( lower );
1315
1316 if( isCopperOutside( footprint, poly1 ) )
1317 {
1318 wxLogTrace( traceBoardOutline, wxT( "Using lower shape" ) );
1319 aOutlines = poly2;
1320 }
1321 else
1322 {
1323 wxLogTrace( traceBoardOutline, wxT( "Using upper shape" ) );
1324 aOutlines = poly1;
1325 }
1326
1327 // Add all closed polys as holes to the main outline
1328 for( SHAPE_LINE_CHAIN& closedChain : closedChains )
1329 {
1330 wxLogTrace( traceBoardOutline, wxT( "Adding hole to main outline" ) );
1331 aOutlines.AddHole( closedChain, -1 );
1332 }
1333
1334 return true;
1335 }
1336
1337 // We really shouldn't reach this point
1338 return false;
1339}
@ 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:946
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: board.h:932
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition: board.h:462
BOX2I ComputeBoundingBox(bool aBoardEdgesOnly=false) const
Calculate the bounding box containing all board items (or board edge segments).
Definition: board.cpp:1744
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:125
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition: eda_item.h:127
EDA_ITEM_FLAGS GetFlags() const
Definition: eda_item.h:128
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
const VECTOR2I & GetArcMid() const
Definition: shape_arc.h:118
const VECTOR2I & GetP0() const
Definition: shape_arc.h:116
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...
const SHAPE_ARC & Arc(size_t aArc) const
bool IsClosed() const override
virtual const VECTOR2I GetPoint(int aIndex) const override
void SetPoint(int aIndex, const VECTOR2I &aPos)
Move a point to a specific location.
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.
bool IsArcEnd(size_t aIndex) const
void ClearArcs()
Remove all arc references in the line chain, resulting in a chain formed only of straight segments.
ssize_t ArcIndex(size_t aSegment) const
Return the arc index for the given segment index.
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.
const SEG CSegment(int aIndex) const
Return a constant copy of the aIndex segment in the line chain.
void RemoveShape(int aPointIndex)
Remove the shape at the given 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
VECTOR2I center
const SHAPE_LINE_CHAIN chain
int radius
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88