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 else if( !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE ) )
156 {
157 if( !reportPhase( _( "Checking copper graphic hole clearances..." ) ) )
158 return false; // DRC cancelled
159
161 }
162
163 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE ) )
164 {
165 if( !reportPhase( _( "Checking copper zone clearances..." ) ) )
166 return false; // DRC cancelled
167
169 }
170 else if( !m_drcEngine->IsErrorLimitExceeded( DRCE_ZONES_INTERSECT ) )
171 {
172 if( !reportPhase( _( "Checking zones..." ) ) )
173 return false; // DRC cancelled
174
176 }
177
178 return !m_drcEngine->IsCancelled();
179}
180
181
183 SHAPE* itemShape,
184 PCB_LAYER_ID layer,
185 BOARD_ITEM* other )
186{
187 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
188 bool testShorting = !m_drcEngine->IsErrorLimitExceeded( DRCE_SHORTING_ITEMS );
189 bool testHoles = !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE );
190 DRC_CONSTRAINT constraint;
191 int clearance = -1;
192 int actual;
193 VECTOR2I pos;
194 bool has_error = false;
195 NETINFO_ITEM* net = nullptr;
196 NETINFO_ITEM* otherNet = nullptr;
197
198 if( BOARD_CONNECTED_ITEM* connectedItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
199 net = connectedItem->GetNet();
200
201 NETINFO_ITEM* itemNet = net;
202
203 if( BOARD_CONNECTED_ITEM* connectedItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( other ) )
204 otherNet = connectedItem->GetNet();
205
206 std::shared_ptr<SHAPE> otherShape_shared_ptr;
207
208 if( other->Type() == PCB_PAD_T )
209 {
210 PAD* pad = static_cast<PAD*>( other );
211
212 if( !pad->FlashLayer( layer ) )
213 {
214 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
215 testClearance = testShorting = false;
216
217 otherShape_shared_ptr = pad->GetEffectiveHoleShape();
218 }
219 }
220 else if( other->Type() == PCB_VIA_T )
221 {
222 PCB_VIA* via = static_cast<PCB_VIA*>( other );
223
224 if( !via->FlashLayer( layer ) )
225 otherShape_shared_ptr = via->GetEffectiveHoleShape();
226 }
227
228 if( !otherShape_shared_ptr )
229 otherShape_shared_ptr = other->GetEffectiveShape( layer );
230
231 SHAPE* otherShape = otherShape_shared_ptr.get();
232
233 if( testClearance || testShorting )
234 {
235 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, item, other, layer );
236 clearance = constraint.GetValue().Min();
237 }
238
239 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
240 {
241 // Collide (and generate violations) based on a well-defined order so that exclusion
242 // checking against previously-generated violations will work.
243 if( item->m_Uuid > other->m_Uuid )
244 {
245 std::swap( item, other );
246 std::swap( itemShape, otherShape );
247 std::swap( net, otherNet );
248 }
249
250 // Special processing for track:track intersections
251 if( item->Type() == PCB_TRACE_T && other->Type() == PCB_TRACE_T )
252 {
253 PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
254 PCB_TRACK* otherTrack = static_cast<PCB_TRACK*>( other );
255
256 SEG trackSeg( track->GetStart(), track->GetEnd() );
257 SEG otherSeg( otherTrack->GetStart(), otherTrack->GetEnd() );
258
259 if( OPT_VECTOR2I intersection = trackSeg.Intersect( otherSeg ) )
260 {
261 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_TRACKS_CROSSING );
262 drcItem->SetItems( item, other );
263 drcItem->SetViolatingRule( constraint.GetParentRule() );
264 reportTwoPointGeometry( drcItem, *intersection, *intersection, *intersection, layer );
265 return false;
266 }
267 }
268
269 if( itemShape->Collide( otherShape, clearance - m_drcEpsilon, &actual, &pos ) )
270 {
271 if( itemNet && m_drcEngine->IsNetTieExclusion( itemNet->GetNetCode(), layer, pos, other ) )
272 {
273 // Collision occurred as track was entering a pad marked as a net-tie. We
274 // allow these.
275 }
276 else if( actual == 0 && otherNet && testShorting )
277 {
278 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SHORTING_ITEMS );
279 drcItem->SetErrorDetail( wxString::Format( _( "(nets %s and %s)" ),
280 net ? net->GetNetname() : _( "<no net>" ),
281 otherNet ? otherNet->GetNetname() : _( "<no net>" ) ) );
282 drcItem->SetItems( item, other );
283 reportTwoPointGeometry( drcItem, pos, pos, pos, layer );
284 has_error = true;
285
286 if( !m_drcEngine->GetReportAllTrackErrors() )
287 return false;
288 }
289 else if( testClearance )
290 {
291 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
292 drcItem->SetErrorDetail( formatMsg( _( "(%s clearance %s; actual %s)" ),
293 constraint.GetName(),
294 clearance,
295 actual ) );
296 drcItem->SetItems( item, other );
297 drcItem->SetViolatingRule( constraint.GetParentRule() );
298 reportTwoShapeGeometry( drcItem, pos, itemShape, otherShape, layer, actual );
299 has_error = true;
300
301 if( !m_drcEngine->GetReportAllTrackErrors() )
302 return false;
303 }
304 }
305 }
306
307 if( testHoles && ( item->HasHole() || other->HasHole() ) )
308 {
309 std::array<BOARD_ITEM*, 2> a{ item, other };
310 std::array<BOARD_ITEM*, 2> b{ other, item };
311 std::array<NETINFO_ITEM*, 2> b_net{ otherNet, itemNet };
312 std::array<SHAPE*, 2> a_shape{ itemShape, otherShape };
313
314 for( size_t ii = 0; ii < 2; ++ii )
315 {
316 std::shared_ptr<SHAPE_SEGMENT> holeShape;
317
318 if( b[ii]->Type() == PCB_VIA_T )
319 {
320 if( b[ii]->GetLayerSet().Contains( layer ) )
321 holeShape = b[ii]->GetEffectiveHoleShape();
322 else
323 continue;
324 }
325 else
326 {
327 if( b[ii]->HasHole() )
328 holeShape = b[ii]->GetEffectiveHoleShape();
329 else
330 continue;
331 }
332
333 int netcode = b_net[ii] ? b_net[ii]->GetNetCode() : 0;
334
335 if( netcode && m_drcEngine->IsNetTieExclusion( netcode, layer, holeShape->Centre(), a[ii] ) )
336 continue;
337
338 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, b[ii], a[ii], layer );
339 clearance = constraint.GetValue().Min();
340
341 // Test for hole to item clearance even if clearance is 0, because the item cannot be
342 // inside (or intersect) the hole.
343 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE )
344 {
345 if( a_shape[ii]->Collide( holeShape.get(), std::max( 0, clearance - m_drcEpsilon ),
346 &actual, &pos ) )
347 {
348 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
349 drcItem->SetErrorDetail( formatMsg( clearance ? _( "(%s clearance %s; actual %s)" )
350 : _( "(%s clearance %s; actual < 0)" ),
351 constraint.GetName(),
352 clearance,
353 actual ) );
354 drcItem->SetItems( a[ii], b[ii] );
355 drcItem->SetViolatingRule( constraint.GetParentRule() );
356 reportTwoShapeGeometry( drcItem, pos, a_shape[ii], holeShape.get(), layer, actual );
357 return false;
358 }
359 }
360 }
361 }
362
363 return !has_error;
364}
365
366
368 PCB_LAYER_ID aLayer )
369{
370 if( !aZone->GetLayerSet().test( aLayer ) )
371 return;
372
373 if( aZone->GetNetCode() && aItem->IsConnected() )
374 {
375 if( aZone->GetNetCode() == static_cast<BOARD_CONNECTED_ITEM*>( aItem )->GetNetCode() )
376 return;
377 }
378
379 BOX2I itemBBox = aItem->GetBoundingBox();
380 BOX2I worstCaseBBox = itemBBox;
381
382 worstCaseBBox.Inflate( m_board->m_DRCMaxClearance );
383
384 if( !worstCaseBBox.Intersects( aZone->GetBoundingBox() ) )
385 return;
386
387 FOOTPRINT* parentFP = aItem->GetParentFootprint();
388
389 // Ignore graphic items which implement a net-tie to the zone's net on the layer being tested.
390 if( parentFP && parentFP->IsNetTie() && dynamic_cast<PCB_SHAPE*>( aItem ) )
391 {
392 std::set<PAD*> allowedNetTiePads;
393
394 for( PAD* pad : parentFP->Pads() )
395 {
396 if( pad->GetNetCode() == aZone->GetNetCode() && aZone->GetNetCode() != 0 )
397 {
398 if( pad->IsOnLayer( aLayer ) )
399 allowedNetTiePads.insert( pad );
400
401 for( PAD* other : parentFP->GetNetTiePads( pad ) )
402 {
403 if( other->IsOnLayer( aLayer ) )
404 allowedNetTiePads.insert( other );
405 }
406 }
407 }
408
409 if( !allowedNetTiePads.empty() )
410 {
411 std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape();
412
413 for( PAD* pad : allowedNetTiePads )
414 {
415 if( pad->GetBoundingBox().Intersects( itemBBox )
416 && pad->GetEffectiveShape( aLayer )->Collide( itemShape.get() ) )
417 {
418 return;
419 }
420 }
421 }
422 }
423
424 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
425 bool testHoles = !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE );
426
427 if( !testClearance && !testHoles )
428 return;
429
430 DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ aZone ].get();
431
432 if( !zoneTree )
433 return;
434
435 DRC_CONSTRAINT constraint;
436 int clearance = -1;
437 int actual;
438 VECTOR2I pos;
439
440 if( aItem->Type() == PCB_PAD_T )
441 {
442 PAD* pad = static_cast<PAD*>( aItem );
443 bool flashedPad = pad->FlashLayer( aLayer );
444 bool platedHole = pad->HasHole() && pad->GetAttribute() == PAD_ATTRIB::PTH;
445
446 if( !flashedPad && !platedHole )
447 testClearance = false;
448 }
449
450 if( testClearance )
451 {
452 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aItem, aZone, aLayer );
453 clearance = constraint.GetValue().Min();
454 }
455
456 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
457 {
458 std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aLayer, FLASHING::DEFAULT );
459
460 if( zoneTree->QueryColliding( itemBBox, itemShape.get(), aLayer,
461 std::max( 0, clearance - m_drcEpsilon ), &actual, &pos ) )
462 {
463 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
464 drcItem->SetErrorDetail( formatMsg( _( "(%s clearance %s; actual %s)" ),
465 constraint.GetName(),
466 clearance,
467 actual ) );
468 drcItem->SetItems( aItem, aZone );
469 drcItem->SetViolatingRule( constraint.GetParentRule() );
470 reportTwoItemGeometry( drcItem, pos, aItem, aZone, aLayer, actual );
471 }
472 }
473
474 if( testHoles && aItem->HasHole() )
475 {
476 std::shared_ptr<SHAPE_SEGMENT> holeShape;
477
478 if( aItem->Type() == PCB_VIA_T )
479 {
480 if( aItem->GetLayerSet().Contains( aLayer ) )
481 holeShape = aItem->GetEffectiveHoleShape();
482 }
483 else
484 {
485 holeShape = aItem->GetEffectiveHoleShape();
486 }
487
488 if( holeShape )
489 {
490 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, aItem, aZone, aLayer );
491 clearance = constraint.GetValue().Min();
492
493 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
494 {
495 if( zoneTree->QueryColliding( itemBBox, holeShape.get(), aLayer,
496 std::max( 0, clearance - m_drcEpsilon ),
497 &actual, &pos ) )
498 {
499 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
500 drcItem->SetErrorDetail( formatMsg( _( "(%s clearance %s; actual %s)" ),
501 constraint.GetName(),
502 clearance,
503 actual ) );
504 drcItem->SetItems( aItem, aZone );
505 drcItem->SetViolatingRule( constraint.GetParentRule() );
506
507 std::shared_ptr<SHAPE> zoneShape = aZone->GetEffectiveShape( aLayer );
508 reportTwoShapeGeometry( drcItem, pos, holeShape.get(), zoneShape.get(), aLayer, actual );
509 }
510 }
511 }
512 }
513}
514
515
516/*
517 * We have to special-case knockout text as it's most often knocked-out of a zone, so it's
518 * presumed to collide with one. However, if it collides with more than one, and they have
519 * different nets, then we have a short.
520 */
522 NETINFO_ITEM** aInheritedNet,
523 ZONE* aZone )
524{
525 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
526 bool testShorts = !m_drcEngine->IsErrorLimitExceeded( DRCE_SHORTING_ITEMS );
527
528 if( !testClearance && !testShorts )
529 return;
530
531 PCB_LAYER_ID layer = aText->GetLayer();
532
533 if( !aZone->GetLayerSet().test( layer ) )
534 return;
535
536 BOX2I itemBBox = aText->GetBoundingBox();
537 BOX2I worstCaseBBox = itemBBox;
538
539 worstCaseBBox.Inflate( m_board->m_DRCMaxClearance );
540
541 if( !worstCaseBBox.Intersects( aZone->GetBoundingBox() ) )
542 return;
543
544 DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ aZone ].get();
545
546 if( !zoneTree )
547 return;
548
549 std::shared_ptr<SHAPE> itemShape = aText->GetEffectiveShape( layer, FLASHING::DEFAULT );
550
551 if( *aInheritedNet == nullptr )
552 {
553 if( zoneTree->QueryColliding( itemBBox, itemShape.get(), layer ) )
554 *aInheritedNet = aZone->GetNet();
555 }
556
557 if( *aInheritedNet == aZone->GetNet() )
558 return;
559
560 DRC_CONSTRAINT constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aText, aZone, layer );
561 int clearance = constraint.GetValue().Min();
562 int actual;
563 VECTOR2I pos;
564
565 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
566 {
567 if( zoneTree->QueryColliding( itemBBox, itemShape.get(), layer,
568 std::max( 0, clearance - m_drcEpsilon ), &actual, &pos ) )
569 {
570 std::shared_ptr<DRC_ITEM> drcItem;
571
572 if( testShorts && actual == 0 && *aInheritedNet )
573 {
575 drcItem->SetErrorDetail( wxString::Format( _( "(nets %s and %s)" ),
576 ( *aInheritedNet )->GetNetname(),
577 aZone->GetNetname() ) );
578 }
579 else
580 {
581 drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
582 drcItem->SetErrorDetail( formatMsg( _( "(%s clearance %s; actual %s)" ),
583 constraint.GetName(),
584 clearance,
585 actual ) );
586 }
587
588 drcItem->SetItems( aText, aZone );
589 drcItem->SetViolatingRule( constraint.GetParentRule() );
590 reportTwoItemGeometry( drcItem, pos, aText, aZone, layer, actual );
591 }
592 }
593}
594
595
597{
598 std::map<BOARD_ITEM*, int> freePadsUsageMap;
599 std::unordered_map<PTR_PTR_CACHE_KEY, LAYERS_CHECKED> checkedPairs;
600 std::mutex checkedPairsMutex;
601 std::mutex freePadsUsageMapMutex;
602 std::atomic<size_t> done( 0 );
603 size_t count = m_board->Tracks().size();
604
605 REPORT_AUX( wxString::Format( wxT( "Testing %d tracks & vias..." ), count ) );
606
607 LSET boardCopperLayers = LSET::AllCuMask( m_board->GetCopperLayerCount() );
608
609 auto testTrack =
610 [&]( const int trackIdx )
611 {
612 PCB_TRACK* track = m_board->Tracks()[trackIdx];
613
614 for( PCB_LAYER_ID layer : LSET( track->GetLayerSet() & boardCopperLayers ) )
615 {
616 std::shared_ptr<SHAPE> trackShape = track->GetEffectiveShape( layer );
617
618 m_board->m_CopperItemRTreeCache->QueryColliding( track, layer, layer,
619 // Filter:
620 [&]( BOARD_ITEM* other ) -> bool
621 {
622 BOARD_CONNECTED_ITEM* otherCItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( other );
623
624 if( otherCItem && otherCItem->GetNetCode() == track->GetNetCode() )
625 return false;
626
627 BOARD_ITEM* a = track;
628 BOARD_ITEM* b = other;
629
630 // store canonical order so we don't collide in both directions
631 // (a:b and b:a)
632 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
633 std::swap( a, b );
634
635 std::lock_guard<std::mutex> lock( checkedPairsMutex );
636 auto it = checkedPairs.find( { a, b } );
637
638 if( it != checkedPairs.end()
639 && ( it->second.layers.test( layer ) || ( it->second.has_error ) ) )
640 {
641 return false;
642 }
643 else
644 {
645 checkedPairs[ { a, b } ].layers.set( layer );
646 return true;
647 }
648 },
649 // Visitor:
650 [&]( BOARD_ITEM* other ) -> bool
651 {
652 if( m_drcEngine->IsCancelled() )
653 return false;
654
655 if( other->Type() == PCB_PAD_T && static_cast<PAD*>( other )->IsFreePad() )
656 {
657 if( other->GetEffectiveShape( layer )->Collide( trackShape.get() ) )
658 {
659 std::lock_guard<std::mutex> lock( freePadsUsageMapMutex );
660 auto it = freePadsUsageMap.find( other );
661
662 if( it == freePadsUsageMap.end() )
663 {
664 freePadsUsageMap[ other ] = track->GetNetCode();
665 return true; // Continue colliding tests
666 }
667 else if( it->second == track->GetNetCode() )
668 {
669 return true; // Continue colliding tests
670 }
671 }
672 }
673
674 // If we get an error, mark the pair as having a clearance error already
675 if( !testSingleLayerItemAgainstItem( track, trackShape.get(), layer, other ) )
676 {
677 if( !m_drcEngine->GetReportAllTrackErrors() )
678 {
679 BOARD_ITEM* a = track;
680 BOARD_ITEM* b = other;
681
682 // store canonical order so we don't collide in both directions
683 // (a:b and b:a)
684 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
685 std::swap( a, b );
686
687 std::lock_guard<std::mutex> lock( checkedPairsMutex );
688 auto it = checkedPairs.find( { a, b } );
689
690 if( it != checkedPairs.end() )
691 it->second.has_error = true;
692
693 return false; // We're done with this track
694 }
695 }
696
697 return !m_drcEngine->IsCancelled();
698 },
699 m_board->m_DRCMaxClearance );
700
701 for( ZONE* zone : m_board->m_DRCCopperZones )
702 {
703 testItemAgainstZone( track, zone, layer );
704
705 if( m_drcEngine->IsCancelled() )
706 break;
707 }
708 }
709
710 done.fetch_add( 1 );
711 };
712
714
715 auto track_futures = tp.submit_loop( 0, m_board->Tracks().size(), testTrack );
716
717 while( done < count )
718 {
719 reportProgress( done, count );
720
721 if( m_drcEngine->IsCancelled() )
722 {
723 // Wait for the submitted loop tasks to finish
724 track_futures.wait();
725 break;
726 }
727
728 std::this_thread::sleep_for( std::chrono::milliseconds( 250 ) );
729 }
730}
731
732
734 PCB_LAYER_ID aLayer,
735 BOARD_ITEM* other )
736{
737 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
738 bool testShorting = !m_drcEngine->IsErrorLimitExceeded( DRCE_SHORTING_ITEMS );
739 bool testHoles = !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE );
740
741 // Disable some tests for net-tie objects in a footprint
742 if( other->GetParent() == pad->GetParent() )
743 {
744 FOOTPRINT* fp = pad->GetParentFootprint();
745 std::map<wxString, int> padToNetTieGroupMap = fp->MapPadNumbersToNetTieGroups();
746 int padGroupIdx = padToNetTieGroupMap[ pad->GetNumber() ];
747
748 if( other->Type() == PCB_PAD_T )
749 {
750 PAD* otherPad = static_cast<PAD*>( other );
751
752 if( padGroupIdx >= 0 && padGroupIdx == padToNetTieGroupMap[ otherPad->GetNumber() ] )
753 testClearance = testShorting = false;
754
755 if( pad->SameLogicalPadAs( otherPad ) )
756 testHoles = false;
757 }
758
759 if( other->Type() == PCB_SHAPE_T && padGroupIdx >= 0 )
760 testClearance = testShorting = false;
761 }
762
763 BOARD_CONNECTED_ITEM* otherCItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( other );
764 PAD* otherPad = nullptr;
765 PCB_VIA* otherVia = nullptr;
766
767 if( other->Type() == PCB_PAD_T )
768 otherPad = static_cast<PAD*>( other );
769
770 if( other->Type() == PCB_VIA_T )
771 otherVia = static_cast<PCB_VIA*>( other );
772
773 if( !IsCopperLayer( aLayer ) )
774 testClearance = testShorting = false;
775
776 // A NPTH has no cylinder, but it may still have pads on some layers
777 if( pad->GetAttribute() == PAD_ATTRIB::NPTH && !pad->FlashLayer( aLayer ) )
778 testClearance = testShorting = false;
779
780 if( otherPad && otherPad->GetAttribute() == PAD_ATTRIB::NPTH && !otherPad->FlashLayer( aLayer ) )
781 testClearance = testShorting = false;
782
783 // Track clearances are tested in testTrackClearances()
784 if( dynamic_cast<PCB_TRACK*>( other) )
785 testClearance = testShorting = false;
786
787 // Graphic clearances are tested in testGraphicClearances()
788 if( dynamic_cast<PCB_SHAPE*>( other ) )
789 testClearance = testShorting = false;
790
791 int padNet = pad->GetNetCode();
792 int otherNet = otherCItem ? otherCItem->GetNetCode() : 0;
793
794 // Other objects of the same (defined) net get a waiver on clearance and hole tests
795 if( otherNet && otherNet == padNet )
796 {
797 testClearance = testShorting = false;
798 testHoles = false;
799 }
800
801 if( !( pad->GetDrillSize().x > 0 )
802 && !( otherPad && otherPad->GetDrillSize().x > 0 )
803 && !( otherVia && otherVia->GetDrill() > 0 ) )
804 {
805 testHoles = false;
806 }
807
808 if( !testClearance && !testShorting && !testHoles )
809 return true;
810
811 std::shared_ptr<SHAPE> otherShape = other->GetEffectiveShape( aLayer );
812 DRC_CONSTRAINT constraint;
813 int clearance = 0;
814 int actual = 0;
815 VECTOR2I pos;
816 bool has_error = false;
817
818 auto sub_e = [this]( int aclearance )
819 {
820 return std::max( 0, aclearance - m_drcEpsilon );
821 };
822
823 if( otherPad && pad->SameLogicalPadAs( otherPad ) )
824 {
825 // If pads are equivalent (ie: from the same footprint with the same pad number)...
826 // ... and have "real" nets...
827 // then they must be the same net
828 if( testShorting )
829 {
830 if( pad->GetNetCode() == 0 || pad->GetNetCode() == otherPad->GetNetCode() )
831 return true;
832
833 if( pad->GetShortNetname().StartsWith( wxS( "unconnected-(" ) )
834 && otherPad->GetShortNetname().StartsWith( wxS( "unconnected-(" ) ) )
835 {
836 return true;
837 }
838
839 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SHORTING_ITEMS );
840 drcItem->SetErrorDetail( wxString::Format( _( "(nets %s and %s)" ),
841 pad->GetNetname(),
842 otherPad->GetNetname() ) );
843 drcItem->SetItems( pad, otherPad );
844 reportViolation( drcItem, otherPad->GetPosition(), aLayer );
845 has_error = true;
846 }
847
848 return !has_error;
849 }
850
851 if( testClearance || testShorting )
852 {
853 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, pad, other, aLayer );
854 clearance = constraint.GetValue().Min();
855
856 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
857 {
858 if( padShape->Collide( otherShape.get(), sub_e( clearance ), &actual, &pos ) )
859 {
860 if( m_drcEngine->IsNetTieExclusion( pad->GetNetCode(), aLayer, pos, other ) )
861 {
862 // Pads connected to pads of a net-tie footprint are allowed to collide
863 // with the net-tie footprint's graphics.
864 }
865 else if( actual == 0 && padNet && otherNet && testShorting )
866 {
867 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SHORTING_ITEMS );
868 drcItem->SetErrorDetail( wxString::Format( _( "(nets %s and %s)" ),
869 pad->GetNetname(),
870 otherCItem->GetNetname() ) );
871 drcItem->SetItems( pad, other );
872 reportTwoPointGeometry( drcItem, pos, pos, pos, aLayer );
873 has_error = true;
874 testHoles = false; // No need for multiple violations
875 }
876 else if( testClearance )
877 {
878 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
879 drcItem->SetErrorDetail( formatMsg( _( "(%s clearance %s; actual %s)" ),
880 constraint.GetName(),
881 clearance,
882 actual ) );
883 drcItem->SetItems( pad, other );
884 drcItem->SetViolatingRule( constraint.GetParentRule() );
885 reportTwoItemGeometry( drcItem, pos, pad, other, aLayer, actual );
886 has_error = true;
887 testHoles = false; // No need for multiple violations
888 }
889 }
890 }
891 }
892
893 auto doTestHole =
894 [&]( BOARD_ITEM* item, SHAPE* shape, BOARD_ITEM* otherItem, SHAPE* aOtherShape, int aClearance )
895 {
896 if( shape->Collide( aOtherShape, sub_e( aClearance ), &actual, &pos ) )
897 {
898 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
899 drcItem->SetErrorDetail( formatMsg( _( "(%s clearance %s; actual %s)" ),
900 constraint.GetName(),
901 aClearance,
902 actual ) );
903 drcItem->SetItems( item, otherItem );
904 drcItem->SetViolatingRule( constraint.GetParentRule() );
905 reportTwoShapeGeometry( drcItem, pos, shape, aOtherShape, aLayer, actual );
906 has_error = true;
907 testHoles = false; // No need for multiple violations
908 }
909 };
910
911 if( testHoles )
912 {
913 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, pad, other, aLayer );
914
915 if( constraint.GetSeverity() == RPT_SEVERITY_IGNORE )
916 testHoles = false;
917 }
918
919 if( testHoles && otherPad && otherPad->HasHole() )
920 {
921 clearance = constraint.GetValue().Min();
922
923 if( !pad->FlashLayer( aLayer ) )
924 clearance = 0;
925
926 if( clearance > 0 )
927 doTestHole( pad, padShape, otherPad, otherPad->GetEffectiveHoleShape().get(), clearance );
928 }
929
930 if( testHoles && otherVia && otherVia->HasHole() )
931 {
932 clearance = constraint.GetValue().Min();
933
934 if( !otherVia->IsOnLayer( aLayer ) )
935 clearance = 0;
936
937 if( clearance > 0 )
938 doTestHole( pad, padShape, otherVia, otherVia->GetEffectiveHoleShape().get(), clearance );
939 }
940
941 return !has_error;
942}
943
944
946{
948 std::atomic<size_t> done( 1 );
949
950 std::unordered_map<PTR_PTR_CACHE_KEY, LAYERS_CHECKED> checkedPairs;
951 std::mutex checkedPairsMutex;
952
953 LSET boardCopperLayers = LSET::AllCuMask( m_board->GetCopperLayerCount() );
954
955 const auto fp_check =
956 [&]( size_t ii )
957 {
958 FOOTPRINT* footprint = m_board->Footprints()[ ii ];
959
960 for( PAD* pad : footprint->Pads() )
961 {
962 for( PCB_LAYER_ID layer : LSET( pad->GetLayerSet() & boardCopperLayers ) )
963 {
964 if( m_drcEngine->IsCancelled() )
965 return;
966
967 std::shared_ptr<SHAPE> padShape = pad->GetEffectiveShape( layer );
968
969 m_board->m_CopperItemRTreeCache->QueryColliding( pad, layer, layer,
970 // Filter:
971 [&]( BOARD_ITEM* other ) -> bool
972 {
973 BOARD_ITEM* a = pad;
974 BOARD_ITEM* b = other;
975
976 // store canonical order so we don't collide in both
977 // directions (a:b and b:a)
978 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
979 std::swap( a, b );
980
981 std::lock_guard<std::mutex> lock( checkedPairsMutex );
982 auto it = checkedPairs.find( { a, b } );
983
984 if( it != checkedPairs.end()
985 && ( it->second.layers.test( layer ) || it->second.has_error ) )
986 {
987 return false;
988 }
989 else
990 {
991 checkedPairs[ { a, b } ].layers.set( layer );
992 return true;
993 }
994 },
995 // Visitor
996 [&]( BOARD_ITEM* other ) -> bool
997 {
998 if( !testPadAgainstItem( pad, padShape.get(), layer, other ) )
999 {
1000 BOARD_ITEM* a = pad;
1001 BOARD_ITEM* b = other;
1002
1003 std::lock_guard<std::mutex> lock( checkedPairsMutex );
1004 auto it = checkedPairs.find( { a, b } );
1005
1006 if( it != checkedPairs.end() )
1007 it->second.has_error = true;
1008 }
1009
1010 return !m_drcEngine->IsCancelled();
1011 },
1012 m_board->m_DRCMaxClearance );
1013
1014 for( ZONE* zone : m_board->m_DRCCopperZones )
1015 {
1016 testItemAgainstZone( pad, zone, layer );
1017
1018 if( m_drcEngine->IsCancelled() )
1019 return;
1020 }
1021 }
1022 }
1023
1024 done.fetch_add( 1 );
1025 };
1026
1027 size_t numFootprints = m_board->Footprints().size();
1028 auto returns = tp.submit_loop( 0, numFootprints, fp_check );
1029
1030 // Wait for all threads to finish
1031 for( size_t ii = 0; ii < returns.size(); ++ii )
1032 {
1033 while( returns[ii].wait_for( std::chrono::milliseconds( 250 ) ) != std::future_status::ready )
1034 reportProgress( done, numFootprints );
1035 }
1036}
1037
1038
1040{
1042 size_t count = m_board->Drawings().size();
1043 std::atomic<size_t> done( 1 );
1044
1045 for( FOOTPRINT* footprint : m_board->Footprints() )
1046 count += footprint->GraphicalItems().size();
1047
1048 REPORT_AUX( wxString::Format( wxT( "Testing %d graphics..." ), count ) );
1049
1050 auto isKnockoutText =
1051 []( BOARD_ITEM* item )
1052 {
1053 return item->Type() == PCB_TEXT_T && static_cast<PCB_TEXT*>( item )->IsKnockout();
1054 };
1055
1056 auto testGraphicAgainstZone =
1057 [this, isKnockoutText]( BOARD_ITEM* item )
1058 {
1059 if( item->Type() == PCB_REFERENCE_IMAGE_T )
1060 return;
1061
1062 if( !IsCopperLayer( item->GetLayer() ) )
1063 return;
1064
1065 // Knockout text is most often knocked-out of a zone, so it's presumed to
1066 // collide with one. However, if it collides with more than one, and they
1067 // have different nets, then we have a short.
1068 NETINFO_ITEM* inheritedNet = nullptr;
1069
1070 for( ZONE* zone : m_board->m_DRCCopperZones )
1071 {
1072 if( isKnockoutText( item ) )
1073 testKnockoutTextAgainstZone( item, &inheritedNet, zone );
1074 else
1075 testItemAgainstZone( item, zone, item->GetLayer() );
1076
1077 if( m_drcEngine->IsCancelled() )
1078 return;
1079 }
1080 };
1081
1082 std::unordered_map<PTR_PTR_CACHE_KEY, LAYERS_CHECKED> checkedPairs;
1083 std::mutex checkedPairsMutex;
1084
1085 auto testCopperGraphic =
1086 [this, &checkedPairs, &checkedPairsMutex]( BOARD_ITEM* graphic )
1087 {
1088 PCB_LAYER_ID layer = graphic->GetLayer();
1089
1090 m_board->m_CopperItemRTreeCache->QueryColliding( graphic, layer, layer,
1091 // Filter:
1092 [&]( BOARD_ITEM* other ) -> bool
1093 {
1094 // Graphics are often compound shapes so ignore collisions between shapes
1095 // in a single footprint.
1096 if( graphic->Type() == PCB_SHAPE_T && other->Type() == PCB_SHAPE_T
1097 && graphic->GetParentFootprint()
1098 && graphic->GetParentFootprint() == other->GetParentFootprint() )
1099 {
1100 return false;
1101 }
1102
1103 // Track clearances are tested in testTrackClearances()
1104 if( dynamic_cast<PCB_TRACK*>( other) )
1105 return false;
1106
1107 BOARD_ITEM* a = graphic;
1108 BOARD_ITEM* b = other;
1109
1110 // store canonical order so we don't collide in both directions
1111 // (a:b and b:a)
1112 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
1113 std::swap( a, b );
1114
1115 std::lock_guard<std::mutex> lock( checkedPairsMutex );
1116 auto it = checkedPairs.find( { a, b } );
1117
1118 if( it != checkedPairs.end() && it->second.layers.test( layer ) )
1119 {
1120 return false;
1121 }
1122 else
1123 {
1124 checkedPairs[ { a, b } ].layers.set( layer );
1125 return true;
1126 }
1127 },
1128 // Visitor:
1129 [&]( BOARD_ITEM* other ) -> bool
1130 {
1131 testSingleLayerItemAgainstItem( graphic, graphic->GetEffectiveShape().get(),
1132 layer, other );
1133
1134 return !m_drcEngine->IsCancelled();
1135 },
1136 m_board->m_DRCMaxClearance );
1137 };
1138
1139 for( BOARD_ITEM* item : m_board->Drawings() )
1140 {
1141 (void)tp.submit_task(
1142 [this, item, &done, testGraphicAgainstZone, testCopperGraphic]()
1143 {
1144 if( !m_drcEngine->IsCancelled() )
1145 {
1146 testGraphicAgainstZone( item );
1147
1148 if( ( item->Type() == PCB_SHAPE_T || item->Type() == PCB_BARCODE_T )
1149 && item->IsOnCopperLayer() )
1150 {
1151 testCopperGraphic( static_cast<PCB_SHAPE*>( item ) );
1152 }
1153
1154 done.fetch_add( 1 );
1155 }
1156 } );
1157 }
1158
1159 for( FOOTPRINT* footprint : m_board->Footprints() )
1160 {
1161 (void)tp.submit_task(
1162 [this, footprint, &done, testGraphicAgainstZone, testCopperGraphic]()
1163 {
1164 for( BOARD_ITEM* item : footprint->GraphicalItems() )
1165 {
1166 if( !m_drcEngine->IsCancelled() )
1167 {
1168 testGraphicAgainstZone( item );
1169
1170 if( ( item->Type() == PCB_SHAPE_T || item->Type() == PCB_BARCODE_T )
1171 && item->IsOnCopperLayer() )
1172 {
1173 testCopperGraphic( static_cast<PCB_SHAPE*>( item ) );
1174 }
1175
1176 done.fetch_add( 1 );
1177 }
1178 }
1179 } );
1180 }
1181
1182 while( true )
1183 {
1184 reportProgress( done, count );
1185
1186 if( m_drcEngine->IsCancelled() )
1187 break;
1188
1189 if( tp.wait_for( std::chrono::milliseconds( 250 ) ) )
1190 break;
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 =
1208 [this]( ZONE* zoneA, ZONE* zoneB, VECTOR2I& pt, int actual, const DRC_CONSTRAINT& constraint,
1209 PCB_LAYER_ID layer ) -> void
1210 {
1211 std::shared_ptr<DRC_ITEM> drcItem;
1212
1213 if( constraint.IsNull() )
1214 {
1216 drcItem->SetErrorDetail( _( "(intersecting zones must have distinct priorities)" ) );
1217 drcItem->SetItems( zoneA, zoneB );
1218 reportViolation( drcItem, pt, layer );
1219 }
1220 else
1221 {
1222 drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
1223 drcItem->SetErrorDetail( formatMsg( _( "(%s clearance %s; actual %s)" ),
1224 constraint.GetName(),
1225 constraint.GetValue().Min(),
1226 std::max( actual, 0 ) ) );
1227 drcItem->SetItems( zoneA, zoneB );
1228 drcItem->SetViolatingRule( constraint.GetParentRule() );
1229 reportTwoItemGeometry( drcItem, pt, zoneA, zoneB, layer, actual );
1230 }
1231 };
1232
1233 auto checkZones =
1234 [this, testClearance, testIntersects, reportZoneZoneViolation, &poly_segments, &done]
1235 ( int zoneA_idx, int zoneB_idx, bool sameNet, PCB_LAYER_ID layer ) -> void
1236 {
1237 ZONE* zoneA = m_board->m_DRCCopperZones[zoneA_idx];
1238 ZONE* zoneB = m_board->m_DRCCopperZones[zoneB_idx];
1239 int actual = 0;
1240 VECTOR2I pt;
1241
1242 if( sameNet && testIntersects )
1243 {
1244 if( zoneA->Outline()->Collide( zoneB->Outline(), 0, &actual, &pt ) )
1245 {
1246 done.fetch_add( 1 );
1247 reportZoneZoneViolation( zoneA, zoneB, pt, actual, DRC_CONSTRAINT(), layer );
1248 return;
1249 }
1250 }
1251 else if( !sameNet && testClearance )
1252 {
1253 DRC_CONSTRAINT constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, zoneA, zoneB, layer );
1254 int clearance = constraint.GetValue().Min();
1255
1256 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
1257 {
1258 std::map<VECTOR2I, int> conflictPoints;
1259
1260 std::vector<SEG>& refSegments = poly_segments[zoneA_idx][layer];
1261 std::vector<SEG>& testSegments = poly_segments[zoneB_idx][layer];
1262
1263 // Iterate through all the segments in zoneA
1264 for( SEG& refSegment : refSegments )
1265 {
1266 // Iterate through all the segments in zoneB
1267 for( SEG& testSegment : testSegments )
1268 {
1269 // We have ensured that the 'A' segment starts before the 'B' segment, so if the
1270 // 'A' segment ends before the 'B' segment starts, we can skip to the next 'A'
1271 if( refSegment.B.x < testSegment.A.x )
1272 break;
1273
1274 int64_t dist_sq = 0;
1275 VECTOR2I other_pt;
1276 refSegment.NearestPoints( testSegment, pt, other_pt, dist_sq );
1277 actual = std::floor( std::sqrt( dist_sq ) + 0.5 );
1278
1279 if( actual < clearance )
1280 {
1281 done.fetch_add( 1 );
1282 reportZoneZoneViolation( zoneA, zoneB, pt, actual, constraint, layer );
1283 return;
1284 }
1285 }
1286 }
1287 }
1288 }
1289
1290 done.fetch_add( 1 );
1291 };
1292
1293 // Pre-sort zones into layers
1294 std::map<PCB_LAYER_ID, std::vector<size_t>> zone_idx_by_layer;
1295
1296 for ( size_t ii = 0; ii < m_board->m_DRCCopperZones.size(); ii++ )
1297 {
1298 ZONE* zone = m_board->m_DRCCopperZones[ii];
1299
1300 for( PCB_LAYER_ID layer : zone->GetLayerSet() )
1301 {
1302 if( !IsCopperLayer( layer ) )
1303 continue;
1304
1305 zone_idx_by_layer[layer].push_back( ii );
1306 }
1307 }
1308
1309 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, m_board->GetCopperLayerCount() ) )
1310 {
1311 // Skip over layers not used on the current board
1312 if( !m_board->IsLayerEnabled( layer ) )
1313 continue;
1314
1315 for( size_t ii : zone_idx_by_layer[layer] )
1316 {
1317 if( SHAPE_POLY_SET* poly = m_board->m_DRCCopperZones[ii]->GetFill( layer ) )
1318 {
1319 std::vector<SEG>& zone_layer_poly_segs = poly_segments[ii][layer];
1320 zone_layer_poly_segs.reserve( poly->FullPointCount() );
1321
1322 for( auto it = poly->IterateSegmentsWithHoles(); it; it++ )
1323 {
1324 SEG seg = *it;
1325
1326 if( seg.A.x > seg.B.x )
1327 seg.Reverse();
1328
1329 zone_layer_poly_segs.push_back( seg );
1330 }
1331
1332 std::sort( zone_layer_poly_segs.begin(), zone_layer_poly_segs.end() );
1333 }
1334 }
1335
1336 for( auto it_a = zone_idx_by_layer[layer].begin(); it_a != zone_idx_by_layer[layer].end(); ++it_a )
1337 {
1338 size_t ia = *it_a;
1339 ZONE* zoneA = m_board->m_DRCCopperZones[ia];
1340
1341 for( auto it_a2 = std::next( it_a ); it_a2 != zone_idx_by_layer[layer].end(); ++it_a2 )
1342 {
1343 size_t ia2 = *it_a2;
1344 ZONE* zoneB = m_board->m_DRCCopperZones[ia2];
1345
1346 bool sameNet = zoneA->GetNetCode() == zoneB->GetNetCode() && zoneA->GetNetCode() >= 0;
1347
1348 if( sameNet && zoneA->GetAssignedPriority() != zoneB->GetAssignedPriority() )
1349 continue;
1350
1351 // rule areas may overlap at will
1352 if( zoneA->GetIsRuleArea() || zoneB->GetIsRuleArea() )
1353 continue;
1354
1355 // Examine a candidate zone: compare zoneB to zoneA
1356 SHAPE_POLY_SET* polyA = nullptr;
1357 SHAPE_POLY_SET* polyB = nullptr;
1358
1359 if( sameNet )
1360 {
1361 polyA = zoneA->Outline();
1362 polyB = zoneB->Outline();
1363 }
1364 else
1365 {
1366 polyA = zoneA->GetFill( layer );
1367 polyB = zoneB->GetFill( layer );
1368 }
1369
1370 if( !polyA->BBoxFromCaches().Intersects( polyB->BBoxFromCaches() ) )
1371 continue;
1372
1373 count++;
1374 (void)tp.submit_task(
1375 [checkZones, ia, ia2, sameNet, layer]()
1376 {
1377 checkZones( ia, ia2, sameNet, layer );
1378 } );
1379 }
1380 }
1381 }
1382
1383 while( true )
1384 {
1385 reportProgress( done, count );
1386
1387 if( m_drcEngine->IsCancelled() )
1388 break;
1389
1390 if( tp.wait_for( std::chrono::milliseconds( 250 ) ) )
1391 break;
1392 }
1393}
1394
1395namespace detail
1396{
1398}
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:83
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition board_item.h:236
virtual bool IsConnected() const
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
Definition board_item.h:138
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:256
BOARD_ITEM_CONTAINER * GetParent() const
Definition board_item.h:214
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const
virtual bool HasHole() const
Definition board_item.h:160
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:194
SEVERITY GetSeverity() const
Definition drc_rule.h:207
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:186
DRC_RULE * GetParentRule() const
Definition drc_rule.h:190
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition drc_item.cpp:400
Implement an R-tree for fast spatial and layer indexing of connectable items.
Definition drc_rtree.h:50
int QueryColliding(BOARD_ITEM *aRefItem, PCB_LAYER_ID aRefLayer, PCB_LAYER_ID aTargetLayer, std::function< bool(BOARD_ITEM *)> aFilter=nullptr, std::function< bool(BOARD_ITEM *)> aVisitor=nullptr, int aClearance=0) const
This is a fast test which essentially does bounding-box overlap given a worst-case clearance.
Definition drc_rtree.h:217
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)
void reportTwoShapeGeometry(std::shared_ptr< DRC_ITEM > &aDrcItem, const VECTOR2I &aMarkerPos, const SHAPE *aShape1, const SHAPE *aShape2, PCB_LAYER_ID aLayer, int aDistance)
void reportTwoItemGeometry(std::shared_ptr< DRC_ITEM > &aDrcItem, const VECTOR2I &aMarkerPos, const BOARD_ITEM *aItem1, const BOARD_ITEM *aItem2, PCB_LAYER_ID aLayer, int aDistance)
void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer, const std::function< void(PCB_MARKER *)> &aPathGenerator=[](PCB_MARKER *){})
void reportTwoPointGeometry(std::shared_ptr< DRC_ITEM > &aDrcItem, const VECTOR2I &aMarkerPos, const VECTOR2I &ptA, const VECTOR2I &ptB, PCB_LAYER_ID aLayer)
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:55
bool FlashLayer(int aLayer, bool aOnlyCheckIfPermitted=false) const
Check to see whether the pad should be flashed on the specific layer.
Definition pad.cpp:410
const VECTOR2I & GetDrillSize() const
Definition pad.h:312
PAD_ATTRIB GetAttribute() const
Definition pad.h:558
const wxString & GetNumber() const
Definition pad.h:137
VECTOR2I GetPosition() const override
Definition pad.h:209
bool IsFreePad() const
Definition pad.cpp:332
bool HasHole() const override
Definition pad.h:107
std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const override
Return a SHAPE_SEGMENT object representing the pad's hole.
Definition pad.cpp:1033
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:154
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:151
int GetDrill() const
Return the local drill setting for this PCB_VIA.
Definition pcb_track.h:815
std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const override
bool HasHole() const override
Definition pcb_track.h:533
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:701
const BOX2I GetBoundingBox() const override
Definition zone.cpp:613
virtual PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition zone.cpp:484
SHAPE_POLY_SET * Outline()
Definition zone.h:331
SHAPE_POLY_SET * GetFill(PCB_LAYER_ID aLayer)
Definition zone.h:602
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
virtual 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 zone.cpp:1572
The common library.
@ DRCE_HOLE_CLEARANCE
Definition drc_item.h:55
@ DRCE_ZONES_INTERSECT
Definition drc_item.h:48
@ DRCE_CLEARANCE
Definition drc_item.h:44
@ DRCE_SHORTING_ITEMS
Definition drc_item.h:41
@ DRCE_TRACKS_CROSSING
Definition drc_item.h:46
@ 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:677
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:103
@ PTH
Plated through hole pad.
Definition padstack.h:98
@ 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::priority_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_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