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