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>
38
39#include <drc/drc_engine.h>
40#include <drc/drc_rtree.h>
41#include <drc/drc_item.h>
42#include <drc/drc_rule.h>
45#include <pcb_dimension.h>
46
47#include <future>
48
49/*
50 Copper clearance test. Checks all copper items (pads, vias, tracks, drawings, zones) for their
51 electrical clearance.
52
53 Errors generated:
54 - DRCE_CLEARANCE
55 - DRCE_HOLE_CLEARANCE
56 - DRCE_TRACKS_CROSSING
57 - DRCE_ZONES_INTERSECT
58 - DRCE_SHORTING_ITEMS
59*/
60
62{
63public:
68
70
71 virtual bool Run() override;
72
73 virtual const wxString GetName() const override { return wxT( "clearance" ); };
74
75private:
84 bool testSingleLayerItemAgainstItem( BOARD_ITEM* item, SHAPE* itemShape, PCB_LAYER_ID layer,
85 BOARD_ITEM* other );
86
88
89 bool testPadAgainstItem( PAD* pad, SHAPE* padShape, PCB_LAYER_ID layer, BOARD_ITEM* other );
90
91 void testPadClearances();
92
94
95 void testZonesToZones();
96
98
99 void testItemAgainstZone( BOARD_ITEM* aItem, ZONE* aZone, PCB_LAYER_ID aLayer );
100
101 void testKnockoutTextAgainstZone( BOARD_ITEM* aText, NETINFO_ITEM** aInheritedNet, ZONE* aZone );
102
103 int sub_e( int aClearance )
104 {
105 return std::max( 0, aClearance - m_drcEpsilon );
106 };
107
108private:
110};
111
112
114{
115 m_board = m_drcEngine->GetBoard();
116
117 if( m_board->m_DRCMaxClearance <= 0 )
118 {
119 REPORT_AUX( wxT( "No Clearance constraints found. Tests not run." ) );
120 return true; // continue with other tests
121 }
122
123 m_drcEpsilon = m_board->GetDesignSettings().GetDRCEpsilon();
124
125 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE ) )
126 {
127 if( !reportPhase( _( "Checking track & via clearances..." ) ) )
128 return false; // DRC cancelled
129
131 }
132 else if( !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE ) )
133 {
134 if( !reportPhase( _( "Checking hole clearances..." ) ) )
135 return false; // DRC cancelled
136
138 }
139
140 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE ) )
141 {
142 if( !reportPhase( _( "Checking pad clearances..." ) ) )
143 return false; // DRC cancelled
144
146 }
147 else if( !m_drcEngine->IsErrorLimitExceeded( DRCE_SHORTING_ITEMS )
148 || !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE ) )
149 {
150 if( !reportPhase( _( "Checking pads..." ) ) )
151 return false; // DRC cancelled
152
154 }
155
156 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE ) )
157 {
158 if( !reportPhase( _( "Checking copper graphic clearances..." ) ) )
159 return false; // DRC cancelled
160
162 }
163 else if( !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE ) )
164 {
165 if( !reportPhase( _( "Checking copper graphic hole clearances..." ) ) )
166 return false; // DRC cancelled
167
169 }
170
171 if( !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE ) )
172 {
173 if( !reportPhase( _( "Checking copper zone clearances..." ) ) )
174 return false; // DRC cancelled
175
177
178 if( !reportPhase( _( "Checking teardrop clearances..." ) ) )
179 return false; // DRC cancelled
180
182 }
183 else if( !m_drcEngine->IsErrorLimitExceeded( DRCE_ZONES_INTERSECT ) )
184 {
185 if( !reportPhase( _( "Checking zones..." ) ) )
186 return false; // DRC cancelled
187
189 }
190
191 return !m_drcEngine->IsCancelled();
192}
193
194
196 SHAPE* itemShape,
197 PCB_LAYER_ID layer,
198 BOARD_ITEM* other )
199{
200 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
201 bool testShorting = !m_drcEngine->IsErrorLimitExceeded( DRCE_SHORTING_ITEMS );
202 bool testHoles = !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE );
203 DRC_CONSTRAINT constraint;
204 int clearance = -1;
205 int actual;
206 VECTOR2I pos;
207 bool has_error = false;
208 NETINFO_ITEM* itemNet = nullptr;
209 NETINFO_ITEM* otherNet = nullptr;
210
211 if( item->IsConnected() )
212 itemNet = static_cast<BOARD_CONNECTED_ITEM*>( item )->GetNet();
213
214 if( other->IsConnected() )
215 otherNet = static_cast<BOARD_CONNECTED_ITEM*>( other )->GetNet();
216
217 if( itemNet == otherNet )
218 testClearance = testShorting = false;
219
220 std::shared_ptr<SHAPE> otherShape_shared_ptr;
221
222 if( other->Type() == PCB_PAD_T )
223 {
224 PAD* pad = static_cast<PAD*>( other );
225
226 if( !pad->FlashLayer( layer ) )
227 {
228 if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
229 testClearance = testShorting = false;
230
231 otherShape_shared_ptr = pad->GetEffectiveHoleShape();
232 }
233 }
234 else if( other->Type() == PCB_VIA_T )
235 {
236 PCB_VIA* via = static_cast<PCB_VIA*>( other );
237
238 if( !via->FlashLayer( layer ) )
239 otherShape_shared_ptr = via->GetEffectiveHoleShape();
240 }
241
242 if( !otherShape_shared_ptr )
243 otherShape_shared_ptr = other->GetEffectiveShape( layer );
244
245 SHAPE* otherShape = otherShape_shared_ptr.get();
246
247 // Collide (and generate violations) based on a well-defined order so that exclusion checking
248 // against previously-generated violations will work.
249 if( item->m_Uuid > other->m_Uuid )
250 {
251 std::swap( item, other );
252 std::swap( itemShape, otherShape );
253 std::swap( itemNet, otherNet );
254 }
255
256 if( testClearance || testShorting )
257 {
258 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, item, other, layer );
259 clearance = constraint.GetValue().Min();
260 }
261
262 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
263 {
264 // Special processing for track:track intersections
265 if( item->Type() == PCB_TRACE_T && other->Type() == PCB_TRACE_T )
266 {
267 PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
268 PCB_TRACK* otherTrack = static_cast<PCB_TRACK*>( other );
269
270 SEG trackSeg( track->GetStart(), track->GetEnd() );
271 SEG otherSeg( otherTrack->GetStart(), otherTrack->GetEnd() );
272
273 if( OPT_VECTOR2I intersection = trackSeg.Intersect( otherSeg ) )
274 {
275 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_TRACKS_CROSSING );
276 drcItem->SetItems( item, other );
277 drcItem->SetViolatingRule( constraint.GetParentRule() );
278 reportTwoPointGeometry( drcItem, *intersection, *intersection, *intersection, layer );
279 return false;
280 }
281 }
282
283 if( itemShape->Collide( otherShape, sub_e( clearance ), &actual, &pos ) )
284 {
285 if( itemNet && m_drcEngine->IsNetTieExclusion( itemNet->GetNetCode(), layer, pos, other ) )
286 {
287 // Collision occurred as track was entering a pad marked as a net-tie. We
288 // allow these.
289 }
290 else if( actual == 0 && otherNet && testShorting )
291 {
292 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SHORTING_ITEMS );
293 drcItem->SetErrorDetail( wxString::Format( _( "(nets %s and %s)" ),
294 itemNet ? itemNet->GetNetname() : _( "<no net>" ),
295 otherNet ? otherNet->GetNetname() : _( "<no net>" ) ) );
296 drcItem->SetItems( item, other );
297 reportTwoPointGeometry( drcItem, pos, pos, pos, layer );
298 has_error = true;
299
300 if( !m_drcEngine->GetReportAllTrackErrors() )
301 return false;
302 }
303 else if( testClearance )
304 {
305 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
306 drcItem->SetErrorDetail( formatMsg( _( "(%s clearance %s; actual %s)" ),
307 constraint.GetName(),
308 clearance,
309 actual ) );
310 drcItem->SetItems( item, other );
311 drcItem->SetViolatingRule( constraint.GetParentRule() );
312 reportTwoShapeGeometry( drcItem, pos, itemShape, otherShape, layer, actual );
313 has_error = true;
314
315 if( !m_drcEngine->GetReportAllTrackErrors() )
316 return false;
317 }
318 }
319 }
320
321 if( testHoles && ( item->HasHole() || other->HasHole() ) )
322 {
323 std::array<BOARD_ITEM*, 2> a{ item, other };
324 std::array<BOARD_ITEM*, 2> b{ other, item };
325 std::array<NETINFO_ITEM*, 2> b_net{ otherNet, itemNet };
326 std::array<SHAPE*, 2> a_shape{ itemShape, otherShape };
327
328 for( size_t ii = 0; ii < 2; ++ii )
329 {
330 std::shared_ptr<SHAPE_SEGMENT> holeShape;
331
332 if( b[ii]->Type() == PCB_VIA_T )
333 {
334 if( b[ii]->GetLayerSet().Contains( layer ) )
335 holeShape = b[ii]->GetEffectiveHoleShape();
336 else
337 continue;
338 }
339 else
340 {
341 if( b[ii]->HasHole() )
342 holeShape = b[ii]->GetEffectiveHoleShape();
343 else
344 continue;
345 }
346
347 int netcode = b_net[ii] ? b_net[ii]->GetNetCode() : 0;
348
349 if( netcode && m_drcEngine->IsNetTieExclusion( netcode, layer, holeShape->Centre(), a[ii] ) )
350 continue;
351
352 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, b[ii], a[ii], layer );
353 clearance = constraint.GetValue().Min();
354
355 // Test for hole to item clearance even if clearance is 0, because the item cannot be
356 // inside (or intersect) the hole.
357 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE )
358 {
359 if( a_shape[ii]->Collide( holeShape.get(), sub_e( clearance ), &actual, &pos ) )
360 {
361 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
362 drcItem->SetErrorDetail( formatMsg( clearance ? _( "(%s clearance %s; actual %s)" )
363 : _( "(%s clearance %s; actual < 0)" ),
364 constraint.GetName(),
365 clearance,
366 actual ) );
367 drcItem->SetItems( a[ii], b[ii] );
368 drcItem->SetViolatingRule( constraint.GetParentRule() );
369 reportTwoShapeGeometry( drcItem, pos, a_shape[ii], holeShape.get(), layer, actual );
370 return false;
371 }
372 }
373 }
374 }
375
376 return !has_error;
377}
378
379
381 PCB_LAYER_ID aLayer )
382{
383 if( !aZone->GetLayerSet().test( aLayer ) )
384 return;
385
386 if( aZone->GetNetCode() && aItem->IsConnected() )
387 {
388 if( aZone->GetNetCode() == static_cast<BOARD_CONNECTED_ITEM*>( aItem )->GetNetCode() )
389 return;
390 }
391
392 BOX2I itemBBox = aItem->GetBoundingBox();
393 BOX2I worstCaseBBox = itemBBox;
394
395 worstCaseBBox.Inflate( m_board->m_DRCMaxClearance );
396
397 if( !worstCaseBBox.Intersects( aZone->GetBoundingBox() ) )
398 return;
399
400 FOOTPRINT* parentFP = aItem->GetParentFootprint();
401
402 // Ignore graphic items which implement a net-tie to the zone's net on the layer being tested.
403 if( parentFP && parentFP->IsNetTie() && dynamic_cast<PCB_SHAPE*>( aItem ) )
404 {
405 std::set<PAD*> allowedNetTiePads;
406
407 for( PAD* pad : parentFP->Pads() )
408 {
409 if( pad->GetNetCode() == aZone->GetNetCode() && aZone->GetNetCode() != 0 )
410 {
411 if( pad->IsOnLayer( aLayer ) )
412 allowedNetTiePads.insert( pad );
413
414 for( PAD* other : parentFP->GetNetTiePads( pad ) )
415 {
416 if( other->IsOnLayer( aLayer ) )
417 allowedNetTiePads.insert( other );
418 }
419 }
420 }
421
422 if( !allowedNetTiePads.empty() )
423 {
424 std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape();
425
426 for( PAD* pad : allowedNetTiePads )
427 {
428 if( pad->GetBoundingBox().Intersects( itemBBox )
429 && pad->GetEffectiveShape( aLayer )->Collide( itemShape.get() ) )
430 {
431 return;
432 }
433 }
434 }
435 }
436
437 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
438 bool testHoles = !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE );
439
440 if( !testClearance && !testHoles )
441 return;
442
443 DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ aZone ].get();
444
445 if( !zoneTree )
446 return;
447
448 DRC_CONSTRAINT constraint;
449 int clearance = -1;
450 int actual;
451 VECTOR2I pos;
452
453 if( aItem->Type() == PCB_PAD_T )
454 {
455 PAD* pad = static_cast<PAD*>( aItem );
456 bool flashedPad = pad->FlashLayer( aLayer );
457 bool platedHole = pad->HasHole() && pad->GetAttribute() == PAD_ATTRIB::PTH;
458
459 if( !flashedPad && !platedHole )
460 testClearance = false;
461 }
462
463 if( testClearance )
464 {
465 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aItem, aZone, aLayer );
466 clearance = constraint.GetValue().Min();
467 }
468
469 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
470 {
471 std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aLayer, FLASHING::DEFAULT );
472
473 if( zoneTree->QueryColliding( itemBBox, itemShape.get(), aLayer, sub_e( clearance ), &actual, &pos ) )
474 {
475 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
476 drcItem->SetErrorDetail( formatMsg( _( "(%s clearance %s; actual %s)" ),
477 constraint.GetName(),
478 clearance,
479 actual ) );
480 drcItem->SetItems( aItem, aZone );
481 drcItem->SetViolatingRule( constraint.GetParentRule() );
482 reportTwoItemGeometry( drcItem, pos, aItem, aZone, aLayer, actual );
483 }
484 }
485
486 if( testHoles && aItem->HasHole() )
487 {
488 std::shared_ptr<SHAPE_SEGMENT> holeShape;
489
490 if( aItem->Type() == PCB_VIA_T )
491 {
492 if( aItem->GetLayerSet().Contains( aLayer ) )
493 holeShape = aItem->GetEffectiveHoleShape();
494 }
495 else
496 {
497 holeShape = aItem->GetEffectiveHoleShape();
498 }
499
500 if( holeShape )
501 {
502 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, aItem, aZone, aLayer );
503 clearance = constraint.GetValue().Min();
504
505 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
506 {
507 if( zoneTree->QueryColliding( itemBBox, holeShape.get(), aLayer, sub_e( clearance ), &actual, &pos ) )
508 {
509 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
510 drcItem->SetErrorDetail( formatMsg( _( "(%s clearance %s; actual %s)" ),
511 constraint.GetName(),
512 clearance,
513 actual ) );
514 drcItem->SetItems( aItem, aZone );
515 drcItem->SetViolatingRule( constraint.GetParentRule() );
516
517 std::shared_ptr<SHAPE> zoneShape = aZone->GetEffectiveShape( aLayer );
518 reportTwoShapeGeometry( drcItem, pos, holeShape.get(), zoneShape.get(), aLayer, actual );
519 }
520 }
521 }
522 }
523}
524
525
526/*
527 * We have to special-case knockout text as it's most often knocked-out of a zone, so it's
528 * presumed to collide with one. However, if it collides with more than one, and they have
529 * different nets, then we have a short.
530 */
532 NETINFO_ITEM** aInheritedNet,
533 ZONE* aZone )
534{
535 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
536 bool testShorts = !m_drcEngine->IsErrorLimitExceeded( DRCE_SHORTING_ITEMS );
537
538 if( !testClearance && !testShorts )
539 return;
540
541 PCB_LAYER_ID layer = aText->GetLayer();
542
543 if( !aZone->GetLayerSet().test( layer ) )
544 return;
545
546 BOX2I itemBBox = aText->GetBoundingBox();
547 BOX2I worstCaseBBox = itemBBox;
548
549 worstCaseBBox.Inflate( m_board->m_DRCMaxClearance );
550
551 if( !worstCaseBBox.Intersects( aZone->GetBoundingBox() ) )
552 return;
553
554 DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ aZone ].get();
555
556 if( !zoneTree )
557 return;
558
559 std::shared_ptr<SHAPE> itemShape = aText->GetEffectiveShape( layer, FLASHING::DEFAULT );
560
561 if( *aInheritedNet == nullptr )
562 {
563 if( zoneTree->QueryColliding( itemBBox, itemShape.get(), layer ) )
564 *aInheritedNet = aZone->GetNet();
565 }
566
567 if( *aInheritedNet == aZone->GetNet() )
568 return;
569
570 DRC_CONSTRAINT constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aText, aZone, layer );
571 int clearance = constraint.GetValue().Min();
572 int actual;
573 VECTOR2I pos;
574
575 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance >= 0 )
576 {
577 if( zoneTree->QueryColliding( itemBBox, itemShape.get(), layer, sub_e( clearance ), &actual, &pos ) )
578 {
579 std::shared_ptr<DRC_ITEM> drcItem;
580
581 if( testShorts && actual == 0 && *aInheritedNet )
582 {
584 drcItem->SetErrorDetail( wxString::Format( _( "(nets %s and %s)" ),
585 ( *aInheritedNet )->GetNetname(),
586 aZone->GetNetname() ) );
587 }
588 else
589 {
590 drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
591 drcItem->SetErrorDetail( formatMsg( _( "(%s clearance %s; actual %s)" ),
592 constraint.GetName(),
593 clearance,
594 actual ) );
595 }
596
597 drcItem->SetItems( aText, aZone );
598 drcItem->SetViolatingRule( constraint.GetParentRule() );
599 reportTwoItemGeometry( drcItem, pos, aText, aZone, layer, actual );
600 }
601 }
602}
603
604
606{
607 std::map<BOARD_ITEM*, int> freePadsUsageMap;
608 std::mutex freePadsUsageMapMutex;
609 std::atomic<size_t> done( 0 );
610 size_t count = m_board->Tracks().size();
611
612 REPORT_AUX( wxString::Format( wxT( "Testing %d tracks & vias..." ), count ) );
613
614 LSET boardCopperLayers = LSET::AllCuMask( m_board->GetCopperLayerCount() );
615
616 auto testTrack =
617 [&]( const int trackIdx )
618 {
619 PCB_TRACK* track = m_board->Tracks()[trackIdx];
620
621 for( PCB_LAYER_ID layer : LSET( track->GetLayerSet() & boardCopperLayers ) )
622 {
623 std::shared_ptr<SHAPE> trackShape = track->GetEffectiveShape( layer );
624
625 m_board->m_CopperItemRTreeCache->QueryColliding( track, layer, layer,
626 // Filter:
627 [&]( BOARD_ITEM* other ) -> bool
628 {
629 if( other->IsConnected()
630 && static_cast<BOARD_CONNECTED_ITEM*>( other )->GetNetCode()
631 == track->GetNetCode() )
632 {
633 return false;
634 }
635
636 // For track-vs-track pairs, use pointer ordering to ensure each
637 // pair is tested exactly once across all threads, eliminating the
638 // need for a shared checkedPairs mutex.
639 KICAD_T otherType = other->Type();
640
641 if( ( otherType == PCB_TRACE_T || otherType == PCB_ARC_T
642 || otherType == PCB_VIA_T )
643 && static_cast<void*>( track ) > static_cast<void*>( other ) )
644 {
645 return false;
646 }
647
648 return true;
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( !testSingleLayerItemAgainstItem( track, trackShape.get(),
676 layer, other ) )
677 {
678 if( !m_drcEngine->GetReportAllTrackErrors() )
679 return false;
680 }
681
682 return !m_drcEngine->IsCancelled();
683 },
684 m_board->m_DRCMaxClearance );
685
686 auto zoneIt = m_board->m_DRCCopperZonesByLayer.find( layer );
687
688 if( zoneIt != m_board->m_DRCCopperZonesByLayer.end() )
689 {
690 for( ZONE* zone : zoneIt->second )
691 {
692 testItemAgainstZone( track, zone, layer );
693
694 if( m_drcEngine->IsCancelled() )
695 break;
696 }
697 }
698 }
699
700 done.fetch_add( 1 );
701 };
702
704
705 auto track_futures = tp.submit_loop( 0, m_board->Tracks().size(), testTrack,
706 m_board->Tracks().size() );
707
708 while( done < count )
709 {
710 reportProgress( done, count );
711
712 if( m_drcEngine->IsCancelled() )
713 {
714 // Wait for the submitted loop tasks to finish
715 track_futures.wait();
716 break;
717 }
718
719 std::this_thread::sleep_for( std::chrono::milliseconds( 250 ) );
720 }
721}
722
723
725 PCB_LAYER_ID aLayer,
726 BOARD_ITEM* other )
727{
728 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
729 bool testShorting = !m_drcEngine->IsErrorLimitExceeded( DRCE_SHORTING_ITEMS );
730 bool testHoles = !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE );
731
732 // Disable some tests for net-tie objects in a footprint
733 if( other->GetParent() == pad->GetParent() )
734 {
735 FOOTPRINT* fp = pad->GetParentFootprint();
736 std::map<wxString, int> padToNetTieGroupMap = fp->MapPadNumbersToNetTieGroups();
737 int padGroupIdx = padToNetTieGroupMap[ pad->GetNumber() ];
738
739 if( other->Type() == PCB_PAD_T )
740 {
741 PAD* otherPad = static_cast<PAD*>( other );
742
743 if( padGroupIdx >= 0 && padGroupIdx == padToNetTieGroupMap[ otherPad->GetNumber() ] )
744 testClearance = testShorting = false;
745
746 if( pad->SameLogicalPadAs( otherPad ) )
747 testHoles = false;
748 }
749
750 if( other->Type() == PCB_SHAPE_T && padGroupIdx >= 0 )
751 testClearance = testShorting = false;
752 }
753
754 BOARD_CONNECTED_ITEM* otherCItem = other->IsConnected()
755 ? static_cast<BOARD_CONNECTED_ITEM*>( other )
756 : nullptr;
757 PAD* otherPad = nullptr;
758 PCB_VIA* otherVia = nullptr;
759
760 if( other->Type() == PCB_PAD_T )
761 otherPad = static_cast<PAD*>( other );
762
763 if( other->Type() == PCB_VIA_T )
764 otherVia = static_cast<PCB_VIA*>( other );
765
766 if( !IsCopperLayer( aLayer ) )
767 testClearance = testShorting = false;
768
769 // A NPTH has no cylinder, but it may still have pads on some layers
770 if( pad->GetAttribute() == PAD_ATTRIB::NPTH && !pad->FlashLayer( aLayer ) )
771 testClearance = testShorting = false;
772
773 if( otherPad && otherPad->GetAttribute() == PAD_ATTRIB::NPTH && !otherPad->FlashLayer( aLayer ) )
774 testClearance = testShorting = false;
775
776 // Track clearances are tested in testTrackClearances()
777 if( other->Type() == PCB_TRACE_T || other->Type() == PCB_ARC_T || other->Type() == PCB_VIA_T )
778 testClearance = testShorting = false;
779
780 // Graphic clearances are tested in testGraphicClearances()
781 if( other->Type() == PCB_SHAPE_T || other->Type() == PCB_TEXTBOX_T )
782 testClearance = testShorting = false;
783
784 int padNet = pad->GetNetCode();
785 int otherNet = otherCItem ? otherCItem->GetNetCode() : 0;
786
787 // Other objects of the same (defined) net get a waiver on clearance and hole tests
788 if( otherNet && otherNet == padNet )
789 {
790 testClearance = testShorting = false;
791 testHoles = false;
792 }
793
794 if( !( pad->GetDrillSize().x > 0 )
795 && !( otherPad && otherPad->GetDrillSize().x > 0 )
796 && !( otherVia && otherVia->GetDrill() > 0 ) )
797 {
798 testHoles = false;
799 }
800
801 if( !testClearance && !testShorting && !testHoles )
802 return true;
803
804 std::shared_ptr<SHAPE> otherShape = other->GetEffectiveShape( aLayer );
805 DRC_CONSTRAINT constraint;
806 int clearance = 0;
807 int actual = 0;
808 VECTOR2I pos;
809 bool has_error = false;
810
811 if( otherPad && pad->SameLogicalPadAs( otherPad ) )
812 {
813 // If pads are equivalent (ie: from the same footprint with the same pad number)...
814 // ... and have "real" nets...
815 // then they must be the same net
816 if( testShorting )
817 {
818 if( pad->GetNetCode() == 0 || pad->GetNetCode() == otherPad->GetNetCode() )
819 return true;
820
821 if( pad->GetShortNetname().StartsWith( wxS( "unconnected-(" ) )
822 && otherPad->GetShortNetname().StartsWith( wxS( "unconnected-(" ) ) )
823 {
824 return true;
825 }
826
827 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SHORTING_ITEMS );
828 drcItem->SetErrorDetail( wxString::Format( _( "(nets %s and %s)" ),
829 pad->GetNetname(),
830 otherPad->GetNetname() ) );
831 drcItem->SetItems( pad, otherPad );
832 reportViolation( drcItem, otherPad->GetPosition(), aLayer );
833 has_error = true;
834 }
835
836 return !has_error;
837 }
838
839 if( testClearance || testShorting )
840 {
841 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, pad, other, aLayer );
842 clearance = constraint.GetValue().Min();
843
844 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
845 {
846 if( padShape->Collide( otherShape.get(), sub_e( clearance ), &actual, &pos ) )
847 {
848 if( m_drcEngine->IsNetTieExclusion( pad->GetNetCode(), aLayer, pos, other ) )
849 {
850 // Pads connected to pads of a net-tie footprint are allowed to collide
851 // with the net-tie footprint's graphics.
852 }
853 else if( actual == 0 && padNet && otherNet && testShorting )
854 {
855 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_SHORTING_ITEMS );
856 drcItem->SetErrorDetail( wxString::Format( _( "(nets %s and %s)" ),
857 pad->GetNetname(),
858 otherCItem->GetNetname() ) );
859 drcItem->SetItems( pad, other );
860 reportTwoPointGeometry( drcItem, pos, pos, pos, aLayer );
861 has_error = true;
862 testHoles = false; // No need for multiple violations
863 }
864 else if( testClearance )
865 {
866 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
867 drcItem->SetErrorDetail( formatMsg( _( "(%s clearance %s; actual %s)" ),
868 constraint.GetName(),
869 clearance,
870 actual ) );
871 drcItem->SetItems( pad, other );
872 drcItem->SetViolatingRule( constraint.GetParentRule() );
873 reportTwoItemGeometry( drcItem, pos, pad, other, aLayer, actual );
874 has_error = true;
875 testHoles = false; // No need for multiple violations
876 }
877 }
878 }
879 }
880
881 auto doTestHole =
882 [&]( BOARD_ITEM* item, SHAPE* shape, BOARD_ITEM* otherItem, SHAPE* aOtherShape, int aClearance )
883 {
884 if( shape->Collide( aOtherShape, sub_e( aClearance ), &actual, &pos ) )
885 {
886 std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_HOLE_CLEARANCE );
887 drcItem->SetErrorDetail( formatMsg( _( "(%s clearance %s; actual %s)" ),
888 constraint.GetName(),
889 aClearance,
890 actual ) );
891 drcItem->SetItems( item, otherItem );
892 drcItem->SetViolatingRule( constraint.GetParentRule() );
893 reportTwoShapeGeometry( drcItem, pos, shape, aOtherShape, aLayer, actual );
894 has_error = true;
895 testHoles = false; // No need for multiple violations
896 }
897 };
898
899 if( testHoles )
900 {
901 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, pad, other, aLayer );
902
903 if( constraint.GetSeverity() == RPT_SEVERITY_IGNORE )
904 testHoles = false;
905 }
906
907 if( testHoles && otherPad && otherPad->HasHole() )
908 {
909 clearance = constraint.GetValue().Min();
910
911 if( !pad->FlashLayer( aLayer ) )
912 clearance = 0;
913
914 if( clearance > 0 )
915 doTestHole( pad, padShape, otherPad, otherPad->GetEffectiveHoleShape().get(), clearance );
916 }
917
918 if( testHoles && otherVia && otherVia->HasHole() )
919 {
920 clearance = constraint.GetValue().Min();
921
922 if( !otherVia->IsOnLayer( aLayer ) )
923 clearance = 0;
924
925 if( clearance > 0 )
926 doTestHole( pad, padShape, otherVia, otherVia->GetEffectiveHoleShape().get(), clearance );
927 }
928
929 return !has_error;
930}
931
932
934{
936 std::atomic<size_t> done( 1 );
937
938 LSET boardCopperLayers = LSET::AllCuMask( m_board->GetCopperLayerCount() );
939
940 const auto fp_check =
941 [&]( size_t ii )
942 {
943 FOOTPRINT* footprint = m_board->Footprints()[ ii ];
944
945 for( PAD* pad : footprint->Pads() )
946 {
947 // Through-hole pads are tested per-layer, so overlapping TH pads
948 // may produce one violation per shared copper layer. This is
949 // intentional for parallelized DRC to avoid cross-layer shared state.
950 for( PCB_LAYER_ID layer : LSET( pad->GetLayerSet() & boardCopperLayers ) )
951 {
952 if( m_drcEngine->IsCancelled() )
953 return;
954
955 std::shared_ptr<SHAPE> padShape = pad->GetEffectiveShape( layer );
956
957 m_board->m_CopperItemRTreeCache->QueryColliding( pad, layer, layer,
958 // Filter:
959 [&]( BOARD_ITEM* other ) -> bool
960 {
961 // For pad-vs-pad pairs, use pointer ordering to ensure
962 // each pair is tested only once across all threads.
963 if( other->Type() == PCB_PAD_T
964 && static_cast<void*>( pad )
965 > static_cast<void*>( other ) )
966 {
967 return false;
968 }
969
970 return true;
971 },
972 // Visitor
973 [&]( BOARD_ITEM* other ) -> bool
974 {
975 testPadAgainstItem( pad, padShape.get(), layer, other );
976 return !m_drcEngine->IsCancelled();
977 },
978 m_board->m_DRCMaxClearance );
979
980 auto zoneIt = m_board->m_DRCCopperZonesByLayer.find( layer );
981
982 if( zoneIt != m_board->m_DRCCopperZonesByLayer.end() )
983 {
984 for( ZONE* zone : zoneIt->second )
985 {
986 testItemAgainstZone( pad, zone, layer );
987
988 if( m_drcEngine->IsCancelled() )
989 return;
990 }
991 }
992 }
993 }
994
995 done.fetch_add( 1 );
996 };
997
998 size_t numFootprints = m_board->Footprints().size();
999 auto returns = tp.submit_loop( 0, numFootprints, fp_check, numFootprints );
1000
1001 // Wait for all threads to finish
1002 for( size_t ii = 0; ii < returns.size(); ++ii )
1003 {
1004 while( returns[ii].wait_for( std::chrono::milliseconds( 250 ) ) != std::future_status::ready )
1005 reportProgress( done, numFootprints );
1006 }
1007}
1008
1009
1011{
1013 size_t count = m_board->Drawings().size();
1014 std::atomic<size_t> done( 1 );
1015
1016 for( FOOTPRINT* footprint : m_board->Footprints() )
1017 count += footprint->GraphicalItems().size();
1018
1019 REPORT_AUX( wxString::Format( wxT( "Testing %d graphics..." ), count ) );
1020
1021 auto isKnockoutText =
1022 []( BOARD_ITEM* item )
1023 {
1024 return item->Type() == PCB_TEXT_T && static_cast<PCB_TEXT*>( item )->IsKnockout();
1025 };
1026
1027 auto testGraphicAgainstZone =
1028 [this, isKnockoutText]( BOARD_ITEM* item )
1029 {
1030 if( item->Type() == PCB_REFERENCE_IMAGE_T )
1031 return;
1032
1033 if( !IsCopperLayer( item->GetLayer() ) )
1034 return;
1035
1036 // Knockout text is most often knocked-out of a zone, so it's presumed to
1037 // collide with one. However, if it collides with more than one, and they
1038 // have different nets, then we have a short.
1039 NETINFO_ITEM* inheritedNet = nullptr;
1040
1041 PCB_LAYER_ID layer = item->GetLayer();
1042 auto zoneIt = m_board->m_DRCCopperZonesByLayer.find( layer );
1043
1044 if( zoneIt != m_board->m_DRCCopperZonesByLayer.end() )
1045 {
1046 for( ZONE* zone : zoneIt->second )
1047 {
1048 if( isKnockoutText( item ) )
1049 testKnockoutTextAgainstZone( item, &inheritedNet, zone );
1050 else
1051 testItemAgainstZone( item, zone, layer );
1052
1053 if( m_drcEngine->IsCancelled() )
1054 return;
1055 }
1056 }
1057 };
1058
1059 auto testCopperGraphic =
1060 [this]( BOARD_ITEM* graphic )
1061 {
1062 PCB_LAYER_ID layer = graphic->GetLayer();
1063
1064 m_board->m_CopperItemRTreeCache->QueryColliding( graphic, layer, layer,
1065 // Filter:
1066 [&]( BOARD_ITEM* other ) -> bool
1067 {
1068 // Graphics are often compound shapes so ignore collisions
1069 // between shapes in a single footprint.
1070 if( graphic->Type() == PCB_SHAPE_T && other->Type() == PCB_SHAPE_T
1071 && graphic->GetParentFootprint()
1072 && graphic->GetParentFootprint()
1073 == other->GetParentFootprint() )
1074 {
1075 return false;
1076 }
1077
1078 // Track clearances are tested in testTrackClearances()
1079 if( other->Type() == PCB_TRACE_T || other->Type() == PCB_ARC_T
1080 || other->Type() == PCB_VIA_T )
1081 {
1082 return false;
1083 }
1084
1085 int graphicNet = graphic->IsConnected()
1086 ? static_cast<BOARD_CONNECTED_ITEM*>( graphic )->GetNetCode()
1087 : 0;
1088 int otherNet = other->IsConnected()
1089 ? static_cast<BOARD_CONNECTED_ITEM*>( other )->GetNetCode()
1090 : 0;
1091
1092 if( graphicNet && graphicNet == otherNet )
1093 return false;
1094
1095 // For graphic-graphic pairs, use pointer ordering for dedup
1096 if( ( other->Type() == PCB_SHAPE_T
1097 || other->Type() == PCB_TEXTBOX_T
1098 || other->Type() == PCB_BARCODE_T )
1099 && static_cast<void*>( graphic )
1100 > static_cast<void*>( other ) )
1101 {
1102 return false;
1103 }
1104
1105 return true;
1106 },
1107 // Visitor:
1108 [&]( BOARD_ITEM* other ) -> bool
1109 {
1110 testSingleLayerItemAgainstItem( graphic, graphic->GetEffectiveShape().get(),
1111 layer, other );
1112
1113 return !m_drcEngine->IsCancelled();
1114 },
1115 m_board->m_DRCMaxClearance );
1116 };
1117
1118 for( BOARD_ITEM* item : m_board->Drawings() )
1119 {
1120 (void)tp.submit_task(
1121 [this, item, &done, testGraphicAgainstZone, testCopperGraphic]()
1122 {
1123 if( !m_drcEngine->IsCancelled() )
1124 {
1125 testGraphicAgainstZone( item );
1126
1127 if( ( item->Type() == PCB_SHAPE_T || item->Type() == PCB_BARCODE_T )
1128 && item->IsOnCopperLayer() )
1129 {
1130 testCopperGraphic( static_cast<PCB_SHAPE*>( item ) );
1131 }
1132
1133 done.fetch_add( 1 );
1134 }
1135 } );
1136 }
1137
1138 for( FOOTPRINT* footprint : m_board->Footprints() )
1139 {
1140 (void)tp.submit_task(
1141 [this, footprint, &done, testGraphicAgainstZone, testCopperGraphic]()
1142 {
1143 for( BOARD_ITEM* item : footprint->GraphicalItems() )
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
1161 while( true )
1162 {
1163 reportProgress( done, count );
1164
1165 if( m_drcEngine->IsCancelled() )
1166 break;
1167
1168 if( tp.wait_for( std::chrono::milliseconds( 250 ) ) )
1169 break;
1170 }
1171}
1172
1173
1175{
1176 LSET boardCopperLayers = LSET::AllCuMask( m_board->GetCopperLayerCount() );
1177
1178 for( ZONE* teardrop : m_board->m_DRCCopperZones )
1179 {
1180 if( !teardrop->IsTeardropArea() )
1181 continue;
1182
1183 for( PCB_LAYER_ID layer : LSET( teardrop->GetLayerSet() & boardCopperLayers ) )
1184 {
1185 if( m_drcEngine->IsCancelled() )
1186 return;
1187
1188 auto zoneIt = m_board->m_DRCCopperZonesByLayer.find( layer );
1189
1190 if( zoneIt == m_board->m_DRCCopperZonesByLayer.end() )
1191 continue;
1192
1193 for( ZONE* zone : zoneIt->second )
1194 {
1195 if( zone == teardrop )
1196 continue;
1197
1198 // For teardrop-vs-teardrop pairs, use pointer ordering so each
1199 // pair is tested only once.
1200 if( zone->IsTeardropArea() && zone < teardrop )
1201 continue;
1202
1203 testItemAgainstZone( teardrop, zone, layer );
1204
1205 if( m_drcEngine->IsCancelled() )
1206 return;
1207 }
1208 }
1209 }
1210}
1211
1212
1214{
1215 using SEG_RTREE = KIRTREE::PACKED_RTREE<size_t, int, 2>;
1216
1217 bool testClearance = !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE );
1218 bool testIntersects = !m_drcEngine->IsErrorLimitExceeded( DRCE_ZONES_INTERSECT );
1219
1220 std::vector<std::map<PCB_LAYER_ID, std::vector<SEG>>> poly_segments( m_board->m_DRCCopperZones.size() );
1221 std::vector<std::map<PCB_LAYER_ID, SEG_RTREE>> seg_rtrees( m_board->m_DRCCopperZones.size() );
1222
1224 std::atomic<size_t> done( 0 );
1225 size_t count = 0;
1226
1227 auto reportZoneZoneViolation =
1228 [this]( ZONE* zoneA, ZONE* zoneB, VECTOR2I& pt, int actual, const DRC_CONSTRAINT& constraint,
1229 PCB_LAYER_ID layer ) -> void
1230 {
1231 std::shared_ptr<DRC_ITEM> drcItem;
1232
1233 if( constraint.IsNull() )
1234 {
1236 drcItem->SetErrorDetail( _( "(intersecting zones must have distinct priorities)" ) );
1237 drcItem->SetItems( zoneA, zoneB );
1238 reportViolation( drcItem, pt, layer );
1239 }
1240 else
1241 {
1242 drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
1243 drcItem->SetErrorDetail( formatMsg( _( "(%s clearance %s; actual %s)" ),
1244 constraint.GetName(),
1245 constraint.GetValue().Min(),
1246 std::max( actual, 0 ) ) );
1247 drcItem->SetItems( zoneA, zoneB );
1248 drcItem->SetViolatingRule( constraint.GetParentRule() );
1249 reportTwoItemGeometry( drcItem, pt, zoneA, zoneB, layer, actual );
1250 }
1251 };
1252
1253 auto checkZones =
1254 [this, testClearance, testIntersects, reportZoneZoneViolation,
1255 &poly_segments, &seg_rtrees, &done]
1256 ( int zoneA_idx, int zoneB_idx, bool sameNet, PCB_LAYER_ID layer ) -> void
1257 {
1258 ZONE* zoneA = m_board->m_DRCCopperZones[zoneA_idx];
1259 ZONE* zoneB = m_board->m_DRCCopperZones[zoneB_idx];
1260 int actual = 0;
1261 VECTOR2I pt;
1262
1263 if( sameNet && testIntersects )
1264 {
1265 if( zoneA->Outline()->Collide( zoneB->Outline(), 0, &actual, &pt ) )
1266 {
1267 done.fetch_add( 1 );
1268 reportZoneZoneViolation( zoneA, zoneB, pt, actual, DRC_CONSTRAINT(), layer );
1269 return;
1270 }
1271 }
1272 else if( !sameNet && testClearance )
1273 {
1274 DRC_CONSTRAINT constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, zoneA, zoneB, layer );
1275 int clearance = constraint.GetValue().Min();
1276
1277 if( constraint.GetSeverity() != RPT_SEVERITY_IGNORE && clearance > 0 )
1278 {
1279 std::vector<SEG>& refSegments = poly_segments[zoneA_idx][layer];
1280 std::vector<SEG>& testSegments = poly_segments[zoneB_idx][layer];
1281
1282 auto testIt = seg_rtrees[zoneB_idx].find( layer );
1283
1284 if( testIt != seg_rtrees[zoneB_idx].end() && !testIt->second.empty() )
1285 {
1286 const SEG_RTREE& testTree = testIt->second;
1287
1288 for( SEG& refSegment : refSegments )
1289 {
1290 int minX = std::min( refSegment.A.x, refSegment.B.x ) - clearance;
1291 int minY = std::min( refSegment.A.y, refSegment.B.y ) - clearance;
1292 int maxX = std::max( refSegment.A.x, refSegment.B.x ) + clearance;
1293 int maxY = std::max( refSegment.A.y, refSegment.B.y ) + clearance;
1294 int qmin[2] = { minX, minY };
1295 int qmax[2] = { maxX, maxY };
1296 bool found = false;
1297
1298 auto visitor = [&]( size_t segIdx ) -> bool
1299 {
1300 SEG& testSegment = testSegments[segIdx];
1301 int64_t dist_sq = 0;
1302 VECTOR2I other_pt;
1303
1304 refSegment.NearestPoints( testSegment, pt, other_pt, dist_sq );
1305 actual = std::floor( std::sqrt( dist_sq ) + 0.5 );
1306
1307 if( actual < clearance )
1308 {
1309 found = true;
1310 return false;
1311 }
1312
1313 return true;
1314 };
1315
1316 testTree.Search( qmin, qmax, visitor );
1317
1318 if( found )
1319 {
1320 done.fetch_add( 1 );
1321 reportZoneZoneViolation( zoneA, zoneB, pt, actual, constraint,
1322 layer );
1323 return;
1324 }
1325 }
1326 }
1327 }
1328 }
1329
1330 done.fetch_add( 1 );
1331 };
1332
1333 // Pre-sort zones into layers
1334 std::map<PCB_LAYER_ID, std::vector<size_t>> zone_idx_by_layer;
1335
1336 for ( size_t ii = 0; ii < m_board->m_DRCCopperZones.size(); ii++ )
1337 {
1338 ZONE* zone = m_board->m_DRCCopperZones[ii];
1339
1340 // Teardrop areas are tested as tracks, not zones
1341 if( zone->IsTeardropArea() )
1342 continue;
1343
1344 for( PCB_LAYER_ID layer : zone->GetLayerSet() )
1345 {
1346 if( !IsCopperLayer( layer ) )
1347 continue;
1348
1349 zone_idx_by_layer[layer].push_back( ii );
1350 }
1351 }
1352
1353 for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, m_board->GetCopperLayerCount() ) )
1354 {
1355 // Skip over layers not used on the current board
1356 if( !m_board->IsLayerEnabled( layer ) )
1357 continue;
1358
1359 for( size_t ii : zone_idx_by_layer[layer] )
1360 {
1361 if( SHAPE_POLY_SET* poly = m_board->m_DRCCopperZones[ii]->GetFill( layer ) )
1362 {
1363 std::vector<SEG>& zone_layer_poly_segs = poly_segments[ii][layer];
1364 zone_layer_poly_segs.reserve( poly->FullPointCount() );
1365
1366 for( auto it = poly->IterateSegmentsWithHoles(); it; it++ )
1367 {
1368 SEG seg = *it;
1369
1370 if( seg.A.x > seg.B.x )
1371 seg.Reverse();
1372
1373 zone_layer_poly_segs.push_back( seg );
1374 }
1375
1376 SEG_RTREE::Builder builder;
1377 builder.Reserve( zone_layer_poly_segs.size() );
1378
1379 for( size_t si = 0; si < zone_layer_poly_segs.size(); ++si )
1380 {
1381 const SEG& seg = zone_layer_poly_segs[si];
1382 int smin[2] = { std::min( seg.A.x, seg.B.x ), std::min( seg.A.y, seg.B.y ) };
1383 int smax[2] = { std::max( seg.A.x, seg.B.x ), std::max( seg.A.y, seg.B.y ) };
1384 builder.Add( smin, smax, si );
1385 }
1386
1387 seg_rtrees[ii][layer] = builder.Build();
1388 }
1389 }
1390
1391 for( auto it_a = zone_idx_by_layer[layer].begin(); it_a != zone_idx_by_layer[layer].end(); ++it_a )
1392 {
1393 size_t ia = *it_a;
1394 ZONE* zoneA = m_board->m_DRCCopperZones[ia];
1395
1396 for( auto it_a2 = std::next( it_a ); it_a2 != zone_idx_by_layer[layer].end(); ++it_a2 )
1397 {
1398 size_t ia2 = *it_a2;
1399 ZONE* zoneB = m_board->m_DRCCopperZones[ia2];
1400
1401 bool sameNet = zoneA->GetNetCode() == zoneB->GetNetCode() && zoneA->GetNetCode() >= 0;
1402
1403 if( sameNet && zoneA->GetAssignedPriority() != zoneB->GetAssignedPriority() )
1404 continue;
1405
1406 // rule areas may overlap at will
1407 if( zoneA->GetIsRuleArea() || zoneB->GetIsRuleArea() )
1408 continue;
1409
1410 // Examine a candidate zone: compare zoneB to zoneA
1411 SHAPE_POLY_SET* polyA = nullptr;
1412 SHAPE_POLY_SET* polyB = nullptr;
1413
1414 if( sameNet )
1415 {
1416 polyA = zoneA->Outline();
1417 polyB = zoneB->Outline();
1418 }
1419 else
1420 {
1421 polyA = zoneA->GetFill( layer );
1422 polyB = zoneB->GetFill( layer );
1423 }
1424
1425 if( !polyA || !polyB
1426 || !polyA->BBoxFromCaches().Intersects( polyB->BBoxFromCaches() ) )
1427 continue;
1428
1429 count++;
1430 (void)tp.submit_task(
1431 [checkZones, ia, ia2, sameNet, layer]()
1432 {
1433 checkZones( ia, ia2, sameNet, layer );
1434 } );
1435 }
1436 }
1437 }
1438
1439 while( true )
1440 {
1441 reportProgress( done, count );
1442
1443 if( m_drcEngine->IsCancelled() )
1444 break;
1445
1446 if( tp.wait_for( std::chrono::milliseconds( 250 ) ) )
1447 break;
1448 }
1449}
1450
1451namespace detail
1452{
1454}
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:84
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition board_item.h:268
virtual bool IsConnected() const
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
Definition board_item.h:158
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:288
BOARD_ITEM_CONTAINER * GetParent() const
Definition board_item.h:234
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const
virtual bool HasHole() const
Definition board_item.h:180
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:204
SEVERITY GetSeverity() const
Definition drc_rule.h:217
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:196
DRC_RULE * GetParentRule() const
Definition drc_rule.h:200
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition drc_item.cpp:407
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:229
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:120
const KIID m_Uuid
Definition eda_item.h:528
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:112
std::vector< PAD * > GetNetTiePads(PAD *aPad) const
std::map< wxString, int > MapPadNumbersToNetTieGroups() const
std::deque< PAD * > & Pads()
Definition footprint.h:326
bool IsNetTie() const
Definition footprint.h:450
Static (immutable) packed R-tree built via Hilbert-curve bulk loading.
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:608
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:50
const wxString & GetNetname() const
Definition netinfo.h:103
int GetNetCode() const
Definition netinfo.h:97
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:439
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:362
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:1066
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:97
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:94
int GetDrill() const
Return the local drill setting for this PCB_VIA.
Definition pcb_track.h:758
std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const override
bool HasHole() const override
Definition pcb_track.h:476
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:716
const BOX2I GetBoundingBox() const override
Definition zone.cpp:651
SHAPE_POLY_SET * Outline()
Definition zone.h:336
SHAPE_POLY_SET * GetFill(PCB_LAYER_ID aLayer)
Definition zone.h:609
bool IsTeardropArea() const
Definition zone.h:691
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition zone.h:137
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:1643
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:55
@ HOLE_CLEARANCE_CONSTRAINT
Definition drc_rule.h:57
#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:679
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)
VECTOR2I end
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
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:75
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:85
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:94
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:90
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:89
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:86
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:98
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:84
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:95
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:93
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687