KiCad PCB EDA Suite
drc_test_provider_copper_clearance.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2004-2021 KiCad Developers.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, you may find one here:
18  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19  * or you may search the http://www.gnu.org website for the version 2 license,
20  * or you may write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23 
24 #include <common.h>
25 #include <math_for_graphics.h>
26 #include <board_design_settings.h>
27 #include <footprint.h>
28 #include <pcb_shape.h>
29 #include <pad.h>
30 #include <pcb_track.h>
31 #include <zone.h>
32 
33 #include <geometry/seg.h>
35 #include <geometry/shape_segment.h>
36 
37 #include <drc/drc_engine.h>
38 #include <drc/drc_rtree.h>
39 #include <drc/drc_item.h>
40 #include <drc/drc_rule.h>
42 #include <pcb_dimension.h>
43 
44 /*
45  Copper clearance test. Checks all copper items (pads, vias, tracks, drawings, zones) for their
46  electrical clearance.
47 
48  Errors generated:
49  - DRCE_CLEARANCE
50  - DRCE_HOLE_CLEARANCE
51  - DRCE_TRACKS_CROSSING
52  - DRCE_ZONES_INTERSECT
53  - DRCE_SHORTING_ITEMS
54 */
55 
57 {
58 public:
61  m_drcEpsilon( 0 )
62  {
63  }
64 
66  {
67  }
68 
69  virtual bool Run() override;
70 
71  virtual const wxString GetName() const override
72  {
73  return "clearance";
74  };
75 
76  virtual const wxString GetDescription() const override
77  {
78  return "Tests copper item clearance";
79  }
80 
81  virtual std::set<DRC_CONSTRAINT_T> GetConstraintTypes() const override;
82 
83  int GetNumPhases() const override;
84 
85 private:
86  bool testTrackAgainstItem( PCB_TRACK* track, SHAPE* trackShape, PCB_LAYER_ID layer,
87  BOARD_ITEM* other );
88 
89  void testTrackClearances();
90 
91  bool testPadAgainstItem( PAD* pad, SHAPE* padShape, PCB_LAYER_ID layer, BOARD_ITEM* other );
92 
93  void testPadClearances();
94 
95  void testZonesToZones();
96 
97  void testItemAgainstZones( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer );
98 
99 private:
102 
103  std::vector<ZONE*> m_zones;
104 };
105 
106 
108 {
110  DRC_CONSTRAINT worstConstraint;
111 
112  if( m_drcEngine->QueryWorstConstraint( CLEARANCE_CONSTRAINT, worstConstraint ) )
113  m_largestClearance = worstConstraint.GetValue().Min();
114 
116  m_largestClearance = std::max( m_largestClearance, worstConstraint.GetValue().Min() );
117 
118  if( m_largestClearance <= 0 )
119  {
120  reportAux( "No Clearance constraints found. Tests not run." );
121  return true; // continue with other tests
122  }
123 
125 
126  m_zones.clear();
127 
128  for( ZONE* zone : m_board->Zones() )
129  {
130  if( !zone->GetIsRuleArea() )
131  {
132  m_zones.push_back( zone );
133  m_largestClearance = std::max( m_largestClearance, zone->GetLocalClearance() );
134  }
135  }
136 
137  for( FOOTPRINT* footprint : m_board->Footprints() )
138  {
139  for( PAD* pad : footprint->Pads() )
140  m_largestClearance = std::max( m_largestClearance, pad->GetLocalClearance() );
141 
142  for( ZONE* zone : footprint->Zones() )
143  {
144  if( !zone->GetIsRuleArea() )
145  {
146  m_zones.push_back( zone );
147  m_largestClearance = std::max( m_largestClearance, zone->GetLocalClearance() );
148  }
149  }
150  }
151 
152  reportAux( "Worst clearance : %d nm", m_largestClearance );
153 
154  // This is the number of tests between 2 calls to the progress bar
155  size_t delta = 50;
156  size_t count = 0;
157  size_t ii = 0;
158 
160 
161  auto countItems =
162  [&]( BOARD_ITEM* item ) -> bool
163  {
164  ++count;
165  return true;
166  };
167 
168  auto addToCopperTree =
169  [&]( BOARD_ITEM* item ) -> bool
170  {
171  if( !reportProgress( ii++, count, delta ) )
172  return false;
173 
174  LSET layers = item->GetLayerSet();
175 
176  // Special-case pad holes which pierce all the copper layers
177  if( item->Type() == PCB_PAD_T )
178  {
179  PAD* pad = static_cast<PAD*>( item );
180 
181  if( pad->GetDrillSizeX() > 0 && pad->GetDrillSizeY() > 0 )
182  layers |= LSET::AllCuMask();
183  }
184 
185  for( PCB_LAYER_ID layer : layers.Seq() )
186  {
187  if( IsCopperLayer( layer ) )
188  m_copperTree.Insert( item, layer, m_largestClearance );
189  }
190 
191  return true;
192  };
193 
194  if( !reportPhase( _( "Gathering copper items..." ) ) )
195  return false; // DRC cancelled
196 
197  static const std::vector<KICAD_T> itemTypes = {
201  };
202 
203  forEachGeometryItem( itemTypes, LSET::AllCuMask(), countItems );
204  forEachGeometryItem( itemTypes, LSET::AllCuMask(), addToCopperTree );
205 
206  reportAux( "Testing %d copper items and %d zones...", count, m_zones.size() );
207 
209  {
210  if( !reportPhase( _( "Checking track & via clearances..." ) ) )
211  return false; // DRC cancelled
212 
214  }
216  {
217  if( !reportPhase( _( "Checking hole clearances..." ) ) )
218  return false; // DRC cancelled
219 
221  }
222 
224  {
225  if( !reportPhase( _( "Checking pad clearances..." ) ) )
226  return false; // DRC cancelled
227 
229  }
232  {
233  if( !reportPhase( _( "Checking pads..." ) ) )
234  return false; // DRC cancelled
235 
237  }
238 
240  {
241  if( !reportPhase( _( "Checking copper zone clearances..." ) ) )
242  return false; // DRC cancelled
243 
245  }
247  {
248  if( !reportPhase( _( "Checking zones..." ) ) )
249  return false; // DRC cancelled
250 
252  }
253 
255 
256  return true;
257 }
258 
259 
261  PCB_LAYER_ID layer,
262  BOARD_ITEM* other )
263 {
264  bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
266  DRC_CONSTRAINT constraint;
267  int clearance = -1;
268  int actual;
269  VECTOR2I pos;
270 
271  if( other->Type() == PCB_PAD_T )
272  {
273  PAD* pad = static_cast<PAD*>( other );
274 
275  if( pad->GetAttribute() == PAD_ATTRIB::NPTH && !pad->FlashLayer( layer ) )
276  testClearance = false;
277  }
278 
279  if( testClearance )
280  {
281  constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, track, other, layer );
282  clearance = constraint.GetValue().Min();
283  }
284 
285  if( clearance >= 0 )
286  {
287  // Special processing for track:track intersections
288  if( track->Type() == PCB_TRACE_T && other->Type() == PCB_TRACE_T )
289  {
290  SEG trackSeg( track->GetStart(), track->GetEnd() );
291  SEG otherSeg( track->GetStart(), track->GetEnd() );
292 
293  if( OPT_VECTOR2I intersection = trackSeg.Intersect( otherSeg ) )
294  {
295  std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_TRACKS_CROSSING );
296  drcItem->SetItems( track, other );
297  drcItem->SetViolatingRule( constraint.GetParentRule() );
298 
299  reportViolation( drcItem, (wxPoint) intersection.get() );
300 
302  }
303  }
304 
305  std::shared_ptr<SHAPE> otherShape = DRC_ENGINE::GetShape( other, layer );
306 
307  if( trackShape->Collide( otherShape.get(), clearance - m_drcEpsilon, &actual, &pos ) )
308  {
309  std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
310 
311  m_msg.Printf( _( "(%s clearance %s; actual %s)" ),
312  constraint.GetName(),
313  MessageTextFromValue( userUnits(), clearance ),
314  MessageTextFromValue( userUnits(), actual ) );
315 
316  drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
317  drce->SetItems( track, other );
318  drce->SetViolatingRule( constraint.GetParentRule() );
319 
320  reportViolation( drce, (wxPoint) pos );
321 
323  return false;
324  }
325  }
326 
327  if( testHoles && ( other->Type() == PCB_VIA_T || other->Type() == PCB_PAD_T ) )
328  {
329  std::unique_ptr<SHAPE_SEGMENT> holeShape;
330 
331  if( other->Type() == PCB_VIA_T )
332  {
333  PCB_VIA* via = static_cast<PCB_VIA*>( other );
334  pos = via->GetPosition();
335 
336  if( via->GetLayerSet().Contains( layer ) )
337  holeShape.reset( new SHAPE_SEGMENT( pos, pos, via->GetDrill() ) );
338  }
339  else if( other->Type() == PCB_PAD_T )
340  {
341  PAD* pad = static_cast<PAD*>( other );
342 
343  if( pad->GetDrillSize().x )
344  holeShape.reset( new SHAPE_SEGMENT( *pad->GetEffectiveHoleShape() ) );
345  }
346 
347  if( holeShape )
348  {
349  constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, other, track, layer );
350  clearance = constraint.GetValue().Min();
351 
352  if( clearance > 0 && trackShape->Collide( holeShape.get(),
353  std::max( 0, clearance - m_drcEpsilon ),
354  &actual, &pos ) )
355  {
356  std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
357 
358  m_msg.Printf( _( "(%s clearance %s; actual %s)" ),
359  constraint.GetName(),
360  MessageTextFromValue( userUnits(), clearance ),
361  MessageTextFromValue( userUnits(), actual ) );
362 
363  drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
364  drce->SetItems( track, other );
365  drce->SetViolatingRule( constraint.GetParentRule() );
366 
367  reportViolation( drce, (wxPoint) pos );
368 
370  return false;
371  }
372  }
373  }
374 
375  return true;
376 }
377 
378 
380  PCB_LAYER_ID aLayer )
381 {
382  for( ZONE* zone : m_zones )
383  {
384  if( !zone->GetLayerSet().test( aLayer ) )
385  continue;
386 
387  if( zone->GetNetCode() && aItem->IsConnected() )
388  {
389  if( zone->GetNetCode() == static_cast<BOARD_CONNECTED_ITEM*>( aItem )->GetNetCode() )
390  continue;
391  }
392 
393  if( aItem->GetBoundingBox().Intersects( zone->GetCachedBoundingBox() ) )
394  {
395  bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
397 
398  if( !testClearance && !testHoles )
399  return;
400 
401  DRC_RTREE* zoneTree = m_board->m_CopperZoneRTrees[ zone ].get();
402  EDA_RECT itemBBox = aItem->GetBoundingBox();
403  DRC_CONSTRAINT constraint;
404  int clearance = -1;
405  int actual;
406  VECTOR2I pos;
407 
408  if( zoneTree && testClearance )
409  {
410  constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aItem, zone, aLayer );
411  clearance = constraint.GetValue().Min();
412  }
413 
414  if( clearance >= 0 )
415  {
416  std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aLayer );
417 
418  if( aItem->Type() == PCB_PAD_T )
419  {
420  PAD* pad = static_cast<PAD*>( aItem );
421 
422  if( !pad->FlashLayer( aLayer ) )
423  {
424  if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 )
425  continue;
426 
427  const SHAPE_SEGMENT* hole = pad->GetEffectiveHoleShape();
428  int size = hole->GetWidth();
429 
430  // Note: drill size represents finish size, which means the actual hole
431  // size is the plating thickness larger.
432  if( pad->GetAttribute() == PAD_ATTRIB::PTH )
434 
435  itemShape = std::make_shared<SHAPE_SEGMENT>( hole->GetSeg(), size );
436  }
437  }
438 
439  if( zoneTree && zoneTree->QueryColliding( itemBBox, itemShape.get(), aLayer,
440  std::max( 0, clearance - m_drcEpsilon ),
441  &actual, &pos ) )
442  {
443  std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
444 
445  m_msg.Printf( _( "(%s clearance %s; actual %s)" ),
446  constraint.GetName(),
447  MessageTextFromValue( userUnits(), clearance ),
448  MessageTextFromValue( userUnits(), actual ) );
449 
450  drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
451  drce->SetItems( aItem, zone );
452  drce->SetViolatingRule( constraint.GetParentRule() );
453 
454  reportViolation( drce, (wxPoint) pos );
455  }
456  }
457 
458  if( testHoles && ( aItem->Type() == PCB_VIA_T || aItem->Type() == PCB_PAD_T ) )
459  {
460  std::unique_ptr<SHAPE_SEGMENT> holeShape;
461 
462  if( aItem->Type() == PCB_VIA_T )
463  {
464  PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
465  pos = via->GetPosition();
466 
467  if( via->GetLayerSet().Contains( aLayer ) )
468  holeShape.reset( new SHAPE_SEGMENT( pos, pos, via->GetDrill() ) );
469  }
470  else if( aItem->Type() == PCB_PAD_T )
471  {
472  PAD* pad = static_cast<PAD*>( aItem );
473 
474  if( pad->GetDrillSize().x )
475  holeShape.reset( new SHAPE_SEGMENT( *pad->GetEffectiveHoleShape() ) );
476  }
477 
478  if( holeShape )
479  {
480  constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, aItem, zone,
481  aLayer );
482  clearance = constraint.GetValue().Min();
483 
484  if( zoneTree && zoneTree->QueryColliding( itemBBox, holeShape.get(), aLayer,
485  std::max( 0, clearance - m_drcEpsilon ),
486  &actual, &pos ) )
487  {
488  std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
489 
490  m_msg.Printf( _( "(%s clearance %s; actual %s)" ),
491  constraint.GetName(),
492  MessageTextFromValue( userUnits(), clearance ),
493  MessageTextFromValue( userUnits(), actual ) );
494 
495  drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
496  drce->SetItems( aItem, zone );
497  drce->SetViolatingRule( constraint.GetParentRule() );
498 
499  reportViolation( drce, (wxPoint) pos );
500  }
501  }
502  }
503  }
504  }
505 }
506 
507 
509 {
510  // This is the number of tests between 2 calls to the progress bar
511  const int delta = 100;
512  int ii = 0;
513 
514  reportAux( "Testing %d tracks & vias...", m_board->Tracks().size() );
515 
516  std::map< std::pair<BOARD_ITEM*, BOARD_ITEM*>, int> checkedPairs;
517 
518  for( PCB_TRACK* track : m_board->Tracks() )
519  {
520  if( !reportProgress( ii++, m_board->Tracks().size(), delta ) )
521  break;
522 
523  for( PCB_LAYER_ID layer : track->GetLayerSet().Seq() )
524  {
525  std::shared_ptr<SHAPE> trackShape = track->GetEffectiveShape( layer );
526 
527  m_copperTree.QueryColliding( track, layer, layer,
528  // Filter:
529  [&]( BOARD_ITEM* other ) -> bool
530  {
531  // It would really be better to know what particular nets a nettie
532  // should allow, but for now it is what it is.
533  if( DRC_ENGINE::IsNetTie( other ) )
534  return false;
535 
536  auto otherCItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( other );
537 
538  if( otherCItem && otherCItem->GetNetCode() == track->GetNetCode() )
539  return false;
540 
541  BOARD_ITEM* a = track;
542  BOARD_ITEM* b = other;
543 
544  // store canonical order so we don't collide in both directions
545  // (a:b and b:a)
546  if( static_cast<void*>( a ) > static_cast<void*>( b ) )
547  std::swap( a, b );
548 
549  if( checkedPairs.count( { a, b } ) )
550  {
551  return false;
552  }
553  else
554  {
555  checkedPairs[ { a, b } ] = 1;
556  return true;
557  }
558  },
559  // Visitor:
560  [&]( BOARD_ITEM* other ) -> bool
561  {
562  return testTrackAgainstItem( track, trackShape.get(), layer, other );
563  },
565 
566  testItemAgainstZones( track, layer );
567  }
568  }
569 }
570 
571 
573  PCB_LAYER_ID layer,
574  BOARD_ITEM* other )
575 {
576  bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
577  bool testShorting = !m_drcEngine->IsErrorLimitExceeded( DRCE_SHORTING_ITEMS );
579 
580  // Disable some tests *within* a single footprint
581  if( other->GetParent() == pad->GetParent() )
582  {
583  FOOTPRINT* fp = static_cast<FOOTPRINT*>( pad->GetParent() );
584 
585  // Graphic items are allowed to act as net-ties within their own footprint
586  if( fp->IsNetTie() && ( other->Type() == PCB_FP_SHAPE_T || other->Type() == PCB_PAD_T ) )
587  testClearance = false;
588 
589  // No hole testing within a footprint
590  testHoles = false;
591  }
592 
593  PAD* otherPad = nullptr;
594  PCB_VIA* otherVia = nullptr;
595 
596  if( other->Type() == PCB_PAD_T )
597  otherPad = static_cast<PAD*>( other );
598 
599  if( other->Type() == PCB_VIA_T )
600  otherVia = static_cast<PCB_VIA*>( other );
601 
602  if( !IsCopperLayer( layer ) )
603  testClearance = false;
604 
605  // A NPTH has no cylinder, but it may still have pads on some layers
606  if( pad->GetAttribute() == PAD_ATTRIB::NPTH && !pad->FlashLayer( layer ) )
607  testClearance = false;
608 
609  if( otherPad && otherPad->GetAttribute() == PAD_ATTRIB::NPTH && !otherPad->FlashLayer( layer ) )
610  testClearance = false;
611 
612  // Track clearances are tested in testTrackClearances()
613  if( dynamic_cast<PCB_TRACK*>( other) )
614  testClearance = false;
615 
616  int padNet = pad->GetNetCode();
617  int otherPadNet = otherPad ? otherPad->GetNetCode() : 0;
618  int otherViaNet = otherVia ? otherVia->GetNetCode() : 0;
619 
620  // Pads and vias of the same (defined) net get a waiver on clearance and hole tests
621  if( ( otherPadNet && otherPadNet == padNet ) || ( otherViaNet && otherViaNet == padNet ) )
622  {
623  testClearance = false;
624  testHoles = false;
625  }
626 
627  if( !( pad->GetDrillSize().x > 0 )
628  && !( otherPad && otherPad->GetDrillSize().x > 0 )
629  && !( otherVia && otherVia->GetDrill() > 0 ) )
630  {
631  testHoles = false;
632  }
633 
634  if( !testClearance && !testShorting && !testHoles )
635  return false;
636 
637  std::shared_ptr<SHAPE> otherShape = DRC_ENGINE::GetShape( other, layer );
638  DRC_CONSTRAINT constraint;
639  int clearance;
640  int actual;
641  VECTOR2I pos;
642 
643  if( otherPad && pad->SameLogicalPadAs( otherPad ) )
644  {
645  // If pads are equivalent (ie: from the same footprint with the same pad number)...
646  // ... and have nets...
647  // then they must be the same net
648  if( pad->GetNetCode() && otherPad->GetNetCode()
649  && pad->GetNetCode() != otherPad->GetNetCode()
650  && testShorting )
651  {
652  std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_SHORTING_ITEMS );
653 
654  m_msg.Printf( _( "(nets %s and %s)" ),
655  pad->GetNetname(),
656  otherPad->GetNetname() );
657 
658  drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
659  drce->SetItems( pad, otherPad );
660 
661  reportViolation( drce, otherPad->GetPosition() );
662  }
663 
664  return true;
665  }
666 
667  if( testClearance )
668  {
669  constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, pad, other, layer );
670  clearance = constraint.GetValue().Min();
671 
672  if( clearance > 0 && padShape->Collide( otherShape.get(),
673  std::max( 0, clearance - m_drcEpsilon ),
674  &actual, &pos ) )
675  {
676  std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
677 
678  m_msg.Printf( _( "(%s clearance %s; actual %s)" ),
679  constraint.GetName(),
680  MessageTextFromValue( userUnits(), clearance ),
681  MessageTextFromValue( userUnits(), actual ) );
682 
683  drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
684  drce->SetItems( pad, other );
685  drce->SetViolatingRule( constraint.GetParentRule() );
686 
687  reportViolation( drce, (wxPoint) pos );
688  testHoles = false; // No need for multiple violations
689  }
690  }
691 
692  if( testHoles )
693  {
694  constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, pad, other, layer );
695  clearance = constraint.GetValue().Min();
696  }
697 
698  if( testHoles && otherPad && pad->FlashLayer( layer ) && otherPad->GetDrillSize().x )
699  {
700  if( clearance > 0 && padShape->Collide( otherPad->GetEffectiveHoleShape(),
701  std::max( 0, clearance - m_drcEpsilon ),
702  &actual, &pos ) )
703  {
704  std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
705 
706  m_msg.Printf( _( "(%s clearance %s; actual %s)" ),
707  constraint.GetName(),
708  MessageTextFromValue( userUnits(), clearance ),
709  MessageTextFromValue( userUnits(), actual ) );
710 
711  drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
712  drce->SetItems( pad, other );
713  drce->SetViolatingRule( constraint.GetParentRule() );
714 
715  reportViolation( drce, (wxPoint) pos );
716  testHoles = false; // No need for multiple violations
717  }
718  }
719 
720  if( testHoles && otherPad && otherPad->FlashLayer( layer ) && pad->GetDrillSize().x )
721  {
722  if( clearance >= 0 && otherShape->Collide( pad->GetEffectiveHoleShape(),
723  std::max( 0, clearance - m_drcEpsilon ),
724  &actual, &pos ) )
725  {
726  std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
727 
728  m_msg.Printf( _( "(%s clearance %s; actual %s)" ),
729  constraint.GetName(),
730  MessageTextFromValue( userUnits(), clearance ),
731  MessageTextFromValue( userUnits(), actual ) );
732 
733  drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
734  drce->SetItems( pad, other );
735  drce->SetViolatingRule( constraint.GetParentRule() );
736 
737  reportViolation( drce, (wxPoint) pos );
738  testHoles = false; // No need for multiple violations
739  }
740  }
741 
742  if( testHoles && otherVia && otherVia->IsOnLayer( layer ) )
743  {
744  pos = otherVia->GetPosition();
745  otherShape.reset( new SHAPE_SEGMENT( pos, pos, otherVia->GetDrill() ) );
746 
747  if( clearance > 0 && padShape->Collide( otherShape.get(),
748  std::max( 0, clearance - m_drcEpsilon ),
749  &actual, &pos ) )
750  {
751  std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
752 
753  m_msg.Printf( _( "(%s clearance %s; actual %s)" ),
754  constraint.GetName(),
755  MessageTextFromValue( userUnits(), clearance ),
756  MessageTextFromValue( userUnits(), actual ) );
757 
758  drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
759  drce->SetItems( pad, otherVia );
760  drce->SetViolatingRule( constraint.GetParentRule() );
761 
762  reportViolation( drce, (wxPoint) pos );
763  }
764  }
765 
766  return true;
767 }
768 
769 
771 {
772  const int delta = 50; // This is the number of tests between 2 calls to the progress bar
773 
774  size_t count = 0;
775 
776  for( FOOTPRINT* footprint : m_board->Footprints() )
777  count += footprint->Pads().size();
778 
779  reportAux( "Testing %d pads...", count );
780 
781  int ii = 0;
782  std::map< std::pair<BOARD_ITEM*, BOARD_ITEM*>, int> checkedPairs;
783 
784  for( FOOTPRINT* footprint : m_board->Footprints() )
785  {
786  for( PAD* pad : footprint->Pads() )
787  {
788  if( !reportProgress( ii++, count, delta ) )
789  break;
790 
791  for( PCB_LAYER_ID layer : pad->GetLayerSet().Seq() )
792  {
793  std::shared_ptr<SHAPE> padShape = DRC_ENGINE::GetShape( pad, layer );
794 
795  m_copperTree.QueryColliding( pad, layer, layer,
796  // Filter:
797  [&]( BOARD_ITEM* other ) -> bool
798  {
799  BOARD_ITEM* a = pad;
800  BOARD_ITEM* b = other;
801 
802  // store canonical order so we don't collide in both directions
803  // (a:b and b:a)
804  if( static_cast<void*>( a ) > static_cast<void*>( b ) )
805  std::swap( a, b );
806 
807  if( checkedPairs.count( { a, b } ) )
808  {
809  return false;
810  }
811  else
812  {
813  checkedPairs[ { a, b } ] = 1;
814  return true;
815  }
816  },
817  // Visitor
818  [&]( BOARD_ITEM* other ) -> bool
819  {
820  return testPadAgainstItem( pad, padShape.get(), layer, other );
821  },
823 
824  testItemAgainstZones( pad, layer );
825  }
826  }
827  }
828 }
829 
830 
832 {
833  const int delta = 50; // This is the number of tests between 2 calls to the progress bar
834 
835  SHAPE_POLY_SET buffer;
836  SHAPE_POLY_SET* boardOutline = nullptr;
837 
838  if( m_board->GetBoardPolygonOutlines( buffer ) )
839  boardOutline = &buffer;
840 
841  for( int layer_id = F_Cu; layer_id <= B_Cu; ++layer_id )
842  {
843  PCB_LAYER_ID layer = static_cast<PCB_LAYER_ID>( layer_id );
844  std::vector<SHAPE_POLY_SET> smoothed_polys;
845  smoothed_polys.resize( m_zones.size() );
846 
847  // Skip over layers not used on the current board
848  if( !m_board->IsLayerEnabled( layer ) )
849  continue;
850 
851  for( size_t ii = 0; ii < m_zones.size(); ii++ )
852  {
853  if( m_zones[ii]->IsOnLayer( layer ) )
854  m_zones[ii]->BuildSmoothedPoly( smoothed_polys[ii], layer, boardOutline );
855  }
856 
857  // iterate through all areas
858  for( size_t ia = 0; ia < m_zones.size(); ia++ )
859  {
860  if( !reportProgress( layer_id * m_zones.size() + ia, B_Cu * m_zones.size(), delta ) )
861  break;
862 
863  ZONE* zoneRef = m_zones[ia];
864 
865  if( !zoneRef->IsOnLayer( layer ) )
866  continue;
867 
868  // If we are testing a single zone, then iterate through all other zones
869  // Otherwise, we have already tested the zone combination
870  for( size_t ia2 = ia + 1; ia2 < m_zones.size(); ia2++ )
871  {
872  ZONE* zoneToTest = m_zones[ia2];
873 
874  if( zoneRef == zoneToTest )
875  continue;
876 
877  // test for same layer
878  if( !zoneToTest->IsOnLayer( layer ) )
879  continue;
880 
881  // Test for same net
882  if( zoneRef->GetNetCode() == zoneToTest->GetNetCode()
883  && zoneRef->GetNetCode() >= 0 )
884  continue;
885 
886  // test for different priorities
887  if( zoneRef->GetPriority() != zoneToTest->GetPriority() )
888  continue;
889 
890  // rule areas may overlap at will
891  if( zoneRef->GetIsRuleArea() || zoneToTest->GetIsRuleArea() )
892  continue;
893 
894  // Examine a candidate zone: compare zoneToTest to zoneRef
895 
896  // Get clearance used in zone to zone test.
897  auto constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, zoneRef, zoneToTest,
898  layer );
899  int zone2zoneClearance = constraint.GetValue().Min();
900 
901  // test for some corners of zoneRef inside zoneToTest
902  for( auto iterator = smoothed_polys[ia].IterateWithHoles(); iterator; iterator++ )
903  {
904  VECTOR2I currentVertex = *iterator;
905  wxPoint pt( currentVertex.x, currentVertex.y );
906 
907  if( smoothed_polys[ia2].Contains( currentVertex ) )
908  {
909  std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_ZONES_INTERSECT );
910  drce->SetItems( zoneRef, zoneToTest );
911  drce->SetViolatingRule( constraint.GetParentRule() );
912 
913  reportViolation( drce, pt );
914  }
915  }
916 
917  // test for some corners of zoneToTest inside zoneRef
918  for( auto iterator = smoothed_polys[ia2].IterateWithHoles(); iterator; iterator++ )
919  {
920  VECTOR2I currentVertex = *iterator;
921  wxPoint pt( currentVertex.x, currentVertex.y );
922 
923  if( smoothed_polys[ia].Contains( currentVertex ) )
924  {
925  std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_ZONES_INTERSECT );
926  drce->SetItems( zoneToTest, zoneRef );
927  drce->SetViolatingRule( constraint.GetParentRule() );
928 
929  reportViolation( drce, pt );
930  }
931  }
932 
933  // Iterate through all the segments of refSmoothedPoly
934  std::map<wxPoint, int> conflictPoints;
935 
936  for( auto refIt = smoothed_polys[ia].IterateSegmentsWithHoles(); refIt; refIt++ )
937  {
938  // Build ref segment
939  SEG refSegment = *refIt;
940 
941  // Iterate through all the segments in smoothed_polys[ia2]
942  for( auto testIt = smoothed_polys[ia2].IterateSegmentsWithHoles(); testIt; testIt++ )
943  {
944  // Build test segment
945  SEG testSegment = *testIt;
946  wxPoint pt;
947 
948  int ax1, ay1, ax2, ay2;
949  ax1 = refSegment.A.x;
950  ay1 = refSegment.A.y;
951  ax2 = refSegment.B.x;
952  ay2 = refSegment.B.y;
953 
954  int bx1, by1, bx2, by2;
955  bx1 = testSegment.A.x;
956  by1 = testSegment.A.y;
957  bx2 = testSegment.B.x;
958  by2 = testSegment.B.y;
959 
960  int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2,
961  0,
962  ax1, ay1, ax2, ay2,
963  0,
964  zone2zoneClearance,
965  &pt.x, &pt.y );
966 
967  if( d < zone2zoneClearance )
968  {
969  if( conflictPoints.count( pt ) )
970  conflictPoints[ pt ] = std::min( conflictPoints[ pt ], d );
971  else
972  conflictPoints[ pt ] = d;
973  }
974  }
975  }
976 
977  for( const std::pair<const wxPoint, int>& conflict : conflictPoints )
978  {
979  int actual = conflict.second;
980  std::shared_ptr<DRC_ITEM> drce;
981 
982  if( actual <= 0 )
983  {
985  }
986  else
987  {
989 
990  m_msg.Printf( _( "(%s clearance %s; actual %s)" ),
991  constraint.GetName(),
992  MessageTextFromValue( userUnits(), zone2zoneClearance ),
993  MessageTextFromValue( userUnits(), conflict.second ) );
994 
995  drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + m_msg );
996  }
997 
998  drce->SetItems( zoneRef, zoneToTest );
999  drce->SetViolatingRule( constraint.GetParentRule() );
1000 
1001  reportViolation( drce, conflict.first );
1002  }
1003  }
1004  }
1005  }
1006 }
1007 
1008 
1010 {
1011  return 4;
1012 }
1013 
1014 
1016 {
1018 }
1019 
1020 
1021 namespace detail
1022 {
1024 }
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:750
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
Definition: pcb_track.cpp:387
wxString MessageTextFromValue(EDA_UNITS aUnits, int aValue, bool aAddUnitLabel, EDA_DATA_TYPE aType)
Convert a value to a string using double notation.
Definition: base_units.cpp:104
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition: typeinfo.h:100
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:101
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition: drc_item.cpp:266
class FP_TEXT, text in a footprint
Definition: typeinfo.h:92
bool GetBoardPolygonOutlines(SHAPE_POLY_SET &aOutlines, OUTLINE_ERROR_HANDLER *aErrorHandler=nullptr)
Extract the board outlines and build a closed polygon from lines, arcs and circle items on edge cut l...
Definition: board.cpp:1880
ZONES & Zones()
Definition: board.h:239
wxPoint GetPosition() const override
Definition: pcb_track.h:392
const wxPoint & GetEnd() const
Definition: pcb_track.h:105
unsigned GetPriority() const
Definition: zone.h:122
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition: zone.h:733
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:49
static std::shared_ptr< SHAPE > GetShape(BOARD_ITEM *aItem, PCB_LAYER_ID aLayer)
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const wxPoint &aMarkerPos)
void testItemAgainstZones(BOARD_ITEM *aItem, PCB_LAYER_ID aLayer)
int GetHolePlatingThickness() const
Pad & via drills are finish size.
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition: typeinfo.h:102
bool IsErrorLimitExceeded(int error_code)
class PCB_TEXT, text on a layer
Definition: typeinfo.h:91
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:97
virtual bool reportProgress(int aCount, int aSize, int aDelta)
const SHAPE_SEGMENT * GetEffectiveHoleShape() const
Return a SHAPE object representing the pad's hole.
Definition: pad.cpp:304
virtual bool IsOnLayer(PCB_LAYER_ID) const override
Test to see if this object is on the given layer.
Definition: zone.cpp:320
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:93
class PAD, a pad in a footprint
Definition: typeinfo.h:89
T Min() const
Definition: minoptmax.h:33
bool IsLayerEnabled(PCB_LAYER_ID aLayer) const
A proxy function that calls the correspondent function in m_BoardSettings tests whether a given layer...
Definition: board.cpp:493
static bool IsNetTie(BOARD_ITEM *aItem)
LSEQ Seq(const PCB_LAYER_ID *aWishListSequence, unsigned aCount) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:411
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:590
virtual void reportRuleStatistics()
virtual bool Collide(const VECTOR2I &aP, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const
Check if the boundary of shape (this) lies closer to the point aP than aClearance,...
Definition: shape.h:165
wxString GetName() const
Definition: drc_rule.h:130
const SEG & GetSeg() const
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:95
Plated through hole pad.
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
DRC_RULE * GetParentRule() const
Definition: drc_rule.h:126
const wxSize & GetDrillSize() const
Definition: pad.h:243
like PAD_PTH, but not plated
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
bool FlashLayer(int aLayer) const
Check to see whether the pad should be flashed on the specific layer.
Definition: pad.cpp:221
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:504
bool QueryWorstConstraint(DRC_CONSTRAINT_T aRuleId, DRC_CONSTRAINT &aConstraint)
OPT< VECTOR2I > OPT_VECTOR2I
Definition: seg.h:38
BOARD * GetBoard() const
Definition: drc_engine.h:88
virtual bool reportPhase(const wxString &aStageName)
Represent a set of closed polygons.
virtual std::set< DRC_CONSTRAINT_T > GetConstraintTypes() const override
FOOTPRINTS & Footprints()
Definition: board.h:233
bool GetReportAllTrackErrors() const
Definition: drc_engine.h:156
int GetDrill() const
Function GetDrill returns the local drill setting for this PCB_VIA.
Definition: pcb_track.h:462
bool IsNetTie() const
Definition: footprint.h:243
void clear()
Remove all items from the RTree.
Definition: drc_rtree.h:120
#define _(s)
bool testPadAgainstItem(PAD *pad, SHAPE *padShape, PCB_LAYER_ID layer, BOARD_ITEM *other)
Handle a list of polygons defining a copper zone.
Definition: zone.h:56
An abstract shape on 2D plane.
Definition: shape.h:116
EDA_UNITS userUnits() const
DRC_CONSTRAINT EvalRules(DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
Definition: drc_engine.cpp:765
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition: typeinfo.h:99
bool IsCopperLayer(LAYER_NUM aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:796
bool testTrackAgainstItem(PCB_TRACK *track, SHAPE *trackShape, PCB_LAYER_ID layer, BOARD_ITEM *other)
Definition: seg.h:40
virtual const wxString GetName() const override
int forEachGeometryItem(const std::vector< KICAD_T > &aTypes, LSET aLayers, const std::function< bool(BOARD_ITEM *)> &aFunc)
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:122
wxPoint GetPosition() const override
Definition: pad.h:178
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:65
PAD_ATTRIB GetAttribute() const
Definition: pad.h:371
DRC_ENGINE * m_drcEngine
Definition: layer_ids.h:71
VECTOR2I A
Definition: seg.h:48
Handle the component boundary box.
Definition: eda_rect.h:42
The common library.
void Insert(BOARD_ITEM *aItem, PCB_LAYER_ID aLayer, int aWorstClearance=0)
Insert an item into the tree on a particular layer with an optional worst clearance.
Definition: drc_rtree.h:86
bool Intersects(const EDA_RECT &aRect) const
Test for a common area between rectangles.
Definition: eda_rect.cpp:150
std::map< ZONE *, std::unique_ptr< DRC_RTREE > > m_CopperZoneRTrees
Definition: board.h:1096
virtual std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER) const
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
Definition: board_item.cpp:167
constexpr int delta
virtual bool IsConnected() const
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
Definition: board_item.h:103
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition: typeinfo.h:103
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
virtual const EDA_RECT GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition: eda_item.cpp:75
virtual const wxString GetDescription() const override
Definition: pad.h:57
Implement an R-tree for fast spatial and layer indexing of connectable items.
Definition: drc_rtree.h:44
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:135
int GetWidth() const
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:90
TRACKS & Tracks()
Definition: board.h:230
virtual void reportAux(wxString fmt,...)
const wxPoint & GetStart() const
Definition: pcb_track.h:108
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:113
int QueryColliding(BOARD_ITEM *aRefItem, PCB_LAYER_ID aRefLayer, PCB_LAYER_ID aTargetLayer, std::function< bool(BOARD_ITEM *)> aFilter=nullptr, std::function< bool(BOARD_ITEM *)> aVisitor=nullptr, int aClearance=0) const
This is a fast test which essentially does bounding-box overlap given a worst-case clearance.
Definition: drc_rtree.h:165
VECTOR2I B
Definition: seg.h:49