Add insideFrontCourtyard and insideBackCourtyard DRC functions.
Fixes https://gitlab.com/kicad/code/kicad/issues/7972
This commit is contained in:
parent
eb5a85ba91
commit
ce46fe6581
|
@ -276,6 +276,8 @@ public:
|
|||
std::unique_lock<std::mutex> cacheLock( m_CachesMutex );
|
||||
m_InsideAreaCache.clear();
|
||||
m_InsideCourtyardCache.clear();
|
||||
m_InsideFCourtyardCache.clear();
|
||||
m_InsideBCourtyardCache.clear();
|
||||
}
|
||||
|
||||
m_CopperZoneRTrees.clear();
|
||||
|
@ -1149,6 +1151,8 @@ public:
|
|||
|
||||
std::mutex m_CachesMutex;
|
||||
std::map< std::pair<BOARD_ITEM*, BOARD_ITEM*>, bool > m_InsideCourtyardCache;
|
||||
std::map< std::pair<BOARD_ITEM*, BOARD_ITEM*>, bool > m_InsideFCourtyardCache;
|
||||
std::map< std::pair<BOARD_ITEM*, BOARD_ITEM*>, bool > m_InsideBCourtyardCache;
|
||||
std::map< std::pair<BOARD_ITEM*, BOARD_ITEM*>, bool > m_InsideAreaCache;
|
||||
|
||||
std::map< ZONE*, std::unique_ptr<DRC_RTREE> > m_CopperZoneRTrees;
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
(rule <rule_name> <rule_clause> ...)
|
||||
|
||||
|
||||
<br><br>
|
||||
|
||||
### Rule Clauses
|
||||
|
||||
(constraint <constraint_type> ...)
|
||||
|
@ -14,6 +16,8 @@
|
|||
(layer "<layer_name>")
|
||||
|
||||
|
||||
<br><br>
|
||||
|
||||
### Constraint Types
|
||||
|
||||
* annular_width
|
||||
|
@ -32,6 +36,8 @@
|
|||
* via_count
|
||||
|
||||
|
||||
<br><br>
|
||||
|
||||
### Item Types
|
||||
|
||||
* buried_via
|
||||
|
@ -44,6 +50,7 @@
|
|||
* via
|
||||
* zone
|
||||
|
||||
<br>
|
||||
|
||||
### Examples
|
||||
|
||||
|
@ -67,7 +74,7 @@
|
|||
(rule HV_unshielded
|
||||
(constraint clearance (min 2mm))
|
||||
(condition "A.NetClass == 'HV' && !A.insideArea('Shield*')"))
|
||||
|
||||
<br><br>
|
||||
|
||||
### Notes
|
||||
|
||||
|
@ -78,32 +85,45 @@ precedence over earlier rules; once a matching rule is found
|
|||
no further rules will be checked.
|
||||
|
||||
Use Ctrl+/ to comment or uncomment line(s).
|
||||
|
||||
|
||||
<br><br><br>
|
||||
|
||||
### Expression functions
|
||||
|
||||
All function parameters support simple wildcards (`*` and `?`).
|
||||
<br><br>
|
||||
|
||||
A.insideCourtyard('<footprint_refdes>')
|
||||
True if any part of `A` lies within the given footprint's courtyard.
|
||||
True if any part of `A` lies within the given footprint's principal courtyard.
|
||||
<br><br>
|
||||
|
||||
A.insideFrontCourtyard('<footprint_refdes>')
|
||||
True if any part of `A` lies within the given footprint's front courtyard.
|
||||
<br><br>
|
||||
|
||||
A.insideBackCourtyard('<footprint_refdes>')
|
||||
True if any part of `A` lies within the given footprint's back courtyard.
|
||||
<br><br>
|
||||
|
||||
A.insideArea('<zone_name>')
|
||||
True if any part of `A` lies within the given zone's outline.
|
||||
<br><br>
|
||||
|
||||
A.isPlated()
|
||||
True if `A` has a hole which is plated.
|
||||
<br><br>
|
||||
|
||||
A.isDiffPair()
|
||||
True if `A` has a net that is part of a differential pair.
|
||||
<br><br>
|
||||
|
||||
A.inDiffPair('<net_name>')
|
||||
True if `A` has net that is part of the specified differential pair.
|
||||
`<net_name>` is the base name of the differential pair. For example, `inDiffPair('CLK')`
|
||||
matches items in the `CLK_P` and `CLK_N` nets.
|
||||
matches items in the `CLK_P` and `CLK_N` nets.<br>
|
||||
|
||||
A.memberOf('<group_name>')
|
||||
True if `A` is a member of the given group. Includes nested membership.
|
||||
<br><br>
|
||||
|
||||
A.existsOnLayer('<layer_name>')
|
||||
True if `A` exists on the given layer. The layer name can be
|
||||
|
@ -113,7 +133,7 @@ the canonical name (ie: `F.Cu`).
|
|||
NB: this returns true if `A` is on the given layer, independently
|
||||
of whether or not the rule is being evaluated for that layer.
|
||||
For the latter use a `(layer "layer_name")` clause in the rule.
|
||||
|
||||
<br><br><br>
|
||||
|
||||
### More Examples
|
||||
|
||||
|
|
|
@ -135,6 +135,40 @@ static void isPlated( LIBEVAL::CONTEXT* aCtx, void* self )
|
|||
}
|
||||
|
||||
|
||||
static bool insideFootprintCourtyard( BOARD_ITEM* aItem, const EDA_RECT& aItemBBox,
|
||||
std::shared_ptr<SHAPE> aItemShape, PCB_EXPR_CONTEXT* aCtx,
|
||||
FOOTPRINT* aFootprint, PCB_LAYER_ID aSide = UNDEFINED_LAYER )
|
||||
{
|
||||
SHAPE_POLY_SET footprintCourtyard;
|
||||
|
||||
if( aSide == F_Cu )
|
||||
footprintCourtyard = aFootprint->GetPolyCourtyardFront();
|
||||
else if( aSide == B_Cu )
|
||||
footprintCourtyard = aFootprint->GetPolyCourtyardBack();
|
||||
else if( aFootprint->IsFlipped() )
|
||||
footprintCourtyard = aFootprint->GetPolyCourtyardBack();
|
||||
else
|
||||
footprintCourtyard = aFootprint->GetPolyCourtyardFront();
|
||||
|
||||
if( aItem->Type() == PCB_ZONE_T || aItem->Type() == PCB_FP_ZONE_T )
|
||||
{
|
||||
// A zone must be entirely inside the courtyard to be considered
|
||||
if( !aFootprint->GetBoundingBox().Contains( aItemBBox ) )
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !aFootprint->GetBoundingBox().Intersects( aItemBBox ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !aItemShape )
|
||||
aItemShape = aItem->GetEffectiveShape( aCtx->GetLayer() );
|
||||
|
||||
return footprintCourtyard.Collide( aItemShape.get() );
|
||||
};
|
||||
|
||||
|
||||
static void insideCourtyard( LIBEVAL::CONTEXT* aCtx, void* self )
|
||||
{
|
||||
PCB_EXPR_CONTEXT* context = static_cast<PCB_EXPR_CONTEXT*>( aCtx );
|
||||
|
@ -166,34 +200,6 @@ static void insideCourtyard( LIBEVAL::CONTEXT* aCtx, void* self )
|
|||
else
|
||||
itemBBox = item->GetBoundingBox();
|
||||
|
||||
auto realInsideFootprint =
|
||||
[&]( FOOTPRINT* footprint ) -> bool
|
||||
{
|
||||
SHAPE_POLY_SET footprintCourtyard;
|
||||
|
||||
if( footprint->IsFlipped() )
|
||||
footprintCourtyard = footprint->GetPolyCourtyardBack();
|
||||
else
|
||||
footprintCourtyard = footprint->GetPolyCourtyardFront();
|
||||
|
||||
if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
|
||||
{
|
||||
// A zone must be entirely inside the courtyard to be considered
|
||||
if( !footprint->GetBoundingBox().Contains( itemBBox ) )
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !footprint->GetBoundingBox().Intersects( itemBBox ) )
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !shape )
|
||||
shape = item->GetEffectiveShape( context->GetLayer() );
|
||||
|
||||
return footprintCourtyard.Collide( shape.get() );
|
||||
};
|
||||
|
||||
auto insideFootprint =
|
||||
[&]( FOOTPRINT* footprint ) -> bool
|
||||
{
|
||||
|
@ -207,10 +213,166 @@ static void insideCourtyard( LIBEVAL::CONTEXT* aCtx, void* self )
|
|||
if( i != board->m_InsideCourtyardCache.end() )
|
||||
return i->second;
|
||||
|
||||
bool isInside = realInsideFootprint( footprint );
|
||||
bool result = insideFootprintCourtyard( item, itemBBox, shape, context, footprint );
|
||||
|
||||
board->m_InsideCourtyardCache[ key ] = isInside;
|
||||
return isInside;
|
||||
board->m_InsideCourtyardCache[ key ] = result;
|
||||
return result;
|
||||
};
|
||||
|
||||
if( arg->AsString() == "A" )
|
||||
{
|
||||
if( insideFootprint( dynamic_cast<FOOTPRINT*>( context->GetItem( 0 ) ) ) )
|
||||
result->Set( 1.0 );
|
||||
}
|
||||
else if( arg->AsString() == "B" )
|
||||
{
|
||||
if( insideFootprint( dynamic_cast<FOOTPRINT*>( context->GetItem( 1 ) ) ) )
|
||||
result->Set( 1.0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
for( FOOTPRINT* candidate : board->Footprints() )
|
||||
{
|
||||
if( candidate->GetReference().Matches( arg->AsString() ) )
|
||||
{
|
||||
if( insideFootprint( candidate ) )
|
||||
{
|
||||
result->Set( 1.0 );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void insideFrontCourtyard( LIBEVAL::CONTEXT* aCtx, void* self )
|
||||
{
|
||||
PCB_EXPR_CONTEXT* context = static_cast<PCB_EXPR_CONTEXT*>( aCtx );
|
||||
LIBEVAL::VALUE* arg = aCtx->Pop();
|
||||
LIBEVAL::VALUE* result = aCtx->AllocValue();
|
||||
|
||||
result->Set( 0.0 );
|
||||
aCtx->Push( result );
|
||||
|
||||
if( !arg )
|
||||
{
|
||||
aCtx->ReportError( wxString::Format( _( "Missing argument to '%s'" ),
|
||||
wxT( "insideFrontCourtyard()" ) ) );
|
||||
return;
|
||||
}
|
||||
|
||||
PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
|
||||
BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
|
||||
|
||||
if( !item )
|
||||
return;
|
||||
|
||||
BOARD* board = item->GetBoard();
|
||||
EDA_RECT itemBBox;
|
||||
std::shared_ptr<SHAPE> shape;
|
||||
|
||||
if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
|
||||
itemBBox = static_cast<ZONE*>( item )->GetCachedBoundingBox();
|
||||
else
|
||||
itemBBox = item->GetBoundingBox();
|
||||
|
||||
auto insideFootprint =
|
||||
[&]( FOOTPRINT* footprint ) -> bool
|
||||
{
|
||||
if( !footprint )
|
||||
return false;
|
||||
|
||||
std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex );
|
||||
std::pair<BOARD_ITEM*, BOARD_ITEM*> key( footprint, item );
|
||||
auto i = board->m_InsideFCourtyardCache.find( key );
|
||||
|
||||
if( i != board->m_InsideFCourtyardCache.end() )
|
||||
return i->second;
|
||||
|
||||
bool result = insideFootprintCourtyard( item, itemBBox, shape, context, footprint,
|
||||
F_Cu );
|
||||
|
||||
board->m_InsideFCourtyardCache[ key ] = result;
|
||||
return result;
|
||||
};
|
||||
|
||||
if( arg->AsString() == "A" )
|
||||
{
|
||||
if( insideFootprint( dynamic_cast<FOOTPRINT*>( context->GetItem( 0 ) ) ) )
|
||||
result->Set( 1.0 );
|
||||
}
|
||||
else if( arg->AsString() == "B" )
|
||||
{
|
||||
if( insideFootprint( dynamic_cast<FOOTPRINT*>( context->GetItem( 1 ) ) ) )
|
||||
result->Set( 1.0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
for( FOOTPRINT* candidate : board->Footprints() )
|
||||
{
|
||||
if( candidate->GetReference().Matches( arg->AsString() ) )
|
||||
{
|
||||
if( insideFootprint( candidate ) )
|
||||
{
|
||||
result->Set( 1.0 );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void insideBackCourtyard( LIBEVAL::CONTEXT* aCtx, void* self )
|
||||
{
|
||||
PCB_EXPR_CONTEXT* context = static_cast<PCB_EXPR_CONTEXT*>( aCtx );
|
||||
LIBEVAL::VALUE* arg = aCtx->Pop();
|
||||
LIBEVAL::VALUE* result = aCtx->AllocValue();
|
||||
|
||||
result->Set( 0.0 );
|
||||
aCtx->Push( result );
|
||||
|
||||
if( !arg )
|
||||
{
|
||||
aCtx->ReportError( wxString::Format( _( "Missing argument to '%s'" ),
|
||||
wxT( "insideBackCourtyard()" ) ) );
|
||||
return;
|
||||
}
|
||||
|
||||
PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
|
||||
BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
|
||||
|
||||
if( !item )
|
||||
return;
|
||||
|
||||
BOARD* board = item->GetBoard();
|
||||
EDA_RECT itemBBox;
|
||||
std::shared_ptr<SHAPE> shape;
|
||||
|
||||
if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
|
||||
itemBBox = static_cast<ZONE*>( item )->GetCachedBoundingBox();
|
||||
else
|
||||
itemBBox = item->GetBoundingBox();
|
||||
|
||||
auto insideFootprint =
|
||||
[&]( FOOTPRINT* footprint ) -> bool
|
||||
{
|
||||
if( !footprint )
|
||||
return false;
|
||||
|
||||
std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex );
|
||||
std::pair<BOARD_ITEM*, BOARD_ITEM*> key( footprint, item );
|
||||
auto i = board->m_InsideBCourtyardCache.find( key );
|
||||
|
||||
if( i != board->m_InsideBCourtyardCache.end() )
|
||||
return i->second;
|
||||
|
||||
bool result = insideFootprintCourtyard( item, itemBBox, shape, context, footprint,
|
||||
B_Cu );
|
||||
|
||||
board->m_InsideBCourtyardCache[ key ] = result;
|
||||
return result;
|
||||
};
|
||||
|
||||
if( arg->AsString() == "A" )
|
||||
|
@ -613,6 +775,8 @@ void PCB_EXPR_BUILTIN_FUNCTIONS::RegisterAllFunctions()
|
|||
RegisterFunc( "existsOnLayer('x')", existsOnLayer );
|
||||
RegisterFunc( "isPlated()", isPlated );
|
||||
RegisterFunc( "insideCourtyard('x')", insideCourtyard );
|
||||
RegisterFunc( "insideFrontCourtyard('x')", insideFrontCourtyard );
|
||||
RegisterFunc( "insideBackCourtyard('x')", insideBackCourtyard );
|
||||
RegisterFunc( "insideArea('x')", insideArea );
|
||||
RegisterFunc( "isMicroVia()", isMicroVia );
|
||||
RegisterFunc( "isBlindBuriedVia()", isBlindBuriedVia );
|
||||
|
|
Loading…
Reference in New Issue