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

770 {
771  PCB_TYPE_COLLECTOR items;
772  bool success = false;
773 
774  // Get all the DRAWSEGMENTS and module graphics into 'items',
775  // then keep only those on layer == Edge_Cuts.
776  static const KICAD_T scan_graphics[] = { PCB_LINE_T, PCB_MODULE_EDGE_T, EOT };
777  items.Collect( aBoard, scan_graphics );
778 
779  // Make a working copy of aSegList, because the list is modified during calculations
780  std::vector< DRAWSEGMENT* > segList;
781 
782  for( int ii = 0; ii < items.GetCount(); ii++ )
783  {
784  if( items[ii]->GetLayer() == Edge_Cuts )
785  segList.push_back( static_cast< DRAWSEGMENT* >( items[ii] ) );
786  }
787 
788  if( segList.size() )
789  {
790  success = ConvertOutlineToPolygon( segList, aOutlines, aErrorText, aTolerance,
791  aErrorLocation );
792  }
793 
794  if( !success || !aOutlines.OutlineCount() )
795  {
796  // Couldn't create a valid polygon outline. Use the board edge cuts bounding box to
797  // create a rectangular outline, or, failing that, the bounding box of the items on
798  // the board.
799 
800  EDA_RECT bbbox = aBoard->GetBoardEdgesBoundingBox();
801 
802  // If null area, uses the global bounding box.
803  if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
804  bbbox = aBoard->ComputeBoundingBox();
805 
806  // Ensure non null area. If happen, gives a minimal size.
807  if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
808  bbbox.Inflate( Millimeter2iu( 1.0 ) );
809 
810  aOutlines.RemoveAllContours();
811  aOutlines.NewOutline();
812 
813  wxPoint corner;
814  aOutlines.Append( bbbox.GetOrigin() );
815 
816  corner.x = bbbox.GetOrigin().x;
817  corner.y = bbbox.GetEnd().y;
818  aOutlines.Append( corner );
819 
820  aOutlines.Append( bbbox.GetEnd() );
821 
822  corner.x = bbbox.GetEnd().x;
823  corner.y = bbbox.GetOrigin().y;
824  aOutlines.Append( corner );
825  }
826 
827  return success;
828 }
int OutlineCount() const
Returns the number of outlines in the set
const EDA_RECT GetBoardEdgesBoundingBox() const
Function GetBoardEdgesBoundingBox Returns the board bounding box calculated using exclusively the boa...
Definition: class_board.h:798
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:577
int GetHeight() const
Definition: eda_rect.h:120
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
Function ComputeBoundingBox calculates the bounding box containing all board items (or board edge seg...
Collect all BOARD_ITEM objects of a given set of KICAD_T type(s).
Definition: collectors.h:621
class DRAWSEGMENT, a segment not on copper layers
Definition: typeinfo.h:91
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(), 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 185 of file convert_drawsegment_list_to_polygon.cpp.

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