Add insideFrontCourtyard and insideBackCourtyard DRC functions.

Fixes https://gitlab.com/kicad/code/kicad/issues/7972
This commit is contained in:
Jeff Young 2021-03-20 20:09:57 +00:00
parent eb5a85ba91
commit ce46fe6581
3 changed files with 225 additions and 37 deletions

View File

@ -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;

View File

@ -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

View File

@ -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 );