KiCad PCB EDA Suite
convert_drawsegment_list_to_polygon.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 2015 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
6  * Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25 
32 #include <trigo.h>
33 #include <macros.h>
34 
35 #include <math/vector2d.h>
36 #include <pcb_shape.h>
37 #include <class_module.h>
38 #include <base_units.h>
42 
43 #include <wx/log.h>
44 
45 
53 const wxChar* traceBoardOutline = wxT( "KICAD_BOARD_OUTLINE" );
54 
67 static unsigned close_ness( const wxPoint& aLeft, const wxPoint& aRight )
68 {
69  // Don't need an accurate distance calculation, just something
70  // approximating it, for relative ordering.
71  return unsigned( std::abs( aLeft.x - aRight.x ) + abs( aLeft.y - aRight.y ) );
72 }
73 
83 inline bool close_enough( const wxPoint& aLeft, const wxPoint& aRight, unsigned aLimit )
84 {
85  // We don't use an accurate distance calculation, just something
86  // approximating it, since aLimit is non-exact anyway except when zero.
87  return close_ness( aLeft, aRight ) <= aLimit;
88 }
89 
99 inline bool close_st( const wxPoint& aReference, const wxPoint& aFirst, const wxPoint& aSecond )
100 {
101  // We don't use an accurate distance calculation, just something
102  // approximating to find the closest to the reference.
103  return close_ness( aReference, aFirst ) <= close_ness( aReference, aSecond );
104 }
105 
106 
116 static PCB_SHAPE* findPoint( const wxPoint& aPoint, std::vector< PCB_SHAPE* >& aList,
117  unsigned aLimit )
118 {
119  unsigned min_d = INT_MAX;
120  int ndx_min = 0;
121 
122  // find the point closest to aPoint and perhaps exactly matching aPoint.
123  for( size_t i = 0; i < aList.size(); ++i )
124  {
125  PCB_SHAPE* graphic = aList[i];
126  unsigned d;
127 
128  switch( graphic->GetShape() )
129  {
130  case S_ARC:
131  if( aPoint == graphic->GetArcStart() || aPoint == graphic->GetArcEnd() )
132  {
133  aList.erase( aList.begin() + i );
134  return graphic;
135  }
136 
137  d = close_ness( aPoint, graphic->GetArcStart() );
138  if( d < min_d )
139  {
140  min_d = d;
141  ndx_min = i;
142  }
143 
144  d = close_ness( aPoint, graphic->GetArcEnd() );
145  if( d < min_d )
146  {
147  min_d = d;
148  ndx_min = i;
149  }
150  break;
151 
152  default:
153  if( aPoint == graphic->GetStart() || aPoint == graphic->GetEnd() )
154  {
155  aList.erase( aList.begin() + i );
156  return graphic;
157  }
158 
159  d = close_ness( aPoint, graphic->GetStart() );
160  if( d < min_d )
161  {
162  min_d = d;
163  ndx_min = i;
164  }
165 
166  d = close_ness( aPoint, graphic->GetEnd() );
167  if( d < min_d )
168  {
169  min_d = d;
170  ndx_min = i;
171  }
172  }
173  }
174 
175  if( min_d <= aLimit )
176  {
177  PCB_SHAPE* graphic = aList[ndx_min];
178  aList.erase( aList.begin() + ndx_min );
179  return graphic;
180  }
181 
182  return NULL;
183 }
184 
185 
198 bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET& aPolygons,
199  wxString* aErrorText, unsigned int aTolerance,
200  wxPoint* aErrorLocation )
201 {
202  if( aSegList.size() == 0 )
203  return true;
204 
205  // Return value
206  bool polygonComplete = true;
207 
208  wxString msg;
209 
210  // Make a working copy of aSegList, because the list is modified during calculations
211  std::vector<PCB_SHAPE*> segList = aSegList;
212 
213  PCB_SHAPE* graphic;
214  wxPoint prevPt;
215 
216  // Find edge point with minimum x, this should be in the outer polygon
217  // which will define the perimeter polygon polygon.
218  wxPoint xmin = wxPoint( INT_MAX, 0 );
219  int xmini = 0;
220 
221  for( size_t i = 0; i < segList.size(); i++ )
222  {
223  graphic = (PCB_SHAPE*) segList[i];
224 
225  switch( graphic->GetShape() )
226  {
227  case S_RECT:
228  case S_SEGMENT:
229  {
230  if( graphic->GetStart().x < xmin.x )
231  {
232  xmin = graphic->GetStart();
233  xmini = i;
234  }
235 
236  if( graphic->GetEnd().x < xmin.x )
237  {
238  xmin = graphic->GetEnd();
239  xmini = i;
240  }
241  }
242  break;
243 
244  case S_ARC:
245  {
246  wxPoint pstart = graphic->GetArcStart();
247  wxPoint center = graphic->GetCenter();
248  double angle = -graphic->GetAngle();
249  double radius = graphic->GetRadius();
250  int steps = GetArcToSegmentCount( radius, aTolerance, angle / 10.0 );
251  wxPoint pt;
252 
253  for( int step = 1; step<=steps; ++step )
254  {
255  double rotation = ( angle * step ) / steps;
256 
257  pt = pstart;
258 
259  RotatePoint( &pt, center, rotation );
260 
261  if( pt.x < xmin.x )
262  {
263  xmin = pt;
264  xmini = i;
265  }
266  }
267  }
268  break;
269 
270  case S_CIRCLE:
271  {
272  wxPoint pt = graphic->GetCenter();
273 
274  // pt has minimum x point
275  pt.x -= graphic->GetRadius();
276 
277  // when the radius <= 0, this is a mal-formed circle. Skip it
278  if( graphic->GetRadius() > 0 && pt.x < xmin.x )
279  {
280  xmin = pt;
281  xmini = i;
282  }
283  }
284  break;
285 
286  case S_CURVE:
287  {
288  graphic->RebuildBezierToSegmentsPointsList( graphic->GetWidth() );
289 
290  for( const wxPoint& pt : graphic->GetBezierPoints())
291  {
292  if( pt.x < xmin.x )
293  {
294  xmin = pt;
295  xmini = i;
296  }
297  }
298  }
299  break;
300 
301  case S_POLYGON:
302  {
303  const SHAPE_POLY_SET poly = graphic->GetPolyShape();
304  MODULE* module = aSegList[0]->GetParentModule();
305  double orientation = module ? module->GetOrientation() : 0.0;
306  VECTOR2I offset = module ? module->GetPosition() : VECTOR2I( 0, 0 );
307 
308  for( auto iter = poly.CIterate(); iter; iter++ )
309  {
310  VECTOR2I pt = *iter;
311  RotatePoint( pt, orientation );
312  pt += offset;
313 
314  if( pt.x < xmin.x )
315  {
316  xmin.x = pt.x;
317  xmin.y = pt.y;
318  xmini = i;
319  }
320  }
321  }
322  break;
323 
324  default:
325  break;
326  }
327  }
328 
329  // Grab the left most point, assume its on the board's perimeter, and see if we
330  // can put enough graphics together by matching endpoints to formulate a cohesive
331  // polygon.
332 
333  graphic = (PCB_SHAPE*) segList[xmini];
334 
335  // The first PCB_SHAPE is in 'graphic', ok to remove it from 'items'
336  segList.erase( segList.begin() + xmini );
337 
338  // Output the outline perimeter as polygon.
339  if( graphic->GetShape() == S_CIRCLE )
340  {
341  TransformCircleToPolygon( aPolygons, graphic->GetCenter(), graphic->GetRadius(),
342  ARC_LOW_DEF, ERROR_INSIDE );
343  }
344  else if( graphic->GetShape() == S_RECT )
345  {
346  std::vector<wxPoint> pts = graphic->GetRectCorners();
347 
348  aPolygons.NewOutline();
349 
350  for( const wxPoint& pt : pts )
351  aPolygons.Append( pt );
352  }
353  else if( graphic->GetShape() == S_POLYGON )
354  {
355  MODULE* module = graphic->GetParentModule(); // NULL for items not in footprints
356  double orientation = module ? module->GetOrientation() : 0.0;
357  VECTOR2I offset = module ? module->GetPosition() : VECTOR2I( 0, 0 );
358 
359  aPolygons.NewOutline();
360 
361  for( auto it = graphic->GetPolyShape().CIterate( 0 ); it; it++ )
362  {
363  auto pt = *it;
364  RotatePoint( pt, orientation );
365  pt += offset;
366  aPolygons.Append( pt );
367  }
368  }
369  else
370  {
371  // Polygon start point. Arbitrarily chosen end of the
372  // segment and build the poly from here.
373 
374  wxPoint startPt = graphic->GetShape() == S_ARC ? graphic->GetArcEnd()
375  : graphic->GetEnd();
376 
377  prevPt = startPt;
378  aPolygons.NewOutline();
379  aPolygons.Append( prevPt );
380 
381  // Do not append the other end point yet of this 'graphic', this first
382  // 'graphic' might be an arc or a curve.
383 
384  for(;;)
385  {
386  switch( graphic->GetShape() )
387  {
388  case S_SEGMENT:
389  {
390  wxPoint nextPt;
391 
392  // Use the line segment end point furthest away from prevPt as we assume
393  // the other end to be ON prevPt or very close to it.
394 
395  if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
396  nextPt = graphic->GetEnd();
397  else
398  nextPt = graphic->GetStart();
399 
400  aPolygons.Append( nextPt );
401  prevPt = nextPt;
402  }
403  break;
404 
405  case S_ARC:
406  // We do not support arcs in polygons, so approximate an arc with a series of
407  // short lines and put those line segments into the !same! PATH.
408  {
409  wxPoint pstart = graphic->GetArcStart();
410  wxPoint pend = graphic->GetArcEnd();
411  wxPoint pcenter = graphic->GetCenter();
412  double angle = -graphic->GetAngle();
413  double radius = graphic->GetRadius();
414  int steps = GetArcToSegmentCount( radius, aTolerance, angle / 10.0 );
415 
416  if( !close_enough( prevPt, pstart, aTolerance ) )
417  {
418  wxASSERT( close_enough( prevPt, graphic->GetArcEnd(), aTolerance ) );
419 
420  angle = -angle;
421  std::swap( pstart, pend );
422  }
423 
424  wxPoint nextPt;
425 
426  for( int step = 1; step<=steps; ++step )
427  {
428  double rotation = ( angle * step ) / steps;
429  nextPt = pstart;
430  RotatePoint( &nextPt, pcenter, rotation );
431 
432  aPolygons.Append( nextPt );
433  }
434 
435  prevPt = nextPt;
436  }
437  break;
438 
439  case S_CURVE:
440  // We do not support Bezier curves in polygons, so approximate with a series
441  // of short lines and put those line segments into the !same! PATH.
442  {
443  wxPoint nextPt;
444  bool reverse = false;
445 
446  // Use the end point furthest away from
447  // prevPt as we assume the other end to be ON prevPt or
448  // very close to it.
449 
450  if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
451  nextPt = graphic->GetEnd();
452  else
453  {
454  nextPt = graphic->GetStart();
455  reverse = true;
456  }
457 
458  if( reverse )
459  {
460  for( int jj = graphic->GetBezierPoints().size()-1; jj >= 0; jj-- )
461  aPolygons.Append( graphic->GetBezierPoints()[jj] );
462  }
463  else
464  {
465  for( size_t jj = 0; jj < graphic->GetBezierPoints().size(); jj++ )
466  aPolygons.Append( graphic->GetBezierPoints()[jj] );
467  }
468 
469  prevPt = nextPt;
470  }
471  break;
472 
473  default:
474  if( aErrorText )
475  {
476  msg.Printf( "Unsupported PCB_SHAPE type %s.",
477  BOARD_ITEM::ShowShape( graphic->GetShape() ) );
478 
479  *aErrorText << msg << "\n";
480  }
481 
482  if( aErrorLocation )
483  *aErrorLocation = graphic->GetPosition();
484 
485  return false;
486  }
487 
488  // Get next closest segment.
489 
490  graphic = findPoint( prevPt, segList, aTolerance );
491 
492  // If there are no more close segments, check if the board
493  // outline polygon can be closed.
494 
495  if( !graphic )
496  {
497  if( close_enough( startPt, prevPt, aTolerance ) )
498  {
499  // Close the polygon back to start point
500  // aPolygons.Append( startPt ); // not needed
501  }
502  else
503  {
504  if( aErrorText )
505  {
506  msg.Printf( _( "Unable to find edge with an endpoint of (%s, %s)." ),
509 
510  *aErrorText << msg << "\n";
511  }
512 
513  if( aErrorLocation )
514  *aErrorLocation = prevPt;
515 
516  polygonComplete = false;
517  break;
518  }
519  break;
520  }
521  }
522  }
523 
524  int holeNum = -1;
525 
526  while( segList.size() )
527  {
528  // emit a signal layers keepout for every interior polygon left...
529  int hole = aPolygons.NewHole();
530  holeNum++;
531 
532  graphic = (PCB_SHAPE*) segList[0];
533  segList.erase( segList.begin() );
534 
535  // Both circles and polygons on the edge cuts layer are closed items that
536  // do not connect to other elements, so we process them independently
537  if( graphic->GetShape() == S_POLYGON )
538  {
539  MODULE* module = graphic->GetParentModule(); // NULL for items not in footprints
540  double orientation = module ? module->GetOrientation() : 0.0;
541  VECTOR2I offset = module ? module->GetPosition() : VECTOR2I( 0, 0 );
542 
543  for( auto it = graphic->GetPolyShape().CIterate(); it; it++ )
544  {
545  auto val = *it;
546  RotatePoint( val, orientation );
547  val += offset;
548 
549  aPolygons.Append( val, -1, hole );
550  }
551  }
552  else if( graphic->GetShape() == S_CIRCLE )
553  {
554  // make a circle by segments;
555  wxPoint center = graphic->GetCenter();
556  double angle = 3600.0;
557  wxPoint start = center;
558  int radius = graphic->GetRadius();
559  int steps = GetArcToSegmentCount( radius, aTolerance, 360.0 );
560  wxPoint nextPt;
561 
562  start.x += radius;
563 
564  for( int step = 0; step < steps; ++step )
565  {
566  double rotation = ( angle * step ) / steps;
567  nextPt = start;
568  RotatePoint( &nextPt.x, &nextPt.y, center.x, center.y, rotation );
569  aPolygons.Append( nextPt, -1, hole );
570  }
571  }
572  else if( graphic->GetShape() == S_RECT )
573  {
574  std::vector<wxPoint> pts = graphic->GetRectCorners();
575 
576  for( const wxPoint& pt : pts )
577  aPolygons.Append( pt, -1, hole );
578  }
579  else
580  {
581  // Polygon start point. Arbitrarily chosen end of the
582  // segment and build the poly from here.
583 
584  wxPoint startPt( graphic->GetEnd() );
585  prevPt = graphic->GetEnd();
586  aPolygons.Append( prevPt, -1, hole );
587 
588  // do not append the other end point yet, this first 'graphic' might be an arc
589  for(;;)
590  {
591  switch( graphic->GetShape() )
592  {
593  case S_SEGMENT:
594  {
595  wxPoint nextPt;
596 
597  // Use the line segment end point furthest away from
598  // prevPt as we assume the other end to be ON prevPt or
599  // very close to it.
600 
601  if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
602  nextPt = graphic->GetEnd();
603  else
604  nextPt = graphic->GetStart();
605 
606  prevPt = nextPt;
607  aPolygons.Append( prevPt, -1, hole );
608  }
609  break;
610 
611  case S_ARC:
612  // Freerouter does not yet understand arcs, so approximate
613  // an arc with a series of short lines and put those
614  // line segments into the !same! PATH.
615  {
616  wxPoint pstart = graphic->GetArcStart();
617  wxPoint pend = graphic->GetArcEnd();
618  wxPoint pcenter = graphic->GetCenter();
619  double angle = -graphic->GetAngle();
620  int radius = graphic->GetRadius();
621  int steps = GetArcToSegmentCount( radius, aTolerance, angle / 10.0 );
622 
623  if( !close_enough( prevPt, pstart, aTolerance ) )
624  {
625  wxASSERT( close_enough( prevPt, graphic->GetArcEnd(), aTolerance ) );
626 
627  angle = -angle;
628  std::swap( pstart, pend );
629  }
630 
631  wxPoint nextPt;
632 
633  for( int step = 1; step <= steps; ++step )
634  {
635  double rotation = ( angle * step ) / steps;
636 
637  nextPt = pstart;
638  RotatePoint( &nextPt, pcenter, rotation );
639 
640  aPolygons.Append( nextPt, -1, hole );
641  }
642 
643  prevPt = nextPt;
644  }
645  break;
646 
647  case S_CURVE:
648  // We do not support Bezier curves in polygons, so approximate
649  // with a series of short lines and put those
650  // line segments into the !same! PATH.
651  {
652  wxPoint nextPt;
653  bool reverse = false;
654 
655  // Use the end point furthest away from
656  // prevPt as we assume the other end to be ON prevPt or
657  // very close to it.
658 
659  if( close_st( prevPt, graphic->GetStart(), graphic->GetEnd() ) )
660  nextPt = graphic->GetEnd();
661  else
662  {
663  nextPt = graphic->GetStart();
664  reverse = true;
665  }
666 
667  if( reverse )
668  {
669  for( int jj = graphic->GetBezierPoints().size()-1; jj >= 0; jj-- )
670  aPolygons.Append( graphic->GetBezierPoints()[jj], -1, hole );
671  }
672  else
673  {
674  for( const wxPoint& pt : graphic->GetBezierPoints())
675  aPolygons.Append( pt, -1, hole );
676  }
677 
678  prevPt = nextPt;
679  }
680  break;
681 
682  default:
683  if( aErrorText )
684  {
685  msg.Printf( "Unsupported PCB_SHAPE type %s.",
686  BOARD_ITEM::ShowShape( graphic->GetShape() ) );
687 
688  *aErrorText << msg << "\n";
689  }
690 
691  if( aErrorLocation )
692  *aErrorLocation = graphic->GetPosition();
693 
694  return false;
695  }
696 
697  // Get next closest segment.
698 
699  graphic = findPoint( prevPt, segList, aTolerance );
700 
701  // If there are no more close segments, check if polygon
702  // can be closed.
703 
704  if( !graphic )
705  {
706  if( close_enough( startPt, prevPt, aTolerance ) )
707  {
708  // Close the polygon back to start point
709  // aPolygons.Append( startPt, -1, hole ); // not needed
710  }
711  else
712  {
713  if( aErrorText )
714  {
715  msg.Printf( _( "Unable to find edge with an endpoint of (%s, %s)." ),
718 
719  *aErrorText << msg << "\n";
720  }
721 
722  if( aErrorLocation )
723  *aErrorLocation = prevPt;
724 
725  aPolygons.Hole( 0, holeNum ).SetClosed( false );
726  polygonComplete = false;
727  }
728  break;
729  }
730  }
731  }
732  }
733 
734  // All of the silliness that follows is to work around the segment iterator
735  // while checking for collisions.
736  // TODO: Implement proper segment and point iterators that follow std
737  for( auto seg1 = aPolygons.IterateSegmentsWithHoles(); seg1; seg1++ )
738  {
739  auto seg2 = seg1;
740 
741  for( ++seg2; seg2; seg2++ )
742  {
743  // Check for exact overlapping segments. This is not viewed
744  // as an intersection below
745  if( *seg1 == *seg2 ||
746  ( ( *seg1 ).A == ( *seg2 ).B && ( *seg1 ).B == ( *seg2 ).A ) )
747  {
748  if( aErrorLocation )
749  {
750  aErrorLocation->x = ( *seg1 ).A.x;
751  aErrorLocation->y = ( *seg1 ).A.y;
752  }
753 
754  return false;
755  }
756 
757  if( boost::optional<VECTOR2I> pt = seg1.Get().Intersect( seg2.Get(), true ) )
758  {
759  if( aErrorLocation )
760  {
761  aErrorLocation->x = pt->x;
762  aErrorLocation->y = pt->y;
763  }
764 
765  return false;
766  }
767  }
768  }
769 
770  return polygonComplete;
771 }
772 
773 #include <class_board.h>
774 #include <collectors.h>
775 
776 /* This function is used to extract a board outlines (3D view, automatic zones build ...)
777  * Any closed outline inside the main outline is a hole
778  * All contours should be closed, i.e. valid closed polygon vertices
779  */
780 bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, wxString* aErrorText,
781  unsigned int aTolerance, wxPoint* aErrorLocation )
782 {
783  PCB_TYPE_COLLECTOR items;
784  bool success = false;
785 
786  // Get all the DRAWSEGMENTS and module graphics into 'items',
787  // then keep only those on layer == Edge_Cuts.
788  static const KICAD_T scan_graphics[] = { PCB_SHAPE_T, PCB_FP_SHAPE_T, EOT };
789  items.Collect( aBoard, scan_graphics );
790 
791  // Make a working copy of aSegList, because the list is modified during calculations
792  std::vector<PCB_SHAPE*> segList;
793 
794  for( int ii = 0; ii < items.GetCount(); ii++ )
795  {
796  if( items[ii]->GetLayer() == Edge_Cuts )
797  segList.push_back( static_cast<PCB_SHAPE*>( items[ii] ) );
798  }
799 
800  if( segList.size() )
801  {
802  success = ConvertOutlineToPolygon( segList, aOutlines, aErrorText, aTolerance,
803  aErrorLocation );
804  }
805  else if( aErrorText )
806  {
807  *aErrorText = _( "No edges found on Edge.Cuts layer." );
808  }
809 
810  if( !success || !aOutlines.OutlineCount() )
811  {
812  // Couldn't create a valid polygon outline. Use the board edge cuts bounding box to
813  // create a rectangular outline, or, failing that, the bounding box of the items on
814  // the board.
815 
816  EDA_RECT bbbox = aBoard->GetBoardEdgesBoundingBox();
817 
818  // If null area, uses the global bounding box.
819  if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
820  bbbox = aBoard->ComputeBoundingBox();
821 
822  // Ensure non null area. If happen, gives a minimal size.
823  if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
824  bbbox.Inflate( Millimeter2iu( 1.0 ) );
825 
826  aOutlines.RemoveAllContours();
827  aOutlines.NewOutline();
828 
829  wxPoint corner;
830  aOutlines.Append( bbbox.GetOrigin() );
831 
832  corner.x = bbbox.GetOrigin().x;
833  corner.y = bbbox.GetEnd().y;
834  aOutlines.Append( corner );
835 
836  aOutlines.Append( bbbox.GetEnd() );
837 
838  corner.x = bbbox.GetEnd().x;
839  corner.y = bbbox.GetOrigin().y;
840  aOutlines.Append( corner );
841  }
842 
843  return success;
844 }
845 
846 
859 void buildBoardBoundingBoxPoly( const BOARD* aBoard, SHAPE_POLY_SET& aOutline )
860 {
861  EDA_RECT bbbox = aBoard->GetBoundingBox();
862  SHAPE_LINE_CHAIN chain;
863 
864  // If null area, uses the global bounding box.
865  if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
866  bbbox = aBoard->ComputeBoundingBox();
867 
868  // Ensure non null area. If happen, gives a minimal size.
869  if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
870  bbbox.Inflate( Millimeter2iu( 1.0 ) );
871 
872  // Inflate slightly (by 1/10th the size of the box)
873  bbbox.Inflate( bbbox.GetWidth() / 10, bbbox.GetHeight() / 10 );
874 
875  chain.Append( bbbox.GetOrigin() );
876  chain.Append( bbbox.GetOrigin().x, bbbox.GetEnd().y );
877  chain.Append( bbbox.GetEnd() );
878  chain.Append( bbbox.GetEnd().x, bbbox.GetOrigin().y );
879  chain.SetClosed( true );
880 
881  aOutline.RemoveAllContours();
882  aOutline.AddOutline( chain );
883 }
884 
885 
886 bool isCopperOutside( const MODULE* aMod, SHAPE_POLY_SET& aShape )
887 {
888  bool padOutside = false;
889 
890  for( D_PAD* pad : aMod->Pads() )
891  {
892  SHAPE_POLY_SET poly = aShape;
893 
894  poly.BooleanIntersection( *pad->GetEffectivePolygon(), SHAPE_POLY_SET::PM_FAST );
895 
896  if( poly.OutlineCount() == 0 )
897  {
898  wxPoint padPos = pad->GetPosition();
899  wxLogTrace( traceBoardOutline, "Tested pad (%d, %d): outside", padPos.x, padPos.y );
900  padOutside = true;
901  break;
902  }
903 
904  wxPoint padPos = pad->GetPosition();
905  wxLogTrace( traceBoardOutline, "Tested pad (%d, %d): not outside", padPos.x, padPos.y );
906  }
907 
908  return padOutside;
909 }
910 
911 
912 VECTOR2I projectPointOnSegment( const VECTOR2I& aEndPoint, const SHAPE_POLY_SET& aOutline,
913  int aOutlineNum = 0 )
914 {
915  int minDistance = -1;
916  VECTOR2I projPoint;
917 
918  for( auto it = aOutline.CIterateSegments( aOutlineNum ); it; it++ )
919  {
920  auto seg = it.Get();
921  int dis = seg.Distance( aEndPoint );
922 
923  if( minDistance < 0 || ( dis < minDistance ) )
924  {
925  minDistance = dis;
926  projPoint = seg.NearestPoint( aEndPoint );
927  }
928  }
929 
930  return projPoint;
931 }
932 
933 
934 int findEndSegments( SHAPE_LINE_CHAIN& aChain, SEG& aStartSeg, SEG& aEndSeg )
935 {
936  int foundSegs = 0;
937 
938  for( int i = 0; i < aChain.SegmentCount(); i++ )
939  {
940  SEG seg = aChain.Segment( i );
941 
942  bool foundA = false;
943  bool foundB = false;
944 
945  for( int j = 0; j < aChain.SegmentCount(); j++ )
946  {
947  // Don't test the segment against itself
948  if( i == j )
949  continue;
950 
951  SEG testSeg = aChain.Segment( j );
952 
953  if( testSeg.Contains( seg.A ) )
954  foundA = true;
955 
956  if( testSeg.Contains( seg.B ) )
957  foundB = true;
958  }
959 
960  // This segment isn't a start or end
961  if( foundA && foundB )
962  continue;
963 
964  if( foundSegs == 0 )
965  {
966  // The first segment we encounter is the "start" segment
967  wxLogTrace( traceBoardOutline, "Found start segment: (%d, %d)-(%d, %d)",
968  seg.A.x, seg.A.y, seg.B.x, seg.B.y );
969  aStartSeg = seg;
970  foundSegs++;
971  }
972  else
973  {
974  // Once we find both start and end, we can stop
975  wxLogTrace( traceBoardOutline, "Found end segment: (%d, %d)-(%d, %d)",
976  seg.A.x, seg.A.y, seg.B.x, seg.B.y );
977  aEndSeg = seg;
978  foundSegs++;
979  break;
980  }
981  }
982 
983  return foundSegs;
984 }
985 
986 
1000  wxString* aErrorText, unsigned int aTolerance,
1001  wxPoint* aErrorLocation )
1002 {
1003  PCB_TYPE_COLLECTOR items;
1004 
1005  SHAPE_POLY_SET outlines;
1006 
1007  // Get all the DRAWSEGMENTS and module graphics into 'items',
1008  // then keep only those on layer == Edge_Cuts.
1009  static const KICAD_T scan_graphics[] = { PCB_SHAPE_T, PCB_FP_SHAPE_T, EOT };
1010  items.Collect( aBoard, scan_graphics );
1011 
1012  // Make a working copy of aSegList, because the list is modified during calculations
1013  std::vector<PCB_SHAPE*> segList;
1014 
1015  for( int ii = 0; ii < items.GetCount(); ii++ )
1016  {
1017  if( items[ii]->GetLayer() == Edge_Cuts )
1018  segList.push_back( static_cast<PCB_SHAPE*>( items[ii] ) );
1019  }
1020 
1021  bool success = ConvertOutlineToPolygon( segList, outlines, aErrorText, aTolerance, aErrorLocation );
1022 
1023  MODULE* boardMod = aBoard->GetFirstModule();
1024 
1025  // No module loaded
1026  if( !boardMod )
1027  {
1028  wxLogTrace( traceBoardOutline, "No module found on board" );
1029 
1030  if( aErrorText )
1031  *aErrorText = _( "No footprint loaded" );
1032 
1033  return false;
1034  }
1035 
1036  // A closed outline was found
1037  if( success )
1038  {
1039  wxLogTrace( traceBoardOutline, "Closed outline found" );
1040 
1041  // If copper is outside a closed polygon, treat it as a hole
1042  if( isCopperOutside( boardMod, outlines ) )
1043  {
1044  wxLogTrace( traceBoardOutline, "Treating outline as a hole" );
1045 
1046  buildBoardBoundingBoxPoly( aBoard, aOutlines );
1047 
1048  // Copy all outlines from the conversion as holes into the new outline
1049  for( int i = 0; i < outlines.OutlineCount(); i++ )
1050  {
1051  SHAPE_LINE_CHAIN& out = outlines.Outline( i );
1052 
1053  if( out.IsClosed() )
1054  aOutlines.AddHole( out, -1 );
1055 
1056  for( int j = 0; j < outlines.HoleCount( i ); j++ )
1057  {
1058  SHAPE_LINE_CHAIN& hole = outlines.Hole( i, j );
1059 
1060  if( hole.IsClosed() )
1061  aOutlines.AddHole( hole, -1 );
1062  }
1063  }
1064  }
1065  // If all copper is inside, then the computed outline is the board outline
1066  else
1067  {
1068  wxLogTrace( traceBoardOutline, "Treating outline as board edge" );
1069  aOutlines = outlines;
1070  }
1071 
1072  return true;
1073  }
1074  // No board outlines were found, so use the bounding box
1075  else if( outlines.OutlineCount() == 0 )
1076  {
1077  wxLogTrace( traceBoardOutline, "Using footprint bounding box" );
1078  buildBoardBoundingBoxPoly( aBoard, aOutlines );
1079 
1080  return true;
1081  }
1082  // There is an outline present, but it is not closed
1083  else
1084  {
1085  wxLogTrace( traceBoardOutline, "Trying to build outline" );
1086 
1087  std::vector<SHAPE_LINE_CHAIN> closedChains;
1088  std::vector<SHAPE_LINE_CHAIN> openChains;
1089 
1090  // The ConvertOutlineToPolygon function returns only one main
1091  // outline and the rest as holes, so we promote the holes and process them
1092  openChains.push_back( outlines.Outline( 0 ) );
1093 
1094  for( int j = 0; j < outlines.HoleCount( 0 ); j++ )
1095  {
1096  SHAPE_LINE_CHAIN hole = outlines.Hole( 0, j );
1097 
1098  if( hole.IsClosed() )
1099  {
1100  wxLogTrace( traceBoardOutline, "Found closed hole" );
1101  closedChains.push_back( hole );
1102  }
1103  else
1104  {
1105  wxLogTrace( traceBoardOutline, "Found open hole" );
1106  openChains.push_back( hole );
1107  }
1108  }
1109 
1110  SHAPE_POLY_SET bbox;
1111  buildBoardBoundingBoxPoly( aBoard, bbox );
1112 
1113  // Treat the open polys as the board edge
1114  SHAPE_LINE_CHAIN chain = openChains[0];
1115  SHAPE_LINE_CHAIN rect = bbox.Outline( 0 );
1116 
1117  // We know the outline chain is open, so set to non-closed to get better segment count
1118  chain.SetClosed( false );
1119 
1120  SEG startSeg;
1121  SEG endSeg;
1122 
1123  // The two possible board outlines
1124  SHAPE_LINE_CHAIN upper;
1125  SHAPE_LINE_CHAIN lower;
1126 
1127  findEndSegments( chain, startSeg, endSeg );
1128 
1129  if( chain.SegmentCount() == 0 )
1130  {
1131  // Something is wrong, bail out with the overall module bounding box
1132  wxLogTrace( traceBoardOutline, "No line segments in provided outline" );
1133  aOutlines = bbox;
1134  return true;
1135  }
1136  else if( chain.SegmentCount() == 1 )
1137  {
1138  // This case means there is only 1 line segment making up the edge cuts of the footprint,
1139  // so we just need to use it to cut the bounding box in half.
1140  wxLogTrace( traceBoardOutline, "Only 1 line segment in provided outline" );
1141 
1142  startSeg = chain.Segment( 0 );
1143 
1144  // Intersect with all the sides of the rectangle
1145  OPT_VECTOR2I inter0 = startSeg.IntersectLines( rect.Segment( 0 ) );
1146  OPT_VECTOR2I inter1 = startSeg.IntersectLines( rect.Segment( 1 ) );
1147  OPT_VECTOR2I inter2 = startSeg.IntersectLines( rect.Segment( 2 ) );
1148  OPT_VECTOR2I inter3 = startSeg.IntersectLines( rect.Segment( 3 ) );
1149 
1150  if( inter0 && inter2 && !inter1 && !inter3 )
1151  {
1152  // Intersects the vertical rectangle sides only
1153  wxLogTrace( traceBoardOutline, "Segment intersects only vertical bbox sides" );
1154 
1155  // The upper half
1156  upper.Append( *inter0 );
1157  upper.Append( rect.GetPoint( 1 ) );
1158  upper.Append( rect.GetPoint( 2 ) );
1159  upper.Append( *inter2 );
1160  upper.SetClosed( true );
1161 
1162  // The lower half
1163  lower.Append( *inter0 );
1164  lower.Append( rect.GetPoint( 0 ) );
1165  lower.Append( rect.GetPoint( 3 ) );
1166  lower.Append( *inter2 );
1167  lower.SetClosed( true );
1168  }
1169  else if( inter1 && inter3 && !inter0 && !inter2 )
1170  {
1171  // Intersects the horizontal rectangle sides only
1172  wxLogTrace( traceBoardOutline, "Segment intersects only horizontal bbox sides" );
1173 
1174  // The left half
1175  upper.Append( *inter1 );
1176  upper.Append( rect.GetPoint( 1 ) );
1177  upper.Append( rect.GetPoint( 0 ) );
1178  upper.Append( *inter3 );
1179  upper.SetClosed( true );
1180 
1181  // The right half
1182  lower.Append( *inter1 );
1183  lower.Append( rect.GetPoint( 2 ) );
1184  lower.Append( rect.GetPoint( 3 ) );
1185  lower.Append( *inter3 );
1186  lower.SetClosed( true );
1187  }
1188  else
1189  {
1190  // Angled line segment that cuts across a corner
1191  wxLogTrace( traceBoardOutline, "Segment intersects two perpendicular bbox sides" );
1192 
1193  // Figure out which actual lines are intersected, since IntersectLines assumes an infinite line
1194  bool hit0 = rect.Segment( 0 ).Contains( *inter0 );
1195  bool hit1 = rect.Segment( 1 ).Contains( *inter1 );
1196  bool hit2 = rect.Segment( 2 ).Contains( *inter2 );
1197  bool hit3 = rect.Segment( 3 ).Contains( *inter3 );
1198 
1199  if( hit0 && hit1 )
1200  {
1201  // Cut across the upper left corner
1202  wxLogTrace( traceBoardOutline, "Segment cuts upper left corner" );
1203 
1204  // The upper half
1205  upper.Append( *inter0 );
1206  upper.Append( rect.GetPoint( 1 ) );
1207  upper.Append( *inter1 );
1208  upper.SetClosed( true );
1209 
1210  // The lower half
1211  lower.Append( *inter0 );
1212  lower.Append( rect.GetPoint( 0 ) );
1213  lower.Append( rect.GetPoint( 3 ) );
1214  lower.Append( rect.GetPoint( 2 ) );
1215  lower.Append( *inter1 );
1216  lower.SetClosed( true );
1217  }
1218  else if( hit1 && hit2 )
1219  {
1220  // Cut across the upper right corner
1221  wxLogTrace( traceBoardOutline, "Segment cuts upper right corner" );
1222 
1223  // The upper half
1224  upper.Append( *inter1 );
1225  upper.Append( rect.GetPoint( 2 ) );
1226  upper.Append( *inter2 );
1227  upper.SetClosed( true );
1228 
1229  // The lower half
1230  lower.Append( *inter1 );
1231  lower.Append( rect.GetPoint( 1 ) );
1232  lower.Append( rect.GetPoint( 0 ) );
1233  lower.Append( rect.GetPoint( 3 ) );
1234  lower.Append( *inter2 );
1235  lower.SetClosed( true );
1236  }
1237  else if( hit2 && hit3 )
1238  {
1239  // Cut across the lower right corner
1240  wxLogTrace( traceBoardOutline, "Segment cuts lower right corner" );
1241 
1242  // The upper half
1243  upper.Append( *inter2 );
1244  upper.Append( rect.GetPoint( 2 ) );
1245  upper.Append( rect.GetPoint( 1 ) );
1246  upper.Append( rect.GetPoint( 0 ) );
1247  upper.Append( *inter3 );
1248  upper.SetClosed( true );
1249 
1250  // The bottom half
1251  lower.Append( *inter2 );
1252  lower.Append( rect.GetPoint( 3 ) );
1253  lower.Append( *inter3 );
1254  lower.SetClosed( true );
1255  }
1256  else
1257  {
1258  // Cut across the lower left corner
1259  wxLogTrace( traceBoardOutline, "Segment cuts upper left corner" );
1260 
1261  // The upper half
1262  upper.Append( *inter0 );
1263  upper.Append( rect.GetPoint( 1 ) );
1264  upper.Append( rect.GetPoint( 2 ) );
1265  upper.Append( rect.GetPoint( 3 ) );
1266  upper.Append( *inter3 );
1267  upper.SetClosed( true );
1268 
1269  // The bottom half
1270  lower.Append( *inter0 );
1271  lower.Append( rect.GetPoint( 0 ) );
1272  lower.Append( *inter3 );
1273  lower.SetClosed( true );
1274  }
1275  }
1276  }
1277  else
1278  {
1279  // More than 1 segment
1280  wxLogTrace( traceBoardOutline, "Multiple segments in outline" );
1281 
1282  // Just a temporary thing
1283  aOutlines = bbox;
1284  return true;
1285  }
1286 
1287  // Figure out which is the correct outline
1288  SHAPE_POLY_SET poly1;
1289  SHAPE_POLY_SET poly2;
1290 
1291  poly1.NewOutline();
1292  poly1.Append( upper );
1293 
1294  poly2.NewOutline();
1295  poly2.Append( lower );
1296 
1297  if( isCopperOutside( boardMod, poly1 ) )
1298  {
1299  wxLogTrace( traceBoardOutline, "Using lower shape" );
1300  aOutlines = poly2;
1301  }
1302  else
1303  {
1304  wxLogTrace( traceBoardOutline, "Using upper shape" );
1305  aOutlines = poly1;
1306  }
1307 
1308  // Add all closed polys as holes to the main outline
1309  for( SHAPE_LINE_CHAIN& closedChain : closedChains )
1310  {
1311  wxLogTrace( traceBoardOutline, "Adding hole to main outline" );
1312  aOutlines.AddHole( closedChain, -1 );
1313  }
1314 
1315  return true;
1316  }
1317 
1318  // We really shouldn't reach this point
1319  return false;
1320 }
wxPoint GetArcEnd() const
Definition: pcb_shape.cpp:366
int NewHole(int aOutline=-1)
Creates a new hole in a given outline
const EDA_RECT GetBoundingBox() const override
Function GetBoundingBox returns the orthogonal, bounding box of this object for display purposes.
Definition: class_board.h:765
usual segment : line with rounded ends
double GetOrientation() const
Definition: class_module.h:206
int OutlineCount() const
Returns the number of outlines in the set
VECTOR2I projectPointOnSegment(const VECTOR2I &aEndPoint, const SHAPE_POLY_SET &aOutline, int aOutlineNum=0)
bool ConvertOutlineToPolygon(std::vector< PCB_SHAPE * > &aSegList, SHAPE_POLY_SET &aPolygons, wxString *aErrorText, unsigned int aTolerance, wxPoint *aErrorLocation)
Function ConvertOutlineToPolygon build a polygon (with holes) from a PCB_SHAPE list,...
SEGMENT_ITERATOR IterateSegmentsWithHoles()
Returns an iterator object, for all outlines in the set (with holes)
SHAPE_POLY_SET & GetPolyShape()
Definition: pcb_shape.h:243
const wxPoint & GetEnd() const
Function GetEnd returns the ending point of the graphic.
Definition: pcb_shape.h:140
Implementation of conversion functions that require both schematic and board internal units.
const EDA_RECT GetBoardEdgesBoundingBox() const
Returns the board bounding box calculated using exclusively the board edges (graphics on Edge....
Definition: class_board.h:779
int GetRadius() const
Function GetRadius returns the radius of this item Has meaning only for arc and circle.
Definition: pcb_shape.h:185
std::vector< wxPoint > GetRectCorners() const
Definition: pcb_shape.cpp:956
int GetWidth() const
Definition: pcb_shape.h:100
int GetWidth() const
Definition: eda_rect.h:119
PADS & Pads()
Definition: class_module.h:182
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Returns the reference to aHole-th hole in the aIndex-th outline
static wxString ShowShape(PCB_SHAPE_TYPE_T aShape)
Function ShowShape converts the enum PCB_SHAPE_TYPE_T integer value to a wxString.
Bezier Curve.
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:94
OPT_VECTOR2I IntersectLines(const SEG &aSeg) const
Function IntersectLines()
Definition: seg.h:191
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:208
VECTOR2< int > VECTOR2I
Definition: vector2d.h:594
Arcs (with rounded ends)
void buildBoardBoundingBoxPoly(const BOARD *aBoard, SHAPE_POLY_SET &aOutline)
Get the complete bounding box of the board (including all items).
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 ...
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
const wxChar * traceBoardOutline
Flag to enable debug tracing for the board outline creation.
void Append(int aX, int aY, bool aAllowDuplication=false)
Function Append()
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...
This file contains miscellaneous commonly used macros and functions.
wxPoint GetArcStart() const
Definition: pcb_shape.h:163
bool BuildBoardPolygonOutlines(BOARD *aBoard, SHAPE_POLY_SET &aOutlines, wxString *aErrorText, unsigned int aTolerance, wxPoint *aErrorLocation)
int GetCount() const
Function GetCount returns the number of objects in the list.
Definition: collector.h:105
const wxPoint GetEnd() const
Definition: eda_rect.h:116
void SetClosed(bool aClosed)
Function SetClosed()
int findEndSegments(SHAPE_LINE_CHAIN &aChain, SEG &aStartSeg, SEG &aEndSeg)
segment with non rounded ends
#define NULL
bool IsClosed() const override
Function IsClosed()
OPT< VECTOR2I > OPT_VECTOR2I
Definition: seg.h:37
SHAPE_POLY_SET.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Returns the reference to aIndex-th outline in the set
const wxPoint GetOrigin() const
Definition: eda_rect.h:114
wxPoint GetCenter() const override
Function GetCenter()
Definition: pcb_shape.cpp:334
const wxPoint & GetStart() const
Function GetStart returns the starting point of the graphic.
Definition: pcb_shape.h:129
MODULE * GetFirstModule() const
Gets the first module in the list (used in footprint viewer/editor) or NULL if none.
Definition: class_board.h:346
static PCB_SHAPE * findPoint(const wxPoint &aPoint, std::vector< PCB_SHAPE * > &aList, unsigned aLimit)
Searches for a PCB_SHAPE matching a given end point or start point in a list, and if found,...
const std::vector< wxPoint > & GetBezierPoints() const
Definition: pcb_shape.h:229
a few functions useful in geometry calculations.
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.
void BooleanIntersection(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Performs boolean polyset intersection For aFastMode meaning, see function booleanOp
int NewOutline()
Creates a new empty polygon in the set and returns its index
int HoleCount(int aOutline) const
Returns the number of holes in a given outline
CONST_ITERATOR CIterate(int aFirst, int aLast, bool aIterateHoles=false) const
int SegmentCount() const
Function SegmentCount()
virtual const VECTOR2I GetPoint(int aIndex) const override
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:620
int AddHole(const SHAPE_LINE_CHAIN &aHole, int aOutline=-1)
Adds a new hole to the given outline (default: last) and returns its index
int GetHeight() const
Definition: eda_rect.h:120
Definition: seg.h:39
void TransformCircleToPolygon(SHAPE_POLY_SET &aCornerBuffer, wxPoint aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc)
Function TransformCircleToPolygon convert a circle to a polygon, using multiple straight lines.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index
wxPoint GetPosition() const override
Definition: pcb_shape.cpp:67
SEG Segment(int aIndex)
Function Segment()
Information pertinent to a Pcbnew printed circuit board.
Definition: class_board.h:186
polygon (not yet used for tracks, but could be in microwave apps)
#define _(s)
Definition: 3d_actions.cpp:33
SHAPE_LINE_CHAIN.
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
void RemoveAllContours()
Removes all outlines & holes (clears) the polygon set.
VECTOR2I A
Definition: seg.h:47
double GetAngle() const
Definition: pcb_shape.h:108
EDA_RECT handles the component boundary box.
Definition: eda_rect.h:44
void RebuildBezierToSegmentsPointsList(int aMinSegLen)
Rebuild the m_BezierPoints vertex list that approximate the Bezier curve by a list of segments Has me...
Definition: pcb_shape.cpp:308
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).
wxString StringFromValue(EDA_UNITS aUnits, double aValue, bool aAddUnitSymbol, EDA_DATA_TYPE aType)
Function StringFromValue returns the string from aValue according to units (inch, mm ....
Definition: base_units.cpp:244
wxPoint GetPosition() const override
Definition: class_module.h:201
PCB_SHAPE_TYPE_T GetShape() const
Definition: pcb_shape.h:114
CONST_SEGMENT_ITERATOR CIterateSegments(int aFirst, int aLast, bool aIterateHoles=false) const
Returns an iterator object, for iterating between aFirst and aLast outline, with or without holes (de...
bool BuildFootprintPolygonOutlines(BOARD *aBoard, SHAPE_POLY_SET &aOutlines, wxString *aErrorText, unsigned int aTolerance, wxPoint *aErrorLocation)
This function is used to extract a board outline for a footprint view.
Collect all BOARD_ITEM objects of a given set of KICAD_T type(s).
Definition: collectors.h:626
bool isCopperOutside(const MODULE *aMod, SHAPE_POLY_SET &aShape)
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:91
static constexpr int Millimeter2iu(double mm)
int GetArcToSegmentCount(int aRadius, int aErrorMax, double aArcAngleDegree)
MODULE * GetParentModule() const
Function GetParentModule returns a pointer to the parent module, or NULL if PCB_SHAPE does not belong...
Definition: pcb_shape.cpp:448
EDA_RECT & Inflate(wxCoord dx, wxCoord dy)
Function Inflate inflates the rectangle horizontally by dx and vertically by dy.
Definition: eda_rect.cpp:363
bool Contains(const SEG &aSeg) const
Definition: seg.h:299
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)
VECTOR2I B
Definition: seg.h:48