Make sure physical_clearance rules are run against courtyard layers.

This commit is contained in:
Jeff Young 2022-05-10 23:01:25 +01:00
parent d81378d786
commit 37838dffb9
4 changed files with 102 additions and 41 deletions

View File

@ -265,3 +265,10 @@ For the latter use a `(layer "layer_name")` clause in the rule.
(rule "disallow solder mask margin overrides" (rule "disallow solder mask margin overrides"
(constraint assertion "A.Soldermask_Margin_Override == 0mm") (constraint assertion "A.Soldermask_Margin_Override == 0mm")
(condition "A.Type == 'Pad'")) (condition "A.Type == 'Pad'"))
# Enforce a mechanical clearance between components and board edge
(rule front_mechanical_board_edge_clearance
(layer "F.Courtyard")
(constraint physical_clearance (min 3mm))
(condition "B.Layer == 'Edge.Cuts'"))

View File

@ -145,12 +145,15 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::Run()
static const std::vector<KICAD_T> itemTypes = { static const std::vector<KICAD_T> itemTypes = {
PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T,
PCB_FOOTPRINT_T,
PCB_PAD_T, PCB_PAD_T,
PCB_SHAPE_T, PCB_FP_SHAPE_T, PCB_SHAPE_T, PCB_FP_SHAPE_T,
PCB_TEXT_T, PCB_FP_TEXT_T, PCB_TEXTBOX_T, PCB_FP_TEXTBOX_T, PCB_TEXT_T, PCB_FP_TEXT_T, PCB_TEXTBOX_T, PCB_FP_TEXTBOX_T,
PCB_DIMENSION_T PCB_DIMENSION_T
}; };
static const LSET courtyards( 2, F_CrtYd, B_CrtYd );
forEachGeometryItem( itemTypes, LSET::AllLayersMask(), forEachGeometryItem( itemTypes, LSET::AllLayersMask(),
[&]( BOARD_ITEM* item ) -> bool [&]( BOARD_ITEM* item ) -> bool
{ {
@ -172,18 +175,22 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::Run()
PAD* pad = static_cast<PAD*>( item ); PAD* pad = static_cast<PAD*>( item );
if( pad->GetDrillSizeX() > 0 && pad->GetDrillSizeY() > 0 ) if( pad->GetDrillSizeX() > 0 && pad->GetDrillSizeY() > 0 )
layers |= LSET::PhysicalLayersMask(); layers |= LSET::PhysicalLayersMask() | courtyards;
} }
else if( item->Type() == PCB_VIA_T ) else if( item->Type() == PCB_VIA_T )
{ {
PCB_VIA* via = static_cast<PCB_VIA*>( item ); PCB_VIA* via = static_cast<PCB_VIA*>( item );
if( via->GetDrill() > 0 ) if( via->GetDrill() > 0 )
layers |= LSET::PhysicalLayersMask(); layers |= LSET::PhysicalLayersMask() | courtyards;
}
else if( item->Type() == PCB_FOOTPRINT_T )
{
layers = courtyards;
} }
else if( item->IsOnLayer( Edge_Cuts ) ) else if( item->IsOnLayer( Edge_Cuts ) )
{ {
layers |= LSET::PhysicalLayersMask(); layers |= LSET::PhysicalLayersMask() | courtyards;
} }
for( PCB_LAYER_ID layer : layers.Seq() ) for( PCB_LAYER_ID layer : layers.Seq() )
@ -198,7 +205,7 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::Run()
if( !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE ) if( !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE )
|| !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE ) ) || !m_drcEngine->IsErrorLimitExceeded( DRCE_HOLE_CLEARANCE ) )
{ {
if( !reportPhase( _( "Checking mechanical clearances..." ) ) ) if( !reportPhase( _( "Checking physical clearances..." ) ) )
return false; // DRC cancelled return false; // DRC cancelled
forEachGeometryItem( itemTypes, LSET::AllLayersMask(), forEachGeometryItem( itemTypes, LSET::AllLayersMask(),
@ -207,7 +214,12 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::Run()
if( !reportProgress( ii++, count, delta ) ) if( !reportProgress( ii++, count, delta ) )
return false; return false;
for( PCB_LAYER_ID layer : item->GetLayerSet().Seq() ) LSET layers = item->GetLayerSet();
if( item->Type() == PCB_FOOTPRINT_T )
layers = courtyards;
for( PCB_LAYER_ID layer : layers.Seq() )
{ {
std::shared_ptr<SHAPE> itemShape = item->GetEffectiveShape( layer ); std::shared_ptr<SHAPE> itemShape = item->GetEffectiveShape( layer );

View File

@ -47,6 +47,7 @@
#include <i18n_utility.h> #include <i18n_utility.h>
#include <drc/drc_item.h> #include <drc/drc_item.h>
#include <geometry/shape_segment.h> #include <geometry/shape_segment.h>
#include <geometry/shape_simple.h>
#include <convert_shape_list_to_polygon.h> #include <convert_shape_list_to_polygon.h>
#include <geometry/convex_hull.h> #include <geometry/convex_hull.h>
#include "fp_textbox.h" #include "fp_textbox.h"
@ -2131,8 +2132,19 @@ std::shared_ptr<SHAPE> FOOTPRINT::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHI
// 2) just the pads and "edges" (ie: non-text graphic items) // 2) just the pads and "edges" (ie: non-text graphic items)
// 3) the courtyard // 3) the courtyard
// We'll go with (2) for now.... // We'll go with (2) for now, unless the caller is clearly looking for (3)
if( aLayer == F_CrtYd || aLayer == B_CrtYd )
{
const SHAPE_POLY_SET& courtyard = GetPolyCourtyard( aLayer );
if( courtyard.OutlineCount() == 0 ) // malformed/empty polygon
return shape;
shape->AddShape( new SHAPE_SIMPLE( courtyard.COutline( 0 ) ) );
}
else
{
for( PAD* pad : Pads() ) for( PAD* pad : Pads() )
shape->AddShape( pad->GetEffectiveShape( aLayer, aFlash )->Clone() ); shape->AddShape( pad->GetEffectiveShape( aLayer, aFlash )->Clone() );
@ -2141,6 +2153,7 @@ std::shared_ptr<SHAPE> FOOTPRINT::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHI
if( item->Type() == PCB_FP_SHAPE_T ) if( item->Type() == PCB_FP_SHAPE_T )
shape->AddShape( item->GetEffectiveShape( aLayer, aFlash )->Clone() ); shape->AddShape( item->GetEffectiveShape( aLayer, aFlash )->Clone() );
} }
}
return shape; return shape;
} }

View File

@ -885,14 +885,20 @@ int BOARD_INSPECTION_TOOL::InspectClearance( const TOOL_EVENT& aEvent )
{ {
PCB_LAYER_ID layer = UNDEFINED_LAYER; PCB_LAYER_ID layer = UNDEFINED_LAYER;
if( a->IsOnLayer( edgeLayer ) && b->IsOnLayer( active ) && IsCopperLayer( active ) ) if( a->IsOnLayer( edgeLayer ) && b->Type() != PCB_FOOTPRINT_T )
{
if( b->IsOnLayer( active ) && IsCopperLayer( active ) )
layer = active; layer = active;
else if( b->IsOnLayer( edgeLayer ) && a->IsOnLayer( active ) && IsCopperLayer( active ) ) else if( IsCopperLayer( b->GetLayer() ) )
layer = active;
else if( a->IsOnLayer( edgeLayer ) && IsCopperLayer( b->GetLayer() ) )
layer = b->GetLayer(); layer = b->GetLayer();
else if( b->IsOnLayer( edgeLayer ) && IsCopperLayer( a->GetLayer() ) ) }
else if( b->IsOnLayer( edgeLayer ) && a->Type() != PCB_FOOTPRINT_T )
{
if( a->IsOnLayer( active ) && IsCopperLayer( active ) )
layer = active;
else if( IsCopperLayer( a->GetLayer() ) )
layer = a->GetLayer(); layer = a->GetLayer();
}
if( layer >= 0 ) if( layer >= 0 )
{ {
@ -916,16 +922,12 @@ int BOARD_INSPECTION_TOOL::InspectClearance( const TOOL_EVENT& aEvent )
r = m_inspectClearanceDialog->AddPage( _( "Physical Clearances" ) ); r = m_inspectClearanceDialog->AddPage( _( "Physical Clearances" ) );
if( layerIntersection.any() ) auto reportPhysicalClearance =
[&]( PCB_LAYER_ID aLayer )
{ {
PCB_LAYER_ID layer = active; reportHeader( _( "Physical clearance resolution for:" ), a, b, aLayer, r );
if( !layerIntersection.test( layer ) ) constraint = drcEngine.EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, a, b, aLayer, r );
layer = layerIntersection.Seq().front();
reportHeader( _( "Physical clearance resolution for:" ), a, b, layer, r );
constraint = drcEngine.EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, a, b, layer, r );
clearance = constraint.m_Value.Min(); clearance = constraint.m_Value.Min();
if( compileError ) if( compileError )
@ -947,6 +949,33 @@ int BOARD_INSPECTION_TOOL::InspectClearance( const TOOL_EVENT& aEvent )
r->Report( "" ); r->Report( "" );
r->Report( "" ); r->Report( "" );
r->Report( "" ); r->Report( "" );
};
if( layerIntersection.any() )
{
PCB_LAYER_ID layer = active;
if( !layerIntersection.test( layer ) )
layer = layerIntersection.Seq().front();
reportPhysicalClearance( layer );
}
if( aFP && b->IsOnLayer( Edge_Cuts ) )
{
if( !aFP->GetPolyCourtyard( F_CrtYd ).IsEmpty() )
reportPhysicalClearance( F_CrtYd );
if( !aFP->GetPolyCourtyard( B_CrtYd ).IsEmpty() )
reportPhysicalClearance( B_CrtYd );
}
else if( bFP && a->IsOnLayer( Edge_Cuts ) )
{
if( !bFP->GetPolyCourtyard( F_CrtYd ).IsEmpty() )
reportPhysicalClearance( F_CrtYd );
if( !bFP->GetPolyCourtyard( B_CrtYd ).IsEmpty() )
reportPhysicalClearance( B_CrtYd );
} }
if( hasHole( a ) || hasHole( b ) ) if( hasHole( a ) || hasHole( b ) )