KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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 );
191 DRC_CONSTRAINT constraint;
192 int clearance = -1;
193 int actual;
194 VECTOR2I pos;
195 bool has_error = false;
196 int otherNet = 0;
197
198 if( BOARD_CONNECTED_ITEM* connectedItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( other ) )
199 otherNet = connectedItem->GetNetCode();
200
201 std::shared_ptr<SHAPE> otherShape = other->GetEffectiveShape( layer );
202
203 if( other->Type() == PCB_PAD_T )
204 {
205 PAD* pad = static_cast<PAD*>( other );
206
207 if( pad->GetAttribute() == PAD_ATTRIB::NPTH && !pad->FlashLayer( layer ) )
208 testClearance = testShorting = false;
209 }
210
211 if( testClearance || testShorting )
212 {
213 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, track, other, layer );
214 clearance = constraint.GetValue().Min();
215 }
216
217 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
218 {
219 // Special processing for track:track intersections
220 if( track->Type() == PCB_TRACE_T && other->Type() == PCB_TRACE_T )
221 {
222 SEG trackSeg( track->GetStart(), track->GetEnd() );
223 SEG otherSeg( track->GetStart(), track->GetEnd() );
224
225 if( OPT_VECTOR2I intersection = trackSeg.Intersect( otherSeg ) )
226 {
227 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_TRACKS_CROSSING );
228 drcItem->SetItems( track, other );
229 drcItem->SetViolatingRule( constraint.GetParentRule() );
230
231 reportViolation( drcItem, *intersection, layer );
232
233 return false;
234 }
235 }
236
237 if( trackShape->Collide( otherShape.get(), clearance - m_drcEpsilon, &actual, &pos ) )
238 {
239 if( m_drcEngine->IsNetTieExclusion( track->GetNetCode(), layer, pos, other ) )
240 {
241 // Collision occurred as track was entering a pad marked as a net-tie. We
242 // allow these.
243 }
244 else if( actual == 0 && otherNet && testShorting )
245 {
246 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_SHORTING_ITEMS );
247 wxString msg;
248
249 msg.Printf( _( "(nets %s and %s)" ),
250 track->GetNetname(),
251 static_cast<BOARD_CONNECTED_ITEM*>( other )->GetNetname() );
252
253 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
254 drce->SetItems( track, other );
255
256 reportViolation( drce, pos, layer );
257 has_error = true;
258
260 return false;
261 }
262 else if( testClearance )
263 {
264 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
265 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
266 constraint.GetName(),
267 clearance,
268 actual );
269
270 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
271 drce->SetItems( track, other );
272 drce->SetViolatingRule( constraint.GetParentRule() );
273
274 reportViolation( drce, pos, layer );
275 has_error = true;
276
278 return false;
279 }
280 }
281 }
282
283 if( testHoles && ( track->HasHole() || other->HasHole() ) )
284 {
285 std::array<BOARD_ITEM*, 2> a{ track, other };
286 std::array<BOARD_ITEM*, 2> b{ other, track };
287 std::array<SHAPE*, 2> a_shape{ trackShape, otherShape.get() };
288
289 for( size_t ii = 0; ii < 2; ++ii )
290 {
291 std::shared_ptr<SHAPE_SEGMENT> holeShape;
292
293 // We only test a track item here against an item with a hole.
294 // If either case is not valid, simply move on
295 if( !( dynamic_cast<PCB_TRACK*>( a[ii] ) ) || !b[ii]->HasHole() )
296 {
297 continue;
298 }
299 if( b[ii]->Type() == PCB_VIA_T )
300 {
301 if( b[ii]->GetLayerSet().Contains( layer ) )
302 holeShape = b[ii]->GetEffectiveHoleShape();
303 }
304 else
305 {
306 holeShape = b[ii]->GetEffectiveHoleShape();
307 }
308
309 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, b[ii], a[ii], layer );
310 clearance = constraint.GetValue().Min();
311
312 // Test for hole to item clearance even if clearance is 0, because the item cannot be
313 // inside (or intersect) the hole.
314 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE )
315 {
316 if( a_shape[ii]->Collide( holeShape.get(), std::max( 0, clearance - m_drcEpsilon ),
317 &actual, &pos ) )
318 {
319 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
320 wxString msg = formatMsg( clearance ? _( "(%s clearance %s; actual %s)" )
321 : _( "(%s clearance %s; actual < 0)" ),
322 constraint.GetName(),
323 clearance,
324 actual );
325
326 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
327 drce->SetItems( a[ii], b[ii] );
328 drce->SetViolatingRule( constraint.GetParentRule() );
329
330 reportViolation( drce, pos, layer );
331 return false;
332 }
333 }
334 }
335 }
336
337 return !has_error;
338}
339
340
342 PCB_LAYER_ID aLayer )
343{
344 if( !aZone->GetLayerSet().test( aLayer ) )
345 return;
346
347 if( aZone->GetNetCode() && aItem->IsConnected() )
348 {
349 if( aZone->GetNetCode() == static_cast<BOARD_CONNECTED_ITEM*>( aItem )->GetNetCode() )
350 return;
351 }
352
353 BOX2I itemBBox = aItem->GetBoundingBox();
354 BOX2I worstCaseBBox = itemBBox;
355
356 worstCaseBBox.Inflate( m_board->m_DRCMaxClearance );
357
358 if( !worstCaseBBox.Intersects( aZone->GetBoundingBox() ) )
359 return;
360
361 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
363
364 if( !testClearance && !testHoles )
365 return;
366
367 DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ aZone ].get();
368
369 if( !zoneTree )
370 return;
371
372 DRC_CONSTRAINT constraint;
373 int clearance = -1;
374 int actual;
375 VECTOR2I pos;
376
377 if( aItem->Type() == PCB_PAD_T )
378 {
379 PAD* pad = static_cast<PAD*>( aItem );
380 bool flashedPad = pad->FlashLayer( aLayer );
381 bool platedHole = pad->HasHole() && pad->GetAttribute() == PAD_ATTRIB::PTH;
382
383 if( !flashedPad && !platedHole )
384 testClearance = false;
385 }
386
387 if( testClearance )
388 {
389 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aItem, aZone, aLayer );
390 clearance = constraint.GetValue().Min();
391 }
392
393 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
394 {
395 std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aLayer, FLASHING::DEFAULT );
396
397 if( zoneTree->QueryColliding( itemBBox, itemShape.get(), aLayer,
398 std::max( 0, clearance - m_drcEpsilon ), &actual, &pos ) )
399 {
400 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
401 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
402 constraint.GetName(),
403 clearance,
404 actual );
405
406 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
407 drce->SetItems( aItem, aZone );
408 drce->SetViolatingRule( constraint.GetParentRule() );
409
410 reportViolation( drce, pos, aLayer );
411 }
412 }
413
414 if( testHoles && aItem->HasHole() )
415 {
416 std::shared_ptr<SHAPE_SEGMENT> holeShape;
417
418 if( aItem->Type() == PCB_VIA_T )
419 {
420 if( aItem->GetLayerSet().Contains( aLayer ) )
421 holeShape = aItem->GetEffectiveHoleShape();
422 }
423 else
424 {
425 holeShape = aItem->GetEffectiveHoleShape();
426 }
427
428 if( holeShape )
429 {
430 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, aItem, aZone, aLayer );
431 clearance = constraint.GetValue().Min();
432
433 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
434 {
435 if( zoneTree->QueryColliding( itemBBox, holeShape.get(), aLayer,
436 std::max( 0, clearance - m_drcEpsilon ),
437 &actual, &pos ) )
438 {
439 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
440 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
441 constraint.GetName(),
442 clearance,
443 actual );
444
445 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
446 drce->SetItems( aItem, aZone );
447 drce->SetViolatingRule( constraint.GetParentRule() );
448
449 reportViolation( drce, pos, aLayer );
450 }
451 }
452 }
453 }
454}
455
456
458{
459 // This is the number of tests between 2 calls to the progress bar
460 const int progressDelta = 100;
461 int ii = 0;
462
463 reportAux( wxT( "Testing %d tracks & vias..." ), m_board->Tracks().size() );
464
465 std::map<BOARD_ITEM*, int> freePadsUsageMap;
466 std::unordered_map<PTR_PTR_CACHE_KEY, layers_checked> checkedPairs;
467
468 LSET boardCopperLayers = LSET::AllCuMask( m_board->GetCopperLayerCount() );
469
470 for( PCB_TRACK* track : m_board->Tracks() )
471 {
472 if( !reportProgress( ii++, m_board->Tracks().size(), progressDelta ) )
473 break;
474
475 for( PCB_LAYER_ID layer : LSET( track->GetLayerSet() & boardCopperLayers ).Seq() )
476 {
477 std::shared_ptr<SHAPE> trackShape = track->GetEffectiveShape( layer );
478
479 m_board->m_CopperItemRTreeCache->QueryColliding( track, layer, layer,
480 // Filter:
481 [&]( BOARD_ITEM* other ) -> bool
482 {
483 auto otherCItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( other );
484
485 if( otherCItem && otherCItem->GetNetCode() == track->GetNetCode() )
486 return false;
487
488 BOARD_ITEM* a = track;
489 BOARD_ITEM* b = other;
490
491 // store canonical order so we don't collide in both directions
492 // (a:b and b:a)
493 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
494 std::swap( a, b );
495
496 auto it = checkedPairs.find( { a, b } );
497
498 if( it != checkedPairs.end() && ( it->second.layers.test( layer )
499 || ( it->second.has_error && !m_drcEngine->GetReportAllTrackErrors() ) ) )
500 {
501 return false;
502 }
503 else
504 {
505 checkedPairs[ { a, b } ].layers.set( layer );
506 return true;
507 }
508 },
509 // Visitor:
510 [&]( BOARD_ITEM* other ) -> bool
511 {
512 if( other->Type() == PCB_PAD_T && static_cast<PAD*>( other )->IsFreePad() )
513 {
514 if( other->GetEffectiveShape( layer )->Collide( trackShape.get() ) )
515 {
516 auto it = freePadsUsageMap.find( other );
517
518 if( it == freePadsUsageMap.end() )
519 {
520 freePadsUsageMap[ other ] = track->GetNetCode();
521 return false;
522 }
523 else if( it->second == track->GetNetCode() )
524 {
525 return false;
526 }
527 }
528 }
529
530 BOARD_ITEM* a = track;
531 BOARD_ITEM* b = other;
532
533 // store canonical order so we don't collide in both directions
534 // (a:b and b:a)
535 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
536 std::swap( a, b );
537
538 auto it = checkedPairs.find( { a, b } );
539
540 // If we get an error, mark the pair as having a clearance error already
541 // Only continue if we are reporting all track errors
542 if( !testTrackAgainstItem( track, trackShape.get(), layer, other ) )
543 {
544 if( it != checkedPairs.end() )
545 it->second.has_error = true;
546
548 }
549
550 return !m_drcEngine->IsCancelled();
551 },
553
554 for( ZONE* zone : m_board->m_DRCCopperZones )
555 {
556 testItemAgainstZone( track, zone, layer );
557
558 if( m_drcEngine->IsCancelled() )
559 break;
560 }
561 }
562 }
563}
564
565
567 PCB_LAYER_ID aLayer,
568 BOARD_ITEM* other )
569{
570 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
573
574 // Disable some tests for net-tie objects in a footprint
575 if( other->GetParent() == pad->GetParent() )
576 {
577 FOOTPRINT* fp = pad->GetParentFootprint();
578 std::map<wxString, int> padToNetTieGroupMap = fp->MapPadNumbersToNetTieGroups();
579 int padGroupIdx = padToNetTieGroupMap[ pad->GetNumber() ];
580
581 if( other->Type() == PCB_PAD_T )
582 {
583 PAD* otherPad = static_cast<PAD*>( other );
584
585 if( padGroupIdx >= 0 && padGroupIdx == padToNetTieGroupMap[ otherPad->GetNumber() ] )
586 testClearance = testShorting = false;
587
588 if( pad->SameLogicalPadAs( otherPad ) )
589 testHoles = false;
590 }
591
592 if( other->Type() == PCB_SHAPE_T && padGroupIdx >= 0 )
593 testClearance = testShorting = false;
594 }
595
596 PAD* otherPad = nullptr;
597 PCB_VIA* otherVia = nullptr;
598
599 if( other->Type() == PCB_PAD_T )
600 otherPad = static_cast<PAD*>( other );
601
602 if( other->Type() == PCB_VIA_T )
603 otherVia = static_cast<PCB_VIA*>( other );
604
605 if( !IsCopperLayer( aLayer ) )
606 testClearance = testShorting = false;
607
608 // A NPTH has no cylinder, but it may still have pads on some layers
609 if( pad->GetAttribute() == PAD_ATTRIB::NPTH && !pad->FlashLayer( aLayer ) )
610 testClearance = testShorting = false;
611
612 if( otherPad && otherPad->GetAttribute() == PAD_ATTRIB::NPTH && !otherPad->FlashLayer( aLayer ) )
613 testClearance = testShorting = false;
614
615 // Track clearances are tested in testTrackClearances()
616 if( dynamic_cast<PCB_TRACK*>( other) )
617 testClearance = testShorting = false;
618
619 int padNet = pad->GetNetCode();
620 int otherNet = 0;
621
622 if( BOARD_CONNECTED_ITEM* connectedItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( other ) )
623 otherNet = connectedItem->GetNetCode();
624
625 // Pads and vias of the same (defined) net get a waiver on clearance and hole tests
626 if( ( otherPad || otherVia ) && otherNet && otherNet == padNet )
627 {
628 testClearance = testShorting = false;
629 testHoles = false;
630 }
631
632 if( !( pad->GetDrillSize().x > 0 )
633 && !( otherPad && otherPad->GetDrillSize().x > 0 )
634 && !( otherVia && otherVia->GetDrill() > 0 ) )
635 {
636 testHoles = false;
637 }
638
639 if( !testClearance && !testShorting && !testHoles )
640 return false;
641
642 std::shared_ptr<SHAPE> otherShape = other->GetEffectiveShape( aLayer );
643 DRC_CONSTRAINT constraint;
644 int clearance = 0;
645 int actual = 0;
646 VECTOR2I pos;
647
648 if( otherPad && pad->SameLogicalPadAs( otherPad ) )
649 {
650 // If pads are equivalent (ie: from the same footprint with the same pad number)...
651 // ... and have nets...
652 // then they must be the same net
653 if( pad->GetNetCode() && otherPad->GetNetCode()
654 && pad->GetNetCode() != otherPad->GetNetCode()
655 && testShorting )
656 {
657 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_SHORTING_ITEMS );
658 wxString msg;
659
660 msg.Printf( _( "(nets %s and %s)" ),
661 pad->GetNetname(),
662 otherPad->GetNetname() );
663
664 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
665 drce->SetItems( pad, otherPad );
666
667 reportViolation( drce, otherPad->GetPosition(), aLayer );
668 }
669
670 return !m_drcEngine->IsCancelled();
671 }
672
673 if( testClearance || testShorting )
674 {
675 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, pad, other, aLayer );
676 clearance = constraint.GetValue().Min();
677
678 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
679 {
680 if( padShape->Collide( otherShape.get(), std::max( 0, clearance - m_drcEpsilon ),
681 &actual, &pos ) )
682 {
683 if( m_drcEngine->IsNetTieExclusion( pad->GetNetCode(), aLayer, pos, other ) )
684 {
685 // Pads connected to pads of a net-tie footprint are allowed to collide
686 // with the net-tie footprint's graphics.
687 }
688 else if( actual == 0 && otherNet && testShorting )
689 {
690 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_SHORTING_ITEMS );
691 wxString msg;
692
693 msg.Printf( _( "(nets %s and %s)" ),
694 pad->GetNetname(),
695 static_cast<BOARD_CONNECTED_ITEM*>( other )->GetNetname() );
696
697 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
698 drce->SetItems( pad, other );
699
700 reportViolation( drce, pos, aLayer );
701 testHoles = false; // No need for multiple violations
702 }
703 else if( testClearance )
704 {
705 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
706 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
707 constraint.GetName(),
708 clearance,
709 actual );
710
711 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
712 drce->SetItems( pad, other );
713 drce->SetViolatingRule( constraint.GetParentRule() );
714
715 reportViolation( drce, pos, aLayer );
716 testHoles = false; // No need for multiple violations
717 }
718 }
719 }
720 }
721
722 if( testHoles )
723 {
724 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, pad, other, aLayer );
725 clearance = constraint.GetValue().Min();
726
727 if( constraint.GetSeverity() == RPT_SEVERITY_IGNORE )
728 testHoles = false;
729 }
730
731 if( testHoles && otherPad && pad->FlashLayer( aLayer ) && otherPad->HasHole() )
732 {
733 if( clearance > 0 && padShape->Collide( otherPad->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, other );
745 drce->SetViolatingRule( constraint.GetParentRule() );
746
747 reportViolation( drce, pos, aLayer );
748 testHoles = false; // No need for multiple violations
749 }
750 }
751
752 if( testHoles && otherPad && otherPad->FlashLayer( aLayer ) && pad->HasHole() )
753 {
754 if( clearance > 0 && otherShape->Collide( pad->GetEffectiveHoleShape().get(),
755 std::max( 0, clearance - m_drcEpsilon ),
756 &actual, &pos ) )
757 {
758 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
759 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
760 constraint.GetName(),
761 clearance,
762 actual );
763
764 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
765 drce->SetItems( pad, other );
766 drce->SetViolatingRule( constraint.GetParentRule() );
767
768 reportViolation( drce, pos, aLayer );
769 testHoles = false; // No need for multiple violations
770 }
771 }
772
773 if( testHoles && otherVia && otherVia->IsOnLayer( aLayer ) )
774 {
775 if( clearance > 0 && padShape->Collide( otherVia->GetEffectiveHoleShape().get(),
776 std::max( 0, clearance - m_drcEpsilon ),
777 &actual, &pos ) )
778 {
779 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
780 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
781 constraint.GetName(),
782 clearance,
783 actual );
784
785 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
786 drce->SetItems( pad, otherVia );
787 drce->SetViolatingRule( constraint.GetParentRule() );
788
789 reportViolation( drce, pos, aLayer );
790 }
791 }
792
793 return !m_drcEngine->IsCancelled();
794}
795
796
798{
799 const int progressDelta = 100;
800 size_t count = 0;
801 int ii = 0;
802
803 for( FOOTPRINT* footprint : m_board->Footprints() )
804 count += footprint->Pads().size();
805
806 reportAux( wxT( "Testing %d pads..." ), count );
807
808 std::unordered_map<PTR_PTR_CACHE_KEY, int> checkedPairs;
809
810 LSET boardCopperLayers = LSET::AllCuMask( m_board->GetCopperLayerCount() );
811
812 for( FOOTPRINT* footprint : m_board->Footprints() )
813 {
814 for( PAD* pad : footprint->Pads() )
815 {
816 for( PCB_LAYER_ID layer : LSET( pad->GetLayerSet() & boardCopperLayers ).Seq() )
817 {
818 std::shared_ptr<SHAPE> padShape = pad->GetEffectiveShape( layer );
819
820 m_board->m_CopperItemRTreeCache->QueryColliding( pad, layer, layer,
821 // Filter:
822 [&]( BOARD_ITEM* other ) -> bool
823 {
824 BOARD_ITEM* a = pad;
825 BOARD_ITEM* b = other;
826
827 // store canonical order so we don't collide in both directions
828 // (a:b and b:a)
829 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
830 std::swap( a, b );
831
832 if( checkedPairs.find( { a, b } ) != checkedPairs.end() )
833 {
834 return false;
835 }
836 else
837 {
838 checkedPairs[ { a, b } ] = 1;
839 return true;
840 }
841 },
842 // Visitor
843 [&]( BOARD_ITEM* other ) -> bool
844 {
845 return testPadAgainstItem( pad, padShape.get(), layer, other );
846 },
848
849 for( ZONE* zone : m_board->m_DRCCopperZones )
850 {
851 testItemAgainstZone( pad, zone, layer );
852
853 if( m_drcEngine->IsCancelled() )
854 return;
855 }
856 }
857
858 if( !reportProgress( ii++, count, progressDelta ) )
859 return;
860 }
861
862 if( m_drcEngine->IsCancelled() )
863 return;
864 }
865}
866
867
869{
870 const int progressDelta = 50;
871
872 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
873 bool testIntersects = !m_drcEngine->IsErrorLimitExceeded( DRCE_ZONES_INTERSECT );
874
875 SHAPE_POLY_SET buffer;
876 SHAPE_POLY_SET* boardOutline = nullptr;
877 DRC_CONSTRAINT constraint;
878 int zone2zoneClearance;
879
880 if( m_board->GetBoardPolygonOutlines( buffer ) )
881 boardOutline = &buffer;
882
883 for( int layer_id = F_Cu; layer_id <= B_Cu; ++layer_id )
884 {
885 PCB_LAYER_ID layer = static_cast<PCB_LAYER_ID>( layer_id );
886 std::vector<SHAPE_POLY_SET> smoothed_polys;
887 smoothed_polys.resize( m_board->m_DRCCopperZones.size() );
888
889 // Skip over layers not used on the current board
890 if( !m_board->IsLayerEnabled( layer ) )
891 continue;
892
893 for( size_t ii = 0; ii < m_board->m_DRCCopperZones.size(); ii++ )
894 {
895 if( m_board->m_DRCCopperZones[ii]->IsOnLayer( layer ) )
896 {
897 m_board->m_DRCCopperZones[ii]->BuildSmoothedPoly( smoothed_polys[ii], layer,
898 boardOutline );
899 }
900 }
901
902 // iterate through all areas
903 for( size_t ia = 0; ia < m_board->m_DRCCopperZones.size(); ia++ )
904 {
905 if( !reportProgress( layer_id * m_board->m_DRCCopperZones.size() + ia,
906 B_Cu * m_board->m_DRCCopperZones.size(), progressDelta ) )
907 {
908 return; // DRC cancelled
909 }
910
911 ZONE* zoneA = m_board->m_DRCCopperZones[ia];
912
913 if( !zoneA->IsOnLayer( layer ) )
914 continue;
915
916 for( size_t ia2 = ia + 1; ia2 < m_board->m_DRCCopperZones.size(); ia2++ )
917 {
918 ZONE* zoneB = m_board->m_DRCCopperZones[ia2];
919
920 // test for same layer
921 if( !zoneB->IsOnLayer( layer ) )
922 continue;
923
924 // Test for same net
925 if( zoneA->GetNetCode() == zoneB->GetNetCode() && zoneA->GetNetCode() >= 0 )
926 continue;
927
928 // test for different priorities
929 if( zoneA->GetAssignedPriority() != zoneB->GetAssignedPriority() )
930 continue;
931
932 // rule areas may overlap at will
933 if( zoneA->GetIsRuleArea() || zoneB->GetIsRuleArea() )
934 continue;
935
936 // Examine a candidate zone: compare zoneB to zoneA
937
938 // Get clearance used in zone to zone test.
939 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, zoneA, zoneB, layer );
940 zone2zoneClearance = constraint.GetValue().Min();
941
942 if( constraint.GetSeverity() == RPT_SEVERITY_IGNORE )
943 continue;
944
945 if( testIntersects )
946 {
947 // test for some corners of zoneA inside zoneB
948 for( auto it = smoothed_polys[ia].IterateWithHoles(); it; it++ )
949 {
950 VECTOR2I currentVertex = *it;
951
952 if( smoothed_polys[ia2].Contains( currentVertex ) )
953 {
954 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_ZONES_INTERSECT );
955 drce->SetItems( zoneA, zoneB );
956 drce->SetViolatingRule( constraint.GetParentRule() );
957
958 reportViolation( drce, currentVertex, layer );
959 }
960 }
961
962 // test for some corners of zoneB inside zoneA
963 for( auto it = smoothed_polys[ia2].IterateWithHoles(); it; it++ )
964 {
965 VECTOR2I currentVertex = *it;
966
967 if( smoothed_polys[ia].Contains( currentVertex ) )
968 {
969 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_ZONES_INTERSECT );
970 drce->SetItems( zoneB, zoneA );
971 drce->SetViolatingRule( constraint.GetParentRule() );
972
973 reportViolation( drce, currentVertex, layer );
974 }
975 }
976 }
977
978 // Iterate through all the segments of refSmoothedPoly
979 std::map<VECTOR2I, int> conflictPoints;
980
981 for( auto refIt = smoothed_polys[ia].IterateSegmentsWithHoles(); refIt; refIt++ )
982 {
983 // Build ref segment
984 SEG refSegment = *refIt;
985
986 // Iterate through all the segments in smoothed_polys[ia2]
987 for( auto it = smoothed_polys[ia2].IterateSegmentsWithHoles(); it; it++ )
988 {
989 // Build test segment
990 SEG testSegment = *it;
991 VECTOR2I pt;
992
993 int ax1, ay1, ax2, ay2;
994 ax1 = refSegment.A.x;
995 ay1 = refSegment.A.y;
996 ax2 = refSegment.B.x;
997 ay2 = refSegment.B.y;
998
999 int bx1, by1, bx2, by2;
1000 bx1 = testSegment.A.x;
1001 by1 = testSegment.A.y;
1002 bx2 = testSegment.B.x;
1003 by2 = testSegment.B.y;
1004
1005 int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2, 0,
1006 ax1, ay1, ax2, ay2, 0,
1007 zone2zoneClearance, &pt.x, &pt.y );
1008
1009 if( d < zone2zoneClearance )
1010 {
1011 if( conflictPoints.count( pt ) )
1012 conflictPoints[ pt ] = std::min( conflictPoints[ pt ], d );
1013 else
1014 conflictPoints[ pt ] = d;
1015 }
1016 }
1017 }
1018
1019 for( const std::pair<const VECTOR2I, int>& conflict : conflictPoints )
1020 {
1021 int actual = conflict.second;
1022 std::shared_ptr<DRC_ITEM> drce;
1023
1024 if( actual <= 0 && testIntersects )
1025 {
1027 }
1028 else if( testClearance )
1029 {
1031 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
1032 constraint.GetName(),
1033 zone2zoneClearance,
1034 std::max( actual, 0 ) );
1035
1036 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
1037 }
1038
1039 if( drce )
1040 {
1041 drce->SetItems( zoneA, zoneB );
1042 drce->SetViolatingRule( constraint.GetParentRule() );
1043
1044 reportViolation( drce, conflict.first, layer );
1045 }
1046 }
1047
1048 if( m_drcEngine->IsCancelled() )
1049 return;
1050 }
1051 }
1052 }
1053}
1054
1055
1056namespace detail
1057{
1059}
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:71
virtual bool IsConnected() const
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
Definition: board_item.h:128
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:220
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:201
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:176
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const
Definition: board_item.cpp:230
virtual bool HasHole() const
Definition: board_item.h:141
std::vector< ZONE * > m_DRCCopperZones
Definition: board.h:1189
bool GetBoardPolygonOutlines(SHAPE_POLY_SET &aOutlines, OUTLINE_ERROR_HANDLER *aErrorHandler=nullptr, bool aAllowUseArcsInPolygons=false)
Extract the board outlines and build a closed polygon from lines, arcs and circle items on edge cut l...
Definition: board.cpp:2027
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:637
FOOTPRINTS & Footprints()
Definition: board.h:312
int GetCopperLayerCount() const
Definition: board.cpp:587
TRACKS & Tracks()
Definition: board.h:309
std::shared_ptr< DRC_RTREE > m_CopperItemRTreeCache
Definition: board.h:1184
int m_DRCMaxClearance
Definition: board.h:1190
std::unordered_map< ZONE *, std::unique_ptr< DRC_RTREE > > m_CopperZoneRTreeCache
Definition: board.h:1183
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:728
bool Intersects(const BOX2< Vec > &aRect) const
Definition: box2.h:270
BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:507
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:666
bool IsCancelled() const
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)
DRC_ENGINE * m_drcEngine
void reportAux(const wxString &aMsg)
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:73
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:97
std::map< wxString, int > MapPadNumbersToNetTieGroups() const
Definition: footprint.cpp:2176
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:536
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:606
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:266
const VECTOR2I & GetDrillSize() const
Definition: pad.h:258
PAD_ATTRIB GetAttribute() const
Definition: pad.h:393
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:406
const VECTOR2I & GetStart() const
Definition: pcb_track.h:113
const VECTOR2I & GetEnd() const
Definition: pcb_track.h:110
int GetDrill() const
Return the local drill setting for this PCB_VIA.
Definition: pcb_track.h:530
bool IsOnLayer(PCB_LAYER_ID aLayer, bool aIncludeCourtyards=false) const override
Test to see if this object is on the given layer.
Definition: pcb_track.cpp:483
std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const override
Definition: pcb_track.cpp:455
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:196
Represent a set of closed polygons.
An abstract shape on 2D plane.
Definition: shape.h:124
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:179
Handle a list of polygons defining a copper zone.
Definition: zone.h:72
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition: zone.h:710
virtual bool IsOnLayer(PCB_LAYER_ID, bool aIncludeCourtyards=false) const override
Test to see if this object is on the given layer.
Definition: zone.cpp:345
const BOX2I GetBoundingBox() const override
Definition: zone.cpp:351
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: zone.h:129
unsigned GetAssignedPriority() const
Definition: zone.h:119
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:831
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
@ 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_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:93
@ 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:92