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

View File

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

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