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