KiCad PCB EDA Suite
convert_shape_list_to_polygon.h File Reference

Go to the source code of this file.

Typedefs

typedef const std::function< void(const wxString &msg, BOARD_ITEM *itemA, BOARD_ITEM *itemB, const VECTOR2I &pt)> OUTLINE_ERROR_HANDLER
 

Functions

bool ConvertOutlineToPolygon (std::vector< PCB_SHAPE * > &aSegList, SHAPE_POLY_SET &aPolygons, int aErrorMax, int aChainingEpsilon, OUTLINE_ERROR_HANDLER *aErrorHandler=nullptr)
 Function ConvertOutlineToPolygon build a polygon (with holes) from a PCB_SHAPE list, which is expected to be a outline, therefore a closed main outline with perhaps closed inner outlines. More...
 
bool BuildBoardPolygonOutlines (BOARD *aBoard, SHAPE_POLY_SET &aOutlines, int aErrorMax, int aChainingEpsilon, OUTLINE_ERROR_HANDLER *aErrorHandler=nullptr)
 Extracts the board outlines and build a closed polygon from lines, arcs and circle items on edge cut layer. More...
 

Typedef Documentation

◆ OUTLINE_ERROR_HANDLER

typedef const std::function<void( const wxString& msg, BOARD_ITEM* itemA, BOARD_ITEM* itemB, const VECTOR2I& pt )> OUTLINE_ERROR_HANDLER

Definition at line 33 of file convert_shape_list_to_polygon.h.

Function Documentation

◆ BuildBoardPolygonOutlines()

bool BuildBoardPolygonOutlines ( BOARD aBoard,
SHAPE_POLY_SET aOutlines,
int  aErrorMax,
int  aChainingEpsilon,
OUTLINE_ERROR_HANDLER aErrorHandler = nullptr 
)

Extracts the board outlines and build a closed polygon from lines, arcs and circle items on edge cut layer.

Any closed outline inside the main outline is a hole. All contours should be closed, i.e. are valid vertices for a closed polygon.

Returns
true if success, false if a contour is not valid

Definition at line 842 of file convert_shape_list_to_polygon.cpp.

844{
845 PCB_TYPE_COLLECTOR items;
846 bool success = false;
847
848 // Get all the PCB and FP shapes into 'items', then keep only those on layer == Edge_Cuts.
849 items.Collect( aBoard, { PCB_SHAPE_T, PCB_FP_SHAPE_T } );
850
851 // Make a working copy of aSegList, because the list is modified during calculations
852 std::vector<PCB_SHAPE*> segList;
853
854 for( int ii = 0; ii < items.GetCount(); ii++ )
855 {
856 if( items[ii]->GetLayer() == Edge_Cuts )
857 segList.push_back( static_cast<PCB_SHAPE*>( items[ii] ) );
858 }
859
860 if( segList.size() )
861 {
862 success = ConvertOutlineToPolygon( segList, aOutlines, aErrorMax, aChainingEpsilon,
863 aErrorHandler );
864 }
865
866 if( !success || !aOutlines.OutlineCount() )
867 {
868 // Couldn't create a valid polygon outline. Use the board edge cuts bounding box to
869 // create a rectangular outline, or, failing that, the bounding box of the items on
870 // the board.
871
872 BOX2I bbbox = aBoard->GetBoardEdgesBoundingBox();
873
874 // If null area, uses the global bounding box.
875 if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
876 bbbox = aBoard->ComputeBoundingBox();
877
878 // Ensure non null area. If happen, gives a minimal size.
879 if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
880 bbbox.Inflate( pcbIUScale.mmToIU( 1.0 ) );
881
882 aOutlines.RemoveAllContours();
883 aOutlines.NewOutline();
884
885 wxPoint corner;
886 aOutlines.Append( bbbox.GetOrigin() );
887
888 corner.x = bbbox.GetOrigin().x;
889 corner.y = bbbox.GetEnd().y;
890 aOutlines.Append( corner );
891
892 aOutlines.Append( bbbox.GetEnd() );
893
894 corner.x = bbbox.GetEnd().x;
895 corner.y = bbbox.GetOrigin().y;
896 aOutlines.Append( corner );
897 }
898
899 return success;
900}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
const BOX2I GetBoardEdgesBoundingBox() const
Return the board bounding box calculated using exclusively the board edges (graphics on Edge....
Definition: board.h:821
BOX2I ComputeBoundingBox(bool aBoardEdgesOnly=false) const
Calculate the bounding box containing all board items (or board edge segments).
Definition: board.cpp:1166
const Vec & GetOrigin() const
Definition: box2.h:183
coord_type GetHeight() const
Definition: box2.h:188
coord_type GetWidth() const
Definition: box2.h:187
const Vec GetEnd() const
Definition: box2.h:185
BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:506
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:81
Collect all BOARD_ITEM objects of a given set of KICAD_T type(s).
Definition: collectors.h:522
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:631
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Add a new vertex to the contour indexed by aOutline and aHole (defaults to the outline of the last po...
int NewOutline()
Creates a new hole in a given outline.
int OutlineCount() const
Return the number of vertices in a given outline/hole.
bool ConvertOutlineToPolygon(std::vector< PCB_SHAPE * > &aSegList, SHAPE_POLY_SET &aPolygons, int aErrorMax, int aChainingEpsilon, OUTLINE_ERROR_HANDLER *aErrorHandler)
Function ConvertOutlineToPolygon Build a polygon (with holes) from a PCB_SHAPE list,...
@ Edge_Cuts
Definition: layer_ids.h:113
constexpr int mmToIU(double mm) const
Definition: base_units.h:89
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88
@ PCB_FP_SHAPE_T
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:94

References SHAPE_POLY_SET::Append(), PCB_TYPE_COLLECTOR::Collect(), BOARD::ComputeBoundingBox(), ConvertOutlineToPolygon(), Edge_Cuts, BOARD::GetBoardEdgesBoundingBox(), COLLECTOR::GetCount(), BOX2< Vec >::GetEnd(), BOX2< Vec >::GetHeight(), BOX2< Vec >::GetOrigin(), BOX2< Vec >::GetWidth(), BOX2< Vec >::Inflate(), EDA_IU_SCALE::mmToIU(), SHAPE_POLY_SET::NewOutline(), SHAPE_POLY_SET::OutlineCount(), PCB_FP_SHAPE_T, PCB_SHAPE_T, pcbIUScale, SHAPE_POLY_SET::RemoveAllContours(), VECTOR2< T >::x, and VECTOR2< T >::y.

Referenced by BOARD::GetBoardPolygonOutlines(), DIALOG_EXPORT_STEP::onExportButton(), and DRC_TEST_PROVIDER_MISC::testOutline().

◆ ConvertOutlineToPolygon()

bool ConvertOutlineToPolygon ( std::vector< PCB_SHAPE * > &  aSegList,
SHAPE_POLY_SET aPolygons,
int  aErrorMax,
int  aChainingEpsilon,
OUTLINE_ERROR_HANDLER aErrorHandler 
)

Function ConvertOutlineToPolygon build a polygon (with holes) from a PCB_SHAPE list, which is expected to be a outline, therefore a closed main outline with perhaps closed inner outlines.

These closed inner outlines are considered as holes in the main outline

Parameters
aSegListthe initial list of drawsegments (only lines, circles and arcs).
aPolygonswill contain the complex polygon.
aErrorMaxis the max error distance when polygonizing a curve (internal units)
aChainingEpsilonis the max distance from one endPt to the next startPt (internal units)
aErrorHandler= an optional error handler

Function ConvertOutlineToPolygon build a polygon (with holes) from a PCB_SHAPE list, which is expected to be a outline, therefore a closed main outline with perhaps closed inner outlines.

These closed inner outlines are considered as holes in the main outline.

Parameters
aSegListthe initial list of drawsegments (only lines, circles and arcs).
aPolygonswill contain the complex polygon.
aErrorMaxis the max error distance when polygonizing a curve (internal units)
aChainingEpsilonis the max error distance when polygonizing a curve (internal units)
aErrorHandler= an optional error handler

Definition at line 146 of file convert_shape_list_to_polygon.cpp.

149{
150 if( aSegList.size() == 0 )
151 return true;
152
153 bool polygonComplete = false;
154 bool selfIntersecting = false;
155
156 wxString msg;
157 PCB_SHAPE* graphic = nullptr;
158
159 std::set<PCB_SHAPE*> startCandidates( aSegList.begin(), aSegList.end() );
160
161 // Find edge point with minimum x, this should be in the outer polygon
162 // which will define the perimeter polygon polygon.
163 VECTOR2I xmin = VECTOR2I( INT_MAX, 0 );
164 int xmini = 0;
165
166 for( size_t i = 0; i < aSegList.size(); i++ )
167 {
168 graphic = (PCB_SHAPE*) aSegList[i];
169 graphic->ClearFlags( SKIP_STRUCT );
170
171 switch( graphic->GetShape() )
172 {
173 case SHAPE_T::RECT:
174 case SHAPE_T::SEGMENT:
175 {
176 if( graphic->GetStart().x < xmin.x )
177 {
178 xmin = graphic->GetStart();
179 xmini = i;
180 }
181
182 if( graphic->GetEnd().x < xmin.x )
183 {
184 xmin = graphic->GetEnd();
185 xmini = i;
186 }
187 }
188 break;
189
190 case SHAPE_T::ARC:
191 {
192 VECTOR2I pstart = graphic->GetStart();
193 VECTOR2I center = graphic->GetCenter();
194 EDA_ANGLE angle = -graphic->GetArcAngle();
195 double radius = graphic->GetRadius();
196 int steps = GetArcToSegmentCount( radius, aErrorMax, angle );
197
198 for( int step = 1; step<=steps; ++step )
199 {
200 EDA_ANGLE rotation = ( angle * step ) / steps;
201 VECTOR2I pt = pstart;
202
203 RotatePoint( pt, center, rotation );
204
205 if( pt.x < xmin.x )
206 {
207 xmin = pt;
208 xmini = i;
209 }
210 }
211 }
212 break;
213
214 case SHAPE_T::CIRCLE:
215 {
216 VECTOR2I pt = graphic->GetCenter();
217
218 // pt has minimum x point
219 pt.x -= graphic->GetRadius();
220
221 // when the radius <= 0, this is a mal-formed circle. Skip it
222 if( graphic->GetRadius() > 0 && pt.x < xmin.x )
223 {
224 xmin = pt;
225 xmini = i;
226 }
227 }
228 break;
229
230 case SHAPE_T::BEZIER:
231 {
232 graphic->RebuildBezierToSegmentsPointsList( graphic->GetWidth() );
233
234 for( const VECTOR2I& pt : graphic->GetBezierPoints() )
235 {
236 if( pt.x < xmin.x )
237 {
238 xmin = pt;
239 xmini = i;
240 }
241 }
242 }
243 break;
244
245 case SHAPE_T::POLY:
246 {
247 const SHAPE_POLY_SET& poly = graphic->GetPolyShape();
248 EDA_ANGLE orientation = ANGLE_0;
249 VECTOR2I offset = VECTOR2I( 0, 0 );
250
251 if( graphic->GetParentFootprint() )
252 {
253 orientation = graphic->GetParentFootprint()->GetOrientation();
254 offset = graphic->GetParentFootprint()->GetPosition();
255 }
256
257 for( auto iter = poly.CIterate(); iter; iter++ )
258 {
259 VECTOR2I pt = *iter;
260 RotatePoint( pt, orientation );
261 pt += offset;
262
263 if( pt.x < xmin.x )
264 {
265 xmin.x = pt.x;
266 xmin.y = pt.y;
267 xmini = i;
268 }
269 }
270 }
271 break;
272
273 default:
274 break;
275 }
276 }
277
278 // Keep a list of where the various segments came from so after doing our combined-polygon
279 // tests we can still report errors against the individual graphic items.
280 std::map<std::pair<VECTOR2I, VECTOR2I>, PCB_SHAPE*> segOwners;
281
282 auto fetchOwner =
283 [&]( const SEG& seg ) -> PCB_SHAPE*
284 {
285 auto it = segOwners.find( std::make_pair( seg.A, seg.B ) );
286 return it == segOwners.end() ? nullptr : it->second;
287 };
288
289 // Grab the left most point, assume its on the board's perimeter, and see if we can put
290 // enough graphics together by matching endpoints to formulate a cohesive polygon.
291
292 PCB_SHAPE* prevGraphic = nullptr;
293 VECTOR2I prevPt;
294
295 graphic = (PCB_SHAPE*) aSegList[xmini];
296 graphic->SetFlags( SKIP_STRUCT );
297 startCandidates.erase( graphic );
298
299 // Output the outline perimeter as polygon.
300 if( graphic->GetShape() == SHAPE_T::CIRCLE )
301 {
302 TransformCircleToPolygon( aPolygons, graphic->GetCenter(), graphic->GetRadius(),
304 polygonComplete = true;
305 }
306 else if( graphic->GetShape() == SHAPE_T::RECT )
307 {
308 std::vector<VECTOR2I> pts = graphic->GetRectCorners();
309
310 aPolygons.NewOutline();
311
312 for( const VECTOR2I& pt : pts )
313 aPolygons.Append( pt );
314
315 segOwners[ std::make_pair( pts[0], pts[1] ) ] = graphic;
316 segOwners[ std::make_pair( pts[1], pts[2] ) ] = graphic;
317 segOwners[ std::make_pair( pts[2], pts[3] ) ] = graphic;
318 segOwners[ std::make_pair( pts[3], pts[0] ) ] = graphic;
319
320 polygonComplete = true;
321 }
322 else if( graphic->GetShape() == SHAPE_T::POLY )
323 {
324 EDA_ANGLE orientation = ANGLE_0;
325 VECTOR2I offset = VECTOR2I( 0, 0 );
326
327 if( graphic->GetParentFootprint() )
328 {
329 orientation = graphic->GetParentFootprint()->GetOrientation();
330 offset = graphic->GetParentFootprint()->GetPosition();
331 }
332
333 aPolygons.NewOutline();
334 bool first = true;
335
336 for( auto it = graphic->GetPolyShape().CIterate( 0 ); it; it++ )
337 {
338 VECTOR2I pt = *it;
339 RotatePoint( pt, orientation );
340 pt += offset;
341 aPolygons.Append( pt );
342
343 if( first )
344 first = false;
345 else
346 segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
347
348 prevPt = pt;
349 }
350
351 polygonComplete = true;
352 }
353 else
354 {
355 // Polygon start point. Arbitrarily choose an end of the segment and build the polygon
356 // from there.
357
358 VECTOR2I startPt = graphic->GetEnd();
359
360 prevPt = startPt;
361 aPolygons.NewOutline();
362 aPolygons.Append( prevPt );
363
364 // Do not append the other end point yet of this 'graphic', this first 'graphic' might
365 // be an arc or a curve.
366
367 for(;;)
368 {
369 switch( graphic->GetShape() )
370 {
371 case SHAPE_T::RECT:
372 case SHAPE_T::CIRCLE:
373 {
374 // As a non-first item, closed shapes can't be anything but self-intersecting
375
376 if( aErrorHandler )
377 {
378 wxASSERT( prevGraphic );
379 (*aErrorHandler)( _( "(self-intersecting)" ), prevGraphic, graphic, prevPt );
380 }
381
382 selfIntersecting = true;
383
384 // A closed shape will finish where it started, so no point in updating prevPt
385 }
386 break;
387
388 case SHAPE_T::SEGMENT:
389 {
390 VECTOR2I nextPt;
391
392 // Use the line segment end point furthest away from prevPt as we assume the
393 // other end to be ON prevPt or very close to it.
394
395 if( closer_to_first( prevPt, graphic->GetStart(), graphic->GetEnd()) )
396 nextPt = graphic->GetEnd();
397 else
398 nextPt = graphic->GetStart();
399
400 aPolygons.Append( nextPt );
401 segOwners[ std::make_pair( prevPt, nextPt ) ] = graphic;
402 prevPt = nextPt;
403 }
404 break;
405
406 case SHAPE_T::ARC:
407 {
408 // We do not support arcs in polygons, so approximate an arc with a series of
409 // short lines and put those line segments into the !same! PATH.
410
411 VECTOR2I pstart = graphic->GetStart();
412 VECTOR2I pend = graphic->GetEnd();
413 VECTOR2I pcenter = graphic->GetCenter();
414 EDA_ANGLE angle = -graphic->GetArcAngle();
415 double radius = graphic->GetRadius();
416 int steps = GetArcToSegmentCount( radius, aErrorMax, angle );
417
418 if( !close_enough( prevPt, pstart, aChainingEpsilon ) )
419 {
420 wxASSERT( close_enough( prevPt, graphic->GetEnd(), aChainingEpsilon ) );
421
422 angle = -angle;
423 std::swap( pstart, pend );
424 }
425
426 // Create intermediate points between start and end:
427 for( int step = 1; step < steps; ++step )
428 {
429 EDA_ANGLE rotation = ( angle * step ) / steps;
430 VECTOR2I pt = pstart;
431
432 RotatePoint( pt, pcenter, rotation );
433
434 aPolygons.Append( pt );
435 segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
436 prevPt = pt;
437 }
438
439 // Append the last arc end point
440 aPolygons.Append( pend );
441 segOwners[ std::make_pair( prevPt, pend ) ] = graphic;
442 prevPt = pend;
443 }
444 break;
445
446 case SHAPE_T::BEZIER:
447 {
448 // We do not support Bezier curves in polygons, so approximate with a series
449 // of short lines and put those line segments into the !same! PATH.
450
451 VECTOR2I nextPt;
452 bool first = true;
453 bool reverse = false;
454
455 // Use the end point furthest away from
456 // prevPt as we assume the other end to be ON prevPt or
457 // very close to it.
458
459 if( closer_to_first( prevPt, graphic->GetStart(), graphic->GetEnd()) )
460 {
461 nextPt = graphic->GetEnd();
462 }
463 else
464 {
465 nextPt = graphic->GetStart();
466 reverse = true;
467 }
468
469 if( reverse )
470 {
471 for( int jj = graphic->GetBezierPoints().size()-1; jj >= 0; jj-- )
472 {
473 const VECTOR2I& pt = graphic->GetBezierPoints()[jj];
474 aPolygons.Append( pt );
475
476 if( first )
477 first = false;
478 else
479 segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
480
481 prevPt = pt;
482 }
483 }
484 else
485 {
486 for( const VECTOR2I& pt : graphic->GetBezierPoints() )
487 {
488 aPolygons.Append( pt );
489
490 if( first )
491 first = false;
492 else
493 segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
494
495 prevPt = pt;
496 }
497 }
498
499 prevPt = nextPt;
500 }
501 break;
502
503 default:
505 return false;
506 }
507
508 // Get next closest segment.
509
510 PCB_SHAPE* nextGraphic = findNext( graphic, prevPt, aSegList, aChainingEpsilon );
511
512 if( nextGraphic && !( nextGraphic->GetFlags() & SKIP_STRUCT ) )
513 {
514 prevGraphic = graphic;
515 graphic = nextGraphic;
516 graphic->SetFlags( SKIP_STRUCT );
517 startCandidates.erase( graphic );
518 continue;
519 }
520
521 // Finished, or ran into trouble...
522
523 if( close_enough( startPt, prevPt, aChainingEpsilon ) )
524 {
525 polygonComplete = true;
526 break;
527 }
528 else if( nextGraphic ) // encountered already-used segment, but not at the start
529 {
530 if( aErrorHandler )
531 (*aErrorHandler)( _( "(self-intersecting)" ), graphic, nextGraphic, prevPt );
532
533 polygonComplete = false;
534 break;
535 }
536 else // encountered discontinuity
537 {
538 if( aErrorHandler )
539 (*aErrorHandler)( _( "(not a closed shape)" ), graphic, nullptr, prevPt );
540
541 polygonComplete = false;
542 break;
543 }
544 }
545 }
546
547 int holeNum = -1;
548
549 while( startCandidates.size() )
550 {
551 int hole = aPolygons.NewHole();
552 bool firstPt = true;
553 holeNum++;
554
555 graphic = (PCB_SHAPE*) *startCandidates.begin();
556 graphic->SetFlags( SKIP_STRUCT );
557 startCandidates.erase( startCandidates.begin() );
558
559 // Both circles and polygons on the edge cuts layer are closed items that do not
560 // connect to other elements, so we process them independently
561 if( graphic->GetShape() == SHAPE_T::POLY )
562 {
563 EDA_ANGLE orientation = ANGLE_0;
564 VECTOR2I offset = VECTOR2I( 0, 0 );
565
566 if( graphic->GetParentFootprint() )
567 {
568 orientation = graphic->GetParentFootprint()->GetOrientation();
569 offset = graphic->GetParentFootprint()->GetPosition();
570 }
571
572 for( auto it = graphic->GetPolyShape().CIterate(); it; it++ )
573 {
574 VECTOR2I pt = *it;
575 RotatePoint( pt, orientation );
576 pt += offset;
577
578 aPolygons.Append( pt, -1, hole );
579
580 if( firstPt )
581 firstPt = false;
582 else
583 segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
584
585 prevPt = pt;
586 }
587 }
588 else if( graphic->GetShape() == SHAPE_T::CIRCLE )
589 {
590 // make a circle by segments;
591 VECTOR2I center = graphic->GetCenter();
592 VECTOR2I start = center;
593 int radius = graphic->GetRadius();
594 int steps = GetArcToSegmentCount( radius, aErrorMax, FULL_CIRCLE );
595 VECTOR2I nextPt;
596
597 start.x += radius;
598
599 for( int step = 0; step < steps; ++step )
600 {
601 nextPt = start;
602 RotatePoint( nextPt, center, ANGLE_360 * step / steps );
603 aPolygons.Append( nextPt, -1, hole );
604
605 if( firstPt )
606 firstPt = false;
607 else
608 segOwners[ std::make_pair( prevPt, nextPt ) ] = graphic;
609
610 prevPt = nextPt;
611 }
612 }
613 else if( graphic->GetShape() == SHAPE_T::RECT )
614 {
615 std::vector<VECTOR2I> pts = graphic->GetRectCorners();
616
617 for( const VECTOR2I& pt : pts )
618 {
619 aPolygons.Append( pt, -1, hole );
620
621 if( firstPt )
622 firstPt = false;
623 else
624 segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
625
626 prevPt = pt;
627 }
628 }
629 else
630 {
631 // Polygon start point. Arbitrarily chosen end of the segment and build the poly
632 // from here.
633
634 VECTOR2I startPt = graphic->GetEnd();
635 prevPt = startPt;
636 aPolygons.Append( prevPt, -1, hole );
637
638 // do not append the other end point yet, this first 'graphic' might be an arc
639 for(;;)
640 {
641 switch( graphic->GetShape() )
642 {
643 case SHAPE_T::SEGMENT:
644 {
645 VECTOR2I nextPt;
646
647 // Use the line segment end point furthest away from prevPt as we assume
648 // the other end to be ON prevPt or very close to it.
649
650 if( closer_to_first( prevPt, graphic->GetStart(), graphic->GetEnd()) )
651 nextPt = graphic->GetEnd();
652 else
653 nextPt = graphic->GetStart();
654
655 aPolygons.Append( nextPt, -1, hole );
656 segOwners[ std::make_pair( prevPt, nextPt ) ] = graphic;
657 prevPt = nextPt;
658 }
659 break;
660
661 case SHAPE_T::ARC:
662 // We do not support arcs in polygons, so approximate an arc with a series of
663 // short lines and put those line segments into the !same! PATH.
664 {
665 VECTOR2I pstart = graphic->GetStart();
666 VECTOR2I pend = graphic->GetEnd();
667 VECTOR2I pcenter = graphic->GetCenter();
668 EDA_ANGLE angle = -graphic->GetArcAngle();
669 int radius = graphic->GetRadius();
670 int steps = GetArcToSegmentCount( radius, aErrorMax, angle );
671
672 if( !close_enough( prevPt, pstart, aChainingEpsilon ) )
673 {
674 wxASSERT( close_enough( prevPt, graphic->GetEnd(), aChainingEpsilon ) );
675
676 angle = -angle;
677 std::swap( pstart, pend );
678 }
679
680 // Create intermediate points between start and end:
681 for( int step = 1; step < steps; ++step )
682 {
683 EDA_ANGLE rotation = ( angle * step ) / steps;
684 VECTOR2I pt = pstart;
685
686 RotatePoint( pt, pcenter, rotation );
687
688 aPolygons.Append( pt, -1, hole );
689 segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
690 prevPt = pt;
691 }
692
693 // Append the last arc end point
694 aPolygons.Append( pend, -1, hole );
695 segOwners[ std::make_pair( prevPt, pend ) ] = graphic;
696 prevPt = pend;
697 }
698 break;
699
700 case SHAPE_T::BEZIER:
701 // We do not support Bezier curves in polygons, so approximate with a series
702 // of short lines and put those line segments into the !same! PATH.
703 {
704 VECTOR2I nextPt;
705 bool reverse = false;
706
707 // Use the end point furthest away from prevPt as we assume the other
708 // end to be ON prevPt or very close to it.
709
710 if( closer_to_first( prevPt, graphic->GetStart(), graphic->GetEnd()) )
711 {
712 nextPt = graphic->GetEnd();
713 }
714 else
715 {
716 nextPt = graphic->GetStart();
717 reverse = true;
718 }
719
720 if( reverse )
721 {
722 for( int jj = graphic->GetBezierPoints().size()-1; jj >= 0; jj-- )
723 {
724 const VECTOR2I& pt = graphic->GetBezierPoints()[jj];
725 aPolygons.Append( pt, -1, hole );
726 segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
727 prevPt = pt;
728 }
729 }
730 else
731 {
732 for( const VECTOR2I& pt : graphic->GetBezierPoints() )
733 {
734 aPolygons.Append( pt, -1, hole );
735 segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
736 prevPt = pt;
737 }
738 }
739
740 prevPt = nextPt;
741 }
742 break;
743
744 default:
746 return false;
747 }
748
749 // Get next closest segment.
750
751 PCB_SHAPE* nextGraphic = findNext( graphic, prevPt, aSegList, aChainingEpsilon );
752
753 if( nextGraphic && !( nextGraphic->GetFlags() & SKIP_STRUCT ) )
754 {
755 graphic = nextGraphic;
756 graphic->SetFlags( SKIP_STRUCT );
757 startCandidates.erase( graphic );
758 continue;
759 }
760
761 // Finished, or ran into trouble...
762
763 if( close_enough( startPt, prevPt, aChainingEpsilon ) )
764 {
765 break;
766 }
767 else if( nextGraphic ) // encountered already-used segment, but not at the start
768 {
769 if( aErrorHandler )
770 (*aErrorHandler)( _( "(self-intersecting)" ), graphic, nextGraphic, prevPt );
771
772 polygonComplete = false;
773 break;
774 }
775 else // encountered discontinuity
776 {
777 if( aErrorHandler )
778 (*aErrorHandler)( _( "(not a closed shape)" ), graphic, nullptr, prevPt );
779
780 polygonComplete = false;
781 break;
782 }
783 }
784 }
785 }
786
787 if( !polygonComplete )
788 return false;
789
790 // All of the silliness that follows is to work around the segment iterator while checking
791 // for collisions.
792 // TODO: Implement proper segment and point iterators that follow std
793
794 for( auto seg1 = aPolygons.IterateSegmentsWithHoles(); seg1; seg1++ )
795 {
796 auto seg2 = seg1;
797
798 for( ++seg2; seg2; seg2++ )
799 {
800 // Check for exact overlapping segments.
801 if( *seg1 == *seg2 || ( ( *seg1 ).A == ( *seg2 ).B && ( *seg1 ).B == ( *seg2 ).A ) )
802 {
803 if( aErrorHandler )
804 {
805 BOARD_ITEM* a = fetchOwner( *seg1 );
806 BOARD_ITEM* b = fetchOwner( *seg2 );
807
808 if( a && b )
809 (*aErrorHandler)( _( "(self-intersecting)" ), a, b, ( *seg1 ).A );
810 }
811
812 selfIntersecting = true;
813 }
814
815 if( OPT_VECTOR2I pt = seg1.Get().Intersect( seg2.Get(), true ) )
816 {
817 if( aErrorHandler )
818 {
819 BOARD_ITEM* a = fetchOwner( *seg1 );
820 BOARD_ITEM* b = fetchOwner( *seg2 );
821
822 if( a && b )
823 (*aErrorHandler)( _( "(self-intersecting)" ), a, b, *pt );
824 }
825
826 selfIntersecting = true;
827 }
828 }
829 }
830
831 return !selfIntersecting;
832}
constexpr int ARC_LOW_DEF
Definition: base_units.h:120
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:50
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:142
void ClearFlags(EDA_ITEM_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition: eda_item.h:143
EDA_ITEM_FLAGS GetFlags() const
Definition: eda_item.h:144
EDA_ANGLE GetArcAngle() const
Definition: eda_shape.cpp:538
void RebuildBezierToSegmentsPointsList(int aMinSegLen)
Rebuild the m_bezierPoints vertex list that approximate the Bezier curve by a list of segments.
Definition: eda_shape.cpp:370
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:243
int GetRadius() const
Definition: eda_shape.cpp:476
SHAPE_T GetShape() const
Definition: eda_shape.h:111
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:141
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:116
std::vector< VECTOR2I > GetRectCorners() const
Definition: eda_shape.cpp:983
int GetWidth() const
Definition: eda_shape.h:107
const std::vector< VECTOR2I > & GetBezierPoints() const
Definition: eda_shape.h:226
wxString SHAPE_T_asString() const
Definition: eda_shape.cpp:75
EDA_ANGLE GetOrientation() const
Definition: footprint.h:195
VECTOR2I GetPosition() const override
Definition: footprint.h:192
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_shape.h:65
FOOTPRINT * GetParentFootprint() const
Return the parent footprint or NULL if PCB_SHAPE does not belong to a footprint.
Definition: pcb_shape.cpp:247
Definition: seg.h:42
Represent a set of closed polygons.
CONST_ITERATOR CIterate(int aFirst, int aLast, bool aIterateHoles=false) const
int NewHole(int aOutline=-1)
Adds a new outline to the set and returns its index.
SEGMENT_ITERATOR IterateSegmentsWithHoles()
Return an iterator object, for the aOutline-th outline in the set (with holes).
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aCornerBuffer, const VECTOR2I &aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a circle to a polygon, using multiple straight lines.
static PCB_SHAPE * findNext(PCB_SHAPE *aShape, const VECTOR2I &aPoint, const std::vector< PCB_SHAPE * > &aList, unsigned aLimit)
Searches for a PCB_SHAPE matching a given end point or start point in a list.
bool close_enough(VECTOR2I aLeft, VECTOR2I aRight, unsigned aLimit)
Function close_enough is a local and tunable method of qualifying the proximity of two points.
bool closer_to_first(VECTOR2I aRef, VECTOR2I aFirst, VECTOR2I aSecond)
Function closer_to_first Local method which qualifies whether the start or end point of a segment is ...
#define _(s)
static constexpr EDA_ANGLE & ANGLE_360
Definition: eda_angle.h:418
static constexpr EDA_ANGLE & FULL_CIRCLE
Definition: eda_angle.h:410
static constexpr EDA_ANGLE & ANGLE_0
Definition: eda_angle.h:412
#define SKIP_STRUCT
flag indicating that the structure should be ignored
@ ERROR_INSIDE
int GetArcToSegmentCount(int aRadius, int aErrorMax, const EDA_ANGLE &aArcAngle)
#define UNIMPLEMENTED_FOR(type)
Definition: macros.h:120
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
std::optional< VECTOR2I > OPT_VECTOR2I
Definition: seg.h:39
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Definition: trigo.cpp:183
VECTOR2< int > VECTOR2I
Definition: vector2d.h:618

References _, PNS::angle(), ANGLE_0, ANGLE_360, SHAPE_POLY_SET::Append(), ARC, ARC_LOW_DEF, BEZIER, CIRCLE, SHAPE_POLY_SET::CIterate(), EDA_ITEM::ClearFlags(), close_enough(), closer_to_first(), ERROR_INSIDE, findNext(), FULL_CIRCLE, EDA_SHAPE::GetArcAngle(), GetArcToSegmentCount(), EDA_SHAPE::GetBezierPoints(), PCB_SHAPE::GetCenter(), EDA_SHAPE::GetEnd(), EDA_ITEM::GetFlags(), FOOTPRINT::GetOrientation(), PCB_SHAPE::GetParentFootprint(), EDA_SHAPE::GetPolyShape(), FOOTPRINT::GetPosition(), EDA_SHAPE::GetRadius(), EDA_SHAPE::GetRectCorners(), EDA_SHAPE::GetShape(), EDA_SHAPE::GetStart(), EDA_SHAPE::GetWidth(), SHAPE_POLY_SET::IterateSegmentsWithHoles(), SHAPE_POLY_SET::NewHole(), SHAPE_POLY_SET::NewOutline(), POLY, EDA_SHAPE::RebuildBezierToSegmentsPointsList(), RECT, RotatePoint(), SEGMENT, EDA_ITEM::SetFlags(), EDA_SHAPE::SHAPE_T_asString(), SKIP_STRUCT, TransformCircleToPolygon(), UNIMPLEMENTED_FOR, VECTOR2< T >::x, and VECTOR2< T >::y.

Referenced by BuildBoardPolygonOutlines(), FOOTPRINT::BuildCourtyardCaches(), and BuildFootprintPolygonOutlines().