KiCad PCB EDA Suite
drc_clearance_test_functions.cpp
Go to the documentation of this file.
1 
5 /*
6  * This program source code file is part of KiCad, a free EDA CAD application.
7  *
8  * Copyright (C) 2004-2017 Jean-Pierre Charras, jp.charras at wanadoo.fr
9  * Copyright (C) 2007 Dick Hollenbeck, dick@softplc.com
10  * Copyright (C) 2017 KiCad Developers, see change_log.txt for contributors.
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, you may find one here:
24  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
25  * or you may search the http://www.gnu.org website for the version 2 license,
26  * or you may write to the Free Software Foundation, Inc.,
27  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
28  */
29 
34 #include <fctsys.h>
35 #include <wxPcbStruct.h>
36 #include <trigo.h>
37 
38 #include <pcbnew.h>
39 #include <drc_stuff.h>
40 
41 #include <class_board.h>
42 #include <class_module.h>
43 #include <class_track.h>
44 #include <class_zone.h>
45 #include <class_marker_pcb.h>
46 #include <math_for_graphics.h>
49 
50 
51 /* compare 2 convex polygons and return true if distance > aDist
52  * i.e if for each edge of the first polygon distance from each edge of the other polygon
53  * is >= aDist
54  */
55 bool poly2polyDRC( wxPoint* aTref, int aTrefCount,
56  wxPoint* aTcompare, int aTcompareCount, int aDist )
57 {
58  /* Test if one polygon is contained in the other and thus the polygon overlap.
59  * This case is not covered by the following check if one polygone is
60  * completely contained in the other (because edges don't intersect)!
61  */
62  if( TestPointInsidePolygon( aTref, aTrefCount, aTcompare[0] ) )
63  return false;
64 
65  if( TestPointInsidePolygon( aTcompare, aTcompareCount, aTref[0] ) )
66  return false;
67 
68  for( int ii = 0, jj = aTrefCount - 1; ii < aTrefCount; jj = ii, ii++ )
69  { // for all edges in aTref
70  for( int kk = 0, ll = aTcompareCount - 1; kk < aTcompareCount; ll = kk, kk++ )
71  { // for all edges in aTcompare
72  double d;
74  aTref[ii].x, aTref[ii].y, aTref[jj].x, aTref[jj].y,
75  aTcompare[kk].x, aTcompare[kk].y, aTcompare[ll].x, aTcompare[ll].y,
76  NULL, NULL, &d );
77 
78  if( intersect || ( d < aDist ) )
79  return false;
80  }
81  }
82 
83  return true;
84 }
85 
86 /* compare a trapezoids (can be rectangle) and a segment and return true if distance > aDist
87  */
88 bool poly2segmentDRC( wxPoint* aTref, int aTrefCount, wxPoint aSegStart, wxPoint aSegEnd, int aDist )
89 {
90  /* Test if the segment is contained in the polygon.
91  * This case is not covered by the following check if the segment is
92  * completely contained in the polygon (because edges don't intersect)!
93  */
94  if( TestPointInsidePolygon( aTref, aTrefCount, aSegStart ) )
95  return false;
96 
97  for( int ii = 0, jj = aTrefCount-1; ii < aTrefCount; jj = ii, ii++ )
98  { // for all edges in polygon
99  double d;
101  aTref[ii].x, aTref[ii].y, aTref[jj].x, aTref[jj].y,
102  aSegStart.x, aSegStart.y, aSegEnd.x, aSegEnd.y,
103  NULL, NULL, &d );
104 
105  if( intersect || ( d < aDist) )
106  return false;
107  }
108 
109  return true;
110 }
111 
112 /* compare a polygon to a point and return true if distance > aDist
113  * do not use this function for horizontal or vertical rectangles
114  * because there is a faster an easier way to compare the distance
115  */
116 bool convex2pointDRC( wxPoint* aTref, int aTrefCount, wxPoint aPcompare, int aDist )
117 {
118  /* Test if aPcompare point is contained in the polygon.
119  * This case is not covered by the following check if this point is inside the polygon
120  */
121  if( TestPointInsidePolygon( aTref, aTrefCount, aPcompare ) )
122  {
123  return false;
124  }
125 
126  // Test distance between aPcompare and each segment of the polygon:
127  for( int ii = 0, jj = aTrefCount - 1; ii < aTrefCount; jj = ii, ii++ ) // for all edge in polygon
128  {
129  if( TestSegmentHit( aPcompare, aTref[ii], aTref[jj], aDist ) )
130  return false;
131  }
132 
133  return true;
134 }
135 
136 
137 bool DRC::doTrackDrc( TRACK* aRefSeg, TRACK* aStart, bool testPads )
138 {
139  TRACK* track;
140  wxPoint delta; // length on X and Y axis of segments
141  LSET layerMask;
142  int net_code_ref;
143  wxPoint shape_pos;
144 
145  NETCLASSPTR netclass = aRefSeg->GetNetClass();
147 
148  /* In order to make some calculations more easier or faster,
149  * pads and tracks coordinates will be made relative to the reference segment origin
150  */
151  wxPoint origin = aRefSeg->GetStart(); // origin will be the origin of other coordinates
152 
153  m_segmEnd = delta = aRefSeg->GetEnd() - origin;
154  m_segmAngle = 0;
155 
156  layerMask = aRefSeg->GetLayerSet();
157  net_code_ref = aRefSeg->GetNetCode();
158 
159  // Phase 0 : Test vias
160  if( aRefSeg->Type() == PCB_VIA_T )
161  {
162  const VIA *refvia = static_cast<const VIA*>( aRefSeg );
163  // test if the via size is smaller than minimum
164  if( refvia->GetViaType() == VIA_MICROVIA )
165  {
166  if( refvia->GetWidth() < dsnSettings.m_MicroViasMinSize )
167  {
168  m_currentMarker = fillMarker( refvia, NULL,
170  return false;
171  }
172  if( refvia->GetDrillValue() < dsnSettings.m_MicroViasMinDrill )
173  {
174  m_currentMarker = fillMarker( refvia, NULL,
176  return false;
177  }
178  }
179  else
180  {
181  if( refvia->GetWidth() < dsnSettings.m_ViasMinSize )
182  {
183  m_currentMarker = fillMarker( refvia, NULL,
185  return false;
186  }
187  if( refvia->GetDrillValue() < dsnSettings.m_ViasMinDrill )
188  {
189  m_currentMarker = fillMarker( refvia, NULL,
191  return false;
192  }
193  }
194 
195  // test if via's hole is bigger than its diameter
196  // This test is necessary since the via hole size and width can be modified
197  // and a default via hole can be bigger than some vias sizes
198  if( refvia->GetDrillValue() > refvia->GetWidth() )
199  {
200  m_currentMarker = fillMarker( refvia, NULL,
202  return false;
203  }
204 
205  // For microvias: test if they are blind vias and only between 2 layers
206  // because they are used for very small drill size and are drill by laser
207  // and **only one layer** can be drilled
208  if( refvia->GetViaType() == VIA_MICROVIA )
209  {
210  PCB_LAYER_ID layer1, layer2;
211  bool err = true;
212 
213  refvia->LayerPair( &layer1, &layer2 );
214 
215  if( layer1 > layer2 )
216  std::swap( layer1, layer2 );
217 
218  if( layer2 == B_Cu && layer1 == m_pcb->GetDesignSettings().GetCopperLayerCount() - 2 )
219  err = false;
220  else if( layer1 == F_Cu && layer2 == In1_Cu )
221  err = false;
222 
223  if( err )
224  {
225  m_currentMarker = fillMarker( refvia, NULL,
227  return false;
228  }
229  }
230  }
231  else // This is a track segment
232  {
233  if( aRefSeg->GetWidth() < dsnSettings.m_TrackMinWidth )
234  {
235  m_currentMarker = fillMarker( aRefSeg, NULL,
237  return false;
238  }
239  }
240 
241  // for a non horizontal or vertical segment Compute the segment angle
242  // in tenths of degrees and its length
243  if( delta.x || delta.y )
244  {
245  // Compute the segment angle in 0,1 degrees
246  m_segmAngle = ArcTangente( delta.y, delta.x );
247 
248  // Compute the segment length: we build an equivalent rotated segment,
249  // this segment is horizontal, therefore dx = length
250  RotatePoint( &delta, m_segmAngle ); // delta.x = length, delta.y = 0
251  }
252 
253  m_segmLength = delta.x;
254 
255  /******************************************/
256  /* Phase 1 : test DRC track to pads : */
257  /******************************************/
258 
259  /* Use a dummy pad to test DRC tracks versus holes, for pads not on all copper layers
260  * but having a hole
261  * This dummy pad has the size and shape of the hole
262  * to test tracks to pad hole DRC, using checkClearanceSegmToPad test function.
263  * Therefore, this dummy pad is a circle or an oval.
264  * A pad must have a parent because some functions expect a non null parent
265  * to find the parent board, and some other data
266  */
267  MODULE dummymodule( m_pcb ); // Creates a dummy parent
268  D_PAD dummypad( &dummymodule );
269 
270  dummypad.SetLayerSet( LSET::AllCuMask() ); // Ensure the hole is on all layers
271 
272  // Compute the min distance to pads
273  if( testPads )
274  {
275  unsigned pad_count = m_pcb->GetPadCount();
276 
277  auto pads = m_pcb->GetPads();
278 
279  for( unsigned ii = 0; ii<pad_count; ++ii )
280  {
281  D_PAD* pad = pads[ii];
282 
283  /* No problem if pads are on an other layer,
284  * But if a drill hole exists (a pad on a single layer can have a hole!)
285  * we must test the hole
286  */
287  if( !( pad->GetLayerSet() & layerMask ).any() )
288  {
289  /* We must test the pad hole. In order to use the function
290  * checkClearanceSegmToPad(),a pseudo pad is used, with a shape and a
291  * size like the hole
292  */
293  if( pad->GetDrillSize().x == 0 )
294  continue;
295 
296  dummypad.SetSize( pad->GetDrillSize() );
297  dummypad.SetPosition( pad->GetPosition() );
298  dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ?
300  dummypad.SetOrientation( pad->GetOrientation() );
301 
302  m_padToTestPos = dummypad.GetPosition() - origin;
303 
304  if( !checkClearanceSegmToPad( &dummypad, aRefSeg->GetWidth(),
305  netclass->GetClearance() ) )
306  {
307  m_currentMarker = fillMarker( aRefSeg, pad,
309  return false;
310  }
311 
312  continue;
313  }
314 
315  // The pad must be in a net (i.e pt_pad->GetNet() != 0 )
316  // but no problem if the pad netcode is the current netcode (same net)
317  if( pad->GetNetCode() // the pad must be connected
318  && net_code_ref == pad->GetNetCode() ) // the pad net is the same as current net -> Ok
319  continue;
320 
321  // DRC for the pad
322  shape_pos = pad->ShapePos();
323  m_padToTestPos = shape_pos - origin;
324 
325  if( !checkClearanceSegmToPad( pad, aRefSeg->GetWidth(), aRefSeg->GetClearance( pad ) ) )
326  {
327  m_currentMarker = fillMarker( aRefSeg, pad,
329  return false;
330  }
331  }
332  }
333 
334  /***********************************************/
335  /* Phase 2: test DRC with other track segments */
336  /***********************************************/
337 
338  // At this point the reference segment is the X axis
339 
340  // Test the reference segment with other track segments
341  wxPoint segStartPoint;
342  wxPoint segEndPoint;
343  for( track = aStart; track; track = track->Next() )
344  {
345  // No problem if segments have the same net code:
346  if( net_code_ref == track->GetNetCode() )
347  continue;
348 
349  // No problem if segment are on different layers :
350  if( !( layerMask & track->GetLayerSet() ).any() )
351  continue;
352 
353  // the minimum distance = clearance plus half the reference track
354  // width plus half the other track's width
355  int w_dist = aRefSeg->GetClearance( track );
356  w_dist += (aRefSeg->GetWidth() + track->GetWidth()) / 2;
357 
358  // Due to many double to int conversions during calculations, which
359  // create rounding issues,
360  // the exact clearance margin cannot be really known.
361  // To avoid false bad DRC detection due to these rounding issues,
362  // slightly decrease the w_dist (remove one nanometer is enough !)
363  w_dist -= 1;
364 
365  // If the reference segment is a via, we test it here
366  if( aRefSeg->Type() == PCB_VIA_T )
367  {
368  delta = track->GetEnd() - track->GetStart();
369  segStartPoint = aRefSeg->GetStart() - track->GetStart();
370 
371  if( track->Type() == PCB_VIA_T )
372  {
373  // Test distance between two vias, i.e. two circles, trivial case
374  if( EuclideanNorm( segStartPoint ) < w_dist )
375  {
376  m_currentMarker = fillMarker( aRefSeg, track,
378  return false;
379  }
380  }
381  else // test via to segment
382  {
383  // Compute l'angle du segment a tester;
384  double angle = ArcTangente( delta.y, delta.x );
385 
386  // Compute new coordinates ( the segment become horizontal)
387  RotatePoint( &delta, angle );
388  RotatePoint( &segStartPoint, angle );
389 
390  if( !checkMarginToCircle( segStartPoint, w_dist, delta.x ) )
391  {
392  m_currentMarker = fillMarker( track, aRefSeg,
394  return false;
395  }
396  }
397 
398  continue;
399  }
400 
401  /* We compute segStartPoint, segEndPoint = starting and ending point coordinates for
402  * the segment to test in the new axis : the new X axis is the
403  * reference segment. We must translate and rotate the segment to test
404  */
405  segStartPoint = track->GetStart() - origin;
406  segEndPoint = track->GetEnd() - origin;
407  RotatePoint( &segStartPoint, m_segmAngle );
408  RotatePoint( &segEndPoint, m_segmAngle );
409  if( track->Type() == PCB_VIA_T )
410  {
411  if( checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) )
412  continue;
413 
414  m_currentMarker = fillMarker( aRefSeg, track,
416  return false;
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 an other 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) ) /* possible error drc */
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  {
441  m_currentMarker = fillMarker( aRefSeg, track,
443  return false;
444  }
445 
446  if( !checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) )
447  {
448  m_currentMarker = fillMarker( aRefSeg, track,
450  return false;
451  }
452  }
453 
454  if( segEndPoint.x > (-w_dist) && segEndPoint.x < (m_segmLength + w_dist) )
455  {
456  // the end point is inside the reference range
457  // .....X
458  // O--REF--+
459  // Fine test : we consider the rounded shape of the ends
460  if( segEndPoint.x >= 0 && segEndPoint.x <= m_segmLength )
461  {
462  m_currentMarker = fillMarker( aRefSeg, track,
464  return false;
465  }
466 
467  if( !checkMarginToCircle( segEndPoint, w_dist, m_segmLength ) )
468  {
469  m_currentMarker = fillMarker( aRefSeg, track,
471  return false;
472  }
473  }
474 
475  if( segStartPoint.x <=0 && segEndPoint.x >= 0 )
476  {
477  // the segment straddles the reference range (this actually only
478  // checks if it straddles the origin, because the other cases where already
479  // handled)
480  // X.............X
481  // O--REF--+
482  m_currentMarker = fillMarker( aRefSeg, track,
484  return false;
485  }
486  }
487  else if( segStartPoint.x == segEndPoint.x ) // perpendicular segments
488  {
489  if( ( segStartPoint.x <= (-w_dist) ) || ( segStartPoint.x >= (m_segmLength + w_dist) ) )
490  continue;
491 
492  // Test if segments are crossing
493  if( segStartPoint.y > segEndPoint.y )
494  std::swap( segStartPoint.y, segEndPoint.y );
495 
496  if( (segStartPoint.y < 0) && (segEndPoint.y > 0) )
497  {
498  m_currentMarker = fillMarker( aRefSeg, track,
500  return false;
501  }
502 
503  // At this point the drc error is due to an end near a reference segm end
504  if( !checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) )
505  {
506  m_currentMarker = fillMarker( aRefSeg, track,
508  return false;
509  }
510  if( !checkMarginToCircle( segEndPoint, w_dist, m_segmLength ) )
511  {
512  m_currentMarker = fillMarker( aRefSeg, track,
514  return false;
515  }
516  }
517  else // segments quelconques entre eux
518  {
519  // calcul de la "surface de securite du segment de reference
520  // First rought 'and fast) test : the track segment is like a rectangle
521 
522  m_xcliplo = m_ycliplo = -w_dist;
523  m_xcliphi = m_segmLength + w_dist;
524  m_ycliphi = w_dist;
525 
526  // A fine test is needed because a serment is not exactly a
527  // rectangle, it has rounded ends
528  if( !checkLine( segStartPoint, segEndPoint ) )
529  {
530  /* 2eme passe : the track has rounded ends.
531  * we must a fine test for each rounded end and the
532  * rectangular zone
533  */
534 
535  m_xcliplo = 0;
537 
538  if( !checkLine( segStartPoint, segEndPoint ) )
539  {
540  m_currentMarker = fillMarker( aRefSeg, track,
542  return false;
543  }
544  else // The drc error is due to the starting or the ending point of the reference segment
545  {
546  // Test the starting and the ending point
547  segStartPoint = track->GetStart();
548  segEndPoint = track->GetEnd();
549  delta = segEndPoint - segStartPoint;
550 
551  // Compute the segment orientation (angle) en 0,1 degre
552  double angle = ArcTangente( delta.y, delta.x );
553 
554  // Compute the segment length: delta.x = length after rotation
555  RotatePoint( &delta, angle );
556 
557  /* Comute the reference segment coordinates relatives to a
558  * X axis = current tested segment
559  */
560  wxPoint relStartPos = aRefSeg->GetStart() - segStartPoint;
561  wxPoint relEndPos = aRefSeg->GetEnd() - segStartPoint;
562 
563  RotatePoint( &relStartPos, angle );
564  RotatePoint( &relEndPos, angle );
565 
566  if( !checkMarginToCircle( relStartPos, w_dist, delta.x ) )
567  {
568  m_currentMarker = fillMarker( aRefSeg, track,
570  return false;
571  }
572 
573  if( !checkMarginToCircle( relEndPos, w_dist, delta.x ) )
574  {
575  m_currentMarker = fillMarker( aRefSeg, track,
577  return false;
578  }
579  }
580  }
581  }
582  }
583 
584  return true;
585 }
586 
587 
588 bool DRC::doEdgeZoneDrc( ZONE_CONTAINER* aArea, int aCornerIndex )
589 {
590  if( !aArea->IsOnCopperLayer() ) // Cannot have a Drc error if not on copper layer
591  return true;
592  // Get polygon, contour and vertex index.
594 
595  // If the vertex does not exist, there is no conflict
596  if( !aArea->Outline()->GetRelativeIndices( aCornerIndex, &index ) )
597  return true;
598 
599  // Retrieve the selected contour
600  SHAPE_LINE_CHAIN contour;
601  contour = aArea->Outline()->Polygon( index.m_polygon )[index.m_contour];
602 
603  // Retrieve the segment that starts at aCornerIndex-th corner.
604  SEG selectedSegment = contour.Segment( index.m_vertex );
605 
606  VECTOR2I start = selectedSegment.A;
607  VECTOR2I end = selectedSegment.B;
608 
609  // iterate through all areas
610  for( int ia2 = 0; ia2 < m_pcb->GetAreaCount(); ia2++ )
611  {
612  ZONE_CONTAINER* area_to_test = m_pcb->GetArea( ia2 );
613  int zone_clearance = std::max( area_to_test->GetZoneClearance(),
614  aArea->GetZoneClearance() );
615 
616  // test for same layer
617  if( area_to_test->GetLayer() != aArea->GetLayer() )
618  continue;
619 
620  // Test for same net
621  if( ( aArea->GetNetCode() == area_to_test->GetNetCode() ) && (aArea->GetNetCode() >= 0) )
622  continue;
623 
624  // test for same priority
625  if( area_to_test->GetPriority() != aArea->GetPriority() )
626  continue;
627 
628  // test for same type
629  if( area_to_test->GetIsKeepout() != aArea->GetIsKeepout() )
630  continue;
631 
632  // For keepout, there is no clearance, so use a minimal value for it
633  // use 1, not 0 as value to avoid some issues in tests
634  if( area_to_test->GetIsKeepout() )
635  zone_clearance = 1;
636 
637  // test for ending line inside area_to_test
638  if( area_to_test->Outline()->Contains( end ) )
639  {
640  // COPPERAREA_COPPERAREA error: corner inside copper area
641  m_currentMarker = fillMarker( aArea, static_cast<wxPoint>( end ),
643  m_currentMarker );
644  return false;
645  }
646 
647  // now test spacing between areas
648  int ax1 = start.x;
649  int ay1 = start.y;
650  int ax2 = end.x;
651  int ay2 = end.y;
652 
653  // Iterate through all edges in the polygon.
655  for( iterator = area_to_test->Outline()->IterateSegmentsWithHoles(); iterator; iterator++ )
656  {
657  SEG segment = *iterator;
658 
659  int bx1 = segment.A.x;
660  int by1 = segment.A.y;
661  int bx2 = segment.B.x;
662  int by2 = segment.B.y;
663 
664  int x, y; // variables containing the intersecting point coordinates
665  int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2,
666  0,
667  ax1, ay1, ax2, ay2,
668  0,
669  zone_clearance,
670  &x, &y );
671 
672  if( d < zone_clearance )
673  {
674  // COPPERAREA_COPPERAREA error : edge intersect or too close
675  m_currentMarker = fillMarker( aArea, wxPoint( x, y ),
677  m_currentMarker );
678  return false;
679  }
680 
681  }
682  }
683 
684  return true;
685 }
686 
687 
688 bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
689 {
690  int dist;
691  double pad_angle;
692 
693  // Get the clearance between the 2 pads. this is the min distance between aRefPad and aPad
694  int dist_min = aRefPad->GetClearance( aPad );
695 
696  // relativePadPos is the aPad shape position relative to the aRefPad shape position
697  wxPoint relativePadPos = aPad->ShapePos() - aRefPad->ShapePos();
698 
699  dist = KiROUND( EuclideanNorm( relativePadPos ) );
700 
701  // Quick test: Clearance is OK if the bounding circles are further away than "dist_min"
702  int delta = dist - aRefPad->GetBoundingRadius() - aPad->GetBoundingRadius();
703 
704  if( delta >= dist_min )
705  return true;
706 
707  /* Here, pads are near and DRC depend on the pad shapes
708  * We must compare distance using a fine shape analysis
709  * Because a circle or oval shape is the easier shape to test, try to have
710  * aRefPad shape type = PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL.
711  * if aRefPad = TRAP. and aPad = RECT, also swap pads
712  * Swap aRefPad and aPad if needed
713  */
714  bool swap_pads;
715  swap_pads = false;
716 
717  // swap pads to make comparisons easier
718  // Note also a ROUNDRECT pad with a corner radius = r can be considered as
719  // a smaller RECT (size - 2*r) with a clearance increased by r
720  // priority is aRefPad = ROUND then OVAL then RECT/ROUNDRECT then other
721  if( aRefPad->GetShape() != aPad->GetShape() && aRefPad->GetShape() != PAD_SHAPE_CIRCLE )
722  {
723  // pad ref shape is here oval, rect, roundrect, trapezoid or custom
724  switch( aPad->GetShape() )
725  {
726  case PAD_SHAPE_CIRCLE:
727  swap_pads = true;
728  break;
729 
730  case PAD_SHAPE_OVAL:
731  swap_pads = true;
732  break;
733 
734  case PAD_SHAPE_RECT:
735  case PAD_SHAPE_ROUNDRECT:
736  if( aRefPad->GetShape() != PAD_SHAPE_OVAL )
737  swap_pads = true;
738  break;
739 
740  case PAD_SHAPE_TRAPEZOID:
741  case PAD_SHAPE_CUSTOM:
742  break;
743  }
744  }
745 
746  if( swap_pads )
747  {
748  std::swap( aRefPad, aPad );
749  relativePadPos = -relativePadPos;
750  }
751 
752  // corners of aRefPad (used only for rect/roundrect/trap pad)
753  wxPoint polyref[4];
754  // corners of aRefPad (used only for custom pad)
755  SHAPE_POLY_SET polysetref;
756 
757  // corners of aPad (used only for rect/roundrect/trap pad)
758  wxPoint polycompare[4];
759  // corners of aPad (used only custom pad)
760  SHAPE_POLY_SET polysetcompare;
761 
762  /* Because pad exchange, aRefPad shape is PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL,
763  * if one of the 2 pads was a PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL.
764  * Therefore, if aRefPad is a PAD_SHAPE_RECT, PAD_SHAPE_ROUNDRECT or a PAD_SHAPE_TRAPEZOID,
765  * aPad is also a PAD_SHAPE_RECT, PAD_SHAPE_ROUNDRECT or a PAD_SHAPE_TRAPEZOID
766  */
767  bool diag = true;
768 
769  switch( aRefPad->GetShape() )
770  {
771  case PAD_SHAPE_CIRCLE:
772 
773  /* One can use checkClearanceSegmToPad to test clearance
774  * aRefPad is like a track segment with a null length and a witdth = GetSize().x
775  */
776  m_segmLength = 0;
777  m_segmAngle = 0;
778 
779  m_segmEnd.x = m_segmEnd.y = 0;
780 
781  m_padToTestPos = relativePadPos;
782  diag = checkClearanceSegmToPad( aPad, aRefPad->GetSize().x, dist_min );
783  break;
784 
785  case PAD_SHAPE_TRAPEZOID:
786  case PAD_SHAPE_ROUNDRECT:
787  case PAD_SHAPE_RECT:
788  case PAD_SHAPE_CUSTOM:
789  // pad_angle = pad orient relative to the aRefPad orient
790  pad_angle = aRefPad->GetOrientation() + aPad->GetOrientation();
791  NORMALIZE_ANGLE_POS( pad_angle );
792 
793  if( aRefPad->GetShape() == PAD_SHAPE_ROUNDRECT )
794  {
795  int padRadius = aRefPad->GetRoundRectCornerRadius();
796  dist_min += padRadius;
797  GetRoundRectCornerCenters( polyref, padRadius, wxPoint( 0, 0 ),
798  aRefPad->GetSize(), aRefPad->GetOrientation() );
799  }
800  else if( aRefPad->GetShape() == PAD_SHAPE_CUSTOM )
801  {
802  polysetref.Append( aRefPad->GetCustomShapeAsPolygon() );
803 
804  // The reference pad can be rotated. calculate the rotated
805  // coordiantes ( note, the ref pad position is the origin of
806  // coordinates for this drc test)
807  aRefPad->CustomShapeAsPolygonToBoardPosition( &polysetref,
808  wxPoint( 0, 0 ), aRefPad->GetOrientation() );
809  }
810  else
811  {
812  // BuildPadPolygon has meaning for rect a trapeziod shapes
813  // and returns the 4 corners
814  aRefPad->BuildPadPolygon( polyref, wxSize( 0, 0 ), aRefPad->GetOrientation() );
815  }
816 
817  switch( aPad->GetShape() )
818  {
819  case PAD_SHAPE_ROUNDRECT:
820  case PAD_SHAPE_RECT:
821  case PAD_SHAPE_TRAPEZOID:
822  case PAD_SHAPE_CUSTOM:
823  if( aPad->GetShape() == PAD_SHAPE_ROUNDRECT )
824  {
825  int padRadius = aPad->GetRoundRectCornerRadius();
826  dist_min += padRadius;
827  GetRoundRectCornerCenters( polycompare, padRadius, relativePadPos,
828  aPad->GetSize(), aPad->GetOrientation() );
829  }
830  else if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
831  {
832  polysetcompare.Append( aPad->GetCustomShapeAsPolygon() );
833 
834  // The pad to compare can be rotated. calculate the rotated
835  // coordinattes ( note, the pad to compare position
836  // is the relativePadPos for this drc test
837  aPad->CustomShapeAsPolygonToBoardPosition( &polysetcompare,
838  relativePadPos, aPad->GetOrientation() );
839  }
840  else
841  {
842  aPad->BuildPadPolygon( polycompare, wxSize( 0, 0 ), aPad->GetOrientation() );
843 
844  // Move aPad shape to relativePadPos
845  for( int ii = 0; ii < 4; ii++ )
846  polycompare[ii] += relativePadPos;
847  }
848  // And now test polygons: We have 3 cases:
849  // one poly is complex and the other is basic (has only 4 corners)
850  // both polys are complex
851  // both polys are basic (have only 4 corners) the most usual case
852  if( polysetref.OutlineCount() && polysetcompare.OutlineCount() == 0)
853  {
854  const SHAPE_LINE_CHAIN& refpoly = polysetref.COutline( 0 );
855  // And now test polygons:
856  if( !poly2polyDRC( (wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(),
857  polycompare, 4, dist_min ) )
858  diag = false;
859  }
860  else if( polysetref.OutlineCount() == 0 && polysetcompare.OutlineCount())
861  {
862  const SHAPE_LINE_CHAIN& cmppoly = polysetcompare.COutline( 0 );
863  // And now test polygons:
864  if( !poly2polyDRC( (wxPoint*) &cmppoly.CPoint( 0 ), cmppoly.PointCount(),
865  polyref, 4, dist_min ) )
866  diag = false;
867  }
868  else if( polysetref.OutlineCount() && polysetcompare.OutlineCount() )
869  {
870  const SHAPE_LINE_CHAIN& refpoly = polysetref.COutline( 0 );
871  const SHAPE_LINE_CHAIN& cmppoly = polysetcompare.COutline( 0 );
872 
873  // And now test polygons:
874  if( !poly2polyDRC( (wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(),
875  (wxPoint*) &cmppoly.CPoint( 0 ), cmppoly.PointCount(), dist_min ) )
876  diag = false;
877  }
878  else if( !poly2polyDRC( polyref, 4, polycompare, 4, dist_min ) )
879  diag = false;
880  break;
881 
882  default:
883  wxLogDebug( wxT( "DRC::checkClearancePadToPad: unexpected pad shape %d" ), aPad->GetShape() );
884  break;
885  }
886  break;
887 
888  case PAD_SHAPE_OVAL: /* an oval pad is like a track segment */
889  {
890  /* Create a track segment with same dimensions as the oval aRefPad
891  * and use checkClearanceSegmToPad function to test aPad to aRefPad clearance
892  */
893  int segm_width;
894  m_segmAngle = aRefPad->GetOrientation(); // Segment orient.
895 
896  if( aRefPad->GetSize().y < aRefPad->GetSize().x ) // Build an horizontal equiv segment
897  {
898  segm_width = aRefPad->GetSize().y;
899  m_segmLength = aRefPad->GetSize().x - aRefPad->GetSize().y;
900  }
901  else // Vertical oval: build an horizontal equiv segment and rotate 90.0 deg
902  {
903  segm_width = aRefPad->GetSize().x;
904  m_segmLength = aRefPad->GetSize().y - aRefPad->GetSize().x;
905  m_segmAngle += 900;
906  }
907 
908  /* the start point must be 0,0 and currently relativePadPos
909  * is relative the center of pad coordinate */
910  wxPoint segstart;
911  segstart.x = -m_segmLength / 2; // Start point coordinate of the horizontal equivalent segment
912 
913  RotatePoint( &segstart, m_segmAngle ); // actual start point coordinate of the equivalent segment
914  // Calculate segment end position relative to the segment origin
915  m_segmEnd.x = -2 * segstart.x;
916  m_segmEnd.y = -2 * segstart.y;
917 
918  // Recalculate the equivalent segment angle in 0,1 degrees
919  // to prepare a call to checkClearanceSegmToPad()
921 
922  // move pad position relative to the segment origin
923  m_padToTestPos = relativePadPos - segstart;
924 
925  // Use segment to pad check to test the second pad:
926  diag = checkClearanceSegmToPad( aPad, segm_width, dist_min );
927  break;
928  }
929 
930  default:
931  wxMessageBox( wxT( "DRC::checkClearancePadToPad: unknown pad shape" ) );
932  break;
933  }
934 
935  return diag;
936 }
937 
938 
939 /* test if distance between a segment is > aMinDist
940  * segment start point is assumed in (0,0) and segment start point in m_segmEnd
941  * and its orientation is m_segmAngle (m_segmAngle must be already initialized)
942  * and have aSegmentWidth.
943  */
944 bool DRC::checkClearanceSegmToPad( const D_PAD* aPad, int aSegmentWidth, int aMinDist )
945 {
946  // Note:
947  // we are using a horizontal segment for test, because we know here
948  // only the length and orientation+ of the segment
949  // Therefore the coordinates of the shape of pad to compare
950  // must be calculated in a axis system rotated by m_segmAngle
951  // and centered to the segment origin, before they can be tested
952  // against the segment
953  // We are using:
954  // m_padToTestPos the position of the pad shape in this axis system
955  // m_segmAngle the axis system rotation
956 
957  int segmHalfWidth = aSegmentWidth / 2;
958  int distToLine = segmHalfWidth + aMinDist;
959 
960  wxSize padHalfsize; // half dimension of the pad
961 
962  if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
963  {
964  // For a custom pad, the pad size has no meaning, we only can
965  // use the bounding radius
966  padHalfsize.x = padHalfsize.y = aPad->GetBoundingRadius();
967  }
968  else
969  {
970  padHalfsize.x = aPad->GetSize().x >> 1;
971  padHalfsize.y = aPad->GetSize().y >> 1;
972  }
973 
974  if( aPad->GetShape() == PAD_SHAPE_TRAPEZOID ) // The size is bigger, due to GetDelta() extra size
975  {
976  padHalfsize.x += std::abs(aPad->GetDelta().y) / 2; // Remember: GetDelta().y is the GetSize().x change
977  padHalfsize.y += std::abs(aPad->GetDelta().x) / 2; // Remember: GetDelta().x is the GetSize().y change
978  }
979 
980  if( aPad->GetShape() == PAD_SHAPE_CIRCLE )
981  {
982  /* Easy case: just test the distance between segment and pad centre
983  * calculate pad coordinates in the X,Y axis with X axis = segment to test
984  */
986  return checkMarginToCircle( m_padToTestPos, distToLine + padHalfsize.x, m_segmLength );
987  }
988 
989  /* calculate the bounding box of the pad, including the clearance and the segment width
990  * if the line from 0 to m_segmEnd does not intersect this bounding box,
991  * the clearance is always OK
992  * But if intersect, a better analysis of the pad shape must be done.
993  */
994  m_xcliplo = m_padToTestPos.x - distToLine - padHalfsize.x;
995  m_ycliplo = m_padToTestPos.y - distToLine - padHalfsize.y;
996  m_xcliphi = m_padToTestPos.x + distToLine + padHalfsize.x;
997  m_ycliphi = m_padToTestPos.y + distToLine + padHalfsize.y;
998 
999  wxPoint startPoint;
1000  wxPoint endPoint = m_segmEnd;
1001 
1002  double orient = aPad->GetOrientation();
1003 
1004  RotatePoint( &startPoint, m_padToTestPos, -orient );
1005  RotatePoint( &endPoint, m_padToTestPos, -orient );
1006 
1007  if( checkLine( startPoint, endPoint ) )
1008  return true;
1009 
1010  /* segment intersects the bounding box. But there is not always a DRC error.
1011  * A fine analysis of the pad shape must be done.
1012  */
1013  switch( aPad->GetShape() )
1014  {
1015  case PAD_SHAPE_CIRCLE:
1016  // This case was already tested, so it cannot be found here.
1017  // it is here just to avoid compil warning, and to remember
1018  // it is already tested.
1019  return false;
1020 
1021  case PAD_SHAPE_OVAL:
1022  {
1023  /* an oval is a complex shape, but is a rectangle and 2 circles
1024  * these 3 basic shapes are more easy to test.
1025  *
1026  * In calculations we are using a vertical oval shape
1027  * (i.e. a vertical rounded segment)
1028  * for horizontal oval shapes, swap x and y size and rotate the shape
1029  */
1030  if( padHalfsize.x > padHalfsize.y )
1031  {
1032  std::swap( padHalfsize.x, padHalfsize.y );
1033  orient = AddAngles( orient, 900 );
1034  }
1035 
1036  // here, padHalfsize.x is the radius of rounded ends.
1037 
1038  int deltay = padHalfsize.y - padHalfsize.x;
1039  // here: padHalfsize.x = radius,
1040  // deltay = dist between the centre pad and the centre of a rounded end
1041 
1042  // Test the rectangular area between the two circles (the rounded ends)
1043  m_xcliplo = m_padToTestPos.x - distToLine - padHalfsize.x;
1044  m_ycliplo = m_padToTestPos.y - deltay;
1045  m_xcliphi = m_padToTestPos.x + distToLine + padHalfsize.x;
1046  m_ycliphi = m_padToTestPos.y + deltay;
1047 
1048  if( !checkLine( startPoint, endPoint ) )
1049  {
1050  return false;
1051  }
1052 
1053  // test the first circle
1054  startPoint.x = m_padToTestPos.x; // startPoint = centre of the upper circle of the oval shape
1055  startPoint.y = m_padToTestPos.y + deltay;
1056 
1057  // Calculate the actual position of the circle, given the pad orientation:
1058  RotatePoint( &startPoint, m_padToTestPos, orient );
1059 
1060  // Calculate the actual position of the circle in the new X,Y axis:
1061  RotatePoint( &startPoint, m_segmAngle );
1062 
1063  if( !checkMarginToCircle( startPoint, padHalfsize.x + distToLine, m_segmLength ) )
1064  {
1065  return false;
1066  }
1067 
1068  // test the second circle
1069  startPoint.x = m_padToTestPos.x; // startPoint = centre of the lower circle of the oval shape
1070  startPoint.y = m_padToTestPos.y - deltay;
1071  RotatePoint( &startPoint, m_padToTestPos, orient );
1072  RotatePoint( &startPoint, m_segmAngle );
1073 
1074  if( !checkMarginToCircle( startPoint, padHalfsize.x + distToLine, m_segmLength ) )
1075  {
1076  return false;
1077  }
1078  }
1079  break;
1080 
1081  case PAD_SHAPE_ROUNDRECT:
1082  {
1083  // a round rect is a smaller rect, with a clearance augmented by the corners radius
1084  int r = aPad->GetRoundRectCornerRadius();
1085  padHalfsize.x -= r;
1086  padHalfsize.y -= r;
1087  distToLine += r;
1088  }
1089  // Fall through
1090  case PAD_SHAPE_RECT:
1091  // the area to test is a rounded rectangle.
1092  // this can be done by testing 2 rectangles and 4 circles (the corners)
1093 
1094  // Testing the first rectangle dimx + distToLine, dimy:
1095  m_xcliplo = m_padToTestPos.x - padHalfsize.x - distToLine;
1096  m_ycliplo = m_padToTestPos.y - padHalfsize.y;
1097  m_xcliphi = m_padToTestPos.x + padHalfsize.x + distToLine;
1098  m_ycliphi = m_padToTestPos.y + padHalfsize.y;
1099 
1100  if( !checkLine( startPoint, endPoint ) )
1101  return false;
1102 
1103  // Testing the second rectangle dimx , dimy + distToLine
1104  m_xcliplo = m_padToTestPos.x - padHalfsize.x;
1105  m_ycliplo = m_padToTestPos.y - padHalfsize.y - distToLine;
1106  m_xcliphi = m_padToTestPos.x + padHalfsize.x;
1107  m_ycliphi = m_padToTestPos.y + padHalfsize.y + distToLine;
1108 
1109  if( !checkLine( startPoint, endPoint ) )
1110  return false;
1111 
1112  // testing the 4 circles which are the clearance area of each corner:
1113 
1114  // testing the left top corner of the rectangle
1115  startPoint.x = m_padToTestPos.x - padHalfsize.x;
1116  startPoint.y = m_padToTestPos.y - padHalfsize.y;
1117  RotatePoint( &startPoint, m_padToTestPos, orient );
1118  RotatePoint( &startPoint, m_segmAngle );
1119 
1120  if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
1121  return false;
1122 
1123  // testing the right top corner of the rectangle
1124  startPoint.x = m_padToTestPos.x + padHalfsize.x;
1125  startPoint.y = m_padToTestPos.y - padHalfsize.y;
1126  RotatePoint( &startPoint, m_padToTestPos, orient );
1127  RotatePoint( &startPoint, m_segmAngle );
1128 
1129  if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
1130  return false;
1131 
1132  // testing the left bottom corner of the rectangle
1133  startPoint.x = m_padToTestPos.x - padHalfsize.x;
1134  startPoint.y = m_padToTestPos.y + padHalfsize.y;
1135  RotatePoint( &startPoint, m_padToTestPos, orient );
1136  RotatePoint( &startPoint, m_segmAngle );
1137 
1138  if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
1139  return false;
1140 
1141  // testing the right bottom corner of the rectangle
1142  startPoint.x = m_padToTestPos.x + padHalfsize.x;
1143  startPoint.y = m_padToTestPos.y + padHalfsize.y;
1144  RotatePoint( &startPoint, m_padToTestPos, orient );
1145  RotatePoint( &startPoint, m_segmAngle );
1146 
1147  if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
1148  return false;
1149 
1150  break;
1151 
1152  case PAD_SHAPE_TRAPEZOID:
1153  {
1154  wxPoint poly[4];
1155  aPad->BuildPadPolygon( poly, wxSize( 0, 0 ), orient );
1156 
1157  // Move shape to m_padToTestPos
1158  for( int ii = 0; ii < 4; ii++ )
1159  {
1160  poly[ii] += m_padToTestPos;
1161  RotatePoint( &poly[ii], m_segmAngle );
1162  }
1163 
1164  if( !poly2segmentDRC( poly, 4, wxPoint( 0, 0 ),
1165  wxPoint(m_segmLength,0), distToLine ) )
1166  return false;
1167  }
1168  break;
1169 
1170  case PAD_SHAPE_CUSTOM:
1171  {
1172  SHAPE_POLY_SET polyset;
1173  polyset.Append( aPad->GetCustomShapeAsPolygon() );
1174  // The pad can be rotated. calculate the coordinates
1175  // relatives to the segment being tested
1176  // Note, the pad position relative to the segment origin
1177  // is m_padToTestPos
1178  aPad->CustomShapeAsPolygonToBoardPosition( &polyset,
1179  m_padToTestPos, orient );
1180 
1181  // Rotate all coordinates by m_segmAngle, because the segment orient
1182  // is m_segmAngle
1183  // we are using a horizontal segment for test, because we know here
1184  // only the lenght and orientation+ of the segment
1185  // therefore all coordinates of the pad to test must be rotated by
1186  // m_segmAngle (they are already relative to the segment origin)
1187  aPad->CustomShapeAsPolygonToBoardPosition( &polyset,
1188  wxPoint( 0, 0 ), m_segmAngle );
1189 
1190  const SHAPE_LINE_CHAIN& refpoly = polyset.COutline( 0 );
1191 
1192  if( !poly2segmentDRC( (wxPoint*) &refpoly.CPoint( 0 ),
1193  refpoly.PointCount(),
1194  wxPoint( 0, 0 ), wxPoint(m_segmLength,0),
1195  distToLine ) )
1196  return false;
1197  }
1198  break;
1199  }
1200 
1201  return true;
1202 }
1203 
1204 
1211 bool DRC::checkMarginToCircle( wxPoint aCentre, int aRadius, int aLength )
1212 {
1213  if( abs( aCentre.y ) >= aRadius ) // trivial case
1214  return true;
1215 
1216  // Here, distance between aCentre and X axis is < aRadius
1217  if( (aCentre.x > -aRadius ) && ( aCentre.x < (aLength + aRadius) ) )
1218  {
1219  if( (aCentre.x >= 0) && (aCentre.x <= aLength) )
1220  return false; // aCentre is between the starting point and the ending point of the segm
1221 
1222  if( aCentre.x > aLength ) // aCentre is after the ending point
1223  aCentre.x -= aLength; // move aCentre to the starting point of the segment
1224 
1225  if( EuclideanNorm( aCentre ) < aRadius )
1226  // distance between aCentre and the starting point or the ending point is < aRadius
1227  return false;
1228  }
1229 
1230  return true;
1231 }
1232 
1233 
1234 // Helper function used in checkLine::
1235 static inline int USCALE( unsigned arg, unsigned num, unsigned den )
1236 {
1237  int ii;
1238 
1239  ii = KiROUND( ( (double) arg * num ) / den );
1240  return ii;
1241 }
1242 
1243 
1249 bool DRC::checkLine( wxPoint aSegStart, wxPoint aSegEnd )
1250 {
1251 #define WHEN_OUTSIDE return true
1252 #define WHEN_INSIDE
1253  int temp;
1254 
1255  if( aSegStart.x > aSegEnd.x )
1256  std::swap( aSegStart, aSegEnd );
1257 
1258  if( (aSegEnd.x < m_xcliplo) || (aSegStart.x > m_xcliphi) )
1259  {
1260  WHEN_OUTSIDE;
1261  }
1262 
1263  if( aSegStart.y < aSegEnd.y )
1264  {
1265  if( (aSegEnd.y < m_ycliplo) || (aSegStart.y > m_ycliphi) )
1266  {
1267  WHEN_OUTSIDE;
1268  }
1269 
1270  if( aSegStart.y < m_ycliplo )
1271  {
1272  temp = USCALE( (aSegEnd.x - aSegStart.x), (m_ycliplo - aSegStart.y),
1273  (aSegEnd.y - aSegStart.y) );
1274 
1275  if( (aSegStart.x += temp) > m_xcliphi )
1276  {
1277  WHEN_OUTSIDE;
1278  }
1279 
1280  aSegStart.y = m_ycliplo;
1281  WHEN_INSIDE;
1282  }
1283 
1284  if( aSegEnd.y > m_ycliphi )
1285  {
1286  temp = USCALE( (aSegEnd.x - aSegStart.x), (aSegEnd.y - m_ycliphi),
1287  (aSegEnd.y - aSegStart.y) );
1288 
1289  if( (aSegEnd.x -= temp) < m_xcliplo )
1290  {
1291  WHEN_OUTSIDE;
1292  }
1293 
1294  aSegEnd.y = m_ycliphi;
1295  WHEN_INSIDE;
1296  }
1297 
1298  if( aSegStart.x < m_xcliplo )
1299  {
1300  temp = USCALE( (aSegEnd.y - aSegStart.y), (m_xcliplo - aSegStart.x),
1301  (aSegEnd.x - aSegStart.x) );
1302  aSegStart.y += temp;
1303  aSegStart.x = m_xcliplo;
1304  WHEN_INSIDE;
1305  }
1306 
1307  if( aSegEnd.x > m_xcliphi )
1308  {
1309  temp = USCALE( (aSegEnd.y - aSegStart.y), (aSegEnd.x - m_xcliphi),
1310  (aSegEnd.x - aSegStart.x) );
1311  aSegEnd.y -= temp;
1312  aSegEnd.x = m_xcliphi;
1313  WHEN_INSIDE;
1314  }
1315  }
1316  else
1317  {
1318  if( (aSegStart.y < m_ycliplo) || (aSegEnd.y > m_ycliphi) )
1319  {
1320  WHEN_OUTSIDE;
1321  }
1322 
1323  if( aSegStart.y > m_ycliphi )
1324  {
1325  temp = USCALE( (aSegEnd.x - aSegStart.x), (aSegStart.y - m_ycliphi),
1326  (aSegStart.y - aSegEnd.y) );
1327 
1328  if( (aSegStart.x += temp) > m_xcliphi )
1329  {
1330  WHEN_OUTSIDE;
1331  }
1332 
1333  aSegStart.y = m_ycliphi;
1334  WHEN_INSIDE;
1335  }
1336 
1337  if( aSegEnd.y < m_ycliplo )
1338  {
1339  temp = USCALE( (aSegEnd.x - aSegStart.x), (m_ycliplo - aSegEnd.y),
1340  (aSegStart.y - aSegEnd.y) );
1341 
1342  if( (aSegEnd.x -= temp) < m_xcliplo )
1343  {
1344  WHEN_OUTSIDE;
1345  }
1346 
1347  aSegEnd.y = m_ycliplo;
1348  WHEN_INSIDE;
1349  }
1350 
1351  if( aSegStart.x < m_xcliplo )
1352  {
1353  temp = USCALE( (aSegStart.y - aSegEnd.y), (m_xcliplo - aSegStart.x),
1354  (aSegEnd.x - aSegStart.x) );
1355  aSegStart.y -= temp;
1356  aSegStart.x = m_xcliplo;
1357  WHEN_INSIDE;
1358  }
1359 
1360  if( aSegEnd.x > m_xcliphi )
1361  {
1362  temp = USCALE( (aSegStart.y - aSegEnd.y), (aSegEnd.x - m_xcliphi),
1363  (aSegEnd.x - aSegStart.x) );
1364  aSegEnd.y += temp;
1365  aSegEnd.x = m_xcliphi;
1366  WHEN_INSIDE;
1367  }
1368  }
1369 
1370  if( ( (aSegEnd.x + aSegStart.x) / 2 <= m_xcliphi )
1371  && ( (aSegEnd.x + aSegStart.x) / 2 >= m_xcliplo ) \
1372  && ( (aSegEnd.y + aSegStart.y) / 2 <= m_ycliphi )
1373  && ( (aSegEnd.y + aSegStart.y) / 2 >= m_ycliplo ) )
1374  {
1375  return false;
1376  }
1377  else
1378  {
1379  return true;
1380  }
1381 }
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:112
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:646
KICAD_T Type() const
Function Type()
Definition: base_struct.h:225
#define DRCE_TRACK_SEGMENTS_TOO_CLOSE
2 parallel track segments too close: segm ends between segref ends
Definition: drc_stuff.h:51
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:60
#define WHEN_OUTSIDE
static int KiROUND(double v)
KiROUND rounds a floating point number to an int using "round halfway cases away from zero"...
Definition: common.h:107
SEGMENT_ITERATOR IterateSegmentsWithHoles()
Returns an iterator object, for all outlines in the set (with holes)
MARKER_PCB * fillMarker(const TRACK *aTrack, BOARD_ITEM *aItem, int aErrorCode, MARKER_PCB *fillMe)
Function fillMarker optionally creates a marker and fills it in with information, but does not add it...
int m_ycliplo
Definition: drc_stuff.h:200
#define DRCE_ENDS_PROBLEM1
track ends are too close
Definition: drc_stuff.h:53
const SHAPE_POLY_SET & GetCustomShapeAsPolygon() const
Accessor to the custom shape as one polygon.
Definition: class_pad.h:341
#define DRCE_TRACK_ENDS1
2 parallel track segments too close: fine start point test
Definition: drc_stuff.h:47
void BuildPadPolygon(wxPoint aCoord[4], wxSize aInflateValue, double aRotation) const
Function BuildPadPolygon Has meaning only for polygonal pads (trapezoid and rectangular) Build the Co...
int PointCount() const
Function PointCount()
bool TestForIntersectionOfStraightLineSegments(int x1i, int y1i, int x1f, int y1f, int x2i, int y2i, int x2f, int y2f, int *x, int *y, double *d)
Function TestForIntersectionOfStraightLineSegments Test for intersection of line segments If lines ar...
#define DRCE_TOO_SMALL_MICROVIA_DRILL
Too small micro via drill.
Definition: drc_stuff.h:70
#define DRCE_TRACK_NEAR_VIA
track too close to via
Definition: drc_stuff.h:44
Class BOARD to handle a board.
int GetRoundRectCornerRadius() const
Function GetRoundRectCornerRadius Has meaning only for rounded rect pads.
Definition: class_pad.h:511
int m_ycliphi
Definition: drc_stuff.h:202
virtual PCB_LAYER_ID GetLayer() const override
Function GetLayer returns the primary layer this item is on.
Definition: class_zone.cpp:178
static const int dist[10][10]
Definition: dist.cpp:57
SHAPE_POLY_SET * Outline()
Definition: class_zone.h:236
bool poly2segmentDRC(wxPoint *aTref, int aTrefCount, wxPoint aSegStart, wxPoint aSegEnd, int aDist)
#define COPPERAREA_CLOSE_TO_COPPERAREA
copper area outlines are too close
Definition: drc_stuff.h:62
Struct VERTEX_INDEX.
int m_segmLength
Definition: drc_stuff.h:194
void SetPosition(const wxPoint &aPos) override
Definition: class_pad.h:219
Classes to handle copper zones.
Class SEGMENT_ITERATOR_TEMPLATE.
PAD_DRILL_SHAPE_T GetDrillShape() const
Definition: class_pad.h:381
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:317
int GetClearanceBetweenSegments(int x1i, int y1i, int x1f, int y1f, int w1, int x2i, int y2i, int x2f, int y2f, int w2, int max_cl, int *x, int *y)
int OutlineCount() const
Returns the number of outlines in the set
void NORMALIZE_ANGLE_POS(T &Angle)
Definition: trigo.h:241
const wxSize & GetDrillSize() const
Definition: class_pad.h:275
#define DRCE_TRACKS_CROSSING
tracks are crossing
Definition: drc_stuff.h:52
wxPoint m_padToTestPos
Definition: drc_stuff.h:185
PAD_SHAPE_T GetShape() const
Function GetShape.
Definition: class_pad.h:216
#define DRCE_ENDS_PROBLEM2
track ends are too close
Definition: drc_stuff.h:54
#define abs(a)
Definition: auxiliary.h:84
#define DRCE_TOO_SMALL_MICROVIA
Too small micro via size.
Definition: drc_stuff.h:68
static const int delta[8][2]
Definition: solve.cpp:112
const wxPoint & GetEnd() const
Definition: class_track.h:120
Functions relatives to tracks, vias and segments used to fill zones.
BOARD * m_pcb
Definition: drc_stuff.h:205
bool GetIsKeepout() const
Accessors to parameters used in Keepout zones:
Definition: class_zone.h:602
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_ENDS_PROBLEM5
track ends are too close
Definition: drc_stuff.h:57
Markers used to show a drc problem on boards.
PCB_LAYER_ID
A quick note on layer IDs:
Class LSET is a set of PCB_LAYER_IDs.
int GetCopperLayerCount() const
Function GetCopperLayerCount.
bool TestPointInsidePolygon(const CPOLYGONS_LIST &aPolysList, int aIdxstart, int aIdxend, int aRefx, int aRefy)
Function TestPointInsidePolygon test if a point is inside or outside a polygon.
void CustomShapeAsPolygonToBoardPosition(SHAPE_POLY_SET *aMergedPolygon, wxPoint aPosition, double aRotation) const
When created, the corners coordinates are relative to the pad position, orientation 0...
VIATYPE_T GetViaType() const
Definition: class_track.h:443
double ArcTangente(int dy, int dx)
Definition: trigo.cpp:271
#define DRCE_VIA_NEAR_TRACK
via too close to track
Definition: drc_stuff.h:46
std::shared_ptr< NETCLASS > GetNetClass() const
Function GetNetClass returns the NETCLASS for this item.
T AddAngles(T a1, T2 a2)
Add two angles (keeping the result normalized). T2 is here.
Definition: trigo.h:267
int GetBoundingRadius() const
Function GetBoundingRadius returns the radius of a minimum sized circle which fully encloses this pad...
Definition: class_pad.h:586
Class SHAPE_POLY_SET.
const wxPoint & GetStart() const
Definition: class_track.h:123
const wxPoint & GetPosition() const override
Definition: class_pad.h:220
int m_TrackMinWidth
track min value for width ((min copper size value
int m_ViasMinSize
vias (not micro vias) min diameter
LSET GetLayerSet() const override
Function GetLayerSet returns a "layer mask", which is a bitmap of all layers on which the TRACK segme...
Definition: class_pad.h:395
bool GetRelativeIndices(int aGlobalIdx, VERTEX_INDEX *aRelativeIndices) const
Function GetRelativeIndices.
bool convex2pointDRC(wxPoint *aTref, int aTrefCount, wxPoint aPcompare, int aDist)
int m_ViasMinDrill
vias (not micro vias) min drill diameter
virtual LSET GetLayerSet() const
Function GetLayerSet returns a "layer mask", which is a bitmap of all layers on which the TRACK segme...
const wxSize & GetSize() const
Definition: class_pad.h:269
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Function GetDesignSettings.
Definition: class_board.h:532
void SetSize(const wxSize &aSize)
Definition: class_pad.h:268
static bool checkMarginToCircle(wxPoint aCentre, int aRadius, int aLength)
Helper function checkMarginToCircle Check the distance from a point to a segment. ...
#define DRCE_TRACK_NEAR_PAD
pad too close to track
Definition: drc_stuff.h:43
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
int GetAreaCount() const
Function GetAreaCount.
Definition: class_board.h:1011
static bool intersect(const SEGMENT_WITH_NORMALS &aSeg, const SFVEC2F &aStart, const SFVEC2F &aEnd)
Definition: cpolygon2d.cpp:293
virtual int GetClearance(BOARD_CONNECTED_ITEM *aItem=NULL) const override
Function GetClearance returns the clearance in internal units.
int m_xcliphi
Definition: drc_stuff.h:201
int m_MicroViasMinSize
micro vias (not vias) min diameter
unsigned GetPadCount() const
Function GetPadCount.
bool doEdgeZoneDrc(ZONE_CONTAINER *aArea, int aCornerIndex)
Function doEdgeZoneDrc tests a segment in ZONE_CONTAINER * aArea: Test Edge inside other areas Test E...
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 checkClearancePadToPad(D_PAD *aRefPad, D_PAD *aPad)
Function checkClearancePadToPad.
int GetNetCode() const
Function GetNetCode.
MARKER_PCB * m_currentMarker
Definition: drc_stuff.h:176
#define DRCE_TOO_SMALL_TRACK_WIDTH
Too small track width.
Definition: drc_stuff.h:66
Definition: seg.h:36
#define WHEN_INSIDE
bool checkClearanceSegmToPad(const D_PAD *aPad, int aSegmentWidth, int aMinDist)
Function checkClearanceSegmToPad check the distance from a pad to segment.
const std::vector< D_PAD * > GetPads()
Function GetPads returns a reference to a list of all the pads.
wxPoint m_segmEnd
Definition: drc_stuff.h:186
void SetLayerSet(LSET aLayerMask)
Definition: class_pad.h:394
SEG Segment(int aIndex)
Function Segment()
bool IsOnCopperLayer() const
Function IsOnCopperLayer.
Definition: class_zone.cpp:184
TRACK * Next() const
Definition: class_track.h:100
ZONE_CONTAINER * GetArea(int index) const
Function GetArea returns the Area (Zone Container) at a given index.
Definition: class_board.h:982
int GetDrillValue() const
Function GetDrillValue "calculates" the drill value for vias (m-Drill if > 0, or default drill value ...
#define max(a, b)
Definition: auxiliary.h:86
Class SHAPE_LINE_CHAIN.
int GetWidth() const
Definition: class_track.h:117
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
#define DRCE_ENDS_PROBLEM3
track ends are too close
Definition: drc_stuff.h:55
#define DRCE_TRACK_ENDS4
2 parallel track segments too close: fine end point test
Definition: drc_stuff.h:50
double GetOrientation() const
Function GetOrientation returns the rotation angle of the pad in tenths of degrees, but soon degrees.
Definition: class_pad.h:375
VECTOR2I A
Definition: seg.h:46
const wxSize & GetDelta() const
Definition: class_pad.h:272
bool poly2polyDRC(wxPoint *aTref, int aTrefCount, wxPoint *aTcompare, int aTcompareCount, int aDist)
DRC control: these functions make a DRC between pads, tracks and pads versus tracks.
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...
#define DRCE_VIA_NEAR_VIA
via too close to via
Definition: drc_stuff.h:45
int GetClearance(BOARD_CONNECTED_ITEM *aItem=NULL) const override
Function GetClearance returns the clearance in internal units.
Definition: class_pad.cpp:546
void SetShape(PAD_SHAPE_T aShape)
Definition: class_pad.h:217
wxPoint ShapePos() const
Definition: class_pad.cpp:500
void SetOrientation(double aAngle)
Function SetOrientation sets the rotation angle of the pad.
Definition: class_pad.cpp:401
class VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
bool doTrackDrc(TRACK *aRefSeg, TRACK *aStart, bool doPads=true)
Function DoTrackDrc tests the current segment.
Module description (excepted pads)
POLYGON & Polygon(int aIndex)
Returns the aIndex-th subpolygon in the set
int m_MicroViasMinDrill
micro vias (not vias) min drill diameter
bool TestSegmentHit(const wxPoint &aRefPoint, wxPoint aStart, wxPoint aEnd, int aDist)
Function TestSegmentHit test for hit on line segment i.e.
Definition: trigo.cpp:142
unsigned GetPriority() const
Function GetPriority.
Definition: class_zone.h:101
#define DRCE_TRACK_ENDS2
2 parallel track segments too close: fine start point test
Definition: drc_stuff.h:48
#define DRCE_TRACK_ENDS3
2 parallel track segments too close: fine end point test
Definition: drc_stuff.h:49
#define DRCE_ENDS_PROBLEM4
track ends are too close
Definition: drc_stuff.h:56
const VECTOR2I & CPoint(int aIndex) const
Function CPoint()
int m_xcliplo
Definition: drc_stuff.h:199
#define DRCE_MICRO_VIA_INCORRECT_LAYER_PAIR
micro via's layer pair incorrect (layers must be adjacent)
Definition: drc_stuff.h:60
int GetZoneClearance() const
Definition: class_zone.h:192
double m_segmAngle
Definition: drc_stuff.h:193
bool Contains(const VECTOR2I &aP, int aSubpolyIndex=-1) const
Returns true if a given subpolygon contains the point aP.
#define DRCE_TRACK_NEAR_THROUGH_HOLE
thru hole is too close to track
Definition: drc_stuff.h:42
#define COPPERAREA_INSIDE_COPPERAREA
copper area outlines intersect
Definition: drc_stuff.h:61
#define DRCE_VIA_HOLE_BIGGER
via's hole is bigger than its diameter
Definition: drc_stuff.h:59
Class BOARD_DESIGN_SETTINGS contains design settings for a BOARD object.
#define DRCE_TOO_SMALL_VIA_DRILL
Too small via drill.
Definition: drc_stuff.h:69
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_VIA
Too small via size.
Definition: drc_stuff.h:67
VECTOR2I B
Definition: seg.h:47