40#include <unordered_set>
77 m_bridgeRule.m_Name =
_(
"board setup solder mask min width" );
82 virtual bool Run()
override;
84 virtual const wxString
GetName()
const override {
return wxT(
"solder_mask_issues" ); };
99 int aTestNet,
const VECTOR2I& aPos );
102 const std::map<wxString, int>& aNetTieGroups,
161 for(
size_t ii = 0; ii <
ids.size(); ++ii )
163 if(
ids[ ii ] != aRhs.
ids[ ii ] )
164 return ids[ ii ] < aRhs.
ids[ ii ];
199 ZONE* zone =
static_cast<ZONE*
>( aItem );
208 clearance +=
static_cast<PAD*
>( aItem )->GetSolderMaskExpansion( layer );
238 const size_t progressDelta = 500;
299 const size_t progressDelta = 250;
330 item,
nullptr, maskLayer );
347 drce->SetErrorDetail(
formatMsg(
_(
"(%s clearance %s; actual %s)" ),
353 drce->SetItems( item );
368 return static_cast<PAD*
>( aItem )->IsNPTHWithNoCopper();
389 return maskLayers.count() > 0 && copperLayers.count() == 0;
423 FOOTPRINT* aApertureFootprint,
const std::map<wxString, int>& aNetTieGroups,
436 return aNetTieGroups.contains( padA->
GetNumber() );
438 return aNetTieGroups.contains( padB->
GetNumber() );
449 if( fp->AllowSolderMaskBridges() )
456 std::map<wxString, int> padNumberToGroupIdxMap = fp->MapPadNumbersToNetTieGroups();
457 int groupIdx = padNumberToGroupIdxMap[
pad->GetNumber() ];
464 if(
pad->GetNetCode() == aTestNet )
467 for(
PAD* other : fp->GetNetTiePads(
pad ) )
469 if( other->GetNetCode() == aTestNet )
488 std::optional<DRC_CONSTRAINT> itemConstraint;
496 m_itemTree->QueryColliding( aItem, aRefLayer, aTargetLayer,
507 if( otherNet > 0 && otherNet == itemNet )
518 if( board->GetDesignSettings().m_AllowSoldermaskBridgesInFPs )
527 if(
pad && otherPad && (
pad->SameLogicalPadAs( otherPad )
528 ||
pad->SharesNetTieGroup( otherPad ) ) )
537 if( otherNet < 0 || nets.count( otherNet ) )
543 const std::set<int>& nets = otherFP->GetNetTieCache( other );
545 if( itemNet < 0 || nets.count( itemNet ) )
553 if(
static_cast<void*
>( a ) >
static_cast<void*
>( b ) )
560 if( it !=
m_checkedPairs.end() && it->second.test( aTargetLayer ) )
600 else if(
via && !
via->IsTented( aRefLayer ) )
607 else if( otherVia && !otherVia->
IsTented( aTargetLayer ) )
609 else if( otherShape )
612 if( itemShape->Collide( otherItemShape.get(),
clearance, &
actual, &pos ) )
614 if( !itemConstraint.has_value() )
625 || ( itemConstraintIgnored && otherConstraintIgnored ) )
672 if( zoneNet == connectedItem->
GetNetCode() && zoneNet > 0 )
676 BOX2I inflatedBBox( aItemBBox );
680 clearance +=
pad->GetSolderMaskExpansion( aTargetLayer );
681 else if(
via && !
via->IsTented( aTargetLayer ) )
721 std::atomic<int> count = 0;
722 std::vector<BOARD_ITEM*> test_items;
727 test_items.push_back( item );
733 auto returns =
tp.submit_loop( 0, test_items.size(),
734 [&](
size_t i ) ->
bool
736 BOARD_ITEM* item = test_items[ i ];
738 if( m_drcEngine->IsCancelled() )
741 BOX2I itemBBox = item->GetBoundingBox();
743 if( item->IsOnLayer( F_Mask ) && !isNPTHPadWithNoCopper( item ) )
746 testItemAgainstItems( item, itemBBox, F_Mask, F_Mask );
749 testMaskItemAgainstZones( item, itemBBox, F_Mask, F_Cu );
754 testItemAgainstItems( item, itemBBox, F_Cu, F_Mask );
768 testItemAgainstItems( item, itemBBox, B_Cu, B_Mask );
776 for(
auto& ret : returns )
781 while( ret.wait_for( std::chrono::milliseconds( 100 ) ) == std::future_status::timeout )
782 reportProgress( count, test_items.size() );
787 reportMaskApertureBridges();
788 flushBridgeViolations();
802 std::swap( aItemA, aItemB );
806 std::swap( aItemB, aItemC );
811 std::array<KIID, 3> ids = { aItemA->
m_Uuid, aItemB->
m_Uuid,
816 if( ids[1] < ids[0] )
817 std::swap( ids[0], ids[1] );
819 if( aItemC && ids[2] < ids[1] )
821 std::swap( ids[1], ids[2] );
823 if( ids[1] < ids[0] )
824 std::swap( ids[0], ids[1] );
827 PENDING_BRIDGE bridge = { aItemA, aItemB, aItemC, aPos, aLayer, ids };
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" );
862 drce->SetErrorMessage(
IsFrontLayer( bridge.layer ) ? frontMsg : backMsg );
865 drce->SetItems( bridge.a, bridge.b, bridge.c );
871 BOARD_ITEM* itemX = bridge.c ? bridge.b : bridge.a;
872 BOARD_ITEM* itemY = bridge.c ? bridge.c : bridge.b;
880 if( shapeX->NearestPoints( shapeY.get(), ptX, ptY ) )
891 std::vector<PTR_LAYER_CACHE_KEY> apertureKeys;
895 apertureKeys.push_back( key );
897 std::sort( apertureKeys.begin(), apertureKeys.end(),
900 if( a.A->m_Uuid != b.A->m_Uuid )
901 return a.A->m_Uuid < b.A->m_Uuid;
903 return a.Layer < b.Layer;
918 std::map<wxString, int> netTieGroups;
920 if( apertureFootprint )
925 std::sort( items.begin(), items.end(),
928 if( a.item->m_Uuid != b.item->m_Uuid )
929 return a.item->m_Uuid < b.item->m_Uuid;
931 return a.net < b.net;
943 refItem = entry.item;
952 const bool reportAllTracks =
m_drcEngine->GetReportAllTrackErrors();
954 std::unordered_set<PTR_PTR_CACHE_KEY> reportedPairs;
961 if( collision.net == refNet )
966 bool collisionInFootprint = apertureFootprint
969 if( collisionInFootprint
976 bool reportedAnyTrack =
false;
980 if( entry.net == collision.net )
983 if( collisionInFootprint
995 if( entryIsTrack && reportedAnyTrack && !reportAllTracks )
1003 if( std::less<BOARD_ITEM*>{}( hi, lo ) )
1004 std::swap( lo, hi );
1006 if( !reportedPairs.insert( { lo, hi } ).second )
1009 collectBridge( aperture, entry.item, collision.item, collision.pos, maskLayer );
1012 reportedAnyTrack =
true;
1024 REPORT_AUX( wxT(
"Solder mask violations ignored. Tests not run." ) );
1033 auto updateLargestClearance =
1034 [&](
int aClearance )
1041 for(
PAD*
pad : footprint->Pads() )
1043 pad->Padstack().ForEachUniqueLayer(
1046 updateLargestClearance(
pad->GetSolderMaskExpansion( aLayer ) );
1050 for(
BOARD_ITEM* item : footprint->GraphicalItems() )
1053 updateLargestClearance(
static_cast<PCB_SHAPE*
>( item )->GetSolderMaskExpansion() );
1058 updateLargestClearance( track->GetSolderMaskExpansion() );
1063 updateLargestClearance(
static_cast<PCB_SHAPE*
>( item )->GetSolderMaskExpansion() );
1074 m_board->GetDesignSettings().m_SolderMaskToCopperClearance );
1096 if( !
reportPhase(
_(
"Checking solder mask to silk clearance..." ) ) )
1103 if( !
reportPhase(
_(
"Checking solder mask web integrity..." ) ) )
ERROR_LOC
When approximating an arc or circle, should the error be placed on the outside or inside of the curve...
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...
virtual bool IsConnected() const
Returns information if the object is derived from BOARD_CONNECTED_ITEM.
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.
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.
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.
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
constexpr bool Intersects(const BOX2< Vec > &aRect) const
SEVERITY GetSeverity() const
const MINOPTMAX< int > & GetValue() const
DRC_RULE * GetParentRule() const
static std::shared_ptr< DRC_ITEM > Create(int aErrorCode)
Constructs a DRC_ITEM for the given error code.
Implement an R-tree for fast spatial and layer indexing of connectable items.
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.
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_itemTree
void addItemToRTrees(BOARD_ITEM *aItem)
std::unique_ptr< DRC_RTREE > m_fullSolderMaskRTree
void testSilkToMaskClearance()
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).
void reportMaskApertureBridges()
std::mutex m_apertureMutex
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 flushBridgeViolations()
std::mutex m_checkedPairsMutex
DRC_TEST_PROVIDER_SOLDER_MASK()
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
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
KICAD_T Type() const
Returns the type of object.
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.
LSET is a set of PCB_LAYER_IDs.
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
const wxString & GetNumber() const
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...
int GetSolderMaskExpansion(PCB_LAYER_ID aLayer) const
bool SharesNetTieGroup(const PAD *aOther) const
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
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.
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...
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
std::shared_ptr< SHAPE_POLY_SET > GetFilledPolysList(PCB_LAYER_ID aLayer) const
const BOX2I GetBoundingBox() const override
void SetFillFlag(PCB_LAYER_ID aLayer, bool aFlag)
SHAPE_POLY_SET * GetFill(PCB_LAYER_ID aLayer)
void SetIsFilled(bool isFilled)
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
@ CHAMFER_ALL_CORNERS
All angles are chamfered.
@ DRCE_SILK_MASK_CLEARANCE
@ BRIDGED_MASK_CONSTRAINT
@ SILK_CLEARANCE_CONSTRAINT
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)
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
PCB_LAYER_ID
A quick note on layer IDs:
static DRC_REGISTER_TEST_PROVIDER< DRC_TEST_PROVIDER_ANNULAR_WIDTH > dummy
std::array< KIID, 3 > ids
bool operator<(const PENDING_BRIDGE &aRhs) const
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
BS::priority_thread_pool thread_pool
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
@ PCB_ZONE_T
class ZONE, a copper pour area
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
@ PCB_PAD_T
class PAD, a pad in a footprint
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
VECTOR2< int32_t > VECTOR2I