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

880 {
881  PCB_TYPE_COLLECTOR items;
882  bool success = false;
883 
884  // Get all the PCB and FP shapes into 'items', then keep only those on layer == Edge_Cuts.
885  static const KICAD_T scan_graphics[] = { PCB_SHAPE_T, PCB_FP_SHAPE_T, EOT };
886  items.Collect( aBoard, scan_graphics );
887 
888  // Make a working copy of aSegList, because the list is modified during calculations
889  std::vector<PCB_SHAPE*> segList;
890 
891  for( int ii = 0; ii < items.GetCount(); ii++ )
892  {
893  if( items[ii]->GetLayer() == Edge_Cuts )
894  segList.push_back( static_cast<PCB_SHAPE*>( items[ii] ) );
895  }
896 
897  if( segList.size() )
898  {
899  success = ConvertOutlineToPolygon( segList, aOutlines, aErrorMax, aChainingEpsilon,
900  aErrorHandler );
901  }
902 
903  if( !success || !aOutlines.OutlineCount() )
904  {
905  // Couldn't create a valid polygon outline. Use the board edge cuts bounding box to
906  // create a rectangular outline, or, failing that, the bounding box of the items on
907  // the board.
908 
909  EDA_RECT bbbox = aBoard->GetBoardEdgesBoundingBox();
910 
911  // If null area, uses the global bounding box.
912  if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
913  bbbox = aBoard->ComputeBoundingBox();
914 
915  // Ensure non null area. If happen, gives a minimal size.
916  if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
917  bbbox.Inflate( Millimeter2iu( 1.0 ) );
918 
919  aOutlines.RemoveAllContours();
920  aOutlines.NewOutline();
921 
922  wxPoint corner;
923  aOutlines.Append( bbbox.GetOrigin() );
924 
925  corner.x = bbbox.GetOrigin().x;
926  corner.y = bbbox.GetEnd().y;
927  aOutlines.Append( corner );
928 
929  aOutlines.Append( bbbox.GetEnd() );
930 
931  corner.x = bbbox.GetEnd().x;
932  corner.y = bbbox.GetOrigin().y;
933  aOutlines.Append( corner );
934  }
935 
936  return success;
937 }
int OutlineCount() const
Return the number of vertices in a given outline/hole.
const EDA_RECT GetBoardEdgesBoundingBox() const
Returns the board bounding box calculated using exclusively the board edges (graphics on Edge....
Definition: board.h:810
int GetWidth() const
Definition: eda_rect.h:114
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:108
const wxPoint GetOrigin() const
Definition: eda_rect.h:106
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:115
Handle the component boundary box.
Definition: eda_rect.h:42
static bool GetLayer(MODEL_VRML &aModel, LAYER_NUM layer, VRML_LAYER **vlayer)
EDA_RECT ComputeBoundingBox(bool aBoardEdgesOnly=false) const
Calculate the bounding box containing all board items (or board edge segments).
Definition: board.cpp:1029
Collect all BOARD_ITEM objects of a given set of KICAD_T type(s).
Definition: collectors.h:618
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:363
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(), GetLayer(), 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 177 of file convert_drawsegment_list_to_polygon.cpp.

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

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