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

751 {
752  PCB_TYPE_COLLECTOR items;
753 
754  // Get all the DRAWSEGMENTS and module graphics into 'items',
755  // then keep only those on layer == Edge_Cuts.
756  static const KICAD_T scan_graphics[] = { PCB_LINE_T, PCB_MODULE_EDGE_T, EOT };
757  items.Collect( aBoard, scan_graphics );
758 
759  // Make a working copy of aSegList, because the list is modified during calculations
760  std::vector< DRAWSEGMENT* > segList;
761 
762  for( int ii = 0; ii < items.GetCount(); ii++ )
763  {
764  if( items[ii]->GetLayer() == Edge_Cuts )
765  segList.push_back( static_cast< DRAWSEGMENT* >( items[ii] ) );
766  }
767 
768  bool success = ConvertOutlineToPolygon( segList, aOutlines, aErrorText, aTolerance, aErrorLocation );
769 
770  if( !success || !aOutlines.OutlineCount() )
771  {
772  // Creates a valid polygon outline is not possible.
773  // So uses the board edge cuts bounding box to create a
774  // rectangular outline
775  // When no edge cuts items, build a contour
776  // from global bounding box
777 
778  EDA_RECT bbbox = aBoard->GetBoardEdgesBoundingBox();
779 
780  // If null area, uses the global bounding box.
781  if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
782  bbbox = aBoard->ComputeBoundingBox();
783 
784  // Ensure non null area. If happen, gives a minimal size.
785  if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
786  bbbox.Inflate( Millimeter2iu( 1.0 ) );
787 
788  aOutlines.RemoveAllContours();
789  aOutlines.NewOutline();
790 
791  wxPoint corner;
792  aOutlines.Append( bbbox.GetOrigin() );
793 
794  corner.x = bbbox.GetOrigin().x;
795  corner.y = bbbox.GetEnd().y;
796  aOutlines.Append( corner );
797 
798  aOutlines.Append( bbbox.GetEnd() );
799 
800  corner.x = bbbox.GetEnd().x;
801  corner.y = bbbox.GetOrigin().y;
802  aOutlines.Append( corner );
803  }
804 
805  return success;
806 }
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:791
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:116
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:520
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_SEGMENT:
211  {
212  if( graphic->GetStart().x < xmin.x )
213  {
214  xmin = graphic->GetStart();
215  xmini = i;
216  }
217 
218  if( graphic->GetEnd().x < xmin.x )
219  {
220  xmin = graphic->GetEnd();
221  xmini = i;
222  }
223  }
224  break;
225 
226  case S_ARC:
227  // Freerouter does not yet understand arcs, so approximate
228  // an arc with a series of short lines and put those
229  // line segments into the !same! PATH.
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, ARC_LOW_DEF, 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( unsigned int jj = 0; jj < graphic->GetBezierPoints().size(); jj++ )
276  {
277  wxPoint pt = graphic->GetBezierPoints()[jj];
278 
279  if( pt.x < xmin.x )
280  {
281  xmin = pt;
282  xmini = i;
283  }
284  }
285  }
286  break;
287 
288  case S_POLYGON:
289  {
290  const auto poly = graphic->GetPolyShape();
291  MODULE* module = aSegList[0]->GetParentModule();
292  double orientation = module ? module->GetOrientation() : 0.0;
293  VECTOR2I offset = module ? module->GetPosition() : VECTOR2I( 0, 0 );
294 
295  for( auto iter = poly.CIterate(); iter; iter++ )
296  {
297  auto pt = *iter;
298  RotatePoint( pt, orientation );
299  pt += offset;
300 
301  if( pt.x < xmin.x )
302  {
303  xmin.x = pt.x;
304  xmin.y = pt.y;
305  xmini = i;
306  }
307  }
308  }
309  break;
310  default:
311  break;
312  }
313  }
314 
315  // Grab the left most point, assume its on the board's perimeter, and see if we
316  // can put enough graphics together by matching endpoints to formulate a cohesive
317  // polygon.
318 
319  graphic = (DRAWSEGMENT*) segList[xmini];
320 
321  // The first DRAWSEGMENT is in 'graphic', ok to remove it from 'items'
322  segList.erase( segList.begin() + xmini );
323 
324  // Output the outline perimeter as polygon.
325  if( graphic->GetShape() == S_CIRCLE )
326  {
327  TransformCircleToPolygon( aPolygons, graphic->GetCenter(), graphic->GetRadius(), aTolerance );
328  }
329  else if( graphic->GetShape() == S_POLYGON )
330  {
331  MODULE* module = graphic->GetParentModule(); // NULL for items not in footprints
332  double orientation = module ? module->GetOrientation() : 0.0;
333  VECTOR2I offset = module ? module->GetPosition() : VECTOR2I( 0, 0 );
334 
335  aPolygons.NewOutline();
336 
337  for( auto it = graphic->GetPolyShape().CIterate( 0 ); it; it++ )
338  {
339  auto pt = *it;
340  RotatePoint( pt, orientation );
341  pt += offset;
342  aPolygons.Append( pt );
343  }
344  }
345  else
346  {
347  // Polygon start point. Arbitrarily chosen end of the
348  // segment and build the poly from here.
349 
350  wxPoint startPt = wxPoint( graphic->GetEnd() );
351  prevPt = graphic->GetEnd();
352  aPolygons.NewOutline();
353  aPolygons.Append( prevPt );
354 
355  // Do not append the other end point yet of this 'graphic', this first
356  // 'graphic' might be an arc or a curve.
357 
358  for(;;)
359  {
360  switch( graphic->GetShape() )
361  {
362  case S_SEGMENT:
363  {
364  wxPoint nextPt;
365 
366  // Use the line segment end point furthest away from
367  // prevPt as we assume the other end to be ON prevPt or
368  // very close to it.
369 
370  if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
371  nextPt = graphic->GetEnd();
372  else
373  nextPt = graphic->GetStart();
374 
375  aPolygons.Append( nextPt );
376  prevPt = nextPt;
377  }
378  break;
379 
380  case S_ARC:
381  // We do not support arcs in polygons, so approximate
382  // an arc with a series of short lines and put those
383  // line segments into the !same! PATH.
384  {
385  wxPoint pstart = graphic->GetArcStart();
386  wxPoint pend = graphic->GetArcEnd();
387  wxPoint pcenter = graphic->GetCenter();
388  double angle = -graphic->GetAngle();
389  double radius = graphic->GetRadius();
390  int steps = GetArcToSegmentCount( radius, aTolerance, angle / 10.0 );
391 
392  if( !close_enough( prevPt, pstart, aTolerance ) )
393  {
394  wxASSERT( close_enough( prevPt, graphic->GetArcEnd(), aTolerance ) );
395 
396  angle = -angle;
397  std::swap( pstart, pend );
398  }
399 
400  wxPoint nextPt;
401 
402  for( int step = 1; step<=steps; ++step )
403  {
404  double rotation = ( angle * step ) / steps;
405  nextPt = pstart;
406  RotatePoint( &nextPt, pcenter, rotation );
407 
408  aPolygons.Append( nextPt );
409  }
410 
411  prevPt = nextPt;
412  }
413  break;
414 
415  case S_CURVE:
416  // We do not support Bezier curves in polygons, so approximate
417  // with a series of short lines and put those
418  // line segments into the !same! PATH.
419  {
420  wxPoint nextPt;
421  bool reverse = false;
422 
423  // Use the end point furthest away from
424  // prevPt as we assume the other end to be ON prevPt or
425  // very close to it.
426 
427  if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
428  nextPt = graphic->GetEnd();
429  else
430  {
431  nextPt = graphic->GetStart();
432  reverse = true;
433  }
434 
435  if( reverse )
436  {
437  for( int jj = graphic->GetBezierPoints().size()-1; jj >= 0; jj-- )
438  aPolygons.Append( graphic->GetBezierPoints()[jj] );
439  }
440  else
441  {
442  for( size_t jj = 0; jj < graphic->GetBezierPoints().size(); jj++ )
443  aPolygons.Append( graphic->GetBezierPoints()[jj] );
444  }
445 
446  prevPt = nextPt;
447  }
448  break;
449 
450  default:
451  if( aErrorText )
452  {
453  msg.Printf( "Unsupported DRAWSEGMENT type %s.",
454  BOARD_ITEM::ShowShape( graphic->GetShape() ) );
455 
456  *aErrorText << msg << "\n";
457  }
458 
459  if( aErrorLocation )
460  *aErrorLocation = graphic->GetPosition();
461 
462  return false;
463  }
464 
465  // Get next closest segment.
466 
467  graphic = findPoint( prevPt, segList, aTolerance );
468 
469  // If there are no more close segments, check if the board
470  // outline polygon can be closed.
471 
472  if( !graphic )
473  {
474  if( close_enough( startPt, prevPt, aTolerance ) )
475  {
476  // Close the polygon back to start point
477  // aPolygons.Append( startPt ); // not needed
478  }
479  else
480  {
481  if( aErrorText )
482  {
483  msg.Printf( _( "Unable to find segment with an endpoint of (%s, %s)." ),
484  StringFromValue( EDA_UNITS::MILLIMETRES, prevPt.x, true ),
485  StringFromValue( EDA_UNITS::MILLIMETRES, prevPt.y, true ) );
486 
487  *aErrorText << msg << "\n";
488  }
489 
490  if( aErrorLocation )
491  *aErrorLocation = prevPt;
492 
493  return false;
494  }
495  break;
496  }
497  }
498  }
499 
500  while( segList.size() )
501  {
502  // emit a signal layers keepout for every interior polygon left...
503  int hole = aPolygons.NewHole();
504 
505  graphic = (DRAWSEGMENT*) segList[0];
506  segList.erase( segList.begin() );
507 
508  // Both circles and polygons on the edge cuts layer are closed items that
509  // do not connect to other elements, so we process them independently
510  if( graphic->GetShape() == S_POLYGON )
511  {
512  MODULE* module = graphic->GetParentModule(); // NULL for items not in footprints
513  double orientation = module ? module->GetOrientation() : 0.0;
514  VECTOR2I offset = module ? module->GetPosition() : VECTOR2I( 0, 0 );
515 
516  for( auto it = graphic->GetPolyShape().CIterate(); it; it++ )
517  {
518  auto val = *it;
519  RotatePoint( val, orientation );
520  val += offset;
521 
522  aPolygons.Append( val, -1, hole );
523  }
524  }
525  else if( graphic->GetShape() == S_CIRCLE )
526  {
527  // make a circle by segments;
528  wxPoint center = graphic->GetCenter();
529  double angle = 3600.0;
530  wxPoint start = center;
531  int radius = graphic->GetRadius();
532  int steps = std::max<int>( 4, GetArcToSegmentCount( radius, aTolerance, 360.0 ) );
533  wxPoint nextPt;
534 
535  start.x += radius;
536 
537  for( int step = 0; step < steps; ++step )
538  {
539  double rotation = ( angle * step ) / steps;
540  nextPt = start;
541  RotatePoint( &nextPt.x, &nextPt.y, center.x, center.y, rotation );
542  aPolygons.Append( nextPt, -1, hole );
543  }
544  }
545  else
546  {
547  // Polygon start point. Arbitrarily chosen end of the
548  // segment and build the poly from here.
549 
550  wxPoint startPt( graphic->GetEnd() );
551  prevPt = graphic->GetEnd();
552  aPolygons.Append( prevPt, -1, hole );
553 
554  // do not append the other end point yet, this first 'graphic' might be an arc
555  for(;;)
556  {
557  switch( graphic->GetShape() )
558  {
559  case S_SEGMENT:
560  {
561  wxPoint nextPt;
562 
563  // Use the line segment end point furthest away from
564  // prevPt as we assume the other end to be ON prevPt or
565  // very close to it.
566 
567  if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
568  {
569  nextPt = graphic->GetEnd();
570  }
571  else
572  {
573  nextPt = graphic->GetStart();
574  }
575 
576  prevPt = nextPt;
577  aPolygons.Append( prevPt, -1, hole );
578  }
579  break;
580 
581  case S_ARC:
582  // Freerouter does not yet understand arcs, so approximate
583  // an arc with a series of short lines and put those
584  // line segments into the !same! PATH.
585  {
586  wxPoint pstart = graphic->GetArcStart();
587  wxPoint pend = graphic->GetArcEnd();
588  wxPoint pcenter = graphic->GetCenter();
589  double angle = -graphic->GetAngle();
590  int radius = graphic->GetRadius();
591  int steps = GetArcToSegmentCount( radius, aTolerance, angle / 10.0 );
592 
593  if( !close_enough( prevPt, pstart, aTolerance ) )
594  {
595  wxASSERT( close_enough( prevPt, graphic->GetArcEnd(), aTolerance ) );
596 
597  angle = -angle;
598  std::swap( pstart, pend );
599  }
600 
601  wxPoint nextPt;
602 
603  for( int step = 1; step <= steps; ++step )
604  {
605  double rotation = ( angle * step ) / steps;
606 
607  nextPt = pstart;
608  RotatePoint( &nextPt, pcenter, rotation );
609 
610  aPolygons.Append( nextPt, -1, hole );
611  }
612 
613  prevPt = nextPt;
614  }
615  break;
616 
617  case S_CURVE:
618  // We do not support Bezier curves in polygons, so approximate
619  // with a series of short lines and put those
620  // line segments into the !same! PATH.
621  {
622  wxPoint nextPt;
623  bool reverse = false;
624 
625  // Use the end point furthest away from
626  // prevPt as we assume the other end to be ON prevPt or
627  // very close to it.
628 
629  if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
630  nextPt = graphic->GetEnd();
631  else
632  {
633  nextPt = graphic->GetStart();
634  reverse = true;
635  }
636 
637  if( reverse )
638  {
639  for( int jj = graphic->GetBezierPoints().size()-1; jj >= 0; jj-- )
640  aPolygons.Append( graphic->GetBezierPoints()[jj], -1, hole );
641  }
642  else
643  {
644  for( size_t jj = 0; jj < graphic->GetBezierPoints().size(); jj++ )
645  aPolygons.Append( graphic->GetBezierPoints()[jj], -1, hole );
646  }
647 
648  prevPt = nextPt;
649  }
650  break;
651 
652  default:
653  if( aErrorText )
654  {
655  msg.Printf( "Unsupported DRAWSEGMENT type %s.",
656  BOARD_ITEM::ShowShape( graphic->GetShape() ) );
657 
658  *aErrorText << msg << "\n";
659  }
660 
661  if( aErrorLocation )
662  *aErrorLocation = graphic->GetPosition();
663 
664  return false;
665  }
666 
667  // Get next closest segment.
668 
669  graphic = findPoint( prevPt, segList, aTolerance );
670 
671  // If there are no more close segments, check if polygon
672  // can be closed.
673 
674  if( !graphic )
675  {
676  if( close_enough( startPt, prevPt, aTolerance ) )
677  {
678  // Close the polygon back to start point
679  // aPolygons.Append( startPt, -1, hole ); // not needed
680  }
681  else
682  {
683  if( aErrorText )
684  {
685  msg.Printf( _( "Unable to find segment with an endpoint of (%s, %s)." ),
686  StringFromValue( EDA_UNITS::MILLIMETRES, prevPt.x, true ),
687  StringFromValue( EDA_UNITS::MILLIMETRES, prevPt.y, true ) );
688 
689  *aErrorText << msg << "\n";
690  }
691 
692  if( aErrorLocation )
693  *aErrorLocation = prevPt;
694 
695  return false;
696  }
697  break;
698  }
699  }
700  }
701  }
702 
703  // All of the silliness that follows is to work around the segment iterator
704  // while checking for collisions.
705  // TODO: Implement proper segment and point iterators that follow std
706  for( auto seg1 = aPolygons.IterateSegmentsWithHoles(); seg1; seg1++ )
707  {
708  auto seg2 = seg1;
709 
710  for( ++seg2; seg2; seg2++ )
711  {
712  // Check for exact overlapping segments. This is not viewed
713  // as an intersection below
714  if( *seg1 == *seg2 ||
715  ( ( *seg1 ).A == ( *seg2 ).B && ( *seg1 ).B == ( *seg2 ).A ) )
716  {
717  if( aErrorLocation )
718  {
719  aErrorLocation->x = ( *seg1 ).A.x;
720  aErrorLocation->y = ( *seg1 ).A.y;
721  }
722 
723  return false;
724  }
725 
726  if( auto pt = seg1.Get().Intersect( seg2.Get(), true ) )
727  {
728  if( aErrorLocation )
729  {
730  aErrorLocation->x = pt->x;
731  aErrorLocation->y = pt->y;
732  }
733 
734  return false;
735  }
736  }
737  }
738 
739  return true;
740 }
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:215
SEGMENT_ITERATOR IterateSegmentsWithHoles()
Returns an iterator object, for all outlines in the set (with holes)
const wxPoint GetCenter() const override
Function GetCenter()
STROKE_T GetShape() const
const wxPoint GetArcEnd() 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...
wxString StringFromValue(EDA_UNITS aUnits, double aValue, bool aAddUnitSymbol, bool aUseMils)
Function StringFromValue returns the string from aValue according to units (inch, mm ....
Definition: base_units.cpp:219
const wxPoint GetPosition() const override
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...
Arcs (with rounded ends)
MODULE * GetParentModule() const
Function GetParentModule returns a pointer to the parent module, or NULL if DRAWSEGMENT does not belo...
const wxPoint & GetArcStart() const
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:31
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.
const wxPoint GetPosition() const override
Definition: class_module.h:210
int GetArcToSegmentCount(int aRadius, int aErrorMax, double aArcAngleDegree)
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::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_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 }
STROKE_T GetShape() const
const wxPoint GetArcEnd() 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 & GetArcStart() const
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().