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 The 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 <layer_range.h>
29#include <pcb_shape.h>
30#include <pad.h>
31#include <pcb_track.h>
32#include <thread_pool.h>
33#include <zone.h>
34
35#include <geometry/seg.h>
38
39#include <drc/drc_engine.h>
40#include <drc/drc_rtree.h>
41#include <drc/drc_item.h>
42#include <drc/drc_rule.h>
45#include <pcb_dimension.h>
46
47#include <future>
48
49/*
50 Copper clearance test. Checks all copper items (pads, vias, tracks, drawings, zones) for their
51 electrical clearance.
52
53 Errors generated:
54 - DRCE_CLEARANCE
55 - DRCE_HOLE_CLEARANCE
56 - DRCE_TRACKS_CROSSING
57 - DRCE_ZONES_INTERSECT
58 - DRCE_SHORTING_ITEMS
59*/
60
62{
63public:
66 m_drcEpsilon( 0 )
67 {
68 }
69
71 {
72 }
73
74 virtual bool Run() override;
75
76 virtual const wxString GetName() const override
77 {
78 return wxT( "clearance" );
79 };
80
81 virtual const wxString GetDescription() const override
82 {
83 return wxT( "Tests copper item clearance" );
84 }
85
86private:
95 bool testSingleLayerItemAgainstItem( BOARD_ITEM* item, SHAPE* itemShape, PCB_LAYER_ID layer,
96 BOARD_ITEM* other );
97
99
100 void testPadAgainstItem( PAD* pad, SHAPE* padShape, PCB_LAYER_ID layer, BOARD_ITEM* other );
101
102 void testPadClearances();
103
105
106 void testZonesToZones();
107
108 void testItemAgainstZone( BOARD_ITEM* aItem, ZONE* aZone, PCB_LAYER_ID aLayer );
109
110 void testKnockoutTextAgainstZone( BOARD_ITEM* aText, NETINFO_ITEM** aInheritedNet, ZONE* aZone );
111
112 typedef struct checked
113 {
115 : layers(), has_error( false ) {}
116
118 : layers( { aLayer } ), has_error( false ) {}
119
123
124private:
126};
127
128
130{
132
133 if( m_board->m_DRCMaxClearance <= 0 )
134 {
135 reportAux( wxT( "No Clearance constraints found. Tests not run." ) );
136 return true; // continue with other tests
137 }
138
140
142 {
143 if( !reportPhase( _( "Checking track & via clearances..." ) ) )
144 return false; // DRC cancelled
145
147 }
149 {
150 if( !reportPhase( _( "Checking hole clearances..." ) ) )
151 return false; // DRC cancelled
152
154 }
155
157 {
158 if( !reportPhase( _( "Checking pad clearances..." ) ) )
159 return false; // DRC cancelled
160
162 }
165 {
166 if( !reportPhase( _( "Checking pads..." ) ) )
167 return false; // DRC cancelled
168
170 }
171
173 {
174 if( !reportPhase( _( "Checking copper graphic clearances..." ) ) )
175 return false; // DRC cancelled
176
178 }
179
181 {
182 if( !reportPhase( _( "Checking copper zone clearances..." ) ) )
183 return false; // DRC cancelled
184
186 }
188 {
189 if( !reportPhase( _( "Checking zones..." ) ) )
190 return false; // DRC cancelled
191
193 }
194
196
197 return !m_drcEngine->IsCancelled();
198}
199
200
202 SHAPE* itemShape,
203 PCB_LAYER_ID layer,
204 BOARD_ITEM* other )
205{
206 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
209 DRC_CONSTRAINT constraint;
210 int clearance = -1;
211 int actual;
212 VECTOR2I pos;
213 bool has_error = false;
214 NETINFO_ITEM* net = nullptr;
215 NETINFO_ITEM* otherNet = nullptr;
216
217 if( BOARD_CONNECTED_ITEM* connectedItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
218 net = connectedItem->GetNet();
219
220 NETINFO_ITEM* trackNet = net;
221
222 if( BOARD_CONNECTED_ITEM* connectedItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( other ) )
223 otherNet = connectedItem->GetNet();
224
225 std::shared_ptr<SHAPE> otherShapeStorage = other->GetEffectiveShape( layer );
226 SHAPE* otherShape = otherShapeStorage.get();
227
228 if( other->Type() == PCB_PAD_T )
229 {
230 PAD* pad = static_cast<PAD*>( other );
231
232 if( pad->GetAttribute() == PAD_ATTRIB::NPTH && !pad->FlashLayer( layer ) )
233 testClearance = testShorting = false;
234 }
235
236 if( testClearance || testShorting )
237 {
238 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, item, other, layer );
239 clearance = constraint.GetValue().Min();
240 }
241
242 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
243 {
244 // Collide (and generate violations) based on a well-defined order so that exclusion
245 // checking against previously-generated violations will work.
246 if( item->m_Uuid > other->m_Uuid )
247 {
248 std::swap( item, other );
249 std::swap( itemShape, otherShape );
250 std::swap( net, otherNet );
251 }
252
253 // Special processing for track:track intersections
254 if( item->Type() == PCB_TRACE_T && other->Type() == PCB_TRACE_T )
255 {
256 PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
257 PCB_TRACK* otherTrack = static_cast<PCB_TRACK*>( other );
258
259 SEG trackSeg( track->GetStart(), track->GetEnd() );
260 SEG otherSeg( otherTrack->GetStart(), otherTrack->GetEnd() );
261
262 if( OPT_VECTOR2I intersection = trackSeg.Intersect( otherSeg ) )
263 {
264 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_TRACKS_CROSSING );
265 drcItem->SetItems( item, other );
266 drcItem->SetViolatingRule( constraint.GetParentRule() );
267
268 reportViolation( drcItem, *intersection, layer );
269
270 return false;
271 }
272 }
273
274 if( itemShape->Collide( otherShape, clearance - m_drcEpsilon, &actual, &pos ) )
275 {
276 if( m_drcEngine->IsNetTieExclusion( trackNet->GetNetCode(), layer, pos, other ) )
277 {
278 // Collision occurred as track was entering a pad marked as a net-tie. We
279 // allow these.
280 }
281 else if( actual == 0 && otherNet && testShorting )
282 {
283 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_SHORTING_ITEMS );
284 wxString msg;
285
286 msg.Printf( _( "(nets %s and %s)" ),
287 net ? net->GetNetname() : _( "<no net>" ),
288 otherNet ? otherNet->GetNetname() : _( "<no net>" ) );
289
290 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
291 drce->SetItems( item, other );
292
293 reportViolation( drce, pos, layer );
294 has_error = true;
295
297 return false;
298 }
299 else if( testClearance )
300 {
301 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
302 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
303 constraint.GetName(),
304 clearance,
305 actual );
306
307 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
308 drce->SetItems( item, other );
309 drce->SetViolatingRule( constraint.GetParentRule() );
310
311 ReportAndShowPathCuToCu( drce, pos, layer, item, other, layer, actual );
312 has_error = true;
313
315 return false;
316 }
317 }
318 }
319
320 if( testHoles && ( item->HasHole() || other->HasHole() ) )
321 {
322 std::array<BOARD_ITEM*, 2> a{ item, other };
323 std::array<BOARD_ITEM*, 2> b{ other, item };
324 std::array<SHAPE*, 2> a_shape{ itemShape, otherShape };
325
326 for( size_t ii = 0; ii < 2; ++ii )
327 {
328 std::shared_ptr<SHAPE_SEGMENT> holeShape;
329
330 // We only test a track item here against an item with a hole.
331 // If either case is not valid, simply move on
332 if( !( dynamic_cast<PCB_TRACK*>( a[ii] ) ) || !b[ii]->HasHole() )
333 {
334 continue;
335 }
336
337 if( b[ii]->Type() == PCB_VIA_T )
338 {
339 if( b[ii]->GetLayerSet().Contains( layer ) )
340 holeShape = b[ii]->GetEffectiveHoleShape();
341 }
342 else
343 {
344 holeShape = b[ii]->GetEffectiveHoleShape();
345 }
346
347 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, b[ii], a[ii], layer );
348 clearance = constraint.GetValue().Min();
349
350 // Test for hole to item clearance even if clearance is 0, because the item cannot be
351 // inside (or intersect) the hole.
352 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE )
353 {
354 if( a_shape[ii]->Collide( holeShape.get(), std::max( 0, clearance - m_drcEpsilon ),
355 &actual, &pos ) )
356 {
357 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
358 wxString msg = formatMsg( clearance ? _( "(%s clearance %s; actual %s)" )
359 : _( "(%s clearance %s; actual < 0)" ),
360 constraint.GetName(),
361 clearance,
362 actual );
363
364 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
365 drce->SetItems( a[ii], b[ii] );
366 drce->SetViolatingRule( constraint.GetParentRule() );
367
368 ReportAndShowPathCuToCu( drce, pos, layer, a[ii], b[ii], layer, actual );
369 return false;
370 }
371 }
372 }
373 }
374
375 return !has_error;
376}
377
378
380 PCB_LAYER_ID aLayer )
381{
382 if( !aZone->GetLayerSet().test( aLayer ) )
383 return;
384
385 if( aZone->GetNetCode() && aItem->IsConnected() )
386 {
387 if( aZone->GetNetCode() == static_cast<BOARD_CONNECTED_ITEM*>( aItem )->GetNetCode() )
388 return;
389 }
390
391 BOX2I itemBBox = aItem->GetBoundingBox();
392 BOX2I worstCaseBBox = itemBBox;
393
394 worstCaseBBox.Inflate( m_board->m_DRCMaxClearance );
395
396 if( !worstCaseBBox.Intersects( aZone->GetBoundingBox() ) )
397 return;
398
399 FOOTPRINT* parentFP = aItem->GetParentFootprint();
400
401 // Ignore graphic items which implement a net-tie to the zone's net on the layer being tested.
402 if( parentFP && parentFP->IsNetTie() && dynamic_cast<PCB_SHAPE*>( aItem ) )
403 {
404 std::set<PAD*> allowedNetTiePads;
405
406 for( PAD* pad : parentFP->Pads() )
407 {
408 if( pad->GetNetCode() == aZone->GetNetCode() && aZone->GetNetCode() != 0 )
409 {
410 if( pad->IsOnLayer( aLayer ) )
411 allowedNetTiePads.insert( pad );
412
413 for( PAD* other : parentFP->GetNetTiePads( pad ) )
414 {
415 if( other->IsOnLayer( aLayer ) )
416 allowedNetTiePads.insert( other );
417 }
418 }
419 }
420
421 if( !allowedNetTiePads.empty() )
422 {
423 std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape();
424
425 for( PAD* pad : allowedNetTiePads )
426 {
427 if( pad->GetBoundingBox().Intersects( itemBBox )
428 && pad->GetEffectiveShape( aLayer )->Collide( itemShape.get() ) )
429 {
430 return;
431 }
432 }
433 }
434 }
435
436 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
438
439 if( !testClearance && !testHoles )
440 return;
441
442 DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ aZone ].get();
443
444 if( !zoneTree )
445 return;
446
447 DRC_CONSTRAINT constraint;
448 int clearance = -1;
449 int actual;
450 VECTOR2I pos;
451
452 if( aItem->Type() == PCB_PAD_T )
453 {
454 PAD* pad = static_cast<PAD*>( aItem );
455 bool flashedPad = pad->FlashLayer( aLayer );
456 bool platedHole = pad->HasHole() && pad->GetAttribute() == PAD_ATTRIB::PTH;
457
458 if( !flashedPad && !platedHole )
459 testClearance = false;
460 }
461
462 if( testClearance )
463 {
464 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aItem, aZone, aLayer );
465 clearance = constraint.GetValue().Min();
466 }
467
468 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
469 {
470 std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aLayer, FLASHING::DEFAULT );
471
472 if( zoneTree->QueryColliding( itemBBox, itemShape.get(), aLayer,
473 std::max( 0, clearance - m_drcEpsilon ), &actual, &pos ) )
474 {
475 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
476 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
477 constraint.GetName(),
478 clearance,
479 actual );
480
481 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
482 drce->SetItems( aItem, aZone );
483 drce->SetViolatingRule( constraint.GetParentRule() );
484 ReportAndShowPathCuToCu( drce, pos, aLayer, aItem, aZone, aLayer, actual );
485 }
486 }
487
488 if( testHoles && aItem->HasHole() )
489 {
490 std::shared_ptr<SHAPE_SEGMENT> holeShape;
491
492 if( aItem->Type() == PCB_VIA_T )
493 {
494 if( aItem->GetLayerSet().Contains( aLayer ) )
495 holeShape = aItem->GetEffectiveHoleShape();
496 }
497 else
498 {
499 holeShape = aItem->GetEffectiveHoleShape();
500 }
501
502 if( holeShape )
503 {
504 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, aItem, aZone, aLayer );
505 clearance = constraint.GetValue().Min();
506
507 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
508 {
509 if( zoneTree->QueryColliding( itemBBox, holeShape.get(), aLayer,
510 std::max( 0, clearance - m_drcEpsilon ),
511 &actual, &pos ) )
512 {
513 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
514 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
515 constraint.GetName(),
516 clearance,
517 actual );
518
519 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
520 drce->SetItems( aItem, aZone );
521 drce->SetViolatingRule( constraint.GetParentRule() );
522 ReportAndShowPathCuToCu( drce, pos, aLayer, aItem, aZone, aLayer, actual );
523 }
524 }
525 }
526 }
527}
528
529
530/*
531 * We have to special-case knockout text as it's most often knocked-out of a zone, so it's
532 * presumed to collide with one. However, if it collides with more than one, and they have
533 * different nets, then we have a short.
534 */
536 NETINFO_ITEM** aInheritedNet,
537 ZONE* aZone )
538{
539 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
541
542 if( !testClearance && !testShorts )
543 return;
544
545 PCB_LAYER_ID layer = aText->GetLayer();
546
547 if( !aZone->GetLayerSet().test( layer ) )
548 return;
549
550 BOX2I itemBBox = aText->GetBoundingBox();
551 BOX2I worstCaseBBox = itemBBox;
552
553 worstCaseBBox.Inflate( m_board->m_DRCMaxClearance );
554
555 if( !worstCaseBBox.Intersects( aZone->GetBoundingBox() ) )
556 return;
557
558 DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ aZone ].get();
559
560 if( !zoneTree )
561 return;
562
563 std::shared_ptr<SHAPE> itemShape = aText->GetEffectiveShape( layer, FLASHING::DEFAULT );
564
565 if( *aInheritedNet == nullptr )
566 {
567 if( zoneTree->QueryColliding( itemBBox, itemShape.get(), layer ) )
568 *aInheritedNet = aZone->GetNet();
569 }
570
571 if( *aInheritedNet == aZone->GetNet() )
572 return;
573
574 DRC_CONSTRAINT constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aText, aZone, layer );
575 int clearance = constraint.GetValue().Min();
576 int actual;
577 VECTOR2I pos;
578
579 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
580 {
581 if( zoneTree->QueryColliding( itemBBox, itemShape.get(), layer,
582 std::max( 0, clearance - m_drcEpsilon ), &actual, &pos ) )
583 {
584 std::shared_ptr<DRC_ITEM> drce;
585 wxString msg;
586
587 if( testShorts && actual == 0 && *aInheritedNet )
588 {
590 msg.Printf( _( "(nets %s and %s)" ),
591 ( *aInheritedNet )->GetNetname(),
592 aZone->GetNetname() );
593 }
594 else
595 {
597 msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
598 constraint.GetName(),
599 clearance,
600 actual );
601 }
602
603 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
604 drce->SetItems( aText, aZone );
605 drce->SetViolatingRule( constraint.GetParentRule() );
606 ReportAndShowPathCuToCu( drce, pos, layer, aText, aZone, layer, actual );
607 }
608 }
609}
610
611
613{
614 std::map<BOARD_ITEM*, int> freePadsUsageMap;
615 std::unordered_map<PTR_PTR_CACHE_KEY, layers_checked> checkedPairs;
616 std::mutex checkedPairsMutex;
617 std::mutex freePadsUsageMapMutex;
618 std::atomic<size_t> done( 0 );
619 size_t count = m_board->Tracks().size();
620
621 reportAux( wxT( "Testing %d tracks & vias..." ), count );
622
623 LSET boardCopperLayers = LSET::AllCuMask( m_board->GetCopperLayerCount() );
624
625 auto testTrack = [&]( const int start_idx, const int end_idx )
626 {
627 for( int trackIdx = start_idx; trackIdx < end_idx; ++trackIdx )
628 {
629 PCB_TRACK* track = m_board->Tracks()[trackIdx];
630
631 for( PCB_LAYER_ID layer : LSET( track->GetLayerSet() & boardCopperLayers ).Seq() )
632 {
633 std::shared_ptr<SHAPE> trackShape = track->GetEffectiveShape( layer );
634
635 m_board->m_CopperItemRTreeCache->QueryColliding( track, layer, layer,
636 // Filter:
637 [&]( BOARD_ITEM* other ) -> bool
638 {
639 auto otherCItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( other );
640
641 if( otherCItem && otherCItem->GetNetCode() == track->GetNetCode() )
642 return false;
643
644 BOARD_ITEM* a = track;
645 BOARD_ITEM* b = other;
646
647 // store canonical order so we don't collide in both directions
648 // (a:b and b:a)
649 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
650 std::swap( a, b );
651
652 std::lock_guard<std::mutex> lock( checkedPairsMutex );
653 auto it = checkedPairs.find( { a, b } );
654
655 if( it != checkedPairs.end() && ( it->second.layers.test( layer )
656 || ( it->second.has_error && !m_drcEngine->GetReportAllTrackErrors() ) ) )
657 {
658 return false;
659 }
660 else
661 {
662 checkedPairs[ { a, b } ].layers.set( layer );
663 return true;
664 }
665 },
666 // Visitor:
667 [&]( BOARD_ITEM* other ) -> bool
668 {
669 if( m_drcEngine->IsCancelled() )
670 return false;
671
672 if( other->Type() == PCB_PAD_T && static_cast<PAD*>( other )->IsFreePad() )
673 {
674 if( other->GetEffectiveShape( layer )->Collide( trackShape.get() ) )
675 {
676 std::lock_guard<std::mutex> lock( freePadsUsageMapMutex );
677 auto it = freePadsUsageMap.find( other );
678
679 if( it == freePadsUsageMap.end() )
680 {
681 freePadsUsageMap[ other ] = track->GetNetCode();
682 return true; // Continue colliding tests
683 }
684 else if( it->second == track->GetNetCode() )
685 {
686 return true; // Continue colliding tests
687 }
688 }
689 }
690
691 BOARD_ITEM* a = track;
692 BOARD_ITEM* b = other;
693
694 // store canonical order so we don't collide in both directions
695 // (a:b and b:a)
696 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
697 std::swap( a, b );
698
699 // If we get an error, mark the pair as having a clearance error already
700 if( !testSingleLayerItemAgainstItem( track, trackShape.get(), layer, other ) )
701 {
702 std::lock_guard<std::mutex> lock( checkedPairsMutex );
703 auto it = checkedPairs.find( { a, b } );
704
705 if( it != checkedPairs.end() )
706 it->second.has_error = true;
707
709 return false; // We're done with this track
710 }
711
712 return !m_drcEngine->IsCancelled();
713 },
715
716 for( ZONE* zone : m_board->m_DRCCopperZones )
717 {
718 testItemAgainstZone( track, zone, layer );
719
720 if( m_drcEngine->IsCancelled() )
721 break;
722 }
723 }
724
725 done.fetch_add( 1 );
726 }
727 };
728
730
731 tp.push_loop( m_board->Tracks().size(), testTrack );
732
733 while( done < count )
734 {
735 reportProgress( done, count );
736
737 if( m_drcEngine->IsCancelled() )
738 {
739 tp.wait_for_tasks();
740 break;
741 }
742
743 std::this_thread::sleep_for( std::chrono::milliseconds( 250 ) );
744 }
745}
746
747
749 PCB_LAYER_ID aLayer,
750 BOARD_ITEM* other )
751{
752 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
755
756 // Disable some tests for net-tie objects in a footprint
757 if( other->GetParent() == pad->GetParent() )
758 {
759 FOOTPRINT* fp = pad->GetParentFootprint();
760 std::map<wxString, int> padToNetTieGroupMap = fp->MapPadNumbersToNetTieGroups();
761 int padGroupIdx = padToNetTieGroupMap[ pad->GetNumber() ];
762
763 if( other->Type() == PCB_PAD_T )
764 {
765 PAD* otherPad = static_cast<PAD*>( other );
766
767 if( padGroupIdx >= 0 && padGroupIdx == padToNetTieGroupMap[ otherPad->GetNumber() ] )
768 testClearance = testShorting = false;
769
770 if( pad->SameLogicalPadAs( otherPad ) )
771 testHoles = false;
772 }
773
774 if( other->Type() == PCB_SHAPE_T && padGroupIdx >= 0 )
775 testClearance = testShorting = false;
776 }
777
778 PAD* otherPad = nullptr;
779 PCB_VIA* otherVia = nullptr;
780
781 if( other->Type() == PCB_PAD_T )
782 otherPad = static_cast<PAD*>( other );
783
784 if( other->Type() == PCB_VIA_T )
785 otherVia = static_cast<PCB_VIA*>( other );
786
787 if( !IsCopperLayer( aLayer ) )
788 testClearance = testShorting = false;
789
790 // A NPTH has no cylinder, but it may still have pads on some layers
791 if( pad->GetAttribute() == PAD_ATTRIB::NPTH && !pad->FlashLayer( aLayer ) )
792 testClearance = testShorting = false;
793
794 if( otherPad && otherPad->GetAttribute() == PAD_ATTRIB::NPTH && !otherPad->FlashLayer( aLayer ) )
795 testClearance = testShorting = false;
796
797 // Track clearances are tested in testTrackClearances()
798 if( dynamic_cast<PCB_TRACK*>( other) )
799 testClearance = testShorting = false;
800
801 int padNet = pad->GetNetCode();
802 int otherNet = 0;
803
804 if( BOARD_CONNECTED_ITEM* connectedItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( other ) )
805 otherNet = connectedItem->GetNetCode();
806
807 // Other objects of the same (defined) net get a waiver on clearance and hole tests
808 if( otherNet && otherNet == padNet )
809 {
810 testClearance = testShorting = false;
811 testHoles = false;
812 }
813
814 if( !( pad->GetDrillSize().x > 0 )
815 && !( otherPad && otherPad->GetDrillSize().x > 0 )
816 && !( otherVia && otherVia->GetDrill() > 0 ) )
817 {
818 testHoles = false;
819 }
820
821 if( !testClearance && !testShorting && !testHoles )
822 return;
823
824 std::shared_ptr<SHAPE> otherShape = other->GetEffectiveShape( aLayer );
825 DRC_CONSTRAINT constraint;
826 int clearance = 0;
827 int actual = 0;
828 VECTOR2I pos;
829
830 if( otherPad && pad->SameLogicalPadAs( otherPad ) )
831 {
832 // If pads are equivalent (ie: from the same footprint with the same pad number)...
833 // ... and have "real" nets...
834 // then they must be the same net
835 if( testShorting )
836 {
837 if( pad->GetNetCode() == 0 || pad->GetNetCode() == otherPad->GetNetCode() )
838 return;
839
840 if( pad->GetShortNetname().StartsWith( wxS( "unconnected-(" ) )
841 && otherPad->GetShortNetname().StartsWith( wxS( "unconnected-(" ) ) )
842 {
843 return;
844 }
845
846 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_SHORTING_ITEMS );
847 wxString msg;
848
849 msg.Printf( _( "(nets %s and %s)" ),
850 pad->GetNetname(),
851 otherPad->GetNetname() );
852
853 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
854 drce->SetItems( pad, otherPad );
855
856 reportViolation( drce, otherPad->GetPosition(), aLayer );
857 }
858
859 return;
860 }
861
862 if( testClearance || testShorting )
863 {
864 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, pad, other, aLayer );
865 clearance = constraint.GetValue().Min();
866
867 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
868 {
869 if( padShape->Collide( otherShape.get(), std::max( 0, clearance - m_drcEpsilon ),
870 &actual, &pos ) )
871 {
872 if( m_drcEngine->IsNetTieExclusion( pad->GetNetCode(), aLayer, pos, other ) )
873 {
874 // Pads connected to pads of a net-tie footprint are allowed to collide
875 // with the net-tie footprint's graphics.
876 }
877 else if( actual == 0 && padNet && otherNet && testShorting )
878 {
879 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_SHORTING_ITEMS );
880 wxString msg;
881
882 msg.Printf( _( "(nets %s and %s)" ),
883 pad->GetNetname(),
884 static_cast<BOARD_CONNECTED_ITEM*>( other )->GetNetname() );
885
886 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
887 drce->SetItems( pad, other );
888
889 reportViolation( drce, pos, aLayer );
890 testHoles = false; // No need for multiple violations
891 }
892 else if( testClearance )
893 {
894 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
895 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
896 constraint.GetName(),
897 clearance,
898 actual );
899
900 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
901 drce->SetItems( pad, other );
902 drce->SetViolatingRule( constraint.GetParentRule() );
903 ReportAndShowPathCuToCu( drce, pos, aLayer, pad, other, aLayer, actual );
904 testHoles = false; // No need for multiple violations
905 }
906 }
907 }
908 }
909
910 if( testHoles )
911 {
912 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, pad, other, aLayer );
913 clearance = constraint.GetValue().Min();
914
915 if( constraint.GetSeverity() == RPT_SEVERITY_IGNORE )
916 testHoles = false;
917 }
918
919 if( testHoles && otherPad && pad->FlashLayer( aLayer ) && otherPad->HasHole() )
920 {
921 if( clearance > 0 && padShape->Collide( otherPad->GetEffectiveHoleShape().get(),
922 std::max( 0, clearance - m_drcEpsilon ),
923 &actual, &pos ) )
924 {
925 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
926 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
927 constraint.GetName(),
928 clearance,
929 actual );
930
931 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
932 drce->SetItems( pad, other );
933 drce->SetViolatingRule( constraint.GetParentRule() );
934 ReportAndShowPathCuToCu( drce, pos, aLayer, pad, other, aLayer, actual );
935 testHoles = false; // No need for multiple violations
936 }
937 }
938
939 if( testHoles && otherPad && otherPad->FlashLayer( aLayer ) && pad->HasHole() )
940 {
941 if( clearance > 0 && otherShape->Collide( pad->GetEffectiveHoleShape().get(),
942 std::max( 0, clearance - m_drcEpsilon ),
943 &actual, &pos ) )
944 {
945 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
946 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
947 constraint.GetName(),
948 clearance,
949 actual );
950
951 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
952 drce->SetItems( pad, other );
953 drce->SetViolatingRule( constraint.GetParentRule() );
954
955 reportViolation( drce, pos, aLayer );
956 testHoles = false; // No need for multiple violations
957 }
958 }
959
960 if( testHoles && otherVia && otherVia->IsOnLayer( aLayer ) )
961 {
962 if( clearance > 0 && padShape->Collide( otherVia->GetEffectiveHoleShape().get(),
963 std::max( 0, clearance - m_drcEpsilon ),
964 &actual, &pos ) )
965 {
966 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
967 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
968 constraint.GetName(),
969 clearance,
970 actual );
971
972 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
973 drce->SetItems( pad, otherVia );
974 drce->SetViolatingRule( constraint.GetParentRule() );
975 ReportAndShowPathCuToCu( drce, pos, aLayer, pad, otherVia, aLayer, actual );
976 }
977 }
978}
979
980
982{
984 size_t count = 0;
985 std::atomic<size_t> done( 1 );
986
987 for( FOOTPRINT* footprint : m_board->Footprints() )
988 count += footprint->Pads().size();
989
990 reportAux( wxT( "Testing %d pads..." ), count );
991
992 std::unordered_map<PTR_PTR_CACHE_KEY, int> checkedPairs;
993
994 LSET boardCopperLayers = LSET::AllCuMask( m_board->GetCopperLayerCount() );
995
996 std::future<void> retn = tp.submit(
997 [&]()
998 {
999 for( FOOTPRINT* footprint : m_board->Footprints() )
1000 {
1001 for( PAD* pad : footprint->Pads() )
1002 {
1003 for( PCB_LAYER_ID layer : LSET( pad->GetLayerSet() & boardCopperLayers ).Seq() )
1004 {
1005 if( m_drcEngine->IsCancelled() )
1006 return;
1007
1008 std::shared_ptr<SHAPE> padShape = pad->GetEffectiveShape( layer );
1009
1010 m_board->m_CopperItemRTreeCache->QueryColliding( pad, layer, layer,
1011 // Filter:
1012 [&]( BOARD_ITEM* other ) -> bool
1013 {
1014 BOARD_ITEM* a = pad;
1015 BOARD_ITEM* b = other;
1016
1017 // store canonical order so we don't collide in both
1018 // directions (a:b and b:a)
1019 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
1020 std::swap( a, b );
1021
1022 if( checkedPairs.find( { a, b } ) != checkedPairs.end() )
1023 {
1024 return false;
1025 }
1026 else
1027 {
1028 checkedPairs[ { a, b } ] = 1;
1029 return true;
1030 }
1031 },
1032 // Visitor
1033 [&]( BOARD_ITEM* other ) -> bool
1034 {
1035 testPadAgainstItem( pad, padShape.get(), layer, other );
1036
1037 return !m_drcEngine->IsCancelled();
1038 },
1039 m_board->m_DRCMaxClearance );
1040
1041 for( ZONE* zone : m_board->m_DRCCopperZones )
1042 {
1043 testItemAgainstZone( pad, zone, layer );
1044
1045 if( m_drcEngine->IsCancelled() )
1046 return;
1047 }
1048 }
1049
1050 done.fetch_add( 1 );
1051 }
1052 }
1053 } );
1054
1055 std::future_status status = retn.wait_for( std::chrono::milliseconds( 250 ) );
1056
1057 while( status != std::future_status::ready )
1058 {
1059 reportProgress( done, count );
1060 status = retn.wait_for( std::chrono::milliseconds( 250 ) );
1061 }
1062}
1063
1064
1066{
1068 size_t count = m_board->Drawings().size();
1069 std::atomic<size_t> done( 1 );
1070
1071 for( FOOTPRINT* footprint : m_board->Footprints() )
1072 count += footprint->GraphicalItems().size();
1073
1074 reportAux( wxT( "Testing %d graphics..." ), count );
1075
1076 auto isKnockoutText =
1077 []( BOARD_ITEM* item )
1078 {
1079 return item->Type() == PCB_TEXT_T && static_cast<PCB_TEXT*>( item )->IsKnockout();
1080 };
1081
1082 auto testGraphicAgainstZone =
1083 [&]( BOARD_ITEM* item )
1084 {
1085 if( item->Type() == PCB_REFERENCE_IMAGE_T )
1086 return;
1087
1088 if( !IsCopperLayer( item->GetLayer() ) )
1089 return;
1090
1091 // Knockout text is most often knocked-out of a zone, so it's presumed to
1092 // collide with one. However, if it collides with more than one, and they
1093 // have different nets, then we have a short.
1094 NETINFO_ITEM* inheritedNet = nullptr;
1095
1096 for( ZONE* zone : m_board->m_DRCCopperZones )
1097 {
1098 if( isKnockoutText( item ) )
1099 testKnockoutTextAgainstZone( item, &inheritedNet, zone );
1100 else
1101 testItemAgainstZone( item, zone, item->GetLayer() );
1102
1103 if( m_drcEngine->IsCancelled() )
1104 return;
1105 }
1106 };
1107
1108 std::unordered_map<PTR_PTR_CACHE_KEY, layers_checked> checkedPairs;
1109
1110 auto testCopperGraphic =
1111 [&]( PCB_SHAPE* aShape )
1112 {
1113 PCB_LAYER_ID layer = aShape->GetLayer();
1114
1115 m_board->m_CopperItemRTreeCache->QueryColliding( aShape, layer, layer,
1116 // Filter:
1117 [&]( BOARD_ITEM* other ) -> bool
1118 {
1119 auto otherCItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( other );
1120
1121 if( otherCItem && otherCItem->GetNetCode() == aShape->GetNetCode() )
1122 return false;
1123
1124 // Pads and tracks handled separately
1125 if( other->Type() == PCB_PAD_T || other->Type() == PCB_ARC_T ||
1126 other->Type() == PCB_TRACE_T || other->Type() == PCB_VIA_T )
1127 {
1128 return false;
1129 }
1130
1131 BOARD_ITEM* a = aShape;
1132 BOARD_ITEM* b = other;
1133
1134 // store canonical order so we don't collide in both directions
1135 // (a:b and b:a)
1136 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
1137 std::swap( a, b );
1138
1139 auto it = checkedPairs.find( { a, b } );
1140
1141 if( it != checkedPairs.end() && it->second.layers.test( layer ) )
1142 {
1143 return false;
1144 }
1145 else
1146 {
1147 checkedPairs[ { a, b } ].layers.set( layer );
1148 return true;
1149 }
1150 },
1151 // Visitor:
1152 [&]( BOARD_ITEM* other ) -> bool
1153 {
1154 BOARD_ITEM* a = aShape;
1155 BOARD_ITEM* b = other;
1156
1157 // store canonical order so we don't collide in both directions
1158 // (a:b and b:a)
1159 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
1160 std::swap( a, b );
1161
1162 auto it = checkedPairs.find( { a, b } );
1163
1164 if( !testSingleLayerItemAgainstItem( aShape,
1165 aShape->GetEffectiveShape().get(),
1166 layer, other ) )
1167 {
1168 if( it != checkedPairs.end() )
1169 it->second.has_error = true;
1170 }
1171
1172 return !m_drcEngine->IsCancelled();
1173 },
1175 };
1176
1177 std::future<void> retn = tp.submit(
1178 [&]()
1179 {
1180 for( BOARD_ITEM* item : m_board->Drawings() )
1181 {
1182 testGraphicAgainstZone( item );
1183
1184 if( item->Type() == PCB_SHAPE_T && item->IsOnCopperLayer() )
1185 testCopperGraphic( static_cast<PCB_SHAPE*>( item ) );
1186
1187 done.fetch_add( 1 );
1188
1189 if( m_drcEngine->IsCancelled() )
1190 break;
1191 }
1192
1193 for( FOOTPRINT* footprint : m_board->Footprints() )
1194 {
1195 for( BOARD_ITEM* item : footprint->GraphicalItems() )
1196 {
1197 testGraphicAgainstZone( item );
1198
1199 done.fetch_add( 1 );
1200
1201 if( m_drcEngine->IsCancelled() )
1202 break;
1203 }
1204 }
1205 } );
1206
1207 std::future_status status = retn.wait_for( std::chrono::milliseconds( 250 ) );
1208
1209 while( status != std::future_status::ready )
1210 {
1211 reportProgress( done, count );
1212 status = retn.wait_for( std::chrono::milliseconds( 250 ) );
1213 }
1214}
1215
1216
1218{
1219 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
1220 bool testIntersects = !m_drcEngine->IsErrorLimitExceeded( DRCE_ZONES_INTERSECT );
1221
1222 std::vector<std::map<PCB_LAYER_ID, std::vector<SEG>>> poly_segments;
1223 poly_segments.resize( m_board->m_DRCCopperZones.size() );
1224
1225 // Contains the index for zoneA, zoneB, the conflict point, the actual clearance, the
1226 // constraint, and the layer
1227 using report_data = std::tuple<int, int, VECTOR2I, int, DRC_CONSTRAINT, PCB_LAYER_ID>;
1228
1229 std::vector<std::future<report_data>> futures;
1231 std::atomic<size_t> done( 1 );
1232
1233 auto checkZones =
1234 [this, testClearance, testIntersects, &poly_segments, &done]
1235 ( int zoneA_idx, int zoneB_idx, bool sameNet, PCB_LAYER_ID layer ) -> report_data
1236 {
1237 ZONE* zoneA = m_board->m_DRCCopperZones[zoneA_idx];
1238 ZONE* zoneB = m_board->m_DRCCopperZones[zoneB_idx];
1239 int actual = 0;
1240 VECTOR2I pt;
1241
1242 if( sameNet && testIntersects )
1243 {
1244 if( zoneA->Outline()->Collide( zoneB->Outline(), 0, &actual, &pt ) )
1245 {
1246 done.fetch_add( 1 );
1247 return std::make_tuple( zoneA_idx, zoneB_idx, pt, 0, DRC_CONSTRAINT(), layer );
1248 }
1249 }
1250 else if( !sameNet && testClearance )
1251 {
1253 zoneA, zoneB, layer );
1254 int clearance = constraint.GetValue().Min();
1255
1256 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
1257 {
1258 std::map<VECTOR2I, int> conflictPoints;
1259
1260 std::vector<SEG>& refSegments = poly_segments[zoneA_idx][layer];
1261 std::vector<SEG>& testSegments = poly_segments[zoneB_idx][layer];
1262
1263 // Iterate through all the segments in zoneA
1264 for( SEG& refSegment : refSegments )
1265 {
1266 int ax1 = refSegment.A.x;
1267 int ay1 = refSegment.A.y;
1268 int ax2 = refSegment.B.x;
1269 int ay2 = refSegment.B.y;
1270
1271 // Iterate through all the segments in zoneB
1272 for( SEG& testSegment : testSegments )
1273 {
1274 // Build test segment
1275 int bx1 = testSegment.A.x;
1276 int by1 = testSegment.A.y;
1277 int bx2 = testSegment.B.x;
1278 int by2 = testSegment.B.y;
1279
1280 // We have ensured that the 'A' segment starts before the 'B' segment,
1281 // so if the 'A' segment ends before the 'B' segment starts, we can skip
1282 // to the next 'A'
1283 if( ax2 < bx1 )
1284 break;
1285
1286 actual = GetClearanceBetweenSegments( bx1, by1, bx2, by2, 0,
1287 ax1, ay1, ax2, ay2, 0,
1288 clearance, &pt.x, &pt.y );
1289
1290 if( actual < clearance )
1291 {
1292 done.fetch_add( 1 );
1293 return std::make_tuple( zoneA_idx, zoneB_idx, pt, actual, constraint,
1294 layer );
1295 }
1296 }
1297 }
1298 }
1299 }
1300
1301 done.fetch_add( 1 );
1302 return std::make_tuple( -1, -1, VECTOR2I(), 0, DRC_CONSTRAINT(), F_Cu );
1303 };
1304
1305
1307 {
1308 // Skip over layers not used on the current board
1309 if( !m_board->IsLayerEnabled( layer ) )
1310 continue;
1311
1312 for( size_t ii = 0; ii < m_board->m_DRCCopperZones.size(); ii++ )
1313 {
1314 if( m_board->m_DRCCopperZones[ii]->IsOnLayer( layer ) )
1315 {
1316 SHAPE_POLY_SET poly = *m_board->m_DRCCopperZones[ii]->GetFilledPolysList( layer );
1317 std::vector<SEG>& zone_layer_poly_segs = poly_segments[ii][layer];
1318
1319 poly.BuildBBoxCaches();
1320 zone_layer_poly_segs.reserve( poly.FullPointCount() );
1321
1322 for( auto it = poly.IterateSegmentsWithHoles(); it; it++ )
1323 {
1324 SEG seg = *it;
1325
1326 if( seg.A.x > seg.B.x )
1327 seg.Reverse();
1328
1329 zone_layer_poly_segs.push_back( seg );
1330 }
1331
1332 std::sort( zone_layer_poly_segs.begin(), zone_layer_poly_segs.end() );
1333 }
1334 }
1335
1336 std::vector<std::pair<int, int>> zonePairs;
1337
1338 for( size_t ia = 0; ia < m_board->m_DRCCopperZones.size(); ia++ )
1339 {
1340 ZONE* zoneA = m_board->m_DRCCopperZones[ia];
1341
1342 if( !zoneA->IsOnLayer( layer ) )
1343 continue;
1344
1345 for( size_t ia2 = ia + 1; ia2 < m_board->m_DRCCopperZones.size(); ia2++ )
1346 {
1347 ZONE* zoneB = m_board->m_DRCCopperZones[ia2];
1348
1349 // test for same layer
1350 if( !zoneB->IsOnLayer( layer ) )
1351 continue;
1352
1353 bool sameNet = zoneA->GetNetCode() == zoneB->GetNetCode() && zoneA->GetNetCode() >= 0;
1354
1355 if( sameNet && zoneA->GetAssignedPriority() != zoneB->GetAssignedPriority() )
1356 continue;
1357
1358 // rule areas may overlap at will
1359 if( zoneA->GetIsRuleArea() || zoneB->GetIsRuleArea() )
1360 continue;
1361
1362 // Examine a candidate zone: compare zoneB to zoneA
1363 SHAPE_POLY_SET* polyA = nullptr;
1364 SHAPE_POLY_SET* polyB = nullptr;
1365
1366 if( sameNet )
1367 {
1368 polyA = zoneA->Outline();
1369 polyB = zoneB->Outline();
1370 }
1371 else
1372 {
1373 polyA = zoneA->GetFill( layer );
1374 polyB = zoneB->GetFill( layer );
1375 }
1376
1377 if( !polyA->BBoxFromCaches().Intersects( polyB->BBoxFromCaches() ) )
1378 continue;
1379
1380 futures.push_back( tp.submit( checkZones, ia, ia2, sameNet, layer ) );
1381 }
1382 }
1383 }
1384
1385 size_t count = futures.size();
1386
1387 for( auto& task : futures )
1388 {
1389 if( !task.valid() )
1390 continue;
1391
1392 std::future_status result;
1393
1394 while( true )
1395 {
1396 result = task.wait_for( std::chrono::milliseconds( 250 ) );
1397
1398 reportProgress( done, count );
1399
1400 if( m_drcEngine->IsCancelled() )
1401 break;
1402
1403 if( result == std::future_status::ready )
1404 {
1405 report_data data = task.get();
1406 int zoneA_idx = std::get<0>( data );
1407 int zoneB_idx = std::get<1>( data );
1408 VECTOR2I pt = std::get<2>( data );
1409 int actual = std::get<3>( data );
1410 DRC_CONSTRAINT constraint = std::get<4>( data );
1411 PCB_LAYER_ID layer = std::get<5>( data );
1412
1413 if( zoneA_idx >= 0 )
1414 {
1415 ZONE* zoneA = m_board->m_DRCCopperZones[zoneA_idx];
1416 ZONE* zoneB = m_board->m_DRCCopperZones[zoneB_idx];
1417
1418 std::shared_ptr<DRC_ITEM> drce;
1419
1420 if( constraint.IsNull() )
1421 {
1423 wxString msg = _( "(intersecting zones must have distinct priorities)" );
1424 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
1425 drce->SetItems( zoneA, zoneB );
1426 reportViolation( drce, pt, layer );
1427 }
1428 else
1429 {
1431 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
1432 constraint.GetName(),
1433 constraint.GetValue().Min(),
1434 std::max( actual, 0 ) );
1435
1436 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
1437 drce->SetItems( zoneA, zoneB );
1438 drce->SetViolatingRule( constraint.GetParentRule() );
1439 ReportAndShowPathCuToCu( drce, pt, layer, zoneA, zoneB, layer, actual );
1440 }
1441 }
1442
1443 break;
1444 }
1445 }
1446 }
1447}
1448
1449
1450namespace detail
1451{
1453}
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
NETINFO_ITEM * GetNet() const
Return #NET_INFO object for a given item.
const wxString & GetShortNetname() const
int GetDRCEpsilon() const
Return an epsilon which accounts for rounding errors, etc.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:78
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:229
virtual bool IsConnected() const
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
Definition: board_item.h:131
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:279
FOOTPRINT * GetParentFootprint() const
Definition: board_item.cpp:299
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:249
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:207
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const
Definition: board_item.cpp:289
virtual bool HasHole() const
Definition: board_item.h:153
std::vector< ZONE * > m_DRCCopperZones
Definition: board.h:1330
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:864
int GetCopperLayerCount() const
Definition: board.cpp:790
const FOOTPRINTS & Footprints() const
Definition: board.h:338
const TRACKS & Tracks() const
Definition: board.h:336
std::shared_ptr< DRC_RTREE > m_CopperItemRTreeCache
Definition: board.h:1324
int m_DRCMaxClearance
Definition: board.h:1331
std::unordered_map< ZONE *, std::unique_ptr< DRC_RTREE > > m_CopperZoneRTreeCache
Definition: board.h:1323
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:955
const DRAWINGS & Drawings() const
Definition: board.h:340
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:558
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition: box2.h:311
wxString GetName() const
Definition: drc_rule.h:165
SEVERITY GetSeverity() const
Definition: drc_rule.h:178
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:157
DRC_RULE * GetParentRule() const
Definition: drc_rule.h:161
bool IsNull() const
Definition: drc_rule.h:152
BOARD * GetBoard() const
Definition: drc_engine.h:96
bool GetReportAllTrackErrors() const
Definition: drc_engine.h:170
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:690
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:393
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:214
void ReportAndShowPathCuToCu(std::shared_ptr< DRC_ITEM > &aDrce, const VECTOR2I &aMarkerPos, int aMarkerLayer, const BOARD_ITEM *aItem1, const BOARD_ITEM *aItem2, PCB_LAYER_ID layer, int aDistance)
void testPadAgainstItem(PAD *pad, SHAPE *padShape, PCB_LAYER_ID layer, BOARD_ITEM *other)
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).
bool testSingleLayerItemAgainstItem(BOARD_ITEM *item, SHAPE *itemShape, PCB_LAYER_ID layer, BOARD_ITEM *other)
Checks for track/via/hole <-> clearance.
void testItemAgainstZone(BOARD_ITEM *aItem, ZONE *aZone, PCB_LAYER_ID aLayer)
virtual const wxString GetDescription() const override
virtual const wxString GetName() const override
void testKnockoutTextAgainstZone(BOARD_ITEM *aText, NETINFO_ITEM **aInheritedNet, ZONE *aZone)
virtual bool reportPhase(const wxString &aStageName)
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer, DRC_CUSTOM_MARKER_HANDLER *aCustomHandler=nullptr)
DRC_ENGINE * m_drcEngine
void reportAux(const wxString &aMsg)
wxString formatMsg(const wxString &aFormatString, const wxString &aSource, double aConstraint, double aActual, EDA_DATA_TYPE aDataType=EDA_DATA_TYPE::DISTANCE)
virtual void reportRuleStatistics()
virtual bool reportProgress(size_t aCount, size_t aSize, size_t aDelta=1)
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition: eda_item.cpp:84
const KIID m_Uuid
Definition: eda_item.h:502
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:108
std::vector< PAD * > GetNetTiePads(PAD *aPad) const
Definition: footprint.cpp:3240
std::map< wxString, int > MapPadNumbersToNetTieGroups() const
Definition: footprint.cpp:3185
std::deque< PAD * > & Pads()
Definition: footprint.h:211
bool IsNetTie() const
Definition: footprint.h:302
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:37
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:583
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:297
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition: lset.h:63
T Min() const
Definition: minoptmax.h:33
Handle the data for a net.
Definition: netinfo.h:56
const wxString & GetNetname() const
Definition: netinfo.h:114
int GetNetCode() const
Definition: netinfo.h:108
Definition: pad.h:54
bool FlashLayer(int aLayer, bool aOnlyCheckIfPermitted=false) const
Check to see whether the pad should be flashed on the specific layer.
Definition: pad.cpp:357
const VECTOR2I & GetDrillSize() const
Definition: pad.h:305
PAD_ATTRIB GetAttribute() const
Definition: pad.h:440
const wxString & GetNumber() const
Definition: pad.h:136
VECTOR2I GetPosition() const override
Definition: pad.h:208
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:584
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: pcb_track.cpp:1242
const VECTOR2I & GetStart() const
Definition: pcb_track.h:152
std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING aFlash=FLASHING::DEFAULT) const override
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
Definition: pcb_track.cpp:2187
const VECTOR2I & GetEnd() const
Definition: pcb_track.h:149
int GetDrill() const
Return the local drill setting for this PCB_VIA.
Definition: pcb_track.h:676
std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const override
Definition: pcb_track.cpp:911
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
Definition: pcb_track.cpp:1172
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:254
void Reverse()
Definition: seg.h:358
Represent a set of closed polygons.
bool Collide(const SHAPE *aShape, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the shape aShape than aClearance,...
int FullPointCount() const
Return the number of points in the shape poly set.
void BuildBBoxCaches() const
Construct BBoxCaches for Contains(), below.
SEGMENT_ITERATOR IterateSegmentsWithHoles()
Returns an iterator object, for all outlines in the set (with holes)
const BOX2I BBoxFromCaches() const
An abstract shape on 2D plane.
Definition: shape.h:126
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:181
Handle a list of polygons defining a copper zone.
Definition: zone.h:74
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition: zone.h:752
const BOX2I GetBoundingBox() const override
Definition: zone.cpp:645
virtual PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: zone.cpp:486
SHAPE_POLY_SET * Outline()
Definition: zone.h:368
SHAPE_POLY_SET * GetFill(PCB_LAYER_ID aLayer)
Definition: zone.h:654
virtual bool IsOnLayer(PCB_LAYER_ID) const override
Test to see if this object is on the given layer.
Definition: zone.cpp:639
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: zone.h:136
unsigned GetAssignedPriority() const
Definition: zone.h:126
The common library.
@ DRCE_HOLE_CLEARANCE
Definition: drc_item.h:54
@ DRCE_ZONES_INTERSECT
Definition: drc_item.h:47
@ DRCE_CLEARANCE
Definition: drc_item.h:43
@ DRCE_SHORTING_ITEMS
Definition: drc_item.h:40
@ DRCE_TRACKS_CROSSING
Definition: drc_item.h:45
@ CLEARANCE_CONSTRAINT
Definition: drc_rule.h:49
@ HOLE_CLEARANCE_CONSTRAINT
Definition: drc_rule.h:51
#define _(s)
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition: layer_ids.h:663
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ B_Cu
Definition: layer_ids.h:65
@ 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)
int clearance
int actual
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
Definition: thread_pool.cpp:30
static thread_pool * tp
Definition: thread_pool.cpp:28
BS::thread_pool thread_pool
Definition: thread_pool.h:31
@ 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:97
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition: typeinfo.h:92
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition: typeinfo.h:89
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:98
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:96
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:695