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 );
|
std::unique_lock<std::mutex> cacheLock( m_CachesMutex );
|
||||||
m_InsideAreaCache.clear();
|
m_InsideAreaCache.clear();
|
||||||
m_InsideCourtyardCache.clear();
|
m_InsideCourtyardCache.clear();
|
||||||
|
m_InsideFCourtyardCache.clear();
|
||||||
|
m_InsideBCourtyardCache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_CopperZoneRTrees.clear();
|
m_CopperZoneRTrees.clear();
|
||||||
|
@ -1149,6 +1151,8 @@ public:
|
||||||
|
|
||||||
std::mutex m_CachesMutex;
|
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_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< std::pair<BOARD_ITEM*, BOARD_ITEM*>, bool > m_InsideAreaCache;
|
||||||
|
|
||||||
std::map< ZONE*, std::unique_ptr<DRC_RTREE> > m_CopperZoneRTrees;
|
std::map< ZONE*, std::unique_ptr<DRC_RTREE> > m_CopperZoneRTrees;
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
(rule <rule_name> <rule_clause> ...)
|
(rule <rule_name> <rule_clause> ...)
|
||||||
|
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
|
||||||
### Rule Clauses
|
### Rule Clauses
|
||||||
|
|
||||||
(constraint <constraint_type> ...)
|
(constraint <constraint_type> ...)
|
||||||
|
@ -14,6 +16,8 @@
|
||||||
(layer "<layer_name>")
|
(layer "<layer_name>")
|
||||||
|
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
|
||||||
### Constraint Types
|
### Constraint Types
|
||||||
|
|
||||||
* annular_width
|
* annular_width
|
||||||
|
@ -32,6 +36,8 @@
|
||||||
* via_count
|
* via_count
|
||||||
|
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
|
||||||
### Item Types
|
### Item Types
|
||||||
|
|
||||||
* buried_via
|
* buried_via
|
||||||
|
@ -44,6 +50,7 @@
|
||||||
* via
|
* via
|
||||||
* zone
|
* zone
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
|
@ -67,7 +74,7 @@
|
||||||
(rule HV_unshielded
|
(rule HV_unshielded
|
||||||
(constraint clearance (min 2mm))
|
(constraint clearance (min 2mm))
|
||||||
(condition "A.NetClass == 'HV' && !A.insideArea('Shield*')"))
|
(condition "A.NetClass == 'HV' && !A.insideArea('Shield*')"))
|
||||||
|
<br><br>
|
||||||
|
|
||||||
### Notes
|
### Notes
|
||||||
|
|
||||||
|
@ -78,32 +85,45 @@ precedence over earlier rules; once a matching rule is found
|
||||||
no further rules will be checked.
|
no further rules will be checked.
|
||||||
|
|
||||||
Use Ctrl+/ to comment or uncomment line(s).
|
Use Ctrl+/ to comment or uncomment line(s).
|
||||||
|
<br><br><br>
|
||||||
|
|
||||||
|
|
||||||
### Expression functions
|
### Expression functions
|
||||||
|
|
||||||
All function parameters support simple wildcards (`*` and `?`).
|
All function parameters support simple wildcards (`*` and `?`).
|
||||||
|
<br><br>
|
||||||
|
|
||||||
A.insideCourtyard('<footprint_refdes>')
|
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>')
|
A.insideArea('<zone_name>')
|
||||||
True if any part of `A` lies within the given zone's outline.
|
True if any part of `A` lies within the given zone's outline.
|
||||||
|
<br><br>
|
||||||
|
|
||||||
A.isPlated()
|
A.isPlated()
|
||||||
True if `A` has a hole which is plated.
|
True if `A` has a hole which is plated.
|
||||||
|
<br><br>
|
||||||
|
|
||||||
A.isDiffPair()
|
A.isDiffPair()
|
||||||
True if `A` has a net that is part of a differential pair.
|
True if `A` has a net that is part of a differential pair.
|
||||||
|
<br><br>
|
||||||
|
|
||||||
A.inDiffPair('<net_name>')
|
A.inDiffPair('<net_name>')
|
||||||
True if `A` has net that is part of the specified differential pair.
|
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')`
|
`<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>')
|
A.memberOf('<group_name>')
|
||||||
True if `A` is a member of the given group. Includes nested membership.
|
True if `A` is a member of the given group. Includes nested membership.
|
||||||
|
<br><br>
|
||||||
|
|
||||||
A.existsOnLayer('<layer_name>')
|
A.existsOnLayer('<layer_name>')
|
||||||
True if `A` exists on the given layer. The layer name can be
|
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
|
NB: this returns true if `A` is on the given layer, independently
|
||||||
of whether or not the rule is being evaluated for that layer.
|
of whether or not the rule is being evaluated for that layer.
|
||||||
For the latter use a `(layer "layer_name")` clause in the rule.
|
For the latter use a `(layer "layer_name")` clause in the rule.
|
||||||
|
<br><br><br>
|
||||||
|
|
||||||
### More Examples
|
### 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 )
|
static void insideCourtyard( LIBEVAL::CONTEXT* aCtx, void* self )
|
||||||
{
|
{
|
||||||
PCB_EXPR_CONTEXT* context = static_cast<PCB_EXPR_CONTEXT*>( aCtx );
|
PCB_EXPR_CONTEXT* context = static_cast<PCB_EXPR_CONTEXT*>( aCtx );
|
||||||
|
@ -166,34 +200,6 @@ static void insideCourtyard( LIBEVAL::CONTEXT* aCtx, void* self )
|
||||||
else
|
else
|
||||||
itemBBox = item->GetBoundingBox();
|
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 =
|
auto insideFootprint =
|
||||||
[&]( FOOTPRINT* footprint ) -> bool
|
[&]( FOOTPRINT* footprint ) -> bool
|
||||||
{
|
{
|
||||||
|
@ -207,10 +213,166 @@ static void insideCourtyard( LIBEVAL::CONTEXT* aCtx, void* self )
|
||||||
if( i != board->m_InsideCourtyardCache.end() )
|
if( i != board->m_InsideCourtyardCache.end() )
|
||||||
return i->second;
|
return i->second;
|
||||||
|
|
||||||
bool isInside = realInsideFootprint( footprint );
|
bool result = insideFootprintCourtyard( item, itemBBox, shape, context, footprint );
|
||||||
|
|
||||||
board->m_InsideCourtyardCache[ key ] = isInside;
|
board->m_InsideCourtyardCache[ key ] = result;
|
||||||
return isInside;
|
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" )
|
if( arg->AsString() == "A" )
|
||||||
|
@ -613,6 +775,8 @@ void PCB_EXPR_BUILTIN_FUNCTIONS::RegisterAllFunctions()
|
||||||
RegisterFunc( "existsOnLayer('x')", existsOnLayer );
|
RegisterFunc( "existsOnLayer('x')", existsOnLayer );
|
||||||
RegisterFunc( "isPlated()", isPlated );
|
RegisterFunc( "isPlated()", isPlated );
|
||||||
RegisterFunc( "insideCourtyard('x')", insideCourtyard );
|
RegisterFunc( "insideCourtyard('x')", insideCourtyard );
|
||||||
|
RegisterFunc( "insideFrontCourtyard('x')", insideFrontCourtyard );
|
||||||
|
RegisterFunc( "insideBackCourtyard('x')", insideBackCourtyard );
|
||||||
RegisterFunc( "insideArea('x')", insideArea );
|
RegisterFunc( "insideArea('x')", insideArea );
|
||||||
RegisterFunc( "isMicroVia()", isMicroVia );
|
RegisterFunc( "isMicroVia()", isMicroVia );
|
||||||
RegisterFunc( "isBlindBuriedVia()", isBlindBuriedVia );
|
RegisterFunc( "isBlindBuriedVia()", isBlindBuriedVia );
|
||||||
|
|
Loading…
Reference in New Issue