KiCad PCB EDA Suite
Loading...
Searching...
No Matches
drc_test_provider_copper_clearance.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2004-2024 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 <core/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)" ), net->GetNetname(), otherNet->GetNetname() );
287
288 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
289 drce->SetItems( item, other );
290
291 reportViolation( drce, pos, layer );
292 has_error = true;
293
295 return false;
296 }
297 else if( testClearance )
298 {
299 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
300 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
301 constraint.GetName(),
302 clearance,
303 actual );
304
305 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
306 drce->SetItems( item, other );
307 drce->SetViolatingRule( constraint.GetParentRule() );
308
309 ReportAndShowPathCuToCu( drce, pos, layer, item, other, layer, actual );
310 has_error = true;
311
313 return false;
314 }
315 }
316 }
317
318 if( testHoles && ( item->HasHole() || other->HasHole() ) )
319 {
320 std::array<BOARD_ITEM*, 2> a{ item, other };
321 std::array<BOARD_ITEM*, 2> b{ other, item };
322 std::array<SHAPE*, 2> a_shape{ itemShape, otherShape };
323
324 for( size_t ii = 0; ii < 2; ++ii )
325 {
326 std::shared_ptr<SHAPE_SEGMENT> holeShape;
327
328 // We only test a track item here against an item with a hole.
329 // If either case is not valid, simply move on
330 if( !( dynamic_cast<PCB_TRACK*>( a[ii] ) ) || !b[ii]->HasHole() )
331 {
332 continue;
333 }
334
335 if( b[ii]->Type() == PCB_VIA_T )
336 {
337 if( b[ii]->GetLayerSet().Contains( layer ) )
338 holeShape = b[ii]->GetEffectiveHoleShape();
339 }
340 else
341 {
342 holeShape = b[ii]->GetEffectiveHoleShape();
343 }
344
345 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, b[ii], a[ii], layer );
346 clearance = constraint.GetValue().Min();
347
348 // Test for hole to item clearance even if clearance is 0, because the item cannot be
349 // inside (or intersect) the hole.
350 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE )
351 {
352 if( a_shape[ii]->Collide( holeShape.get(), std::max( 0, clearance - m_drcEpsilon ),
353 &actual, &pos ) )
354 {
355 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
356 wxString msg = formatMsg( clearance ? _( "(%s clearance %s; actual %s)" )
357 : _( "(%s clearance %s; actual < 0)" ),
358 constraint.GetName(),
359 clearance,
360 actual );
361
362 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
363 drce->SetItems( a[ii], b[ii] );
364 drce->SetViolatingRule( constraint.GetParentRule() );
365
366 ReportAndShowPathCuToCu( drce, pos, layer, a[ii], b[ii], layer, actual );
367 return false;
368 }
369 }
370 }
371 }
372
373 return !has_error;
374}
375
376
378 PCB_LAYER_ID aLayer )
379{
380 if( !aZone->GetLayerSet().test( aLayer ) )
381 return;
382
383 if( aZone->GetNetCode() && aItem->IsConnected() )
384 {
385 if( aZone->GetNetCode() == static_cast<BOARD_CONNECTED_ITEM*>( aItem )->GetNetCode() )
386 return;
387 }
388
389 BOX2I itemBBox = aItem->GetBoundingBox();
390 BOX2I worstCaseBBox = itemBBox;
391
392 worstCaseBBox.Inflate( m_board->m_DRCMaxClearance );
393
394 if( !worstCaseBBox.Intersects( aZone->GetBoundingBox() ) )
395 return;
396
397 FOOTPRINT* parentFP = aItem->GetParentFootprint();
398
399 // Ignore graphic items which implement a net-tie to the zone's net on the layer being tested.
400 if( parentFP && parentFP->IsNetTie() && dynamic_cast<PCB_SHAPE*>( aItem ) )
401 {
402 std::set<PAD*> allowedNetTiePads;
403
404 for( PAD* pad : parentFP->Pads() )
405 {
406 if( pad->GetNetCode() == aZone->GetNetCode() && aZone->GetNetCode() != 0 )
407 {
408 if( pad->IsOnLayer( aLayer ) )
409 allowedNetTiePads.insert( pad );
410
411 for( PAD* other : parentFP->GetNetTiePads( pad ) )
412 {
413 if( other->IsOnLayer( aLayer ) )
414 allowedNetTiePads.insert( other );
415 }
416 }
417 }
418
419 if( !allowedNetTiePads.empty() )
420 {
421 std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape();
422
423 for( PAD* pad : allowedNetTiePads )
424 {
425 if( pad->GetBoundingBox().Intersects( itemBBox )
426 && pad->GetEffectiveShape( aLayer )->Collide( itemShape.get() ) )
427 {
428 return;
429 }
430 }
431 }
432 }
433
434 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
436
437 if( !testClearance && !testHoles )
438 return;
439
440 DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ aZone ].get();
441
442 if( !zoneTree )
443 return;
444
445 DRC_CONSTRAINT constraint;
446 int clearance = -1;
447 int actual;
448 VECTOR2I pos;
449
450 if( aItem->Type() == PCB_PAD_T )
451 {
452 PAD* pad = static_cast<PAD*>( aItem );
453 bool flashedPad = pad->FlashLayer( aLayer );
454 bool platedHole = pad->HasHole() && pad->GetAttribute() == PAD_ATTRIB::PTH;
455
456 if( !flashedPad && !platedHole )
457 testClearance = false;
458 }
459
460 if( testClearance )
461 {
462 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aItem, aZone, aLayer );
463 clearance = constraint.GetValue().Min();
464 }
465
466 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
467 {
468 std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aLayer, FLASHING::DEFAULT );
469
470 if( zoneTree->QueryColliding( itemBBox, itemShape.get(), aLayer,
471 std::max( 0, clearance - m_drcEpsilon ), &actual, &pos ) )
472 {
473 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
474 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
475 constraint.GetName(),
476 clearance,
477 actual );
478
479 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
480 drce->SetItems( aItem, aZone );
481 drce->SetViolatingRule( constraint.GetParentRule() );
482 ReportAndShowPathCuToCu( drce, pos, aLayer, aItem, aZone, aLayer, actual );
483 }
484 }
485
486 if( testHoles && aItem->HasHole() )
487 {
488 std::shared_ptr<SHAPE_SEGMENT> holeShape;
489
490 if( aItem->Type() == PCB_VIA_T )
491 {
492 if( aItem->GetLayerSet().Contains( aLayer ) )
493 holeShape = aItem->GetEffectiveHoleShape();
494 }
495 else
496 {
497 holeShape = aItem->GetEffectiveHoleShape();
498 }
499
500 if( holeShape )
501 {
502 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, aItem, aZone, aLayer );
503 clearance = constraint.GetValue().Min();
504
505 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
506 {
507 if( zoneTree->QueryColliding( itemBBox, holeShape.get(), aLayer,
508 std::max( 0, clearance - m_drcEpsilon ),
509 &actual, &pos ) )
510 {
511 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
512 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
513 constraint.GetName(),
514 clearance,
515 actual );
516
517 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
518 drce->SetItems( aItem, aZone );
519 drce->SetViolatingRule( constraint.GetParentRule() );
520 ReportAndShowPathCuToCu( drce, pos, aLayer, aItem, aZone, aLayer, actual );
521 }
522 }
523 }
524 }
525}
526
527
528/*
529 * We have to special-case knockout text as it's most often knocked-out of a zone, so it's
530 * presumed to collide with one. However, if it collides with more than one, and they have
531 * different nets, then we have a short.
532 */
534 NETINFO_ITEM** aInheritedNet,
535 ZONE* aZone )
536{
537 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
539
540 if( !testClearance && !testShorts )
541 return;
542
543 PCB_LAYER_ID layer = aText->GetLayer();
544
545 if( !aZone->GetLayerSet().test( layer ) )
546 return;
547
548 BOX2I itemBBox = aText->GetBoundingBox();
549 BOX2I worstCaseBBox = itemBBox;
550
551 worstCaseBBox.Inflate( m_board->m_DRCMaxClearance );
552
553 if( !worstCaseBBox.Intersects( aZone->GetBoundingBox() ) )
554 return;
555
556 DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ aZone ].get();
557
558 if( !zoneTree )
559 return;
560
561 std::shared_ptr<SHAPE> itemShape = aText->GetEffectiveShape( layer, FLASHING::DEFAULT );
562
563 if( *aInheritedNet == nullptr )
564 {
565 if( zoneTree->QueryColliding( itemBBox, itemShape.get(), layer ) )
566 *aInheritedNet = aZone->GetNet();
567 }
568
569 if( *aInheritedNet == aZone->GetNet() )
570 return;
571
572 DRC_CONSTRAINT constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aText, aZone, layer );
573 int clearance = constraint.GetValue().Min();
574 int actual;
575 VECTOR2I pos;
576
577 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
578 {
579 if( zoneTree->QueryColliding( itemBBox, itemShape.get(), layer,
580 std::max( 0, clearance - m_drcEpsilon ), &actual, &pos ) )
581 {
582 std::shared_ptr<DRC_ITEM> drce;
583 wxString msg;
584
585 if( testShorts && actual == 0 && *aInheritedNet )
586 {
588 msg.Printf( _( "(nets %s and %s)" ),
589 ( *aInheritedNet )->GetNetname(),
590 aZone->GetNetname() );
591 }
592 else
593 {
595 msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
596 constraint.GetName(),
597 clearance,
598 actual );
599 }
600
601 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
602 drce->SetItems( aText, aZone );
603 drce->SetViolatingRule( constraint.GetParentRule() );
604 ReportAndShowPathCuToCu( drce, pos, layer, aText, aZone, layer, actual );
605 }
606 }
607}
608
609
611{
612 std::map<BOARD_ITEM*, int> freePadsUsageMap;
613 std::unordered_map<PTR_PTR_CACHE_KEY, layers_checked> checkedPairs;
614 std::mutex checkedPairsMutex;
615 std::mutex freePadsUsageMapMutex;
616 std::atomic<size_t> done( 0 );
617 size_t count = m_board->Tracks().size();
618
619 reportAux( wxT( "Testing %d tracks & vias..." ), count );
620
621 LSET boardCopperLayers = LSET::AllCuMask( m_board->GetCopperLayerCount() );
622
623 auto testTrack = [&]( const int start_idx, const int end_idx )
624 {
625 for( int trackIdx = start_idx; trackIdx < end_idx; ++trackIdx )
626 {
627 PCB_TRACK* track = m_board->Tracks()[trackIdx];
628
629 for( PCB_LAYER_ID layer : LSET( track->GetLayerSet() & boardCopperLayers ).Seq() )
630 {
631 std::shared_ptr<SHAPE> trackShape = track->GetEffectiveShape( layer );
632
633 m_board->m_CopperItemRTreeCache->QueryColliding( track, layer, layer,
634 // Filter:
635 [&]( BOARD_ITEM* other ) -> bool
636 {
637 auto otherCItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( other );
638
639 if( otherCItem && otherCItem->GetNetCode() == track->GetNetCode() )
640 return false;
641
642 BOARD_ITEM* a = track;
643 BOARD_ITEM* b = other;
644
645 // store canonical order so we don't collide in both directions
646 // (a:b and b:a)
647 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
648 std::swap( a, b );
649
650 std::lock_guard<std::mutex> lock( checkedPairsMutex );
651 auto it = checkedPairs.find( { a, b } );
652
653 if( it != checkedPairs.end() && ( it->second.layers.test( layer )
654 || ( it->second.has_error && !m_drcEngine->GetReportAllTrackErrors() ) ) )
655 {
656 return false;
657 }
658 else
659 {
660 checkedPairs[ { a, b } ].layers.set( layer );
661 return true;
662 }
663 },
664 // Visitor:
665 [&]( BOARD_ITEM* other ) -> bool
666 {
667 if( m_drcEngine->IsCancelled() )
668 return false;
669
670 if( other->Type() == PCB_PAD_T && static_cast<PAD*>( other )->IsFreePad() )
671 {
672 if( other->GetEffectiveShape( layer )->Collide( trackShape.get() ) )
673 {
674 std::lock_guard<std::mutex> lock( freePadsUsageMapMutex );
675 auto it = freePadsUsageMap.find( other );
676
677 if( it == freePadsUsageMap.end() )
678 {
679 freePadsUsageMap[ other ] = track->GetNetCode();
680 return true; // Continue colliding tests
681 }
682 else if( it->second == track->GetNetCode() )
683 {
684 return true; // Continue colliding tests
685 }
686 }
687 }
688
689 BOARD_ITEM* a = track;
690 BOARD_ITEM* b = other;
691
692 // store canonical order so we don't collide in both directions
693 // (a:b and b:a)
694 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
695 std::swap( a, b );
696
697 // If we get an error, mark the pair as having a clearance error already
698 if( !testSingleLayerItemAgainstItem( track, trackShape.get(), layer, other ) )
699 {
700 std::lock_guard<std::mutex> lock( checkedPairsMutex );
701 auto it = checkedPairs.find( { a, b } );
702
703 if( it != checkedPairs.end() )
704 it->second.has_error = true;
705
707 return false; // We're done with this track
708 }
709
710 return !m_drcEngine->IsCancelled();
711 },
713
714 for( ZONE* zone : m_board->m_DRCCopperZones )
715 {
716 testItemAgainstZone( track, zone, layer );
717
718 if( m_drcEngine->IsCancelled() )
719 break;
720 }
721 }
722
723 done.fetch_add( 1 );
724 }
725 };
726
728
729 tp.push_loop( m_board->Tracks().size(), testTrack );
730
731 while( done < count )
732 {
733 reportProgress( done, count );
734
735 if( m_drcEngine->IsCancelled() )
736 {
737 tp.wait_for_tasks();
738 break;
739 }
740
741 std::this_thread::sleep_for( std::chrono::milliseconds( 250 ) );
742 }
743}
744
745
747 PCB_LAYER_ID aLayer,
748 BOARD_ITEM* other )
749{
750 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
753
754 // Disable some tests for net-tie objects in a footprint
755 if( other->GetParent() == pad->GetParent() )
756 {
757 FOOTPRINT* fp = pad->GetParentFootprint();
758 std::map<wxString, int> padToNetTieGroupMap = fp->MapPadNumbersToNetTieGroups();
759 int padGroupIdx = padToNetTieGroupMap[ pad->GetNumber() ];
760
761 if( other->Type() == PCB_PAD_T )
762 {
763 PAD* otherPad = static_cast<PAD*>( other );
764
765 if( padGroupIdx >= 0 && padGroupIdx == padToNetTieGroupMap[ otherPad->GetNumber() ] )
766 testClearance = testShorting = false;
767
768 if( pad->SameLogicalPadAs( otherPad ) )
769 testHoles = false;
770 }
771
772 if( other->Type() == PCB_SHAPE_T && padGroupIdx >= 0 )
773 testClearance = testShorting = false;
774 }
775
776 PAD* otherPad = nullptr;
777 PCB_VIA* otherVia = nullptr;
778
779 if( other->Type() == PCB_PAD_T )
780 otherPad = static_cast<PAD*>( other );
781
782 if( other->Type() == PCB_VIA_T )
783 otherVia = static_cast<PCB_VIA*>( other );
784
785 if( !IsCopperLayer( aLayer ) )
786 testClearance = testShorting = false;
787
788 // A NPTH has no cylinder, but it may still have pads on some layers
789 if( pad->GetAttribute() == PAD_ATTRIB::NPTH && !pad->FlashLayer( aLayer ) )
790 testClearance = testShorting = false;
791
792 if( otherPad && otherPad->GetAttribute() == PAD_ATTRIB::NPTH && !otherPad->FlashLayer( aLayer ) )
793 testClearance = testShorting = false;
794
795 // Track clearances are tested in testTrackClearances()
796 if( dynamic_cast<PCB_TRACK*>( other) )
797 testClearance = testShorting = false;
798
799 int padNet = pad->GetNetCode();
800 int otherNet = 0;
801
802 if( BOARD_CONNECTED_ITEM* connectedItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( other ) )
803 otherNet = connectedItem->GetNetCode();
804
805 // Other objects of the same (defined) net get a waiver on clearance and hole tests
806 if( otherNet && otherNet == padNet )
807 {
808 testClearance = testShorting = false;
809 testHoles = false;
810 }
811
812 if( !( pad->GetDrillSize().x > 0 )
813 && !( otherPad && otherPad->GetDrillSize().x > 0 )
814 && !( otherVia && otherVia->GetDrill() > 0 ) )
815 {
816 testHoles = false;
817 }
818
819 if( !testClearance && !testShorting && !testHoles )
820 return;
821
822 std::shared_ptr<SHAPE> otherShape = other->GetEffectiveShape( aLayer );
823 DRC_CONSTRAINT constraint;
824 int clearance = 0;
825 int actual = 0;
826 VECTOR2I pos;
827
828 if( otherPad && pad->SameLogicalPadAs( otherPad ) )
829 {
830 // If pads are equivalent (ie: from the same footprint with the same pad number)...
831 // ... and have "real" nets...
832 // then they must be the same net
833 if( testShorting )
834 {
835 if( pad->GetNetCode() == 0 || pad->GetNetCode() == otherPad->GetNetCode() )
836 return;
837
838 if( pad->GetShortNetname().StartsWith( wxS( "unconnected-(" ) )
839 && otherPad->GetShortNetname().StartsWith( wxS( "unconnected-(" ) ) )
840 {
841 return;
842 }
843
844 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_SHORTING_ITEMS );
845 wxString msg;
846
847 msg.Printf( _( "(nets %s and %s)" ),
848 pad->GetNetname(),
849 otherPad->GetNetname() );
850
851 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
852 drce->SetItems( pad, otherPad );
853
854 reportViolation( drce, otherPad->GetPosition(), aLayer );
855 }
856
857 return;
858 }
859
860 if( testClearance || testShorting )
861 {
862 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, pad, other, aLayer );
863 clearance = constraint.GetValue().Min();
864
865 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
866 {
867 if( padShape->Collide( otherShape.get(), std::max( 0, clearance - m_drcEpsilon ),
868 &actual, &pos ) )
869 {
870 if( m_drcEngine->IsNetTieExclusion( pad->GetNetCode(), aLayer, pos, other ) )
871 {
872 // Pads connected to pads of a net-tie footprint are allowed to collide
873 // with the net-tie footprint's graphics.
874 }
875 else if( actual == 0 && otherNet && testShorting )
876 {
877 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_SHORTING_ITEMS );
878 wxString msg;
879
880 msg.Printf( _( "(nets %s and %s)" ),
881 pad->GetNetname(),
882 static_cast<BOARD_CONNECTED_ITEM*>( other )->GetNetname() );
883
884 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
885 drce->SetItems( pad, other );
886
887 reportViolation( drce, pos, aLayer );
888 testHoles = false; // No need for multiple violations
889 }
890 else if( testClearance )
891 {
892 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
893 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
894 constraint.GetName(),
895 clearance,
896 actual );
897
898 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
899 drce->SetItems( pad, other );
900 drce->SetViolatingRule( constraint.GetParentRule() );
901 ReportAndShowPathCuToCu( drce, pos, aLayer, pad, other, aLayer, actual );
902 testHoles = false; // No need for multiple violations
903 }
904 }
905 }
906 }
907
908 if( testHoles )
909 {
910 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, pad, other, aLayer );
911 clearance = constraint.GetValue().Min();
912
913 if( constraint.GetSeverity() == RPT_SEVERITY_IGNORE )
914 testHoles = false;
915 }
916
917 if( testHoles && otherPad && pad->FlashLayer( aLayer ) && otherPad->HasHole() )
918 {
919 if( clearance > 0 && padShape->Collide( otherPad->GetEffectiveHoleShape().get(),
920 std::max( 0, clearance - m_drcEpsilon ),
921 &actual, &pos ) )
922 {
923 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
924 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
925 constraint.GetName(),
926 clearance,
927 actual );
928
929 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
930 drce->SetItems( pad, other );
931 drce->SetViolatingRule( constraint.GetParentRule() );
932 ReportAndShowPathCuToCu( drce, pos, aLayer, pad, other, aLayer, actual );
933 testHoles = false; // No need for multiple violations
934 }
935 }
936
937 if( testHoles && otherPad && otherPad->FlashLayer( aLayer ) && pad->HasHole() )
938 {
939 if( clearance > 0 && otherShape->Collide( pad->GetEffectiveHoleShape().get(),
940 std::max( 0, clearance - m_drcEpsilon ),
941 &actual, &pos ) )
942 {
943 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
944 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
945 constraint.GetName(),
946 clearance,
947 actual );
948
949 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
950 drce->SetItems( pad, other );
951 drce->SetViolatingRule( constraint.GetParentRule() );
952
953 reportViolation( drce, pos, aLayer );
954 testHoles = false; // No need for multiple violations
955 }
956 }
957
958 if( testHoles && otherVia && otherVia->IsOnLayer( aLayer ) )
959 {
960 if( clearance > 0 && padShape->Collide( otherVia->GetEffectiveHoleShape().get(),
961 std::max( 0, clearance - m_drcEpsilon ),
962 &actual, &pos ) )
963 {
964 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
965 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
966 constraint.GetName(),
967 clearance,
968 actual );
969
970 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
971 drce->SetItems( pad, otherVia );
972 drce->SetViolatingRule( constraint.GetParentRule() );
973 ReportAndShowPathCuToCu( drce, pos, aLayer, pad, otherVia, aLayer, actual );
974 }
975 }
976}
977
978
980{
982 size_t count = 0;
983 std::atomic<size_t> done( 1 );
984
985 for( FOOTPRINT* footprint : m_board->Footprints() )
986 count += footprint->Pads().size();
987
988 reportAux( wxT( "Testing %d pads..." ), count );
989
990 std::unordered_map<PTR_PTR_CACHE_KEY, int> checkedPairs;
991
992 LSET boardCopperLayers = LSET::AllCuMask( m_board->GetCopperLayerCount() );
993
994 std::future<void> retn = tp.submit(
995 [&]()
996 {
997 for( FOOTPRINT* footprint : m_board->Footprints() )
998 {
999 for( PAD* pad : footprint->Pads() )
1000 {
1001 for( PCB_LAYER_ID layer : LSET( pad->GetLayerSet() & boardCopperLayers ).Seq() )
1002 {
1003 if( m_drcEngine->IsCancelled() )
1004 return;
1005
1006 std::shared_ptr<SHAPE> padShape = pad->GetEffectiveShape( layer );
1007
1008 m_board->m_CopperItemRTreeCache->QueryColliding( pad, layer, layer,
1009 // Filter:
1010 [&]( BOARD_ITEM* other ) -> bool
1011 {
1012 BOARD_ITEM* a = pad;
1013 BOARD_ITEM* b = other;
1014
1015 // store canonical order so we don't collide in both
1016 // directions (a:b and b:a)
1017 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
1018 std::swap( a, b );
1019
1020 if( checkedPairs.find( { a, b } ) != checkedPairs.end() )
1021 {
1022 return false;
1023 }
1024 else
1025 {
1026 checkedPairs[ { a, b } ] = 1;
1027 return true;
1028 }
1029 },
1030 // Visitor
1031 [&]( BOARD_ITEM* other ) -> bool
1032 {
1033 testPadAgainstItem( pad, padShape.get(), layer, other );
1034
1035 return !m_drcEngine->IsCancelled();
1036 },
1037 m_board->m_DRCMaxClearance );
1038
1039 for( ZONE* zone : m_board->m_DRCCopperZones )
1040 {
1041 testItemAgainstZone( pad, zone, layer );
1042
1043 if( m_drcEngine->IsCancelled() )
1044 return;
1045 }
1046 }
1047
1048 done.fetch_add( 1 );
1049 }
1050 }
1051 } );
1052
1053 std::future_status status = retn.wait_for( std::chrono::milliseconds( 250 ) );
1054
1055 while( status != std::future_status::ready )
1056 {
1057 reportProgress( done, count );
1058 status = retn.wait_for( std::chrono::milliseconds( 250 ) );
1059 }
1060}
1061
1062
1064{
1066 size_t count = m_board->Drawings().size();
1067 std::atomic<size_t> done( 1 );
1068
1069 for( FOOTPRINT* footprint : m_board->Footprints() )
1070 count += footprint->GraphicalItems().size();
1071
1072 reportAux( wxT( "Testing %d graphics..." ), count );
1073
1074 auto isKnockoutText =
1075 []( BOARD_ITEM* item )
1076 {
1077 return item->Type() == PCB_TEXT_T && static_cast<PCB_TEXT*>( item )->IsKnockout();
1078 };
1079
1080 auto testGraphicAgainstZone =
1081 [&]( BOARD_ITEM* item )
1082 {
1083 if( item->Type() == PCB_REFERENCE_IMAGE_T )
1084 return;
1085
1086 if( !IsCopperLayer( item->GetLayer() ) )
1087 return;
1088
1089 // Knockout text is most often knocked-out of a zone, so it's presumed to
1090 // collide with one. However, if it collides with more than one, and they
1091 // have different nets, then we have a short.
1092 NETINFO_ITEM* inheritedNet = nullptr;
1093
1094 for( ZONE* zone : m_board->m_DRCCopperZones )
1095 {
1096 if( isKnockoutText( item ) )
1097 testKnockoutTextAgainstZone( item, &inheritedNet, zone );
1098 else
1099 testItemAgainstZone( item, zone, item->GetLayer() );
1100
1101 if( m_drcEngine->IsCancelled() )
1102 return;
1103 }
1104 };
1105
1106 std::unordered_map<PTR_PTR_CACHE_KEY, layers_checked> checkedPairs;
1107
1108 auto testCopperGraphic =
1109 [&]( PCB_SHAPE* aShape )
1110 {
1111 PCB_LAYER_ID layer = aShape->GetLayer();
1112
1113 m_board->m_CopperItemRTreeCache->QueryColliding( aShape, layer, layer,
1114 // Filter:
1115 [&]( BOARD_ITEM* other ) -> bool
1116 {
1117 auto otherCItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( other );
1118
1119 if( otherCItem && otherCItem->GetNetCode() == aShape->GetNetCode() )
1120 return false;
1121
1122 // Pads and tracks handled separately
1123 if( other->Type() == PCB_PAD_T || other->Type() == PCB_ARC_T ||
1124 other->Type() == PCB_TRACE_T || other->Type() == PCB_VIA_T )
1125 {
1126 return false;
1127 }
1128
1129 BOARD_ITEM* a = aShape;
1130 BOARD_ITEM* b = other;
1131
1132 // store canonical order so we don't collide in both directions
1133 // (a:b and b:a)
1134 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
1135 std::swap( a, b );
1136
1137 auto it = checkedPairs.find( { a, b } );
1138
1139 if( it != checkedPairs.end() && it->second.layers.test( layer ) )
1140 {
1141 return false;
1142 }
1143 else
1144 {
1145 checkedPairs[ { a, b } ].layers.set( layer );
1146 return true;
1147 }
1148 },
1149 // Visitor:
1150 [&]( BOARD_ITEM* other ) -> bool
1151 {
1152 BOARD_ITEM* a = aShape;
1153 BOARD_ITEM* b = other;
1154
1155 // store canonical order so we don't collide in both directions
1156 // (a:b and b:a)
1157 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
1158 std::swap( a, b );
1159
1160 auto it = checkedPairs.find( { a, b } );
1161
1162 if( !testSingleLayerItemAgainstItem( aShape,
1163 aShape->GetEffectiveShape().get(),
1164 layer, other ) )
1165 {
1166 if( it != checkedPairs.end() )
1167 it->second.has_error = true;
1168 }
1169
1170 return !m_drcEngine->IsCancelled();
1171 },
1173 };
1174
1175 std::future<void> retn = tp.submit(
1176 [&]()
1177 {
1178 for( BOARD_ITEM* item : m_board->Drawings() )
1179 {
1180 testGraphicAgainstZone( item );
1181
1182 if( item->Type() == PCB_SHAPE_T && item->IsOnCopperLayer() )
1183 testCopperGraphic( static_cast<PCB_SHAPE*>( item ) );
1184
1185 done.fetch_add( 1 );
1186
1187 if( m_drcEngine->IsCancelled() )
1188 break;
1189 }
1190
1191 for( FOOTPRINT* footprint : m_board->Footprints() )
1192 {
1193 for( BOARD_ITEM* item : footprint->GraphicalItems() )
1194 {
1195 testGraphicAgainstZone( item );
1196
1197 done.fetch_add( 1 );
1198
1199 if( m_drcEngine->IsCancelled() )
1200 break;
1201 }
1202 }
1203 } );
1204
1205 std::future_status status = retn.wait_for( std::chrono::milliseconds( 250 ) );
1206
1207 while( status != std::future_status::ready )
1208 {
1209 reportProgress( done, count );
1210 status = retn.wait_for( std::chrono::milliseconds( 250 ) );
1211 }
1212}
1213
1214
1216{
1217 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
1218 bool testIntersects = !m_drcEngine->IsErrorLimitExceeded( DRCE_ZONES_INTERSECT );
1219 DRC_CONSTRAINT constraint;
1220
1221 std::vector<std::map<PCB_LAYER_ID, std::vector<SEG>>> poly_segments;
1222 poly_segments.resize( m_board->m_DRCCopperZones.size() );
1223
1224 // Contains the index for zoneA, zoneB, the conflict point, the actual clearance, the
1225 // required clearance, and the layer
1226 using report_data = std::tuple<int, int, VECTOR2I, int, int, PCB_LAYER_ID>;
1227
1228 std::vector<std::future<report_data>> futures;
1230 std::atomic<size_t> done( 1 );
1231
1232 auto checkZones =
1233 [this, testClearance, testIntersects, &poly_segments, &done]
1234 ( int zoneA, int zoneB, int clearance, PCB_LAYER_ID layer ) -> report_data
1235 {
1236 // Iterate through all the segments of refSmoothedPoly
1237 std::map<VECTOR2I, int> conflictPoints;
1238
1239 std::vector<SEG>& refSegments = poly_segments[zoneA][layer];
1240 std::vector<SEG>& testSegments = poly_segments[zoneB][layer];
1241 bool reported = false;
1242 auto invalid_result = std::make_tuple( -1, -1, VECTOR2I(), 0, 0, F_Cu );
1243
1244 for( SEG& refSegment : refSegments )
1245 {
1246 int ax1 = refSegment.A.x;
1247 int ay1 = refSegment.A.y;
1248 int ax2 = refSegment.B.x;
1249 int ay2 = refSegment.B.y;
1250
1251 // Iterate through all the segments in smoothed_polys[ia2]
1252 for( SEG& testSegment : testSegments )
1253 {
1254 // Build test segment
1255 VECTOR2I pt;
1256
1257 int bx1 = testSegment.A.x;
1258 int by1 = testSegment.A.y;
1259 int bx2 = testSegment.B.x;
1260 int by2 = testSegment.B.y;
1261
1262 // We have ensured that the 'A' segment starts before the 'B' segment,
1263 // so if the 'A' segment ends before the 'B' segment starts, we can skip
1264 // to the next 'A'
1265 if( ax2 < bx1 )
1266 break;
1267
1268 int d = GetClearanceBetweenSegments( bx1, by1, bx2, by2, 0,
1269 ax1, ay1, ax2, ay2, 0,
1270 clearance, &pt.x, &pt.y );
1271
1272 if( d < clearance )
1273 {
1274 if( d == 0 && testIntersects )
1275 reported = true;
1276 else if( testClearance )
1277 reported = true;
1278
1279 if( reported )
1280 {
1281 done.fetch_add( 1 );
1282 return std::make_tuple( zoneA, zoneB, pt, d, clearance, layer );
1283 }
1284 }
1285
1286 if( m_drcEngine->IsCancelled() )
1287 return invalid_result;
1288 }
1289 }
1290
1291 done.fetch_add( 1 );
1292 return invalid_result;
1293 };
1294
1295
1297 {
1298 int zone2zoneClearance;
1299
1300 // Skip over layers not used on the current board
1301 if( !m_board->IsLayerEnabled( layer ) )
1302 continue;
1303
1304 for( size_t ii = 0; ii < m_board->m_DRCCopperZones.size(); ii++ )
1305 {
1306 if( m_board->m_DRCCopperZones[ii]->IsOnLayer( layer ) )
1307 {
1308 SHAPE_POLY_SET poly = *m_board->m_DRCCopperZones[ii]->GetFilledPolysList( layer );
1309 std::vector<SEG>& zone_layer_poly_segs = poly_segments[ii][layer];
1310
1311 poly.BuildBBoxCaches();
1312 zone_layer_poly_segs.reserve( poly.FullPointCount() );
1313
1314 for( auto it = poly.IterateSegmentsWithHoles(); it; it++ )
1315 {
1316 SEG seg = *it;
1317
1318 if( seg.A.x > seg.B.x )
1319 seg.Reverse();
1320
1321 zone_layer_poly_segs.push_back( seg );
1322 }
1323
1324 std::sort( zone_layer_poly_segs.begin(), zone_layer_poly_segs.end() );
1325 }
1326 }
1327
1328 std::vector<std::pair<int, int>> zonePairs;
1329
1330 for( size_t ia = 0; ia < m_board->m_DRCCopperZones.size(); ia++ )
1331 {
1332 ZONE* zoneA = m_board->m_DRCCopperZones[ia];
1333
1334 if( !zoneA->IsOnLayer( layer ) )
1335 continue;
1336
1337 for( size_t ia2 = ia + 1; ia2 < m_board->m_DRCCopperZones.size(); ia2++ )
1338 {
1339 ZONE* zoneB = m_board->m_DRCCopperZones[ia2];
1340
1341 // test for same layer
1342 if( !zoneB->IsOnLayer( layer ) )
1343 continue;
1344
1345 // Test for same net
1346 if( zoneA->GetNetCode() == zoneB->GetNetCode() && zoneA->GetNetCode() >= 0 )
1347 continue;
1348
1349 // rule areas may overlap at will
1350 if( zoneA->GetIsRuleArea() || zoneB->GetIsRuleArea() )
1351 continue;
1352
1353 // Examine a candidate zone: compare zoneB to zoneA
1354 SHAPE_POLY_SET* polyA = m_board->m_DRCCopperZones[ia]->GetFill( layer );
1355 SHAPE_POLY_SET* polyB = m_board->m_DRCCopperZones[ia2]->GetFill( layer );
1356
1357 if( !polyA->BBoxFromCaches().Intersects( polyB->BBoxFromCaches() ) )
1358 continue;
1359
1360 // Get clearance used in zone to zone test.
1361 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, zoneA, zoneB, layer );
1362 zone2zoneClearance = constraint.GetValue().Min();
1363
1364 if( constraint.GetSeverity() == RPT_SEVERITY_IGNORE || zone2zoneClearance <= 0 )
1365 continue;
1366
1367 futures.push_back( tp.submit( checkZones, ia, ia2, zone2zoneClearance, layer ) );
1368 }
1369 }
1370 }
1371
1372 size_t count = futures.size();
1373
1374 for( auto& task : futures )
1375 {
1376 if( !task.valid() )
1377 continue;
1378
1379 std::future_status result;
1380
1381 while( true )
1382 {
1383 result = task.wait_for( std::chrono::milliseconds( 250 ) );
1384
1385 reportProgress( done, count );
1386
1387 if( m_drcEngine->IsCancelled() )
1388 break;
1389
1390 if( result == std::future_status::ready )
1391 {
1392 report_data data = task.get();
1393 int zoneA_idx = std::get<0>( data );
1394 int zoneB_idx = std::get<1>( data );
1395 VECTOR2I pt = std::get<2>( data );
1396 int actual = std::get<3>( data );
1397 int required = std::get<4>( data );
1398 PCB_LAYER_ID layer = std::get<5>( data );
1399
1400 if( zoneA_idx >= 0 )
1401 {
1402 ZONE* zoneA = m_board->m_DRCCopperZones[zoneA_idx];
1403 ZONE* zoneB = m_board->m_DRCCopperZones[zoneB_idx];
1404
1405 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, zoneA, zoneB, layer );
1406 std::shared_ptr<DRC_ITEM> drce;
1407
1408 if( actual <= 0 && testIntersects )
1409 {
1411 }
1412 else if( testClearance )
1413 {
1415 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
1416 constraint.GetName(),
1417 required,
1418 std::max( actual, 0 ) );
1419
1420 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
1421 }
1422
1423 if( drce )
1424 {
1425 drce->SetItems( zoneA, zoneB );
1426 drce->SetViolatingRule( constraint.GetParentRule() );
1427 ReportAndShowPathCuToCu( drce, pt, layer, zoneA, zoneB, layer, actual );
1428 }
1429 }
1430
1431 break;
1432 }
1433 }
1434 }
1435}
1436
1437
1438namespace detail
1439{
1441}
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
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:237
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:257
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:215
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:1305
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:804
int GetCopperLayerCount() const
Definition: board.cpp:741
const FOOTPRINTS & Footprints() const
Definition: board.h:331
const TRACKS & Tracks() const
Definition: board.h:329
std::shared_ptr< DRC_RTREE > m_CopperItemRTreeCache
Definition: board.h:1299
int m_DRCMaxClearance
Definition: board.h:1306
std::unordered_map< ZONE *, std::unique_ptr< DRC_RTREE > > m_CopperZoneRTreeCache
Definition: board.h:1298
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:895
const DRAWINGS & Drawings() const
Definition: board.h:333
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:99
bool GetReportAllTrackErrors() const
Definition: drc_engine.h:190
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:696
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:382
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_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:489
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:101
std::vector< PAD * > GetNetTiePads(PAD *aPad) const
Definition: footprint.cpp:3139
std::map< wxString, int > MapPadNumbersToNetTieGroups() const
Definition: footprint.cpp:3084
std::deque< PAD * > & Pads()
Definition: footprint.h:206
bool IsNetTie() const
Definition: footprint.h:297
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:36
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:686
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:420
bool Contains(PCB_LAYER_ID aLayer) const
See if the layer set contains a PCB layer.
Definition: lset.h:62
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:340
const VECTOR2I & GetDrillSize() const
Definition: pad.h:307
PAD_ATTRIB GetAttribute() const
Definition: pad.h:442
const wxString & GetNumber() const
Definition: pad.h:134
VECTOR2I GetPosition() const override
Definition: pad.h:210
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:545
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:1920
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:567
virtual PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: zone.cpp:443
virtual bool IsOnLayer(PCB_LAYER_ID) const override
Test to see if this object is on the given layer.
Definition: zone.cpp:561
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)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:531
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)
static thread_pool * tp
Definition: thread_pool.cpp:30
BS::thread_pool thread_pool
Definition: thread_pool.h:30
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
Definition: thread_pool.cpp:32
@ 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:691