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