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