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