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 && 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 DRC_CONSTRAINT constraint;
1222
1223 std::vector<std::map<PCB_LAYER_ID, std::vector<SEG>>> poly_segments;
1224 poly_segments.resize( m_board->m_DRCCopperZones.size() );
1225
1226 // Contains the index for zoneA, zoneB, the conflict point, the actual clearance, the
1227 // required clearance, and the layer
1228 using report_data = std::tuple<int, int, VECTOR2I, int, int, PCB_LAYER_ID>;
1229
1230 std::vector<std::future<report_data>> futures;
1232 std::atomic<size_t> done( 1 );
1233
1234 auto checkZones =
1235 [this, testClearance, testIntersects, &poly_segments, &done]
1236 ( int zoneA, int zoneB, int clearance, PCB_LAYER_ID layer ) -> report_data
1237 {
1238 // Iterate through all the segments of refSmoothedPoly
1239 std::map<VECTOR2I, int> conflictPoints;
1240
1241 std::vector<SEG>& refSegments = poly_segments[zoneA][layer];
1242 std::vector<SEG>& testSegments = poly_segments[zoneB][layer];
1243 bool reported = false;
1244 auto invalid_result = std::make_tuple( -1, -1, VECTOR2I(), 0, 0, F_Cu );
1245
1246 for( SEG& refSegment : refSegments )
1247 {
1248 int ax1 = refSegment.A.x;
1249 int ay1 = refSegment.A.y;
1250 int ax2 = refSegment.B.x;
1251 int ay2 = refSegment.B.y;
1252
1253 // Iterate through all the segments in smoothed_polys[ia2]
1254 for( SEG& testSegment : testSegments )
1255 {
1256 // Build test segment
1257 VECTOR2I pt;
1258
1259 int bx1 = testSegment.A.x;
1260 int by1 = testSegment.A.y;
1261 int bx2 = testSegment.B.x;
1262 int by2 = testSegment.B.y;
1263
1264 // We have ensured that the 'A' segment starts before the 'B' segment,
1265 // so if the 'A' segment ends before the 'B' segment starts, we can skip
1266 // to the next 'A'
1267 if( ax2 < bx1 )
1268 break;
1269
1270 int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2, 0,
1271 ax1, ay1, ax2, ay2, 0,
1272 clearance, &pt.x, &pt.y );
1273
1274 if( d < clearance )
1275 {
1276 if( d == 0 && testIntersects )
1277 reported = true;
1278 else if( testClearance )
1279 reported = true;
1280
1281 if( reported )
1282 {
1283 done.fetch_add( 1 );
1284 return std::make_tuple( zoneA, zoneB, pt, d, clearance, layer );
1285 }
1286 }
1287
1288 if( m_drcEngine->IsCancelled() )
1289 return invalid_result;
1290 }
1291 }
1292
1293 done.fetch_add( 1 );
1294 return invalid_result;
1295 };
1296
1297
1299 {
1300 int zone2zoneClearance;
1301
1302 // Skip over layers not used on the current board
1303 if( !m_board->IsLayerEnabled( layer ) )
1304 continue;
1305
1306 for( size_t ii = 0; ii < m_board->m_DRCCopperZones.size(); ii++ )
1307 {
1308 if( m_board->m_DRCCopperZones[ii]->IsOnLayer( layer ) )
1309 {
1310 SHAPE_POLY_SET poly = *m_board->m_DRCCopperZones[ii]->GetFilledPolysList( layer );
1311 std::vector<SEG>& zone_layer_poly_segs = poly_segments[ii][layer];
1312
1313 poly.BuildBBoxCaches();
1314 zone_layer_poly_segs.reserve( poly.FullPointCount() );
1315
1316 for( auto it = poly.IterateSegmentsWithHoles(); it; it++ )
1317 {
1318 SEG seg = *it;
1319
1320 if( seg.A.x > seg.B.x )
1321 seg.Reverse();
1322
1323 zone_layer_poly_segs.push_back( seg );
1324 }
1325
1326 std::sort( zone_layer_poly_segs.begin(), zone_layer_poly_segs.end() );
1327 }
1328 }
1329
1330 std::vector<std::pair<int, int>> zonePairs;
1331
1332 for( size_t ia = 0; ia < m_board->m_DRCCopperZones.size(); ia++ )
1333 {
1334 ZONE* zoneA = m_board->m_DRCCopperZones[ia];
1335
1336 if( !zoneA->IsOnLayer( layer ) )
1337 continue;
1338
1339 for( size_t ia2 = ia + 1; ia2 < m_board->m_DRCCopperZones.size(); ia2++ )
1340 {
1341 ZONE* zoneB = m_board->m_DRCCopperZones[ia2];
1342
1343 // test for same layer
1344 if( !zoneB->IsOnLayer( layer ) )
1345 continue;
1346
1347 // Test for same net
1348 if( zoneA->GetNetCode() == zoneB->GetNetCode() && zoneA->GetNetCode() >= 0 )
1349 continue;
1350
1351 // rule areas may overlap at will
1352 if( zoneA->GetIsRuleArea() || zoneB->GetIsRuleArea() )
1353 continue;
1354
1355 // Examine a candidate zone: compare zoneB to zoneA
1356 SHAPE_POLY_SET* polyA = m_board->m_DRCCopperZones[ia]->GetFill( layer );
1357 SHAPE_POLY_SET* polyB = m_board->m_DRCCopperZones[ia2]->GetFill( layer );
1358
1359 if( !polyA->BBoxFromCaches().Intersects( polyB->BBoxFromCaches() ) )
1360 continue;
1361
1362 // Get clearance used in zone to zone test.
1363 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, zoneA, zoneB, layer );
1364 zone2zoneClearance = constraint.GetValue().Min();
1365
1366 if( constraint.GetSeverity() == RPT_SEVERITY_IGNORE || zone2zoneClearance <= 0 )
1367 continue;
1368
1369 futures.push_back( tp.submit( checkZones, ia, ia2, zone2zoneClearance, layer ) );
1370 }
1371 }
1372 }
1373
1374 size_t count = futures.size();
1375
1376 for( auto& task : futures )
1377 {
1378 if( !task.valid() )
1379 continue;
1380
1381 std::future_status result;
1382
1383 while( true )
1384 {
1385 result = task.wait_for( std::chrono::milliseconds( 250 ) );
1386
1387 reportProgress( done, count );
1388
1389 if( m_drcEngine->IsCancelled() )
1390 break;
1391
1392 if( result == std::future_status::ready )
1393 {
1394 report_data data = task.get();
1395 int zoneA_idx = std::get<0>( data );
1396 int zoneB_idx = std::get<1>( data );
1397 VECTOR2I pt = std::get<2>( data );
1398 int actual = std::get<3>( data );
1399 int required = std::get<4>( data );
1400 PCB_LAYER_ID layer = std::get<5>( data );
1401
1402 if( zoneA_idx >= 0 )
1403 {
1404 ZONE* zoneA = m_board->m_DRCCopperZones[zoneA_idx];
1405 ZONE* zoneB = m_board->m_DRCCopperZones[zoneB_idx];
1406
1407 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, zoneA, zoneB, layer );
1408 std::shared_ptr<DRC_ITEM> drce;
1409
1410 if( actual <= 0 && testIntersects )
1411 {
1413 }
1414 else if( testClearance )
1415 {
1417 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
1418 constraint.GetName(),
1419 required,
1420 std::max( actual, 0 ) );
1421
1422 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
1423 }
1424
1425 if( drce )
1426 {
1427 drce->SetItems( zoneA, zoneB );
1428 drce->SetViolatingRule( constraint.GetParentRule() );
1429 ReportAndShowPathCuToCu( drce, pt, layer, zoneA, zoneB, layer, actual );
1430 }
1431 }
1432
1433 break;
1434 }
1435 }
1436 }
1437}
1438
1439
1440namespace detail
1441{
1443}
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:79
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:239
virtual bool IsConnected() const
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
Definition: board_item.h:133
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:278
FOOTPRINT * GetParentFootprint() const
Definition: board_item.cpp:298
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:259
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:217
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const
Definition: board_item.cpp:288
virtual bool HasHole() const
Definition: board_item.h:155
std::vector< ZONE * > m_DRCCopperZones
Definition: board.h:1318
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:843
int GetCopperLayerCount() const
Definition: board.cpp:780
const FOOTPRINTS & Footprints() const
Definition: board.h:336
const TRACKS & Tracks() const
Definition: board.h:334
std::shared_ptr< DRC_RTREE > m_CopperItemRTreeCache
Definition: board.h:1312
int m_DRCMaxClearance
Definition: board.h:1319
std::unordered_map< ZONE *, std::unique_ptr< DRC_RTREE > > m_CopperZoneRTreeCache
Definition: board.h:1311
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:934
const DRAWINGS & Drawings() const
Definition: board.h:338
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:160
SEVERITY GetSeverity() const
Definition: drc_rule.h:173
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:152
DRC_RULE * GetParentRule() const
Definition: drc_rule.h:156
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:395
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:217
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)
wxString formatMsg(const wxString &aFormatString, const wxString &aSource, double aConstraint, double aActual)
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)
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:77
const KIID m_Uuid
Definition: eda_item.h:490
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:101
std::vector< PAD * > GetNetTiePads(PAD *aPad) const
Definition: footprint.cpp:3225
std::map< wxString, int > MapPadNumbersToNetTieGroups() const
Definition: footprint.cpp:3170
std::deque< PAD * > & Pads()
Definition: footprint.h:204
bool IsNetTie() const
Definition: footprint.h:295
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:562
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:295
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:341
const VECTOR2I & GetDrillSize() const
Definition: pad.h:303
PAD_ATTRIB GetAttribute() const
Definition: pad.h:438
const wxString & GetNumber() const
Definition: pad.h:134
VECTOR2I GetPosition() const override
Definition: pad.h:206
bool HasHole() const override
Definition: pad.h:104
std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const override
Return a SHAPE_SEGMENT object representing the pad's hole.
Definition: pad.cpp:569
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: pcb_track.cpp:1046
const VECTOR2I & GetStart() const
Definition: pcb_track.h:122
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:1931
const VECTOR2I & GetEnd() const
Definition: pcb_track.h:119
int GetDrill() const
Return the local drill setting for this PCB_VIA.
Definition: pcb_track.h:618
std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const override
Definition: pcb_track.cpp:853
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
Definition: pcb_track.cpp:976
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.
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:73
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition: zone.h:724
const BOX2I GetBoundingBox() const override
Definition: zone.cpp:570
virtual PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: zone.cpp:448
virtual bool IsOnLayer(PCB_LAYER_ID) const override
Test to see if this object is on the given layer.
Definition: zone.cpp:564
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: zone.h:133
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:581
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)
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