KiCad PCB EDA Suite
Loading...
Searching...
No Matches
drc_test_provider_solder_mask.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>
27#include <footprint.h>
28#include <pad.h>
29#include <pcb_track.h>
30#include <pcb_text.h>
31#include <thread_pool.h>
32#include <zone.h>
33#include <geometry/seg.h>
34#include <drc/drc_engine.h>
35#include <drc/drc_item.h>
36#include <drc/drc_rule.h>
38#include <drc/drc_rtree.h>
39
40/*
41 Solder mask tests. Checks for silkscreen which is clipped by mask openings and for bridges
42 between mask apertures with different nets.
43 Errors generated:
44 - DRCE_SILK_MASK_CLEARANCE
45 - DRCE_SOLDERMASK_BRIDGE
46*/
47
49{
50public:
52 m_board( nullptr ),
53 m_webWidth( 0 ),
54 m_maxError( 0 ),
56 {
57 m_bridgeRule.m_Name = _( "board setup solder mask min width" );
58 }
59
60 virtual ~DRC_TEST_PROVIDER_SOLDER_MASK() = default;
61
62 virtual bool Run() override;
63
64 virtual const wxString GetName() const override { return wxT( "solder_mask_issues" ); };
65
66private:
67 void addItemToRTrees( BOARD_ITEM* aItem );
68 void buildRTrees();
69
71 void testMaskBridges();
72
73 void testItemAgainstItems( BOARD_ITEM* aItem, const BOX2I& aItemBBox,
74 PCB_LAYER_ID aRefLayer, PCB_LAYER_ID aTargetLayer );
75 void testMaskItemAgainstZones( BOARD_ITEM* item, const BOX2I& itemBBox,
76 PCB_LAYER_ID refLayer, PCB_LAYER_ID targetLayer );
77
78 bool checkMaskAperture( BOARD_ITEM* aMaskItem, BOARD_ITEM* aTestItem, PCB_LAYER_ID aTestLayer,
79 int aTestNet, BOARD_ITEM** aCollidingItem );
80
81 bool checkItemMask( BOARD_ITEM* aMaskItem, int aTestNet );
82
83private:
85
90
91 std::unique_ptr<DRC_RTREE> m_fullSolderMaskRTree;
92 std::unique_ptr<DRC_RTREE> m_itemTree;
93
95 std::unordered_map<PTR_PTR_CACHE_KEY, LSET> m_checkedPairs;
96
97 // Shapes used to define solder mask apertures don't have nets, so we assign them the
98 // first object+net that bridges their aperture (after which any other nets will generate
99 // violations).
100 std::unordered_map<PTR_LAYER_CACHE_KEY, std::pair<BOARD_ITEM*, int>> m_maskApertureNetMap;
101};
102
103
105{
106 ZONE* solderMask = m_board->m_SolderMaskBridges;
107
108 if( aItem->Type() == PCB_ZONE_T )
109 {
110 ZONE* zone = static_cast<ZONE*>( aItem );
111
112 for( PCB_LAYER_ID layer : { F_Mask, B_Mask } )
113 {
114 if( zone->IsOnLayer( layer ) )
115 {
116 solderMask->GetFill( layer )->BooleanAdd( *zone->GetFilledPolysList( layer ) );
117 }
118 }
119 }
120 else if( aItem->Type() == PCB_PAD_T )
121 {
122 for( PCB_LAYER_ID layer : { F_Mask, B_Mask } )
123 {
124 if( aItem->IsOnLayer( layer ) )
125 {
126 PAD* pad = static_cast<PAD*>( aItem );
127 int clearance = ( m_webWidth / 2 ) + pad->GetSolderMaskExpansion( layer );
128
129 aItem->TransformShapeToPolygon( *solderMask->GetFill( layer ), layer, clearance,
131
132 m_itemTree->Insert( aItem, layer, m_largestClearance );
133 }
134 }
135 }
136 else if( aItem->Type() == PCB_VIA_T )
137 {
138 for( PCB_LAYER_ID layer : { F_Mask, B_Mask } )
139 {
140 if( aItem->IsOnLayer( layer ) )
141 {
142 PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
143 int clearance = ( m_webWidth / 2 ) + via->GetSolderMaskExpansion();
144
145 via->TransformShapeToPolygon( *solderMask->GetFill( layer ), layer, clearance,
147
148 m_itemTree->Insert( aItem, layer, m_largestClearance );
149 }
150 }
151 }
152 else if( aItem->Type() == PCB_FIELD_T || aItem->Type() == PCB_TEXT_T )
153 {
154 for( PCB_LAYER_ID layer : { F_Mask, B_Mask } )
155 {
156 if( aItem->IsOnLayer( layer ) )
157 {
158 const PCB_TEXT* text = static_cast<const PCB_TEXT*>( aItem );
159
160 text->TransformTextToPolySet( *solderMask->GetFill( layer ),
162
163 m_itemTree->Insert( aItem, layer, m_largestClearance );
164 }
165 }
166 }
167 else
168 {
169 for( PCB_LAYER_ID layer : { F_Mask, B_Mask } )
170 {
171 if( aItem->IsOnLayer( layer ) )
172 {
173 aItem->TransformShapeToPolygon( *solderMask->GetFill( layer ), layer,
175
176 m_itemTree->Insert( aItem, layer, m_largestClearance );
177 }
178 }
179 }
180}
181
182
184{
185 ZONE* solderMask = m_board->m_SolderMaskBridges;
186 LSET layers( { F_Mask, B_Mask, F_Cu, B_Cu } );
187
188 const size_t progressDelta = 500;
189 int count = 0;
190 int ii = 0;
191
192 solderMask->GetFill( F_Mask )->RemoveAllContours();
193 solderMask->GetFill( B_Mask )->RemoveAllContours();
194
195 m_fullSolderMaskRTree = std::make_unique<DRC_RTREE>();
196 m_itemTree = std::make_unique<DRC_RTREE>();
197
199 [&]( BOARD_ITEM* item ) -> bool
200 {
201 ++count;
202 return true;
203 } );
204
206 [&]( BOARD_ITEM* item ) -> bool
207 {
208 if( !reportProgress( ii++, count, progressDelta ) )
209 return false;
210
211 addItemToRTrees( item );
212 return true;
213 } );
214
215 solderMask->GetFill( F_Mask )->Simplify();
216 solderMask->GetFill( B_Mask )->Simplify();
217
218 if( m_webWidth > 0 )
219 {
220 solderMask->GetFill( F_Mask )->Deflate( m_webWidth / 2, CORNER_STRATEGY::CHAMFER_ALL_CORNERS, m_maxError );
221 solderMask->GetFill( B_Mask )->Deflate( m_webWidth / 2, CORNER_STRATEGY::CHAMFER_ALL_CORNERS, m_maxError );
222 }
223
224 solderMask->SetFillFlag( F_Mask, true );
225 solderMask->SetFillFlag( B_Mask, true );
226 solderMask->SetIsFilled( true );
227
228 solderMask->CacheTriangulation();
229
230 m_fullSolderMaskRTree->Insert( solderMask, F_Mask );
231 m_fullSolderMaskRTree->Insert( solderMask, B_Mask );
232
233 m_checkedPairs.clear();
234}
235
236
238{
239 LSET silkLayers( { F_SilkS, B_SilkS } );
240
241 // If we have no minimum web width then we delegate to the silk checker which does object-to-object
242 // testing (instead of object-to-solder-mask-zone-fill checking that we do here).
243 if( m_webWidth <= 0 )
244 return;
245
246 const size_t progressDelta = 250;
247 int count = 0;
248 int ii = 0;
249
251 [&]( BOARD_ITEM* item ) -> bool
252 {
253 ++count;
254 return true;
255 } );
256
258 [&]( BOARD_ITEM* item ) -> bool
259 {
261 return false;
262
263 if( !reportProgress( ii++, count, progressDelta ) )
264 return false;
265
266 if( isInvisibleText( item ) )
267 return true;
268
269 for( PCB_LAYER_ID layer : silkLayers )
270 {
271 if( !item->IsOnLayer( layer ) )
272 continue;
273
274 PCB_LAYER_ID maskLayer = layer == F_SilkS ? F_Mask : B_Mask;
275 BOX2I itemBBox = item->GetBoundingBox();
277 item, nullptr, maskLayer );
278 int clearance = constraint.GetValue().Min();
279 int actual;
280 VECTOR2I pos;
281
282 if( constraint.GetSeverity() == RPT_SEVERITY_IGNORE || clearance < 0 )
283 return true;
284
285 std::shared_ptr<SHAPE> itemShape = item->GetEffectiveShape( layer );
286
287 if( m_fullSolderMaskRTree->QueryColliding( itemBBox, itemShape.get(), maskLayer,
288 clearance, &actual, &pos ) )
289 {
290 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_SILK_MASK_CLEARANCE );
291
292 if( clearance > 0 )
293 {
294 wxString msg = formatMsg( _( "(%s clearance %s; actual %s)" ),
295 constraint.GetName(),
296 clearance,
297 actual );
298
299 drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
300 }
301
302 drce->SetItems( item );
303 drce->SetViolatingRule( constraint.GetParentRule() );
304
305 reportViolation( drce, pos, layer );
306 }
307 }
308
309 return true;
310 } );
311}
312
313
315{
316 if( aItem->Type() == PCB_PAD_T )
317 {
318 PAD* pad = static_cast<PAD*>( aItem );
319
320 // TODO(JE) padstacks
321 if( pad->GetAttribute() == PAD_ATTRIB::NPTH
322 && ( pad->GetShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::CIRCLE
323 || pad->GetShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::OVAL )
324 && pad->GetSize( PADSTACK::ALL_LAYERS ).x <= pad->GetDrillSize().x
325 && pad->GetSize( PADSTACK::ALL_LAYERS ).y <= pad->GetDrillSize().y )
326 {
327 return true;
328 }
329 }
330
331 return false;
332}
333
334
335// Simple mask apertures aren't associated with copper items, so they only constitute a bridge
336// when they expose other copper items having at least two distinct nets. We use a map to record
337// the first net exposed by each mask aperture (on each copper layer).
338//
339// Note that this algorithm is also used for free pads.
340
342{
343 if( aItem->Type() == PCB_PAD_T && static_cast<PAD*>( aItem )->IsFreePad() )
344 return true;
345
346 static const LSET saved( { F_Mask, B_Mask } );
347
348 LSET maskLayers = aItem->GetLayerSet() & saved;
349 LSET copperLayers = ( aItem->GetLayerSet() & ~saved ) & LSET::AllCuMask();
350
351 return maskLayers.count() > 0 && copperLayers.count() == 0;
352}
353
354
356 PCB_LAYER_ID aTestLayer, int aTestNet,
357 BOARD_ITEM** aCollidingItem )
358{
359 if( aTestLayer == F_Mask && !aTestItem->IsOnLayer( PADSTACK::ALL_LAYERS ) )
360 return false;
361
362 if( aTestLayer == B_Mask && !aTestItem->IsOnLayer( B_Cu ) )
363 return false;
364
365 PCB_LAYER_ID maskLayer = IsFrontLayer( aTestLayer ) ? F_Mask : B_Mask;
366
367 FOOTPRINT* fp = aMaskItem->GetParentFootprint();
368
369 // Mask apertures in footprints which allow soldermask bridges are ignored entirely.
370 if( fp && fp->AllowSolderMaskBridges() )
371 return false;
372
373 PTR_LAYER_CACHE_KEY key = { aMaskItem, maskLayer };
374
375 auto ii = m_maskApertureNetMap.find( key );
376
377 if( ii == m_maskApertureNetMap.end() )
378 {
379 m_maskApertureNetMap[ key ] = { aTestItem, aTestNet };
380
381 // First net; no bridge yet....
382 return false;
383 }
384
385 auto& [cacheKey, cacheEntry] = *ii;
386 auto& [alreadyEncounteredItem, encounteredItemNet] = cacheEntry;
387
388 if( encounteredItemNet == aTestNet && aTestNet >= 0 )
389 {
390 // Same net; still no bridge...
391 return false;
392 }
393
394 if( fp && aTestItem->GetParentFootprint() == fp )
395 {
396 std::map<wxString, int> padToNetTieGroupMap = fp->MapPadNumbersToNetTieGroups();
397 PAD* padA = nullptr;
398 PAD* padB = nullptr;
399
400 if( alreadyEncounteredItem->Type() == PCB_PAD_T )
401 padA = static_cast<PAD*>( alreadyEncounteredItem );
402
403 if( aTestItem->Type() == PCB_PAD_T )
404 padB = static_cast<PAD*>( aTestItem );
405
406 if( padA && padB && ( padA->SameLogicalPadAs( padB ) || padA->SharesNetTieGroup( padB ) ) )
407 {
408 return false;
409 }
410 else if( padA && aTestItem->Type() == PCB_SHAPE_T )
411 {
412 if( padToNetTieGroupMap.contains( padA->GetNumber() ) )
413 return false;
414 }
415 else if( padB && alreadyEncounteredItem->Type() == PCB_SHAPE_T )
416 {
417 if( padToNetTieGroupMap.contains( padB->GetNumber() ) )
418 return false;
419 }
420 }
421
422 *aCollidingItem = alreadyEncounteredItem;
423 return true;
424}
425
426
428{
429 if( FOOTPRINT* fp = aMaskItem->GetParentFootprint() )
430 {
431 // If we're allowing bridges then we're allowing bridges. Nothing to check.
432 if( fp->AllowSolderMaskBridges() )
433 return false;
434
435 // Graphic items are used to implement net-ties between pads of a group within a net-tie
436 // footprint. They must be allowed to intrude into their pad's mask aperture.
437 if( aTestNet < 0 && aMaskItem->Type() == PCB_PAD_T && fp->IsNetTie() )
438 {
439 std::map<wxString, int> padNumberToGroupIdxMap = fp->MapPadNumbersToNetTieGroups();
440
441 if( padNumberToGroupIdxMap[ static_cast<PAD*>( aMaskItem )->GetNumber() ] >= 0 )
442 return false;
443 }
444 }
445
446 return true;
447}
448
449
451 PCB_LAYER_ID aRefLayer,
452 PCB_LAYER_ID aTargetLayer )
453{
454 PAD* pad = aItem->Type() == PCB_PAD_T ? static_cast<PAD*>( aItem ) : nullptr;
455 PCB_VIA* via = aItem->Type() == PCB_VIA_T ? static_cast<PCB_VIA*>( aItem ) : nullptr;
456 int itemNet = -1;
457
458 if( aItem->IsConnected() )
459 itemNet = static_cast<BOARD_CONNECTED_ITEM*>( aItem )->GetNetCode();
460
461 std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aRefLayer );
462
463 m_itemTree->QueryColliding( aItem, aRefLayer, aTargetLayer,
464 // Filter:
465 [&]( BOARD_ITEM* other ) -> bool
466 {
467 FOOTPRINT* itemFP = aItem->GetParentFootprint();
468 PAD* otherPad = other->Type() == PCB_PAD_T ? static_cast<PAD*>( other )
469 : nullptr;
470 int otherNet = -1;
471
472 if( other->IsConnected() )
473 otherNet = static_cast<BOARD_CONNECTED_ITEM*>( other )->GetNetCode();
474
475 if( otherNet > 0 && otherNet == itemNet )
476 return false;
477
478 if( isNullAperture( other ) )
479 return false;
480
481 if( itemFP && itemFP == other->GetParentFootprint() )
482 {
483 // Board-wide exclusion
484 if( BOARD* board = itemFP->GetBoard() )
485 {
486 if( board->GetDesignSettings().m_AllowSoldermaskBridgesInFPs )
487 return false;
488 }
489
490 // Footprint-specific exclusion
491 if( itemFP->AllowSolderMaskBridges() )
492 return false;
493 }
494
495 if( pad && otherPad && ( pad->SameLogicalPadAs( otherPad )
496 || pad->SharesNetTieGroup( otherPad ) ) )
497 {
498 return false;
499 }
500
501 BOARD_ITEM* a = aItem;
502 BOARD_ITEM* b = other;
503
504 // store canonical order so we don't collide in both directions (a:b and b:a)
505 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
506 std::swap( a, b );
507
508 {
509 std::lock_guard<std::mutex> lock( m_checkedPairsMutex );
510 auto it = m_checkedPairs.find( { a, b } );
511
512 if( it != m_checkedPairs.end() && it->second.test( aTargetLayer ) )
513 {
514 return false;
515 }
516 else
517 {
518 m_checkedPairs[{ a, b }].set( aTargetLayer );
519 return true;
520 }
521 }
522 },
523 // Visitor:
524 [&]( BOARD_ITEM* other ) -> bool
525 {
526 PAD* otherPad = other->Type() == PCB_PAD_T ? static_cast<PAD*>( other )
527 : nullptr;
528 PCB_VIA* otherVia = other->Type() == PCB_VIA_T ? static_cast<PCB_VIA*>( other )
529 : nullptr;
530 auto otherShape = other->GetEffectiveShape( aTargetLayer );
531 int otherNet = -1;
532
533 if( other->IsConnected() )
534 otherNet = static_cast<BOARD_CONNECTED_ITEM*>( other )->GetNetCode();
535
536 int actual;
537 VECTOR2I pos;
538 int clearance = 0;
539
540 if( aRefLayer == F_Mask || aRefLayer == B_Mask )
541 {
542 // Aperture-to-aperture must enforce web-min-width
544 }
545 else // ( aRefLayer == F_Cu || aRefLayer == B_Cu )
546 {
547 // Copper-to-aperture uses the solder-mask-to-copper-clearance
549 }
550
551 if( pad )
552 clearance += pad->GetSolderMaskExpansion( PADSTACK::ALL_LAYERS );
553 else if( via && !via->IsTented( aRefLayer ) )
554 clearance += via->GetSolderMaskExpansion();
555
556 if( otherPad )
558 else if( otherVia && !otherVia->IsTented( aRefLayer ) )
559 clearance += otherVia->GetSolderMaskExpansion();
560
561 if( itemShape->Collide( otherShape.get(), clearance, &actual, &pos ) )
562 {
563 wxString msg;
564 BOARD_ITEM* colliding = nullptr;
565
566 if( aTargetLayer == F_Mask )
567 msg = _( "Front solder mask aperture bridges items with different nets" );
568 else
569 msg = _( "Rear solder mask aperture bridges items with different nets" );
570
571 // Simple mask apertures aren't associated with copper items, so they only
572 // constitute a bridge when they expose other copper items having at least
573 // two distinct nets.
574 if( isMaskAperture( aItem ) )
575 {
576 if( checkMaskAperture( aItem, other, aRefLayer, otherNet, &colliding ) )
577 {
579
580 drce->SetErrorMessage( msg );
581 drce->SetItems( aItem, colliding, other );
582 drce->SetViolatingRule( &m_bridgeRule );
583 reportViolation( drce, pos, aTargetLayer );
584 }
585 }
586 else if( isMaskAperture( other ) )
587 {
588 if( checkMaskAperture( other, aItem, aRefLayer, itemNet, &colliding ) )
589 {
591
592 drce->SetErrorMessage( msg );
593 drce->SetItems( other, colliding, aItem );
594 drce->SetViolatingRule( &m_bridgeRule );
595 reportViolation( drce, pos, aTargetLayer );
596 }
597 }
598 else if( checkItemMask( other, itemNet ) )
599 {
601
602 drce->SetErrorMessage( msg );
603 drce->SetItems( aItem, other );
604 drce->SetViolatingRule( &m_bridgeRule );
605 reportViolation( drce, pos, aTargetLayer );
606 }
607 }
608
609 return !m_drcEngine->IsCancelled();
610 },
612}
613
614
616 const BOX2I& aItemBBox,
617 PCB_LAYER_ID aMaskLayer,
618 PCB_LAYER_ID aTargetLayer )
619{
620 for( ZONE* zone : m_board->m_DRCCopperZones )
621 {
622 if( !zone->GetLayerSet().test( aTargetLayer ) )
623 continue;
624
625 int zoneNet = zone->GetNetCode();
626
627 if( aItem->IsConnected() )
628 {
629 BOARD_CONNECTED_ITEM* connectedItem = static_cast<BOARD_CONNECTED_ITEM*>( aItem );
630
631 if( zoneNet == connectedItem->GetNetCode() && zoneNet > 0 )
632 continue;
633 }
634
635 BOX2I inflatedBBox( aItemBBox );
637
638 if( aItem->Type() == PCB_PAD_T )
639 clearance += static_cast<PAD*>( aItem )->GetSolderMaskExpansion( PADSTACK::ALL_LAYERS );
640 else if( aItem->Type() == PCB_VIA_T )
641 clearance += static_cast<PCB_VIA*>( aItem )->GetSolderMaskExpansion();
642
643 inflatedBBox.Inflate( clearance );
644
645 if( !inflatedBBox.Intersects( zone->GetBoundingBox() ) )
646 continue;
647
648 DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ zone ].get();
649 int actual;
650 VECTOR2I pos;
651
652 std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aMaskLayer );
653
654 if( zoneTree && zoneTree->QueryColliding( aItemBBox, itemShape.get(), aTargetLayer,
655 clearance, &actual, &pos ) )
656 {
657 wxString msg;
658 BOARD_ITEM* colliding = nullptr;
659
660 if( aMaskLayer == F_Mask )
661 msg = _( "Front solder mask aperture bridges items with different nets" );
662 else
663 msg = _( "Rear solder mask aperture bridges items with different nets" );
664
665 // Simple mask apertures aren't associated with copper items, so they only constitute
666 // a bridge when they expose other copper items having at least two distinct nets.
667 if( isMaskAperture( aItem ) && zoneNet >= 0 )
668 {
669 if( checkMaskAperture( aItem, zone, aTargetLayer, zoneNet, &colliding ) )
670 {
672
673 drce->SetErrorMessage( msg );
674 drce->SetItems( aItem, colliding, zone );
675 drce->SetViolatingRule( &m_bridgeRule );
676 reportViolation( drce, pos, aTargetLayer );
677 }
678 }
679 else
680 {
682
683 drce->SetErrorMessage( msg );
684 drce->SetItems( aItem, zone );
685 drce->SetViolatingRule( &m_bridgeRule );
686 reportViolation( drce, pos, aTargetLayer );
687 }
688 }
689
690 if( m_drcEngine->IsCancelled() )
691 return;
692 }
693}
694
695
697{
698 LSET copperAndMaskLayers( { F_Mask, B_Mask, F_Cu, B_Cu } );
699 std::atomic<int> count = 0;
700 std::vector<BOARD_ITEM*> test_items;
701
702 forEachGeometryItem( s_allBasicItemsButZones, copperAndMaskLayers,
703 [&]( BOARD_ITEM* item ) -> bool
704 {
705 test_items.push_back( item );
706 return true;
707 } );
708
710
711 auto returns = tp.parallelize_loop( test_items.size(), [&]( size_t a, size_t b ) -> bool
712 {
713 for( size_t i = a; i < b; ++i )
714 {
715 BOARD_ITEM* item = test_items[ i ];
716
717 if( m_drcEngine->IsErrorLimitExceeded( DRCE_SOLDERMASK_BRIDGE ) )
718 return false;
719
720 BOX2I itemBBox = item->GetBoundingBox();
721
722 if( item->IsOnLayer( F_Mask ) && !isNullAperture( item ) )
723 {
724 // Test for aperture-to-aperture collisions
725 testItemAgainstItems( item, itemBBox, F_Mask, F_Mask );
726
727 // Test for aperture-to-zone collisions
728 testMaskItemAgainstZones( item, itemBBox, F_Mask, F_Cu );
729 }
730 else if( item->IsOnLayer( PADSTACK::ALL_LAYERS ) )
731 {
732 // Test for copper-item-to-aperture collisions
733 testItemAgainstItems( item, itemBBox, F_Cu, F_Mask );
734 }
735
736 if( item->IsOnLayer( B_Mask ) && !isNullAperture( item ) )
737 {
738 // Test for aperture-to-aperture collisions
739 testItemAgainstItems( item, itemBBox, B_Mask, B_Mask );
740
741 // Test for aperture-to-zone collisions
742 testMaskItemAgainstZones( item, itemBBox, B_Mask, B_Cu );
743 }
744 else if( item->IsOnLayer( B_Cu ) )
745 {
746 // Test for copper-item-to-aperture collisions
747 testItemAgainstItems( item, itemBBox, B_Cu, B_Mask );
748 }
749
750 ++count;
751 }
752
753 return true;
754 } );
755
756 for( size_t i = 0; i < returns.size(); ++i )
757 {
758 auto& ret = returns[ i ];
759
760 if( !ret.valid() )
761 continue;
762
763 while( ret.wait_for( std::chrono::milliseconds( 100 ) ) == std::future_status::timeout )
764 {
765 reportProgress( count, test_items.size() );
766 }
767 }
768}
769
770
772{
775 {
776 REPORT_AUX( wxT( "Solder mask violations ignored. Tests not run." ) );
777 return true; // continue with other tests
778 }
779
784
785 for( FOOTPRINT* footprint : m_board->Footprints() )
786 {
787 for( PAD* pad : footprint->Pads() )
788 m_largestClearance = std::max( m_largestClearance, pad->GetSolderMaskExpansion( PADSTACK::ALL_LAYERS ) );
789 }
790
791 // Order is important here: m_webWidth must be added in before m_largestCourtyardClearance is
792 // maxed with the various SILK_CLEARANCE_CONSTRAINTS.
794
795 DRC_CONSTRAINT worstClearanceConstraint;
796
797 if( m_drcEngine->QueryWorstConstraint( SILK_CLEARANCE_CONSTRAINT, worstClearanceConstraint ) )
798 m_largestClearance = std::max( m_largestClearance, worstClearanceConstraint.m_Value.Min() );
799
800 if( !reportPhase( _( "Building solder mask..." ) ) )
801 return false; // DRC cancelled
802
803 m_checkedPairs.clear();
804 m_maskApertureNetMap.clear();
805
806 buildRTrees();
807
808 if( !reportPhase( _( "Checking solder mask to silk clearance..." ) ) )
809 return false; // DRC cancelled
810
812
813 if( !reportPhase( _( "Checking solder mask web integrity..." ) ) )
814 return false; // DRC cancelled
815
817
818 return !m_drcEngine->IsCancelled();
819}
820
821
822namespace detail
823{
825}
@ ERROR_OUTSIDE
Definition: approximation.h:33
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:79
virtual bool IsConnected() const
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
Definition: board_item.h:134
virtual void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const
Convert the item shape to a closed polygon.
Definition: board_item.cpp:303
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition: board_item.h:314
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.
Definition: board_item.cpp:326
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Definition: board_item.cpp:79
FOOTPRINT * GetParentFootprint() const
Definition: board_item.cpp:97
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:252
Information pertinent to a Pcbnew printed circuit board.
Definition: board.h:317
ZONE * m_SolderMaskBridges
Definition: board.h:1385
std::vector< ZONE * > m_DRCCopperZones
Definition: board.h:1382
const FOOTPRINTS & Footprints() const
Definition: board.h:358
std::unordered_map< ZONE *, std::unique_ptr< DRC_RTREE > > m_CopperZoneRTreeCache
Definition: board.h:1375
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:1024
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
MINOPTMAX< int > m_Value
Definition: drc_rule.h:202
BOARD * GetBoard() const
Definition: drc_engine.h:100
bool IsErrorLimitExceeded(int error_code)
DRC_CONSTRAINT EvalRules(DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
Definition: drc_engine.cpp:706
bool IsCancelled() const
bool QueryWorstConstraint(DRC_CONSTRAINT_T aRuleId, DRC_CONSTRAINT &aConstraint)
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition: drc_item.cpp:393
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
wxString m_Name
Definition: drc_rule.h:120
virtual const wxString GetName() const override
void testMaskItemAgainstZones(BOARD_ITEM *item, const BOX2I &itemBBox, PCB_LAYER_ID refLayer, PCB_LAYER_ID targetLayer)
virtual ~DRC_TEST_PROVIDER_SOLDER_MASK()=default
bool checkMaskAperture(BOARD_ITEM *aMaskItem, BOARD_ITEM *aTestItem, PCB_LAYER_ID aTestLayer, int aTestNet, BOARD_ITEM **aCollidingItem)
std::unique_ptr< DRC_RTREE > m_fullSolderMaskRTree
virtual bool Run() override
Run this provider against the given PCB with configured options (if any).
std::unordered_map< PTR_PTR_CACHE_KEY, LSET > m_checkedPairs
bool checkItemMask(BOARD_ITEM *aMaskItem, int aTestNet)
std::unordered_map< PTR_LAYER_CACHE_KEY, std::pair< BOARD_ITEM *, int > > m_maskApertureNetMap
void testItemAgainstItems(BOARD_ITEM *aItem, const BOX2I &aItemBBox, PCB_LAYER_ID aRefLayer, PCB_LAYER_ID aTargetLayer)
Represent a DRC "provider" which runs some DRC functions over a BOARD and spits out DRC_ITEM and posi...
static std::vector< KICAD_T > s_allBasicItemsButZones
virtual bool reportPhase(const wxString &aStageName)
virtual void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer, DRC_CUSTOM_MARKER_HANDLER *aCustomHandler=nullptr)
int forEachGeometryItem(const std::vector< KICAD_T > &aTypes, const LSET &aLayers, const std::function< bool(BOARD_ITEM *)> &aFunc)
static std::vector< KICAD_T > s_allBasicItems
DRC_ENGINE * m_drcEngine
bool isInvisibleText(const BOARD_ITEM *aItem) const
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
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:110
bool AllowSolderMaskBridges() const
Definition: footprint.h:299
std::map< wxString, int > MapPadNumbersToNetTieGroups() const
Definition: footprint.cpp:3250
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:37
static LSET AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition: lset.cpp:591
T Min() const
Definition: minoptmax.h:33
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition: padstack.h:145
Definition: pad.h:54
const wxString & GetNumber() const
Definition: pad.h:136
bool SameLogicalPadAs(const PAD *aOther) const
Before we had custom pad shapes it was common to have multiple overlapping pads to represent a more c...
Definition: pad.h:159
int GetSolderMaskExpansion(PCB_LAYER_ID aLayer) const
Definition: pad.cpp:1177
bool IsFreePad() const
Definition: pad.cpp:284
bool SharesNetTieGroup(const PAD *aOther) const
Definition: pad.cpp:261
int GetSolderMaskExpansion() const
Definition: pcb_track.cpp:1120
void RemoveAllContours()
Remove all outlines & holes (clears) the polygon set.
void BooleanAdd(const SHAPE_POLY_SET &b)
Perform boolean polyset union.
void Simplify()
Simplify the polyset (merges overlapping polys, eliminates degeneracy/self-intersections)
void Deflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError)
Handle a list of polygons defining a copper zone.
Definition: zone.h:74
const std::shared_ptr< SHAPE_POLY_SET > & GetFilledPolysList(PCB_LAYER_ID aLayer) const
Definition: zone.h:600
void CacheTriangulation(PCB_LAYER_ID aLayer=UNDEFINED_LAYER)
Create a list of triangles that "fill" the solid areas used for instance to draw these solid areas on...
Definition: zone.cpp:1301
const BOX2I GetBoundingBox() const override
Definition: zone.cpp:621
void SetFillFlag(PCB_LAYER_ID aLayer, bool aFlag)
Definition: zone.h:290
SHAPE_POLY_SET * GetFill(PCB_LAYER_ID aLayer)
Definition: zone.h:606
void SetIsFilled(bool isFilled)
Definition: zone.h:293
virtual bool IsOnLayer(PCB_LAYER_ID) const override
Test to see if this object is on the given layer.
Definition: zone.cpp:615
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: zone.h:136
The common library.
@ DRCE_SILK_MASK_CLEARANCE
Definition: drc_item.h:96
@ DRCE_SOLDERMASK_BRIDGE
Definition: drc_item.h:93
@ SILK_CLEARANCE_CONSTRAINT
Definition: drc_rule.h:56
#define REPORT_AUX(s)
bool isMaskAperture(BOARD_ITEM *aItem)
bool isNullAperture(BOARD_ITEM *aItem)
#define _(s)
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
Definition: layer_ids.h:767
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ B_Mask
Definition: layer_ids.h:98
@ B_Cu
Definition: layer_ids.h:65
@ F_Mask
Definition: layer_ids.h:97
@ F_SilkS
Definition: layer_ids.h:100
@ B_SilkS
Definition: layer_ids.h:101
@ 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
@ RPT_SEVERITY_IGNORE
int clearance
int actual
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
Definition: thread_pool.cpp:30
static thread_pool * tp
Definition: thread_pool.cpp:28
BS::thread_pool thread_pool
Definition: thread_pool.h:31
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:97
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition: typeinfo.h:107
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition: typeinfo.h:92
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition: typeinfo.h:90
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87