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

View File

@ -47,6 +47,7 @@
#include <i18n_utility.h>
#include <drc/drc_item.h>
#include <geometry/shape_segment.h>
#include <geometry/shape_simple.h>
#include <convert_shape_list_to_polygon.h>
#include <geometry/convex_hull.h>
#include "fp_textbox.h"
@ -2131,15 +2132,27 @@ std::shared_ptr<SHAPE> FOOTPRINT::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHI
// 2) just the pads and "edges" (ie: non-text graphic items)
// 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)
for( PAD* pad : Pads() )
shape->AddShape( pad->GetEffectiveShape( aLayer, aFlash )->Clone() );
for( BOARD_ITEM* item : GraphicalItems() )
if( aLayer == F_CrtYd || aLayer == B_CrtYd )
{
if( item->Type() == PCB_FP_SHAPE_T )
shape->AddShape( item->GetEffectiveShape( aLayer, aFlash )->Clone() );
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() )
shape->AddShape( pad->GetEffectiveShape( aLayer, aFlash )->Clone() );
for( BOARD_ITEM* item : GraphicalItems() )
{
if( item->Type() == PCB_FP_SHAPE_T )
shape->AddShape( item->GetEffectiveShape( aLayer, aFlash )->Clone() );
}
}
return shape;

View File

@ -885,14 +885,20 @@ int BOARD_INSPECTION_TOOL::InspectClearance( const TOOL_EVENT& aEvent )
{
PCB_LAYER_ID layer = UNDEFINED_LAYER;
if( a->IsOnLayer( edgeLayer ) && b->IsOnLayer( active ) && IsCopperLayer( active ) )
layer = active;
else if( b->IsOnLayer( edgeLayer ) && a->IsOnLayer( active ) && IsCopperLayer( active ) )
layer = active;
else if( a->IsOnLayer( edgeLayer ) && IsCopperLayer( b->GetLayer() ) )
layer = b->GetLayer();
else if( b->IsOnLayer( edgeLayer ) && IsCopperLayer( a->GetLayer() ) )
layer = a->GetLayer();
if( a->IsOnLayer( edgeLayer ) && b->Type() != PCB_FOOTPRINT_T )
{
if( b->IsOnLayer( active ) && IsCopperLayer( active ) )
layer = active;
else if( IsCopperLayer( b->GetLayer() ) )
layer = b->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();
}
if( layer >= 0 )
{
@ -916,6 +922,35 @@ int BOARD_INSPECTION_TOOL::InspectClearance( const TOOL_EVENT& aEvent )
r = m_inspectClearanceDialog->AddPage( _( "Physical Clearances" ) );
auto reportPhysicalClearance =
[&]( PCB_LAYER_ID aLayer )
{
reportHeader( _( "Physical clearance resolution for:" ), a, b, aLayer, r );
constraint = drcEngine.EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, a, b, aLayer, r );
clearance = constraint.m_Value.Min();
if( compileError )
{
reportCompileError( r );
}
else if( !drcEngine.HasRulesForConstraintType( PHYSICAL_CLEARANCE_CONSTRAINT ) )
{
r->Report( "" );
r->Report( _( "No 'physical_clearance' constraints defined." ) );
}
else
{
r->Report( "" );
r->Report( wxString::Format( _( "Resolved clearance: %s." ),
StringFromValue( units, clearance, true ) ) );
}
r->Report( "" );
r->Report( "" );
r->Report( "" );
};
if( layerIntersection.any() )
{
PCB_LAYER_ID layer = active;
@ -923,30 +958,24 @@ int BOARD_INSPECTION_TOOL::InspectClearance( const TOOL_EVENT& aEvent )
if( !layerIntersection.test( layer ) )
layer = layerIntersection.Seq().front();
reportHeader( _( "Physical clearance resolution for:" ), a, b, layer, r );
reportPhysicalClearance( layer );
}
constraint = drcEngine.EvalRules( PHYSICAL_CLEARANCE_CONSTRAINT, a, b, layer, r );
clearance = constraint.m_Value.Min();
if( aFP && b->IsOnLayer( Edge_Cuts ) )
{
if( !aFP->GetPolyCourtyard( F_CrtYd ).IsEmpty() )
reportPhysicalClearance( F_CrtYd );
if( compileError )
{
reportCompileError( r );
}
else if( !drcEngine.HasRulesForConstraintType( PHYSICAL_CLEARANCE_CONSTRAINT ) )
{
r->Report( "" );
r->Report( _( "No 'physical_clearance' constraints defined." ) );
}
else
{
r->Report( "" );
r->Report( wxString::Format( _( "Resolved clearance: %s." ),
StringFromValue( units, clearance, true ) ) );
}
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 );
r->Report( "" );
r->Report( "" );
r->Report( "" );
if( !bFP->GetPolyCourtyard( B_CrtYd ).IsEmpty() )
reportPhysicalClearance( B_CrtYd );
}
if( hasHole( a ) || hasHole( b ) )