KiCad PCB EDA Suite
drc_clearance_test_functions.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) 2004-2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 2007 Dick Hollenbeck, dick@softplc.com
6  * Copyright (C) 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 
26 #include <fctsys.h>
27 #include <pcb_edit_frame.h>
28 #include <trigo.h>
29 #include <pcbnew.h>
30 #include <tools/drc.h>
31 #include <class_board.h>
32 #include <class_module.h>
33 #include <class_track.h>
34 #include <class_zone.h>
35 #include <class_drawsegment.h>
36 #include <class_marker_pcb.h>
37 #include <math_for_graphics.h>
40 #include <board_commit.h>
41 
42 
48 bool poly2polyDRC( wxPoint* aTref, int aTrefCount, wxPoint* aTtest, int aTtestCount, int aDist )
49 {
50  /* Test if one polygon is contained in the other and thus the polygon overlap.
51  * This case is not covered by the following check if one polygone is
52  * completely contained in the other (because edges don't intersect)!
53  */
54  if( TestPointInsidePolygon( aTref, aTrefCount, aTtest[0] ) )
55  return false;
56 
57  if( TestPointInsidePolygon( aTtest, aTtestCount, aTref[0] ) )
58  return false;
59 
60  for( int ii = 0, jj = aTrefCount - 1; ii < aTrefCount; jj = ii, ii++ )
61  {
62  // for all edges in aTref
63  for( int kk = 0, ll = aTtestCount - 1; kk < aTtestCount; ll = kk, kk++ )
64  {
65  // for all edges in aTtest
66  double d;
68  aTref[ii].x, aTref[ii].y, aTref[jj].x, aTref[jj].y,
69  aTtest[kk].x, aTtest[kk].y, aTtest[ll].x, aTtest[ll].y,
70  nullptr, nullptr, &d );
71 
72  if( intersect || ( d < aDist ) )
73  return false;
74  }
75  }
76 
77  return true;
78 }
79 
80 
81 /*
82  * compare a trapezoid (can be rectangle) and a segment and return true if distance > aDist
83  */
84 bool poly2segmentDRC( wxPoint* aTref, int aTrefCount, wxPoint aSegStart, wxPoint aSegEnd, int aDist )
85 {
86  /* Test if the segment is contained in the polygon.
87  * This case is not covered by the following check if the segment is
88  * completely contained in the polygon (because edges don't intersect)!
89  */
90  if( TestPointInsidePolygon( aTref, aTrefCount, aSegStart ) )
91  return false;
92 
93  for( int ii = 0, jj = aTrefCount-1; ii < aTrefCount; jj = ii, ii++ )
94  { // for all edges in polygon
95  double d;
97  aTref[ii].x, aTref[ii].y, aTref[jj].x, aTref[jj].y,
98  aSegStart.x, aSegStart.y, aSegEnd.x, aSegEnd.y,
99  NULL, NULL, &d );
100 
101  if( intersect || ( d < aDist) )
102  return false;
103  }
104 
105  return true;
106 }
107 
108 
109 #define PUSH_NEW_MARKER_3( a, b, c ) push_back( m_markerFactory.NewMarker( a, b, c ) )
110 #define PUSH_NEW_MARKER_4( a, b, c, d ) push_back( m_markerFactory.NewMarker( a, b, c, d ) )
111 
112 
113 bool DRC::doTrackDrc( TRACK* aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterator aEndIt,
114  bool aTestZones )
115 {
116  TRACK* track;
117  wxPoint delta; // length on X and Y axis of segments
118  wxPoint shape_pos;
119 
120  std::vector<MARKER_PCB*> markers;
121 
122  auto commitMarkers = [&]()
123  {
124  BOARD_COMMIT commit( m_pcbEditorFrame );
125 
126  for( MARKER_PCB* marker : markers )
127  commit.Add( marker );
128 
129  commit.Push( wxEmptyString, false, false );
130  };
131 
132  // Returns false if we should return false from call site, or true to continue
133  auto handleNewMarker = [&]() -> bool
134  {
136  {
137  if( markers.size() > 0 )
138  commitMarkers();
139 
140  return false;
141  }
142  else
143  return true;
144  };
145 
146  NETCLASSPTR netclass = aRefSeg->GetNetClass();
148 
149  /* In order to make some calculations more easier or faster,
150  * pads and tracks coordinates will be made relative to the reference segment origin
151  */
152  wxPoint origin = aRefSeg->GetStart(); // origin will be the origin of other coordinates
153 
154  m_segmEnd = delta = aRefSeg->GetEnd() - origin;
155  m_segmAngle = 0;
156 
157  LSET layerMask = aRefSeg->GetLayerSet();
158  int net_code_ref = aRefSeg->GetNetCode();
159  int ref_seg_clearance = netclass->GetClearance();
160  int ref_seg_width = aRefSeg->GetWidth();
161 
162 
163  /******************************************/
164  /* Phase 0 : via DRC tests : */
165  /******************************************/
166 
167  if( aRefSeg->Type() == PCB_VIA_T )
168  {
169  VIA *refvia = static_cast<VIA*>( aRefSeg );
170  wxPoint refviaPos = refvia->GetPosition();
171 
172  // test if the via size is smaller than minimum
173  if( refvia->GetViaType() == VIA_MICROVIA )
174  {
175  if( refvia->GetWidth() < dsnSettings.m_MicroViasMinSize )
176  {
177  markers.PUSH_NEW_MARKER_3( refviaPos, refvia, DRCE_TOO_SMALL_MICROVIA );
178 
179  if( !handleNewMarker() )
180  return false;
181  }
182 
183  if( refvia->GetDrillValue() < dsnSettings.m_MicroViasMinDrill )
184  {
185  markers.PUSH_NEW_MARKER_3( refviaPos, refvia, DRCE_TOO_SMALL_MICROVIA_DRILL );
186 
187  if( !handleNewMarker() )
188  return false;
189  }
190  }
191  else
192  {
193  if( refvia->GetWidth() < dsnSettings.m_ViasMinSize )
194  {
195  markers.PUSH_NEW_MARKER_3( refviaPos, refvia, DRCE_TOO_SMALL_VIA );
196 
197  if( !handleNewMarker() )
198  return false;
199  }
200 
201  if( refvia->GetDrillValue() < dsnSettings.m_ViasMinDrill )
202  {
203  markers.PUSH_NEW_MARKER_3( refviaPos, refvia, DRCE_TOO_SMALL_VIA_DRILL );
204 
205  if( !handleNewMarker() )
206  return false;
207  }
208  }
209 
210  // test if via's hole is bigger than its diameter
211  // This test is necessary since the via hole size and width can be modified
212  // and a default via hole can be bigger than some vias sizes
213  if( refvia->GetDrillValue() > refvia->GetWidth() )
214  {
215  markers.PUSH_NEW_MARKER_3( refviaPos, refvia, DRCE_VIA_HOLE_BIGGER );
216 
217  if( !handleNewMarker() )
218  return false;
219  }
220 
221  // test if the type of via is allowed due to design rules
222  if( refvia->GetViaType() == VIA_MICROVIA && !dsnSettings.m_MicroViasAllowed )
223  {
224  markers.PUSH_NEW_MARKER_3( refviaPos, refvia, DRCE_MICRO_VIA_NOT_ALLOWED );
225 
226  if( !handleNewMarker() )
227  return false;
228  }
229 
230  // test if the type of via is allowed due to design rules
231  if( refvia->GetViaType() == VIA_BLIND_BURIED && !dsnSettings.m_BlindBuriedViaAllowed )
232  {
233  markers.PUSH_NEW_MARKER_3( refviaPos, refvia, DRCE_BURIED_VIA_NOT_ALLOWED );
234 
235  if( !handleNewMarker() )
236  return false;
237  }
238 
239  // For microvias: test if they are blind vias and only between 2 layers
240  // because they are used for very small drill size and are drill by laser
241  // and **only one layer** can be drilled
242  if( refvia->GetViaType() == VIA_MICROVIA )
243  {
244  PCB_LAYER_ID layer1, layer2;
245  bool err = true;
246 
247  refvia->LayerPair( &layer1, &layer2 );
248 
249  if( layer1 > layer2 )
250  std::swap( layer1, layer2 );
251 
252  if( layer2 == B_Cu && layer1 == dsnSettings.GetCopperLayerCount() - 2 )
253  err = false;
254  else if( layer1 == F_Cu && layer2 == In1_Cu )
255  err = false;
256 
257  if( err )
258  {
259  markers.PUSH_NEW_MARKER_3( refviaPos, refvia, DRCE_MICRO_VIA_INCORRECT_LAYER_PAIR );
260 
261  if( !handleNewMarker() )
262  return false;
263  }
264  }
265 
266  }
267  else // This is a track segment
268  {
269  if( ref_seg_width < dsnSettings.m_TrackMinWidth )
270  {
271  wxPoint refsegMiddle = ( aRefSeg->GetStart() + aRefSeg->GetEnd() ) / 2;
272 
273  markers.PUSH_NEW_MARKER_3( refsegMiddle, aRefSeg, DRCE_TOO_SMALL_TRACK_WIDTH );
274 
275  if( !handleNewMarker() )
276  return false;
277  }
278  }
279 
280  // for a non horizontal or vertical segment Compute the segment angle
281  // in tenths of degrees and its length
282  if( delta.x || delta.y )
283  {
284  // Compute the segment angle in 0,1 degrees
285  m_segmAngle = ArcTangente( delta.y, delta.x );
286 
287  // Compute the segment length: we build an equivalent rotated segment,
288  // this segment is horizontal, therefore dx = length
289  RotatePoint( &delta, m_segmAngle ); // delta.x = length, delta.y = 0
290  }
291 
292  m_segmLength = delta.x;
293 
294  /******************************************/
295  /* Phase 1 : test DRC track to pads : */
296  /******************************************/
297 
298  /* Use a dummy pad to test DRC tracks versus holes, for pads not on all copper layers
299  * but having a hole
300  * This dummy pad has the size and shape of the hole
301  * to test tracks to pad hole DRC, using checkClearanceSegmToPad test function.
302  * Therefore, this dummy pad is a circle or an oval.
303  * A pad must have a parent because some functions expect a non null parent
304  * to find the parent board, and some other data
305  */
306  MODULE dummymodule( m_pcb ); // Creates a dummy parent
307  D_PAD dummypad( &dummymodule );
308 
309  dummypad.SetLayerSet( LSET::AllCuMask() ); // Ensure the hole is on all layers
310 
311  // Compute the min distance to pads
312  for( MODULE* mod : m_pcb->Modules() )
313  {
314  for( D_PAD* pad : mod->Pads() )
315  {
316  SEG padSeg( pad->GetPosition(), pad->GetPosition() );
317 
318  // No problem if pads are on another layer, but if a drill hole exists (a pad on
319  // a single layer can have a hole!) we must test the hole
320  if( !( pad->GetLayerSet() & layerMask ).any() )
321  {
322  // We must test the pad hole. In order to use checkClearanceSegmToPad(), a
323  // pseudo pad is used, with a shape and a size like the hole
324  if( pad->GetDrillSize().x == 0 )
325  continue;
326 
327  dummypad.SetSize( pad->GetDrillSize() );
328  dummypad.SetPosition( pad->GetPosition() );
329  dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ?
331  dummypad.SetOrientation( pad->GetOrientation() );
332 
333  m_padToTestPos = dummypad.GetPosition() - origin;
334 
335  if( !checkClearanceSegmToPad( &dummypad, ref_seg_width, ref_seg_clearance ) )
336  {
337  markers.PUSH_NEW_MARKER_4( aRefSeg, pad, padSeg, DRCE_TRACK_NEAR_THROUGH_HOLE );
338 
339  if( !handleNewMarker() )
340  return false;
341  }
342 
343  continue;
344  }
345 
346  // The pad must be in a net (i.e pt_pad->GetNet() != 0 )
347  // but no problem if the pad netcode is the current netcode (same net)
348  if( pad->GetNetCode() // the pad must be connected
349  && net_code_ref == pad->GetNetCode() ) // the pad net is the same as current net -> Ok
350  continue;
351 
352  // DRC for the pad
353  shape_pos = pad->ShapePos();
354  m_padToTestPos = shape_pos - origin;
355  int segToPadClearance = std::max( ref_seg_clearance, pad->GetClearance() );
356 
357  if( !checkClearanceSegmToPad( pad, ref_seg_width, segToPadClearance ) )
358  {
359  markers.PUSH_NEW_MARKER_4( aRefSeg, pad, padSeg, DRCE_TRACK_NEAR_PAD );
360 
361  if( !handleNewMarker() )
362  return false;
363  }
364  }
365  }
366 
367  /***********************************************/
368  /* Phase 2: test DRC with other track segments */
369  /***********************************************/
370 
371  // At this point the reference segment is the X axis
372 
373  // Test the reference segment with other track segments
374  wxPoint segStartPoint;
375  wxPoint segEndPoint;
376 
377  for( auto it = aStartIt; it != aEndIt; it++ )
378  {
379  track = *it;
380  // No problem if segments have the same net code:
381  if( net_code_ref == track->GetNetCode() )
382  continue;
383 
384  // No problem if segment are on different layers :
385  if( !( layerMask & track->GetLayerSet() ).any() )
386  continue;
387 
388  // the minimum distance = clearance plus half the reference track
389  // width plus half the other track's width
390  int w_dist = std::max( ref_seg_clearance, track->GetClearance() );
391  w_dist += ( ref_seg_width + track->GetWidth() ) / 2;
392 
393  // Due to many double to int conversions during calculations, which
394  // create rounding issues,
395  // the exact clearance margin cannot be really known.
396  // To avoid false bad DRC detection due to these rounding issues,
397  // slightly decrease the w_dist (remove one nanometer is enough !)
398  w_dist -= 1;
399 
400  // If the reference segment is a via, we test it here
401  if( aRefSeg->Type() == PCB_VIA_T )
402  {
403  delta = track->GetEnd() - track->GetStart();
404  segStartPoint = aRefSeg->GetStart() - track->GetStart();
405  wxPoint pos = aRefSeg->GetPosition();
406 
407  if( track->Type() == PCB_VIA_T )
408  {
409  // Test distance between two vias, i.e. two circles, trivial case
410  if( EuclideanNorm( segStartPoint ) < w_dist )
411  {
412  markers.PUSH_NEW_MARKER_4( pos, aRefSeg, track, DRCE_VIA_NEAR_VIA );
413 
414  if( !handleNewMarker() )
415  return false;
416  }
417  }
418  else // test via to segment
419  {
420  // Compute l'angle du segment a tester;
421  double angle = ArcTangente( delta.y, delta.x );
422 
423  // Compute new coordinates ( the segment become horizontal)
424  RotatePoint( &delta, angle );
425  RotatePoint( &segStartPoint, angle );
426 
427  if( !checkMarginToCircle( segStartPoint, w_dist, delta.x ) )
428  {
429  markers.PUSH_NEW_MARKER_4( pos, aRefSeg, track, DRCE_VIA_NEAR_TRACK );
430 
431  if( !handleNewMarker() )
432  return false;
433  }
434  }
435 
436  continue;
437  }
438 
439  /* We compute segStartPoint, segEndPoint = starting and ending point coordinates for
440  * the segment to test in the new axis : the new X axis is the
441  * reference segment. We must translate and rotate the segment to test
442  */
443  segStartPoint = track->GetStart() - origin;
444  segEndPoint = track->GetEnd() - origin;
445  RotatePoint( &segStartPoint, m_segmAngle );
446  RotatePoint( &segEndPoint, m_segmAngle );
447 
448  SEG seg( segStartPoint, segEndPoint );
449 
450  if( track->Type() == PCB_VIA_T )
451  {
452  if( checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) )
453  continue;
454 
455  markers.PUSH_NEW_MARKER_4( aRefSeg, track, seg, DRCE_TRACK_NEAR_VIA );
456 
457  if( !handleNewMarker() )
458  return false;
459  }
460 
461  /* We have changed axis:
462  * the reference segment is Horizontal.
463  * 3 cases : the segment to test can be parallel, perpendicular or have another direction
464  */
465  if( segStartPoint.y == segEndPoint.y ) // parallel segments
466  {
467  if( abs( segStartPoint.y ) >= w_dist )
468  continue;
469 
470  // Ensure segStartPoint.x <= segEndPoint.x
471  if( segStartPoint.x > segEndPoint.x )
472  std::swap( segStartPoint.x, segEndPoint.x );
473 
474  if( segStartPoint.x > ( -w_dist ) && segStartPoint.x < ( m_segmLength + w_dist ) )
475  {
476  // the start point is inside the reference range
477  // X........
478  // O--REF--+
479 
480  // Fine test : we consider the rounded shape of each end of the track segment:
481  if( segStartPoint.x >= 0 && segStartPoint.x <= m_segmLength )
482  {
483  markers.PUSH_NEW_MARKER_4( aRefSeg, track, seg, DRCE_TRACK_ENDS1 );
484 
485  if( !handleNewMarker() )
486  return false;
487  }
488 
489  if( !checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) )
490  {
491  markers.PUSH_NEW_MARKER_4( aRefSeg, track, seg, DRCE_TRACK_ENDS2 );
492 
493  if( !handleNewMarker() )
494  return false;
495  }
496  }
497 
498  if( segEndPoint.x > ( -w_dist ) && segEndPoint.x < ( m_segmLength + w_dist ) )
499  {
500  // the end point is inside the reference range
501  // .....X
502  // O--REF--+
503  // Fine test : we consider the rounded shape of the ends
504  if( segEndPoint.x >= 0 && segEndPoint.x <= m_segmLength )
505  {
506  markers.PUSH_NEW_MARKER_4( aRefSeg, track, seg, DRCE_TRACK_ENDS3 );
507 
508  if( !handleNewMarker() )
509  return false;
510  }
511 
512  if( !checkMarginToCircle( segEndPoint, w_dist, m_segmLength ) )
513  {
514  markers.PUSH_NEW_MARKER_4( aRefSeg, track, seg, DRCE_TRACK_ENDS4 );
515 
516  if( !handleNewMarker() )
517  return false;
518  }
519  }
520 
521  if( segStartPoint.x <= 0 && segEndPoint.x >= 0 )
522  {
523  // the segment straddles the reference range (this actually only
524  // checks if it straddles the origin, because the other cases where already
525  // handled)
526  // X.............X
527  // O--REF--+
528  markers.PUSH_NEW_MARKER_4( aRefSeg, track, seg, DRCE_TRACK_SEGMENTS_TOO_CLOSE );
529 
530  if( !handleNewMarker() )
531  return false;
532  }
533  }
534  else if( segStartPoint.x == segEndPoint.x ) // perpendicular segments
535  {
536  if( segStartPoint.x <= -w_dist || segStartPoint.x >= m_segmLength + w_dist )
537  continue;
538 
539  // Test if segments are crossing
540  if( segStartPoint.y > segEndPoint.y )
541  std::swap( segStartPoint.y, segEndPoint.y );
542 
543  if( ( segStartPoint.y < 0 ) && ( segEndPoint.y > 0 ) )
544  {
545  MARKER_PCB* m = m_markerFactory.NewMarker( aRefSeg, track, seg,
547  m->SetPosition( wxPoint( track->GetStart().x, aRefSeg->GetStart().y ) );
548  markers.push_back( m );
549 
550  if( !handleNewMarker() )
551  return false;
552  }
553 
554  // At this point the drc error is due to an end near a reference segm end
555  if( !checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) )
556  {
557  markers.PUSH_NEW_MARKER_4( aRefSeg, track, seg, DRCE_ENDS_PROBLEM1 );
558 
559  if( !handleNewMarker() )
560  return false;
561  }
562  if( !checkMarginToCircle( segEndPoint, w_dist, m_segmLength ) )
563  {
564  markers.PUSH_NEW_MARKER_4( aRefSeg, track, seg, DRCE_ENDS_PROBLEM2 );
565 
566  if( !handleNewMarker() )
567  return false;
568  }
569  }
570  else // segments quelconques entre eux
571  {
572  // calcul de la "surface de securite du segment de reference
573  // First rought 'and fast) test : the track segment is like a rectangle
574 
575  m_xcliplo = m_ycliplo = -w_dist;
576  m_xcliphi = m_segmLength + w_dist;
577  m_ycliphi = w_dist;
578 
579  // A fine test is needed because a serment is not exactly a
580  // rectangle, it has rounded ends
581  if( !checkLine( segStartPoint, segEndPoint ) )
582  {
583  /* 2eme passe : the track has rounded ends.
584  * we must a fine test for each rounded end and the
585  * rectangular zone
586  */
587 
588  m_xcliplo = 0;
590 
591  if( !checkLine( segStartPoint, segEndPoint ) )
592  {
593  wxPoint failurePoint;
594  MARKER_PCB* m;
595 
596  if( SegmentIntersectsSegment( aRefSeg->GetStart(), aRefSeg->GetEnd(),
597  track->GetStart(), track->GetEnd(),
598  &failurePoint ) )
599  {
600  m = m_markerFactory.NewMarker( aRefSeg, track, seg, DRCE_TRACKS_CROSSING );
601  m->SetPosition( failurePoint );
602  }
603  else
604  {
605  m = m_markerFactory.NewMarker( aRefSeg, track, seg, DRCE_ENDS_PROBLEM3 );
606  }
607 
608  markers.push_back( m );
609 
610  if( !handleNewMarker() )
611  return false;
612  }
613  else // The drc error is due to the starting or the ending point of the reference segment
614  {
615  // Test the starting and the ending point
616  segStartPoint = track->GetStart();
617  segEndPoint = track->GetEnd();
618  delta = segEndPoint - segStartPoint;
619 
620  // Compute the segment orientation (angle) en 0,1 degre
621  double angle = ArcTangente( delta.y, delta.x );
622 
623  // Compute the segment length: delta.x = length after rotation
624  RotatePoint( &delta, angle );
625 
626  /* Comute the reference segment coordinates relatives to a
627  * X axis = current tested segment
628  */
629  wxPoint relStartPos = aRefSeg->GetStart() - segStartPoint;
630  wxPoint relEndPos = aRefSeg->GetEnd() - segStartPoint;
631 
632  RotatePoint( &relStartPos, angle );
633  RotatePoint( &relEndPos, angle );
634 
635  if( !checkMarginToCircle( relStartPos, w_dist, delta.x ) )
636  {
637  markers.PUSH_NEW_MARKER_4( aRefSeg, track, seg, DRCE_ENDS_PROBLEM4 );
638 
639  if( !handleNewMarker() )
640  return false;
641  }
642 
643  if( !checkMarginToCircle( relEndPos, w_dist, delta.x ) )
644  {
645  markers.PUSH_NEW_MARKER_4( aRefSeg, track, seg, DRCE_ENDS_PROBLEM5 );
646 
647  if( !handleNewMarker() )
648  return false;
649  }
650  }
651  }
652  }
653  }
654 
655  /***************************************/
656  /* Phase 3: test DRC with copper zones */
657  /***************************************/
658  // Can be *very* time consumming.
659  if( aTestZones )
660  {
661  SEG refSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() );
662 
663  for( ZONE_CONTAINER* zone : m_pcb->Zones() )
664  {
665  if( zone->GetFilledPolysList().IsEmpty() || zone->GetIsKeepout() )
666  continue;
667 
668  if( !( layerMask & zone->GetLayerSet() ).any() )
669  continue;
670 
671  if( zone->GetNetCode() && zone->GetNetCode() == net_code_ref )
672  continue;
673 
674  int clearance = std::max( ref_seg_clearance, zone->GetClearance() );
675  SHAPE_POLY_SET* outline = const_cast<SHAPE_POLY_SET*>( &zone->GetFilledPolysList() );
676 
677  if( outline->Distance( refSeg, ref_seg_width ) < clearance )
679  }
680  }
681 
682  /***********************************************/
683  /* Phase 4: test DRC with to board edge */
684  /***********************************************/
685  {
686  SEG test_seg( aRefSeg->GetStart(), aRefSeg->GetEnd() );
687 
688  int clearance = std::max( ref_seg_clearance, dsnSettings.m_CopperEdgeClearance );
689 
690  // the minimum distance = clearance plus half the reference track width
691  SEG::ecoord w_dist = clearance + ref_seg_width / 2;
692  SEG::ecoord w_dist_sq = w_dist * w_dist;
693 
694  for( auto it = m_board_outlines.IterateSegmentsWithHoles(); it; it++ )
695  {
696  if( test_seg.SquaredDistance( *it ) < w_dist_sq )
697  {
698  VECTOR2I pt = test_seg.NearestPoint( *it );
699 
700  KICAD_T types[] = { PCB_LINE_T, EOT };
701  DRAWSEGMENT* edge = nullptr;
702  INSPECTOR_FUNC inspector = [&] ( EDA_ITEM* item, void* testData )
703  {
704  DRAWSEGMENT* test_edge = dynamic_cast<DRAWSEGMENT*>( item );
705 
706  if( !test_edge || test_edge->GetLayer() != Edge_Cuts )
707  return SEARCH_CONTINUE;
708 
709  if( test_edge->HitTest((wxPoint) pt, w_dist ) )
710  {
711  edge = test_edge;
712  return SEARCH_QUIT;
713  }
714 
715  return SEARCH_CONTINUE;
716  };
717 
718  // Best-efforts search for edge segment
719  BOARD::IterateForward<BOARD_ITEM*>( m_pcb->Drawings(), inspector, nullptr, types );
720 
721  if( edge )
722  markers.PUSH_NEW_MARKER_4( (wxPoint) pt, aRefSeg, edge, DRCE_TRACK_NEAR_EDGE );
723  else
724  markers.PUSH_NEW_MARKER_3( (wxPoint) pt, aRefSeg, DRCE_TRACK_NEAR_EDGE );
725 
726  if( !handleNewMarker() )
727  return false;
728  }
729  }
730  }
731 
732 
733  if( markers.size() > 0 )
734  {
735  commitMarkers();
736  return false;
737  }
738  else
739  return true;
740 }
741 
742 
743 bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
744 {
745  int dist;
746  double pad_angle;
747 
748  // Get the clearance between the 2 pads. this is the min distance between aRefPad and aPad
749  int dist_min = aRefPad->GetClearance( aPad );
750 
751  // relativePadPos is the aPad shape position relative to the aRefPad shape position
752  wxPoint relativePadPos = aPad->ShapePos() - aRefPad->ShapePos();
753 
754  dist = KiROUND( EuclideanNorm( relativePadPos ) );
755 
756  // Quick test: Clearance is OK if the bounding circles are further away than "dist_min"
757  int delta = dist - aRefPad->GetBoundingRadius() - aPad->GetBoundingRadius();
758 
759  if( delta >= dist_min )
760  return true;
761 
762  /* Here, pads are near and DRC depend on the pad shapes
763  * We must compare distance using a fine shape analysis
764  * Because a circle or oval shape is the easier shape to test, try to have
765  * aRefPad shape type = PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL.
766  * if aRefPad = TRAP. and aPad = RECT, also swap pads
767  * Swap aRefPad and aPad if needed
768  */
769  bool swap_pads;
770  swap_pads = false;
771 
772  // swap pads to make comparisons easier
773  // Note also a ROUNDRECT pad with a corner radius = r can be considered as
774  // a smaller RECT (size - 2*r) with a clearance increased by r
775  // priority is aRefPad = ROUND then OVAL then RECT/ROUNDRECT then other
776  if( aRefPad->GetShape() != aPad->GetShape() && aRefPad->GetShape() != PAD_SHAPE_CIRCLE )
777  {
778  // pad ref shape is here oval, rect, roundrect, chamfered rect, trapezoid or custom
779  switch( aPad->GetShape() )
780  {
781  case PAD_SHAPE_CIRCLE:
782  swap_pads = true;
783  break;
784 
785  case PAD_SHAPE_OVAL:
786  swap_pads = true;
787  break;
788 
789  case PAD_SHAPE_RECT:
790  case PAD_SHAPE_ROUNDRECT:
791  if( aRefPad->GetShape() != PAD_SHAPE_OVAL )
792  swap_pads = true;
793  break;
794 
795  case PAD_SHAPE_TRAPEZOID:
797  case PAD_SHAPE_CUSTOM:
798  break;
799  }
800  }
801 
802  if( swap_pads )
803  {
804  std::swap( aRefPad, aPad );
805  relativePadPos = -relativePadPos;
806  }
807 
808  // corners of aRefPad (used only for rect/roundrect/trap pad)
809  wxPoint polyref[4];
810  // corners of aRefPad (used only for custom pad)
811  SHAPE_POLY_SET polysetref;
812 
813  // corners of aPad (used only for rect/roundrect/trap pad)
814  wxPoint polycompare[4];
815  // corners of aPad (used only custom pad)
816  SHAPE_POLY_SET polysetcompare;
817 
818  /* Because pad exchange, aRefPad shape is PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL,
819  * if one of the 2 pads was a PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL.
820  * Therefore, if aRefPad is a PAD_SHAPE_RECT, PAD_SHAPE_ROUNDRECT or a PAD_SHAPE_TRAPEZOID,
821  * aPad is also a PAD_SHAPE_RECT, PAD_SHAPE_ROUNDRECT or a PAD_SHAPE_TRAPEZOID
822  */
823  bool diag = true;
824 
825  switch( aRefPad->GetShape() )
826  {
827  case PAD_SHAPE_CIRCLE:
828 
829  /* One can use checkClearanceSegmToPad to test clearance
830  * aRefPad is like a track segment with a null length and a witdth = GetSize().x
831  */
832  m_segmLength = 0;
833  m_segmAngle = 0;
834 
835  m_segmEnd.x = m_segmEnd.y = 0;
836 
837  m_padToTestPos = relativePadPos;
838  diag = checkClearanceSegmToPad( aPad, aRefPad->GetSize().x, dist_min );
839  break;
840 
841  case PAD_SHAPE_TRAPEZOID:
842  case PAD_SHAPE_ROUNDRECT:
844  case PAD_SHAPE_RECT:
845  case PAD_SHAPE_CUSTOM:
846  // pad_angle = pad orient relative to the aRefPad orient
847  pad_angle = aRefPad->GetOrientation() + aPad->GetOrientation();
848  NORMALIZE_ANGLE_POS( pad_angle );
849 
850  if( aRefPad->GetShape() == PAD_SHAPE_ROUNDRECT )
851  {
852  int padRadius = aRefPad->GetRoundRectCornerRadius();
853  dist_min += padRadius;
854  GetRoundRectCornerCenters( polyref, padRadius, wxPoint( 0, 0 ), aRefPad->GetSize(),
855  aRefPad->GetOrientation() );
856  }
857  else if( aRefPad->GetShape() == PAD_SHAPE_CHAMFERED_RECT )
858  {
859  auto board = aRefPad->GetBoard();
860  int maxError = ARC_HIGH_DEF;
861 
862  if( board )
863  maxError = board->GetDesignSettings().m_MaxError;
864 
865  // The reference pad can be rotated. calculate the rotated
866  // coordinates ( note, the ref pad position is the origin of
867  // coordinates for this drc test)
868  int padRadius = aRefPad->GetRoundRectCornerRadius();
869  TransformRoundChamferedRectToPolygon( polysetref, wxPoint( 0, 0 ), aRefPad->GetSize(),
870  aRefPad->GetOrientation(),
871  padRadius, aRefPad->GetChamferRectRatio(),
872  aRefPad->GetChamferPositions(), maxError );
873  }
874  else if( aRefPad->GetShape() == PAD_SHAPE_CUSTOM )
875  {
876  polysetref.Append( aRefPad->GetCustomShapeAsPolygon() );
877 
878  // The reference pad can be rotated. calculate the rotated
879  // coordiantes ( note, the ref pad position is the origin of
880  // coordinates for this drc test)
881  aRefPad->CustomShapeAsPolygonToBoardPosition( &polysetref, wxPoint( 0, 0 ),
882  aRefPad->GetOrientation() );
883  }
884  else
885  {
886  // BuildPadPolygon has meaning for rect a trapeziod shapes
887  // and returns the 4 corners
888  aRefPad->BuildPadPolygon( polyref, wxSize( 0, 0 ), aRefPad->GetOrientation() );
889  }
890 
891  switch( aPad->GetShape() )
892  {
893  case PAD_SHAPE_ROUNDRECT:
894  case PAD_SHAPE_RECT:
896  case PAD_SHAPE_TRAPEZOID:
897  case PAD_SHAPE_CUSTOM:
898  if( aPad->GetShape() == PAD_SHAPE_ROUNDRECT )
899  {
900  int padRadius = aPad->GetRoundRectCornerRadius();
901  dist_min += padRadius;
902  GetRoundRectCornerCenters( polycompare, padRadius, relativePadPos, aPad->GetSize(),
903  aPad->GetOrientation() );
904  }
905  else if( aPad->GetShape() == PAD_SHAPE_CHAMFERED_RECT )
906  {
907  auto board = aRefPad->GetBoard();
908  int maxError = ARC_HIGH_DEF;
909 
910  if( board )
911  maxError = board->GetDesignSettings().m_MaxError;
912 
913  // The reference pad can be rotated. calculate the rotated
914  // coordinates ( note, the ref pad position is the origin of
915  // coordinates for this drc test)
916  int padRadius = aPad->GetRoundRectCornerRadius();
917  TransformRoundChamferedRectToPolygon( polysetcompare, relativePadPos,
918  aPad->GetSize(), aPad->GetOrientation(),
919  padRadius, aPad->GetChamferRectRatio(),
920  aPad->GetChamferPositions(), maxError );
921  }
922  else if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
923  {
924  polysetcompare.Append( aPad->GetCustomShapeAsPolygon() );
925 
926  // The pad to compare can be rotated. calculate the rotated
927  // coordinattes ( note, the pad to compare position
928  // is the relativePadPos for this drc test
929  aPad->CustomShapeAsPolygonToBoardPosition( &polysetcompare, relativePadPos,
930  aPad->GetOrientation() );
931  }
932  else
933  {
934  aPad->BuildPadPolygon( polycompare, wxSize( 0, 0 ), aPad->GetOrientation() );
935 
936  // Move aPad shape to relativePadPos
937  for( int ii = 0; ii < 4; ii++ )
938  polycompare[ii] += relativePadPos;
939  }
940  // And now test polygons: We have 3 cases:
941  // one poly is complex and the other is basic (has only 4 corners)
942  // both polys are complex
943  // both polys are basic (have only 4 corners) the most usual case
944  if( polysetref.OutlineCount() && polysetcompare.OutlineCount() == 0)
945  {
946  const SHAPE_LINE_CHAIN& refpoly = polysetref.COutline( 0 );
947  // And now test polygons:
948  if( !poly2polyDRC( (wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(),
949  polycompare, 4, dist_min ) ) // Therefore error
950  diag = false;
951  }
952  else if( polysetref.OutlineCount() == 0 && polysetcompare.OutlineCount())
953  {
954  const SHAPE_LINE_CHAIN& cmppoly = polysetcompare.COutline( 0 );
955  // And now test polygons:
956  if( !poly2polyDRC( (wxPoint*) &cmppoly.CPoint( 0 ), cmppoly.PointCount(),
957  polyref, 4, dist_min ) ) // Therefore error
958  diag = false;
959  }
960  else if( polysetref.OutlineCount() && polysetcompare.OutlineCount() )
961  {
962  const SHAPE_LINE_CHAIN& refpoly = polysetref.COutline( 0 );
963  const SHAPE_LINE_CHAIN& cmppoly = polysetcompare.COutline( 0 );
964 
965  // And now test polygons:
966  if( !poly2polyDRC( (wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(),
967  (wxPoint*) &cmppoly.CPoint( 0 ), cmppoly.PointCount(),
968  dist_min ) ) // Therefore error
969  diag = false;
970  }
971  else
972  {
973  if( !poly2polyDRC( polyref, 4, polycompare, 4, dist_min ) ) // Therefore error
974  diag = false;
975  }
976  break;
977 
978  default:
979  wxLogDebug( wxT( "DRC::checkClearancePadToPad: unexpected pad shape %d" ), aPad->GetShape() );
980  break;
981  }
982  break;
983 
984  case PAD_SHAPE_OVAL: /* an oval pad is like a track segment */
985  {
986  /* Create a track segment with same dimensions as the oval aRefPad
987  * and use checkClearanceSegmToPad function to test aPad to aRefPad clearance
988  */
989  int segm_width;
990  m_segmAngle = aRefPad->GetOrientation(); // Segment orient.
991 
992  if( aRefPad->GetSize().y < aRefPad->GetSize().x ) // Build an horizontal equiv segment
993  {
994  segm_width = aRefPad->GetSize().y;
995  m_segmLength = aRefPad->GetSize().x - aRefPad->GetSize().y;
996  }
997  else // Vertical oval: build an horizontal equiv segment and rotate 90.0 deg
998  {
999  segm_width = aRefPad->GetSize().x;
1000  m_segmLength = aRefPad->GetSize().y - aRefPad->GetSize().x;
1001  m_segmAngle += 900;
1002  }
1003 
1004  /* the start point must be 0,0 and currently relativePadPos
1005  * is relative the center of pad coordinate */
1006  wxPoint segstart;
1007  segstart.x = -m_segmLength / 2; // Start point coordinate of the horizontal equivalent segment
1008 
1009  RotatePoint( &segstart, m_segmAngle ); // actual start point coordinate of the equivalent segment
1010  // Calculate segment end position relative to the segment origin
1011  m_segmEnd.x = -2 * segstart.x;
1012  m_segmEnd.y = -2 * segstart.y;
1013 
1014  // Recalculate the equivalent segment angle in 0,1 degrees
1015  // to prepare a call to checkClearanceSegmToPad()
1017 
1018  // move pad position relative to the segment origin
1019  m_padToTestPos = relativePadPos - segstart;
1020 
1021  // Use segment to pad check to test the second pad:
1022  diag = checkClearanceSegmToPad( aPad, segm_width, dist_min );
1023  break;
1024  }
1025 
1026  default:
1027  wxLogDebug( wxT( "DRC::checkClearancePadToPad: unknown pad shape" ) );
1028  break;
1029  }
1030 
1031  return diag;
1032 }
1033 
1034 
1035 /* test if distance between a segment is > aMinDist
1036  * segment start point is assumed in (0,0) and segment start point in m_segmEnd
1037  * and its orientation is m_segmAngle (m_segmAngle must be already initialized)
1038  * and have aSegmentWidth.
1039  */
1040 bool DRC::checkClearanceSegmToPad( const D_PAD* aPad, int aSegmentWidth, int aMinDist )
1041 {
1042  // Note:
1043  // we are using a horizontal segment for test, because we know here
1044  // only the length and orientation+ of the segment
1045  // Therefore the coordinates of the shape of pad to compare
1046  // must be calculated in a axis system rotated by m_segmAngle
1047  // and centered to the segment origin, before they can be tested
1048  // against the segment
1049  // We are using:
1050  // m_padToTestPos the position of the pad shape in this axis system
1051  // m_segmAngle the axis system rotation
1052 
1053  int segmHalfWidth = aSegmentWidth / 2;
1054  int distToLine = segmHalfWidth + aMinDist;
1055 
1056  wxSize padHalfsize; // half dimension of the pad
1057 
1058  if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
1059  {
1060  // For a custom pad, the pad size has no meaning, we only can
1061  // use the bounding radius
1062  padHalfsize.x = padHalfsize.y = aPad->GetBoundingRadius();
1063  }
1064  else
1065  {
1066  padHalfsize = aPad->GetSize() / 2;
1067  }
1068 
1069  if( aPad->GetShape() == PAD_SHAPE_TRAPEZOID ) // The size is bigger, due to GetDelta() extra size
1070  {
1071  padHalfsize.x += std::abs(aPad->GetDelta().y) / 2; // Remember: GetDelta().y is the GetSize().x change
1072  padHalfsize.y += std::abs(aPad->GetDelta().x) / 2; // Remember: GetDelta().x is the GetSize().y change
1073  }
1074 
1075  if( aPad->GetShape() == PAD_SHAPE_CIRCLE )
1076  {
1077  /* Easy case: just test the distance between segment and pad centre
1078  * calculate pad coordinates in the X,Y axis with X axis = segment to test
1079  */
1081  return checkMarginToCircle( m_padToTestPos, distToLine + padHalfsize.x, m_segmLength );
1082  }
1083 
1084  /* calculate the bounding box of the pad, including the clearance and the segment width
1085  * if the line from 0 to m_segmEnd does not intersect this bounding box,
1086  * the clearance is always OK
1087  * But if intersect, a better analysis of the pad shape must be done.
1088  */
1089  m_xcliplo = m_padToTestPos.x - distToLine - padHalfsize.x;
1090  m_ycliplo = m_padToTestPos.y - distToLine - padHalfsize.y;
1091  m_xcliphi = m_padToTestPos.x + distToLine + padHalfsize.x;
1092  m_ycliphi = m_padToTestPos.y + distToLine + padHalfsize.y;
1093 
1094  wxPoint startPoint( 0, 0 );
1095  wxPoint endPoint = m_segmEnd;
1096 
1097  double orient = aPad->GetOrientation();
1098 
1099  RotatePoint( &startPoint, m_padToTestPos, -orient );
1100  RotatePoint( &endPoint, m_padToTestPos, -orient );
1101 
1102  if( checkLine( startPoint, endPoint ) )
1103  return true;
1104 
1105  /* segment intersects the bounding box. But there is not always a DRC error.
1106  * A fine analysis of the pad shape must be done.
1107  */
1108  switch( aPad->GetShape() )
1109  {
1110  case PAD_SHAPE_CIRCLE:
1111  // This case was already tested, so it cannot be found here.
1112  // it is here just to avoid compil warning, and to remember
1113  // it is already tested.
1114  return false;
1115 
1116  case PAD_SHAPE_OVAL:
1117  {
1118  /* an oval is a complex shape, but is a rectangle and 2 circles
1119  * these 3 basic shapes are more easy to test.
1120  *
1121  * In calculations we are using a vertical or horizontal oval shape
1122  * (i.e. a vertical or horizontal rounded segment)
1123  */
1124  wxPoint cstart = m_padToTestPos;
1125  wxPoint cend = m_padToTestPos; // center of each circle
1126  int delta = std::abs( padHalfsize.y - padHalfsize.x );
1127  int radius = std::min( padHalfsize.y, padHalfsize.x );
1128 
1129  if( padHalfsize.x > padHalfsize.y ) // horizontal equivalent segment
1130  {
1131  cstart.x -= delta;
1132  cend.x += delta;
1133  // Build the rectangular clearance area between the two circles
1134  // the rect starts at cstart.x and ends at cend.x and its height
1135  // is (radius + distToLine)*2
1136  m_xcliplo = cstart.x;
1137  m_ycliplo = cstart.y - radius - distToLine;
1138  m_xcliphi = cend.x;
1139  m_ycliphi = cend.y + radius + distToLine;
1140  }
1141  else // vertical equivalent segment
1142  {
1143  cstart.y -= delta;
1144  cend.y += delta;
1145  // Build the rectangular clearance area between the two circles
1146  // the rect starts at cstart.y and ends at cend.y and its width
1147  // is (radius + distToLine)*2
1148  m_xcliplo = cstart.x - distToLine - radius;
1149  m_ycliplo = cstart.y;
1150  m_xcliphi = cend.x + distToLine + radius;
1151  m_ycliphi = cend.y;
1152  }
1153 
1154  // Test the rectangular clearance area between the two circles (the rounded ends)
1155  // If the segment legth is zero, only check the endpoints, skip the rectangle
1156  if( m_segmLength && !checkLine( startPoint, endPoint ) )
1157  {
1158  return false;
1159  }
1160 
1161  // test the first end
1162  // Calculate the actual position of the circle, given the pad orientation:
1163  RotatePoint( &cstart, m_padToTestPos, orient );
1164 
1165  // Calculate the actual position of the circle in the new X,Y axis, relative
1166  // to the segment:
1167  RotatePoint( &cstart, m_segmAngle );
1168 
1169  if( !checkMarginToCircle( cstart, radius + distToLine, m_segmLength ) )
1170  {
1171  return false;
1172  }
1173 
1174  // test the second end
1175  RotatePoint( &cend, m_padToTestPos, orient );
1176  RotatePoint( &cend, m_segmAngle );
1177 
1178  if( !checkMarginToCircle( cend, radius + distToLine, m_segmLength ) )
1179  {
1180  return false;
1181  }
1182  }
1183  break;
1184 
1185  case PAD_SHAPE_ROUNDRECT:
1186  {
1187  // a round rect is a smaller rect, with a clearance augmented by the corners radius
1188  int r = aPad->GetRoundRectCornerRadius();
1189  padHalfsize.x -= r;
1190  padHalfsize.y -= r;
1191  distToLine += r;
1192  }
1193  // Fall through
1194  case PAD_SHAPE_RECT:
1195  // the area to test is a rounded rectangle.
1196  // this can be done by testing 2 rectangles and 4 circles (the corners)
1197 
1198  // Testing the first rectangle dimx + distToLine, dimy:
1199  m_xcliplo = m_padToTestPos.x - padHalfsize.x - distToLine;
1200  m_ycliplo = m_padToTestPos.y - padHalfsize.y;
1201  m_xcliphi = m_padToTestPos.x + padHalfsize.x + distToLine;
1202  m_ycliphi = m_padToTestPos.y + padHalfsize.y;
1203 
1204  if( !checkLine( startPoint, endPoint ) )
1205  return false;
1206 
1207  // Testing the second rectangle dimx , dimy + distToLine
1208  m_xcliplo = m_padToTestPos.x - padHalfsize.x;
1209  m_ycliplo = m_padToTestPos.y - padHalfsize.y - distToLine;
1210  m_xcliphi = m_padToTestPos.x + padHalfsize.x;
1211  m_ycliphi = m_padToTestPos.y + padHalfsize.y + distToLine;
1212 
1213  if( !checkLine( startPoint, endPoint ) )
1214  return false;
1215 
1216  // testing the 4 circles which are the clearance area of each corner:
1217 
1218  // testing the left top corner of the rectangle
1219  startPoint.x = m_padToTestPos.x - padHalfsize.x;
1220  startPoint.y = m_padToTestPos.y - padHalfsize.y;
1221  RotatePoint( &startPoint, m_padToTestPos, orient );
1222  RotatePoint( &startPoint, m_segmAngle );
1223 
1224  if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
1225  return false;
1226 
1227  // testing the right top corner of the rectangle
1228  startPoint.x = m_padToTestPos.x + padHalfsize.x;
1229  startPoint.y = m_padToTestPos.y - padHalfsize.y;
1230  RotatePoint( &startPoint, m_padToTestPos, orient );
1231  RotatePoint( &startPoint, m_segmAngle );
1232 
1233  if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
1234  return false;
1235 
1236  // testing the left bottom corner of the rectangle
1237  startPoint.x = m_padToTestPos.x - padHalfsize.x;
1238  startPoint.y = m_padToTestPos.y + padHalfsize.y;
1239  RotatePoint( &startPoint, m_padToTestPos, orient );
1240  RotatePoint( &startPoint, m_segmAngle );
1241 
1242  if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
1243  return false;
1244 
1245  // testing the right bottom corner of the rectangle
1246  startPoint.x = m_padToTestPos.x + padHalfsize.x;
1247  startPoint.y = m_padToTestPos.y + padHalfsize.y;
1248  RotatePoint( &startPoint, m_padToTestPos, orient );
1249  RotatePoint( &startPoint, m_segmAngle );
1250 
1251  if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
1252  return false;
1253 
1254  break;
1255 
1256  case PAD_SHAPE_TRAPEZOID:
1257  {
1258  wxPoint poly[4];
1259  aPad->BuildPadPolygon( poly, wxSize( 0, 0 ), orient );
1260 
1261  // Move shape to m_padToTestPos
1262  for( int ii = 0; ii < 4; ii++ )
1263  {
1264  poly[ii] += m_padToTestPos;
1265  RotatePoint( &poly[ii], m_segmAngle );
1266  }
1267 
1268  if( !poly2segmentDRC( poly, 4, wxPoint( 0, 0 ),
1269  wxPoint(m_segmLength,0), distToLine ) )
1270  return false;
1271  }
1272  break;
1273 
1274  case PAD_SHAPE_CUSTOM:
1275  {
1276  SHAPE_POLY_SET polyset;
1277  polyset.Append( aPad->GetCustomShapeAsPolygon() );
1278  // The pad can be rotated. calculate the coordinates
1279  // relatives to the segment being tested
1280  // Note, the pad position relative to the segment origin
1281  // is m_padToTestPos
1282  aPad->CustomShapeAsPolygonToBoardPosition( &polyset,
1283  m_padToTestPos, orient );
1284 
1285  // Rotate all coordinates by m_segmAngle, because the segment orient
1286  // is m_segmAngle
1287  // we are using a horizontal segment for test, because we know here
1288  // only the lenght and orientation+ of the segment
1289  // therefore all coordinates of the pad to test must be rotated by
1290  // m_segmAngle (they are already relative to the segment origin)
1291  aPad->CustomShapeAsPolygonToBoardPosition( &polyset,
1292  wxPoint( 0, 0 ), m_segmAngle );
1293 
1294  const SHAPE_LINE_CHAIN& refpoly = polyset.COutline( 0 );
1295 
1296  if( !poly2segmentDRC( (wxPoint*) &refpoly.CPoint( 0 ),
1297  refpoly.PointCount(),
1298  wxPoint( 0, 0 ), wxPoint(m_segmLength,0),
1299  distToLine ) )
1300  return false;
1301  }
1302  break;
1303 
1305  {
1306  auto board = aPad->GetBoard();
1307  int maxError = ARC_HIGH_DEF;
1308 
1309  if( board )
1310  maxError = board->GetDesignSettings().m_MaxError;
1311 
1312  SHAPE_POLY_SET polyset;
1313  // The pad can be rotated. calculate the coordinates
1314  // relatives to the segment being tested
1315  // Note, the pad position relative to the segment origin
1316  // is m_padToTestPos
1317  int padRadius = aPad->GetRoundRectCornerRadius();
1319  aPad->GetOrientation(),
1320  padRadius, aPad->GetChamferRectRatio(),
1321  aPad->GetChamferPositions(), maxError );
1322  // Rotate also coordinates by m_segmAngle, because the segment orient
1323  // is m_segmAngle.
1324  // we are using a horizontal segment for test, because we know here
1325  // only the lenght and orientation of the segment
1326  // therefore all coordinates of the pad to test must be rotated by
1327  // m_segmAngle (they are already relative to the segment origin)
1328  polyset.Rotate( DECIDEG2RAD( -m_segmAngle ), VECTOR2I( 0, 0 ) );
1329 
1330  const SHAPE_LINE_CHAIN& refpoly = polyset.COutline( 0 );
1331 
1332  if( !poly2segmentDRC( (wxPoint*) &refpoly.CPoint( 0 ),
1333  refpoly.PointCount(),
1334  wxPoint( 0, 0 ), wxPoint(m_segmLength,0),
1335  distToLine ) )
1336  return false;
1337  }
1338  break;
1339  }
1340 
1341  return true;
1342 }
1343 
1344 
1351 bool DRC::checkMarginToCircle( wxPoint aCentre, int aRadius, int aLength )
1352 {
1353  if( abs( aCentre.y ) >= aRadius ) // trivial case
1354  return true;
1355 
1356  // Here, distance between aCentre and X axis is < aRadius
1357  if( (aCentre.x > -aRadius ) && ( aCentre.x < (aLength + aRadius) ) )
1358  {
1359  if( (aCentre.x >= 0) && (aCentre.x <= aLength) )
1360  return false; // aCentre is between the starting point and the ending point of the segm
1361 
1362  if( aCentre.x > aLength ) // aCentre is after the ending point
1363  aCentre.x -= aLength; // move aCentre to the starting point of the segment
1364 
1365  if( EuclideanNorm( aCentre ) < aRadius )
1366  // distance between aCentre and the starting point or the ending point is < aRadius
1367  return false;
1368  }
1369 
1370  return true;
1371 }
1372 
1373 
1374 // Helper function used in checkLine::
1375 static inline int USCALE( unsigned arg, unsigned num, unsigned den )
1376 {
1377  int ii;
1378  double result;
1379 
1380  // Trivial check first
1381  if( !arg || !num)
1382  return 0;
1383 
1384  // If arg and num are both non-zero but den is zero, we return effective infinite
1385  if( !den )
1386  return INT_MAX;
1387 
1388  result = ( (double) arg * num ) / den;
1389 
1390  // Ensure that our result doesn't overflow into the sign bit
1391  if( result > INT_MAX )
1392  return INT_MAX;
1393 
1394  ii = KiROUND( ( (double) arg * num ) / den );
1395  return ii;
1396 }
1397 
1398 
1404 bool DRC::checkLine( wxPoint aSegStart, wxPoint aSegEnd )
1405 {
1406 #define WHEN_OUTSIDE return true
1407 #define WHEN_INSIDE
1408  int temp;
1409 
1410  if( aSegStart.x > aSegEnd.x )
1411  std::swap( aSegStart, aSegEnd );
1412 
1413  if( (aSegEnd.x <= m_xcliplo) || (aSegStart.x >= m_xcliphi) )
1414  {
1415  WHEN_OUTSIDE;
1416  }
1417 
1418  if( aSegStart.y < aSegEnd.y )
1419  {
1420  if( (aSegEnd.y <= m_ycliplo) || (aSegStart.y >= m_ycliphi) )
1421  {
1422  WHEN_OUTSIDE;
1423  }
1424 
1425  if( aSegStart.y < m_ycliplo )
1426  {
1427  temp = USCALE( (aSegEnd.x - aSegStart.x), (m_ycliplo - aSegStart.y),
1428  (aSegEnd.y - aSegStart.y) );
1429 
1430  if( (aSegStart.x += temp) >= m_xcliphi )
1431  {
1432  WHEN_OUTSIDE;
1433  }
1434 
1435  aSegStart.y = m_ycliplo;
1436  WHEN_INSIDE;
1437  }
1438 
1439  if( aSegEnd.y > m_ycliphi )
1440  {
1441  temp = USCALE( (aSegEnd.x - aSegStart.x), (aSegEnd.y - m_ycliphi),
1442  (aSegEnd.y - aSegStart.y) );
1443 
1444  if( (aSegEnd.x -= temp) <= m_xcliplo )
1445  {
1446  WHEN_OUTSIDE;
1447  }
1448 
1449  aSegEnd.y = m_ycliphi;
1450  WHEN_INSIDE;
1451  }
1452 
1453  if( aSegStart.x < m_xcliplo )
1454  {
1455  temp = USCALE( (aSegEnd.y - aSegStart.y), (m_xcliplo - aSegStart.x),
1456  (aSegEnd.x - aSegStart.x) );
1457  aSegStart.y += temp;
1458  aSegStart.x = m_xcliplo;
1459  WHEN_INSIDE;
1460  }
1461 
1462  if( aSegEnd.x > m_xcliphi )
1463  {
1464  temp = USCALE( (aSegEnd.y - aSegStart.y), (aSegEnd.x - m_xcliphi),
1465  (aSegEnd.x - aSegStart.x) );
1466  aSegEnd.y -= temp;
1467  aSegEnd.x = m_xcliphi;
1468  WHEN_INSIDE;
1469  }
1470  }
1471  else
1472  {
1473  if( (aSegStart.y <= m_ycliplo) || (aSegEnd.y >= m_ycliphi) )
1474  {
1475  WHEN_OUTSIDE;
1476  }
1477 
1478  if( aSegStart.y > m_ycliphi )
1479  {
1480  temp = USCALE( (aSegEnd.x - aSegStart.x), (aSegStart.y - m_ycliphi),
1481  (aSegStart.y - aSegEnd.y) );
1482 
1483  if( (aSegStart.x += temp) >= m_xcliphi )
1484  {
1485  WHEN_OUTSIDE;
1486  }
1487 
1488  aSegStart.y = m_ycliphi;
1489  WHEN_INSIDE;
1490  }
1491 
1492  if( aSegEnd.y < m_ycliplo )
1493  {
1494  temp = USCALE( (aSegEnd.x - aSegStart.x), (m_ycliplo - aSegEnd.y),
1495  (aSegStart.y - aSegEnd.y) );
1496 
1497  if( (aSegEnd.x -= temp) <= m_xcliplo )
1498  {
1499  WHEN_OUTSIDE;
1500  }
1501 
1502  aSegEnd.y = m_ycliplo;
1503  WHEN_INSIDE;
1504  }
1505 
1506  if( aSegStart.x < m_xcliplo )
1507  {
1508  temp = USCALE( (aSegStart.y - aSegEnd.y), (m_xcliplo - aSegStart.x),
1509  (aSegEnd.x - aSegStart.x) );
1510  aSegStart.y -= temp;
1511  aSegStart.x = m_xcliplo;
1512  WHEN_INSIDE;
1513  }
1514 
1515  if( aSegEnd.x > m_xcliphi )
1516  {
1517  temp = USCALE( (aSegStart.y - aSegEnd.y), (aSegEnd.x - m_xcliphi),
1518  (aSegEnd.x - aSegStart.x) );
1519  aSegEnd.y += temp;
1520  aSegEnd.x = m_xcliphi;
1521  WHEN_INSIDE;
1522  }
1523  }
1524 
1525  // Do not divide here to avoid rounding errors
1526  if( ( (aSegEnd.x + aSegStart.x) < m_xcliphi * 2 )
1527  && ( (aSegEnd.x + aSegStart.x) > m_xcliplo * 2) \
1528  && ( (aSegEnd.y + aSegStart.y) < m_ycliphi * 2 )
1529  && ( (aSegEnd.y + aSegStart.y) > m_ycliplo * 2 ) )
1530  {
1531  return false;
1532  }
1533  else
1534  {
1535  return true;
1536  }
1537 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:123
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Function AllCuMask returns a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:676
#define DRCE_TOO_SMALL_MICROVIA_DRILL
Too small micro via drill.
Definition: drc.h:76
static int USCALE(unsigned arg, unsigned num, unsigned den)
Class ZONE_CONTAINER handles a list of polygons defining a copper zone.
Definition: class_zone.h:57
void LayerPair(PCB_LAYER_ID *top_layer, PCB_LAYER_ID *bottom_layer) const
Function LayerPair Return the 2 layers used by the via (the via actually uses all layers between thes...
#define DRCE_TOO_SMALL_VIA
Too small via size.
Definition: drc.h:73
bool poly2polyDRC(wxPoint *aTref, int aTrefCount, wxPoint *aTtest, int aTtestCount, int aDist)
compare 2 convex polygons and return true if distance > aDist (if no error DRC) i....
int GetNetCode() const
Function GetNetCode.
int OutlineCount() const
Returns the number of outlines in the set
std::function< SEARCH_RESULT(EDA_ITEM *aItem, void *aTestData) > INSPECTOR_FUNC
Typedef INSPECTOR is used to inspect and possibly collect the (search) results of iterating over a li...
Definition: base_struct.h:83
static const int dist[10][10]
Definition: ar_matrix.cpp:320
#define WHEN_OUTSIDE
BOARD * board() const
static int KiROUND(double v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: common.h:115
SEGMENT_ITERATOR IterateSegmentsWithHoles()
Returns an iterator object, for all outlines in the set (with holes)
int m_ycliplo
Definition: drc.h:232
COMMIT & Add(EDA_ITEM *aItem)
Adds a new item to the model
Definition: commit.h:78
const wxPoint & GetStart() const
Definition: class_track.h:109
void TransformRoundChamferedRectToPolygon(SHAPE_POLY_SET &aCornerBuffer, const wxPoint &aPosition, const wxSize &aSize, double aRotation, int aCornerRadius, double aChamferRatio, int aChamferCorners, int aApproxErrorMax, int aMinSegPerCircleCount)
convert a rectangle with rounded corners and/or chamfered corners to a polygon Convert rounded corner...
VECTOR2I::extended_type ecoord
Definition: seg.h:39
#define DRCE_TRACK_ENDS4
2 parallel track segments too close: fine end point test
Definition: drc.h:56
int m_ycliphi
Definition: drc.h:234
bool poly2segmentDRC(wxPoint *aTref, int aTrefCount, wxPoint aSegStart, wxPoint aSegEnd, int aDist)
#define DRCE_TRACK_NEAR_PAD
pad too close to track
Definition: drc.h:49
int m_segmLength
Definition: drc.h:226
void SetPosition(const wxPoint &aPos) override
Definition: class_pad.h:224
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Function GetDesignSettings.
Definition: class_board.h:540
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:229
void NORMALIZE_ANGLE_POS(T &Angle)
Definition: trigo.h:252
VECTOR2< int > VECTOR2I
Definition: vector2d.h:587
int PointCount() const
Function PointCount()
wxPoint m_padToTestPos
Definition: drc.h:217
bool doTrackDrc(TRACK *aRefSeg, TRACKS::iterator aStartIt, TRACKS::iterator aEndIt, bool aTestZones)
Test the current segment.
#define abs(a)
Definition: auxiliary.h:84
#define DRCE_TRACK_ENDS2
2 parallel track segments too close: fine start point test
Definition: drc.h:54
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
#define DRCE_TRACK_SEGMENTS_TOO_CLOSE
2 parallel track segments too close: segm ends between segref ends
Definition: drc.h:57
Definitions for tracks, vias and zones.
#define DRCE_BURIED_VIA_NOT_ALLOWED
buried vias are not allowed
Definition: drc.h:95
#define DRCE_TRACK_NEAR_VIA
track too close to via
Definition: drc.h:50
int GetChamferPositions() const
has meaning only for chamfered rect pads
Definition: class_pad.h:693
BOARD * m_pcb
Definition: drc.h:237
#define DRCE_TOO_SMALL_MICROVIA
Too small micro via size.
Definition: drc.h:74
bool checkLine(wxPoint aSegStart, wxPoint aSegEnd)
Function checkLine (helper function used in drc calculations to see if one track is in contact with a...
#define DRCE_VIA_HOLE_BIGGER
via's hole is bigger than its diameter
Definition: drc.h:65
const VECTOR2I & CPoint(int aIndex) const
Function CPoint()
Markers used to show a drc problem on boards.
PCB_LAYER_ID
A quick note on layer IDs:
void addMarkerToPcb(MARKER_PCB *aMarker)
Adds a DRC marker to the PCB through the COMMIT mechanism.
Definition: drc.cpp:164
Class LSET is a set of PCB_LAYER_IDs.
const wxPoint GetPosition() const override
Definition: class_track.h:100
double ArcTangente(int dy, int dx)
Definition: trigo.cpp:183
int GetDrillValue() const
Function GetDrillValue "calculates" the drill value for vias (m-Drill if > 0, or default drill value ...
MODULES & Modules()
Definition: class_board.h:236
#define DRCE_MICRO_VIA_INCORRECT_LAYER_PAIR
micro via's layer pair incorrect (layers must be adjacent)
Definition: drc.h:66
Class SHAPE_POLY_SET.
int m_TrackMinWidth
track min value for width ((min copper size value
#define DRCE_TRACK_ENDS1
2 parallel track segments too close: fine start point test
Definition: drc.h:53
int m_ViasMinSize
vias (not micro vias) min diameter
double GetChamferRectRatio() const
has meaning only for chamfered rect pads
Definition: class_pad.h:670
MARKER_PCB * NewMarker(TRACK *aTrack, BOARD_ITEM *aConflitItem, const SEG &aConflictSeg, int aErrorCode) const
Creates a marker on a track, via or pad.
virtual BOARD * GetBoard() const
Function GetBoard returns the BOARD in which this BOARD_ITEM resides, or NULL if none.
#define DRCE_TRACK_ENDS3
2 parallel track segments too close: fine end point test
Definition: drc.h:55
#define DRCE_ENDS_PROBLEM2
track ends are too close
Definition: drc.h:60
int m_ViasMinDrill
vias (not micro vias) min drill diameter
#define DRCE_TRACK_NEAR_ZONE
track & zone collide or are too close together
Definition: drc.h:89
#define DRCE_ENDS_PROBLEM3
track ends are too close
Definition: drc.h:61
VIATYPE_T GetViaType() const
Definition: class_track.h:346
void SetSize(const wxSize &aSize)
Definition: class_pad.h:283
static bool checkMarginToCircle(wxPoint aCentre, int aRadius, int aLength)
Check the distance from a point to a segment.
int GetBoundingRadius() const
Function GetBoundingRadius returns the radius of a minimum sized circle which fully encloses this pad...
Definition: class_pad.h:622
bool m_BlindBuriedViaAllowed
true to allow blind/buried vias
bool TestPointInsidePolygon(const wxPoint *aPolysList, int aCount, const wxPoint &aRefPoint)
Function TestPointInsidePolygon (overlaid) same as previous, but mainly use wxPoint.
bool m_reportAllTrackErrors
Definition: drc.h:206
static bool intersect(const SEGMENT_WITH_NORMALS &aSeg, const SFVEC2F &aStart, const SFVEC2F &aEnd)
Definition: cpolygon2d.cpp:293
const wxSize & GetDelta() const
Definition: class_pad.h:287
virtual int GetClearance(BOARD_CONNECTED_ITEM *aItem=NULL) const override
Function GetClearance returns the clearance in internal units.
int m_xcliphi
Definition: drc.h:233
int m_MicroViasMinSize
micro vias (not vias) min diameter
PCB_EDIT_FRAME * m_pcbEditorFrame
The pcb frame editor which owns the board.
Definition: drc.h:236
string & err
Definition: json11.cpp:598
const wxPoint GetPosition() const override
Definition: class_track.h:319
#define DRCE_ENDS_PROBLEM4
track ends are too close
Definition: drc.h:62
bool checkClearancePadToPad(D_PAD *aRefPad, D_PAD *aPad)
void BuildPadPolygon(wxPoint aCoord[4], wxSize aInflateValue, double aRotation) const
Function BuildPadPolygon Has meaning only for polygonal pads (trapezoid and rectangular) Build the Co...
Definition: seg.h:36
#define DRCE_VIA_NEAR_VIA
via too close to via
Definition: drc.h:51
#define WHEN_INSIDE
#define DRCE_TRACKS_CROSSING
tracks are crossing
Definition: drc.h:58
bool checkClearanceSegmToPad(const D_PAD *aPad, int aSegmentWidth, int aMinDist)
Check the distance from a pad to segment.
#define DRCE_ENDS_PROBLEM5
track ends are too close
Definition: drc.h:63
wxPoint m_segmEnd
Definition: drc.h:218
void SetLayerSet(LSET aLayerMask)
Definition: class_pad.h:425
SHAPE_POLY_SET m_board_outlines
The board outline including cutouts.
Definition: drc.h:238
ZONE_CONTAINERS & Zones()
Definition: class_board.h:250
int GetWidth() const
Definition: class_track.h:103
Class to handle a graphic segment.
#define max(a, b)
Definition: auxiliary.h:86
Class SHAPE_LINE_CHAIN.
double GetOrientation() const
Function GetOrientation returns the rotation angle of the pad in tenths of degrees,...
Definition: class_pad.h:406
void CustomShapeAsPolygonToBoardPosition(SHAPE_POLY_SET *aMergedPolygon, wxPoint aPosition, double aRotation) const
When created, the corners coordinates are relative to the pad position, orientation 0,...
std::shared_ptr< NETCLASS > GetNetClass() const
Function GetNetClass returns the NETCLASS for this item.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
wxPoint ShapePos() const
Definition: class_pad.cpp:570
const SHAPE_POLY_SET & GetCustomShapeAsPolygon() const
Accessor to the custom shape as one polygon.
Definition: class_pad.h:358
#define DRCE_VIA_NEAR_TRACK
via too close to track
Definition: drc.h:52
double DECIDEG2RAD(double deg)
Definition: trigo.h:214
bool SegmentIntersectsSegment(const wxPoint &a_p1_l1, const wxPoint &a_p2_l1, const wxPoint &a_p1_l2, const wxPoint &a_p2_l2, wxPoint *aIntersectionPoint)
Function SegmentIntersectsSegment.
Definition: trigo.cpp:58
void GetRoundRectCornerCenters(wxPoint aCenters[4], int aRadius, const wxPoint &aPosition, const wxSize &aSize, double aRotation)
Helper function GetRoundRectCornerCenters Has meaning only for rounded rect Returns the centers of th...
Class EDA_ITEM is a base class for most all the KiCad significant classes, used in schematics and boa...
Definition: base_struct.h:163
int GetClearance(BOARD_CONNECTED_ITEM *aItem=NULL) const override
Function GetClearance returns the clearance in internal units.
Definition: class_pad.cpp:596
virtual void Push(const wxString &aMessage=wxT("A commit"), bool aCreateUndoEntry=true, bool aSetDirtyBit=true) override
Executes the changes.
void SetShape(PAD_SHAPE_T aShape)
Definition: class_pad.h:222
const wxPoint & GetEnd() const
Definition: class_track.h:106
int GetRoundRectCornerRadius() const
Function GetRoundRectCornerRadius Has meaning only for rounded rect pads.
Definition: class_pad.h:546
void SetOrientation(double aAngle)
Function SetOrientation sets the rotation angle of the pad.
Definition: class_pad.cpp:430
bool HitTest(const wxPoint &aPosition, int aAccuracy=0) const override
Function HitTest tests if aPosition is contained within or on the bounding box of an item.
class VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
PAD_SHAPE_T GetShape() const
Function GetShape.
Definition: class_pad.h:221
Module description (excepted pads)
#define DRCE_TRACK_NEAR_EDGE
track too close to board edge
Definition: drc.h:98
bool m_MicroViasAllowed
true to allow micro vias
#define DRCE_MICRO_VIA_NOT_ALLOWED
micro vias are not allowed
Definition: drc.h:94
int m_MicroViasMinDrill
micro vias (not vias) min drill diameter
const wxSize & GetSize() const
Definition: class_pad.h:284
DRC_MARKER_FACTORY m_markerFactory
Class that generates markers.
Definition: drc.h:240
const wxPoint GetPosition() const override
Definition: class_pad.h:225
class DRAWSEGMENT, a segment not on copper layers
Definition: typeinfo.h:91
void Rotate(double aAngle, const VECTOR2I &aCenter)
Function Rotate rotates all vertices by a given angle.
int m_xcliplo
Definition: drc.h:231
virtual PCB_LAYER_ID GetLayer() const
Function GetLayer returns the primary layer this item is on.
int GetCopperLayerCount() const
Function GetCopperLayerCount.
#define DRCE_ENDS_PROBLEM1
track ends are too close
Definition: drc.h:59
DRAWINGS & Drawings()
Definition: class_board.h:245
#define mod(a, n)
Definition: greymap.cpp:24
#define DRCE_TOO_SMALL_VIA_DRILL
Too small via drill.
Definition: drc.h:75
#define DRCE_TRACK_NEAR_THROUGH_HOLE
thru hole is too close to track
Definition: drc.h:48
double m_segmAngle
Definition: drc.h:225
void SetPosition(const wxPoint &aPos) override
bool TestForIntersectionOfStraightLineSegments(int x1i, int y1i, int x1f, int y1f, int x2i, int y2i, int x2f, int y2f, int *x=NULL, int *y=NULL, double *dist=NULL)
Function TestForIntersectionOfStraightLineSegments Test for intersection of line segments If lines ar...
virtual LSET GetLayerSet() const
Function GetLayerSet returns a "layer mask", which is a bitmap of all layers on which the TRACK segme...
KICAD_T Type() const
Function Type()
Definition: base_struct.h:210
#define min(a, b)
Definition: auxiliary.h:85
Class BOARD_DESIGN_SETTINGS contains design settings for a BOARD object.
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)
#define DRCE_TOO_SMALL_TRACK_WIDTH
Too small track width.
Definition: drc.h:72