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,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) // 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)
for( PAD* pad : Pads() ) if( aLayer == F_CrtYd || aLayer == B_CrtYd )
shape->AddShape( pad->GetEffectiveShape( aLayer, aFlash )->Clone() );
for( BOARD_ITEM* item : GraphicalItems() )
{ {
if( item->Type() == PCB_FP_SHAPE_T ) const SHAPE_POLY_SET& courtyard = GetPolyCourtyard( aLayer );
shape->AddShape( item->GetEffectiveShape( aLayer, aFlash )->Clone() );
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; 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 )
layer = active; {
else if( b->IsOnLayer( edgeLayer ) && a->IsOnLayer( active ) && IsCopperLayer( active ) ) if( b->IsOnLayer( active ) && IsCopperLayer( active ) )
layer = active; layer = active;
else if( a->IsOnLayer( edgeLayer ) && IsCopperLayer( b->GetLayer() ) ) else if( IsCopperLayer( b->GetLayer() ) )
layer = b->GetLayer(); layer = b->GetLayer();
else if( b->IsOnLayer( edgeLayer ) && IsCopperLayer( a->GetLayer() ) ) }
layer = 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();
}
if( layer >= 0 ) if( layer >= 0 )
{ {
@ -916,6 +922,35 @@ int BOARD_INSPECTION_TOOL::InspectClearance( const TOOL_EVENT& aEvent )
r = m_inspectClearanceDialog->AddPage( _( "Physical Clearances" ) ); 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() ) if( layerIntersection.any() )
{ {
PCB_LAYER_ID layer = active; PCB_LAYER_ID layer = active;
@ -923,30 +958,24 @@ int BOARD_INSPECTION_TOOL::InspectClearance( const TOOL_EVENT& aEvent )
if( !layerIntersection.test( layer ) ) if( !layerIntersection.test( layer ) )
layer = layerIntersection.Seq().front(); 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 ); if( aFP && b->IsOnLayer( Edge_Cuts ) )
clearance = constraint.m_Value.Min(); {
if( !aFP->GetPolyCourtyard( F_CrtYd ).IsEmpty() )
reportPhysicalClearance( F_CrtYd );
if( compileError ) if( !aFP->GetPolyCourtyard( B_CrtYd ).IsEmpty() )
{ reportPhysicalClearance( B_CrtYd );
reportCompileError( r ); }
} else if( bFP && a->IsOnLayer( Edge_Cuts ) )
else if( !drcEngine.HasRulesForConstraintType( PHYSICAL_CLEARANCE_CONSTRAINT ) ) {
{ if( !bFP->GetPolyCourtyard( F_CrtYd ).IsEmpty() )
r->Report( "" ); reportPhysicalClearance( F_CrtYd );
r->Report( _( "No 'physical_clearance' constraints defined." ) );
}
else
{
r->Report( "" );
r->Report( wxString::Format( _( "Resolved clearance: %s." ),
StringFromValue( units, clearance, true ) ) );
}
r->Report( "" ); if( !bFP->GetPolyCourtyard( B_CrtYd ).IsEmpty() )
r->Report( "" ); reportPhysicalClearance( B_CrtYd );
r->Report( "" );
} }
if( hasHole( a ) || hasHole( b ) ) if( hasHole( a ) || hasHole( b ) )