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