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