KiCad PCB EDA Suite
convert_drawsegment_list_to_polygon.cpp File Reference

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

#include <trigo.h>
#include <macros.h>
#include <math/vector2d.h>
#include <class_drawsegment.h>
#include <class_module.h>
#include <base_units.h>
#include <convert_basic_shapes_to_polygon.h>
#include <geometry/shape_poly_set.h>
#include <geometry/geometry_utils.h>
#include <class_board.h>
#include <collectors.h>

Go to the source code of this file.

Functions

static unsigned close_ness (const wxPoint &aLeft, const wxPoint &aRight)
 Function close_ness is a non-exact distance (also called Manhattan distance) used to approximate the distance between two points. More...
 
bool close_enough (const wxPoint &aLeft, const wxPoint &aRight, unsigned aLimit)
 Function close_enough is a local and tunable method of qualifying the proximity of two points. More...
 
bool close_st (const wxPoint &aReference, const wxPoint &aFirst, const wxPoint &aSecond)
 Function close_st is a local method of qualifying if either the start of end point of a segment is closest to a point. More...
 
static DRAWSEGMENTfindPoint (const wxPoint &aPoint, std::vector< DRAWSEGMENT * > &aList, unsigned aLimit)
 Searches for a DRAWSEGMENT matching a given end point or start point in a list, and if found, removes it from the TYPE_COLLECTOR and returns it, else returns NULL. More...
 
bool ConvertOutlineToPolygon (std::vector< DRAWSEGMENT * > &aSegList, SHAPE_POLY_SET &aPolygons, wxString *aErrorText, unsigned int aTolerance, wxPoint *aErrorLocation)
 Function ConvertOutlineToPolygon build a polygon (with holes) from a DRAWSEGMENT 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, wxString *aErrorText, unsigned int aTolerance, wxPoint *aErrorLocation)
 

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.cpp.

Function Documentation

◆ BuildBoardPolygonOutlines()

bool BuildBoardPolygonOutlines ( BOARD aBoard,
SHAPE_POLY_SET aOutlines,
wxString *  aErrorText,
unsigned int  aTolerance,
wxPoint aErrorLocation 
)

Definition at line 759 of file convert_drawsegment_list_to_polygon.cpp.

761 {
762  PCB_TYPE_COLLECTOR items;
763  bool success = false;
764 
765  // Get all the DRAWSEGMENTS and module graphics into 'items',
766  // then keep only those on layer == Edge_Cuts.
767  static const KICAD_T scan_graphics[] = { PCB_LINE_T, PCB_MODULE_EDGE_T, EOT };
768  items.Collect( aBoard, scan_graphics );
769 
770  // Make a working copy of aSegList, because the list is modified during calculations
771  std::vector< DRAWSEGMENT* > segList;
772 
773  for( int ii = 0; ii < items.GetCount(); ii++ )
774  {
775  if( items[ii]->GetLayer() == Edge_Cuts )
776  segList.push_back( static_cast< DRAWSEGMENT* >( items[ii] ) );
777  }
778 
779  if( segList.size() )
780  {
781  success = ConvertOutlineToPolygon( segList, aOutlines, aErrorText, aTolerance,
782  aErrorLocation );
783  }
784  else if( aErrorText )
785  {
786  *aErrorText = _( "No edges found on Edge.Cuts layer." );
787  }
788 
789  if( !success || !aOutlines.OutlineCount() )
790  {
791  // Couldn't create a valid polygon outline. Use the board edge cuts bounding box to
792  // create a rectangular outline, or, failing that, the bounding box of the items on
793  // the board.
794 
795  EDA_RECT bbbox = aBoard->GetBoardEdgesBoundingBox();
796 
797  // If null area, uses the global bounding box.
798  if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
799  bbbox = aBoard->ComputeBoundingBox();
800 
801  // Ensure non null area. If happen, gives a minimal size.
802  if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
803  bbbox.Inflate( Millimeter2iu( 1.0 ) );
804 
805  aOutlines.RemoveAllContours();
806  aOutlines.NewOutline();
807 
808  wxPoint corner;
809  aOutlines.Append( bbbox.GetOrigin() );
810 
811  corner.x = bbbox.GetOrigin().x;
812  corner.y = bbbox.GetEnd().y;
813  aOutlines.Append( corner );
814 
815  aOutlines.Append( bbbox.GetEnd() );
816 
817  corner.x = bbbox.GetEnd().x;
818  corner.y = bbbox.GetOrigin().y;
819  aOutlines.Append( corner );
820  }
821 
822  return success;
823 }
int OutlineCount() const
Returns the number of outlines in the set
const EDA_RECT GetBoardEdgesBoundingBox() const
Returns the board bounding box calculated using exclusively the board edges (graphics on Edge....
Definition: class_board.h:740
int GetWidth() const
Definition: eda_rect.h:119
bool ConvertOutlineToPolygon(std::vector< DRAWSEGMENT * > &aSegList, SHAPE_POLY_SET &aPolygons, wxString *aErrorText, unsigned int aTolerance, wxPoint *aErrorLocation)
Function ConvertOutlineToPolygon build a polygon (with holes) from a DRAWSEGMENT list,...
class EDGE_MODULE, a footprint edge
Definition: typeinfo.h:94
search types array terminator (End Of Types)
Definition: typeinfo.h:82
KICAD_T
Enum KICAD_T is the set of class identification values, stored in EDA_ITEM::m_StructType.
Definition: typeinfo.h:78
int GetCount() const
Function GetCount returns the number of objects in the list.
Definition: collector.h:101
const wxPoint GetEnd() const
Definition: eda_rect.h:116
const wxPoint GetOrigin() const
Definition: eda_rect.h:114
int NewOutline()
Creates a new empty polygon in the set and returns its index
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:618
int GetHeight() const
Definition: eda_rect.h:120
#define _(s)
Definition: 3d_actions.cpp:33
void RemoveAllContours()
Removes all outlines & holes (clears) the polygon set.
EDA_RECT handles the component boundary box.
Definition: eda_rect.h:44
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).
Collect all BOARD_ITEM objects of a given set of KICAD_T type(s).
Definition: collectors.h:626
class DRAWSEGMENT, a segment not on copper layers
Definition: typeinfo.h:91
static constexpr int Millimeter2iu(double mm)
EDA_RECT & Inflate(wxCoord dx, wxCoord dy)
Function Inflate inflates the rectangle horizontally by dx and vertically by dy.
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)

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_LINE_T, PCB_MODULE_EDGE_T, SHAPE_POLY_SET::RemoveAllContours(), wxPoint::x, and wxPoint::y.

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

◆ close_enough()

bool close_enough ( const wxPoint aLeft,
const wxPoint aRight,
unsigned  aLimit 
)
inline

Function close_enough is a local and tunable method of qualifying the proximity of two points.

Parameters
aLeftis the first point
aRightis the second point
aLimitis a measure of proximity that the caller knows about.
Returns
bool - true if the two points are close enough, else false.

Definition at line 72 of file convert_drawsegment_list_to_polygon.cpp.

73 {
74  // We don't use an accurate distance calculation, just something
75  // approximating it, since aLimit is non-exact anyway except when zero.
76  return close_ness( aLeft, aRight ) <= aLimit;
77 }
static unsigned close_ness(const wxPoint &aLeft, const wxPoint &aRight)
Function close_ness is a non-exact distance (also called Manhattan distance) used to approximate the ...

References close_ness().

Referenced by ConvertOutlineToPolygon().

◆ close_ness()

static unsigned close_ness ( const wxPoint aLeft,
const wxPoint aRight 
)
static

Function close_ness is a non-exact distance (also called Manhattan distance) used to approximate the distance between two points.

The distance is very in-exact, but can be helpful when used to pick between alternative neighboring points.

Parameters
aLeftis the first point
aRightis the second point
Returns
unsigned - a measure of proximity that the caller knows about, in BIU, but remember it is only an approximation.

Definition at line 56 of file convert_drawsegment_list_to_polygon.cpp.

57 {
58  // Don't need an accurate distance calculation, just something
59  // approximating it, for relative ordering.
60  return unsigned( std::abs( aLeft.x - aRight.x ) + abs( aLeft.y - aRight.y ) );
61 }

References wxPoint::x, and wxPoint::y.

Referenced by close_enough(), close_st(), and findPoint().

◆ close_st()

bool close_st ( const wxPoint aReference,
const wxPoint aFirst,
const wxPoint aSecond 
)
inline

Function close_st is a local method of qualifying if either the start of end point of a segment is closest to a point.

Parameters
aReferenceis the reference point
aFirstis the first point
aSecondis the second point
Returns
bool - true if the the first point is closest to the reference, otherwise false.

Definition at line 88 of file convert_drawsegment_list_to_polygon.cpp.

89 {
90  // We don't use an accurate distance calculation, just something
91  // approximating to find the closest to the reference.
92  return close_ness( aReference, aFirst ) <= close_ness( aReference, aSecond );
93 }
static unsigned close_ness(const wxPoint &aLeft, const wxPoint &aRight)
Function close_ness is a non-exact distance (also called Manhattan distance) used to approximate the ...

References close_ness().

Referenced by ConvertOutlineToPolygon().

◆ ConvertOutlineToPolygon()

bool ConvertOutlineToPolygon ( std::vector< DRAWSEGMENT * > &  aSegList,
SHAPE_POLY_SET aPolygons,
wxString *  aErrorText,
unsigned int  aTolerance,
wxPoint aErrorLocation 
)

Function ConvertOutlineToPolygon build a polygon (with holes) from a DRAWSEGMENT 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.
aToleranceis the max distance between points that is still accepted as connected (internal units)
aErrorTextis a wxString to return error message.
aErrorLocationis the optional position of the error in the outline

Definition at line 186 of file convert_drawsegment_list_to_polygon.cpp.

189 {
190  if( aSegList.size() == 0 )
191  return true;
192 
193  wxString msg;
194 
195  // Make a working copy of aSegList, because the list is modified during calculations
196  std::vector< DRAWSEGMENT* > segList = aSegList;
197 
198  DRAWSEGMENT* graphic;
199  wxPoint prevPt;
200 
201  // Find edge point with minimum x, this should be in the outer polygon
202  // which will define the perimeter polygon polygon.
203  wxPoint xmin = wxPoint( INT_MAX, 0 );
204  int xmini = 0;
205 
206  for( size_t i = 0; i < segList.size(); i++ )
207  {
208  graphic = (DRAWSEGMENT*) segList[i];
209 
210  switch( graphic->GetShape() )
211  {
212  case S_RECT:
213  case S_SEGMENT:
214  {
215  if( graphic->GetStart().x < xmin.x )
216  {
217  xmin = graphic->GetStart();
218  xmini = i;
219  }
220 
221  if( graphic->GetEnd().x < xmin.x )
222  {
223  xmin = graphic->GetEnd();
224  xmini = i;
225  }
226  }
227  break;
228 
229  case S_ARC:
230  {
231  wxPoint pstart = graphic->GetArcStart();
232  wxPoint center = graphic->GetCenter();
233  double angle = -graphic->GetAngle();
234  double radius = graphic->GetRadius();
235  int steps = GetArcToSegmentCount( radius, aTolerance, angle / 10.0 );
236  wxPoint pt;
237 
238  for( int step = 1; step<=steps; ++step )
239  {
240  double rotation = ( angle * step ) / steps;
241 
242  pt = pstart;
243 
244  RotatePoint( &pt, center, rotation );
245 
246  if( pt.x < xmin.x )
247  {
248  xmin = pt;
249  xmini = i;
250  }
251  }
252  }
253  break;
254 
255  case S_CIRCLE:
256  {
257  wxPoint pt = graphic->GetCenter();
258 
259  // pt has minimum x point
260  pt.x -= graphic->GetRadius();
261 
262  // when the radius <= 0, this is a mal-formed circle. Skip it
263  if( graphic->GetRadius() > 0 && pt.x < xmin.x )
264  {
265  xmin = pt;
266  xmini = i;
267  }
268  }
269  break;
270 
271  case S_CURVE:
272  {
273  graphic->RebuildBezierToSegmentsPointsList( graphic->GetWidth() );
274 
275  for( const wxPoint& pt : graphic->GetBezierPoints())
276  {
277  if( pt.x < xmin.x )
278  {
279  xmin = pt;
280  xmini = i;
281  }
282  }
283  }
284  break;
285 
286  case S_POLYGON:
287  {
288  const SHAPE_POLY_SET poly = graphic->GetPolyShape();
289  MODULE* module = aSegList[0]->GetParentModule();
290  double orientation = module ? module->GetOrientation() : 0.0;
291  VECTOR2I offset = module ? module->GetPosition() : VECTOR2I( 0, 0 );
292 
293  for( auto iter = poly.CIterate(); iter; iter++ )
294  {
295  VECTOR2I pt = *iter;
296  RotatePoint( pt, orientation );
297  pt += offset;
298 
299  if( pt.x < xmin.x )
300  {
301  xmin.x = pt.x;
302  xmin.y = pt.y;
303  xmini = i;
304  }
305  }
306  }
307  break;
308 
309  default:
310  break;
311  }
312  }
313 
314  // Grab the left most point, assume its on the board's perimeter, and see if we
315  // can put enough graphics together by matching endpoints to formulate a cohesive
316  // polygon.
317 
318  graphic = (DRAWSEGMENT*) segList[xmini];
319 
320  // The first DRAWSEGMENT is in 'graphic', ok to remove it from 'items'
321  segList.erase( segList.begin() + xmini );
322 
323  // Output the outline perimeter as polygon.
324  if( graphic->GetShape() == S_CIRCLE )
325  {
326  TransformCircleToPolygon( aPolygons, graphic->GetCenter(), graphic->GetRadius(), aTolerance );
327  }
328  else if( graphic->GetShape() == S_RECT )
329  {
330  std::vector<wxPoint> pts = graphic->GetRectCorners();
331 
332  aPolygons.NewOutline();
333 
334  for( const wxPoint& pt : pts )
335  aPolygons.Append( pt );
336  }
337  else if( graphic->GetShape() == S_POLYGON )
338  {
339  MODULE* module = graphic->GetParentModule(); // NULL for items not in footprints
340  double orientation = module ? module->GetOrientation() : 0.0;
341  VECTOR2I offset = module ? module->GetPosition() : VECTOR2I( 0, 0 );
342 
343  aPolygons.NewOutline();
344 
345  for( auto it = graphic->GetPolyShape().CIterate( 0 ); it; it++ )
346  {
347  auto pt = *it;
348  RotatePoint( pt, orientation );
349  pt += offset;
350  aPolygons.Append( pt );
351  }
352  }
353  else
354  {
355  // Polygon start point. Arbitrarily chosen end of the
356  // segment and build the poly from here.
357 
358  wxPoint startPt = graphic->GetShape() == S_ARC ? graphic->GetArcEnd()
359  : graphic->GetEnd();
360 
361  prevPt = startPt;
362  aPolygons.NewOutline();
363  aPolygons.Append( prevPt );
364 
365  // Do not append the other end point yet of this 'graphic', this first
366  // 'graphic' might be an arc or a curve.
367 
368  for(;;)
369  {
370  switch( graphic->GetShape() )
371  {
372  case S_SEGMENT:
373  {
374  wxPoint nextPt;
375 
376  // Use the line segment end point furthest away from prevPt as we assume
377  // the other end to be ON prevPt or very close to it.
378 
379  if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
380  nextPt = graphic->GetEnd();
381  else
382  nextPt = graphic->GetStart();
383 
384  aPolygons.Append( nextPt );
385  prevPt = nextPt;
386  }
387  break;
388 
389  case S_ARC:
390  // We do not support arcs in polygons, so approximate an arc with a series of
391  // short lines and put those line segments into the !same! PATH.
392  {
393  wxPoint pstart = graphic->GetArcStart();
394  wxPoint pend = graphic->GetArcEnd();
395  wxPoint pcenter = graphic->GetCenter();
396  double angle = -graphic->GetAngle();
397  double radius = graphic->GetRadius();
398  int steps = GetArcToSegmentCount( radius, aTolerance, angle / 10.0 );
399 
400  if( !close_enough( prevPt, pstart, aTolerance ) )
401  {
402  wxASSERT( close_enough( prevPt, graphic->GetArcEnd(), aTolerance ) );
403 
404  angle = -angle;
405  std::swap( pstart, pend );
406  }
407 
408  wxPoint nextPt;
409 
410  for( int step = 1; step<=steps; ++step )
411  {
412  double rotation = ( angle * step ) / steps;
413  nextPt = pstart;
414  RotatePoint( &nextPt, pcenter, rotation );
415 
416  aPolygons.Append( nextPt );
417  }
418 
419  prevPt = nextPt;
420  }
421  break;
422 
423  case S_CURVE:
424  // We do not support Bezier curves in polygons, so approximate with a series
425  // of short lines and put those line segments into the !same! PATH.
426  {
427  wxPoint nextPt;
428  bool reverse = false;
429 
430  // Use the end point furthest away from
431  // prevPt as we assume the other end to be ON prevPt or
432  // very close to it.
433 
434  if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
435  nextPt = graphic->GetEnd();
436  else
437  {
438  nextPt = graphic->GetStart();
439  reverse = true;
440  }
441 
442  if( reverse )
443  {
444  for( int jj = graphic->GetBezierPoints().size()-1; jj >= 0; jj-- )
445  aPolygons.Append( graphic->GetBezierPoints()[jj] );
446  }
447  else
448  {
449  for( size_t jj = 0; jj < graphic->GetBezierPoints().size(); jj++ )
450  aPolygons.Append( graphic->GetBezierPoints()[jj] );
451  }
452 
453  prevPt = nextPt;
454  }
455  break;
456 
457  default:
458  if( aErrorText )
459  {
460  msg.Printf( "Unsupported DRAWSEGMENT type %s.",
461  BOARD_ITEM::ShowShape( graphic->GetShape() ) );
462 
463  *aErrorText << msg << "\n";
464  }
465 
466  if( aErrorLocation )
467  *aErrorLocation = graphic->GetPosition();
468 
469  return false;
470  }
471 
472  // Get next closest segment.
473 
474  graphic = findPoint( prevPt, segList, aTolerance );
475 
476  // If there are no more close segments, check if the board
477  // outline polygon can be closed.
478 
479  if( !graphic )
480  {
481  if( close_enough( startPt, prevPt, aTolerance ) )
482  {
483  // Close the polygon back to start point
484  // aPolygons.Append( startPt ); // not needed
485  }
486  else
487  {
488  if( aErrorText )
489  {
490  msg.Printf( _( "Unable to find edge with an endpoint of (%s, %s)." ),
491  StringFromValue( EDA_UNITS::MILLIMETRES, prevPt.x, true ),
492  StringFromValue( EDA_UNITS::MILLIMETRES, prevPt.y, true ) );
493 
494  *aErrorText << msg << "\n";
495  }
496 
497  if( aErrorLocation )
498  *aErrorLocation = prevPt;
499 
500  return false;
501  }
502  break;
503  }
504  }
505  }
506 
507  while( segList.size() )
508  {
509  // emit a signal layers keepout for every interior polygon left...
510  int hole = aPolygons.NewHole();
511 
512  graphic = (DRAWSEGMENT*) segList[0];
513  segList.erase( segList.begin() );
514 
515  // Both circles and polygons on the edge cuts layer are closed items that
516  // do not connect to other elements, so we process them independently
517  if( graphic->GetShape() == S_POLYGON )
518  {
519  MODULE* module = graphic->GetParentModule(); // NULL for items not in footprints
520  double orientation = module ? module->GetOrientation() : 0.0;
521  VECTOR2I offset = module ? module->GetPosition() : VECTOR2I( 0, 0 );
522 
523  for( auto it = graphic->GetPolyShape().CIterate(); it; it++ )
524  {
525  auto val = *it;
526  RotatePoint( val, orientation );
527  val += offset;
528 
529  aPolygons.Append( val, -1, hole );
530  }
531  }
532  else if( graphic->GetShape() == S_CIRCLE )
533  {
534  // make a circle by segments;
535  wxPoint center = graphic->GetCenter();
536  double angle = 3600.0;
537  wxPoint start = center;
538  int radius = graphic->GetRadius();
539  int steps = GetArcToSegmentCount( radius, aTolerance, 360.0 );
540  wxPoint nextPt;
541 
542  start.x += radius;
543 
544  for( int step = 0; step < steps; ++step )
545  {
546  double rotation = ( angle * step ) / steps;
547  nextPt = start;
548  RotatePoint( &nextPt.x, &nextPt.y, center.x, center.y, rotation );
549  aPolygons.Append( nextPt, -1, hole );
550  }
551  }
552  else if( graphic->GetShape() == S_RECT )
553  {
554  std::vector<wxPoint> pts = graphic->GetRectCorners();
555 
556  for( const wxPoint& pt : pts )
557  aPolygons.Append( pt, -1, hole );
558  }
559  else
560  {
561  // Polygon start point. Arbitrarily chosen end of the
562  // segment and build the poly from here.
563 
564  wxPoint startPt( graphic->GetEnd() );
565  prevPt = graphic->GetEnd();
566  aPolygons.Append( prevPt, -1, hole );
567 
568  // do not append the other end point yet, this first 'graphic' might be an arc
569  for(;;)
570  {
571  switch( graphic->GetShape() )
572  {
573  case S_SEGMENT:
574  {
575  wxPoint nextPt;
576 
577  // Use the line segment end point furthest away from
578  // prevPt as we assume the other end to be ON prevPt or
579  // very close to it.
580 
581  if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
582  nextPt = graphic->GetEnd();
583  else
584  nextPt = graphic->GetStart();
585 
586  prevPt = nextPt;
587  aPolygons.Append( prevPt, -1, hole );
588  }
589  break;
590 
591  case S_ARC:
592  // Freerouter does not yet understand arcs, so approximate
593  // an arc with a series of short lines and put those
594  // line segments into the !same! PATH.
595  {
596  wxPoint pstart = graphic->GetArcStart();
597  wxPoint pend = graphic->GetArcEnd();
598  wxPoint pcenter = graphic->GetCenter();
599  double angle = -graphic->GetAngle();
600  int radius = graphic->GetRadius();
601  int steps = GetArcToSegmentCount( radius, aTolerance, angle / 10.0 );
602 
603  if( !close_enough( prevPt, pstart, aTolerance ) )
604  {
605  wxASSERT( close_enough( prevPt, graphic->GetArcEnd(), aTolerance ) );
606 
607  angle = -angle;
608  std::swap( pstart, pend );
609  }
610 
611  wxPoint nextPt;
612 
613  for( int step = 1; step <= steps; ++step )
614  {
615  double rotation = ( angle * step ) / steps;
616 
617  nextPt = pstart;
618  RotatePoint( &nextPt, pcenter, rotation );
619 
620  aPolygons.Append( nextPt, -1, hole );
621  }
622 
623  prevPt = nextPt;
624  }
625  break;
626 
627  case S_CURVE:
628  // We do not support Bezier curves in polygons, so approximate
629  // with a series of short lines and put those
630  // line segments into the !same! PATH.
631  {
632  wxPoint nextPt;
633  bool reverse = false;
634 
635  // Use the end point furthest away from
636  // prevPt as we assume the other end to be ON prevPt or
637  // very close to it.
638 
639  if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
640  nextPt = graphic->GetEnd();
641  else
642  {
643  nextPt = graphic->GetStart();
644  reverse = true;
645  }
646 
647  if( reverse )
648  {
649  for( int jj = graphic->GetBezierPoints().size()-1; jj >= 0; jj-- )
650  aPolygons.Append( graphic->GetBezierPoints()[jj], -1, hole );
651  }
652  else
653  {
654  for( const wxPoint& pt : graphic->GetBezierPoints())
655  aPolygons.Append( pt, -1, hole );
656  }
657 
658  prevPt = nextPt;
659  }
660  break;
661 
662  default:
663  if( aErrorText )
664  {
665  msg.Printf( "Unsupported DRAWSEGMENT type %s.",
666  BOARD_ITEM::ShowShape( graphic->GetShape() ) );
667 
668  *aErrorText << msg << "\n";
669  }
670 
671  if( aErrorLocation )
672  *aErrorLocation = graphic->GetPosition();
673 
674  return false;
675  }
676 
677  // Get next closest segment.
678 
679  graphic = findPoint( prevPt, segList, aTolerance );
680 
681  // If there are no more close segments, check if polygon
682  // can be closed.
683 
684  if( !graphic )
685  {
686  if( close_enough( startPt, prevPt, aTolerance ) )
687  {
688  // Close the polygon back to start point
689  // aPolygons.Append( startPt, -1, hole ); // not needed
690  }
691  else
692  {
693  if( aErrorText )
694  {
695  msg.Printf( _( "Unable to find edge with an endpoint of (%s, %s)." ),
696  StringFromValue( EDA_UNITS::MILLIMETRES, prevPt.x, true ),
697  StringFromValue( EDA_UNITS::MILLIMETRES, prevPt.y, true ) );
698 
699  *aErrorText << msg << "\n";
700  }
701 
702  if( aErrorLocation )
703  *aErrorLocation = prevPt;
704 
705  return false;
706  }
707  break;
708  }
709  }
710  }
711  }
712 
713  // All of the silliness that follows is to work around the segment iterator
714  // while checking for collisions.
715  // TODO: Implement proper segment and point iterators that follow std
716  for( auto seg1 = aPolygons.IterateSegmentsWithHoles(); seg1; seg1++ )
717  {
718  auto seg2 = seg1;
719 
720  for( ++seg2; seg2; seg2++ )
721  {
722  // Check for exact overlapping segments. This is not viewed
723  // as an intersection below
724  if( *seg1 == *seg2 ||
725  ( ( *seg1 ).A == ( *seg2 ).B && ( *seg1 ).B == ( *seg2 ).A ) )
726  {
727  if( aErrorLocation )
728  {
729  aErrorLocation->x = ( *seg1 ).A.x;
730  aErrorLocation->y = ( *seg1 ).A.y;
731  }
732 
733  return false;
734  }
735 
736  if( boost::optional<VECTOR2I> pt = seg1.Get().Intersect( seg2.Get(), true ) )
737  {
738  if( aErrorLocation )
739  {
740  aErrorLocation->x = pt->x;
741  aErrorLocation->y = pt->y;
742  }
743 
744  return false;
745  }
746  }
747  }
748 
749  return true;
750 }
int NewHole(int aOutline=-1)
Creates a new hole in a given outline
static wxString ShowShape(STROKE_T aShape)
Function ShowShape converts the enum STROKE_T integer value to a wxString.
double GetOrientation() const
Definition: class_module.h:224
wxPoint GetArcStart() const
wxPoint GetArcEnd() const
SEGMENT_ITERATOR IterateSegmentsWithHoles()
Returns an iterator object, for all outlines in the set (with holes)
STROKE_T GetShape() const
polygon (not yet used for tracks, but could be in microwave apps)
void RebuildBezierToSegmentsPointsList(int aMinSegLen)
Rebuild the m_BezierPoints vertex list that approximate the Bezier curve by a list of segments Has me...
usual segment : line with rounded ends
const std::vector< wxPoint > & GetBezierPoints() const
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:208
VECTOR2< int > VECTOR2I
Definition: vector2d.h:594
const wxPoint & GetEnd() const
Function GetEnd returns the ending point of the graphic.
bool close_st(const wxPoint &aReference, const wxPoint &aFirst, const wxPoint &aSecond)
Function close_st is a local method of qualifying if either the start of end point of a segment is cl...
wxPoint GetPosition() const override
segment with non rounded ends
std::vector< wxPoint > GetRectCorners() const
SHAPE_POLY_SET.
Arcs (with rounded ends)
MODULE * GetParentModule() const
Function GetParentModule returns a pointer to the parent module, or NULL if DRAWSEGMENT does not belo...
int GetRadius() const
Function GetRadius returns the radius of this item Has meaning only for arc and circle.
SHAPE_POLY_SET & GetPolyShape()
Bezier Curve.
bool close_enough(const wxPoint &aLeft, const wxPoint &aRight, unsigned aLimit)
Function close_enough is a local and tunable method of qualifying the proximity of two points.
int NewOutline()
Creates a new empty polygon in the set and returns its index
CONST_ITERATOR CIterate(int aFirst, int aLast, bool aIterateHoles=false) const
int GetWidth() const
double GetAngle() const
#define _(s)
Definition: 3d_actions.cpp:33
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
static DRAWSEGMENT * findPoint(const wxPoint &aPoint, std::vector< DRAWSEGMENT * > &aList, unsigned aLimit)
Searches for a DRAWSEGMENT matching a given end point or start point in a list, and if found,...
void TransformCircleToPolygon(SHAPE_POLY_SET &aCornerBuffer, wxPoint aCenter, int aRadius, int aError)
Function TransformCircleToPolygon convert a circle to a polygon, using multiple straight lines.
const wxPoint & GetStart() const
Function GetStart returns the starting point of the graphic.
wxPoint GetPosition() const override
Definition: class_module.h:219
int GetArcToSegmentCount(int aRadius, int aErrorMax, double aArcAngleDegree)
wxString StringFromValue(EDA_UNITS aUnits, double aValue, bool aAddUnitSymbol, bool aUseMils, EDA_DATA_TYPE aType)
Function StringFromValue returns the string from aValue according to units (inch, mm ....
Definition: base_units.cpp:233
wxPoint GetCenter() const override
Function GetCenter()
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)

References _, PNS::angle(), SHAPE_POLY_SET::Append(), SHAPE_POLY_SET::CIterate(), close_enough(), close_st(), findPoint(), DRAWSEGMENT::GetAngle(), DRAWSEGMENT::GetArcEnd(), DRAWSEGMENT::GetArcStart(), GetArcToSegmentCount(), DRAWSEGMENT::GetBezierPoints(), DRAWSEGMENT::GetCenter(), DRAWSEGMENT::GetEnd(), MODULE::GetOrientation(), DRAWSEGMENT::GetParentModule(), DRAWSEGMENT::GetPolyShape(), DRAWSEGMENT::GetPosition(), MODULE::GetPosition(), DRAWSEGMENT::GetRadius(), DRAWSEGMENT::GetRectCorners(), DRAWSEGMENT::GetShape(), DRAWSEGMENT::GetStart(), DRAWSEGMENT::GetWidth(), SHAPE_POLY_SET::IterateSegmentsWithHoles(), MILLIMETRES, SHAPE_POLY_SET::NewHole(), SHAPE_POLY_SET::NewOutline(), DRAWSEGMENT::RebuildBezierToSegmentsPointsList(), RotatePoint(), S_ARC, S_CIRCLE, S_CURVE, S_POLYGON, S_RECT, S_SEGMENT, BOARD_ITEM::ShowShape(), StringFromValue(), TransformCircleToPolygon(), wxPoint::x, VECTOR2< T >::x, wxPoint::y, and VECTOR2< T >::y.

Referenced by BuildBoardPolygonOutlines(), and MODULE::BuildPolyCourtyard().

◆ findPoint()

static DRAWSEGMENT* findPoint ( const wxPoint aPoint,
std::vector< DRAWSEGMENT * > &  aList,
unsigned  aLimit 
)
static

Searches for a DRAWSEGMENT matching a given end point or start point in a list, and if found, removes it from the TYPE_COLLECTOR and returns it, else returns NULL.

Parameters
aPointThe starting or ending point to search for.
aListThe list to remove from.
aLimitis the distance from aPoint that still constitutes a valid find.
Returns
DRAWSEGMENT* - The first DRAWSEGMENT that has a start or end point matching aPoint, otherwise NULL if none.

Definition at line 105 of file convert_drawsegment_list_to_polygon.cpp.

106 {
107  unsigned min_d = INT_MAX;
108  int ndx_min = 0;
109 
110  // find the point closest to aPoint and perhaps exactly matching aPoint.
111  for( size_t i = 0; i < aList.size(); ++i )
112  {
113  DRAWSEGMENT* graphic = aList[i];
114  unsigned d;
115 
116  switch( graphic->GetShape() )
117  {
118  case S_ARC:
119  if( aPoint == graphic->GetArcStart() || aPoint == graphic->GetArcEnd() )
120  {
121  aList.erase( aList.begin() + i );
122  return graphic;
123  }
124 
125  d = close_ness( aPoint, graphic->GetArcStart() );
126  if( d < min_d )
127  {
128  min_d = d;
129  ndx_min = i;
130  }
131 
132  d = close_ness( aPoint, graphic->GetArcEnd() );
133  if( d < min_d )
134  {
135  min_d = d;
136  ndx_min = i;
137  }
138  break;
139 
140  default:
141  if( aPoint == graphic->GetStart() || aPoint == graphic->GetEnd() )
142  {
143  aList.erase( aList.begin() + i );
144  return graphic;
145  }
146 
147  d = close_ness( aPoint, graphic->GetStart() );
148  if( d < min_d )
149  {
150  min_d = d;
151  ndx_min = i;
152  }
153 
154  d = close_ness( aPoint, graphic->GetEnd() );
155  if( d < min_d )
156  {
157  min_d = d;
158  ndx_min = i;
159  }
160  }
161  }
162 
163  if( min_d <= aLimit )
164  {
165  DRAWSEGMENT* graphic = aList[ndx_min];
166  aList.erase( aList.begin() + ndx_min );
167  return graphic;
168  }
169 
170  return NULL;
171 }
wxPoint GetArcStart() const
wxPoint GetArcEnd() const
STROKE_T GetShape() const
const wxPoint & GetEnd() const
Function GetEnd returns the ending point of the graphic.
static unsigned close_ness(const wxPoint &aLeft, const wxPoint &aRight)
Function close_ness is a non-exact distance (also called Manhattan distance) used to approximate the ...
#define NULL
Arcs (with rounded ends)
const wxPoint & GetStart() const
Function GetStart returns the starting point of the graphic.

References close_ness(), DRAWSEGMENT::GetArcEnd(), DRAWSEGMENT::GetArcStart(), DRAWSEGMENT::GetEnd(), DRAWSEGMENT::GetShape(), DRAWSEGMENT::GetStart(), NULL, and S_ARC.

Referenced by ConvertOutlineToPolygon().