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