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-2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
9  * Copyright (C) 2007 Dick Hollenbeck, dick@softplc.com
10  * Copyright (C) 2018 KiCad Developers, see AUTHORS.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 <pcb_edit_frame.h>
36 #include <trigo.h>
37 
38 #include <pcbnew.h>
39 #include <drc.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  // test if the type of via is allowed due to design rules
206  if( ( refvia->GetViaType() == VIA_MICROVIA ) &&
207  ( m_pcb->GetDesignSettings().m_MicroViasAllowed == false ) )
208  {
209  m_currentMarker = fillMarker( refvia, NULL,
211  return false;
212  }
213 
214  // test if the type of via is allowed due to design rules
215  if( ( refvia->GetViaType() == VIA_BLIND_BURIED ) &&
217  {
218  m_currentMarker = fillMarker( refvia, NULL,
220  return false;
221  }
222 
223  // For microvias: test if they are blind vias and only between 2 layers
224  // because they are used for very small drill size and are drill by laser
225  // and **only one layer** can be drilled
226  if( refvia->GetViaType() == VIA_MICROVIA )
227  {
228 
229 
230  PCB_LAYER_ID layer1, layer2;
231  bool err = true;
232 
233  refvia->LayerPair( &layer1, &layer2 );
234 
235  if( layer1 > layer2 )
236  std::swap( layer1, layer2 );
237 
238  if( layer2 == B_Cu && layer1 == m_pcb->GetDesignSettings().GetCopperLayerCount() - 2 )
239  err = false;
240  else if( layer1 == F_Cu && layer2 == In1_Cu )
241  err = false;
242 
243  if( err )
244  {
245  m_currentMarker = fillMarker( refvia, NULL,
247  return false;
248  }
249  }
250 
251  }
252  else // This is a track segment
253  {
254  if( aRefSeg->GetWidth() < dsnSettings.m_TrackMinWidth )
255  {
256  m_currentMarker = fillMarker( aRefSeg, NULL,
258  return false;
259  }
260  }
261 
262  // for a non horizontal or vertical segment Compute the segment angle
263  // in tenths of degrees and its length
264  if( delta.x || delta.y )
265  {
266  // Compute the segment angle in 0,1 degrees
267  m_segmAngle = ArcTangente( delta.y, delta.x );
268 
269  // Compute the segment length: we build an equivalent rotated segment,
270  // this segment is horizontal, therefore dx = length
271  RotatePoint( &delta, m_segmAngle ); // delta.x = length, delta.y = 0
272  }
273 
274  m_segmLength = delta.x;
275 
276  /******************************************/
277  /* Phase 1 : test DRC track to pads : */
278  /******************************************/
279 
280  /* Use a dummy pad to test DRC tracks versus holes, for pads not on all copper layers
281  * but having a hole
282  * This dummy pad has the size and shape of the hole
283  * to test tracks to pad hole DRC, using checkClearanceSegmToPad test function.
284  * Therefore, this dummy pad is a circle or an oval.
285  * A pad must have a parent because some functions expect a non null parent
286  * to find the parent board, and some other data
287  */
288  MODULE dummymodule( m_pcb ); // Creates a dummy parent
289  D_PAD dummypad( &dummymodule );
290 
291  dummypad.SetLayerSet( LSET::AllCuMask() ); // Ensure the hole is on all layers
292 
293  // Compute the min distance to pads
294  if( testPads )
295  {
296  unsigned pad_count = m_pcb->GetPadCount();
297 
298  auto pads = m_pcb->GetPads();
299 
300  for( unsigned ii = 0; ii<pad_count; ++ii )
301  {
302  D_PAD* pad = pads[ii];
303 
304  /* No problem if pads are on an other layer,
305  * But if a drill hole exists (a pad on a single layer can have a hole!)
306  * we must test the hole
307  */
308  if( !( pad->GetLayerSet() & layerMask ).any() )
309  {
310  /* We must test the pad hole. In order to use the function
311  * checkClearanceSegmToPad(),a pseudo pad is used, with a shape and a
312  * size like the hole
313  */
314  if( pad->GetDrillSize().x == 0 )
315  continue;
316 
317  dummypad.SetSize( pad->GetDrillSize() );
318  dummypad.SetPosition( pad->GetPosition() );
319  dummypad.SetShape( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ?
321  dummypad.SetOrientation( pad->GetOrientation() );
322 
323  m_padToTestPos = dummypad.GetPosition() - origin;
324 
325  if( !checkClearanceSegmToPad( &dummypad, aRefSeg->GetWidth(),
326  netclass->GetClearance() ) )
327  {
328  m_currentMarker = fillMarker( aRefSeg, pad,
330  return false;
331  }
332 
333  continue;
334  }
335 
336  // The pad must be in a net (i.e pt_pad->GetNet() != 0 )
337  // but no problem if the pad netcode is the current netcode (same net)
338  if( pad->GetNetCode() // the pad must be connected
339  && net_code_ref == pad->GetNetCode() ) // the pad net is the same as current net -> Ok
340  continue;
341 
342  // DRC for the pad
343  shape_pos = pad->ShapePos();
344  m_padToTestPos = shape_pos - origin;
345 
346  if( !checkClearanceSegmToPad( pad, aRefSeg->GetWidth(), aRefSeg->GetClearance( pad ) ) )
347  {
348  m_currentMarker = fillMarker( aRefSeg, pad,
350  return false;
351  }
352  }
353  }
354 
355  /***********************************************/
356  /* Phase 2: test DRC with other track segments */
357  /***********************************************/
358 
359  // At this point the reference segment is the X axis
360 
361  // Test the reference segment with other track segments
362  wxPoint segStartPoint;
363  wxPoint segEndPoint;
364  for( track = aStart; track; track = track->Next() )
365  {
366  // No problem if segments have the same net code:
367  if( net_code_ref == track->GetNetCode() )
368  continue;
369 
370  // No problem if segment are on different layers :
371  if( !( layerMask & track->GetLayerSet() ).any() )
372  continue;
373 
374  // the minimum distance = clearance plus half the reference track
375  // width plus half the other track's width
376  int w_dist = aRefSeg->GetClearance( track );
377  w_dist += (aRefSeg->GetWidth() + track->GetWidth()) / 2;
378 
379  // Due to many double to int conversions during calculations, which
380  // create rounding issues,
381  // the exact clearance margin cannot be really known.
382  // To avoid false bad DRC detection due to these rounding issues,
383  // slightly decrease the w_dist (remove one nanometer is enough !)
384  w_dist -= 1;
385 
386  // If the reference segment is a via, we test it here
387  if( aRefSeg->Type() == PCB_VIA_T )
388  {
389  delta = track->GetEnd() - track->GetStart();
390  segStartPoint = aRefSeg->GetStart() - track->GetStart();
391 
392  if( track->Type() == PCB_VIA_T )
393  {
394  // Test distance between two vias, i.e. two circles, trivial case
395  if( EuclideanNorm( segStartPoint ) < w_dist )
396  {
397  m_currentMarker = fillMarker( aRefSeg, track,
399  return false;
400  }
401  }
402  else // test via to segment
403  {
404  // Compute l'angle du segment a tester;
405  double angle = ArcTangente( delta.y, delta.x );
406 
407  // Compute new coordinates ( the segment become horizontal)
408  RotatePoint( &delta, angle );
409  RotatePoint( &segStartPoint, angle );
410 
411  if( !checkMarginToCircle( segStartPoint, w_dist, delta.x ) )
412  {
413  m_currentMarker = fillMarker( track, aRefSeg,
415  return false;
416  }
417  }
418 
419  continue;
420  }
421 
422  /* We compute segStartPoint, segEndPoint = starting and ending point coordinates for
423  * the segment to test in the new axis : the new X axis is the
424  * reference segment. We must translate and rotate the segment to test
425  */
426  segStartPoint = track->GetStart() - origin;
427  segEndPoint = track->GetEnd() - origin;
428  RotatePoint( &segStartPoint, m_segmAngle );
429  RotatePoint( &segEndPoint, m_segmAngle );
430  if( track->Type() == PCB_VIA_T )
431  {
432  if( checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) )
433  continue;
434 
435  m_currentMarker = fillMarker( aRefSeg, track,
437  return false;
438  }
439 
440  /* We have changed axis:
441  * the reference segment is Horizontal.
442  * 3 cases : the segment to test can be parallel, perpendicular or have an other direction
443  */
444  if( segStartPoint.y == segEndPoint.y ) // parallel segments
445  {
446  if( abs( segStartPoint.y ) >= w_dist )
447  continue;
448 
449  // Ensure segStartPoint.x <= segEndPoint.x
450  if( segStartPoint.x > segEndPoint.x )
451  std::swap( segStartPoint.x, segEndPoint.x );
452 
453  if( segStartPoint.x > (-w_dist) && segStartPoint.x < (m_segmLength + w_dist) ) /* possible error drc */
454  {
455  // the start point is inside the reference range
456  // X........
457  // O--REF--+
458 
459  // Fine test : we consider the rounded shape of each end of the track segment:
460  if( segStartPoint.x >= 0 && segStartPoint.x <= m_segmLength )
461  {
462  m_currentMarker = fillMarker( aRefSeg, track,
464  return false;
465  }
466 
467  if( !checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) )
468  {
469  m_currentMarker = fillMarker( aRefSeg, track,
471  return false;
472  }
473  }
474 
475  if( segEndPoint.x > (-w_dist) && segEndPoint.x < (m_segmLength + w_dist) )
476  {
477  // the end point is inside the reference range
478  // .....X
479  // O--REF--+
480  // Fine test : we consider the rounded shape of the ends
481  if( segEndPoint.x >= 0 && segEndPoint.x <= m_segmLength )
482  {
483  m_currentMarker = fillMarker( aRefSeg, track,
485  return false;
486  }
487 
488  if( !checkMarginToCircle( segEndPoint, w_dist, m_segmLength ) )
489  {
490  m_currentMarker = fillMarker( aRefSeg, track,
492  return false;
493  }
494  }
495 
496  if( segStartPoint.x <=0 && segEndPoint.x >= 0 )
497  {
498  // the segment straddles the reference range (this actually only
499  // checks if it straddles the origin, because the other cases where already
500  // handled)
501  // X.............X
502  // O--REF--+
503  m_currentMarker = fillMarker( aRefSeg, track,
505  return false;
506  }
507  }
508  else if( segStartPoint.x == segEndPoint.x ) // perpendicular segments
509  {
510  if( ( segStartPoint.x <= (-w_dist) ) || ( segStartPoint.x >= (m_segmLength + w_dist) ) )
511  continue;
512 
513  // Test if segments are crossing
514  if( segStartPoint.y > segEndPoint.y )
515  std::swap( segStartPoint.y, segEndPoint.y );
516 
517  if( (segStartPoint.y < 0) && (segEndPoint.y > 0) )
518  {
519  m_currentMarker = fillMarker( aRefSeg, track,
521  return false;
522  }
523 
524  // At this point the drc error is due to an end near a reference segm end
525  if( !checkMarginToCircle( segStartPoint, w_dist, m_segmLength ) )
526  {
527  m_currentMarker = fillMarker( aRefSeg, track,
529  return false;
530  }
531  if( !checkMarginToCircle( segEndPoint, w_dist, m_segmLength ) )
532  {
533  m_currentMarker = fillMarker( aRefSeg, track,
535  return false;
536  }
537  }
538  else // segments quelconques entre eux
539  {
540  // calcul de la "surface de securite du segment de reference
541  // First rought 'and fast) test : the track segment is like a rectangle
542 
543  m_xcliplo = m_ycliplo = -w_dist;
544  m_xcliphi = m_segmLength + w_dist;
545  m_ycliphi = w_dist;
546 
547  // A fine test is needed because a serment is not exactly a
548  // rectangle, it has rounded ends
549  if( !checkLine( segStartPoint, segEndPoint ) )
550  {
551  /* 2eme passe : the track has rounded ends.
552  * we must a fine test for each rounded end and the
553  * rectangular zone
554  */
555 
556  m_xcliplo = 0;
558 
559  if( !checkLine( segStartPoint, segEndPoint ) )
560  {
561  m_currentMarker = fillMarker( aRefSeg, track,
563  return false;
564  }
565  else // The drc error is due to the starting or the ending point of the reference segment
566  {
567  // Test the starting and the ending point
568  segStartPoint = track->GetStart();
569  segEndPoint = track->GetEnd();
570  delta = segEndPoint - segStartPoint;
571 
572  // Compute the segment orientation (angle) en 0,1 degre
573  double angle = ArcTangente( delta.y, delta.x );
574 
575  // Compute the segment length: delta.x = length after rotation
576  RotatePoint( &delta, angle );
577 
578  /* Comute the reference segment coordinates relatives to a
579  * X axis = current tested segment
580  */
581  wxPoint relStartPos = aRefSeg->GetStart() - segStartPoint;
582  wxPoint relEndPos = aRefSeg->GetEnd() - segStartPoint;
583 
584  RotatePoint( &relStartPos, angle );
585  RotatePoint( &relEndPos, angle );
586 
587  if( !checkMarginToCircle( relStartPos, w_dist, delta.x ) )
588  {
589  m_currentMarker = fillMarker( aRefSeg, track,
591  return false;
592  }
593 
594  if( !checkMarginToCircle( relEndPos, w_dist, delta.x ) )
595  {
596  m_currentMarker = fillMarker( aRefSeg, track,
598  return false;
599  }
600  }
601  }
602  }
603  }
604 
605  return true;
606 }
607 
608 
609 bool DRC::doEdgeZoneDrc( ZONE_CONTAINER* aArea, int aCornerIndex )
610 {
611  if( !aArea->IsOnCopperLayer() ) // Cannot have a Drc error if not on copper layer
612  return true;
613  // Get polygon, contour and vertex index.
615 
616  // If the vertex does not exist, there is no conflict
617  if( !aArea->Outline()->GetRelativeIndices( aCornerIndex, &index ) )
618  return true;
619 
620  // Retrieve the selected contour
621  SHAPE_LINE_CHAIN contour;
622  contour = aArea->Outline()->Polygon( index.m_polygon )[index.m_contour];
623 
624  // Retrieve the segment that starts at aCornerIndex-th corner.
625  SEG selectedSegment = contour.Segment( index.m_vertex );
626 
627  VECTOR2I start = selectedSegment.A;
628  VECTOR2I end = selectedSegment.B;
629 
630  // iterate through all areas
631  for( int ia2 = 0; ia2 < m_pcb->GetAreaCount(); ia2++ )
632  {
633  ZONE_CONTAINER* area_to_test = m_pcb->GetArea( ia2 );
634  int zone_clearance = std::max( area_to_test->GetZoneClearance(),
635  aArea->GetZoneClearance() );
636 
637  // test for same layer
638  if( area_to_test->GetLayer() != aArea->GetLayer() )
639  continue;
640 
641  // Test for same net
642  if( ( aArea->GetNetCode() == area_to_test->GetNetCode() ) && (aArea->GetNetCode() >= 0) )
643  continue;
644 
645  // test for same priority
646  if( area_to_test->GetPriority() != aArea->GetPriority() )
647  continue;
648 
649  // test for same type
650  if( area_to_test->GetIsKeepout() != aArea->GetIsKeepout() )
651  continue;
652 
653  // For keepout, there is no clearance, so use a minimal value for it
654  // use 1, not 0 as value to avoid some issues in tests
655  if( area_to_test->GetIsKeepout() )
656  zone_clearance = 1;
657 
658  // test for ending line inside area_to_test
659  if( area_to_test->Outline()->Contains( end ) )
660  {
661  // COPPERAREA_COPPERAREA error: corner inside copper area
662  m_currentMarker = fillMarker( aArea, static_cast<wxPoint>( end ),
664  m_currentMarker );
665  return false;
666  }
667 
668  // now test spacing between areas
669  int ax1 = start.x;
670  int ay1 = start.y;
671  int ax2 = end.x;
672  int ay2 = end.y;
673 
674  // Iterate through all edges in the polygon.
676  for( iterator = area_to_test->Outline()->IterateSegmentsWithHoles(); iterator; iterator++ )
677  {
678  SEG segment = *iterator;
679 
680  int bx1 = segment.A.x;
681  int by1 = segment.A.y;
682  int bx2 = segment.B.x;
683  int by2 = segment.B.y;
684 
685  int x, y; // variables containing the intersecting point coordinates
686  int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2,
687  0,
688  ax1, ay1, ax2, ay2,
689  0,
690  zone_clearance,
691  &x, &y );
692 
693  if( d < zone_clearance )
694  {
695  // COPPERAREA_COPPERAREA error : edge intersect or too close
696  m_currentMarker = fillMarker( aArea, wxPoint( x, y ),
698  m_currentMarker );
699  return false;
700  }
701 
702  }
703  }
704 
705  return true;
706 }
707 
708 
709 bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
710 {
711  int dist;
712  double pad_angle;
713 
714  // Get the clearance between the 2 pads. this is the min distance between aRefPad and aPad
715  int dist_min = aRefPad->GetClearance( aPad );
716 
717  // relativePadPos is the aPad shape position relative to the aRefPad shape position
718  wxPoint relativePadPos = aPad->ShapePos() - aRefPad->ShapePos();
719 
720  dist = KiROUND( EuclideanNorm( relativePadPos ) );
721 
722  // Quick test: Clearance is OK if the bounding circles are further away than "dist_min"
723  int delta = dist - aRefPad->GetBoundingRadius() - aPad->GetBoundingRadius();
724 
725  if( delta >= dist_min )
726  return true;
727 
728  /* Here, pads are near and DRC depend on the pad shapes
729  * We must compare distance using a fine shape analysis
730  * Because a circle or oval shape is the easier shape to test, try to have
731  * aRefPad shape type = PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL.
732  * if aRefPad = TRAP. and aPad = RECT, also swap pads
733  * Swap aRefPad and aPad if needed
734  */
735  bool swap_pads;
736  swap_pads = false;
737 
738  // swap pads to make comparisons easier
739  // Note also a ROUNDRECT pad with a corner radius = r can be considered as
740  // a smaller RECT (size - 2*r) with a clearance increased by r
741  // priority is aRefPad = ROUND then OVAL then RECT/ROUNDRECT then other
742  if( aRefPad->GetShape() != aPad->GetShape() && aRefPad->GetShape() != PAD_SHAPE_CIRCLE )
743  {
744  // pad ref shape is here oval, rect, roundrect, trapezoid or custom
745  switch( aPad->GetShape() )
746  {
747  case PAD_SHAPE_CIRCLE:
748  swap_pads = true;
749  break;
750 
751  case PAD_SHAPE_OVAL:
752  swap_pads = true;
753  break;
754 
755  case PAD_SHAPE_RECT:
756  case PAD_SHAPE_ROUNDRECT:
757  if( aRefPad->GetShape() != PAD_SHAPE_OVAL )
758  swap_pads = true;
759  break;
760 
761  case PAD_SHAPE_TRAPEZOID:
762  case PAD_SHAPE_CUSTOM:
763  break;
764  }
765  }
766 
767  if( swap_pads )
768  {
769  std::swap( aRefPad, aPad );
770  relativePadPos = -relativePadPos;
771  }
772 
773  // corners of aRefPad (used only for rect/roundrect/trap pad)
774  wxPoint polyref[4];
775  // corners of aRefPad (used only for custom pad)
776  SHAPE_POLY_SET polysetref;
777 
778  // corners of aPad (used only for rect/roundrect/trap pad)
779  wxPoint polycompare[4];
780  // corners of aPad (used only custom pad)
781  SHAPE_POLY_SET polysetcompare;
782 
783  /* Because pad exchange, aRefPad shape is PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL,
784  * if one of the 2 pads was a PAD_SHAPE_CIRCLE or PAD_SHAPE_OVAL.
785  * Therefore, if aRefPad is a PAD_SHAPE_RECT, PAD_SHAPE_ROUNDRECT or a PAD_SHAPE_TRAPEZOID,
786  * aPad is also a PAD_SHAPE_RECT, PAD_SHAPE_ROUNDRECT or a PAD_SHAPE_TRAPEZOID
787  */
788  bool diag = true;
789 
790  switch( aRefPad->GetShape() )
791  {
792  case PAD_SHAPE_CIRCLE:
793 
794  /* One can use checkClearanceSegmToPad to test clearance
795  * aRefPad is like a track segment with a null length and a witdth = GetSize().x
796  */
797  m_segmLength = 0;
798  m_segmAngle = 0;
799 
800  m_segmEnd.x = m_segmEnd.y = 0;
801 
802  m_padToTestPos = relativePadPos;
803  diag = checkClearanceSegmToPad( aPad, aRefPad->GetSize().x, dist_min );
804  break;
805 
806  case PAD_SHAPE_TRAPEZOID:
807  case PAD_SHAPE_ROUNDRECT:
808  case PAD_SHAPE_RECT:
809  case PAD_SHAPE_CUSTOM:
810  // pad_angle = pad orient relative to the aRefPad orient
811  pad_angle = aRefPad->GetOrientation() + aPad->GetOrientation();
812  NORMALIZE_ANGLE_POS( pad_angle );
813 
814  if( aRefPad->GetShape() == PAD_SHAPE_ROUNDRECT )
815  {
816  int padRadius = aRefPad->GetRoundRectCornerRadius();
817  dist_min += padRadius;
818  GetRoundRectCornerCenters( polyref, padRadius, wxPoint( 0, 0 ),
819  aRefPad->GetSize(), aRefPad->GetOrientation() );
820  }
821  else if( aRefPad->GetShape() == PAD_SHAPE_CUSTOM )
822  {
823  polysetref.Append( aRefPad->GetCustomShapeAsPolygon() );
824 
825  // The reference pad can be rotated. calculate the rotated
826  // coordiantes ( note, the ref pad position is the origin of
827  // coordinates for this drc test)
828  aRefPad->CustomShapeAsPolygonToBoardPosition( &polysetref,
829  wxPoint( 0, 0 ), aRefPad->GetOrientation() );
830  }
831  else
832  {
833  // BuildPadPolygon has meaning for rect a trapeziod shapes
834  // and returns the 4 corners
835  aRefPad->BuildPadPolygon( polyref, wxSize( 0, 0 ), aRefPad->GetOrientation() );
836  }
837 
838  switch( aPad->GetShape() )
839  {
840  case PAD_SHAPE_ROUNDRECT:
841  case PAD_SHAPE_RECT:
842  case PAD_SHAPE_TRAPEZOID:
843  case PAD_SHAPE_CUSTOM:
844  if( aPad->GetShape() == PAD_SHAPE_ROUNDRECT )
845  {
846  int padRadius = aPad->GetRoundRectCornerRadius();
847  dist_min += padRadius;
848  GetRoundRectCornerCenters( polycompare, padRadius, relativePadPos,
849  aPad->GetSize(), aPad->GetOrientation() );
850  }
851  else if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
852  {
853  polysetcompare.Append( aPad->GetCustomShapeAsPolygon() );
854 
855  // The pad to compare can be rotated. calculate the rotated
856  // coordinattes ( note, the pad to compare position
857  // is the relativePadPos for this drc test
858  aPad->CustomShapeAsPolygonToBoardPosition( &polysetcompare,
859  relativePadPos, aPad->GetOrientation() );
860  }
861  else
862  {
863  aPad->BuildPadPolygon( polycompare, wxSize( 0, 0 ), aPad->GetOrientation() );
864 
865  // Move aPad shape to relativePadPos
866  for( int ii = 0; ii < 4; ii++ )
867  polycompare[ii] += relativePadPos;
868  }
869  // And now test polygons: We have 3 cases:
870  // one poly is complex and the other is basic (has only 4 corners)
871  // both polys are complex
872  // both polys are basic (have only 4 corners) the most usual case
873  if( polysetref.OutlineCount() && polysetcompare.OutlineCount() == 0)
874  {
875  const SHAPE_LINE_CHAIN& refpoly = polysetref.COutline( 0 );
876  // And now test polygons:
877  if( !poly2polyDRC( (wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(),
878  polycompare, 4, dist_min ) )
879  diag = false;
880  }
881  else if( polysetref.OutlineCount() == 0 && polysetcompare.OutlineCount())
882  {
883  const SHAPE_LINE_CHAIN& cmppoly = polysetcompare.COutline( 0 );
884  // And now test polygons:
885  if( !poly2polyDRC( (wxPoint*) &cmppoly.CPoint( 0 ), cmppoly.PointCount(),
886  polyref, 4, dist_min ) )
887  diag = false;
888  }
889  else if( polysetref.OutlineCount() && polysetcompare.OutlineCount() )
890  {
891  const SHAPE_LINE_CHAIN& refpoly = polysetref.COutline( 0 );
892  const SHAPE_LINE_CHAIN& cmppoly = polysetcompare.COutline( 0 );
893 
894  // And now test polygons:
895  if( !poly2polyDRC( (wxPoint*) &refpoly.CPoint( 0 ), refpoly.PointCount(),
896  (wxPoint*) &cmppoly.CPoint( 0 ), cmppoly.PointCount(), dist_min ) )
897  diag = false;
898  }
899  else if( !poly2polyDRC( polyref, 4, polycompare, 4, dist_min ) )
900  diag = false;
901  break;
902 
903  default:
904  wxLogDebug( wxT( "DRC::checkClearancePadToPad: unexpected pad shape %d" ), aPad->GetShape() );
905  break;
906  }
907  break;
908 
909  case PAD_SHAPE_OVAL: /* an oval pad is like a track segment */
910  {
911  /* Create a track segment with same dimensions as the oval aRefPad
912  * and use checkClearanceSegmToPad function to test aPad to aRefPad clearance
913  */
914  int segm_width;
915  m_segmAngle = aRefPad->GetOrientation(); // Segment orient.
916 
917  if( aRefPad->GetSize().y < aRefPad->GetSize().x ) // Build an horizontal equiv segment
918  {
919  segm_width = aRefPad->GetSize().y;
920  m_segmLength = aRefPad->GetSize().x - aRefPad->GetSize().y;
921  }
922  else // Vertical oval: build an horizontal equiv segment and rotate 90.0 deg
923  {
924  segm_width = aRefPad->GetSize().x;
925  m_segmLength = aRefPad->GetSize().y - aRefPad->GetSize().x;
926  m_segmAngle += 900;
927  }
928 
929  /* the start point must be 0,0 and currently relativePadPos
930  * is relative the center of pad coordinate */
931  wxPoint segstart;
932  segstart.x = -m_segmLength / 2; // Start point coordinate of the horizontal equivalent segment
933 
934  RotatePoint( &segstart, m_segmAngle ); // actual start point coordinate of the equivalent segment
935  // Calculate segment end position relative to the segment origin
936  m_segmEnd.x = -2 * segstart.x;
937  m_segmEnd.y = -2 * segstart.y;
938 
939  // Recalculate the equivalent segment angle in 0,1 degrees
940  // to prepare a call to checkClearanceSegmToPad()
942 
943  // move pad position relative to the segment origin
944  m_padToTestPos = relativePadPos - segstart;
945 
946  // Use segment to pad check to test the second pad:
947  diag = checkClearanceSegmToPad( aPad, segm_width, dist_min );
948  break;
949  }
950 
951  default:
952  wxMessageBox( wxT( "DRC::checkClearancePadToPad: unknown pad shape" ) );
953  break;
954  }
955 
956  return diag;
957 }
958 
959 
960 /* test if distance between a segment is > aMinDist
961  * segment start point is assumed in (0,0) and segment start point in m_segmEnd
962  * and its orientation is m_segmAngle (m_segmAngle must be already initialized)
963  * and have aSegmentWidth.
964  */
965 bool DRC::checkClearanceSegmToPad( const D_PAD* aPad, int aSegmentWidth, int aMinDist )
966 {
967  // Note:
968  // we are using a horizontal segment for test, because we know here
969  // only the length and orientation+ of the segment
970  // Therefore the coordinates of the shape of pad to compare
971  // must be calculated in a axis system rotated by m_segmAngle
972  // and centered to the segment origin, before they can be tested
973  // against the segment
974  // We are using:
975  // m_padToTestPos the position of the pad shape in this axis system
976  // m_segmAngle the axis system rotation
977 
978  int segmHalfWidth = aSegmentWidth / 2;
979  int distToLine = segmHalfWidth + aMinDist;
980 
981  wxSize padHalfsize; // half dimension of the pad
982 
983  if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
984  {
985  // For a custom pad, the pad size has no meaning, we only can
986  // use the bounding radius
987  padHalfsize.x = padHalfsize.y = aPad->GetBoundingRadius();
988  }
989  else
990  {
991  padHalfsize.x = aPad->GetSize().x >> 1;
992  padHalfsize.y = aPad->GetSize().y >> 1;
993  }
994 
995  if( aPad->GetShape() == PAD_SHAPE_TRAPEZOID ) // The size is bigger, due to GetDelta() extra size
996  {
997  padHalfsize.x += std::abs(aPad->GetDelta().y) / 2; // Remember: GetDelta().y is the GetSize().x change
998  padHalfsize.y += std::abs(aPad->GetDelta().x) / 2; // Remember: GetDelta().x is the GetSize().y change
999  }
1000 
1001  if( aPad->GetShape() == PAD_SHAPE_CIRCLE )
1002  {
1003  /* Easy case: just test the distance between segment and pad centre
1004  * calculate pad coordinates in the X,Y axis with X axis = segment to test
1005  */
1007  return checkMarginToCircle( m_padToTestPos, distToLine + padHalfsize.x, m_segmLength );
1008  }
1009 
1010  /* calculate the bounding box of the pad, including the clearance and the segment width
1011  * if the line from 0 to m_segmEnd does not intersect this bounding box,
1012  * the clearance is always OK
1013  * But if intersect, a better analysis of the pad shape must be done.
1014  */
1015  m_xcliplo = m_padToTestPos.x - distToLine - padHalfsize.x;
1016  m_ycliplo = m_padToTestPos.y - distToLine - padHalfsize.y;
1017  m_xcliphi = m_padToTestPos.x + distToLine + padHalfsize.x;
1018  m_ycliphi = m_padToTestPos.y + distToLine + padHalfsize.y;
1019 
1020  wxPoint startPoint;
1021  wxPoint endPoint = m_segmEnd;
1022 
1023  double orient = aPad->GetOrientation();
1024 
1025  RotatePoint( &startPoint, m_padToTestPos, -orient );
1026  RotatePoint( &endPoint, m_padToTestPos, -orient );
1027 
1028  if( checkLine( startPoint, endPoint ) )
1029  return true;
1030 
1031  /* segment intersects the bounding box. But there is not always a DRC error.
1032  * A fine analysis of the pad shape must be done.
1033  */
1034  switch( aPad->GetShape() )
1035  {
1036  case PAD_SHAPE_CIRCLE:
1037  // This case was already tested, so it cannot be found here.
1038  // it is here just to avoid compil warning, and to remember
1039  // it is already tested.
1040  return false;
1041 
1042  case PAD_SHAPE_OVAL:
1043  {
1044  /* an oval is a complex shape, but is a rectangle and 2 circles
1045  * these 3 basic shapes are more easy to test.
1046  *
1047  * In calculations we are using a vertical or horizontal oval shape
1048  * (i.e. a vertical or horizontal rounded segment)
1049  */
1050  wxPoint cstart = m_padToTestPos;
1051  wxPoint cend = m_padToTestPos; // center of each circle
1052  int delta = std::abs( padHalfsize.y - padHalfsize.x );
1053  int radius = std::min( padHalfsize.y, padHalfsize.x );
1054 
1055  if( padHalfsize.x > padHalfsize.y ) // horizontal equivalent segment
1056  {
1057  cstart.x -= delta;
1058  cend.x += delta;
1059  // Build the rectangular clearance area between the two circles
1060  // the rect starts at cstart.x and ends at cend.x and its height
1061  // is (radius + distToLine)*2
1062  m_xcliplo = cstart.x;
1063  m_ycliplo = cstart.y - radius - distToLine;
1064  m_xcliphi = cend.x;
1065  m_ycliphi = cend.y + radius + distToLine;
1066  }
1067  else // vertical equivalent segment
1068  {
1069  cstart.y -= delta;
1070  cend.y += delta;
1071  // Build the rectangular clearance area between the two circles
1072  // the rect starts at cstart.y and ends at cend.y and its width
1073  // is (radius + distToLine)*2
1074  m_xcliplo = cstart.x - distToLine - radius;
1075  m_ycliplo = cstart.y;
1076  m_xcliphi = cend.x + distToLine +radius;
1077  m_ycliphi = cend.y;
1078  }
1079 
1080  // Test the rectangular clearance area between the two circles (the rounded ends)
1081  if( !checkLine( startPoint, endPoint ) )
1082  {
1083  return false;
1084  }
1085 
1086  // test the first end
1087  // Calculate the actual position of the circle, given the pad orientation:
1088  RotatePoint( &cstart, m_padToTestPos, orient );
1089 
1090  // Calculate the actual position of the circle in the new X,Y axis, relative
1091  // to the segment:
1092  RotatePoint( &cstart, m_segmAngle );
1093 
1094  if( !checkMarginToCircle( cstart, radius + distToLine, m_segmLength ) )
1095  {
1096  return false;
1097  }
1098 
1099  // test the second end
1100  RotatePoint( &cend, m_padToTestPos, orient );
1101  RotatePoint( &cend, m_segmAngle );
1102 
1103  if( !checkMarginToCircle( cend, radius + distToLine, m_segmLength ) )
1104  {
1105  return false;
1106  }
1107  }
1108  break;
1109 
1110  case PAD_SHAPE_ROUNDRECT:
1111  {
1112  // a round rect is a smaller rect, with a clearance augmented by the corners radius
1113  int r = aPad->GetRoundRectCornerRadius();
1114  padHalfsize.x -= r;
1115  padHalfsize.y -= r;
1116  distToLine += r;
1117  }
1118  // Fall through
1119  case PAD_SHAPE_RECT:
1120  // the area to test is a rounded rectangle.
1121  // this can be done by testing 2 rectangles and 4 circles (the corners)
1122 
1123  // Testing the first rectangle dimx + distToLine, dimy:
1124  m_xcliplo = m_padToTestPos.x - padHalfsize.x - distToLine;
1125  m_ycliplo = m_padToTestPos.y - padHalfsize.y;
1126  m_xcliphi = m_padToTestPos.x + padHalfsize.x + distToLine;
1127  m_ycliphi = m_padToTestPos.y + padHalfsize.y;
1128 
1129  if( !checkLine( startPoint, endPoint ) )
1130  return false;
1131 
1132  // Testing the second rectangle dimx , dimy + distToLine
1133  m_xcliplo = m_padToTestPos.x - padHalfsize.x;
1134  m_ycliplo = m_padToTestPos.y - padHalfsize.y - distToLine;
1135  m_xcliphi = m_padToTestPos.x + padHalfsize.x;
1136  m_ycliphi = m_padToTestPos.y + padHalfsize.y + distToLine;
1137 
1138  if( !checkLine( startPoint, endPoint ) )
1139  return false;
1140 
1141  // testing the 4 circles which are the clearance area of each corner:
1142 
1143  // testing the left top corner of the rectangle
1144  startPoint.x = m_padToTestPos.x - padHalfsize.x;
1145  startPoint.y = m_padToTestPos.y - padHalfsize.y;
1146  RotatePoint( &startPoint, m_padToTestPos, orient );
1147  RotatePoint( &startPoint, m_segmAngle );
1148 
1149  if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
1150  return false;
1151 
1152  // testing the right top corner of the rectangle
1153  startPoint.x = m_padToTestPos.x + padHalfsize.x;
1154  startPoint.y = m_padToTestPos.y - padHalfsize.y;
1155  RotatePoint( &startPoint, m_padToTestPos, orient );
1156  RotatePoint( &startPoint, m_segmAngle );
1157 
1158  if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
1159  return false;
1160 
1161  // testing the left bottom corner of the rectangle
1162  startPoint.x = m_padToTestPos.x - padHalfsize.x;
1163  startPoint.y = m_padToTestPos.y + padHalfsize.y;
1164  RotatePoint( &startPoint, m_padToTestPos, orient );
1165  RotatePoint( &startPoint, m_segmAngle );
1166 
1167  if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
1168  return false;
1169 
1170  // testing the right bottom corner of the rectangle
1171  startPoint.x = m_padToTestPos.x + padHalfsize.x;
1172  startPoint.y = m_padToTestPos.y + padHalfsize.y;
1173  RotatePoint( &startPoint, m_padToTestPos, orient );
1174  RotatePoint( &startPoint, m_segmAngle );
1175 
1176  if( !checkMarginToCircle( startPoint, distToLine, m_segmLength ) )
1177  return false;
1178 
1179  break;
1180 
1181  case PAD_SHAPE_TRAPEZOID:
1182  {
1183  wxPoint poly[4];
1184  aPad->BuildPadPolygon( poly, wxSize( 0, 0 ), orient );
1185 
1186  // Move shape to m_padToTestPos
1187  for( int ii = 0; ii < 4; ii++ )
1188  {
1189  poly[ii] += m_padToTestPos;
1190  RotatePoint( &poly[ii], m_segmAngle );
1191  }
1192 
1193  if( !poly2segmentDRC( poly, 4, wxPoint( 0, 0 ),
1194  wxPoint(m_segmLength,0), distToLine ) )
1195  return false;
1196  }
1197  break;
1198 
1199  case PAD_SHAPE_CUSTOM:
1200  {
1201  SHAPE_POLY_SET polyset;
1202  polyset.Append( aPad->GetCustomShapeAsPolygon() );
1203  // The pad can be rotated. calculate the coordinates
1204  // relatives to the segment being tested
1205  // Note, the pad position relative to the segment origin
1206  // is m_padToTestPos
1207  aPad->CustomShapeAsPolygonToBoardPosition( &polyset,
1208  m_padToTestPos, orient );
1209 
1210  // Rotate all coordinates by m_segmAngle, because the segment orient
1211  // is m_segmAngle
1212  // we are using a horizontal segment for test, because we know here
1213  // only the lenght and orientation+ of the segment
1214  // therefore all coordinates of the pad to test must be rotated by
1215  // m_segmAngle (they are already relative to the segment origin)
1216  aPad->CustomShapeAsPolygonToBoardPosition( &polyset,
1217  wxPoint( 0, 0 ), m_segmAngle );
1218 
1219  const SHAPE_LINE_CHAIN& refpoly = polyset.COutline( 0 );
1220 
1221  if( !poly2segmentDRC( (wxPoint*) &refpoly.CPoint( 0 ),
1222  refpoly.PointCount(),
1223  wxPoint( 0, 0 ), wxPoint(m_segmLength,0),
1224  distToLine ) )
1225  return false;
1226  }
1227  break;
1228  }
1229 
1230  return true;
1231 }
1232 
1233 
1240 bool DRC::checkMarginToCircle( wxPoint aCentre, int aRadius, int aLength )
1241 {
1242  if( abs( aCentre.y ) >= aRadius ) // trivial case
1243  return true;
1244 
1245  // Here, distance between aCentre and X axis is < aRadius
1246  if( (aCentre.x > -aRadius ) && ( aCentre.x < (aLength + aRadius) ) )
1247  {
1248  if( (aCentre.x >= 0) && (aCentre.x <= aLength) )
1249  return false; // aCentre is between the starting point and the ending point of the segm
1250 
1251  if( aCentre.x > aLength ) // aCentre is after the ending point
1252  aCentre.x -= aLength; // move aCentre to the starting point of the segment
1253 
1254  if( EuclideanNorm( aCentre ) < aRadius )
1255  // distance between aCentre and the starting point or the ending point is < aRadius
1256  return false;
1257  }
1258 
1259  return true;
1260 }
1261 
1262 
1263 // Helper function used in checkLine::
1264 static inline int USCALE( unsigned arg, unsigned num, unsigned den )
1265 {
1266  int ii;
1267 
1268  ii = KiROUND( ( (double) arg * num ) / den );
1269  return ii;
1270 }
1271 
1272 
1278 bool DRC::checkLine( wxPoint aSegStart, wxPoint aSegEnd )
1279 {
1280 #define WHEN_OUTSIDE return true
1281 #define WHEN_INSIDE
1282  int temp;
1283 
1284  if( aSegStart.x > aSegEnd.x )
1285  std::swap( aSegStart, aSegEnd );
1286 
1287  if( (aSegEnd.x < m_xcliplo) || (aSegStart.x > m_xcliphi) )
1288  {
1289  WHEN_OUTSIDE;
1290  }
1291 
1292  if( aSegStart.y < aSegEnd.y )
1293  {
1294  if( (aSegEnd.y < m_ycliplo) || (aSegStart.y > m_ycliphi) )
1295  {
1296  WHEN_OUTSIDE;
1297  }
1298 
1299  if( aSegStart.y < m_ycliplo )
1300  {
1301  temp = USCALE( (aSegEnd.x - aSegStart.x), (m_ycliplo - aSegStart.y),
1302  (aSegEnd.y - aSegStart.y) );
1303 
1304  if( (aSegStart.x += temp) > m_xcliphi )
1305  {
1306  WHEN_OUTSIDE;
1307  }
1308 
1309  aSegStart.y = m_ycliplo;
1310  WHEN_INSIDE;
1311  }
1312 
1313  if( aSegEnd.y > m_ycliphi )
1314  {
1315  temp = USCALE( (aSegEnd.x - aSegStart.x), (aSegEnd.y - m_ycliphi),
1316  (aSegEnd.y - aSegStart.y) );
1317 
1318  if( (aSegEnd.x -= temp) < m_xcliplo )
1319  {
1320  WHEN_OUTSIDE;
1321  }
1322 
1323  aSegEnd.y = m_ycliphi;
1324  WHEN_INSIDE;
1325  }
1326 
1327  if( aSegStart.x < m_xcliplo )
1328  {
1329  temp = USCALE( (aSegEnd.y - aSegStart.y), (m_xcliplo - aSegStart.x),
1330  (aSegEnd.x - aSegStart.x) );
1331  aSegStart.y += temp;
1332  aSegStart.x = m_xcliplo;
1333  WHEN_INSIDE;
1334  }
1335 
1336  if( aSegEnd.x > m_xcliphi )
1337  {
1338  temp = USCALE( (aSegEnd.y - aSegStart.y), (aSegEnd.x - m_xcliphi),
1339  (aSegEnd.x - aSegStart.x) );
1340  aSegEnd.y -= temp;
1341  aSegEnd.x = m_xcliphi;
1342  WHEN_INSIDE;
1343  }
1344  }
1345  else
1346  {
1347  if( (aSegStart.y < m_ycliplo) || (aSegEnd.y > m_ycliphi) )
1348  {
1349  WHEN_OUTSIDE;
1350  }
1351 
1352  if( aSegStart.y > m_ycliphi )
1353  {
1354  temp = USCALE( (aSegEnd.x - aSegStart.x), (aSegStart.y - m_ycliphi),
1355  (aSegStart.y - aSegEnd.y) );
1356 
1357  if( (aSegStart.x += temp) > m_xcliphi )
1358  {
1359  WHEN_OUTSIDE;
1360  }
1361 
1362  aSegStart.y = m_ycliphi;
1363  WHEN_INSIDE;
1364  }
1365 
1366  if( aSegEnd.y < m_ycliplo )
1367  {
1368  temp = USCALE( (aSegEnd.x - aSegStart.x), (m_ycliplo - aSegEnd.y),
1369  (aSegStart.y - aSegEnd.y) );
1370 
1371  if( (aSegEnd.x -= temp) < m_xcliplo )
1372  {
1373  WHEN_OUTSIDE;
1374  }
1375 
1376  aSegEnd.y = m_ycliplo;
1377  WHEN_INSIDE;
1378  }
1379 
1380  if( aSegStart.x < m_xcliplo )
1381  {
1382  temp = USCALE( (aSegStart.y - aSegEnd.y), (m_xcliplo - aSegStart.x),
1383  (aSegEnd.x - aSegStart.x) );
1384  aSegStart.y -= temp;
1385  aSegStart.x = m_xcliplo;
1386  WHEN_INSIDE;
1387  }
1388 
1389  if( aSegEnd.x > m_xcliphi )
1390  {
1391  temp = USCALE( (aSegStart.y - aSegEnd.y), (aSegEnd.x - m_xcliphi),
1392  (aSegEnd.x - aSegStart.x) );
1393  aSegEnd.y += temp;
1394  aSegEnd.x = m_xcliphi;
1395  WHEN_INSIDE;
1396  }
1397  }
1398 
1399  if( ( (aSegEnd.x + aSegStart.x) / 2 <= m_xcliphi )
1400  && ( (aSegEnd.x + aSegStart.x) / 2 >= m_xcliplo ) \
1401  && ( (aSegEnd.y + aSegStart.y) / 2 <= m_ycliphi )
1402  && ( (aSegEnd.y + aSegStart.y) / 2 >= m_ycliplo ) )
1403  {
1404  return false;
1405  }
1406  else
1407  {
1408  return true;
1409  }
1410 }
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:673
#define DRCE_TOO_SMALL_MICROVIA_DRILL
Too small micro via drill.
Definition: drc.h:74
KICAD_T Type() const
Function Type()
Definition: base_struct.h:227
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 DRCE_TOO_SMALL_VIA
Too small via size.
Definition: drc.h:71
#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)
Creates a marker and fills it in with information but does not add it to the BOARD.
int m_ycliplo
Definition: drc.h:204
const SHAPE_POLY_SET & GetCustomShapeAsPolygon() const
Accessor to the custom shape as one polygon.
Definition: class_pad.h:341
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...
Class BOARD to handle a board.
int GetRoundRectCornerRadius() const
Function GetRoundRectCornerRadius Has meaning only for rounded rect pads.
Definition: class_pad.h:511
#define DRCE_TRACK_ENDS4
2 parallel track segments too close: fine end point test
Definition: drc.h:54
int m_ycliphi
Definition: drc.h:206
virtual PCB_LAYER_ID GetLayer() const override
Function GetLayer returns the primary layer this item is on.
Definition: class_zone.cpp:171
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)
bool Contains(const VECTOR2I &aP, int aSubpolyIndex=-1, bool aIgnoreHoles=false) const
Returns true if a given subpolygon contains the point aP.
#define DRCE_TRACK_NEAR_PAD
pad too close to track
Definition: drc.h:47
Struct VERTEX_INDEX.
int m_segmLength
Definition: drc.h:198
#define COPPERAREA_INSIDE_COPPERAREA
copper area outlines intersect
Definition: drc.h:65
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
wxPoint m_padToTestPos
Definition: drc.h:189
PAD_SHAPE_T GetShape() const
Function GetShape.
Definition: class_pad.h:216
#define abs(a)
Definition: auxiliary.h:84
static const int delta[8][2]
Definition: solve.cpp:112
const wxPoint & GetEnd() const
Definition: class_track.h:119
#define DRCE_TRACK_ENDS2
2 parallel track segments too close: fine start point test
Definition: drc.h:52
#define DRCE_TRACK_SEGMENTS_TOO_CLOSE
2 parallel track segments too close: segm ends between segref ends
Definition: drc.h:55
Functions relatives to tracks, vias and segments used to fill zones.
#define DRCE_BURIED_VIA_NOT_ALLOWED
buried vias are not allowed
Definition: drc.h:92
#define DRCE_TRACK_NEAR_VIA
track too close to via
Definition: drc.h:48
BOARD * m_pcb
Definition: drc.h:209
#define DRCE_TOO_SMALL_MICROVIA
Too small micro via size.
Definition: drc.h:72
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_VIA_HOLE_BIGGER
via&#39;s hole is bigger than its diameter
Definition: drc.h:63
#define COPPERAREA_CLOSE_TO_COPPERAREA
copper area outlines are too close
Definition: drc.h:66
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:455
double ArcTangente(int dy, int dx)
Definition: trigo.cpp:271
std::shared_ptr< NETCLASS > GetNetClass() const
Function GetNetClass returns the NETCLASS for this item.
int GetBoundingRadius() const
Function GetBoundingRadius returns the radius of a minimum sized circle which fully encloses this pad...
Definition: class_pad.h:586
#define DRCE_MICRO_VIA_INCORRECT_LAYER_PAIR
micro via&#39;s layer pair incorrect (layers must be adjacent)
Definition: drc.h:64
Class SHAPE_POLY_SET.
const wxPoint & GetStart() const
Definition: class_track.h:122
int m_TrackMinWidth
track min value for width ((min copper size value
#define DRCE_TRACK_ENDS1
2 parallel track segments too close: fine start point test
Definition: drc.h:51
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.
#define DRCE_TRACK_ENDS3
2 parallel track segments too close: fine end point test
Definition: drc.h:53
#define DRCE_ENDS_PROBLEM2
track ends are too close
Definition: drc.h:58
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...
#define DRCE_ENDS_PROBLEM3
track ends are too close
Definition: drc.h:59
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)
Check the distance from a point to a segment.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
bool m_BlindBuriedViaAllowed
true to allow blind/buried vias
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.h:205
int m_MicroViasMinSize
micro vias (not vias) min diameter
unsigned GetPadCount() const
Function GetPadCount.
bool doEdgeZoneDrc(ZONE_CONTAINER *aArea, int aCornerIndex)
Test a segment in ZONE_CONTAINER * aArea: Test Edge inside other areas Test Edge too close other area...
void LayerPair(PCB_LAYER_ID *top_layer, PCB_LAYER_ID *bottom_layer) const
Function LayerPair Return the 2 layers used by the via (the via actually uses all layers between thes...
#define DRCE_ENDS_PROBLEM4
track ends are too close
Definition: drc.h:60
bool checkClearancePadToPad(D_PAD *aRefPad, D_PAD *aPad)
int GetNetCode() const
Function GetNetCode.
MARKER_PCB * m_currentMarker
Definition: drc.h:180
Definition: seg.h:36
#define DRCE_VIA_NEAR_VIA
via too close to via
Definition: drc.h:49
#define WHEN_INSIDE
#define DRCE_TRACKS_CROSSING
tracks are crossing
Definition: drc.h:56
bool checkClearanceSegmToPad(const D_PAD *aPad, int aSegmentWidth, int aMinDist)
Check the distance from a pad to segment.
#define DRCE_ENDS_PROBLEM5
track ends are too close
Definition: drc.h:61
const std::vector< D_PAD * > GetPads()
Function GetPads returns a reference to a list of all the pads.
wxPoint m_segmEnd
Definition: drc.h:190
void SetLayerSet(LSET aLayerMask)
Definition: class_pad.h:394
SEG Segment(int aIndex)
Function Segment()
bool IsOnCopperLayer() const
Function IsOnCopperLayer.
Definition: class_zone.cpp:177
TRACK * Next() const
Definition: class_track.h:99
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:116
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
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
#define DRCE_VIA_NEAR_TRACK
via too close to track
Definition: drc.h:50
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...
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)
Test the current segment.
Module description (excepted pads)
bool m_MicroViasAllowed
true to allow micro vias
POLYGON & Polygon(int aIndex)
Returns the aIndex-th subpolygon in the set
#define DRCE_MICRO_VIA_NOT_ALLOWED
micro vias are not allowed
Definition: drc.h:91
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
const wxPoint GetPosition() const override
Definition: class_pad.h:220
const VECTOR2I & CPoint(int aIndex) const
Function CPoint()
int m_xcliplo
Definition: drc.h:203
#define DRCE_ENDS_PROBLEM1
track ends are too close
Definition: drc.h:57
#define DRCE_TOO_SMALL_VIA_DRILL
Too small via drill.
Definition: drc.h:73
#define DRCE_TRACK_NEAR_THROUGH_HOLE
thru hole is too close to track
Definition: drc.h:46
int GetZoneClearance() const
Definition: class_zone.h:192
double m_segmAngle
Definition: drc.h:197
#define min(a, b)
Definition: auxiliary.h:85
Class BOARD_DESIGN_SETTINGS contains design settings for a BOARD object.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline) ...
#define DRCE_TOO_SMALL_TRACK_WIDTH
Too small track width.
Definition: drc.h:70
VECTOR2I B
Definition: seg.h:47