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:
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{
107 m_board = m_drcEngine->GetBoard();
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
115 m_drcEpsilon = m_board->GetDesignSettings().GetDRCEpsilon();
116
117 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE ) )
118 {
119 if( !reportPhase( _( "Checking track & via clearances..." ) ) )
120 return false; // DRC cancelled
121
123 }
124 else if( !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE ) )
125 {
126 if( !reportPhase( _( "Checking hole clearances..." ) ) )
127 return false; // DRC cancelled
128
130 }
131
132 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE ) )
133 {
134 if( !reportPhase( _( "Checking pad clearances..." ) ) )
135 return false; // DRC cancelled
136
138 }
139 else if( !m_drcEngine->IsErrorLimitExceeded( DRCE_SHORTING_ITEMS )
140 || !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE ) )
141 {
142 if( !reportPhase( _( "Checking pads..." ) ) )
143 return false; // DRC cancelled
144
146 }
147
148 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE ) )
149 {
150 if( !reportPhase( _( "Checking copper graphic clearances..." ) ) )
151 return false; // DRC cancelled
152
154 }
155
156 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE ) )
157 {
158 if( !reportPhase( _( "Checking copper zone clearances..." ) ) )
159 return false; // DRC cancelled
160
162 }
163 else if( !m_drcEngine->IsErrorLimitExceeded( DRCE_ZONES_INTERSECT ) )
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 );
181 bool testShorting = !m_drcEngine->IsErrorLimitExceeded( DRCE_SHORTING_ITEMS );
182 bool testHoles = !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_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
270 if( !m_drcEngine->GetReportAllTrackErrors() )
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
288 if( !m_drcEngine->GetReportAllTrackErrors() )
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 );
409 bool testHoles = !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_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 );
512 bool testShorts = !m_drcEngine->IsErrorLimitExceeded( DRCE_SHORTING_ITEMS );
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 =
598 [&]( const int trackIdx )
599 {
600 PCB_TRACK* track = m_board->Tracks()[trackIdx];
601
602 for( PCB_LAYER_ID layer : LSET( track->GetLayerSet() & boardCopperLayers ) )
603 {
604 std::shared_ptr<SHAPE> trackShape = track->GetEffectiveShape( layer );
605
606 m_board->m_CopperItemRTreeCache->QueryColliding( track, layer, layer,
607 // Filter:
608 [&]( BOARD_ITEM* other ) -> bool
609 {
610 BOARD_CONNECTED_ITEM* otherCItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( other );
611
612 if( otherCItem && otherCItem->GetNetCode() == track->GetNetCode() )
613 return false;
614
615 BOARD_ITEM* a = track;
616 BOARD_ITEM* b = other;
617
618 // store canonical order so we don't collide in both directions
619 // (a:b and b:a)
620 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
621 std::swap( a, b );
622
623 std::lock_guard<std::mutex> lock( checkedPairsMutex );
624 auto it = checkedPairs.find( { a, b } );
625
626 if( it != checkedPairs.end()
627 && ( it->second.layers.test( layer ) || ( it->second.has_error ) ) )
628 {
629 return false;
630 }
631 else
632 {
633 checkedPairs[ { a, b } ].layers.set( layer );
634 return true;
635 }
636 },
637 // Visitor:
638 [&]( BOARD_ITEM* other ) -> bool
639 {
640 if( m_drcEngine->IsCancelled() )
641 return false;
642
643 if( other->Type() == PCB_PAD_T && static_cast<PAD*>( other )->IsFreePad() )
644 {
645 if( other->GetEffectiveShape( layer )->Collide( trackShape.get() ) )
646 {
647 std::lock_guard<std::mutex> lock( freePadsUsageMapMutex );
648 auto it = freePadsUsageMap.find( other );
649
650 if( it == freePadsUsageMap.end() )
651 {
652 freePadsUsageMap[ other ] = track->GetNetCode();
653 return true; // Continue colliding tests
654 }
655 else if( it->second == track->GetNetCode() )
656 {
657 return true; // Continue colliding tests
658 }
659 }
660 }
661
662 // If we get an error, mark the pair as having a clearance error already
663 if( !testSingleLayerItemAgainstItem( track, trackShape.get(), layer, other ) )
664 {
665 if( !m_drcEngine->GetReportAllTrackErrors() )
666 {
667 BOARD_ITEM* a = track;
668 BOARD_ITEM* b = other;
669
670 // store canonical order so we don't collide in both directions
671 // (a:b and b:a)
672 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
673 std::swap( a, b );
674
675 std::lock_guard<std::mutex> lock( checkedPairsMutex );
676 auto it = checkedPairs.find( { a, b } );
677
678 if( it != checkedPairs.end() )
679 it->second.has_error = true;
680
681 return false; // We're done with this track
682 }
683 }
684
685 return !m_drcEngine->IsCancelled();
686 },
687 m_board->m_DRCMaxClearance );
688
689 for( ZONE* zone : m_board->m_DRCCopperZones )
690 {
691 testItemAgainstZone( track, zone, layer );
692
693 if( m_drcEngine->IsCancelled() )
694 break;
695 }
696 }
697
698 done.fetch_add( 1 );
699 };
700
702
703 auto track_futures = tp.submit_loop( 0, m_board->Tracks().size(), testTrack );
704
705 while( done < count )
706 {
707 reportProgress( done, count );
708
709 if( m_drcEngine->IsCancelled() )
710 {
711 // Wait for the submitted loop tasks to finish
712 track_futures.wait();
713 break;
714 }
715
716 std::this_thread::sleep_for( std::chrono::milliseconds( 250 ) );
717 }
718}
719
720
722 PCB_LAYER_ID aLayer,
723 BOARD_ITEM* other )
724{
725 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
726 bool testShorting = !m_drcEngine->IsErrorLimitExceeded( DRCE_SHORTING_ITEMS );
727 bool testHoles = !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE );
728
729 // Disable some tests for net-tie objects in a footprint
730 if( other->GetParent() == pad->GetParent() )
731 {
732 FOOTPRINT* fp = pad->GetParentFootprint();
733 std::map<wxString, int> padToNetTieGroupMap = fp->MapPadNumbersToNetTieGroups();
734 int padGroupIdx = padToNetTieGroupMap[ pad->GetNumber() ];
735
736 if( other->Type() == PCB_PAD_T )
737 {
738 PAD* otherPad = static_cast<PAD*>( other );
739
740 if( padGroupIdx >= 0 && padGroupIdx == padToNetTieGroupMap[ otherPad->GetNumber() ] )
741 testClearance = testShorting = false;
742
743 if( pad->SameLogicalPadAs( otherPad ) )
744 testHoles = false;
745 }
746
747 if( other->Type() == PCB_SHAPE_T && padGroupIdx >= 0 )
748 testClearance = testShorting = false;
749 }
750
751 BOARD_CONNECTED_ITEM* otherCItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( other );
752 PAD* otherPad = nullptr;
753 PCB_VIA* otherVia = nullptr;
754
755 if( other->Type() == PCB_PAD_T )
756 otherPad = static_cast<PAD*>( other );
757
758 if( other->Type() == PCB_VIA_T )
759 otherVia = static_cast<PCB_VIA*>( other );
760
761 if( !IsCopperLayer( aLayer ) )
762 testClearance = testShorting = false;
763
764 // A NPTH has no cylinder, but it may still have pads on some layers
765 if( pad->GetAttribute() == PAD_ATTRIB::NPTH && !pad->FlashLayer( aLayer ) )
766 testClearance = testShorting = false;
767
768 if( otherPad && otherPad->GetAttribute() == PAD_ATTRIB::NPTH && !otherPad->FlashLayer( aLayer ) )
769 testClearance = testShorting = false;
770
771 // Track clearances are tested in testTrackClearances()
772 if( dynamic_cast<PCB_TRACK*>( other) )
773 testClearance = testShorting = false;
774
775 int padNet = pad->GetNetCode();
776 int otherNet = otherCItem ? otherCItem->GetNetCode() : 0;
777
778 // Other objects of the same (defined) net get a waiver on clearance and hole tests
779 if( otherNet && otherNet == padNet )
780 {
781 testClearance = testShorting = false;
782 testHoles = false;
783 }
784
785 if( !( pad->GetDrillSize().x > 0 )
786 && !( otherPad && otherPad->GetDrillSize().x > 0 )
787 && !( otherVia && otherVia->GetDrill() > 0 ) )
788 {
789 testHoles = false;
790 }
791
792 if( !testClearance && !testShorting && !testHoles )
793 return true;
794
795 std::shared_ptr<SHAPE> otherShape = other->GetEffectiveShape( aLayer );
796 DRC_CONSTRAINT constraint;
797 int clearance = 0;
798 int actual = 0;
799 VECTOR2I pos;
800 bool has_error = false;
801
802 if( otherPad && pad->SameLogicalPadAs( otherPad ) )
803 {
804 // If pads are equivalent (ie: from the same footprint with the same pad number)...
805 // ... and have "real" nets...
806 // then they must be the same net
807 if( testShorting )
808 {
809 if( pad->GetNetCode() == 0 || pad->GetNetCode() == otherPad->GetNetCode() )
810 return true;
811
812 if( pad->GetShortNetname().StartsWith( wxS( "unconnected-(" ) )
813 && otherPad->GetShortNetname().StartsWith( wxS( "unconnected-(" ) ) )
814 {
815 return true;
816 }
817
818 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_SHORTING_ITEMS );
819 wxString msg;
820
821 msg.Printf( _( "(nets %s and %s)" ),
822 pad->GetNetname(),
823 otherPad->GetNetname() );
824
825 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
826 drce->SetItems( pad, otherPad );
827
828 reportViolation( drce, otherPad->GetPosition(), aLayer );
829 has_error = true;
830 }
831
832 return !has_error;
833 }
834
835 if( testClearance || testShorting )
836 {
837 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, pad, other, aLayer );
838 clearance = constraint.GetValue().Min();
839
840 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
841 {
842 if( padShape->Collide( otherShape.get(), std::max( 0, clearance - m_drcEpsilon ),
843 &actual, &pos ) )
844 {
845 if( m_drcEngine->IsNetTieExclusion( pad->GetNetCode(), aLayer, pos, other ) )
846 {
847 // Pads connected to pads of a net-tie footprint are allowed to collide
848 // with the net-tie footprint's graphics.
849 }
850 else if( actual == 0 && padNet && otherNet && testShorting )
851 {
852 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_SHORTING_ITEMS );
853 wxString msg = wxString::Format( _( "(nets %s and %s)" ),
854 pad->GetNetname(),
855 otherCItem->GetNetname() );
856
857 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
858 drce->SetItems( pad, other );
859
860 reportViolation( drce, pos, aLayer );
861 has_error = true;
862 testHoles = false; // No need for multiple violations
863 }
864 else if( testClearance )
865 {
866 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
867 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
868 constraint.GetName(),
869 clearance,
870 actual );
871
872 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
873 drce->SetItems( pad, other );
874 drce->SetViolatingRule( constraint.GetParentRule() );
875 ReportAndShowPathCuToCu( drce, pos, aLayer, pad, other, aLayer, actual );
876 has_error = true;
877 testHoles = false; // No need for multiple violations
878 }
879 }
880 }
881 }
882
883 if( testHoles )
884 {
885 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, pad, other, aLayer );
886 clearance = constraint.GetValue().Min();
887
888 if( constraint.GetSeverity() == RPT_SEVERITY_IGNORE )
889 testHoles = false;
890 }
891
892 if( testHoles && otherPad && pad->FlashLayer( aLayer ) && otherPad->HasHole() )
893 {
894 if( clearance > 0 && padShape->Collide( otherPad->GetEffectiveHoleShape().get(),
895 std::max( 0, clearance - m_drcEpsilon ),
896 &actual, &pos ) )
897 {
898 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
899 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
900 constraint.GetName(),
901 clearance,
902 actual );
903
904 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
905 drce->SetItems( pad, other );
906 drce->SetViolatingRule( constraint.GetParentRule() );
907 ReportAndShowPathCuToCu( drce, pos, aLayer, pad, other, aLayer, actual );
908 has_error = true;
909 testHoles = false; // No need for multiple violations
910 }
911 }
912
913 if( testHoles && otherPad && otherPad->FlashLayer( aLayer ) && pad->HasHole() )
914 {
915 if( clearance > 0 && otherShape->Collide( pad->GetEffectiveHoleShape().get(),
916 std::max( 0, clearance - m_drcEpsilon ),
917 &actual, &pos ) )
918 {
919 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
920 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
921 constraint.GetName(),
922 clearance,
923 actual );
924
925 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
926 drce->SetItems( pad, other );
927 drce->SetViolatingRule( constraint.GetParentRule() );
928
929 reportViolation( drce, pos, aLayer );
930 has_error = true;
931 testHoles = false; // No need for multiple violations
932 }
933 }
934
935 if( testHoles && otherVia && otherVia->IsOnLayer( aLayer ) )
936 {
937 if( clearance > 0 && padShape->Collide( otherVia->GetEffectiveHoleShape().get(),
938 std::max( 0, clearance - m_drcEpsilon ),
939 &actual, &pos ) )
940 {
941 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
942 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
943 constraint.GetName(),
944 clearance,
945 actual );
946
947 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
948 drce->SetItems( pad, otherVia );
949 drce->SetViolatingRule( constraint.GetParentRule() );
950 ReportAndShowPathCuToCu( drce, pos, aLayer, pad, otherVia, aLayer, actual );
951 has_error = true;
952 }
953 }
954
955 return !has_error;
956}
957
958
960{
962 std::atomic<size_t> done( 1 );
963
964 std::unordered_map<PTR_PTR_CACHE_KEY, LAYERS_CHECKED> checkedPairs;
965 std::mutex checkedPairsMutex;
966
967 LSET boardCopperLayers = LSET::AllCuMask( m_board->GetCopperLayerCount() );
968
969 const auto fp_check =
970 [&]( size_t ii )
971 {
972 FOOTPRINT* footprint = m_board->Footprints()[ ii ];
973
974 for( PAD* pad : footprint->Pads() )
975 {
976 for( PCB_LAYER_ID layer : LSET( pad->GetLayerSet() & boardCopperLayers ) )
977 {
978 if( m_drcEngine->IsCancelled() )
979 return;
980
981 std::shared_ptr<SHAPE> padShape = pad->GetEffectiveShape( layer );
982
983 m_board->m_CopperItemRTreeCache->QueryColliding( pad, layer, layer,
984 // Filter:
985 [&]( BOARD_ITEM* other ) -> bool
986 {
987 BOARD_ITEM* a = pad;
988 BOARD_ITEM* b = other;
989
990 // store canonical order so we don't collide in both
991 // directions (a:b and b:a)
992 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
993 std::swap( a, b );
994
995 std::lock_guard<std::mutex> lock( checkedPairsMutex );
996 auto it = checkedPairs.find( { a, b } );
997
998 if( it != checkedPairs.end()
999 && ( it->second.layers.test( layer ) || it->second.has_error ) )
1000 {
1001 return false;
1002 }
1003 else
1004 {
1005 checkedPairs[ { a, b } ].layers.set( layer );
1006 return true;
1007 }
1008 },
1009 // Visitor
1010 [&]( BOARD_ITEM* other ) -> bool
1011 {
1012 if( !testPadAgainstItem( pad, padShape.get(), layer, other ) )
1013 {
1014 BOARD_ITEM* a = pad;
1015 BOARD_ITEM* b = other;
1016
1017 std::lock_guard<std::mutex> lock( checkedPairsMutex );
1018 auto it = checkedPairs.find( { a, b } );
1019
1020 if( it != checkedPairs.end() )
1021 it->second.has_error = true;
1022 }
1023
1024 return !m_drcEngine->IsCancelled();
1025 },
1026 m_board->m_DRCMaxClearance );
1027
1028 for( ZONE* zone : m_board->m_DRCCopperZones )
1029 {
1030 testItemAgainstZone( pad, zone, layer );
1031
1032 if( m_drcEngine->IsCancelled() )
1033 return;
1034 }
1035 }
1036 }
1037
1038 done.fetch_add( 1 );
1039 };
1040
1041 size_t numFootprints = m_board->Footprints().size();
1042 auto returns = tp.submit_loop( 0, numFootprints, fp_check );
1043
1044 // Wait for all threads to finish
1045 for( size_t ii = 0; ii < returns.size(); ++ii )
1046 {
1047 while( returns[ii].wait_for( std::chrono::milliseconds( 250 ) ) != std::future_status::ready )
1048 reportProgress( done, numFootprints );
1049 }
1050}
1051
1052
1054{
1056 size_t count = m_board->Drawings().size();
1057 std::atomic<size_t> done( 1 );
1058
1059 for( FOOTPRINT* footprint : m_board->Footprints() )
1060 count += footprint->GraphicalItems().size();
1061
1062 REPORT_AUX( wxString::Format( wxT( "Testing %d graphics..." ), count ) );
1063
1064 auto isKnockoutText =
1065 []( BOARD_ITEM* item )
1066 {
1067 return item->Type() == PCB_TEXT_T && static_cast<PCB_TEXT*>( item )->IsKnockout();
1068 };
1069
1070 auto testGraphicAgainstZone =
1071 [this, isKnockoutText]( BOARD_ITEM* item )
1072 {
1073 if( item->Type() == PCB_REFERENCE_IMAGE_T )
1074 return;
1075
1076 if( !IsCopperLayer( item->GetLayer() ) )
1077 return;
1078
1079 // Knockout text is most often knocked-out of a zone, so it's presumed to
1080 // collide with one. However, if it collides with more than one, and they
1081 // have different nets, then we have a short.
1082 NETINFO_ITEM* inheritedNet = nullptr;
1083
1084 for( ZONE* zone : m_board->m_DRCCopperZones )
1085 {
1086 if( isKnockoutText( item ) )
1087 testKnockoutTextAgainstZone( item, &inheritedNet, zone );
1088 else
1089 testItemAgainstZone( item, zone, item->GetLayer() );
1090
1091 if( m_drcEngine->IsCancelled() )
1092 return;
1093 }
1094 };
1095
1096 std::unordered_map<PTR_PTR_CACHE_KEY, LAYERS_CHECKED> checkedPairs;
1097 std::mutex checkedPairsMutex;
1098
1099 auto testCopperGraphic =
1100 [this, &checkedPairs, &checkedPairsMutex]( PCB_SHAPE* aShape )
1101 {
1102 PCB_LAYER_ID layer = aShape->GetLayer();
1103
1104 m_board->m_CopperItemRTreeCache->QueryColliding( aShape, layer, layer,
1105 // Filter:
1106 [&]( BOARD_ITEM* other ) -> bool
1107 {
1108 auto otherCItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( other );
1109
1110 if( otherCItem && otherCItem->GetNetCode() == aShape->GetNetCode() )
1111 return false;
1112
1113 // Pads and tracks handled separately
1114 if( other->Type() == PCB_PAD_T || other->Type() == PCB_ARC_T ||
1115 other->Type() == PCB_TRACE_T || other->Type() == PCB_VIA_T )
1116 {
1117 return false;
1118 }
1119
1120 BOARD_ITEM* a = aShape;
1121 BOARD_ITEM* b = other;
1122
1123 // store canonical order so we don't collide in both directions
1124 // (a:b and b:a)
1125 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
1126 std::swap( a, b );
1127
1128 std::lock_guard<std::mutex> lock( checkedPairsMutex );
1129 auto it = checkedPairs.find( { a, b } );
1130
1131 if( it != checkedPairs.end() && it->second.layers.test( layer ) )
1132 {
1133 return false;
1134 }
1135 else
1136 {
1137 checkedPairs[ { a, b } ].layers.set( layer );
1138 return true;
1139 }
1140 },
1141 // Visitor:
1142 [&]( BOARD_ITEM* other ) -> bool
1143 {
1144 testSingleLayerItemAgainstItem( aShape, aShape->GetEffectiveShape().get(),
1145 layer, other );
1146
1147 return !m_drcEngine->IsCancelled();
1148 },
1149 m_board->m_DRCMaxClearance );
1150 };
1151
1152 for( BOARD_ITEM* item : m_board->Drawings() )
1153 {
1154 (void)tp.submit_task(
1155 [this, item, &done, testGraphicAgainstZone, testCopperGraphic]()
1156 {
1157 if( !m_drcEngine->IsCancelled() )
1158 {
1159 testGraphicAgainstZone( item );
1160
1161 if( item->Type() == PCB_SHAPE_T && item->IsOnCopperLayer() )
1162 testCopperGraphic( static_cast<PCB_SHAPE*>( item ) );
1163
1164 done.fetch_add( 1 );
1165 }
1166 } );
1167 }
1168
1169 for( FOOTPRINT* footprint : m_board->Footprints() )
1170 {
1171 (void)tp.submit_task(
1172 [this, footprint, &done, testGraphicAgainstZone]()
1173 {
1174 for( BOARD_ITEM* item : footprint->GraphicalItems() )
1175 {
1176 if( !m_drcEngine->IsCancelled() )
1177 {
1178 testGraphicAgainstZone( item );
1179
1180 done.fetch_add( 1 );
1181 }
1182 }
1183 } );
1184 }
1185
1186 while( true )
1187 {
1188 reportProgress( done, count );
1189
1190 if( m_drcEngine->IsCancelled() )
1191 break;
1192
1193 if( tp.wait_for( std::chrono::milliseconds( 250 ) ) )
1194 break;
1195 }
1196}
1197
1198
1200{
1201 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
1202 bool testIntersects = !m_drcEngine->IsErrorLimitExceeded( DRCE_ZONES_INTERSECT );
1203
1204 std::vector<std::map<PCB_LAYER_ID, std::vector<SEG>>> poly_segments;
1205 poly_segments.resize( m_board->m_DRCCopperZones.size() );
1206
1208 std::atomic<size_t> done( 0 );
1209 size_t count = 0;
1210
1211 auto reportZoneZoneViolation =
1212 [this]( ZONE* zoneA, ZONE* zoneB, VECTOR2I& pt, int actual, const DRC_CONSTRAINT& constraint,
1213 PCB_LAYER_ID layer ) -> void
1214 {
1215 std::shared_ptr<DRC_ITEM> drce;
1216
1217 if( constraint.IsNull() )
1218 {
1220 wxString msg = _( "(intersecting zones must have distinct priorities)" );
1221 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
1222 drce->SetItems( zoneA, zoneB );
1223 reportViolation( drce, pt, layer );
1224 }
1225 else
1226 {
1228 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
1229 constraint.GetName(),
1230 constraint.GetValue().Min(),
1231 std::max( actual, 0 ) );
1232
1233 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
1234 drce->SetItems( zoneA, zoneB );
1235 drce->SetViolatingRule( constraint.GetParentRule() );
1236 ReportAndShowPathCuToCu( drce, pt, layer, zoneA, zoneB, layer, actual );
1237 }
1238 };
1239
1240 auto checkZones =
1241 [this, testClearance, testIntersects, reportZoneZoneViolation, &poly_segments, &done]
1242 ( int zoneA_idx, int zoneB_idx, bool sameNet, PCB_LAYER_ID layer ) -> void
1243 {
1244 ZONE* zoneA = m_board->m_DRCCopperZones[zoneA_idx];
1245 ZONE* zoneB = m_board->m_DRCCopperZones[zoneB_idx];
1246 int actual = 0;
1247 VECTOR2I pt;
1248
1249 if( sameNet && testIntersects )
1250 {
1251 if( zoneA->Outline()->Collide( zoneB->Outline(), 0, &actual, &pt ) )
1252 {
1253 done.fetch_add( 1 );
1254 reportZoneZoneViolation( zoneA, zoneB, pt, actual, DRC_CONSTRAINT(), layer );
1255 return;
1256 }
1257 }
1258 else if( !sameNet && testClearance )
1259 {
1260 DRC_CONSTRAINT constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, zoneA, zoneB, layer );
1261 int clearance = constraint.GetValue().Min();
1262
1263 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
1264 {
1265 std::map<VECTOR2I, int> conflictPoints;
1266
1267 std::vector<SEG>& refSegments = poly_segments[zoneA_idx][layer];
1268 std::vector<SEG>& testSegments = poly_segments[zoneB_idx][layer];
1269
1270 // Iterate through all the segments in zoneA
1271 for( SEG& refSegment : refSegments )
1272 {
1273 // Iterate through all the segments in zoneB
1274 for( SEG& testSegment : testSegments )
1275 {
1276 // We have ensured that the 'A' segment starts before the 'B' segment, so if the
1277 // 'A' segment ends before the 'B' segment starts, we can skip to the next 'A'
1278 if( refSegment.B.x < testSegment.A.x )
1279 break;
1280
1281 int64_t dist_sq = 0;
1282 VECTOR2I other_pt;
1283 refSegment.NearestPoints( testSegment, pt, other_pt, dist_sq );
1284 actual = std::floor( std::sqrt( dist_sq ) + 0.5 );
1285
1286 if( actual < clearance )
1287 {
1288 done.fetch_add( 1 );
1289 reportZoneZoneViolation( zoneA, zoneB, pt, actual, constraint, layer );
1290 return;
1291 }
1292 }
1293 }
1294 }
1295 }
1296
1297 done.fetch_add( 1 );
1298 };
1299
1300 // Pre-sort zones into layers
1301 std::map<PCB_LAYER_ID, std::vector<size_t>> zone_idx_by_layer;
1302
1303 for ( size_t ii = 0; ii < m_board->m_DRCCopperZones.size(); ii++ )
1304 {
1305 ZONE* zone = m_board->m_DRCCopperZones[ii];
1306
1307 for( PCB_LAYER_ID layer : zone->GetLayerSet() )
1308 {
1309 if( !IsCopperLayer( layer ) )
1310 continue;
1311
1312 zone_idx_by_layer[layer].push_back( ii );
1313 }
1314 }
1315
1316 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, m_board->GetCopperLayerCount() ) )
1317 {
1318 // Skip over layers not used on the current board
1319 if( !m_board->IsLayerEnabled( layer ) )
1320 continue;
1321
1322 for( size_t ii : zone_idx_by_layer[layer] )
1323 {
1324 if( SHAPE_POLY_SET* poly = m_board->m_DRCCopperZones[ii]->GetFill( layer ) )
1325 {
1326 std::vector<SEG>& zone_layer_poly_segs = poly_segments[ii][layer];
1327 zone_layer_poly_segs.reserve( poly->FullPointCount() );
1328
1329 for( auto it = poly->IterateSegmentsWithHoles(); it; it++ )
1330 {
1331 SEG seg = *it;
1332
1333 if( seg.A.x > seg.B.x )
1334 seg.Reverse();
1335
1336 zone_layer_poly_segs.push_back( seg );
1337 }
1338
1339 std::sort( zone_layer_poly_segs.begin(), zone_layer_poly_segs.end() );
1340 }
1341 }
1342
1343 for( auto it_a = zone_idx_by_layer[layer].begin(); it_a != zone_idx_by_layer[layer].end(); ++it_a )
1344 {
1345 size_t ia = *it_a;
1346 ZONE* zoneA = m_board->m_DRCCopperZones[ia];
1347
1348 for( auto it_a2 = std::next( it_a ); it_a2 != zone_idx_by_layer[layer].end(); ++it_a2 )
1349 {
1350 size_t ia2 = *it_a2;
1351 ZONE* zoneB = m_board->m_DRCCopperZones[ia2];
1352
1353 bool sameNet = zoneA->GetNetCode() == zoneB->GetNetCode() && zoneA->GetNetCode() >= 0;
1354
1355 if( sameNet && zoneA->GetAssignedPriority() != zoneB->GetAssignedPriority() )
1356 continue;
1357
1358 // rule areas may overlap at will
1359 if( zoneA->GetIsRuleArea() || zoneB->GetIsRuleArea() )
1360 continue;
1361
1362 // Examine a candidate zone: compare zoneB to zoneA
1363 SHAPE_POLY_SET* polyA = nullptr;
1364 SHAPE_POLY_SET* polyB = nullptr;
1365
1366 if( sameNet )
1367 {
1368 polyA = zoneA->Outline();
1369 polyB = zoneB->Outline();
1370 }
1371 else
1372 {
1373 polyA = zoneA->GetFill( layer );
1374 polyB = zoneB->GetFill( layer );
1375 }
1376
1377 if( !polyA->BBoxFromCaches().Intersects( polyB->BBoxFromCaches() ) )
1378 continue;
1379
1380 count++;
1381 (void)tp.submit_task(
1382 [checkZones, ia, ia2, sameNet, layer]()
1383 {
1384 checkZones( ia, ia2, sameNet, layer );
1385 } );
1386 }
1387 }
1388 }
1389
1390 while( true )
1391 {
1392 reportProgress( done, count );
1393
1394 if( m_drcEngine->IsCancelled() )
1395 break;
1396
1397 if( tp.wait_for( std::chrono::milliseconds( 250 ) ) )
1398 break;
1399 }
1400}
1401
1402namespace detail
1403{
1405}
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
NETINFO_ITEM * GetNet() const
Return #NET_INFO object for a given item.
const wxString & GetShortNetname() const
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:79
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition board_item.h: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.
FOOTPRINT * GetParentFootprint() const
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
virtual bool HasHole() const
Definition board_item.h:156
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:170
SEVERITY GetSeverity() const
Definition drc_rule.h:183
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:162
DRC_RULE * GetParentRule() const
Definition drc_rule.h:166
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition drc_item.cpp:381
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, const std::vector< PCB_SHAPE > &aShapes={})
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
std::map< wxString, int > MapPadNumbersToNetTieGroups() const
std::deque< PAD * > & Pads()
Definition footprint.h:224
bool IsNetTie() const
Definition footprint.h:340
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:54
const wxString & GetNetname() const
Definition netinfo.h:112
int GetNetCode() const
Definition netinfo.h:106
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 IsFreePad() const
Definition pad.cpp:284
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.
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.
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
bool IsOnLayer(PCB_LAYER_ID aLayer) const override
Test to see if this object is on the given layer.
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:437
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)
@ DEFAULT
Flashing follows connectivity.
Definition layer_ids.h:185
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:674
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
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
Definition padstack.h:87
@ PTH
Plated through hole pad.
Definition padstack.h:82
@ 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.
static thread_pool * tp
BS::thread_pool< 0 > thread_pool
Definition thread_pool.h:31
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:88
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:97
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:92
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:89
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:87
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:98
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:96
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695