Use individual silk/mask collision checking if solder mask min width is 0.

Using the combinde solder mask doesn't deal well
with exclusions since almost any change will affect
the combined mask.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/17429
This commit is contained in:
Jeff Young
2025-04-14 14:05:35 +01:00
parent 8827323361
commit 38c822db9c
8 changed files with 51 additions and 29 deletions
+1 -1
View File
@@ -187,7 +187,7 @@ BOARD_DESIGN_SETTINGS::BOARD_DESIGN_SETTINGS( JSON_SETTINGS* aParent, const std:
m_DRCSeverities[ DRCE_FOOTPRINT_FILTERS ] = RPT_SEVERITY_IGNORE;
m_DRCSeverities[ DRCE_OVERLAPPING_SILK ] = RPT_SEVERITY_WARNING;
m_DRCSeverities[ DRCE_SILK_CLEARANCE ] = RPT_SEVERITY_WARNING;
m_DRCSeverities[ DRCE_SILK_MASK_CLEARANCE ] = RPT_SEVERITY_WARNING;
m_DRCSeverities[ DRCE_SILK_EDGE_CLEARANCE ] = RPT_SEVERITY_WARNING;
m_DRCSeverities[ DRCE_TEXT_HEIGHT ] = RPT_SEVERITY_WARNING;
m_DRCSeverities[ DRCE_TEXT_THICKNESS ] = RPT_SEVERITY_WARNING;
+1 -1
View File
@@ -191,7 +191,7 @@ void DIALOG_FOOTPRINT_CHECKER::runChecks()
footprint->CheckClippedSilk(
[&]( BOARD_ITEM* aItemA, BOARD_ITEM* aItemB, const VECTOR2I& aPt )
{
errorHandler( aItemA, aItemB, nullptr, DRCE_SILK_CLEARANCE, wxEmptyString, aPt );
errorHandler( aItemA, aItemB, nullptr, DRCE_SILK_MASK_CLEARANCE, wxEmptyString, aPt );
} );
m_checksRun = true;
+3 -3
View File
@@ -236,7 +236,7 @@ DRC_ITEM DRC_ITEM::solderMaskBridge( DRCE_SOLDERMASK_BRIDGE,
_HKI( "Solder mask aperture bridges items with different nets" ),
wxT( "solder_mask_bridge" ) );
DRC_ITEM DRC_ITEM::silkClearance( DRCE_SILK_CLEARANCE,
DRC_ITEM DRC_ITEM::silkMaskClearance( DRCE_SILK_MASK_CLEARANCE,
_HKI( "Silkscreen clipped by solder mask" ),
wxT( "silk_over_copper" ) );
@@ -352,7 +352,7 @@ std::vector<std::reference_wrapper<RC_ITEM>> DRC_ITEM::allItemTypes(
DRC_ITEM::heading_readability,
DRC_ITEM::silkOverlaps,
DRC_ITEM::silkClearance,
DRC_ITEM::silkMaskClearance,
DRC_ITEM::silkEdgeClearance,
DRC_ITEM::textHeightOutOfRange,
DRC_ITEM::textThicknessOutOfRange,
@@ -441,7 +441,7 @@ std::shared_ptr<DRC_ITEM> DRC_ITEM::Create( int aErrorCode )
case DRCE_GENERIC_ERROR: return std::make_shared<DRC_ITEM>( genericError );
case DRCE_COPPER_SLIVER: return std::make_shared<DRC_ITEM>( copperSliver );
case DRCE_OVERLAPPING_SILK: return std::make_shared<DRC_ITEM>( silkOverlaps );
case DRCE_SILK_CLEARANCE: return std::make_shared<DRC_ITEM>( silkClearance );
case DRCE_SILK_MASK_CLEARANCE: return std::make_shared<DRC_ITEM>( silkMaskClearance );
case DRCE_SILK_EDGE_CLEARANCE: return std::make_shared<DRC_ITEM>( silkEdgeClearance );
case DRCE_SOLDERMASK_BRIDGE: return std::make_shared<DRC_ITEM>( solderMaskBridge );
case DRCE_TEXT_HEIGHT: return std::make_shared<DRC_ITEM>( textHeightOutOfRange );
+3 -3
View File
@@ -93,12 +93,12 @@ enum PCB_DRC_CODE {
DRCE_SOLDERMASK_BRIDGE, // failure to maintain min soldermask web thickness
// between copper items with different nets
DRCE_SILK_CLEARANCE, // silkscreen clipped by mask (potentially leaving it
DRCE_SILK_MASK_CLEARANCE, // silkscreen clipped by mask (potentially leaving it
// over pads, exposed copper, etc.)
DRCE_SILK_EDGE_CLEARANCE,
DRCE_TEXT_HEIGHT,
DRCE_TEXT_THICKNESS,
DRCE_OVERLAPPING_SILK, // silk to silk clearance error
DRCE_OVERLAPPING_SILK, // silk-to-silk or silk-to-other clearance error
DRCE_LENGTH_OUT_OF_RANGE,
DRCE_SKEW_OUT_OF_RANGE,
@@ -238,7 +238,7 @@ private:
static DRC_ITEM genericWarning;
static DRC_ITEM genericError;
static DRC_ITEM copperSliver;
static DRC_ITEM silkClearance;
static DRC_ITEM silkMaskClearance;
static DRC_ITEM silkEdgeClearance;
static DRC_ITEM solderMaskBridge;
static DRC_ITEM silkOverlaps;
+27 -11
View File
@@ -75,14 +75,20 @@ bool DRC_TEST_PROVIDER_SILK_CLEARANCE::Run()
{
const int progressDelta = 500;
if( m_drcEngine->IsErrorLimitExceeded( DRCE_OVERLAPPING_SILK ) )
m_board = m_drcEngine->GetBoard();
// If the soldermask min width is greater than 0 then we must use a healing algorithm to generate
// a whole-board soldermask poly, and then test against that. However, that can't deal well with
// DRC exclusions (as any change anywhere on the board that affects the soldermask will null the
// associated exclusions), so we only use that when soldermask min width is > 0.
bool checkIndividualMaskItems = m_board->GetDesignSettings().m_SolderMaskMinWidth <= 0;
if( m_drcEngine->IsErrorLimitExceeded( DRCE_OVERLAPPING_SILK )
&& ( m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_MASK_CLEARANCE) || !checkIndividualMaskItems ) )
{
reportAux( wxT( "Overlapping silk violations ignored. Tests not run." ) );
return true; // continue with other tests
}
m_board = m_drcEngine->GetBoard();
DRC_CONSTRAINT worstClearanceConstraint;
m_largestClearance = 0;
@@ -182,8 +188,11 @@ bool DRC_TEST_PROVIDER_SILK_CLEARANCE::Run()
std::shared_ptr<SHAPE> hole;
if( m_drcEngine->IsErrorLimitExceeded( DRCE_OVERLAPPING_SILK ) )
if( ( m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_MASK_CLEARANCE) || !checkIndividualMaskItems )
&& m_drcEngine->IsErrorLimitExceeded( DRCE_OVERLAPPING_SILK ) )
{
return false;
}
if( isInvisibleText( refItem ) || isInvisibleText( testItem ) )
return true;
@@ -201,14 +210,21 @@ bool DRC_TEST_PROVIDER_SILK_CLEARANCE::Run()
}
}
int errorCode = DRCE_OVERLAPPING_SILK;
DRC_CONSTRAINT constraint = m_drcEngine->EvalRules( SILK_CLEARANCE_CONSTRAINT,
refItem, testItem,
aLayers.second );
refItem, testItem, aLayers.second );
int minClearance = -1;
if( constraint.IsNull() || constraint.GetSeverity() == RPT_SEVERITY_IGNORE )
return true;
if( !constraint.IsNull() && constraint.GetSeverity() != RPT_SEVERITY_IGNORE )
minClearance = constraint.GetValue().Min();
int minClearance = constraint.GetValue().Min();
if( aLayers.second == F_Mask || aLayers.second == B_Mask )
{
if( checkIndividualMaskItems )
minClearance = std::max( minClearance, 0 );
errorCode = DRCE_SILK_MASK_CLEARANCE;
}
if( minClearance < 0 )
return true;
@@ -237,7 +253,7 @@ bool DRC_TEST_PROVIDER_SILK_CLEARANCE::Run()
if( refShape->Collide( testShape, minClearance, &actual, &pos ) )
{
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_OVERLAPPING_SILK );
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( errorCode );
if( minClearance > 0 )
{
+14 -8
View File
@@ -40,7 +40,7 @@
Solder mask tests. Checks for silkscreen which is clipped by mask openings and for bridges
between mask apertures with different nets.
Errors generated:
- DRCE_SILK_CLEARANCE
- DRCE_SILK_MASK_CLEARANCE
- DRCE_SOLDERMASK_BRIDGE
*/
@@ -224,10 +224,11 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::buildRTrees()
solderMask->GetFill( F_Mask )->Simplify();
solderMask->GetFill( B_Mask )->Simplify();
solderMask->GetFill( F_Mask )->Deflate( m_webWidth / 2, CORNER_STRATEGY::CHAMFER_ALL_CORNERS,
m_maxError );
solderMask->GetFill( B_Mask )->Deflate( m_webWidth / 2, CORNER_STRATEGY::CHAMFER_ALL_CORNERS,
m_maxError );
if( m_webWidth > 0 )
{
solderMask->GetFill( F_Mask )->Deflate( m_webWidth / 2, CORNER_STRATEGY::CHAMFER_ALL_CORNERS, m_maxError );
solderMask->GetFill( B_Mask )->Deflate( m_webWidth / 2, CORNER_STRATEGY::CHAMFER_ALL_CORNERS, m_maxError );
}
solderMask->SetFillFlag( F_Mask, true );
solderMask->SetFillFlag( B_Mask, true );
@@ -246,6 +247,11 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::testSilkToMaskClearance()
{
LSET silkLayers( { F_SilkS, B_SilkS } );
// If we have no minimum web width then we delegate to the silk checker which does object-to-object
// testing (instead of object-to-solder-mask-zone-fill checking that we do here).
if( m_webWidth <= 0 )
return;
const size_t progressDelta = 250;
int count = 0;
int ii = 0;
@@ -260,7 +266,7 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::testSilkToMaskClearance()
forEachGeometryItem( s_allBasicItems, silkLayers,
[&]( BOARD_ITEM* item ) -> bool
{
if( m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_CLEARANCE ) )
if( m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_MASK_CLEARANCE ) )
return false;
if( !reportProgress( ii++, count, progressDelta ) )
@@ -290,7 +296,7 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::testSilkToMaskClearance()
if( m_fullSolderMaskRTree->QueryColliding( itemBBox, itemShape.get(), maskLayer,
clearance, &actual, &pos ) )
{
auto drce = DRC_ITEM::Create( DRCE_SILK_CLEARANCE );
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_SILK_MASK_CLEARANCE );
if( clearance > 0 )
{
@@ -758,7 +764,7 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::testMaskBridges()
bool DRC_TEST_PROVIDER_SOLDER_MASK::Run()
{
if( m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_CLEARANCE )
if( m_drcEngine->IsErrorLimitExceeded( DRCE_SILK_MASK_CLEARANCE )
&& m_drcEngine->IsErrorLimitExceeded( DRCE_SOLDERMASK_BRIDGE ) )
{
reportAux( wxT( "Solder mask violations ignored. Tests not run." ) );
@@ -57,7 +57,7 @@ BOOST_FIXTURE_TEST_CASE( DRCCopperGraphicsTest, DRC_COPPER_GRAPHICS_TEST_FIXTURE
bds.m_DRCSeverities[ DRCE_LIB_FOOTPRINT_MISMATCH ] = SEVERITY::RPT_SEVERITY_IGNORE;
bds.m_DRCSeverities[ DRCE_COPPER_SLIVER ] = SEVERITY::RPT_SEVERITY_IGNORE;
bds.m_DRCSeverities[ DRCE_STARVED_THERMAL ] = SEVERITY::RPT_SEVERITY_IGNORE;
bds.m_DRCSeverities[ DRCE_SILK_CLEARANCE ] = SEVERITY::RPT_SEVERITY_IGNORE;
bds.m_DRCSeverities[ DRCE_SILK_MASK_CLEARANCE ] = SEVERITY::RPT_SEVERITY_IGNORE;
bds.m_DRCSeverities[ DRCE_ISOLATED_COPPER ] = SEVERITY::RPT_SEVERITY_IGNORE;
bds.m_DRCSeverities[ DRCE_MIRRORED_TEXT_ON_FRONT_LAYER ] = SEVERITY::RPT_SEVERITY_IGNORE;
bds.m_DRCSeverities[ DRCE_NONMIRRORED_TEXT_ON_BACK_LAYER ] = SEVERITY::RPT_SEVERITY_IGNORE;
@@ -55,7 +55,7 @@ BOOST_FIXTURE_TEST_CASE( DRCSolderMaskBridgingTest, DRC_SOLDER_MASK_BRIDGING_TES
bds.m_DRCSeverities[ DRCE_LIB_FOOTPRINT_MISMATCH ] = SEVERITY::RPT_SEVERITY_IGNORE;
bds.m_DRCSeverities[ DRCE_COPPER_SLIVER ] = SEVERITY::RPT_SEVERITY_IGNORE;
bds.m_DRCSeverities[ DRCE_STARVED_THERMAL ] = SEVERITY::RPT_SEVERITY_IGNORE;
bds.m_DRCSeverities[ DRCE_SILK_CLEARANCE ] = SEVERITY::RPT_SEVERITY_IGNORE;
bds.m_DRCSeverities[ DRCE_SILK_MASK_CLEARANCE ] = SEVERITY::RPT_SEVERITY_IGNORE;
bds.m_DRCEngine->SetViolationHandler(
[&]( const std::shared_ptr<DRC_ITEM>& aItem, VECTOR2I aPos, int aLayer,