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