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, see <https://www.gnu.org/licenses/>.
18 */
19
20#include <common.h>
23#include <footprint.h>
24#include <pad.h>
25#include <pcb_shape.h>
26#include <pcb_track.h>
27#include <pcb_text.h>
28#include <thread_pool.h>
29#include <zone.h>
30#include <geometry/seg.h>
31#include <drc/drc_engine.h>
32#include <drc/drc_item.h>
33#include <drc/drc_rule.h>
35#include <drc/drc_rtree.h>
36
37#include <algorithm>
38#include <array>
39#include <set>
40#include <unordered_set>
41#include <vector>
42
43/*
44 Solder mask tests. Checks for silkscreen which is clipped by mask openings and for bridges
45 between mask apertures with different nets.
46 Errors generated:
47 - DRCE_SILK_MASK_CLEARANCE
48 - DRCE_SOLDERMASK_BRIDGE
49*/
50
51
52static void addItemPolysWithEndings( BOARD_ITEM* aItem, SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer, int aClearance,
53 int aError, ERROR_LOC aErrorLoc )
54{
55 if( aItem->Type() == PCB_SHAPE_T )
56 {
57 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( aItem );
58 shape->TransformWithLineEndingsToPolygon( aBuffer, aClearance, aError, aErrorLoc );
59 }
60 else
61 {
62 aItem->TransformShapeToPolygon( aBuffer, aLayer, aClearance, aError, aErrorLoc );
63 }
64}
65
66
68{
69public:
71 m_board( nullptr ),
72 m_webWidth( 0 ),
73 m_maxError( 0 ),
75 m_bridgeLimit( 0 )
76 {
77 m_bridgeRule.m_Name = _( "board setup solder mask min width" );
78 }
79
80 virtual ~DRC_TEST_PROVIDER_SOLDER_MASK() = default;
81
82 virtual bool Run() override;
83
84 virtual const wxString GetName() const override { return wxT( "solder_mask_issues" ); };
85
86private:
87 void addItemToRTrees( BOARD_ITEM* aItem );
88 void buildRTrees();
89
91 void testMaskBridges();
92
93 void testItemAgainstItems( BOARD_ITEM* aItem, const BOX2I& aItemBBox,
94 PCB_LAYER_ID aRefLayer, PCB_LAYER_ID aTargetLayer );
95 void testMaskItemAgainstZones( BOARD_ITEM* item, const BOX2I& itemBBox,
96 PCB_LAYER_ID refLayer, PCB_LAYER_ID targetLayer );
97
98 void recordMaskAperture( BOARD_ITEM* aMaskItem, BOARD_ITEM* aTestItem, PCB_LAYER_ID aTestLayer,
99 int aTestNet, const VECTOR2I& aPos );
100
101 bool maskApertureBridgeExcluded( FOOTPRINT* aApertureFootprint,
102 const std::map<wxString, int>& aNetTieGroups,
103 BOARD_ITEM* aRefItem, BOARD_ITEM* aTestItem );
104
106
107 void collectBridge( BOARD_ITEM* aItemA, BOARD_ITEM* aItemB, BOARD_ITEM* aItemC,
108 const VECTOR2I& aPos, PCB_LAYER_ID aLayer );
109
111
112 bool checkItemMask( BOARD_ITEM* aItem, int aTestNet );
113
114private:
116
121
122 std::unique_ptr<DRC_RTREE> m_fullSolderMaskRTree;
123 std::unique_ptr<DRC_RTREE> m_itemTree;
124
126 std::unordered_map<PTR_PTR_CACHE_KEY, LSET> m_checkedPairs;
127
128 // Shapes used to define solder mask apertures don't have nets, so a bridge exists only when
129 // an aperture exposes copper on two different nets. Every (item, net) that collides with an
130 // aperture is recorded during the parallel pass, then bridges are decided and reported
131 // single-threaded afterwards so the reported set never depends on worker arrival order.
138
139 std::mutex m_apertureMutex;
140 std::unordered_map<PTR_LAYER_CACHE_KEY, std::vector<MASK_APERTURE_ITEM>> m_maskApertureItems;
141
142 // Bridges are collected during the tests and reported in a deterministic order afterwards.
143 // Without a stable order the error-limit cap keeps a different subset of an over-limit board's
144 // bridges on every run, so the report entries wobble even though the count is stable. Order by
145 // layer, then the sorted UUIDs of the participating items, then the position, none of which
146 // depend on heap addresses or worker scheduling.
148 {
154 std::array<KIID, 3> ids;
155
156 bool operator<( const PENDING_BRIDGE& aRhs ) const
157 {
158 if( layer != aRhs.layer )
159 return layer < aRhs.layer;
160
161 for( size_t ii = 0; ii < ids.size(); ++ii )
162 {
163 if( ids[ ii ] != aRhs.ids[ ii ] )
164 return ids[ ii ] < aRhs.ids[ ii ];
165 }
166
167 if( pos.x != aRhs.pos.x )
168 return pos.x < aRhs.pos.x;
169
170 return pos.y < aRhs.pos.y;
171 }
172 };
173
174 // Only the first m_bridgeLimit bridges in that order can ever be reported, so the collection is
175 // a bounded max-heap rather than the full list. A board whose bridge count runs into the
176 // millions would otherwise cost memory and a sort proportional to a count the report discards.
177 std::mutex m_bridgeMutex;
178 std::vector<PENDING_BRIDGE> m_bridgeViolations;
180};
181
182
184{
185 // Rule areas are purely logical: no copper, no mask, no silk. Skip them entirely
186 // so they cannot contribute to solder-mask bridge or silk-to-mask collisions.
187 if( aItem->Type() == PCB_ZONE_T && static_cast<ZONE*>( aItem )->GetIsRuleArea() )
188 return;
189
190 for( PCB_LAYER_ID layer : { F_Mask, B_Mask } )
191 {
192 if( !aItem->IsOnLayer( layer ) )
193 continue;
194
195 SHAPE_POLY_SET* solderMask = m_board->m_SolderMaskBridges->GetFill( layer );
196
197 if( aItem->Type() == PCB_ZONE_T )
198 {
199 ZONE* zone = static_cast<ZONE*>( aItem );
200
201 solderMask->BooleanAdd( *zone->GetFilledPolysList( layer ) );
202 }
203 else
204 {
205 int clearance = m_webWidth / 2;
206
207 if( aItem->Type() == PCB_PAD_T )
208 clearance += static_cast<PAD*>( aItem )->GetSolderMaskExpansion( layer );
209 else if( aItem->Type() == PCB_VIA_T )
210 clearance += static_cast<PCB_VIA*>( aItem )->GetSolderMaskExpansion();
211 else if( aItem->Type() == PCB_TRACE_T )
212 clearance += static_cast<PCB_TRACK*>( aItem )->GetSolderMaskExpansion();
213 else if( aItem->Type() == PCB_SHAPE_T )
214 clearance += static_cast<PCB_SHAPE*>( aItem )->GetSolderMaskExpansion();
215
216 if( aItem->Type() == PCB_FIELD_T || aItem->Type() == PCB_TEXT_T )
217 {
218 PCB_TEXT* text = static_cast<PCB_TEXT*>( aItem );
219
220 text->TransformTextToPolySet( *solderMask, clearance, m_maxError, ERROR_OUTSIDE );
221 }
222 else
223 {
224 addItemPolysWithEndings( aItem, *solderMask, layer, clearance, m_maxError, ERROR_OUTSIDE );
225 }
226
227 m_itemTree->Insert( aItem, layer, NULL_CONSTRAINT, m_largestClearance );
228 }
229 }
230}
231
232
234{
235 ZONE* solderMask = m_board->m_SolderMaskBridges;
236 LSET layers( { F_Mask, B_Mask, F_Cu, B_Cu } );
237
238 const size_t progressDelta = 500;
239 int count = 0;
240 int ii = 0;
241
242 solderMask->GetFill( F_Mask )->RemoveAllContours();
243 solderMask->GetFill( B_Mask )->RemoveAllContours();
244
245 m_fullSolderMaskRTree = std::make_unique<DRC_RTREE>();
246 m_itemTree = std::make_unique<DRC_RTREE>();
247
249 [&]( BOARD_ITEM* item ) -> bool
250 {
251 ++count;
252 return true;
253 } );
254
256 [&]( BOARD_ITEM* item ) -> bool
257 {
258 if( !reportProgress( ii++, count, progressDelta ) )
259 return false;
260
261 addItemToRTrees( item );
262 return true;
263 } );
264
265 solderMask->GetFill( F_Mask )->Simplify();
266 solderMask->GetFill( B_Mask )->Simplify();
267
268 if( m_webWidth > 0 )
269 {
272 }
273
274 solderMask->SetFillFlag( F_Mask, true );
275 solderMask->SetFillFlag( B_Mask, true );
276 solderMask->SetIsFilled( true );
277
278 solderMask->CacheTriangulation();
279
280 m_fullSolderMaskRTree->Insert( solderMask, F_Mask, NULL_CONSTRAINT );
281 m_fullSolderMaskRTree->Insert( solderMask, B_Mask, NULL_CONSTRAINT );
282 m_fullSolderMaskRTree->Build();
283
284 m_itemTree->Build();
285
286 m_checkedPairs.clear();
287}
288
289
291{
292 LSET silkLayers( { F_SilkS, B_SilkS } );
293
294 // If we have no minimum web width then we delegate to the silk checker which does object-to-object
295 // testing (instead of object-to-solder-mask-zone-fill checking that we do here).
296 if( m_webWidth <= 0 )
297 return;
298
299 const size_t progressDelta = 250;
300 int count = 0;
301 int ii = 0;
302
304 [&]( BOARD_ITEM* item ) -> bool
305 {
306 ++count;
307 return true;
308 } );
309
311 [&]( BOARD_ITEM* item ) -> bool
312 {
313 if( m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_MASK_CLEARANCE ) )
314 return false;
315
316 if( !reportProgress( ii++, count, progressDelta ) )
317 return false;
318
319 if( isInvisibleText( item ) )
320 return true;
321
322 for( PCB_LAYER_ID layer : silkLayers )
323 {
324 if( !item->IsOnLayer( layer ) )
325 continue;
326
327 PCB_LAYER_ID maskLayer = layer == F_SilkS ? F_Mask : B_Mask;
328 BOX2I itemBBox = item->GetBoundingBox();
330 item, nullptr, maskLayer );
331 int clearance = constraint.GetValue().Min();
332 int actual;
333 VECTOR2I pos;
334
335 if( constraint.GetSeverity() == RPT_SEVERITY_IGNORE || clearance < 0 )
336 return true;
337
338 std::shared_ptr<SHAPE> itemShape = item->GetEffectiveShape( layer );
339
340 if( m_fullSolderMaskRTree->QueryColliding( itemBBox, itemShape.get(), maskLayer,
341 clearance, &actual, &pos ) )
342 {
343 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_SILK_MASK_CLEARANCE );
344
345 if( clearance > 0 )
346 {
347 drce->SetErrorDetail( formatMsg( _( "(%s clearance %s; actual %s)" ),
348 constraint.GetName(),
349 clearance,
350 actual ) );
351 }
352
353 drce->SetItems( item );
354 drce->SetViolatingRule( constraint.GetParentRule() );
355
356 reportViolation( drce, pos, layer );
357 }
358 }
359
360 return true;
361 } );
362}
363
364
366{
367 if( aItem->Type() == PCB_PAD_T )
368 return static_cast<PAD*>( aItem )->IsNPTHWithNoCopper();
369
370 return false;
371}
372
373
374// Simple mask apertures aren't associated with copper items, so they only constitute a bridge
375// when they expose other copper items having at least two distinct nets.
376//
377// Note that this algorithm is also used for free pads.
378
380{
381 if( aItem->Type() == PCB_PAD_T && static_cast<PAD*>( aItem )->IsFreePad() )
382 return true;
383
384 static const LSET saved( { F_Mask, B_Mask } );
385
386 LSET maskLayers = aItem->GetLayerSet() & saved;
387 LSET copperLayers = ( aItem->GetLayerSet() & ~saved ) & LSET::AllCuMask();
388
389 return maskLayers.count() > 0 && copperLayers.count() == 0;
390}
391
392
394 PCB_LAYER_ID aTestLayer, int aTestNet,
395 const VECTOR2I& aPos )
396{
397 // Only positive nets can bridge, and the pairing below is quadratic in what is recorded here.
398 if( aTestNet <= 0 )
399 return;
400
401 if( aTestLayer == F_Mask && !aTestItem->IsOnLayer( F_Cu ) )
402 return;
403
404 if( aTestLayer == B_Mask && !aTestItem->IsOnLayer( B_Cu ) )
405 return;
406
407 // Mask apertures in footprints which allow soldermask bridges are ignored entirely.
408 if( FOOTPRINT* fp = aMaskItem->GetParentFootprint(); fp && fp->AllowSolderMaskBridges() )
409 return;
410
411 PCB_LAYER_ID maskLayer = IsFrontLayer( aTestLayer ) ? F_Mask : B_Mask;
412 PTR_LAYER_CACHE_KEY key = { aMaskItem, maskLayer };
413
414 std::lock_guard<std::mutex> lock( m_apertureMutex );
415 m_maskApertureItems[ key ].push_back( { aTestItem, aTestNet, aPos } );
416}
417
418
419// Items belonging to the same net-tie group (or the same logical pad) may legitimately share a
420// mask aperture, so a pairing between them is not a bridge.
421
423 FOOTPRINT* aApertureFootprint, const std::map<wxString, int>& aNetTieGroups,
424 BOARD_ITEM* aRefItem, BOARD_ITEM* aTestItem )
425{
426 if( !aApertureFootprint || aTestItem->GetParentFootprint() != aApertureFootprint )
427 return false;
428
429 PAD* padA = aRefItem->Type() == PCB_PAD_T ? static_cast<PAD*>( aRefItem ) : nullptr;
430 PAD* padB = aTestItem->Type() == PCB_PAD_T ? static_cast<PAD*>( aTestItem ) : nullptr;
431
432 if( padA && padB )
433 return padA->SameLogicalPadAs( padB ) || padA->SharesNetTieGroup( padB );
434
435 if( padA && aTestItem->Type() == PCB_SHAPE_T )
436 return aNetTieGroups.contains( padA->GetNumber() );
437 else if( padB && aRefItem->Type() == PCB_SHAPE_T )
438 return aNetTieGroups.contains( padB->GetNumber() );
439
440 return false;
441}
442
443
445{
446 if( FOOTPRINT* fp = aItem->GetParentFootprint() )
447 {
448 // If we're allowing bridges then we're allowing bridges. Nothing to check.
449 if( fp->AllowSolderMaskBridges() )
450 return false;
451
452 // Items belonging to a net-tie may share the mask aperture of pads in the same group.
453 if( aItem->Type() == PCB_PAD_T && fp->IsNetTie() )
454 {
455 PAD* pad = static_cast<PAD*>( aItem );
456 std::map<wxString, int> padNumberToGroupIdxMap = fp->MapPadNumbersToNetTieGroups();
457 int groupIdx = padNumberToGroupIdxMap[ pad->GetNumber() ];
458
459 if( groupIdx >= 0 )
460 {
461 if( aTestNet < 0 )
462 return false;
463
464 if( pad->GetNetCode() == aTestNet )
465 return false;
466
467 for( PAD* other : fp->GetNetTiePads( pad ) )
468 {
469 if( other->GetNetCode() == aTestNet )
470 return false;
471 }
472 }
473 }
474 }
475
476 return true;
477}
478
479
481 PCB_LAYER_ID aRefLayer, PCB_LAYER_ID aTargetLayer )
482{
483 PAD* pad = aItem->Type() == PCB_PAD_T ? static_cast<PAD*>( aItem ) : nullptr;
484 PCB_VIA* via = aItem->Type() == PCB_VIA_T ? static_cast<PCB_VIA*>( aItem ) : nullptr;
485 PCB_SHAPE* shape = aItem->Type() == PCB_SHAPE_T ? static_cast<PCB_SHAPE*>( aItem ) : nullptr;
486 int itemNet = -1;
487
488 std::optional<DRC_CONSTRAINT> itemConstraint;
489 DRC_CONSTRAINT otherConstraint;
490
491 if( aItem->IsConnected() )
492 itemNet = static_cast<BOARD_CONNECTED_ITEM*>( aItem )->GetNetCode();
493
494 std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aRefLayer );
495
496 m_itemTree->QueryColliding( aItem, aRefLayer, aTargetLayer,
497 // Filter:
498 [&]( BOARD_ITEM* other ) -> bool
499 {
500 FOOTPRINT* itemFP = aItem->GetParentFootprint();
501 PAD* otherPad = other->Type() == PCB_PAD_T ? static_cast<PAD*>( other ) : nullptr;
502 int otherNet = -1;
503
504 if( other->IsConnected() )
505 otherNet = static_cast<BOARD_CONNECTED_ITEM*>( other )->GetNetCode();
506
507 if( otherNet > 0 && otherNet == itemNet )
508 return false;
509
510 if( isNPTHPadWithNoCopper( other ) )
511 return false;
512
513 if( itemFP && itemFP == other->GetParentFootprint() )
514 {
515 // Board-wide exclusion
516 if( BOARD* board = itemFP->GetBoard() )
517 {
518 if( board->GetDesignSettings().m_AllowSoldermaskBridgesInFPs )
519 return false;
520 }
521
522 // Footprint-specific exclusion
523 if( itemFP->AllowSolderMaskBridges() )
524 return false;
525 }
526
527 if( pad && otherPad && ( pad->SameLogicalPadAs( otherPad )
528 || pad->SharesNetTieGroup( otherPad ) ) )
529 {
530 return false;
531 }
532
533 if( itemFP && itemFP->IsNetTie() )
534 {
535 const std::set<int>& nets = itemFP->GetNetTieCache( aItem );
536
537 if( otherNet < 0 || nets.count( otherNet ) )
538 return false;
539 }
540
541 if( FOOTPRINT* otherFP = other->GetParentFootprint(); otherFP && otherFP->IsNetTie() )
542 {
543 const std::set<int>& nets = otherFP->GetNetTieCache( other );
544
545 if( itemNet < 0 || nets.count( itemNet ) )
546 return false;
547 }
548
549 BOARD_ITEM* a = aItem;
550 BOARD_ITEM* b = other;
551
552 // store canonical order so we don't collide in both directions (a:b and b:a)
553 if( static_cast<void*>( a ) > static_cast<void*>( b ) )
554 std::swap( a, b );
555
556 {
557 std::lock_guard<std::mutex> lock( m_checkedPairsMutex );
558 auto it = m_checkedPairs.find( { a, b } );
559
560 if( it != m_checkedPairs.end() && it->second.test( aTargetLayer ) )
561 {
562 return false;
563 }
564 else
565 {
566 m_checkedPairs[{ a, b }].set( aTargetLayer );
567 return true;
568 }
569 }
570 },
571 // Visitor:
572 [&]( BOARD_ITEM* other ) -> bool
573 {
574 PAD* otherPad = other->Type() == PCB_PAD_T ? static_cast<PAD*>( other ) : nullptr;
575 PCB_VIA* otherVia = other->Type() == PCB_VIA_T ? static_cast<PCB_VIA*>( other ) : nullptr;
576 PCB_SHAPE* otherShape = other->Type() == PCB_SHAPE_T ? static_cast<PCB_SHAPE*>( other ) : nullptr;
577 auto otherItemShape = other->GetEffectiveShape( aTargetLayer );
578 int otherNet = -1;
579
580 if( other->IsConnected() )
581 otherNet = static_cast<BOARD_CONNECTED_ITEM*>( other )->GetNetCode();
582
583 int actual;
584 VECTOR2I pos;
585 int clearance = 0;
586
587 if( aRefLayer == F_Mask || aRefLayer == B_Mask )
588 {
589 // Aperture-to-aperture must enforce web-min-width
591 }
592 else // ( aRefLayer == F_Cu || aRefLayer == B_Cu )
593 {
594 // Copper-to-aperture uses the solder-mask-to-copper-clearance
595 clearance = m_board->GetDesignSettings().m_SolderMaskToCopperClearance;
596 }
597
598 if( pad )
599 clearance += pad->GetSolderMaskExpansion( aRefLayer );
600 else if( via && !via->IsTented( aRefLayer ) )
601 clearance += via->GetSolderMaskExpansion();
602 else if( shape )
604
605 if( otherPad )
606 clearance += otherPad->GetSolderMaskExpansion( aTargetLayer );
607 else if( otherVia && !otherVia->IsTented( aTargetLayer ) )
608 clearance += otherVia->GetSolderMaskExpansion();
609 else if( otherShape )
610 clearance += otherShape->GetSolderMaskExpansion();
611
612 if( itemShape->Collide( otherItemShape.get(), clearance, &actual, &pos ) )
613 {
614 if( !itemConstraint.has_value() )
615 itemConstraint = m_drcEngine->EvalRules( BRIDGED_MASK_CONSTRAINT, aItem, nullptr, aRefLayer );
616
617 otherConstraint = m_drcEngine->EvalRules( BRIDGED_MASK_CONSTRAINT, other, nullptr, aTargetLayer );
618
619 bool itemConstraintIgnored = itemConstraint->GetSeverity() == RPT_SEVERITY_IGNORE;
620 bool otherConstraintIgnored = otherConstraint.GetSeverity() == RPT_SEVERITY_IGNORE;
621
622 // Mask apertures are ignored on their own; in other cases both participants must be ignored
623 if( ( isMaskAperture( aItem ) && itemConstraintIgnored )
624 || ( isMaskAperture( other ) && otherConstraintIgnored )
625 || ( itemConstraintIgnored && otherConstraintIgnored ) )
626 {
627 return !m_drcEngine->IsCancelled();
628 }
629
630 // Simple mask apertures aren't associated with copper items, so they only
631 // constitute a bridge when they expose other copper items having at least
632 // two distinct nets. Record the colliding item now and decide/report bridges
633 // deterministically once all threads have finished.
634 if( isMaskAperture( aItem ) )
635 {
636 recordMaskAperture( aItem, other, aRefLayer, otherNet, pos );
637 }
638 else if( isMaskAperture( other ) )
639 {
640 recordMaskAperture( other, aItem, aRefLayer, itemNet, pos );
641 }
642 else if( checkItemMask( other, itemNet ) )
643 {
644 collectBridge( aItem, other, nullptr, pos, aTargetLayer );
645 }
646 }
647
648 return !m_drcEngine->IsCancelled();
649 },
651}
652
653
655 PCB_LAYER_ID aMaskLayer, PCB_LAYER_ID aTargetLayer )
656{
657 PAD* pad = aItem->Type() == PCB_PAD_T ? static_cast<PAD*>( aItem ) : nullptr;
658 PCB_VIA* via = aItem->Type() == PCB_VIA_T ? static_cast<PCB_VIA*>( aItem ) : nullptr;
659 PCB_SHAPE* shape = aItem->Type() == PCB_SHAPE_T ? static_cast<PCB_SHAPE*>( aItem ) : nullptr;
660
661 for( ZONE* zone : m_board->m_DRCCopperZones )
662 {
663 if( !zone->GetLayerSet().test( aTargetLayer ) )
664 continue;
665
666 int zoneNet = zone->GetNetCode();
667
668 if( aItem->IsConnected() )
669 {
670 BOARD_CONNECTED_ITEM* connectedItem = static_cast<BOARD_CONNECTED_ITEM*>( aItem );
671
672 if( zoneNet == connectedItem->GetNetCode() && zoneNet > 0 )
673 continue;
674 }
675
676 BOX2I inflatedBBox( aItemBBox );
677 int clearance = m_board->GetDesignSettings().m_SolderMaskToCopperClearance;
678
679 if( pad )
680 clearance += pad->GetSolderMaskExpansion( aTargetLayer );
681 else if( via && !via->IsTented( aTargetLayer ) )
682 clearance += via->GetSolderMaskExpansion();
683 else if( shape )
685
686 inflatedBBox.Inflate( clearance );
687
688 if( !inflatedBBox.Intersects( zone->GetBoundingBox() ) )
689 continue;
690
691 DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ zone ].get();
692 int actual;
693 VECTOR2I pos;
694
695 std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( aMaskLayer );
696
697 if( zoneTree && zoneTree->QueryColliding( aItemBBox, itemShape.get(), aTargetLayer, clearance,
698 &actual, &pos ) )
699 {
700 // Simple mask apertures aren't associated with copper items, so they only constitute
701 // a bridge when they expose other copper items having at least two distinct nets.
702 if( isMaskAperture( aItem ) && zoneNet >= 0 )
703 {
704 recordMaskAperture( aItem, zone, aMaskLayer, zoneNet, pos );
705 }
706 else
707 {
708 collectBridge( aItem, zone, nullptr, pos, aTargetLayer );
709 }
710 }
711
712 if( m_drcEngine->IsCancelled() )
713 return;
714 }
715}
716
717
719{
720 LSET copperAndMaskLayers( { F_Mask, B_Mask, F_Cu, B_Cu } );
721 std::atomic<int> count = 0;
722 std::vector<BOARD_ITEM*> test_items;
723
724 forEachGeometryItem( s_allBasicItemsButZones, copperAndMaskLayers,
725 [&]( BOARD_ITEM* item ) -> bool
726 {
727 test_items.push_back( item );
728 return true;
729 } );
730
732
733 auto returns = tp.submit_loop( 0, test_items.size(),
734 [&]( size_t i ) -> bool
735 {
736 BOARD_ITEM* item = test_items[ i ];
737
738 if( m_drcEngine->IsCancelled() )
739 return false;
740
741 BOX2I itemBBox = item->GetBoundingBox();
742
743 if( item->IsOnLayer( F_Mask ) && !isNPTHPadWithNoCopper( item ) )
744 {
745 // Test for aperture-to-aperture collisions
746 testItemAgainstItems( item, itemBBox, F_Mask, F_Mask );
747
748 // Test for aperture-to-zone collisions
749 testMaskItemAgainstZones( item, itemBBox, F_Mask, F_Cu );
750 }
751 else if( item->IsOnLayer( F_Cu ) )
752 {
753 // Test for copper-item-to-aperture collisions
754 testItemAgainstItems( item, itemBBox, F_Cu, F_Mask );
755 }
756
757 if( item->IsOnLayer( B_Mask ) && !isNPTHPadWithNoCopper( item ) )
758 {
759 // Test for aperture-to-aperture collisions
760 testItemAgainstItems( item, itemBBox, B_Mask, B_Mask );
761
762 // Test for aperture-to-zone collisions
763 testMaskItemAgainstZones( item, itemBBox, B_Mask, B_Cu );
764 }
765 else if( item->IsOnLayer( B_Cu ) )
766 {
767 // Test for copper-item-to-aperture collisions
768 testItemAgainstItems( item, itemBBox, B_Cu, B_Mask );
769 }
770
771 ++count;
772
773 return true;
774 } );
775
776 for( auto& ret : returns )
777 {
778 if( !ret.valid() )
779 continue;
780
781 while( ret.wait_for( std::chrono::milliseconds( 100 ) ) == std::future_status::timeout )
782 reportProgress( count, test_items.size() );
783 }
784
785 // Decide mask aperture bridges now that all threads have completed and the full set of items
786 // exposed by each aperture is known, then emit every collected bridge in a deterministic order.
787 reportMaskApertureBridges();
788 flushBridgeViolations();
789}
790
791
793 BOARD_ITEM* aItemC, const VECTOR2I& aPos,
794 PCB_LAYER_ID aLayer )
795{
796 // Canonicalize the item order so the reported violation is identical no matter which worker
797 // observed the collision. The aperture (when present) stays first; the copper items are
798 // ordered by UUID.
799 if( !aItemC )
800 {
801 if( aItemB->m_Uuid < aItemA->m_Uuid )
802 std::swap( aItemA, aItemB );
803 }
804 else if( aItemC->m_Uuid < aItemB->m_Uuid )
805 {
806 std::swap( aItemB, aItemC );
807 }
808
809 // Only the third item is ever absent, and the swaps above already ordered the copper items, so
810 // the key needs at most the aperture inserted. The unused slot stays trailing.
811 std::array<KIID, 3> ids = { aItemA->m_Uuid, aItemB->m_Uuid,
812 aItemC ? aItemC->m_Uuid : niluuid };
813
814 // Inserted by hand because std::sort over a two- or three-element runtime range inlines an
815 // introsort GCC cannot bound
816 if( ids[1] < ids[0] )
817 std::swap( ids[0], ids[1] );
818
819 if( aItemC && ids[2] < ids[1] )
820 {
821 std::swap( ids[1], ids[2] );
822
823 if( ids[1] < ids[0] )
824 std::swap( ids[0], ids[1] );
825 }
826
827 PENDING_BRIDGE bridge = { aItemA, aItemB, aItemC, aPos, aLayer, ids };
828
829 std::lock_guard<std::mutex> lock( m_bridgeMutex );
830
831 if( static_cast<int>( m_bridgeViolations.size() ) == m_bridgeLimit )
832 {
833 // Anything sorting after the worst kept bridge can never reach the report.
834 if( !( bridge < m_bridgeViolations.front() ) )
835 return;
836
837 std::pop_heap( m_bridgeViolations.begin(), m_bridgeViolations.end() );
838 m_bridgeViolations.pop_back();
839 }
840
841 m_bridgeViolations.push_back( bridge );
842 std::push_heap( m_bridgeViolations.begin(), m_bridgeViolations.end() );
843}
844
845
847{
848 // The collection is a max-heap holding at most the reportable count, so this puts it in
849 // reporting order without ever having sorted the bridges the cap discards.
850 std::sort_heap( m_bridgeViolations.begin(), m_bridgeViolations.end() );
851
852 const wxString frontMsg = _( "Front solder mask aperture bridges items with different nets" );
853 const wxString backMsg = _( "Rear solder mask aperture bridges items with different nets" );
854
855 for( const PENDING_BRIDGE& bridge : m_bridgeViolations )
856 {
857 if( m_drcEngine->IsErrorLimitExceeded( DRCE_SOLDERMASK_BRIDGE ) || m_drcEngine->IsCancelled() )
858 break;
859
860 std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_SOLDERMASK_BRIDGE );
861
862 drce->SetErrorMessage( IsFrontLayer( bridge.layer ) ? frontMsg : backMsg );
863
864 // SetItems skips null participants, so the two-item case needs no separate call.
865 drce->SetItems( bridge.a, bridge.b, bridge.c );
866 drce->SetViolatingRule( &m_bridgeRule );
867
868 // Recompute the marker position from the two (UUID-ordered) copper items so it does not
869 // depend on which worker observed the collision. Query the copper layer, where every
870 // participant (including zones) has a real shape.
871 BOARD_ITEM* itemX = bridge.c ? bridge.b : bridge.a;
872 BOARD_ITEM* itemY = bridge.c ? bridge.c : bridge.b;
873 PCB_LAYER_ID copperLayer = IsFrontLayer( bridge.layer ) ? F_Cu : B_Cu;
874 VECTOR2I markerPos = itemX->GetPosition();
875
876 std::shared_ptr<SHAPE> shapeX = itemX->GetEffectiveShape( copperLayer );
877 std::shared_ptr<SHAPE> shapeY = itemY->GetEffectiveShape( copperLayer );
878 VECTOR2I ptX, ptY;
879
880 if( shapeX->NearestPoints( shapeY.get(), ptX, ptY ) )
881 markerPos = SEG( ptX, ptY ).Center();
882
883 reportViolation( drce, markerPos, bridge.layer );
884 }
885}
886
887
889{
890 // Visit apertures in a stable order (heap addresses and worker order are not reproducible).
891 std::vector<PTR_LAYER_CACHE_KEY> apertureKeys;
892 apertureKeys.reserve( m_maskApertureItems.size() );
893
894 for( const auto& [key, items] : m_maskApertureItems )
895 apertureKeys.push_back( key );
896
897 std::sort( apertureKeys.begin(), apertureKeys.end(),
898 []( const PTR_LAYER_CACHE_KEY& a, const PTR_LAYER_CACHE_KEY& b ) -> bool
899 {
900 if( a.A->m_Uuid != b.A->m_Uuid )
901 return a.A->m_Uuid < b.A->m_Uuid;
902
903 return a.Layer < b.Layer;
904 } );
905
906 for( const PTR_LAYER_CACHE_KEY& key : apertureKeys )
907 {
908 // Pairing is quadratic in the items an aperture exposes, so stay interruptible.
909 if( m_drcEngine->IsCancelled() )
910 return;
911
912 BOARD_ITEM* aperture = key.A;
913 PCB_LAYER_ID maskLayer = key.Layer;
914
915 // Built once per aperture rather than per candidate pair; the map covers every pad in the
916 // footprint and the pairing below is quadratic.
917 FOOTPRINT* apertureFootprint = aperture->GetParentFootprint();
918 std::map<wxString, int> netTieGroups;
919
920 if( apertureFootprint )
921 netTieGroups = apertureFootprint->MapPadNumbersToNetTieGroups();
922
923 std::vector<MASK_APERTURE_ITEM>& items = m_maskApertureItems.at( key );
924
925 std::sort( items.begin(), items.end(),
926 []( const MASK_APERTURE_ITEM& a, const MASK_APERTURE_ITEM& b ) -> bool
927 {
928 if( a.item->m_Uuid != b.item->m_Uuid )
929 return a.item->m_Uuid < b.item->m_Uuid;
930
931 return a.net < b.net;
932 } );
933
934 // The first positive net exposed by the aperture is the reference net; any item on a
935 // different positive net bridges it.
936 BOARD_ITEM* refItem = nullptr;
937 int refNet = -1;
938
939 for( const MASK_APERTURE_ITEM& entry : items )
940 {
941 if( entry.net > 0 )
942 {
943 refItem = entry.item;
944 refNet = entry.net;
945 break;
946 }
947 }
948
949 if( !refItem )
950 continue;
951
952 const bool reportAllTracks = m_drcEngine->GetReportAllTrackErrors();
953
954 std::unordered_set<PTR_PTR_CACHE_KEY> reportedPairs;
955
956 for( const MASK_APERTURE_ITEM& collision : items )
957 {
958 if( m_drcEngine->IsCancelled() )
959 return;
960
961 if( collision.net == refNet )
962 continue;
963
964 // Footprint-local exclusions need the colliding item inside the aperture's footprint,
965 // which is invariant across the inner loop.
966 bool collisionInFootprint = apertureFootprint
967 && collision.item->GetParentFootprint() == apertureFootprint;
968
969 if( collisionInFootprint
970 && maskApertureBridgeExcluded( apertureFootprint, netTieGroups, refItem,
971 collision.item ) )
972 {
973 continue;
974 }
975
976 bool reportedAnyTrack = false;
977
978 for( const MASK_APERTURE_ITEM& entry : items )
979 {
980 if( entry.net == collision.net )
981 continue;
982
983 if( collisionInFootprint
984 && maskApertureBridgeExcluded( apertureFootprint, netTieGroups, entry.item,
985 collision.item ) )
986 {
987 continue;
988 }
989
990 bool entryIsTrack = entry.item->Type() == PCB_TRACE_T || entry.item->Type() == PCB_ARC_T;
991
992 // Track throttling is per-collision, so it must gate emission before the pair is
993 // marked reported. Marking a suppressed pair here would drop it for good under a
994 // later collision ordering, making the total count depend on the reference net.
995 if( entryIsTrack && reportedAnyTrack && !reportAllTracks )
996 continue;
997
998 // Deduplicate unordered pairs so (B, C) and (C, B) are reported once. std::less
999 // gives a total order over unrelated pointers, which bare < does not.
1000 BOARD_ITEM* lo = entry.item;
1001 BOARD_ITEM* hi = collision.item;
1002
1003 if( std::less<BOARD_ITEM*>{}( hi, lo ) )
1004 std::swap( lo, hi );
1005
1006 if( !reportedPairs.insert( { lo, hi } ).second )
1007 continue;
1008
1009 collectBridge( aperture, entry.item, collision.item, collision.pos, maskLayer );
1010
1011 if( entryIsTrack )
1012 reportedAnyTrack = true;
1013 }
1014 }
1015 }
1016}
1017
1018
1020{
1021 if( m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_MASK_CLEARANCE )
1022 && m_drcEngine->IsErrorLimitExceeded( DRCE_SOLDERMASK_BRIDGE ) )
1023 {
1024 REPORT_AUX( wxT( "Solder mask violations ignored. Tests not run." ) );
1025 return true; // continue with other tests
1026 }
1027
1028 m_board = m_drcEngine->GetBoard();
1029 m_webWidth = m_board->GetDesignSettings().m_SolderMaskMinWidth;
1030 m_maxError = m_board->GetDesignSettings().m_MaxError;
1032
1033 auto updateLargestClearance =
1034 [&]( int aClearance )
1035 {
1036 m_largestClearance = std::max( m_largestClearance, aClearance );
1037 };
1038
1039 for( FOOTPRINT* footprint : m_board->Footprints() )
1040 {
1041 for( PAD* pad : footprint->Pads() )
1042 {
1043 pad->Padstack().ForEachUniqueLayer(
1044 [&]( PCB_LAYER_ID aLayer )
1045 {
1046 updateLargestClearance( pad->GetSolderMaskExpansion( aLayer ) );
1047 } );
1048 }
1049
1050 for( BOARD_ITEM* item : footprint->GraphicalItems() )
1051 {
1052 if( item->Type() == PCB_SHAPE_T )
1053 updateLargestClearance( static_cast<PCB_SHAPE*>( item )->GetSolderMaskExpansion() );
1054 }
1055 }
1056
1057 for( PCB_TRACK* track : m_board->Tracks() )
1058 updateLargestClearance( track->GetSolderMaskExpansion() );
1059
1060 for( BOARD_ITEM* item : m_board->Drawings() )
1061 {
1062 if( item->Type() == PCB_SHAPE_T )
1063 updateLargestClearance( static_cast<PCB_SHAPE*>( item )->GetSolderMaskExpansion() );
1064 }
1065
1066 // Order is important here: m_webWidth must be added in before m_largestClearance is
1067 // maxed with the various clearance constraints.
1069
1070 // Include SolderMaskToCopperClearance so R-tree queries find copper items that are within
1071 // the required distance of mask apertures. Without this, tracks passing near pad apertures
1072 // from different nets would not be found if SolderMaskToCopperClearance > m_largestClearance.
1074 m_board->GetDesignSettings().m_SolderMaskToCopperClearance );
1075
1076 DRC_CONSTRAINT worstClearanceConstraint;
1077
1078 if( m_drcEngine->QueryWorstConstraint( SILK_CLEARANCE_CONSTRAINT, worstClearanceConstraint ) )
1079 m_largestClearance = std::max( m_largestClearance, worstClearanceConstraint.m_Value.Min() );
1080
1081 if( !reportPhase( _( "Building solder mask..." ) ) )
1082 return false; // DRC cancelled
1083
1084 m_checkedPairs.clear();
1085 m_maskApertureItems.clear();
1086 m_bridgeViolations.clear();
1087
1088 // Snapshotting the cap keeps the bridge collection bounded without contending on the engine's
1089 // error-limit mutex from the worker threads. Nothing else reports DRCE_SOLDERMASK_BRIDGE, so
1090 // the budget cannot shrink under us.
1093
1094 buildRTrees();
1095
1096 if( !reportPhase( _( "Checking solder mask to silk clearance..." ) ) )
1097 return false; // DRC cancelled
1098
1100
1101 if( m_bridgeLimit > 0 )
1102 {
1103 if( !reportPhase( _( "Checking solder mask web integrity..." ) ) )
1104 return false; // DRC cancelled
1105
1107 }
1108
1109 return !m_drcEngine->IsCancelled();
1110}
1111
1112
1113namespace detail
1114{
1116}
ERROR_LOC
When approximating an arc or circle, should the error be placed on the outside or inside of the curve...
@ ERROR_OUTSIDE
BOX2< VECTOR2I > BOX2I
Definition box2.h:927
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:84
virtual bool IsConnected() const
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
Definition board_item.h:172
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.
virtual bool IsOnLayer(PCB_LAYER_ID aLayer) const
Test to see if this object is on the given layer.
Definition board_item.h:408
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
FOOTPRINT * GetParentFootprint() const
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition board_item.h:346
virtual std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING aFlash=FLASHING::DEFAULT, DRC_CONSTRAINT_T aUsage=NULL_CONSTRAINT) const
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:409
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:553
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition box2.h:308
wxString GetName() const
Definition drc_rule.h:208
SEVERITY GetSeverity() const
Definition drc_rule.h:221
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:200
MINOPTMAX< int > m_Value
Definition drc_rule.h:244
DRC_RULE * GetParentRule() const
Definition drc_rule.h:204
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Definition drc_item.cpp:444
Implement an R-tree for fast spatial and layer indexing of connectable items.
Definition drc_rtree.h:45
int QueryColliding(BOARD_ITEM *aRefItem, PCB_LAYER_ID aRefLayer, PCB_LAYER_ID aTargetLayer, std::function< bool(BOARD_ITEM *)> aFilter=nullptr, std::function< bool(BOARD_ITEM *)> aVisitor=nullptr, int aClearance=0) const
This is a fast test which essentially does bounding-box overlap given a worst-case clearance.
Definition drc_rtree.h:277
virtual const wxString GetName() const override
void testMaskItemAgainstZones(BOARD_ITEM *item, const BOX2I &itemBBox, PCB_LAYER_ID refLayer, PCB_LAYER_ID targetLayer)
void collectBridge(BOARD_ITEM *aItemA, BOARD_ITEM *aItemB, BOARD_ITEM *aItemC, const VECTOR2I &aPos, PCB_LAYER_ID aLayer)
virtual ~DRC_TEST_PROVIDER_SOLDER_MASK()=default
std::unique_ptr< DRC_RTREE > m_fullSolderMaskRTree
bool maskApertureBridgeExcluded(FOOTPRINT *aApertureFootprint, const std::map< wxString, int > &aNetTieGroups, BOARD_ITEM *aRefItem, BOARD_ITEM *aTestItem)
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
std::vector< PENDING_BRIDGE > m_bridgeViolations
bool checkItemMask(BOARD_ITEM *aItem, int aTestNet)
std::unordered_map< PTR_LAYER_CACHE_KEY, std::vector< MASK_APERTURE_ITEM > > m_maskApertureItems
void recordMaskAperture(BOARD_ITEM *aMaskItem, BOARD_ITEM *aTestItem, PCB_LAYER_ID aTestLayer, int aTestNet, const VECTOR2I &aPos)
void testItemAgainstItems(BOARD_ITEM *aItem, const BOX2I &aItemBBox, PCB_LAYER_ID aRefLayer, PCB_LAYER_ID aTargetLayer)
static std::vector< KICAD_T > s_allBasicItemsButZones
virtual bool reportPhase(const wxString &aStageName)
int forEachGeometryItem(const std::vector< KICAD_T > &aTypes, const LSET &aLayers, const std::function< bool(BOARD_ITEM *)> &aFunc)
void reportViolation(std::shared_ptr< DRC_ITEM > &item, const VECTOR2I &aMarkerPos, int aMarkerLayer, const std::function< void(PCB_MARKER *)> &aPathGenerator=[](PCB_MARKER *){})
static std::vector< KICAD_T > s_allBasicItems
bool isInvisibleText(const BOARD_ITEM *aItem) const
wxString formatMsg(const wxString &aFormatString, const wxString &aSource, double aConstraint, double aActual, EDA_DATA_TYPE aDataType=EDA_DATA_TYPE::DISTANCE)
virtual bool reportProgress(size_t aCount, size_t aSize, size_t aDelta=1)
virtual VECTOR2I GetPosition() const
Definition eda_item.h:348
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition eda_item.cpp:270
const KIID m_Uuid
Definition eda_item.h:597
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
void TransformWithLineEndingsToPolygon(SHAPE_POLY_SET &aBuffer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const
Convert the shape body shortened for line endings plus line-ending geometry to polygons.
bool AllowSolderMaskBridges() const
Definition footprint.h:559
std::map< wxString, int > MapPadNumbersToNetTieGroups() const
const std::set< int > & GetNetTieCache(const BOARD_ITEM *aItem) const
Get the set of net codes that are allowed to connect to a footprint item.
Definition footprint.h:795
bool IsNetTie() const
Definition footprint.h:566
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:604
T Min() const
Definition minoptmax.h:29
Definition pad.h:61
const wxString & GetNumber() const
Definition pad.h:143
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:166
int GetSolderMaskExpansion(PCB_LAYER_ID aLayer) const
Definition pad.cpp:1979
bool IsFreePad() const
Definition pad.cpp:600
bool SharesNetTieGroup(const PAD *aOther) const
Definition pad.cpp:577
int GetSolderMaskExpansion() const
bool IsTented(PCB_LAYER_ID aLayer) const override
Checks if the given object is tented (its copper shape is covered by solder mask) on a given side of ...
int GetSolderMaskExpansion() const
Definition seg.h:38
VECTOR2I Center() const
Definition seg.h:375
Represent a set of closed polygons.
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:70
void CacheTriangulation(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, const SHAPE_POLY_SET::TASK_SUBMITTER &aSubmitter={})
Create a list of triangles that "fill" the solid areas used for instance to draw these solid areas on...
Definition zone.cpp:1637
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition zone.h:807
std::shared_ptr< SHAPE_POLY_SET > GetFilledPolysList(PCB_LAYER_ID aLayer) const
Definition zone.h:692
const BOX2I GetBoundingBox() const override
Definition zone.cpp:788
void SetFillFlag(PCB_LAYER_ID aLayer, bool aFlag)
Definition zone.h:300
SHAPE_POLY_SET * GetFill(PCB_LAYER_ID aLayer)
Definition zone.h:699
void SetIsFilled(bool isFilled)
Definition zone.h:307
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition zone.h:133
@ CHAMFER_ALL_CORNERS
All angles are chamfered.
@ DRCE_SILK_MASK_CLEARANCE
Definition drc_item.h:100
@ DRCE_SOLDERMASK_BRIDGE
Definition drc_item.h:97
@ BRIDGED_MASK_CONSTRAINT
Definition drc_rule.h:88
@ SILK_CLEARANCE_CONSTRAINT
Definition drc_rule.h:58
@ NULL_CONSTRAINT
Definition drc_rule.h:50
#define REPORT_AUX(s)
bool isMaskAperture(BOARD_ITEM *aItem)
static void addItemPolysWithEndings(BOARD_ITEM *aItem, SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aError, ERROR_LOC aErrorLoc)
bool isNPTHPadWithNoCopper(BOARD_ITEM *aItem)
#define _(s)
KIID niluuid(0)
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
Definition layer_ids.h:806
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ B_Mask
Definition layer_ids.h:94
@ B_Cu
Definition layer_ids.h:61
@ F_Mask
Definition layer_ids.h:93
@ F_SilkS
Definition layer_ids.h:96
@ B_SilkS
Definition layer_ids.h:97
@ F_Cu
Definition layer_ids.h:60
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
@ RPT_SEVERITY_IGNORE
int clearance
int actual
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
static thread_pool * tp
BS::priority_thread_pool thread_pool
Definition thread_pool.h:27
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:80
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition typeinfo.h:89
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:100
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:84
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:82
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:79
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:90
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:88
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683