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:
82 bool testTrackAgainstItem( PCB_TRACK* track, SHAPE* trackShape, PCB_LAYER_ID layer,
83 BOARD_ITEM* other );
84
86
87 bool testPadAgainstItem( PAD* pad, SHAPE* padShape, PCB_LAYER_ID layer, BOARD_ITEM* other );
88
89 void testPadClearances();
90
91 void testZonesToZones();
92
93 void testItemAgainstZone( BOARD_ITEM* aItem, ZONE* aZone, PCB_LAYER_ID aLayer );
94
95private:
97};
98
99
101{
103
104 if( m_board->m_DRCMaxClearance <= 0 )
105 {
106 reportAux( wxT( "No Clearance constraints found. Tests not run." ) );
107 return true; // continue with other tests
108 }
109
111
113 {
114 if( !reportPhase( _( "Checking track & via clearances..." ) ) )
115 return false; // DRC cancelled
116
118 }
120 {
121 if( !reportPhase( _( "Checking hole clearances..." ) ) )
122 return false; // DRC cancelled
123
125 }
126
128 {
129 if( !reportPhase( _( "Checking pad clearances..." ) ) )
130 return false; // DRC cancelled
131
133 }
136 {
137 if( !reportPhase( _( "Checking pads..." ) ) )
138 return false; // DRC cancelled
139
141 }
142
144 {
145 if( !reportPhase( _( "Checking copper zone clearances..." ) ) )
146 return false; // DRC cancelled
147
149 }
151 {
152 if( !reportPhase( _( "Checking zones..." ) ) )
153 return false; // DRC cancelled
154
156 }
157
159
160 return !m_drcEngine->IsCancelled();
161}
162
163
165 PCB_LAYER_ID layer,
166 BOARD_ITEM* other )
167{
168 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
170 DRC_CONSTRAINT constraint;
171 int clearance = -1;
172 int actual;
173 VECTOR2I pos;
174
175 std::shared_ptr<SHAPE> otherShape = other->GetEffectiveShape( layer );
176
177 if( other->Type() == PCB_PAD_T )
178 {
179 PAD* pad = static_cast<PAD*>( other );
180
181 if( pad->GetAttribute() == PAD_ATTRIB::NPTH && !pad->FlashLayer( layer ) )
182 testClearance = false;
183 }
184
185 if( testClearance )
186 {
187 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, track, other, layer );
188 clearance = constraint.GetValue().Min();
189 }
190
191 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
192 {
193 // Special processing for track:track intersections
194 if( track->Type() == PCB_TRACE_T && other->Type() == PCB_TRACE_T )
195 {
196 SEG trackSeg( track->GetStart(), track->GetEnd() );
197 SEG otherSeg( track->GetStart(), track->GetEnd() );
198
199 if( OPT_VECTOR2I intersection = trackSeg.Intersect( otherSeg ) )
200 {
201 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_TRACKS_CROSSING );
202 drcItem->SetItems( track, other );
203 drcItem->SetViolatingRule( constraint.GetParentRule() );
204
205 reportViolation( drcItem, *intersection, layer );
206
208 }
209 }
210
211 if( trackShape->Collide( otherShape.get(), clearance - m_drcEpsilon, &actual, &pos ) )
212 {
213 if( m_drcEngine->IsNetTieExclusion( track->GetNetCode(), layer, pos, other ) )
214 {
215 // Collision occurred as track was entering a pad marked as a net-tie. We
216 // allow these.
217 }
218 else
219 {
220 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
221 wxString msg;
222
223 msg.Printf( _( "(%s clearance %s; actual %s)" ),
224 constraint.GetName(),
225 MessageTextFromValue( clearance ),
226 MessageTextFromValue( actual ) );
227
228 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
229 drce->SetItems( track, other );
230 drce->SetViolatingRule( constraint.GetParentRule() );
231
232 reportViolation( drce, pos, layer );
233
235 return false;
236 }
237 }
238 }
239
240 if( testHoles && ( track->HasHole() || other->HasHole() ) )
241 {
242 std::array<BOARD_ITEM*, 2> a{ track, other };
243 std::array<BOARD_ITEM*, 2> b{ other, track };
244 std::array<SHAPE*, 2> a_shape{ trackShape, otherShape.get() };
245
246 bool has_error = false;
247
248 for( size_t ii = 0; ii < 2 && !has_error; ++ii )
249 {
250 std::shared_ptr<SHAPE_SEGMENT> holeShape;
251
252 // We only test a track item here against an item with a hole.
253 // If either case is not valid, simply move on
254 if( !( dynamic_cast<PCB_TRACK*>( a[ii] ) ) || !b[ii]->HasHole() )
255 {
256 continue;
257 }
258 if( b[ii]->Type() == PCB_VIA_T )
259 {
260 if( b[ii]->GetLayerSet().Contains( layer ) )
261 holeShape = b[ii]->GetEffectiveHoleShape();
262 }
263 else
264 {
265 holeShape = b[ii]->GetEffectiveHoleShape();
266 }
267
268 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, b[ii], a[ii], layer );
269 clearance = constraint.GetValue().Min();
270
271 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
272 {
273 if( a_shape[ii]->Collide( holeShape.get(), std::max( 0, clearance - m_drcEpsilon ),
274 &actual, &pos ) )
275 {
276 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
277 wxString msg;
278
279 msg.Printf( _( "(%s clearance %s; actual %s)" ), constraint.GetName(),
280 MessageTextFromValue( clearance ), MessageTextFromValue( actual ) );
281
282 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
283 drce->SetItems( a[ii], b[ii] );
284 drce->SetViolatingRule( constraint.GetParentRule() );
285
286 reportViolation( drce, pos, layer );
287 has_error = true;
288
290 return false;
291 }
292 }
293 }
294 }
295
296 return !m_drcEngine->IsCancelled();
297}
298
299
301 PCB_LAYER_ID aLayer )
302{
303 if( !aZone->GetLayerSet().test( aLayer ) )
304 return;
305
306 if( aZone->GetNetCode() && aItem->IsConnected() )
307 {
308 if( aZone->GetNetCode() == static_cast<BOARD_CONNECTED_ITEM*>( aItem )->GetNetCode() )
309 return;
310 }
311
312 BOX2I itemBBox = aItem->GetBoundingBox();
313 BOX2I worstCaseBBox = itemBBox;
314
315 worstCaseBBox.Inflate( m_board->m_DRCMaxClearance );
316
317 if( !worstCaseBBox.Intersects( aZone->GetBoundingBox() ) )
318 return;
319
320 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
322
323 if( !testClearance && !testHoles )
324 return;
325
326 DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ aZone ].get();
327
328 if( !zoneTree )
329 return;
330
331 DRC_CONSTRAINT constraint;
332 int clearance = -1;
333 int actual;
334 VECTOR2I pos;
335
336 if( aItem->Type() == PCB_PAD_T )
337 {
338 PAD* pad = static_cast<PAD*>( aItem );
339 bool flashedPad = pad->FlashLayer( aLayer );
340 bool platedHole = pad->HasHole() && pad->GetAttribute() == PAD_ATTRIB::PTH;
341
342 if( !flashedPad && !platedHole )
343 testClearance = false;
344 }
345
346 if( testClearance )
347 {
348 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aItem, aZone, aLayer );
349 clearance = constraint.GetValue().Min();
350 }
351
352 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
353 {
354 std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aLayer, FLASHING::DEFAULT );
355
356 if( zoneTree->QueryColliding( itemBBox, itemShape.get(), aLayer,
357 std::max( 0, clearance - m_drcEpsilon ), &actual, &pos ) )
358 {
359 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
360 wxString msg;
361
362 msg.Printf( _( "(%s clearance %s; actual %s)" ),
363 constraint.GetName(),
364 MessageTextFromValue( clearance ),
365 MessageTextFromValue( actual ) );
366
367 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
368 drce->SetItems( aItem, aZone );
369 drce->SetViolatingRule( constraint.GetParentRule() );
370
371 reportViolation( drce, pos, aLayer );
372 }
373 }
374
375 if( testHoles && aItem->HasHole() )
376 {
377 std::shared_ptr<SHAPE_SEGMENT> holeShape;
378
379 if( aItem->Type() == PCB_VIA_T )
380 {
381 if( aItem->GetLayerSet().Contains( aLayer ) )
382 holeShape = aItem->GetEffectiveHoleShape();
383 }
384 else
385 {
386 holeShape = aItem->GetEffectiveHoleShape();
387 }
388
389 if( holeShape )
390 {
391 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, aItem, aZone, aLayer );
392 clearance = constraint.GetValue().Min();
393
394 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
395 {
396 if( zoneTree->QueryColliding( itemBBox, holeShape.get(), aLayer,
397 std::max( 0, clearance - m_drcEpsilon ),
398 &actual, &pos ) )
399 {
400 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
401 wxString msg;
402
403 msg.Printf( _( "(%s clearance %s; actual %s)" ),
404 constraint.GetName(),
405 MessageTextFromValue( clearance ),
406 MessageTextFromValue( actual ) );
407
408 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
409 drce->SetItems( aItem, aZone );
410 drce->SetViolatingRule( constraint.GetParentRule() );
411
412 reportViolation( drce, pos, aLayer );
413 }
414 }
415 }
416 }
417}
418
419
421{
422 // This is the number of tests between 2 calls to the progress bar
423 const int progressDelta = 100;
424 int ii = 0;
425
426 reportAux( wxT( "Testing %d tracks & vias..." ), m_board->Tracks().size() );
427
428 std::map<BOARD_ITEM*, int> freePadsUsageMap;
429 std::unordered_map<PTR_PTR_CACHE_KEY, LSET> checkedPairs;
430
431 for( PCB_TRACK* track : m_board->Tracks() )
432 {
433 if( !reportProgress( ii++, m_board->Tracks().size(), progressDelta ) )
434 break;
435
436 for( PCB_LAYER_ID layer : LSET( track->GetLayerSet() & LSET::AllCuMask() ).Seq() )
437 {
438 std::shared_ptr<SHAPE> trackShape = track->GetEffectiveShape( layer );
439
440 m_board->m_CopperItemRTreeCache->QueryColliding( track, layer, layer,
441 // Filter:
442 [&]( BOARD_ITEM* other ) -> bool
443 {
444 auto otherCItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( other );
445
446 if( otherCItem && otherCItem->GetNetCode() == track->GetNetCode() )
447 return false;
448
449 BOARD_ITEM* a = track;
450 BOARD_ITEM* b = other;
451
452 // store canonical order so we don't collide in both directions
453 // (a:b and b:a)
454 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
455 std::swap( a, b );
456
457 auto it = checkedPairs.find( { a, b } );
458
459 if( it != checkedPairs.end() && it->second.test( layer ) )
460 {
461 return false;
462 }
463 else
464 {
465 checkedPairs[ { a, b } ].set( layer );
466 return true;
467 }
468 },
469 // Visitor:
470 [&]( BOARD_ITEM* other ) -> bool
471 {
472 if( other->Type() == PCB_PAD_T && static_cast<PAD*>( other )->IsFreePad() )
473 {
474 if( other->GetEffectiveShape( layer )->Collide( trackShape.get() ) )
475 {
476 auto it = freePadsUsageMap.find( other );
477
478 if( it == freePadsUsageMap.end() )
479 {
480 freePadsUsageMap[ other ] = track->GetNetCode();
481 return false;
482 }
483 else if( it->second == track->GetNetCode() )
484 {
485 return false;
486 }
487 }
488 }
489
490 return testTrackAgainstItem( track, trackShape.get(), layer, other );
491 },
493
494 for( ZONE* zone : m_board->m_DRCCopperZones )
495 {
496 testItemAgainstZone( track, zone, layer );
497
498 if( m_drcEngine->IsCancelled() )
499 break;
500 }
501 }
502 }
503}
504
505
507 PCB_LAYER_ID aLayer,
508 BOARD_ITEM* other )
509{
510 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
513
514 // Disable some tests for net-tie objects in a footprint
515 if( other->GetParent() == pad->GetParent() )
516 {
517 FOOTPRINT* fp = static_cast<FOOTPRINT*>( pad->GetParent() );
518 std::map<wxString, int> padToNetTieGroupMap = fp->MapPadNumbersToNetTieGroups();
519 int padGroupIdx = padToNetTieGroupMap[ pad->GetNumber() ];
520
521 if( other->Type() == PCB_PAD_T )
522 {
523 PAD* otherPad = static_cast<PAD*>( other );
524
525 if( padGroupIdx >= 0 && padGroupIdx == padToNetTieGroupMap[ otherPad->GetNumber() ] )
526 testClearance = false;
527
528 if( pad->SameLogicalPadAs( otherPad ) )
529 testHoles = false;
530 }
531
532 if( other->Type() == PCB_FP_SHAPE_T && padGroupIdx >= 0 )
533 testClearance = false;
534 }
535
536 PAD* otherPad = nullptr;
537 PCB_VIA* otherVia = nullptr;
538
539 if( other->Type() == PCB_PAD_T )
540 otherPad = static_cast<PAD*>( other );
541
542 if( other->Type() == PCB_VIA_T )
543 otherVia = static_cast<PCB_VIA*>( other );
544
545 if( !IsCopperLayer( aLayer ) )
546 testClearance = false;
547
548 // A NPTH has no cylinder, but it may still have pads on some layers
549 if( pad->GetAttribute() == PAD_ATTRIB::NPTH && !pad->FlashLayer( aLayer ) )
550 testClearance = false;
551
552 if( otherPad && otherPad->GetAttribute() == PAD_ATTRIB::NPTH && !otherPad->FlashLayer( aLayer ) )
553 testClearance = false;
554
555 // Track clearances are tested in testTrackClearances()
556 if( dynamic_cast<PCB_TRACK*>( other) )
557 testClearance = false;
558
559 int padNet = pad->GetNetCode();
560 int otherPadNet = otherPad ? otherPad->GetNetCode() : 0;
561 int otherViaNet = otherVia ? otherVia->GetNetCode() : 0;
562
563 // Pads and vias of the same (defined) net get a waiver on clearance and hole tests
564 if( ( otherPadNet && otherPadNet == padNet ) || ( otherViaNet && otherViaNet == padNet ) )
565 {
566 testClearance = false;
567 testHoles = false;
568 }
569
570 if( !( pad->GetDrillSize().x > 0 )
571 && !( otherPad && otherPad->GetDrillSize().x > 0 )
572 && !( otherVia && otherVia->GetDrill() > 0 ) )
573 {
574 testHoles = false;
575 }
576
577 if( !testClearance && !testShorting && !testHoles )
578 return false;
579
580 std::shared_ptr<SHAPE> otherShape = other->GetEffectiveShape( aLayer );
581 DRC_CONSTRAINT constraint;
582 int clearance;
583 int actual;
584 VECTOR2I pos;
585
586 if( otherPad && pad->SameLogicalPadAs( otherPad ) )
587 {
588 // If pads are equivalent (ie: from the same footprint with the same pad number)...
589 // ... and have nets...
590 // then they must be the same net
591 if( pad->GetNetCode() && otherPad->GetNetCode()
592 && pad->GetNetCode() != otherPad->GetNetCode()
593 && testShorting )
594 {
595 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_SHORTING_ITEMS );
596 wxString msg;
597
598 msg.Printf( _( "(nets %s and %s)" ),
599 pad->GetNetname(),
600 otherPad->GetNetname() );
601
602 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
603 drce->SetItems( pad, otherPad );
604
605 reportViolation( drce, otherPad->GetPosition(), aLayer );
606 }
607
608 return !m_drcEngine->IsCancelled();
609 }
610
611 if( testClearance )
612 {
613 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, pad, other, aLayer );
614 clearance = constraint.GetValue().Min();
615
616 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
617 {
618 if( padShape->Collide( otherShape.get(), std::max( 0, clearance - m_drcEpsilon ),
619 &actual, &pos ) )
620 {
621 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
622 wxString msg;
623
624 msg.Printf( _( "(%s clearance %s; actual %s)" ),
625 constraint.GetName(),
626 MessageTextFromValue( clearance ),
627 MessageTextFromValue( actual ) );
628
629 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
630 drce->SetItems( pad, other );
631 drce->SetViolatingRule( constraint.GetParentRule() );
632
633 reportViolation( drce, pos, aLayer );
634 testHoles = false; // No need for multiple violations
635 }
636 }
637 }
638
639 if( testHoles )
640 {
641 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, pad, other, aLayer );
642 clearance = constraint.GetValue().Min();
643
644 if( constraint.GetSeverity() == RPT_SEVERITY_IGNORE )
645 testHoles = false;
646 }
647
648 if( testHoles && otherPad && pad->FlashLayer( aLayer ) && otherPad->HasHole() )
649 {
650 if( clearance > 0 && padShape->Collide( otherPad->GetEffectiveHoleShape().get(),
651 std::max( 0, clearance - m_drcEpsilon ),
652 &actual, &pos ) )
653 {
654 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
655 wxString msg;
656
657 msg.Printf( _( "(%s clearance %s; actual %s)" ),
658 constraint.GetName(),
659 MessageTextFromValue( clearance ),
660 MessageTextFromValue( actual ) );
661
662 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
663 drce->SetItems( pad, other );
664 drce->SetViolatingRule( constraint.GetParentRule() );
665
666 reportViolation( drce, pos, aLayer );
667 testHoles = false; // No need for multiple violations
668 }
669 }
670
671 if( testHoles && otherPad && otherPad->FlashLayer( aLayer ) && pad->HasHole() )
672 {
673 if( clearance > 0 && otherShape->Collide( pad->GetEffectiveHoleShape().get(),
674 std::max( 0, clearance - m_drcEpsilon ),
675 &actual, &pos ) )
676 {
677 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
678 wxString msg;
679
680 msg.Printf( _( "(%s clearance %s; actual %s)" ),
681 constraint.GetName(),
682 MessageTextFromValue( clearance ),
683 MessageTextFromValue( actual ) );
684
685 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
686 drce->SetItems( pad, other );
687 drce->SetViolatingRule( constraint.GetParentRule() );
688
689 reportViolation( drce, pos, aLayer );
690 testHoles = false; // No need for multiple violations
691 }
692 }
693
694 if( testHoles && otherVia && otherVia->IsOnLayer( aLayer ) )
695 {
696 if( clearance > 0 && padShape->Collide( otherVia->GetEffectiveHoleShape().get(),
697 std::max( 0, clearance - m_drcEpsilon ),
698 &actual, &pos ) )
699 {
700 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
701 wxString msg;
702
703 msg.Printf( _( "(%s clearance %s; actual %s)" ),
704 constraint.GetName(),
705 MessageTextFromValue( clearance ),
706 MessageTextFromValue( actual ) );
707
708 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
709 drce->SetItems( pad, otherVia );
710 drce->SetViolatingRule( constraint.GetParentRule() );
711
712 reportViolation( drce, pos, aLayer );
713 }
714 }
715
716 return !m_drcEngine->IsCancelled();
717}
718
719
721{
722 const int progressDelta = 100;
723 size_t count = 0;
724 int ii = 0;
725
726 for( FOOTPRINT* footprint : m_board->Footprints() )
727 count += footprint->Pads().size();
728
729 reportAux( wxT( "Testing %d pads..." ), count );
730
731 std::unordered_map<PTR_PTR_CACHE_KEY, int> checkedPairs;
732
733 for( FOOTPRINT* footprint : m_board->Footprints() )
734 {
735 for( PAD* pad : footprint->Pads() )
736 {
737 for( PCB_LAYER_ID layer : pad->GetLayerSet().Seq() )
738 {
739 std::shared_ptr<SHAPE> padShape = pad->GetEffectiveShape( layer );
740
741 m_board->m_CopperItemRTreeCache->QueryColliding( pad, layer, layer,
742 // Filter:
743 [&]( BOARD_ITEM* other ) -> bool
744 {
745 BOARD_ITEM* a = pad;
746 BOARD_ITEM* b = other;
747
748 // store canonical order so we don't collide in both directions
749 // (a:b and b:a)
750 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
751 std::swap( a, b );
752
753 if( checkedPairs.find( { a, b } ) != checkedPairs.end() )
754 {
755 return false;
756 }
757 else
758 {
759 checkedPairs[ { a, b } ] = 1;
760 return true;
761 }
762 },
763 // Visitor
764 [&]( BOARD_ITEM* other ) -> bool
765 {
766 return testPadAgainstItem( pad, padShape.get(), layer, other );
767 },
769
770 for( ZONE* zone : m_board->m_DRCCopperZones )
771 {
772 testItemAgainstZone( pad, zone, layer );
773
774 if( m_drcEngine->IsCancelled() )
775 return;
776 }
777 }
778
779 if( !reportProgress( ii++, count, progressDelta ) )
780 return;
781 }
782
783 if( m_drcEngine->IsCancelled() )
784 return;
785 }
786}
787
788
790{
791 const int progressDelta = 50;
792
793 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
794 bool testIntersects = !m_drcEngine->IsErrorLimitExceeded( DRCE_ZONES_INTERSECT );
795
796 SHAPE_POLY_SET buffer;
797 SHAPE_POLY_SET* boardOutline = nullptr;
798 DRC_CONSTRAINT constraint;
799 int zone2zoneClearance;
800
801 if( m_board->GetBoardPolygonOutlines( buffer ) )
802 boardOutline = &buffer;
803
804 for( int layer_id = F_Cu; layer_id <= B_Cu; ++layer_id )
805 {
806 PCB_LAYER_ID layer = static_cast<PCB_LAYER_ID>( layer_id );
807 std::vector<SHAPE_POLY_SET> smoothed_polys;
808 smoothed_polys.resize( m_board->m_DRCCopperZones.size() );
809
810 // Skip over layers not used on the current board
811 if( !m_board->IsLayerEnabled( layer ) )
812 continue;
813
814 for( size_t ii = 0; ii < m_board->m_DRCCopperZones.size(); ii++ )
815 {
816 if( m_board->m_DRCCopperZones[ii]->IsOnLayer( layer ) )
817 {
818 m_board->m_DRCCopperZones[ii]->BuildSmoothedPoly( smoothed_polys[ii], layer,
819 boardOutline );
820 }
821 }
822
823 // iterate through all areas
824 for( size_t ia = 0; ia < m_board->m_DRCCopperZones.size(); ia++ )
825 {
826 if( !reportProgress( layer_id * m_board->m_DRCCopperZones.size() + ia,
827 B_Cu * m_board->m_DRCCopperZones.size(), progressDelta ) )
828 {
829 return; // DRC cancelled
830 }
831
832 ZONE* zoneA = m_board->m_DRCCopperZones[ia];
833
834 if( !zoneA->IsOnLayer( layer ) )
835 continue;
836
837 for( size_t ia2 = ia + 1; ia2 < m_board->m_DRCCopperZones.size(); ia2++ )
838 {
839 ZONE* zoneB = m_board->m_DRCCopperZones[ia2];
840
841 // test for same layer
842 if( !zoneB->IsOnLayer( layer ) )
843 continue;
844
845 // Test for same net
846 if( zoneA->GetNetCode() == zoneB->GetNetCode() && zoneA->GetNetCode() >= 0 )
847 continue;
848
849 // test for different priorities
850 if( zoneA->GetAssignedPriority() != zoneB->GetAssignedPriority() )
851 continue;
852
853 // rule areas may overlap at will
854 if( zoneA->GetIsRuleArea() || zoneB->GetIsRuleArea() )
855 continue;
856
857 // Examine a candidate zone: compare zoneB to zoneA
858
859 // Get clearance used in zone to zone test.
860 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, zoneA, zoneB, layer );
861 zone2zoneClearance = constraint.GetValue().Min();
862
863 if( constraint.GetSeverity() == RPT_SEVERITY_IGNORE )
864 continue;
865
866 if( testIntersects )
867 {
868 // test for some corners of zoneA inside zoneB
869 for( auto it = smoothed_polys[ia].IterateWithHoles(); it; it++ )
870 {
871 VECTOR2I currentVertex = *it;
872 wxPoint pt( currentVertex.x, currentVertex.y );
873
874 if( smoothed_polys[ia2].Contains( currentVertex ) )
875 {
876 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_ZONES_INTERSECT );
877 drce->SetItems( zoneA, zoneB );
878 drce->SetViolatingRule( constraint.GetParentRule() );
879
880 reportViolation( drce, pt, layer );
881 }
882 }
883
884 // test for some corners of zoneB inside zoneA
885 for( auto it = smoothed_polys[ia2].IterateWithHoles(); it; it++ )
886 {
887 VECTOR2I currentVertex = *it;
888 wxPoint pt( currentVertex.x, currentVertex.y );
889
890 if( smoothed_polys[ia].Contains( currentVertex ) )
891 {
892 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_ZONES_INTERSECT );
893 drce->SetItems( zoneB, zoneA );
894 drce->SetViolatingRule( constraint.GetParentRule() );
895
896 reportViolation( drce, pt, layer );
897 }
898 }
899 }
900
901 // Iterate through all the segments of refSmoothedPoly
902 std::map<VECTOR2I, int> conflictPoints;
903
904 for( auto refIt = smoothed_polys[ia].IterateSegmentsWithHoles(); refIt; refIt++ )
905 {
906 // Build ref segment
907 SEG refSegment = *refIt;
908
909 // Iterate through all the segments in smoothed_polys[ia2]
910 for( auto it = smoothed_polys[ia2].IterateSegmentsWithHoles(); it; it++ )
911 {
912 // Build test segment
913 SEG testSegment = *it;
914 VECTOR2I pt;
915
916 int ax1, ay1, ax2, ay2;
917 ax1 = refSegment.A.x;
918 ay1 = refSegment.A.y;
919 ax2 = refSegment.B.x;
920 ay2 = refSegment.B.y;
921
922 int bx1, by1, bx2, by2;
923 bx1 = testSegment.A.x;
924 by1 = testSegment.A.y;
925 bx2 = testSegment.B.x;
926 by2 = testSegment.B.y;
927
928 int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2, 0,
929 ax1, ay1, ax2, ay2, 0,
930 zone2zoneClearance, &pt.x, &pt.y );
931
932 if( d < zone2zoneClearance )
933 {
934 if( conflictPoints.count( pt ) )
935 conflictPoints[ pt ] = std::min( conflictPoints[ pt ], d );
936 else
937 conflictPoints[ pt ] = d;
938 }
939 }
940 }
941
942 for( const std::pair<const VECTOR2I, int>& conflict : conflictPoints )
943 {
944 int actual = conflict.second;
945 std::shared_ptr<DRC_ITEM> drce;
946
947 if( actual <= 0 && testIntersects )
948 {
950 }
951 else if( testClearance )
952 {
954 wxString msg;
955
956 msg.Printf( _( "(%s clearance %s; actual %s)" ),
957 constraint.GetName(),
958 MessageTextFromValue( zone2zoneClearance ),
959 MessageTextFromValue( std::max( actual, 0 ) ) );
960
961 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
962 }
963
964 if( drce )
965 {
966 drce->SetItems( zoneA, zoneB );
967 drce->SetViolatingRule( constraint.GetParentRule() );
968
969 reportViolation( drce, conflict.first, layer );
970 }
971 }
972
973 if( m_drcEngine->IsCancelled() )
974 return;
975 }
976 }
977 }
978}
979
980
981namespace detail
982{
984}
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:50
virtual bool IsConnected() const
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
Definition: board_item.h:105
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:197
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:172
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:150
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const
Definition: board_item.cpp:207
virtual bool HasHole() const
Definition: board_item.h:118
std::vector< ZONE * > m_DRCCopperZones
Definition: board.h:1158
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:527
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:1893
FOOTPRINTS & Footprints()
Definition: board.h:307
std::unique_ptr< DRC_RTREE > m_CopperItemRTreeCache
Definition: board.h:1153
TRACKS & Tracks()
Definition: board.h:304
int m_DRCMaxClearance
Definition: board.h:1159
std::unordered_map< ZONE *, std::unique_ptr< DRC_RTREE > > m_CopperZoneRTreeCache
Definition: board.h:1152
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:618
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:147
SEVERITY GetSeverity() const
Definition: drc_rule.h:160
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:139
DRC_RULE * GetParentRule() const
Definition: drc_rule.h:143
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:671
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
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)
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
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:2252
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:58
const VECTOR2I & GetDrillSize() const
Definition: pad.h:261
PAD_ATTRIB GetAttribute() const
Definition: pad.h:394
const wxString & GetNumber() const
Definition: pad.h:134
VECTOR2I GetPosition() const override
Definition: pad.h:196
bool FlashLayer(int aLayer) const
Check to see whether the pad should be flashed on the specific layer.
Definition: pad.cpp:245
bool HasHole() const override
Definition: pad.h:105
std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const override
Return a SHAPE_SEGMENT object representing the pad's hole.
Definition: pad.cpp:384
const VECTOR2I & GetStart() const
Definition: pcb_track.h:111
const VECTOR2I & GetEnd() const
Definition: pcb_track.h:108
int GetDrill() const
Function GetDrill returns the local drill setting for this PCB_VIA.
Definition: pcb_track.h:471
std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const override
Definition: pcb_track.cpp:421
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
Definition: pcb_track.cpp:449
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
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
A lower-precision version of StringFromValue().
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:691
const BOX2I GetBoundingBox() const override
Definition: zone.cpp:309
virtual bool IsOnLayer(PCB_LAYER_ID) const override
Test to see if this object is on the given layer.
Definition: zone.cpp:303
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: zone.h:115
unsigned GetAssignedPriority() const
Definition: zone.h:106
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:46
@ HOLE_CLEARANCE_CONSTRAINT
Definition: drc_rule.h:47
#define _(s)
bool IsCopperLayer(int aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:823
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