Retire insideArea in favour of intersectsArea and enclosedByArea.

This commit is contained in:
Jeff Young 2022-08-28 17:41:39 +01:00
parent abc7335fd7
commit 667a54ad86
7 changed files with 171 additions and 111 deletions

View File

@ -219,8 +219,8 @@ void BOARD::IncrementTimeStamp()
{ {
std::unique_lock<std::mutex> cacheLock( m_CachesMutex ); std::unique_lock<std::mutex> cacheLock( m_CachesMutex );
m_InsideAreaCache.clear(); m_IntersectsAreaCache.clear();
m_EntirelyInsideAreaCache.clear(); m_EnclosedByAreaCache.clear();
m_InsideCourtyardCache.clear(); m_InsideCourtyardCache.clear();
m_InsideFCourtyardCache.clear(); m_InsideFCourtyardCache.clear();
m_InsideBCourtyardCache.clear(); m_InsideBCourtyardCache.clear();

View File

@ -1148,8 +1148,8 @@ public:
std::unordered_map<PTR_PTR_CACHE_KEY, bool> m_InsideCourtyardCache; std::unordered_map<PTR_PTR_CACHE_KEY, bool> m_InsideCourtyardCache;
std::unordered_map<PTR_PTR_CACHE_KEY, bool> m_InsideFCourtyardCache; std::unordered_map<PTR_PTR_CACHE_KEY, bool> m_InsideFCourtyardCache;
std::unordered_map<PTR_PTR_CACHE_KEY, bool> m_InsideBCourtyardCache; std::unordered_map<PTR_PTR_CACHE_KEY, bool> m_InsideBCourtyardCache;
std::unordered_map<PTR_PTR_LAYER_CACHE_KEY, bool> m_InsideAreaCache; std::unordered_map<PTR_PTR_LAYER_CACHE_KEY, bool> m_IntersectsAreaCache;
std::unordered_map<PTR_PTR_LAYER_CACHE_KEY, bool> m_EntirelyInsideAreaCache; std::unordered_map<PTR_PTR_LAYER_CACHE_KEY, bool> m_EnclosedByAreaCache;
std::unordered_map< wxString, LSET > m_LayerExpressionCache; std::unordered_map< wxString, LSET > m_LayerExpressionCache;
std::unordered_map<ZONE*, std::unique_ptr<DRC_RTREE>> m_CopperZoneRTreeCache; std::unordered_map<ZONE*, std::unique_ptr<DRC_RTREE>> m_CopperZoneRTreeCache;
std::unique_ptr<DRC_RTREE> m_CopperItemRTreeCache; std::unique_ptr<DRC_RTREE> m_CopperItemRTreeCache;

View File

@ -452,7 +452,10 @@ void PANEL_SETUP_RULES::onScintillaCharAdded( wxStyledTextEvent &aEvent )
PCB_EXPR_BUILTIN_FUNCTIONS& functions = PCB_EXPR_BUILTIN_FUNCTIONS::Instance(); PCB_EXPR_BUILTIN_FUNCTIONS& functions = PCB_EXPR_BUILTIN_FUNCTIONS::Instance();
for( const wxString& funcSig : functions.GetSignatures() ) for( const wxString& funcSig : functions.GetSignatures() )
tokens += wxT( "|" ) + funcSig; {
if( !funcSig.Contains( "DEPRECATED" ) )
tokens += wxT( "|" ) + funcSig;
}
} }
else if( expr_context == STRING ) else if( expr_context == STRING )
{ {

View File

@ -104,7 +104,7 @@ Note: `clearance` and `hole_clearance` rules are not run against items of the sa
(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.enclosedByArea('Shield*')"))
(rule heavy_thermals (rule heavy_thermals
@ -130,26 +130,27 @@ Use Ctrl+/ to comment or uncomment line(s).
All function parameters support simple wildcards (`*` and `?`). All function parameters support simple wildcards (`*` and `?`).
<br><br> <br><br>
A.insideCourtyard('<footprint_refdes>') A.intersectsCourtyard('<footprint_refdes>')
True if any part of `A` lies within the given footprint's principal courtyard. True if any part of `A` lies within the given footprint's principal courtyard.
<br><br> <br><br>
A.insideFrontCourtyard('<footprint_refdes>') A.intersectsFrontCourtyard('<footprint_refdes>')
True if any part of `A` lies within the given footprint's front courtyard. True if any part of `A` lies within the given footprint's front courtyard.
<br><br> <br><br>
A.insideBackCourtyard('<footprint_refdes>') A.intersectsBackCourtyard('<footprint_refdes>')
True if any part of `A` lies within the given footprint's back courtyard. True if any part of `A` lies within the given footprint's back courtyard.
<br><br> <br><br>
A.insideArea('<zone_name>') A.intersectsArea('<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> <br><br>
A.entirelyInsideArea('<zone_name>') A.enclosedByArea('<zone_name>')
True if all of `A` lies within the given zone's outline. True if all of `A` lies within the given zone's outline.
NB: this is potentially a more expensive call than `insideArea()`. NB: this is potentially a more expensive call than `intersectsArea()`. Use `intersectsArea()`
where possible.
<br><br> <br><br>
A.isPlated() A.isPlated()
@ -157,7 +158,7 @@ True if `A` has a hole which is plated.
<br><br> <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 a 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><br> <br><br>
@ -178,19 +179,35 @@ 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>
!!! A.insideCourtyard('<footprint_refdes>') !!!
Deprecated; use `intersectsCourtyard()` instead.
<br><br>
!!! A.insideFrontCourtyard('<footprint_refdes>') !!!
Deprecated; use `intersectsFrontCourtyard()` instead.
<br><br>
!!! A.insideBackCourtyard('<footprint_refdes>') !!!
Deprecated; use `intersectsBackCourtyard()` instead.
<br><br>
!!! A.insideArea('<zone_name>') !!!
Deprecated; use `intersectsArea()` instead.
<br><br><br> <br><br><br>
### More Examples ### More Examples
(rule "copper keepout" (rule "copper keepout"
(constraint disallow track via zone) (constraint disallow track via zone)
(condition "A.insideArea('zone3')")) (condition "A.intersectsArea('zone3')"))
(rule "BGA neckdown" (rule "BGA neckdown"
(constraint track_width (min 0.2mm) (opt 0.25mm)) (constraint track_width (min 0.2mm) (opt 0.25mm))
(constraint clearance (min 0.05mm) (opt 0.08mm)) (constraint clearance (min 0.05mm) (opt 0.08mm))
(condition "A.insideCourtyard('U3')")) (condition "A.intersectsCourtyard('U3')"))
# prevent silk over tented vias # prevent silk over tented vias

View File

@ -105,7 +105,7 @@ _HKI( "### Top-level Clauses\n"
"\n" "\n"
" (rule HV_unshielded\n" " (rule HV_unshielded\n"
" (constraint clearance (min 2mm))\n" " (constraint clearance (min 2mm))\n"
" (condition \"A.NetClass == 'HV' && !A.insideArea('Shield*')\"))\n" " (condition \"A.NetClass == 'HV' && !A.enclosedByArea('Shield*')\"))\n"
"\n" "\n"
"\n" "\n"
" (rule heavy_thermals\n" " (rule heavy_thermals\n"
@ -131,26 +131,27 @@ _HKI( "### Top-level Clauses\n"
"All function parameters support simple wildcards (`*` and `?`).\n" "All function parameters support simple wildcards (`*` and `?`).\n"
"<br><br>\n" "<br><br>\n"
"\n" "\n"
" A.insideCourtyard('<footprint_refdes>')\n" " A.intersectsCourtyard('<footprint_refdes>')\n"
"True if any part of `A` lies within the given footprint's principal courtyard.\n" "True if any part of `A` lies within the given footprint's principal courtyard.\n"
"<br><br>\n" "<br><br>\n"
"\n" "\n"
" A.insideFrontCourtyard('<footprint_refdes>')\n" " A.intersectsFrontCourtyard('<footprint_refdes>')\n"
"True if any part of `A` lies within the given footprint's front courtyard.\n" "True if any part of `A` lies within the given footprint's front courtyard.\n"
"<br><br>\n" "<br><br>\n"
"\n" "\n"
" A.insideBackCourtyard('<footprint_refdes>')\n" " A.intersectsBackCourtyard('<footprint_refdes>')\n"
"True if any part of `A` lies within the given footprint's back courtyard.\n" "True if any part of `A` lies within the given footprint's back courtyard.\n"
"<br><br>\n" "<br><br>\n"
"\n" "\n"
" A.insideArea('<zone_name>')\n" " A.intersectsArea('<zone_name>')\n"
"True if any part of `A` lies within the given zone's outline.\n" "True if any part of `A` lies within the given zone's outline.\n"
"<br><br>\n" "<br><br>\n"
"\n" "\n"
" A.entirelyInsideArea('<zone_name>')\n" " A.enclosedByArea('<zone_name>')\n"
"True if all of `A` lies within the given zone's outline. \n" "True if all of `A` lies within the given zone's outline. \n"
"\n" "\n"
"NB: this is potentially a more expensive call than `insideArea()`.\n" "NB: this is potentially a more expensive call than `intersectsArea()`. Use `intersectsArea()` \n"
"where possible.\n"
"<br><br>\n" "<br><br>\n"
"\n" "\n"
" A.isPlated()\n" " A.isPlated()\n"
@ -158,7 +159,7 @@ _HKI( "### Top-level Clauses\n"
"<br><br>\n" "<br><br>\n"
"\n" "\n"
" A.inDiffPair('<net_name>')\n" " A.inDiffPair('<net_name>')\n"
"True if `A` has net that is part of the specified differential pair.\n" "True if `A` has a net that is part of the specified differential pair.\n"
"`<net_name>` is the base name of the differential pair. For example, `inDiffPair('/CLK')`\n" "`<net_name>` is the base name of the differential pair. For example, `inDiffPair('/CLK')`\n"
"matches items in the `/CLK_P` and `/CLK_N` nets.\n" "matches items in the `/CLK_P` and `/CLK_N` nets.\n"
"<br><br>\n" "<br><br>\n"
@ -179,19 +180,35 @@ _HKI( "### Top-level Clauses\n"
"NB: this returns true if `A` is on the given layer, independently\n" "NB: this returns true if `A` is on the given layer, independently\n"
"of whether or not the rule is being evaluated for that layer.\n" "of whether or not the rule is being evaluated for that layer.\n"
"For the latter use a `(layer \"layer_name\")` clause in the rule.\n" "For the latter use a `(layer \"layer_name\")` clause in the rule.\n"
"<br><br>\n"
"\n"
" !!! A.insideCourtyard('<footprint_refdes>') !!!\n"
"Deprecated; use `intersectsCourtyard()` instead.\n"
"<br><br>\n"
"\n"
" !!! A.insideFrontCourtyard('<footprint_refdes>') !!!\n"
"Deprecated; use `intersectsFrontCourtyard()` instead.\n"
"<br><br>\n"
"\n"
" !!! A.insideBackCourtyard('<footprint_refdes>') !!!\n"
"Deprecated; use `intersectsBackCourtyard()` instead.\n"
"<br><br>\n"
"\n"
" !!! A.insideArea('<zone_name>') !!!\n"
"Deprecated; use `intersectsArea()` instead.\n"
"<br><br><br>\n" "<br><br><br>\n"
"\n" "\n"
"### More Examples\n" "### More Examples\n"
"\n" "\n"
" (rule \"copper keepout\"\n" " (rule \"copper keepout\"\n"
" (constraint disallow track via zone)\n" " (constraint disallow track via zone)\n"
" (condition \"A.insideArea('zone3')\"))\n" " (condition \"A.intersectsArea('zone3')\"))\n"
"\n" "\n"
"\n" "\n"
" (rule \"BGA neckdown\"\n" " (rule \"BGA neckdown\"\n"
" (constraint track_width (min 0.2mm) (opt 0.25mm))\n" " (constraint track_width (min 0.2mm) (opt 0.25mm))\n"
" (constraint clearance (min 0.05mm) (opt 0.08mm))\n" " (constraint clearance (min 0.05mm) (opt 0.08mm))\n"
" (condition \"A.insideCourtyard('U3')\"))\n" " (condition \"A.intersectsCourtyard('U3')\"))\n"
"\n" "\n"
"\n" "\n"
" # prevent silk over tented vias\n" " # prevent silk over tented vias\n"

View File

@ -154,7 +154,7 @@ bool DRC_TEST_PROVIDER_DISALLOW::Run()
{ {
std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex ); std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex );
board->m_InsideAreaCache[ key ] = isInside; board->m_IntersectsAreaCache[ key ] = isInside;
} }
done.fetch_add( 1 ); done.fetch_add( 1 );

View File

@ -38,7 +38,6 @@
#include <connectivity/from_to_cache.h> #include <connectivity/from_to_cache.h>
#include <drc/drc_engine.h> #include <drc/drc_engine.h>
#include <geometry/shape_circle.h>
bool fromToFunc( LIBEVAL::CONTEXT* aCtx, void* self ) bool fromToFunc( LIBEVAL::CONTEXT* aCtx, void* self )
{ {
@ -453,9 +452,6 @@ bool collidesWithArea( BOARD_ITEM* aItem, const EDA_RECT& aItemBBox, PCB_EXPR_CO
EDA_RECT areaBBox = aArea->GetCachedBoundingBox(); EDA_RECT areaBBox = aArea->GetCachedBoundingBox();
std::shared_ptr<SHAPE> shape; std::shared_ptr<SHAPE> shape;
if( !areaBBox.Intersects( aItemBBox ) )
return false;
// Collisions include touching, so we need to deflate outline by enough to exclude it. // Collisions include touching, so we need to deflate outline by enough to exclude it.
// This is particularly important for detecting copper fills as they will be exactly // This is particularly important for detecting copper fills as they will be exactly
// touching along the entire exclusion border. // touching along the entire exclusion border.
@ -491,9 +487,7 @@ bool collidesWithArea( BOARD_ITEM* aItem, const EDA_RECT& aItemBBox, PCB_EXPR_CO
if( ( footprint->GetFlags() & MALFORMED_COURTYARDS ) != 0 ) if( ( footprint->GetFlags() & MALFORMED_COURTYARDS ) != 0 )
{ {
if( aCtx->HasErrorCallback() ) if( aCtx->HasErrorCallback() )
{
aCtx->ReportError( _( "Footprint's courtyard is not a single, closed shape." ) ); aCtx->ReportError( _( "Footprint's courtyard is not a single, closed shape." ) );
}
return false; return false;
} }
@ -573,75 +567,18 @@ bool collidesWithArea( BOARD_ITEM* aItem, const EDA_RECT& aItemBBox, PCB_EXPR_CO
} }
bool enclosedByArea( BOARD_ITEM* aItem, const EDA_RECT& aItemBBox, PCB_LAYER_ID layer, ZONE* aArea ) bool searchAreas( BOARD_ITEM* aItem, const wxString& aArg, PCB_EXPR_CONTEXT* aCtx,
std::function<bool( ZONE* )> aFunc )
{ {
BOARD* board = aItem->GetBoard(); BOARD* board = aItem->GetBoard();
EDA_RECT areaBBox = aArea->GetCachedBoundingBox();
SHAPE_POLY_SET itemShape;
if( !areaBBox.Intersects( aItemBBox ) )
return false;
aItem->TransformShapeWithClearanceToPolygon( itemShape, layer, 0,
board->GetDesignSettings().m_MaxError,
ERROR_OUTSIDE );
itemShape.BooleanSubtract( *aArea->Outline(), SHAPE_POLY_SET::PM_FAST );
return itemShape.IsEmpty();
}
bool isInsideArea( BOARD_ITEM* aItem, const EDA_RECT& aItemBBox, PCB_EXPR_CONTEXT* aCtx,
ZONE* aArea, bool aEntirely )
{
if( !aArea || aArea == aItem || aArea->GetParent() == aItem )
return false;
BOARD* board = aArea->GetBoard();
std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex );
auto& cache = aEntirely ? board->m_EntirelyInsideAreaCache
: board->m_InsideAreaCache;
PCB_LAYER_ID layer = aCtx->GetLayer();
PTR_PTR_LAYER_CACHE_KEY key = { aArea, aItem, layer };
auto i = cache.find( key );
if( i != cache.end() )
return i->second;
bool isInside;
if( aEntirely )
isInside = enclosedByArea( aItem, aItemBBox, layer, aArea );
else
isInside = collidesWithArea( aItem, aItemBBox, aCtx, aArea );
cache[ key ] = isInside;
return isInside;
}
bool evalInsideAreaFunc( BOARD_ITEM* aItem, const wxString& aArg, PCB_EXPR_CONTEXT* aCtx,
bool aEntirely )
{
BOARD* board = aItem->GetBoard();
EDA_RECT itemBBox;
if( aItem->Type() == PCB_ZONE_T || aItem->Type() == PCB_FP_ZONE_T )
itemBBox = static_cast<ZONE*>( aItem )->GetCachedBoundingBox();
else
itemBBox = aItem->GetBoundingBox();
if( aArg == wxT( "A" ) ) if( aArg == wxT( "A" ) )
{ {
ZONE* zone = dynamic_cast<ZONE*>( aCtx->GetItem( 0 ) ); return aFunc( dynamic_cast<ZONE*>( aCtx->GetItem( 0 ) ) );
return isInsideArea( aItem, itemBBox, aCtx, zone, aEntirely );
} }
else if( aArg == wxT( "B" ) ) else if( aArg == wxT( "B" ) )
{ {
ZONE* zone = dynamic_cast<ZONE*>( aCtx->GetItem( 1 ) ); return aFunc( dynamic_cast<ZONE*>( aCtx->GetItem( 1 ) ) );
return isInsideArea( aItem, itemBBox, aCtx, zone, aEntirely );
} }
else if( KIID::SniffTest( aArg ) ) else if( KIID::SniffTest( aArg ) )
{ {
@ -652,7 +589,7 @@ bool evalInsideAreaFunc( BOARD_ITEM* aItem, const wxString& aArg, PCB_EXPR_CONTE
// Only a single zone can match the UUID; exit once we find a match whether // Only a single zone can match the UUID; exit once we find a match whether
// "inside" or not // "inside" or not
if( area->m_Uuid == target ) if( area->m_Uuid == target )
return isInsideArea( aItem, itemBBox, aCtx, area, aEntirely ); return aFunc( area );
} }
for( FOOTPRINT* footprint : board->Footprints() ) for( FOOTPRINT* footprint : board->Footprints() )
@ -662,7 +599,7 @@ bool evalInsideAreaFunc( BOARD_ITEM* aItem, const wxString& aArg, PCB_EXPR_CONTE
// Only a single zone can match the UUID; exit once we find a match // Only a single zone can match the UUID; exit once we find a match
// whether "inside" or not // whether "inside" or not
if( area->m_Uuid == target ) if( area->m_Uuid == target )
return isInsideArea( aItem, itemBBox, aCtx, area, aEntirely ); return aFunc( area );
} }
} }
@ -675,7 +612,7 @@ bool evalInsideAreaFunc( BOARD_ITEM* aItem, const wxString& aArg, PCB_EXPR_CONTE
if( area->GetZoneName().Matches( aArg ) ) if( area->GetZoneName().Matches( aArg ) )
{ {
// Many zones can match the name; exit only when we find an "inside" // Many zones can match the name; exit only when we find an "inside"
if( isInsideArea( aItem, itemBBox, aCtx, area, aEntirely ) ) if( aFunc( area ) )
return true; return true;
} }
} }
@ -687,7 +624,7 @@ bool evalInsideAreaFunc( BOARD_ITEM* aItem, const wxString& aArg, PCB_EXPR_CONTE
// Many zones can match the name; exit only when we find an "inside" // Many zones can match the name; exit only when we find an "inside"
if( area->GetZoneName().Matches( aArg ) ) if( area->GetZoneName().Matches( aArg ) )
{ {
if( isInsideArea( aItem, itemBBox, aCtx, area, aEntirely ) ) if( aFunc( area ) )
return true; return true;
} }
} }
@ -698,7 +635,7 @@ bool evalInsideAreaFunc( BOARD_ITEM* aItem, const wxString& aArg, PCB_EXPR_CONTE
} }
static void insideAreaFunc( LIBEVAL::CONTEXT* aCtx, void* self ) static void intersectsAreaFunc( 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 );
LIBEVAL::VALUE* arg = aCtx->Pop(); LIBEVAL::VALUE* arg = aCtx->Pop();
@ -713,14 +650,14 @@ static void insideAreaFunc( LIBEVAL::CONTEXT* aCtx, void* self )
{ {
aCtx->ReportError( wxString::Format( _( "Missing rule-area identifier argument " aCtx->ReportError( wxString::Format( _( "Missing rule-area identifier argument "
"(A, B, or rule-area name) to %s." ), "(A, B, or rule-area name) to %s." ),
wxT( "insideArea()" ) ) ); wxT( "intersectsArea()" ) ) );
} }
return; return;
} }
PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self ); PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr; BOARD_ITEM* item = vref ? vref->GetObject( context ) : nullptr;
if( !item ) if( !item )
return; return;
@ -728,15 +665,48 @@ static void insideAreaFunc( LIBEVAL::CONTEXT* aCtx, void* self )
result->SetDeferredEval( result->SetDeferredEval(
[item, arg, context]() -> double [item, arg, context]() -> double
{ {
if( evalInsideAreaFunc( item, arg->AsString(), context, false ) ) BOARD* board = item->GetBoard();
PCB_LAYER_ID layer = context->GetLayer();
EDA_RECT itemBBox;
if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
itemBBox = static_cast<ZONE*>( item )->GetCachedBoundingBox();
else
itemBBox = item->GetBoundingBox();
if( searchAreas( item, arg->AsString(), context,
[&]( ZONE* aArea )
{
if( !aArea || aArea == item || aArea->GetParent() == item )
return false;
if( !aArea->GetCachedBoundingBox().Intersects( itemBBox ) )
return false;
std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex );
PTR_PTR_LAYER_CACHE_KEY key = { aArea, item, layer };
auto i = board->m_IntersectsAreaCache.find( key );
if( i != board->m_IntersectsAreaCache.end() )
return i->second;
bool collides = collidesWithArea( item, itemBBox, context, aArea );
board->m_IntersectsAreaCache[ key ] = collides;
return collides;
} ) )
{
return 1.0; return 1.0;
}
return 0.0; return 0.0;
} ); } );
} }
static void entirelyInsideAreaFunc( LIBEVAL::CONTEXT* aCtx, void* self ) static void enclosedByAreaFunc( 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 );
LIBEVAL::VALUE* arg = aCtx->Pop(); LIBEVAL::VALUE* arg = aCtx->Pop();
@ -751,14 +721,14 @@ static void entirelyInsideAreaFunc( LIBEVAL::CONTEXT* aCtx, void* self )
{ {
aCtx->ReportError( wxString::Format( _( "Missing rule-area identifier argument " aCtx->ReportError( wxString::Format( _( "Missing rule-area identifier argument "
"(A, B, or rule-area name) to %s." ), "(A, B, or rule-area name) to %s." ),
wxT( "entirelyInsideArea()" ) ) ); wxT( "eclosedByArea()" ) ) );
} }
return; return;
} }
PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self ); PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr; BOARD_ITEM* item = vref ? vref->GetObject( context ) : nullptr;
if( !item ) if( !item )
return; return;
@ -766,8 +736,49 @@ static void entirelyInsideAreaFunc( LIBEVAL::CONTEXT* aCtx, void* self )
result->SetDeferredEval( result->SetDeferredEval(
[item, arg, context]() -> double [item, arg, context]() -> double
{ {
if( evalInsideAreaFunc( item, arg->AsString(), context, true ) ) BOARD* board = item->GetBoard();
int maxError = board->GetDesignSettings().m_MaxError;
PCB_LAYER_ID layer = context->GetLayer();
EDA_RECT itemBBox;
if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
itemBBox = static_cast<ZONE*>( item )->GetCachedBoundingBox();
else
itemBBox = item->GetBoundingBox();
if( searchAreas( item, arg->AsString(), context,
[&]( ZONE* aArea )
{
if( !aArea || aArea == item || aArea->GetParent() == item )
return false;
if( !aArea->GetCachedBoundingBox().Intersects( itemBBox ) )
return false;
std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex );
PTR_PTR_LAYER_CACHE_KEY key = { aArea, item, layer };
auto i = board->m_EnclosedByAreaCache.find( key );
if( i != board->m_EnclosedByAreaCache.end() )
return i->second;
SHAPE_POLY_SET itemShape;
item->TransformShapeWithClearanceToPolygon( itemShape, layer, 0,
maxError, ERROR_OUTSIDE );
itemShape.BooleanSubtract( *aArea->Outline(), SHAPE_POLY_SET::PM_FAST );
bool enclosedByArea = itemShape.IsEmpty();
board->m_EnclosedByAreaCache[ key ] = enclosedByArea;
return enclosedByArea;
} ) )
{
return 1.0; return 1.0;
}
return 0.0; return 0.0;
} ); } );
@ -987,19 +998,31 @@ PCB_EXPR_BUILTIN_FUNCTIONS::PCB_EXPR_BUILTIN_FUNCTIONS()
void PCB_EXPR_BUILTIN_FUNCTIONS::RegisterAllFunctions() void PCB_EXPR_BUILTIN_FUNCTIONS::RegisterAllFunctions()
{ {
m_funcs.clear(); m_funcs.clear();
RegisterFunc( wxT( "existsOnLayer('x')" ), existsOnLayerFunc ); RegisterFunc( wxT( "existsOnLayer('x')" ), existsOnLayerFunc );
RegisterFunc( wxT( "isPlated()" ), isPlatedFunc ); RegisterFunc( wxT( "isPlated()" ), isPlatedFunc );
RegisterFunc( wxT( "insideCourtyard('x')" ), insideCourtyardFunc );
RegisterFunc( wxT( "insideFrontCourtyard('x')" ), insideFrontCourtyardFunc ); RegisterFunc( wxT( "insideCourtyard('x') DEPRECATED" ), insideCourtyardFunc );
RegisterFunc( wxT( "insideBackCourtyard('x')" ), insideBackCourtyardFunc ); RegisterFunc( wxT( "insideFrontCourtyard('x') DEPRECATED" ), insideFrontCourtyardFunc );
RegisterFunc( wxT( "insideArea('x')" ), insideAreaFunc ); RegisterFunc( wxT( "insideBackCourtyard('x') DEPRECATED" ), insideBackCourtyardFunc );
RegisterFunc( wxT( "entirelyInsideArea('x')" ), entirelyInsideAreaFunc ); RegisterFunc( wxT( "intersectsCourtyard('x')" ), insideCourtyardFunc );
RegisterFunc( wxT( "intersectsFrontCourtyard('x')" ), insideFrontCourtyardFunc );
RegisterFunc( wxT( "intersectsBackCourtyard('x')" ), insideBackCourtyardFunc );
RegisterFunc( wxT( "insideArea('x') DEPRECATED" ), intersectsAreaFunc );
RegisterFunc( wxT( "intersectsArea('x')" ), intersectsAreaFunc );
RegisterFunc( wxT( "enclosedByArea('x')" ), enclosedByAreaFunc );
RegisterFunc( wxT( "isMicroVia()" ), isMicroVia ); RegisterFunc( wxT( "isMicroVia()" ), isMicroVia );
RegisterFunc( wxT( "isBlindBuriedVia()" ), isBlindBuriedViaFunc ); RegisterFunc( wxT( "isBlindBuriedVia()" ), isBlindBuriedViaFunc );
RegisterFunc( wxT( "memberOf('x')" ), memberOfFunc ); RegisterFunc( wxT( "memberOf('x')" ), memberOfFunc );
RegisterFunc( wxT( "fromTo('x','y')" ), fromToFunc ); RegisterFunc( wxT( "fromTo('x','y')" ), fromToFunc );
RegisterFunc( wxT( "isCoupledDiffPair()" ), isCoupledDiffPairFunc ); RegisterFunc( wxT( "isCoupledDiffPair()" ), isCoupledDiffPairFunc );
RegisterFunc( wxT( "inDiffPair('x')" ), inDiffPairFunc ); RegisterFunc( wxT( "inDiffPair('x')" ), inDiffPairFunc );
RegisterFunc( wxT( "getField('x')" ), getFieldFunc ); RegisterFunc( wxT( "getField('x')" ), getFieldFunc );
} }