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