Make sure physical_clearance rules are run against courtyard layers.
This commit is contained in:
parent
d81378d786
commit
37838dffb9
|
@ -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'"))
|
||||||
|
|
|
@ -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 );
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 ) )
|
||||||
|
|
Loading…
Reference in New Issue