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-2020 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 
26 #include <trigo.h>
27 #include <macros.h>
28 
29 #include <math/vector2d.h>
30 #include <pcb_shape.h>
31 #include <footprint.h>
32 #include <base_units.h>
37 
38 #include <wx/log.h>
39 
40 
48 const wxChar* traceBoardOutline = wxT( "KICAD_BOARD_OUTLINE" );
49 
59 bool close_enough( VECTOR2I aLeft, VECTOR2I aRight, unsigned aLimit )
60 {
61  return ( aLeft - aRight ).SquaredEuclideanNorm() <= SEG::Square( aLimit );
62 }
63 
73 bool closer_to_first( VECTOR2I aRef, VECTOR2I aFirst, VECTOR2I aSecond )
74 {
75  return ( aRef - aFirst ).SquaredEuclideanNorm() < ( aRef - aSecond ).SquaredEuclideanNorm();
76 }
77 
78 
88 static PCB_SHAPE* findNext( PCB_SHAPE* aShape, const wxPoint& aPoint,
89  const std::vector<PCB_SHAPE*>& aList, unsigned aLimit )
90 {
91  // Look for an unused, exact hit
92  for( PCB_SHAPE* graphic : aList )
93  {
94  if( graphic == aShape || ( graphic->GetFlags() & SKIP_STRUCT ) != 0 )
95  continue;
96 
97  switch( graphic->GetShape() )
98  {
100  if( aPoint == graphic->GetArcStart() || aPoint == graphic->GetArcEnd() )
101  return graphic;
102 
103  break;
104 
105  default:
106  if( aPoint == graphic->GetStart() || aPoint == graphic->GetEnd() )
107  return graphic;
108  }
109  }
110 
111  // Search again for anything that's close, even something already used. (The latter is
112  // important for error reporting.)
113  VECTOR2I pt( aPoint );
114  SEG::ecoord closest_dist_sq = SEG::Square( aLimit );
115  PCB_SHAPE* closest_graphic = nullptr;
116  SEG::ecoord d_sq;
117 
118  for( PCB_SHAPE* graphic : aList )
119  {
120  if( graphic == aShape )
121  continue;
122 
123  switch( graphic->GetShape() )
124  {
125  case PCB_SHAPE_TYPE::ARC:
126  d_sq = ( pt - graphic->GetArcStart() ).SquaredEuclideanNorm();
127 
128  if( d_sq < closest_dist_sq )
129  {
130  closest_dist_sq = d_sq;
131  closest_graphic = graphic;
132  }
133 
134  d_sq = ( pt - graphic->GetArcEnd() ).SquaredEuclideanNorm();
135 
136  if( d_sq < closest_dist_sq )
137  {
138  closest_dist_sq = d_sq;
139  closest_graphic = graphic;
140  }
141  break;
142 
143  default:
144  d_sq = ( pt - graphic->GetStart() ).SquaredEuclideanNorm();
145 
146  if( d_sq < closest_dist_sq )
147  {
148  closest_dist_sq = d_sq;
149  closest_graphic = graphic;
150  }
151 
152  d_sq = ( pt - graphic->GetEnd() ).SquaredEuclideanNorm();
153 
154  if( d_sq < closest_dist_sq )
155  {
156  closest_dist_sq = d_sq;
157  closest_graphic = graphic;
158  }
159  }
160  }
161 
162  return closest_graphic; // Note: will be nullptr if nothing within aLimit
163 }
164 
165 
177 bool ConvertOutlineToPolygon( std::vector<PCB_SHAPE*>& aSegList, SHAPE_POLY_SET& aPolygons,
178  int aErrorMax, int aChainingEpsilon,
179  OUTLINE_ERROR_HANDLER* aErrorHandler )
180 {
181  if( aSegList.size() == 0 )
182  return true;
183 
184  bool polygonComplete = false;
185  bool selfIntersecting = false;
186 
187  wxString msg;
188  PCB_SHAPE* graphic = nullptr;
189 
190  std::set<PCB_SHAPE*> startCandidates( aSegList.begin(), aSegList.end() );
191 
192  // Find edge point with minimum x, this should be in the outer polygon
193  // which will define the perimeter polygon polygon.
194  wxPoint xmin = wxPoint( INT_MAX, 0 );
195  int xmini = 0;
196 
197  for( size_t i = 0; i < aSegList.size(); i++ )
198  {
199  graphic = (PCB_SHAPE*) aSegList[i];
200  graphic->ClearFlags( SKIP_STRUCT );
201 
202  switch( graphic->GetShape() )
203  {
206  {
207  if( graphic->GetStart().x < xmin.x )
208  {
209  xmin = graphic->GetStart();
210  xmini = i;
211  }
212 
213  if( graphic->GetEnd().x < xmin.x )
214  {
215  xmin = graphic->GetEnd();
216  xmini = i;
217  }
218  }
219  break;
220 
221  case PCB_SHAPE_TYPE::ARC:
222  {
223  wxPoint pstart = graphic->GetArcStart();
224  wxPoint center = graphic->GetCenter();
225  double angle = -graphic->GetAngle();
226  double radius = graphic->GetRadius();
227  int steps = GetArcToSegmentCount( radius, aErrorMax, angle / 10.0 );
228  wxPoint pt;
229 
230  for( int step = 1; step<=steps; ++step )
231  {
232  double rotation = ( angle * step ) / steps;
233 
234  pt = pstart;
235 
236  RotatePoint( &pt, center, rotation );
237 
238  if( pt.x < xmin.x )
239  {
240  xmin = pt;
241  xmini = i;
242  }
243  }
244  }
245  break;
246 
248  {
249  wxPoint pt = graphic->GetCenter();
250 
251  // pt has minimum x point
252  pt.x -= graphic->GetRadius();
253 
254  // when the radius <= 0, this is a mal-formed circle. Skip it
255  if( graphic->GetRadius() > 0 && pt.x < xmin.x )
256  {
257  xmin = pt;
258  xmini = i;
259  }
260  }
261  break;
262 
264  {
265  graphic->RebuildBezierToSegmentsPointsList( graphic->GetWidth() );
266 
267  for( const wxPoint& pt : graphic->GetBezierPoints() )
268  {
269  if( pt.x < xmin.x )
270  {
271  xmin = pt;
272  xmini = i;
273  }
274  }
275  }
276  break;
277 
279  {
280  const SHAPE_POLY_SET poly = graphic->GetPolyShape();
281  double orientation = 0.0;
282  VECTOR2I offset = VECTOR2I( 0, 0 );
283 
284  if( graphic->GetParentFootprint() )
285  {
286  orientation = graphic->GetParentFootprint()->GetOrientation();
287  offset = graphic->GetParentFootprint()->GetPosition();
288  }
289 
290  for( auto iter = poly.CIterate(); iter; iter++ )
291  {
292  VECTOR2I pt = *iter;
293  RotatePoint( pt, orientation );
294  pt += offset;
295 
296  if( pt.x < xmin.x )
297  {
298  xmin.x = pt.x;
299  xmin.y = pt.y;
300  xmini = i;
301  }
302  }
303  }
304  break;
305 
306  default:
307  break;
308  }
309  }
310 
311  // Keep a list of where the various segments came from so after doing our combined-polygon
312  // tests we can still report errors against the individual graphic items.
313  std::map<std::pair<VECTOR2I, VECTOR2I>, PCB_SHAPE*> segOwners;
314 
315  auto fetchOwner =
316  [&]( const SEG& seg ) -> PCB_SHAPE*
317  {
318  auto it = segOwners.find( std::make_pair( seg.A, seg.B ) );
319  return it == segOwners.end() ? nullptr : it->second;
320  };
321 
322  // Grab the left most point, assume its on the board's perimeter, and see if we can put
323  // enough graphics together by matching endpoints to formulate a cohesive polygon.
324 
325  PCB_SHAPE* prevGraphic = nullptr;
326  wxPoint prevPt;
327 
328  graphic = (PCB_SHAPE*) aSegList[xmini];
329  graphic->SetFlags( SKIP_STRUCT );
330  startCandidates.erase( graphic );
331 
332  // Output the outline perimeter as polygon.
333  if( graphic->GetShape() == PCB_SHAPE_TYPE::CIRCLE )
334  {
335  TransformCircleToPolygon( aPolygons, graphic->GetCenter(), graphic->GetRadius(),
336  ARC_LOW_DEF, ERROR_INSIDE );
337  polygonComplete = true;
338  }
339  else if( graphic->GetShape() == PCB_SHAPE_TYPE::RECT )
340  {
341  std::vector<wxPoint> pts = graphic->GetRectCorners();
342 
343  aPolygons.NewOutline();
344 
345  for( const wxPoint& pt : pts )
346  aPolygons.Append( pt );
347 
348  segOwners[ std::make_pair( pts[0], pts[1] ) ] = graphic;
349  segOwners[ std::make_pair( pts[1], pts[2] ) ] = graphic;
350  segOwners[ std::make_pair( pts[2], pts[3] ) ] = graphic;
351  segOwners[ std::make_pair( pts[3], pts[0] ) ] = graphic;
352 
353  polygonComplete = true;
354  }
355  else if( graphic->GetShape() == PCB_SHAPE_TYPE::POLYGON )
356  {
357  double orientation = 0.0;
358  VECTOR2I offset = VECTOR2I( 0, 0 );
359 
360  if( graphic->GetParentFootprint() )
361  {
362  orientation = graphic->GetParentFootprint()->GetOrientation();
363  offset = graphic->GetParentFootprint()->GetPosition();
364  }
365 
366  aPolygons.NewOutline();
367  bool first = true;
368 
369  for( auto it = graphic->GetPolyShape().CIterate( 0 ); it; it++ )
370  {
371  VECTOR2I pt = *it;
372  RotatePoint( pt, orientation );
373  pt += offset;
374  aPolygons.Append( pt );
375 
376  if( first )
377  first = false;
378  else
379  segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
380 
381  prevPt = (wxPoint) pt;
382  }
383 
384  polygonComplete = true;
385  }
386  else
387  {
388  // Polygon start point. Arbitrarily chosen end of the
389  // segment and build the poly from here.
390 
391  wxPoint startPt = graphic->GetShape() == PCB_SHAPE_TYPE::ARC ? graphic->GetArcEnd()
392  : graphic->GetEnd();
393 
394  prevPt = startPt;
395  aPolygons.NewOutline();
396  aPolygons.Append( prevPt );
397 
398  // Do not append the other end point yet of this 'graphic', this first
399  // 'graphic' might be an arc or a curve.
400 
401  for(;;)
402  {
403  switch( graphic->GetShape() )
404  {
407  {
408  // As a non-first item, closed shapes can't be anything but self-intersecting
409 
410  if( aErrorHandler )
411  {
412  wxASSERT( prevGraphic );
413  (*aErrorHandler)( _( "(self-intersecting)" ), prevGraphic, graphic, prevPt );
414  }
415 
416  selfIntersecting = true;
417 
418  // A closed shape will finish where it started, so no point in updating prevPt
419  }
420  break;
421 
423  {
424  wxPoint nextPt;
425 
426  // Use the line segment end point furthest away from prevPt as we assume
427  // the other end to be ON prevPt or very close to it.
428 
429  if( closer_to_first( prevPt, graphic->GetStart(), graphic->GetEnd()) )
430  nextPt = graphic->GetEnd();
431  else
432  nextPt = graphic->GetStart();
433 
434  aPolygons.Append( nextPt );
435  segOwners[ std::make_pair( prevPt, nextPt ) ] = graphic;
436  prevPt = nextPt;
437  }
438  break;
439 
440  case PCB_SHAPE_TYPE::ARC:
441  {
442  // We do not support arcs in polygons, so approximate an arc with a series of
443  // short lines and put those line segments into the !same! PATH.
444 
445  wxPoint pstart = graphic->GetArcStart();
446  wxPoint pend = graphic->GetArcEnd();
447  wxPoint pcenter = graphic->GetCenter();
448  double angle = -graphic->GetAngle();
449  double radius = graphic->GetRadius();
450  int steps = GetArcToSegmentCount( radius, aErrorMax, angle / 10.0 );
451 
452  if( !close_enough( prevPt, pstart, aChainingEpsilon ) )
453  {
454  wxASSERT( close_enough( prevPt, graphic->GetArcEnd(), aChainingEpsilon ) );
455 
456  angle = -angle;
457  std::swap( pstart, pend );
458  }
459 
460  // Create intermediate points between start and end:
461  for( int step = 1; step < steps; ++step )
462  {
463  double rotation = ( angle * step ) / steps;
464  wxPoint pt = pstart;
465  RotatePoint( &pt, pcenter, rotation );
466 
467  aPolygons.Append( pt );
468  segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
469  prevPt = pt;
470  }
471 
472  // Append the last arc end point
473  aPolygons.Append( pend );
474  segOwners[ std::make_pair( prevPt, pend ) ] = graphic;
475  prevPt = pend;
476  }
477  break;
478 
480  {
481  // We do not support Bezier curves in polygons, so approximate with a series
482  // of short lines and put those line segments into the !same! PATH.
483 
484  wxPoint nextPt;
485  bool first = true;
486  bool reverse = false;
487 
488  // Use the end point furthest away from
489  // prevPt as we assume the other end to be ON prevPt or
490  // very close to it.
491 
492  if( closer_to_first( prevPt, graphic->GetStart(), graphic->GetEnd()) )
493  {
494  nextPt = graphic->GetEnd();
495  }
496  else
497  {
498  nextPt = graphic->GetStart();
499  reverse = true;
500  }
501 
502  if( reverse )
503  {
504  for( int jj = graphic->GetBezierPoints().size()-1; jj >= 0; jj-- )
505  {
506  const wxPoint& pt = graphic->GetBezierPoints()[jj];
507  aPolygons.Append( pt );
508 
509  if( first )
510  first = false;
511  else
512  segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
513 
514  prevPt = pt;
515  }
516  }
517  else
518  {
519  for( const wxPoint& pt : graphic->GetBezierPoints() )
520  {
521  aPolygons.Append( pt );
522 
523  if( first )
524  first = false;
525  else
526  segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
527 
528  prevPt = pt;
529  }
530  }
531 
532  prevPt = nextPt;
533  }
534  break;
535 
536  default:
537  wxFAIL_MSG( "Unsupported PCB_SHAPE type "
538  + BOARD_ITEM::ShowShape( graphic->GetShape() ) );
539  return false;
540  }
541 
542  // Get next closest segment.
543 
544  PCB_SHAPE* nextGraphic = findNext( graphic, prevPt, aSegList, aChainingEpsilon );
545 
546  if( nextGraphic && !( nextGraphic->GetFlags() & SKIP_STRUCT ) )
547  {
548  prevGraphic = graphic;
549  graphic = nextGraphic;
550  graphic->SetFlags( SKIP_STRUCT );
551  startCandidates.erase( graphic );
552  continue;
553  }
554 
555  // Finished, or ran into trouble...
556 
557  if( close_enough( startPt, prevPt, aChainingEpsilon ) )
558  {
559  polygonComplete = true;
560  break;
561  }
562  else if( nextGraphic ) // encountered already-used segment, but not at the start
563  {
564  if( aErrorHandler )
565  (*aErrorHandler)( _( "(self-intersecting)" ), graphic, nextGraphic, prevPt );
566 
567  polygonComplete = false;
568  break;
569  }
570  else // encountered discontinuity
571  {
572  if( aErrorHandler )
573  (*aErrorHandler)( _( "(not a closed shape)" ), graphic, nullptr, prevPt );
574 
575  polygonComplete = false;
576  break;
577  }
578  }
579  }
580 
581  int holeNum = -1;
582 
583  while( startCandidates.size() )
584  {
585  int hole = aPolygons.NewHole();
586  bool firstPt = true;
587  holeNum++;
588 
589  graphic = (PCB_SHAPE*) *startCandidates.begin();
590  graphic->SetFlags( SKIP_STRUCT );
591  startCandidates.erase( startCandidates.begin() );
592 
593  // Both circles and polygons on the edge cuts layer are closed items that
594  // do not connect to other elements, so we process them independently
595  if( graphic->GetShape() == PCB_SHAPE_TYPE::POLYGON )
596  {
597  double orientation = 0.0;
598  VECTOR2I offset = VECTOR2I( 0, 0 );
599 
600  if( graphic->GetParentFootprint() )
601  {
602  orientation = graphic->GetParentFootprint()->GetOrientation();
603  offset = graphic->GetParentFootprint()->GetPosition();
604  }
605 
606  for( auto it = graphic->GetPolyShape().CIterate(); it; it++ )
607  {
608  VECTOR2I pt = *it;
609  RotatePoint( pt, orientation );
610  pt += offset;
611 
612  aPolygons.Append( pt, -1, hole );
613 
614  if( firstPt )
615  firstPt = false;
616  else
617  segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
618 
619  prevPt = (wxPoint) pt;
620  }
621  }
622  else if( graphic->GetShape() == PCB_SHAPE_TYPE::CIRCLE )
623  {
624  // make a circle by segments;
625  wxPoint center = graphic->GetCenter();
626  double angle = 3600.0;
627  wxPoint start = center;
628  int radius = graphic->GetRadius();
629  int steps = GetArcToSegmentCount( radius, aErrorMax, 360.0 );
630  wxPoint nextPt;
631 
632  start.x += radius;
633 
634  for( int step = 0; step < steps; ++step )
635  {
636  double rotation = ( angle * step ) / steps;
637  nextPt = start;
638  RotatePoint( &nextPt.x, &nextPt.y, center.x, center.y, rotation );
639  aPolygons.Append( nextPt, -1, hole );
640 
641  if( firstPt )
642  firstPt = false;
643  else
644  segOwners[ std::make_pair( prevPt, nextPt ) ] = graphic;
645 
646  prevPt = nextPt;
647  }
648  }
649  else if( graphic->GetShape() == PCB_SHAPE_TYPE::RECT )
650  {
651  std::vector<wxPoint> pts = graphic->GetRectCorners();
652 
653  for( const wxPoint& pt : pts )
654  {
655  aPolygons.Append( pt, -1, hole );
656 
657  if( firstPt )
658  firstPt = false;
659  else
660  segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
661 
662  prevPt = (wxPoint) pt;
663  }
664  }
665  else
666  {
667  // Polygon start point. Arbitrarily chosen end of the segment and build the poly
668  // from here.
669 
670  wxPoint startPt( graphic->GetEnd() );
671  prevPt = graphic->GetEnd();
672  aPolygons.Append( prevPt, -1, hole );
673 
674  // do not append the other end point yet, this first 'graphic' might be an arc
675  for(;;)
676  {
677  switch( graphic->GetShape() )
678  {
680  {
681  wxPoint nextPt;
682 
683  // Use the line segment end point furthest away from
684  // prevPt as we assume the other end to be ON prevPt or
685  // very close to it.
686 
687  if( closer_to_first( prevPt, graphic->GetStart(), graphic->GetEnd()) )
688  nextPt = graphic->GetEnd();
689  else
690  nextPt = graphic->GetStart();
691 
692  aPolygons.Append( nextPt, -1, hole );
693  segOwners[ std::make_pair( prevPt, nextPt ) ] = graphic;
694  prevPt = nextPt;
695  }
696  break;
697 
698  case PCB_SHAPE_TYPE::ARC:
699  // We do not support arcs in polygons, so approximate an arc with a series of
700  // short lines and put those line segments into the !same! PATH.
701  {
702  wxPoint pstart = graphic->GetArcStart();
703  wxPoint pend = graphic->GetArcEnd();
704  wxPoint pcenter = graphic->GetCenter();
705  double angle = -graphic->GetAngle();
706  int radius = graphic->GetRadius();
707  int steps = GetArcToSegmentCount( radius, aErrorMax, angle / 10.0 );
708 
709  if( !close_enough( prevPt, pstart, aChainingEpsilon ) )
710  {
711  wxASSERT( close_enough( prevPt, graphic->GetArcEnd(),
712  aChainingEpsilon ) );
713 
714  angle = -angle;
715  std::swap( pstart, pend );
716  }
717 
718  // Create intermediate points between start and end:
719  for( int step = 1; step < steps; ++step )
720  {
721  double rotation = ( angle * step ) / steps;
722  wxPoint pt = pstart;
723  RotatePoint( &pt, pcenter, rotation );
724 
725  aPolygons.Append( pt, -1, hole );
726  segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
727  prevPt = pt;
728  }
729 
730  // Append the last arc end point
731  aPolygons.Append( pend, -1, hole );
732  segOwners[ std::make_pair( prevPt, pend ) ] = graphic;
733  prevPt = pend;
734  }
735  break;
736 
738  // We do not support Bezier curves in polygons, so approximate with a series
739  // of short lines and put those line segments into the !same! PATH.
740  {
741  wxPoint nextPt;
742  bool reverse = false;
743 
744  // Use the end point furthest away from
745  // prevPt as we assume the other end to be ON prevPt or
746  // very close to it.
747 
748  if( closer_to_first( prevPt, graphic->GetStart(), graphic->GetEnd()) )
749  {
750  nextPt = graphic->GetEnd();
751  }
752  else
753  {
754  nextPt = graphic->GetStart();
755  reverse = true;
756  }
757 
758  if( reverse )
759  {
760  for( int jj = graphic->GetBezierPoints().size()-1; jj >= 0; jj-- )
761  {
762  const wxPoint& pt = graphic->GetBezierPoints()[jj];
763  aPolygons.Append( pt, -1, hole );
764  segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
765  prevPt = pt;
766  }
767  }
768  else
769  {
770  for( const wxPoint& pt : graphic->GetBezierPoints() )
771  {
772  aPolygons.Append( pt, -1, hole );
773  segOwners[ std::make_pair( prevPt, pt ) ] = graphic;
774  prevPt = pt;
775  }
776  }
777 
778  prevPt = nextPt;
779  }
780  break;
781 
782  default:
783  wxFAIL_MSG( "Unsupported PCB_SHAPE type "
784  + BOARD_ITEM::ShowShape( graphic->GetShape() ) );
785 
786  return false;
787  }
788 
789  // Get next closest segment.
790 
791  PCB_SHAPE* nextGraphic = findNext( graphic, prevPt, aSegList, aChainingEpsilon );
792 
793  if( nextGraphic && !( nextGraphic->GetFlags() & SKIP_STRUCT ) )
794  {
795  graphic = nextGraphic;
796  graphic->SetFlags( SKIP_STRUCT );
797  startCandidates.erase( graphic );
798  continue;
799  }
800 
801  // Finished, or ran into trouble...
802 
803  if( close_enough( startPt, prevPt, aChainingEpsilon ) )
804  {
805  break;
806  }
807  else if( nextGraphic ) // encountered already-used segment, but not at the start
808  {
809  if( aErrorHandler )
810  (*aErrorHandler)( _( "(self-intersecting)" ), graphic, nextGraphic, prevPt );
811 
812  polygonComplete = false;
813  break;
814  }
815  else // encountered discontinuity
816  {
817  if( aErrorHandler )
818  (*aErrorHandler)( _( "(not a closed shape)" ), graphic, nullptr, prevPt );
819 
820  polygonComplete = false;
821  break;
822  }
823  }
824  }
825  }
826 
827  if( !polygonComplete )
828  return false;
829 
830  // All of the silliness that follows is to work around the segment iterator while checking
831  // for collisions.
832  // TODO: Implement proper segment and point iterators that follow std
833 
834  for( auto seg1 = aPolygons.IterateSegmentsWithHoles(); seg1; seg1++ )
835  {
836  auto seg2 = seg1;
837 
838  for( ++seg2; seg2; seg2++ )
839  {
840  // Check for exact overlapping segments.
841  if( *seg1 == *seg2 || ( ( *seg1 ).A == ( *seg2 ).B && ( *seg1 ).B == ( *seg2 ).A ) )
842  {
843  if( aErrorHandler )
844  {
845  BOARD_ITEM* a = fetchOwner( *seg1 );
846  BOARD_ITEM* b = fetchOwner( *seg2 );
847 
848  if( a && b )
849  (*aErrorHandler)( _( "(self-intersecting)" ), a, b, (wxPoint) ( *seg1 ).A );
850  }
851 
852  selfIntersecting = true;
853  }
854 
855  if( boost::optional<VECTOR2I> pt = seg1.Get().Intersect( seg2.Get(), true ) )
856  {
857  if( aErrorHandler )
858  {
859  BOARD_ITEM* a = fetchOwner( *seg1 );
860  BOARD_ITEM* b = fetchOwner( *seg2 );
861 
862  if( a && b )
863  (*aErrorHandler)( _( "(self-intersecting)" ), a, b, (wxPoint) pt.get() );
864  }
865 
866  selfIntersecting = true;
867  }
868  }
869  }
870 
871  return !selfIntersecting;
872 }
873 
874 
875 #include <board.h>
876 #include <collectors.h>
877 
878 /* This function is used to extract a board outlines (3D view, automatic zones build ...)
879  * Any closed outline inside the main outline is a hole
880  * All contours should be closed, i.e. valid closed polygon vertices
881  */
882 bool BuildBoardPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, int aErrorMax,
883  int aChainingEpsilon, OUTLINE_ERROR_HANDLER* aErrorHandler )
884 {
885  PCB_TYPE_COLLECTOR items;
886  bool success = false;
887 
888  // Get all the PCB and FP shapes into 'items', then keep only those on layer == Edge_Cuts.
889  static const KICAD_T scan_graphics[] = { PCB_SHAPE_T, PCB_FP_SHAPE_T, EOT };
890  items.Collect( aBoard, scan_graphics );
891 
892  // Make a working copy of aSegList, because the list is modified during calculations
893  std::vector<PCB_SHAPE*> segList;
894 
895  for( int ii = 0; ii < items.GetCount(); ii++ )
896  {
897  if( items[ii]->GetLayer() == Edge_Cuts )
898  segList.push_back( static_cast<PCB_SHAPE*>( items[ii] ) );
899  }
900 
901  if( segList.size() )
902  {
903  success = ConvertOutlineToPolygon( segList, aOutlines, aErrorMax, aChainingEpsilon,
904  aErrorHandler );
905  }
906 
907  if( !success || !aOutlines.OutlineCount() )
908  {
909  // Couldn't create a valid polygon outline. Use the board edge cuts bounding box to
910  // create a rectangular outline, or, failing that, the bounding box of the items on
911  // the board.
912 
913  EDA_RECT bbbox = aBoard->GetBoardEdgesBoundingBox();
914 
915  // If null area, uses the global bounding box.
916  if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
917  bbbox = aBoard->ComputeBoundingBox();
918 
919  // Ensure non null area. If happen, gives a minimal size.
920  if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
921  bbbox.Inflate( Millimeter2iu( 1.0 ) );
922 
923  aOutlines.RemoveAllContours();
924  aOutlines.NewOutline();
925 
926  wxPoint corner;
927  aOutlines.Append( bbbox.GetOrigin() );
928 
929  corner.x = bbbox.GetOrigin().x;
930  corner.y = bbbox.GetEnd().y;
931  aOutlines.Append( corner );
932 
933  aOutlines.Append( bbbox.GetEnd() );
934 
935  corner.x = bbbox.GetEnd().x;
936  corner.y = bbbox.GetOrigin().y;
937  aOutlines.Append( corner );
938  }
939 
940  return success;
941 }
942 
943 
956 void buildBoardBoundingBoxPoly( const BOARD* aBoard, SHAPE_POLY_SET& aOutline )
957 {
958  EDA_RECT bbbox = aBoard->GetBoundingBox();
959  SHAPE_LINE_CHAIN chain;
960 
961  // If null area, uses the global bounding box.
962  if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
963  bbbox = aBoard->ComputeBoundingBox();
964 
965  // Ensure non null area. If happen, gives a minimal size.
966  if( ( bbbox.GetWidth() ) == 0 || ( bbbox.GetHeight() == 0 ) )
967  bbbox.Inflate( Millimeter2iu( 1.0 ) );
968 
969  // Inflate slightly (by 1/10th the size of the box)
970  bbbox.Inflate( bbbox.GetWidth() / 10, bbbox.GetHeight() / 10 );
971 
972  chain.Append( bbbox.GetOrigin() );
973  chain.Append( bbbox.GetOrigin().x, bbbox.GetEnd().y );
974  chain.Append( bbbox.GetEnd() );
975  chain.Append( bbbox.GetEnd().x, bbbox.GetOrigin().y );
976  chain.SetClosed( true );
977 
978  aOutline.RemoveAllContours();
979  aOutline.AddOutline( chain );
980 }
981 
982 
983 bool isCopperOutside( const FOOTPRINT* aMod, SHAPE_POLY_SET& aShape )
984 {
985  bool padOutside = false;
986 
987  for( PAD* pad : aMod->Pads() )
988  {
989  SHAPE_POLY_SET poly = aShape;
990 
991  poly.BooleanIntersection( *pad->GetEffectivePolygon(), SHAPE_POLY_SET::PM_FAST );
992 
993  if( poly.OutlineCount() == 0 )
994  {
995  wxPoint padPos = pad->GetPosition();
996  wxLogTrace( traceBoardOutline, "Tested pad (%d, %d): outside", padPos.x, padPos.y );
997  padOutside = true;
998  break;
999  }
1000 
1001  wxPoint padPos = pad->GetPosition();
1002  wxLogTrace( traceBoardOutline, "Tested pad (%d, %d): not outside", padPos.x, padPos.y );
1003  }
1004 
1005  return padOutside;
1006 }
1007 
1008 
1009 VECTOR2I projectPointOnSegment( const VECTOR2I& aEndPoint, const SHAPE_POLY_SET& aOutline,
1010  int aOutlineNum = 0 )
1011 {
1012  int minDistance = -1;
1013  VECTOR2I projPoint;
1014 
1015  for( auto it = aOutline.CIterateSegments( aOutlineNum ); it; it++ )
1016  {
1017  auto seg = it.Get();
1018  int dis = seg.Distance( aEndPoint );
1019 
1020  if( minDistance < 0 || ( dis < minDistance ) )
1021  {
1022  minDistance = dis;
1023  projPoint = seg.NearestPoint( aEndPoint );
1024  }
1025  }
1026 
1027  return projPoint;
1028 }
1029 
1030 
1031 int findEndSegments( SHAPE_LINE_CHAIN& aChain, SEG& aStartSeg, SEG& aEndSeg )
1032 {
1033  int foundSegs = 0;
1034 
1035  for( int i = 0; i < aChain.SegmentCount(); i++ )
1036  {
1037  SEG seg = aChain.Segment( i );
1038 
1039  bool foundA = false;
1040  bool foundB = false;
1041 
1042  for( int j = 0; j < aChain.SegmentCount(); j++ )
1043  {
1044  // Don't test the segment against itself
1045  if( i == j )
1046  continue;
1047 
1048  SEG testSeg = aChain.Segment( j );
1049 
1050  if( testSeg.Contains( seg.A ) )
1051  foundA = true;
1052 
1053  if( testSeg.Contains( seg.B ) )
1054  foundB = true;
1055  }
1056 
1057  // This segment isn't a start or end
1058  if( foundA && foundB )
1059  continue;
1060 
1061  if( foundSegs == 0 )
1062  {
1063  // The first segment we encounter is the "start" segment
1064  wxLogTrace( traceBoardOutline, "Found start segment: (%d, %d)-(%d, %d)",
1065  seg.A.x, seg.A.y, seg.B.x, seg.B.y );
1066  aStartSeg = seg;
1067  foundSegs++;
1068  }
1069  else
1070  {
1071  // Once we find both start and end, we can stop
1072  wxLogTrace( traceBoardOutline, "Found end segment: (%d, %d)-(%d, %d)",
1073  seg.A.x, seg.A.y, seg.B.x, seg.B.y );
1074  aEndSeg = seg;
1075  foundSegs++;
1076  break;
1077  }
1078  }
1079 
1080  return foundSegs;
1081 }
1082 
1083 
1095 bool BuildFootprintPolygonOutlines( BOARD* aBoard, SHAPE_POLY_SET& aOutlines, int aErrorMax,
1096  int aChainingEpsilon, OUTLINE_ERROR_HANDLER* aErrorHandler )
1097 
1098 {
1099  FOOTPRINT* footprint = aBoard->GetFirstFootprint();
1100 
1101  // No footprint loaded
1102  if( !footprint )
1103  {
1104  wxLogTrace( traceBoardOutline, "No footprint found on board" );
1105  return false;
1106  }
1107 
1108  PCB_TYPE_COLLECTOR items;
1109  SHAPE_POLY_SET outlines;
1110  bool success = false;
1111 
1112  // Get all the SHAPEs into 'items', then keep only those on layer == Edge_Cuts.
1113  static const KICAD_T scan_graphics[] = { PCB_SHAPE_T, PCB_FP_SHAPE_T, EOT };
1114  items.Collect( aBoard, scan_graphics );
1115 
1116  // Make a working copy of aSegList, because the list is modified during calculations
1117  std::vector<PCB_SHAPE*> segList;
1118 
1119  for( int ii = 0; ii < items.GetCount(); ii++ )
1120  {
1121  if( items[ii]->GetLayer() == Edge_Cuts )
1122  segList.push_back( static_cast<PCB_SHAPE*>( items[ii] ) );
1123  }
1124 
1125  if( !segList.empty() )
1126  {
1127  success = ConvertOutlineToPolygon( segList, outlines, aErrorMax, aChainingEpsilon,
1128  aErrorHandler );
1129  }
1130 
1131  // A closed outline was found on Edge_Cuts
1132  if( success )
1133  {
1134  wxLogTrace( traceBoardOutline, "Closed outline found" );
1135 
1136  // If copper is outside a closed polygon, treat it as a hole
1137  if( isCopperOutside( footprint, outlines ) )
1138  {
1139  wxLogTrace( traceBoardOutline, "Treating outline as a hole" );
1140 
1141  buildBoardBoundingBoxPoly( aBoard, aOutlines );
1142 
1143  // Copy all outlines from the conversion as holes into the new outline
1144  for( int i = 0; i < outlines.OutlineCount(); i++ )
1145  {
1146  SHAPE_LINE_CHAIN& out = outlines.Outline( i );
1147 
1148  if( out.IsClosed() )
1149  aOutlines.AddHole( out, -1 );
1150 
1151  for( int j = 0; j < outlines.HoleCount( i ); j++ )
1152  {
1153  SHAPE_LINE_CHAIN& hole = outlines.Hole( i, j );
1154 
1155  if( hole.IsClosed() )
1156  aOutlines.AddHole( hole, -1 );
1157  }
1158  }
1159  }
1160  // If all copper is inside, then the computed outline is the board outline
1161  else
1162  {
1163  wxLogTrace( traceBoardOutline, "Treating outline as board edge" );
1164  aOutlines = outlines;
1165  }
1166 
1167  return true;
1168  }
1169  // No board outlines were found, so use the bounding box
1170  else if( outlines.OutlineCount() == 0 )
1171  {
1172  wxLogTrace( traceBoardOutline, "Using footprint bounding box" );
1173  buildBoardBoundingBoxPoly( aBoard, aOutlines );
1174 
1175  return true;
1176  }
1177  // There is an outline present, but it is not closed
1178  else
1179  {
1180  wxLogTrace( traceBoardOutline, "Trying to build outline" );
1181 
1182  std::vector<SHAPE_LINE_CHAIN> closedChains;
1183  std::vector<SHAPE_LINE_CHAIN> openChains;
1184 
1185  // The ConvertOutlineToPolygon function returns only one main outline and the rest as
1186  // holes, so we promote the holes and process them
1187  openChains.push_back( outlines.Outline( 0 ) );
1188 
1189  for( int j = 0; j < outlines.HoleCount( 0 ); j++ )
1190  {
1191  SHAPE_LINE_CHAIN hole = outlines.Hole( 0, j );
1192 
1193  if( hole.IsClosed() )
1194  {
1195  wxLogTrace( traceBoardOutline, "Found closed hole" );
1196  closedChains.push_back( hole );
1197  }
1198  else
1199  {
1200  wxLogTrace( traceBoardOutline, "Found open hole" );
1201  openChains.push_back( hole );
1202  }
1203  }
1204 
1205  SHAPE_POLY_SET bbox;
1206  buildBoardBoundingBoxPoly( aBoard, bbox );
1207 
1208  // Treat the open polys as the board edge
1209  SHAPE_LINE_CHAIN chain = openChains[0];
1210  SHAPE_LINE_CHAIN rect = bbox.Outline( 0 );
1211 
1212  // We know the outline chain is open, so set to non-closed to get better segment count
1213  chain.SetClosed( false );
1214 
1215  SEG startSeg;
1216  SEG endSeg;
1217 
1218  // The two possible board outlines
1219  SHAPE_LINE_CHAIN upper;
1220  SHAPE_LINE_CHAIN lower;
1221 
1222  findEndSegments( chain, startSeg, endSeg );
1223 
1224  if( chain.SegmentCount() == 0 )
1225  {
1226  // Something is wrong, bail out with the overall footprint bounding box
1227  wxLogTrace( traceBoardOutline, "No line segments in provided outline" );
1228  aOutlines = bbox;
1229  return true;
1230  }
1231  else if( chain.SegmentCount() == 1 )
1232  {
1233  // This case means there is only 1 line segment making up the edge cuts of the
1234  // footprint, so we just need to use it to cut the bounding box in half.
1235  wxLogTrace( traceBoardOutline, "Only 1 line segment in provided outline" );
1236 
1237  startSeg = chain.Segment( 0 );
1238 
1239  // Intersect with all the sides of the rectangle
1240  OPT_VECTOR2I inter0 = startSeg.IntersectLines( rect.Segment( 0 ) );
1241  OPT_VECTOR2I inter1 = startSeg.IntersectLines( rect.Segment( 1 ) );
1242  OPT_VECTOR2I inter2 = startSeg.IntersectLines( rect.Segment( 2 ) );
1243  OPT_VECTOR2I inter3 = startSeg.IntersectLines( rect.Segment( 3 ) );
1244 
1245  if( inter0 && inter2 && !inter1 && !inter3 )
1246  {
1247  // Intersects the vertical rectangle sides only
1248  wxLogTrace( traceBoardOutline, "Segment intersects only vertical bbox sides" );
1249 
1250  // The upper half
1251  upper.Append( *inter0 );
1252  upper.Append( rect.GetPoint( 1 ) );
1253  upper.Append( rect.GetPoint( 2 ) );
1254  upper.Append( *inter2 );
1255  upper.SetClosed( true );
1256 
1257  // The lower half
1258  lower.Append( *inter0 );
1259  lower.Append( rect.GetPoint( 0 ) );
1260  lower.Append( rect.GetPoint( 3 ) );
1261  lower.Append( *inter2 );
1262  lower.SetClosed( true );
1263  }
1264  else if( inter1 && inter3 && !inter0 && !inter2 )
1265  {
1266  // Intersects the horizontal rectangle sides only
1267  wxLogTrace( traceBoardOutline, "Segment intersects only horizontal bbox sides" );
1268 
1269  // The left half
1270  upper.Append( *inter1 );
1271  upper.Append( rect.GetPoint( 1 ) );
1272  upper.Append( rect.GetPoint( 0 ) );
1273  upper.Append( *inter3 );
1274  upper.SetClosed( true );
1275 
1276  // The right half
1277  lower.Append( *inter1 );
1278  lower.Append( rect.GetPoint( 2 ) );
1279  lower.Append( rect.GetPoint( 3 ) );
1280  lower.Append( *inter3 );
1281  lower.SetClosed( true );
1282  }
1283  else
1284  {
1285  // Angled line segment that cuts across a corner
1286  wxLogTrace( traceBoardOutline, "Segment intersects two perpendicular bbox sides" );
1287 
1288  // Figure out which actual lines are intersected, since IntersectLines assumes
1289  // an infinite line
1290  bool hit0 = rect.Segment( 0 ).Contains( *inter0 );
1291  bool hit1 = rect.Segment( 1 ).Contains( *inter1 );
1292  bool hit2 = rect.Segment( 2 ).Contains( *inter2 );
1293  bool hit3 = rect.Segment( 3 ).Contains( *inter3 );
1294 
1295  if( hit0 && hit1 )
1296  {
1297  // Cut across the upper left corner
1298  wxLogTrace( traceBoardOutline, "Segment cuts upper left corner" );
1299 
1300  // The upper half
1301  upper.Append( *inter0 );
1302  upper.Append( rect.GetPoint( 1 ) );
1303  upper.Append( *inter1 );
1304  upper.SetClosed( true );
1305 
1306  // The lower half
1307  lower.Append( *inter0 );
1308  lower.Append( rect.GetPoint( 0 ) );
1309  lower.Append( rect.GetPoint( 3 ) );
1310  lower.Append( rect.GetPoint( 2 ) );
1311  lower.Append( *inter1 );
1312  lower.SetClosed( true );
1313  }
1314  else if( hit1 && hit2 )
1315  {
1316  // Cut across the upper right corner
1317  wxLogTrace( traceBoardOutline, "Segment cuts upper right corner" );
1318 
1319  // The upper half
1320  upper.Append( *inter1 );
1321  upper.Append( rect.GetPoint( 2 ) );
1322  upper.Append( *inter2 );
1323  upper.SetClosed( true );
1324 
1325  // The lower half
1326  lower.Append( *inter1 );
1327  lower.Append( rect.GetPoint( 1 ) );
1328  lower.Append( rect.GetPoint( 0 ) );
1329  lower.Append( rect.GetPoint( 3 ) );
1330  lower.Append( *inter2 );
1331  lower.SetClosed( true );
1332  }
1333  else if( hit2 && hit3 )
1334  {
1335  // Cut across the lower right corner
1336  wxLogTrace( traceBoardOutline, "Segment cuts lower right corner" );
1337 
1338  // The upper half
1339  upper.Append( *inter2 );
1340  upper.Append( rect.GetPoint( 2 ) );
1341  upper.Append( rect.GetPoint( 1 ) );
1342  upper.Append( rect.GetPoint( 0 ) );
1343  upper.Append( *inter3 );
1344  upper.SetClosed( true );
1345 
1346  // The bottom half
1347  lower.Append( *inter2 );
1348  lower.Append( rect.GetPoint( 3 ) );
1349  lower.Append( *inter3 );
1350  lower.SetClosed( true );
1351  }
1352  else
1353  {
1354  // Cut across the lower left corner
1355  wxLogTrace( traceBoardOutline, "Segment cuts upper left corner" );
1356 
1357  // The upper half
1358  upper.Append( *inter0 );
1359  upper.Append( rect.GetPoint( 1 ) );
1360  upper.Append( rect.GetPoint( 2 ) );
1361  upper.Append( rect.GetPoint( 3 ) );
1362  upper.Append( *inter3 );
1363  upper.SetClosed( true );
1364 
1365  // The bottom half
1366  lower.Append( *inter0 );
1367  lower.Append( rect.GetPoint( 0 ) );
1368  lower.Append( *inter3 );
1369  lower.SetClosed( true );
1370  }
1371  }
1372  }
1373  else
1374  {
1375  // More than 1 segment
1376  wxLogTrace( traceBoardOutline, "Multiple segments in outline" );
1377 
1378  // Just a temporary thing
1379  aOutlines = bbox;
1380  return true;
1381  }
1382 
1383  // Figure out which is the correct outline
1384  SHAPE_POLY_SET poly1;
1385  SHAPE_POLY_SET poly2;
1386 
1387  poly1.NewOutline();
1388  poly1.Append( upper );
1389 
1390  poly2.NewOutline();
1391  poly2.Append( lower );
1392 
1393  if( isCopperOutside( footprint, poly1 ) )
1394  {
1395  wxLogTrace( traceBoardOutline, "Using lower shape" );
1396  aOutlines = poly2;
1397  }
1398  else
1399  {
1400  wxLogTrace( traceBoardOutline, "Using upper shape" );
1401  aOutlines = poly1;
1402  }
1403 
1404  // Add all closed polys as holes to the main outline
1405  for( SHAPE_LINE_CHAIN& closedChain : closedChains )
1406  {
1407  wxLogTrace( traceBoardOutline, "Adding hole to main outline" );
1408  aOutlines.AddHole( closedChain, -1 );
1409  }
1410 
1411  return true;
1412  }
1413 
1414  // We really shouldn't reach this point
1415  return false;
1416 }
wxPoint GetArcEnd() const
Definition: pcb_shape.cpp:374
int NewHole(int aOutline=-1)
Adds a new outline to the set and returns its index.
const EDA_RECT GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: board.h:798
int OutlineCount() const
Return the number of vertices in a given outline/hole.
VECTOR2I projectPointOnSegment(const VECTOR2I &aEndPoint, const SHAPE_POLY_SET &aOutline, int aOutlineNum=0)
SEGMENT_ITERATOR IterateSegmentsWithHoles()
Return an iterator object, for the aOutline-th outline in the set (with holes).
SHAPE_POLY_SET & GetPolyShape()
Definition: pcb_shape.h:268
const wxPoint & GetEnd() const
Function GetEnd returns the ending point of the graphic.
Definition: pcb_shape.h:156
Implementation of conversion functions that require both schematic and board internal units.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:82
const EDA_RECT GetBoardEdgesBoundingBox() const
Returns the board bounding box calculated using exclusively the board edges (graphics on Edge....
Definition: board.h:812
PCB_SHAPE_TYPE GetShape() const
Definition: pcb_shape.h:130
int GetRadius() const
Function GetRadius returns the radius of this item Has meaning only for arc and circle.
Definition: pcb_shape.h:201
VECTOR2I::extended_type ecoord
Definition: seg.h:44
std::vector< wxPoint > GetRectCorners() const
Definition: pcb_shape.cpp:971
int GetWidth() const
Definition: pcb_shape.h:118
int GetWidth() const
Definition: eda_rect.h:114
double GetOrientation() const
Definition: footprint.h:186
SHAPE_LINE_CHAIN & Hole(int aOutline, int aHole)
Return the aIndex-th subpolygon in the set.
functions to convert a shape built with DRAWSEGMENTS to a polygon.
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:93
OPT_VECTOR2I IntersectLines(const SEG &aSeg) const
Compute the intersection point of lines passing through ends of (this) and aSeg.
Definition: seg.h:208
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:228
VECTOR2< int > VECTOR2I
Definition: vector2d.h:623
void buildBoardBoundingBoxPoly(const BOARD *aBoard, SHAPE_POLY_SET &aOutline)
Get the complete bounding box of the board (including all items).
static SEG::ecoord Square(int a)
Definition: seg.h:123
search types array terminator (End Of Types)
Definition: typeinfo.h:81
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition: typeinfo.h:77
const wxChar * traceBoardOutline
Flag to enable debug tracing for the board outline creation.
void Append(int aX, int aY, bool aAllowDuplication=false)
Function Append()
PADS & Pads()
Definition: footprint.h:164
This file contains miscellaneous commonly used macros and functions.
wxPoint GetArcStart() const
Definition: pcb_shape.h:179
polygon (not yet used for tracks, but could be in microwave apps)
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:87
const wxPoint GetEnd() const
Definition: eda_rect.h:108
void SetClosed(bool aClosed)
Function SetClosed()
int findEndSegments(SHAPE_LINE_CHAIN &aChain, SEG &aStartSeg, SEG &aEndSeg)
bool closer_to_first(VECTOR2I aRef, VECTOR2I aFirst, VECTOR2I aSecond)
Function closer_to_first Local method which qualifies whether the start or end point of a segment is ...
void SetFlags(STATUS_FLAGS aMask)
Definition: eda_item.h:203
FOOTPRINT * GetFirstFootprint() const
Gets the first footprint on the board or nullptr.
Definition: board.h:382
bool IsClosed() const override
Function IsClosed()
OPT< VECTOR2I > OPT_VECTOR2I
Definition: seg.h:39
Represent a set of closed polygons.
SHAPE_LINE_CHAIN & Outline(int aIndex)
const wxPoint GetOrigin() const
Definition: eda_rect.h:106
bool BuildBoardPolygonOutlines(BOARD *aBoard, SHAPE_POLY_SET &aOutlines, int aErrorMax, int aChainingEpsilon, OUTLINE_ERROR_HANDLER *aErrorHandler)
Extracts the board outlines and build a closed polygon from lines, arcs and circle items on edge cut ...
segment with non rounded ends
wxPoint GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_shape.cpp:342
const wxPoint & GetStart() const
Function GetStart returns the starting point of the graphic.
Definition: pcb_shape.h:145
const std::vector< wxPoint > & GetBezierPoints() const
Definition: pcb_shape.h:253
a few functions useful in geometry calculations.
void BooleanIntersection(const SHAPE_POLY_SET &b, POLYGON_MODE aFastMode)
Perform boolean polyset union between a and b, store the result in it self For aFastMode meaning,...
int NewOutline()
Creates a new hole in a given outline.
int HoleCount(int aOutline) const
Return the reference to aIndex-th outline in the set.
bool ConvertOutlineToPolygon(std::vector< PCB_SHAPE * > &aSegList, SHAPE_POLY_SET &aPolygons, int aErrorMax, int aChainingEpsilon, OUTLINE_ERROR_HANDLER *aErrorHandler)
Function ConvertOutlineToPolygon Build a polygon (with holes) from a PCB_SHAPE list,...
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:605
int AddHole(const SHAPE_LINE_CHAIN &aHole, int aOutline=-1)
Return the area of this poly set.
int GetHeight() const
Definition: eda_rect.h:115
Definition: seg.h:41
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new hole to the given outline (default: last) and returns its index.
SEG Segment(int aIndex)
Function Segment()
FOOTPRINT * GetParentFootprint() const
Function GetParentFootprint returns a pointer to the parent footprint, or NULL if PCB_SHAPE does not ...
Definition: pcb_shape.cpp:479
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:190
#define _(s)
Definition: 3d_actions.cpp:33
SHAPE_LINE_CHAIN.
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
VECTOR2I A
Definition: seg.h:49
double GetAngle() const
Definition: pcb_shape.h:127
Handle the component boundary box.
Definition: eda_rect.h:42
usual segment : line with rounded ends
bool close_enough(VECTOR2I aLeft, VECTOR2I aRight, unsigned aLimit)
Function close_enough is a local and tunable method of qualifying the proximity of two points.
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aCornerBuffer, wxPoint aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc)
Function TransformCircleToPolygon convert a circle to a polygon, using multiple straight lines.
const std::function< void(const wxString &msg, BOARD_ITEM *itemA, BOARD_ITEM *itemB, const wxPoint &pt)> OUTLINE_ERROR_HANDLER
wxPoint GetPosition() const override
Definition: footprint.h:182
void RebuildBezierToSegmentsPointsList(int aMinSegLen)
Rebuild the m_BezierPoints vertex list that approximate the Bezier curve by a list of segments Has me...
Definition: pcb_shape.cpp:316
EDA_RECT ComputeBoundingBox(bool aBoardEdgesOnly=false) const
Calculate the bounding box containing all board items (or board edge segments).
Definition: board.cpp:1050
void ClearFlags(STATUS_FLAGS aMask=EDA_ITEM_ALL_FLAGS)
Definition: eda_item.h:204
CONST_SEGMENT_ITERATOR CIterateSegments(int aFirst, int aLast, bool aIterateHoles=false) const
Return an iterator object, for iterating aPolygonIdx-th polygon edges.
static PCB_SHAPE * findNext(PCB_SHAPE *aShape, const wxPoint &aPoint, const std::vector< PCB_SHAPE * > &aList, unsigned aLimit)
Searches for a PCB_SHAPE matching a given end point or start point in a list.
#define SKIP_STRUCT
flag indicating that the structure should be ignored
Definition: eda_item.h:118
Collect all BOARD_ITEM objects of a given set of KICAD_T type(s).
Definition: collectors.h:618
Definition: pad.h:60
static wxString ShowShape(PCB_SHAPE_TYPE aShape)
Convert the enum #PCB_SHAPE_TYPE_T integer value to a wxString.
Definition: board_item.cpp:31
Arcs (with rounded ends)
STATUS_FLAGS GetFlags() const
Definition: eda_item.h:205
bool BuildFootprintPolygonOutlines(BOARD *aBoard, SHAPE_POLY_SET &aOutlines, int aErrorMax, int aChainingEpsilon, OUTLINE_ERROR_HANDLER *aErrorHandler)
This function is used to extract a board outline for a footprint view.
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:90
static constexpr int Millimeter2iu(double mm)
bool isCopperOutside(const FOOTPRINT *aMod, SHAPE_POLY_SET &aShape)
int GetArcToSegmentCount(int aRadius, int aErrorMax, double aArcAngleDegree)
EDA_RECT & Inflate(wxCoord dx, wxCoord dy)
Inflate the rectangle horizontally by dx and vertically by dy.
Definition: eda_rect.cpp:363
bool Contains(const SEG &aSeg) const
Definition: seg.h:336
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Add a new vertex to the contour indexed by aOutline and aHole (defaults to the outline of the last po...
VECTOR2I B
Definition: seg.h:50