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