KiCad PCB EDA Suite
convert_drawsegment_list_to_polygon.h File Reference

functions to convert a shape built with DRAWSEGMENTS to a polygon. More...

Go to the source code of this file.

Typedefs

typedef const std::function< void(const wxString &msg, BOARD_ITEM *itemA, BOARD_ITEM *itemB, const wxPoint &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...
 

Detailed Description

functions to convert a shape built with DRAWSEGMENTS to a polygon.

expecting the shape describes shape similar to a polygon

Definition in file convert_drawsegment_list_to_polygon.h.

Typedef Documentation

◆ OUTLINE_ERROR_HANDLER

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

Definition at line 35 of file convert_drawsegment_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 883 of file convert_drawsegment_list_to_polygon.cpp.

885 {
886  PCB_TYPE_COLLECTOR items;
887  bool success = false;
888 
889  // Get all the PCB and FP shapes into 'items', then keep only those on layer == Edge_Cuts.
890  static const KICAD_T scan_graphics[] = { PCB_SHAPE_T, PCB_FP_SHAPE_T, EOT };
891  items.Collect( aBoard, scan_graphics );
892 
893  // Make a working copy of aSegList, because the list is modified during calculations
894  std::vector<PCB_SHAPE*> segList;
895 
896  for( int ii = 0; ii < items.GetCount(); ii++ )
897  {
898  if( items[ii]->GetLayer() == Edge_Cuts )
899  segList.push_back( static_cast<PCB_SHAPE*>( items[ii] ) );
900  }
901 
902  if( segList.size() )
903  {
904  success = ConvertOutlineToPolygon( segList, aOutlines, aErrorMax, aChainingEpsilon,
905  aErrorHandler );
906  }
907 
908  if( !success || !aOutlines.OutlineCount() )
909  {
910  // Couldn't create a valid polygon outline. Use the board edge cuts bounding box to
911  // create a rectangular outline, or, failing that, the bounding box of the items on
912  // the board.
913 
914  EDA_RECT bbbox = aBoard->GetBoardEdgesBoundingBox();
915 
916  // If null area, uses the global bounding box.
917  if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
918  bbbox = aBoard->ComputeBoundingBox();
919 
920  // Ensure non null area. If happen, gives a minimal size.
921  if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
922  bbbox.Inflate( Millimeter2iu( 1.0 ) );
923 
924  aOutlines.RemoveAllContours();
925  aOutlines.NewOutline();
926 
927  wxPoint corner;
928  aOutlines.Append( bbbox.GetOrigin() );
929 
930  corner.x = bbbox.GetOrigin().x;
931  corner.y = bbbox.GetEnd().y;
932  aOutlines.Append( corner );
933 
934  aOutlines.Append( bbbox.GetEnd() );
935 
936  corner.x = bbbox.GetEnd().x;
937  corner.y = bbbox.GetOrigin().y;
938  aOutlines.Append( corner );
939  }
940 
941  return success;
942 }
int OutlineCount() const
Return the number of vertices in a given outline/hole.
const EDA_RECT GetBoardEdgesBoundingBox() const
Return the board bounding box calculated using exclusively the board edges (graphics on Edge....
Definition: board.h:742
int GetWidth() const
Definition: eda_rect.h:109
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:93
search types array terminator (End Of Types)
Definition: typeinfo.h:81
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition: typeinfo.h:77
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:87
const wxPoint GetEnd() const
Definition: eda_rect.h:103
const wxPoint GetOrigin() const
Definition: eda_rect.h:101
int NewOutline()
Creates a new hole in a given outline.
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,...
void Collect(BOARD_ITEM *aBoard, const KICAD_T aScanList[])
Collect BOARD_ITEM objects using this class's Inspector method, which does the collection.
Definition: collectors.cpp:605
int GetHeight() const
Definition: eda_rect.h:110
Handle the component boundary box.
Definition: eda_rect.h:42
EDA_RECT ComputeBoundingBox(bool aBoardEdgesOnly=false) const
Calculate the bounding box containing all board items (or board edge segments).
Definition: board.cpp:1104
Collect all BOARD_ITEM objects of a given set of KICAD_T type(s).
Definition: collectors.h:614
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:90
static constexpr int Millimeter2iu(double mm)
EDA_RECT & Inflate(wxCoord dx, wxCoord dy)
Inflate the rectangle horizontally by dx and vertically by dy.
Definition: eda_rect.cpp:364
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...

References SHAPE_POLY_SET::Append(), PCB_TYPE_COLLECTOR::Collect(), BOARD::ComputeBoundingBox(), ConvertOutlineToPolygon(), Edge_Cuts, EOT, BOARD::GetBoardEdgesBoundingBox(), COLLECTOR::GetCount(), EDA_RECT::GetEnd(), EDA_RECT::GetHeight(), EDA_RECT::GetOrigin(), EDA_RECT::GetWidth(), EDA_RECT::Inflate(), Millimeter2iu(), SHAPE_POLY_SET::NewOutline(), SHAPE_POLY_SET::OutlineCount(), PCB_FP_SHAPE_T, PCB_SHAPE_T, and SHAPE_POLY_SET::RemoveAllContours().

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 178 of file convert_drawsegment_list_to_polygon.cpp.

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

References _, PNS::angle(), SHAPE_POLY_SET::Append(), ARC, CIRCLE, SHAPE_POLY_SET::CIterate(), EDA_ITEM::ClearFlags(), close_enough(), closer_to_first(), CURVE, ERROR_INSIDE, findNext(), PCB_SHAPE::GetAngle(), PCB_SHAPE::GetArcEnd(), PCB_SHAPE::GetArcStart(), GetArcToSegmentCount(), PCB_SHAPE::GetBezierPoints(), PCB_SHAPE::GetCenter(), PCB_SHAPE::GetEnd(), EDA_ITEM::GetFlags(), FOOTPRINT::GetOrientation(), PCB_SHAPE::GetParentFootprint(), PCB_SHAPE::GetPolyShape(), FOOTPRINT::GetPosition(), PCB_SHAPE::GetRadius(), PCB_SHAPE::GetRectCorners(), PCB_SHAPE::GetShape(), PCB_SHAPE::GetStart(), PCB_SHAPE::GetWidth(), SHAPE_POLY_SET::IterateSegmentsWithHoles(), SHAPE_POLY_SET::NewHole(), SHAPE_POLY_SET::NewOutline(), POLYGON, PCB_SHAPE::RebuildBezierToSegmentsPointsList(), RECT, RotatePoint(), SEGMENT, EDA_ITEM::SetFlags(), BOARD_ITEM::ShowShape(), SKIP_STRUCT, TransformCircleToPolygon(), VECTOR2< T >::x, and VECTOR2< T >::y.

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