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-2022 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>
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>
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{
58public:
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 wxT( "clearance" );
74 };
75
76 virtual const wxString GetDescription() const override
77 {
78 return wxT( "Tests copper item clearance" );
79 }
80
81private:
90 bool testTrackAgainstItem( PCB_TRACK* track, SHAPE* trackShape, PCB_LAYER_ID layer,
91 BOARD_ITEM* other );
92
94
95 bool testPadAgainstItem( PAD* pad, SHAPE* padShape, PCB_LAYER_ID layer, BOARD_ITEM* other );
96
97 void testPadClearances();
98
99 void testZonesToZones();
100
101 void testItemAgainstZone( BOARD_ITEM* aItem, ZONE* aZone, PCB_LAYER_ID aLayer );
102
103 typedef struct checked
104 {
106 : layers(), has_error( false ) {}
107
109 : layers( aLayer ), has_error( false ) {}
110
114
115private:
117};
118
119
121{
123
124 if( m_board->m_DRCMaxClearance <= 0 )
125 {
126 reportAux( wxT( "No Clearance constraints found. Tests not run." ) );
127 return true; // continue with other tests
128 }
129
131
133 {
134 if( !reportPhase( _( "Checking track & via clearances..." ) ) )
135 return false; // DRC cancelled
136
138 }
140 {
141 if( !reportPhase( _( "Checking hole clearances..." ) ) )
142 return false; // DRC cancelled
143
145 }
146
148 {
149 if( !reportPhase( _( "Checking pad clearances..." ) ) )
150 return false; // DRC cancelled
151
153 }
156 {
157 if( !reportPhase( _( "Checking pads..." ) ) )
158 return false; // DRC cancelled
159
161 }
162
164 {
165 if( !reportPhase( _( "Checking copper zone clearances..." ) ) )
166 return false; // DRC cancelled
167
169 }
171 {
172 if( !reportPhase( _( "Checking zones..." ) ) )
173 return false; // DRC cancelled
174
176 }
177
179
180 return !m_drcEngine->IsCancelled();
181}
182
183
185 PCB_LAYER_ID layer,
186 BOARD_ITEM* other )
187{
188 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
190 DRC_CONSTRAINT constraint;
191 int clearance = -1;
192 int actual;
193 VECTOR2I pos;
194 bool has_error = false;
195
196 std::shared_ptr<SHAPE> otherShape = other->GetEffectiveShape( layer );
197
198 if( other->Type() == PCB_PAD_T )
199 {
200 PAD* pad = static_cast<PAD*>( other );
201
202 if( pad->GetAttribute() == PAD_ATTRIB::NPTH && !pad->FlashLayer( layer ) )
203 testClearance = false;
204 }
205
206 if( testClearance )
207 {
208 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, track, other, layer );
209 clearance = constraint.GetValue().Min();
210 }
211
212 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
213 {
214 // Special processing for track:track intersections
215 if( track->Type() == PCB_TRACE_T && other->Type() == PCB_TRACE_T )
216 {
217 SEG trackSeg( track->GetStart(), track->GetEnd() );
218 SEG otherSeg( track->GetStart(), track->GetEnd() );
219
220 if( OPT_VECTOR2I intersection = trackSeg.Intersect( otherSeg ) )
221 {
222 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_TRACKS_CROSSING );
223 drcItem->SetItems( track, other );
224 drcItem->SetViolatingRule( constraint.GetParentRule() );
225
226 reportViolation( drcItem, *intersection, layer );
227
228 return false;
229 }
230 }
231
232 if( trackShape->Collide( otherShape.get(), clearance - m_drcEpsilon, &actual, &pos ) )
233 {
234 if( m_drcEngine->IsNetTieExclusion( track->GetNetCode(), layer, pos, other ) )
235 {
236 // Collision occurred as track was entering a pad marked as a net-tie. We
237 // allow these.
238 }
239 else
240 {
241 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
242 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
243 constraint.GetName(),
244 clearance,
245 actual );
246
247 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
248 drce->SetItems( track, other );
249 drce->SetViolatingRule( constraint.GetParentRule() );
250
251 reportViolation( drce, pos, layer );
252 has_error = true;
253
255 return false;
256 }
257 }
258 }
259
260 if( testHoles && ( track->HasHole() || other->HasHole() ) )
261 {
262 std::array<BOARD_ITEM*, 2> a{ track, other };
263 std::array<BOARD_ITEM*, 2> b{ other, track };
264 std::array<SHAPE*, 2> a_shape{ trackShape, otherShape.get() };
265
266 for( size_t ii = 0; ii < 2; ++ii )
267 {
268 std::shared_ptr<SHAPE_SEGMENT> holeShape;
269
270 // We only test a track item here against an item with a hole.
271 // If either case is not valid, simply move on
272 if( !( dynamic_cast<PCB_TRACK*>( a[ii] ) ) || !b[ii]->HasHole() )
273 {
274 continue;
275 }
276 if( b[ii]->Type() == PCB_VIA_T )
277 {
278 if( b[ii]->GetLayerSet().Contains( layer ) )
279 holeShape = b[ii]->GetEffectiveHoleShape();
280 }
281 else
282 {
283 holeShape = b[ii]->GetEffectiveHoleShape();
284 }
285
286 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, b[ii], a[ii], layer );
287 clearance = constraint.GetValue().Min();
288
289 // Test for hole to item clearance even if clearance is 0, because the item cannot be
290 // inside (or intersect) the hole.
291 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE )
292 {
293 if( a_shape[ii]->Collide( holeShape.get(), std::max( 0, clearance - m_drcEpsilon ),
294 &actual, &pos ) )
295 {
296 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
297 wxString msg = formatMsg( clearance ? _( "(%s clearance %s; actual %s)" )
298 : _( "(%s clearance %s; actual < 0)" ),
299 constraint.GetName(),
300 clearance,
301 actual );
302
303 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
304 drce->SetItems( a[ii], b[ii] );
305 drce->SetViolatingRule( constraint.GetParentRule() );
306
307 reportViolation( drce, pos, layer );
308 return false;
309 }
310 }
311 }
312 }
313
314 return !has_error;
315}
316
317
319 PCB_LAYER_ID aLayer )
320{
321 if( !aZone->GetLayerSet().test( aLayer ) )
322 return;
323
324 if( aZone->GetNetCode() && aItem->IsConnected() )
325 {
326 if( aZone->GetNetCode() == static_cast<BOARD_CONNECTED_ITEM*>( aItem )->GetNetCode() )
327 return;
328 }
329
330 BOX2I itemBBox = aItem->GetBoundingBox();
331 BOX2I worstCaseBBox = itemBBox;
332
333 worstCaseBBox.Inflate( m_board->m_DRCMaxClearance );
334
335 if( !worstCaseBBox.Intersects( aZone->GetBoundingBox() ) )
336 return;
337
338 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
340
341 if( !testClearance && !testHoles )
342 return;
343
344 DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ aZone ].get();
345
346 if( !zoneTree )
347 return;
348
349 DRC_CONSTRAINT constraint;
350 int clearance = -1;
351 int actual;
352 VECTOR2I pos;
353
354 if( aItem->Type() == PCB_PAD_T )
355 {
356 PAD* pad = static_cast<PAD*>( aItem );
357 bool flashedPad = pad->FlashLayer( aLayer );
358 bool platedHole = pad->HasHole() && pad->GetAttribute() == PAD_ATTRIB::PTH;
359
360 if( !flashedPad && !platedHole )
361 testClearance = false;
362 }
363
364 if( testClearance )
365 {
366 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aItem, aZone, aLayer );
367 clearance = constraint.GetValue().Min();
368 }
369
370 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
371 {
372 std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aLayer, FLASHING::DEFAULT );
373
374 if( zoneTree->QueryColliding( itemBBox, itemShape.get(), aLayer,
375 std::max( 0, clearance - m_drcEpsilon ), &actual, &pos ) )
376 {
377 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
378 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
379 constraint.GetName(),
380 clearance,
381 actual );
382
383 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
384 drce->SetItems( aItem, aZone );
385 drce->SetViolatingRule( constraint.GetParentRule() );
386
387 reportViolation( drce, pos, aLayer );
388 }
389 }
390
391 if( testHoles && aItem->HasHole() )
392 {
393 std::shared_ptr<SHAPE_SEGMENT> holeShape;
394
395 if( aItem->Type() == PCB_VIA_T )
396 {
397 if( aItem->GetLayerSet().Contains( aLayer ) )
398 holeShape = aItem->GetEffectiveHoleShape();
399 }
400 else
401 {
402 holeShape = aItem->GetEffectiveHoleShape();
403 }
404
405 if( holeShape )
406 {
407 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, aItem, aZone, aLayer );
408 clearance = constraint.GetValue().Min();
409
410 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
411 {
412 if( zoneTree->QueryColliding( itemBBox, holeShape.get(), aLayer,
413 std::max( 0, clearance - m_drcEpsilon ),
414 &actual, &pos ) )
415 {
416 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
417 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
418 constraint.GetName(),
419 clearance,
420 actual );
421
422 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
423 drce->SetItems( aItem, aZone );
424 drce->SetViolatingRule( constraint.GetParentRule() );
425
426 reportViolation( drce, pos, aLayer );
427 }
428 }
429 }
430 }
431}
432
433
435{
436 // This is the number of tests between 2 calls to the progress bar
437 const int progressDelta = 100;
438 int ii = 0;
439
440 reportAux( wxT( "Testing %d tracks & vias..." ), m_board->Tracks().size() );
441
442 std::map<BOARD_ITEM*, int> freePadsUsageMap;
443 std::unordered_map<PTR_PTR_CACHE_KEY, layers_checked> checkedPairs;
444
445 for( PCB_TRACK* track : m_board->Tracks() )
446 {
447 if( !reportProgress( ii++, m_board->Tracks().size(), progressDelta ) )
448 break;
449
450 for( PCB_LAYER_ID layer : LSET( track->GetLayerSet() & LSET::AllCuMask() ).Seq() )
451 {
452 std::shared_ptr<SHAPE> trackShape = track->GetEffectiveShape( layer );
453
454 m_board->m_CopperItemRTreeCache->QueryColliding( track, layer, layer,
455 // Filter:
456 [&]( BOARD_ITEM* other ) -> bool
457 {
458 auto otherCItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( other );
459
460 if( otherCItem && otherCItem->GetNetCode() == track->GetNetCode() )
461 return false;
462
463 BOARD_ITEM* a = track;
464 BOARD_ITEM* b = other;
465
466 // store canonical order so we don't collide in both directions
467 // (a:b and b:a)
468 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
469 std::swap( a, b );
470
471 auto it = checkedPairs.find( { a, b } );
472
473 if( it != checkedPairs.end() && ( it->second.layers.test( layer )
474 || ( it->second.has_error && !m_drcEngine->GetReportAllTrackErrors() ) ) )
475 {
476 return false;
477 }
478 else
479 {
480 checkedPairs[ { a, b } ].layers.set( layer );
481 return true;
482 }
483 },
484 // Visitor:
485 [&]( BOARD_ITEM* other ) -> bool
486 {
487 if( other->Type() == PCB_PAD_T && static_cast<PAD*>( other )->IsFreePad() )
488 {
489 if( other->GetEffectiveShape( layer )->Collide( trackShape.get() ) )
490 {
491 auto it = freePadsUsageMap.find( other );
492
493 if( it == freePadsUsageMap.end() )
494 {
495 freePadsUsageMap[ other ] = track->GetNetCode();
496 return false;
497 }
498 else if( it->second == track->GetNetCode() )
499 {
500 return false;
501 }
502 }
503 }
504
505 BOARD_ITEM* a = track;
506 BOARD_ITEM* b = other;
507
508 // store canonical order so we don't collide in both directions
509 // (a:b and b:a)
510 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
511 std::swap( a, b );
512
513 auto it = checkedPairs.find( { a, b } );
514
515 // If we get an error, mark the pair as having a clearance error already
516 // Only continue if we are reporting all track errors
517 if( !testTrackAgainstItem( track, trackShape.get(), layer, other ) )
518 {
519 if( it != checkedPairs.end() )
520 it->second.has_error = true;
521
523 }
524
525 return !m_drcEngine->IsCancelled();
526 },
528
529 for( ZONE* zone : m_board->m_DRCCopperZones )
530 {
531 testItemAgainstZone( track, zone, layer );
532
533 if( m_drcEngine->IsCancelled() )
534 break;
535 }
536 }
537 }
538}
539
540
542 PCB_LAYER_ID aLayer,
543 BOARD_ITEM* other )
544{
545 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
548
549 // Disable some tests for net-tie objects in a footprint
550 if( other->GetParent() == pad->GetParent() )
551 {
552 FOOTPRINT* fp = static_cast<FOOTPRINT*>( pad->GetParent() );
553 std::map<wxString, int> padToNetTieGroupMap = fp->MapPadNumbersToNetTieGroups();
554 int padGroupIdx = padToNetTieGroupMap[ pad->GetNumber() ];
555
556 if( other->Type() == PCB_PAD_T )
557 {
558 PAD* otherPad = static_cast<PAD*>( other );
559
560 if( padGroupIdx >= 0 && padGroupIdx == padToNetTieGroupMap[ otherPad->GetNumber() ] )
561 testClearance = false;
562
563 if( pad->SameLogicalPadAs( otherPad ) )
564 testHoles = false;
565 }
566
567 if( other->Type() == PCB_FP_SHAPE_T && padGroupIdx >= 0 )
568 testClearance = false;
569 }
570
571 PAD* otherPad = nullptr;
572 PCB_VIA* otherVia = nullptr;
573
574 if( other->Type() == PCB_PAD_T )
575 otherPad = static_cast<PAD*>( other );
576
577 if( other->Type() == PCB_VIA_T )
578 otherVia = static_cast<PCB_VIA*>( other );
579
580 if( !IsCopperLayer( aLayer ) )
581 testClearance = false;
582
583 // A NPTH has no cylinder, but it may still have pads on some layers
584 if( pad->GetAttribute() == PAD_ATTRIB::NPTH && !pad->FlashLayer( aLayer ) )
585 testClearance = false;
586
587 if( otherPad && otherPad->GetAttribute() == PAD_ATTRIB::NPTH && !otherPad->FlashLayer( aLayer ) )
588 testClearance = false;
589
590 // Track clearances are tested in testTrackClearances()
591 if( dynamic_cast<PCB_TRACK*>( other) )
592 testClearance = false;
593
594 int padNet = pad->GetNetCode();
595 int otherPadNet = otherPad ? otherPad->GetNetCode() : 0;
596 int otherViaNet = otherVia ? otherVia->GetNetCode() : 0;
597
598 // Pads and vias of the same (defined) net get a waiver on clearance and hole tests
599 if( ( otherPadNet && otherPadNet == padNet ) || ( otherViaNet && otherViaNet == padNet ) )
600 {
601 testClearance = false;
602 testHoles = false;
603 }
604
605 if( !( pad->GetDrillSize().x > 0 )
606 && !( otherPad && otherPad->GetDrillSize().x > 0 )
607 && !( otherVia && otherVia->GetDrill() > 0 ) )
608 {
609 testHoles = false;
610 }
611
612 if( !testClearance && !testShorting && !testHoles )
613 return false;
614
615 std::shared_ptr<SHAPE> otherShape = other->GetEffectiveShape( aLayer );
616 DRC_CONSTRAINT constraint;
617 int clearance;
618 int actual;
619 VECTOR2I pos;
620
621 if( otherPad && pad->SameLogicalPadAs( otherPad ) )
622 {
623 // If pads are equivalent (ie: from the same footprint with the same pad number)...
624 // ... and have nets...
625 // then they must be the same net
626 if( pad->GetNetCode() && otherPad->GetNetCode()
627 && pad->GetNetCode() != otherPad->GetNetCode()
628 && testShorting )
629 {
630 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_SHORTING_ITEMS );
631 wxString msg;
632
633 msg.Printf( _( "(nets %s and %s)" ),
634 pad->GetNetname(),
635 otherPad->GetNetname() );
636
637 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
638 drce->SetItems( pad, otherPad );
639
640 reportViolation( drce, otherPad->GetPosition(), aLayer );
641 }
642
643 return !m_drcEngine->IsCancelled();
644 }
645
646 if( testClearance )
647 {
648 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, pad, other, aLayer );
649 clearance = constraint.GetValue().Min();
650
651 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
652 {
653 if( padShape->Collide( otherShape.get(), std::max( 0, clearance - m_drcEpsilon ),
654 &actual, &pos ) )
655 {
656 if( m_drcEngine->IsNetTieExclusion( pad->GetNetCode(), aLayer, pos, other ) )
657 {
658 // Pads connected to pads of a net-tie footprint are allowed to collide
659 // with the net-tie footprint's graphics.
660 }
661 else
662 {
663 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
664 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
665 constraint.GetName(),
666 clearance,
667 actual );
668
669 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
670 drce->SetItems( pad, other );
671 drce->SetViolatingRule( constraint.GetParentRule() );
672
673 reportViolation( drce, pos, aLayer );
674 testHoles = false; // No need for multiple violations
675 }
676 }
677 }
678 }
679
680 if( testHoles )
681 {
682 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, pad, other, aLayer );
683 clearance = constraint.GetValue().Min();
684
685 if( constraint.GetSeverity() == RPT_SEVERITY_IGNORE )
686 testHoles = false;
687 }
688
689 if( testHoles && otherPad && pad->FlashLayer( aLayer ) && otherPad->HasHole() )
690 {
691 if( clearance > 0 && padShape->Collide( otherPad->GetEffectiveHoleShape().get(),
692 std::max( 0, clearance - m_drcEpsilon ),
693 &actual, &pos ) )
694 {
695 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
696 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
697 constraint.GetName(),
698 clearance,
699 actual );
700
701 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
702 drce->SetItems( pad, other );
703 drce->SetViolatingRule( constraint.GetParentRule() );
704
705 reportViolation( drce, pos, aLayer );
706 testHoles = false; // No need for multiple violations
707 }
708 }
709
710 if( testHoles && otherPad && otherPad->FlashLayer( aLayer ) && pad->HasHole() )
711 {
712 if( clearance > 0 && otherShape->Collide( pad->GetEffectiveHoleShape().get(),
713 std::max( 0, clearance - m_drcEpsilon ),
714 &actual, &pos ) )
715 {
716 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
717 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
718 constraint.GetName(),
719 clearance,
720 actual );
721
722 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
723 drce->SetItems( pad, other );
724 drce->SetViolatingRule( constraint.GetParentRule() );
725
726 reportViolation( drce, pos, aLayer );
727 testHoles = false; // No need for multiple violations
728 }
729 }
730
731 if( testHoles && otherVia && otherVia->IsOnLayer( aLayer ) )
732 {
733 if( clearance > 0 && padShape->Collide( otherVia->GetEffectiveHoleShape().get(),
734 std::max( 0, clearance - m_drcEpsilon ),
735 &actual, &pos ) )
736 {
737 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
738 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
739 constraint.GetName(),
740 clearance,
741 actual );
742
743 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
744 drce->SetItems( pad, otherVia );
745 drce->SetViolatingRule( constraint.GetParentRule() );
746
747 reportViolation( drce, pos, aLayer );
748 }
749 }
750
751 return !m_drcEngine->IsCancelled();
752}
753
754
756{
757 const int progressDelta = 100;
758 size_t count = 0;
759 int ii = 0;
760
761 for( FOOTPRINT* footprint : m_board->Footprints() )
762 count += footprint->Pads().size();
763
764 reportAux( wxT( "Testing %d pads..." ), count );
765
766 std::unordered_map<PTR_PTR_CACHE_KEY, int> checkedPairs;
767
768 for( FOOTPRINT* footprint : m_board->Footprints() )
769 {
770 for( PAD* pad : footprint->Pads() )
771 {
772 for( PCB_LAYER_ID layer : pad->GetLayerSet().Seq() )
773 {
774 std::shared_ptr<SHAPE> padShape = pad->GetEffectiveShape( layer );
775
776 m_board->m_CopperItemRTreeCache->QueryColliding( pad, layer, layer,
777 // Filter:
778 [&]( BOARD_ITEM* other ) -> bool
779 {
780 BOARD_ITEM* a = pad;
781 BOARD_ITEM* b = other;
782
783 // store canonical order so we don't collide in both directions
784 // (a:b and b:a)
785 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
786 std::swap( a, b );
787
788 if( checkedPairs.find( { a, b } ) != checkedPairs.end() )
789 {
790 return false;
791 }
792 else
793 {
794 checkedPairs[ { a, b } ] = 1;
795 return true;
796 }
797 },
798 // Visitor
799 [&]( BOARD_ITEM* other ) -> bool
800 {
801 return testPadAgainstItem( pad, padShape.get(), layer, other );
802 },
804
805 for( ZONE* zone : m_board->m_DRCCopperZones )
806 {
807 testItemAgainstZone( pad, zone, layer );
808
809 if( m_drcEngine->IsCancelled() )
810 return;
811 }
812 }
813
814 if( !reportProgress( ii++, count, progressDelta ) )
815 return;
816 }
817
818 if( m_drcEngine->IsCancelled() )
819 return;
820 }
821}
822
823
825{
826 const int progressDelta = 50;
827
828 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
829 bool testIntersects = !m_drcEngine->IsErrorLimitExceeded( DRCE_ZONES_INTERSECT );
830
831 SHAPE_POLY_SET buffer;
832 SHAPE_POLY_SET* boardOutline = nullptr;
833 DRC_CONSTRAINT constraint;
834 int zone2zoneClearance;
835
836 if( m_board->GetBoardPolygonOutlines( buffer ) )
837 boardOutline = &buffer;
838
839 for( int layer_id = F_Cu; layer_id <= B_Cu; ++layer_id )
840 {
841 PCB_LAYER_ID layer = static_cast<PCB_LAYER_ID>( layer_id );
842 std::vector<SHAPE_POLY_SET> smoothed_polys;
843 smoothed_polys.resize( m_board->m_DRCCopperZones.size() );
844
845 // Skip over layers not used on the current board
846 if( !m_board->IsLayerEnabled( layer ) )
847 continue;
848
849 for( size_t ii = 0; ii < m_board->m_DRCCopperZones.size(); ii++ )
850 {
851 if( m_board->m_DRCCopperZones[ii]->IsOnLayer( layer ) )
852 {
853 m_board->m_DRCCopperZones[ii]->BuildSmoothedPoly( smoothed_polys[ii], layer,
854 boardOutline );
855 }
856 }
857
858 // iterate through all areas
859 for( size_t ia = 0; ia < m_board->m_DRCCopperZones.size(); ia++ )
860 {
861 if( !reportProgress( layer_id * m_board->m_DRCCopperZones.size() + ia,
862 B_Cu * m_board->m_DRCCopperZones.size(), progressDelta ) )
863 {
864 return; // DRC cancelled
865 }
866
867 ZONE* zoneA = m_board->m_DRCCopperZones[ia];
868
869 if( !zoneA->IsOnLayer( layer ) )
870 continue;
871
872 for( size_t ia2 = ia + 1; ia2 < m_board->m_DRCCopperZones.size(); ia2++ )
873 {
874 ZONE* zoneB = m_board->m_DRCCopperZones[ia2];
875
876 // test for same layer
877 if( !zoneB->IsOnLayer( layer ) )
878 continue;
879
880 // Test for same net
881 if( zoneA->GetNetCode() == zoneB->GetNetCode() && zoneA->GetNetCode() >= 0 )
882 continue;
883
884 // test for different priorities
885 if( zoneA->GetAssignedPriority() != zoneB->GetAssignedPriority() )
886 continue;
887
888 // rule areas may overlap at will
889 if( zoneA->GetIsRuleArea() || zoneB->GetIsRuleArea() )
890 continue;
891
892 // Examine a candidate zone: compare zoneB to zoneA
893
894 // Get clearance used in zone to zone test.
895 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, zoneA, zoneB, layer );
896 zone2zoneClearance = constraint.GetValue().Min();
897
898 if( constraint.GetSeverity() == RPT_SEVERITY_IGNORE )
899 continue;
900
901 if( testIntersects )
902 {
903 // test for some corners of zoneA inside zoneB
904 for( auto it = smoothed_polys[ia].IterateWithHoles(); it; it++ )
905 {
906 VECTOR2I currentVertex = *it;
907 wxPoint pt( currentVertex.x, currentVertex.y );
908
909 if( smoothed_polys[ia2].Contains( currentVertex ) )
910 {
911 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_ZONES_INTERSECT );
912 drce->SetItems( zoneA, zoneB );
913 drce->SetViolatingRule( constraint.GetParentRule() );
914
915 reportViolation( drce, pt, layer );
916 }
917 }
918
919 // test for some corners of zoneB inside zoneA
920 for( auto it = smoothed_polys[ia2].IterateWithHoles(); it; it++ )
921 {
922 VECTOR2I currentVertex = *it;
923 wxPoint pt( currentVertex.x, currentVertex.y );
924
925 if( smoothed_polys[ia].Contains( currentVertex ) )
926 {
927 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_ZONES_INTERSECT );
928 drce->SetItems( zoneB, zoneA );
929 drce->SetViolatingRule( constraint.GetParentRule() );
930
931 reportViolation( drce, pt, layer );
932 }
933 }
934 }
935
936 // Iterate through all the segments of refSmoothedPoly
937 std::map<VECTOR2I, int> conflictPoints;
938
939 for( auto refIt = smoothed_polys[ia].IterateSegmentsWithHoles(); refIt; refIt++ )
940 {
941 // Build ref segment
942 SEG refSegment = *refIt;
943
944 // Iterate through all the segments in smoothed_polys[ia2]
945 for( auto it = smoothed_polys[ia2].IterateSegmentsWithHoles(); it; it++ )
946 {
947 // Build test segment
948 SEG testSegment = *it;
949 VECTOR2I pt;
950
951 int ax1, ay1, ax2, ay2;
952 ax1 = refSegment.A.x;
953 ay1 = refSegment.A.y;
954 ax2 = refSegment.B.x;
955 ay2 = refSegment.B.y;
956
957 int bx1, by1, bx2, by2;
958 bx1 = testSegment.A.x;
959 by1 = testSegment.A.y;
960 bx2 = testSegment.B.x;
961 by2 = testSegment.B.y;
962
963 int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2, 0,
964 ax1, ay1, ax2, ay2, 0,
965 zone2zoneClearance, &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 VECTOR2I, int>& conflict : conflictPoints )
978 {
979 int actual = conflict.second;
980 std::shared_ptr<DRC_ITEM> drce;
981
982 if( actual <= 0 && testIntersects )
983 {
985 }
986 else if( testClearance )
987 {
989 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
990 constraint.GetName(),
991 zone2zoneClearance,
992 std::max( actual, 0 ) );
993
994 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
995 }
996
997 if( drce )
998 {
999 drce->SetItems( zoneA, zoneB );
1000 drce->SetViolatingRule( constraint.GetParentRule() );
1001
1002 reportViolation( drce, conflict.first, layer );
1003 }
1004 }
1005
1006 if( m_drcEngine->IsCancelled() )
1007 return;
1008 }
1009 }
1010 }
1011}
1012
1013
1014namespace detail
1015{
1017}
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:58
virtual bool IsConnected() const
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
Definition: board_item.h:115
virtual std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING aFlash=FLASHING::DEFAULT) const
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
Definition: board_item.cpp:219
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:185
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:163
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const
Definition: board_item.cpp:229
virtual bool HasHole() const
Definition: board_item.h:128
std::vector< ZONE * > m_DRCCopperZones
Definition: board.h:1151
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:591
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:1950
FOOTPRINTS & Footprints()
Definition: board.h:307
std::unique_ptr< DRC_RTREE > m_CopperItemRTreeCache
Definition: board.h:1146
TRACKS & Tracks()
Definition: board.h:304
int m_DRCMaxClearance
Definition: board.h:1152
std::unordered_map< ZONE *, std::unique_ptr< DRC_RTREE > > m_CopperZoneRTreeCache
Definition: board.h:1145
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:682
bool Intersects(const BOX2< Vec > &aRect) const
Definition: box2.h:269
BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:506
wxString GetName() const
Definition: drc_rule.h:149
SEVERITY GetSeverity() const
Definition: drc_rule.h:162
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:141
DRC_RULE * GetParentRule() const
Definition: drc_rule.h:145
BOARD * GetBoard() const
Definition: drc_engine.h:89
bool GetReportAllTrackErrors() const
Definition: drc_engine.h:162
bool IsErrorLimitExceeded(int error_code)
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:673
bool IsCancelled() const
static bool IsNetTieExclusion(int aTrackNetCode, PCB_LAYER_ID aTrackLayer, const VECTOR2I &aCollisionPos, BOARD_ITEM *aCollidingItem)
Check if the given collision between a track and another item occurs during the track's entry into a ...
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition: drc_item.cpp:325
Implement an R-tree for fast spatial and layer indexing of connectable items.
Definition: drc_rtree.h:48
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:211
struct DRC_TEST_PROVIDER_COPPER_CLEARANCE::checked layers_checked
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
void testItemAgainstZone(BOARD_ITEM *aItem, ZONE *aZone, PCB_LAYER_ID aLayer)
bool testTrackAgainstItem(PCB_TRACK *track, SHAPE *trackShape, PCB_LAYER_ID layer, BOARD_ITEM *other)
Checks for track/via/hole <-> clearance.
virtual const wxString GetDescription() const override
virtual const wxString GetName() const override
bool testPadAgainstItem(PAD *pad, SHAPE *padShape, PCB_LAYER_ID layer, BOARD_ITEM *other)
virtual bool reportPhase(const wxString &aStageName)
virtual bool reportProgress(int aCount, int aSize, int aDelta)
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer)
virtual void reportAux(wxString fmt,...)
DRC_ENGINE * m_drcEngine
wxString formatMsg(const wxString &aFormatString, const wxString &aSource, int aConstraint, int aActual)
virtual void reportRuleStatistics()
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition: eda_item.cpp:74
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
std::map< wxString, int > MapPadNumbersToNetTieGroups() const
Definition: footprint.cpp:2284
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:530
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
bool Contains(PCB_LAYER_ID aLayer)
See if the layer set contains a PCB layer.
Definition: layer_ids.h:600
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:773
T Min() const
Definition: minoptmax.h:33
Definition: pad.h:59
bool FlashLayer(int aLayer, bool aOnlyCheckIfPermitted=false) const
Check to see whether the pad should be flashed on the specific layer.
Definition: pad.cpp:267
const VECTOR2I & GetDrillSize() const
Definition: pad.h:267
PAD_ATTRIB GetAttribute() const
Definition: pad.h:400
const wxString & GetNumber() const
Definition: pad.h:135
VECTOR2I GetPosition() const override
Definition: pad.h:202
bool HasHole() const override
Definition: pad.h:106
std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const override
Return a SHAPE_SEGMENT object representing the pad's hole.
Definition: pad.cpp:422
const VECTOR2I & GetStart() const
Definition: pcb_track.h:113
const VECTOR2I & GetEnd() const
Definition: pcb_track.h:110
int GetDrill() const
Function GetDrill returns the local drill setting for this PCB_VIA.
Definition: pcb_track.h:522
std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const override
Definition: pcb_track.cpp:458
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
Definition: pcb_track.cpp:486
Definition: seg.h:42
VECTOR2I A
Definition: seg.h:49
VECTOR2I B
Definition: seg.h:50
OPT_VECTOR2I Intersect(const SEG &aSeg, bool aIgnoreEndpoints=false, bool aLines=false) const
Compute intersection point of segment (this) with segment aSeg.
Definition: seg.cpp:188
Represent a set of closed polygons.
An abstract shape on 2D plane.
Definition: shape.h:123
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:178
Handle a list of polygons defining a copper zone.
Definition: zone.h:57
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition: zone.h:697
const BOX2I GetBoundingBox() const override
Definition: zone.cpp:323
virtual bool IsOnLayer(PCB_LAYER_ID) const override
Test to see if this object is on the given layer.
Definition: zone.cpp:317
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: zone.h:122
unsigned GetAssignedPriority() const
Definition: zone.h:112
The common library.
@ DRCE_HOLE_CLEARANCE
Definition: drc_item.h:53
@ DRCE_ZONES_INTERSECT
Definition: drc_item.h:46
@ DRCE_CLEARANCE
Definition: drc_item.h:43
@ DRCE_SHORTING_ITEMS
Definition: drc_item.h:40
@ DRCE_TRACKS_CROSSING
Definition: drc_item.h:44
@ CLEARANCE_CONSTRAINT
Definition: drc_rule.h:47
@ HOLE_CLEARANCE_CONSTRAINT
Definition: drc_rule.h:48
#define _(s)
bool IsCopperLayer(int aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:825
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:59
@ B_Cu
Definition: layer_ids.h:95
@ F_Cu
Definition: layer_ids.h:64
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
@ NPTH
like PAD_PTH, but not plated
@ PTH
Plated through hole pad.
@ RPT_SEVERITY_IGNORE
std::optional< VECTOR2I > OPT_VECTOR2I
Definition: seg.h:39
static bool Collide(const SHAPE_CIRCLE &aA, const SHAPE_CIRCLE &aB, int aClearance, int *aActual, VECTOR2I *aLocation, VECTOR2I *aMTV)
@ PCB_FP_SHAPE_T
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:94
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:102
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:101