|
|
@ -188,9 +188,9 @@ 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 ) |
|
|
|
bool calcIsInsideCourtyard( BOARD_ITEM* aItem, const EDA_RECT& aItemBBox, |
|
|
|
std::shared_ptr<SHAPE>& aItemShape, PCB_EXPR_CONTEXT* aCtx, |
|
|
|
FOOTPRINT* aFootprint, PCB_LAYER_ID aSide ) |
|
|
|
{ |
|
|
|
SHAPE_POLY_SET footprintCourtyard; |
|
|
|
|
|
|
@ -215,6 +215,38 @@ static bool insideFootprintCourtyard( BOARD_ITEM* aItem, const EDA_RECT& aItemBB |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
bool isInsideCourtyard( BOARD_ITEM* aItem, const EDA_RECT& aItemBBox, |
|
|
|
std::shared_ptr<SHAPE>& aItemShape, PCB_EXPR_CONTEXT* aCtx, |
|
|
|
FOOTPRINT* aFootprint, PCB_LAYER_ID aSide ) |
|
|
|
{ |
|
|
|
if( !aFootprint ) |
|
|
|
return false; |
|
|
|
|
|
|
|
BOARD* board = aItem->GetBoard(); |
|
|
|
std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex ); |
|
|
|
std::pair<BOARD_ITEM*, BOARD_ITEM*> key( aFootprint, aItem ); |
|
|
|
|
|
|
|
std::map< std::pair<BOARD_ITEM*, BOARD_ITEM*>, bool >* cache; |
|
|
|
|
|
|
|
switch( aSide ) |
|
|
|
{ |
|
|
|
case F_Cu: cache = &board->m_InsideFCourtyardCache; break; |
|
|
|
case B_Cu: cache = &board->m_InsideBCourtyardCache; break; |
|
|
|
default: cache = &board->m_InsideCourtyardCache; break; |
|
|
|
} |
|
|
|
|
|
|
|
auto i = cache->find( key ); |
|
|
|
|
|
|
|
if( i != cache->end() ) |
|
|
|
return i->second; |
|
|
|
|
|
|
|
bool res = calcIsInsideCourtyard( aItem, aItemBBox, aItemShape, aCtx, aFootprint, aSide ); |
|
|
|
|
|
|
|
(*cache)[ key ] = res; |
|
|
|
return res; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
static void insideCourtyard( LIBEVAL::CONTEXT* aCtx, void* self ) |
|
|
|
{ |
|
|
|
PCB_EXPR_CONTEXT* context = static_cast<PCB_EXPR_CONTEXT*>( aCtx ); |
|
|
@ -241,30 +273,8 @@ static void insideCourtyard( LIBEVAL::CONTEXT* aCtx, void* self ) |
|
|
|
if( !item ) |
|
|
|
return; |
|
|
|
|
|
|
|
auto insideFootprint = |
|
|
|
[context]( BOARD_ITEM* aItem, const EDA_RECT& aItemBBox, |
|
|
|
std::shared_ptr<SHAPE>& aItemShape, FOOTPRINT* aFootprint ) -> bool |
|
|
|
{ |
|
|
|
if( !aFootprint ) |
|
|
|
return false; |
|
|
|
|
|
|
|
BOARD* board = aItem->GetBoard(); |
|
|
|
std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex ); |
|
|
|
std::pair<BOARD_ITEM*, BOARD_ITEM*> key( aFootprint, aItem ); |
|
|
|
auto i = board->m_InsideCourtyardCache.find( key ); |
|
|
|
|
|
|
|
if( i != board->m_InsideCourtyardCache.end() ) |
|
|
|
return i->second; |
|
|
|
|
|
|
|
bool res = insideFootprintCourtyard( aItem, aItemBBox, aItemShape, context, |
|
|
|
aFootprint ); |
|
|
|
|
|
|
|
board->m_InsideCourtyardCache[ key ] = res; |
|
|
|
return res; |
|
|
|
}; |
|
|
|
|
|
|
|
result->SetDeferredEval( |
|
|
|
[item, arg, context, &insideFootprint]() -> double |
|
|
|
[item, arg, context]() -> double |
|
|
|
{ |
|
|
|
BOARD* board = item->GetBoard(); |
|
|
|
EDA_RECT itemBBox; |
|
|
@ -277,23 +287,24 @@ static void insideCourtyard( LIBEVAL::CONTEXT* aCtx, void* self ) |
|
|
|
|
|
|
|
if( arg->AsString() == "A" ) |
|
|
|
{ |
|
|
|
FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( context->GetItem( 0 ) ); |
|
|
|
return insideFootprint( item, itemBBox, itemShape, footprint ) ? 1.0 : 0.0; |
|
|
|
FOOTPRINT* fp = dynamic_cast<FOOTPRINT*>( context->GetItem( 0 ) ); |
|
|
|
|
|
|
|
if( isInsideCourtyard( item, itemBBox, itemShape, context, fp, In1_Cu ) ) |
|
|
|
return 1.0; |
|
|
|
} |
|
|
|
else if( arg->AsString() == "B" ) |
|
|
|
{ |
|
|
|
FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( context->GetItem( 1 ) ); |
|
|
|
return insideFootprint( item, itemBBox, itemShape, footprint ) ? 1.0 : 0.0; |
|
|
|
FOOTPRINT* fp = dynamic_cast<FOOTPRINT*>( context->GetItem( 1 ) ); |
|
|
|
|
|
|
|
if( isInsideCourtyard( item, itemBBox, itemShape, context, fp, In1_Cu ) ) |
|
|
|
return 1.0; |
|
|
|
} |
|
|
|
else |
|
|
|
else for( FOOTPRINT* fp : board->Footprints() ) |
|
|
|
{ |
|
|
|
for( FOOTPRINT* candidate : board->Footprints() ) |
|
|
|
if( fp->GetReference().Matches( arg->AsString() ) ) |
|
|
|
{ |
|
|
|
if( candidate->GetReference().Matches( arg->AsString() ) ) |
|
|
|
{ |
|
|
|
if( insideFootprint( item, itemBBox, itemShape, candidate ) ) |
|
|
|
return 1.0; |
|
|
|
} |
|
|
|
if( isInsideCourtyard( item, itemBBox, itemShape, context, fp, In1_Cu ) ) |
|
|
|
return 1.0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -328,30 +339,8 @@ static void insideFrontCourtyard( LIBEVAL::CONTEXT* aCtx, void* self ) |
|
|
|
if( !item ) |
|
|
|
return; |
|
|
|
|
|
|
|
auto insideFootprint = |
|
|
|
[context]( BOARD_ITEM* aItem, const EDA_RECT& aItemBBox, |
|
|
|
std::shared_ptr<SHAPE>& aItemShape, FOOTPRINT* aFootprint ) -> bool |
|
|
|
{ |
|
|
|
if( !aFootprint ) |
|
|
|
return false; |
|
|
|
|
|
|
|
BOARD* board = aItem->GetBoard(); |
|
|
|
std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex ); |
|
|
|
std::pair<BOARD_ITEM*, BOARD_ITEM*> key( aFootprint, aItem ); |
|
|
|
auto i = board->m_InsideFCourtyardCache.find( key ); |
|
|
|
|
|
|
|
if( i != board->m_InsideFCourtyardCache.end() ) |
|
|
|
return i->second; |
|
|
|
|
|
|
|
bool res = insideFootprintCourtyard( aItem, aItemBBox, aItemShape, context, |
|
|
|
aFootprint, F_Cu ); |
|
|
|
|
|
|
|
board->m_InsideFCourtyardCache[ key ] = res; |
|
|
|
return res; |
|
|
|
}; |
|
|
|
|
|
|
|
result->SetDeferredEval( |
|
|
|
[item, arg, context, &insideFootprint]() -> double |
|
|
|
[item, arg, context]() -> double |
|
|
|
{ |
|
|
|
BOARD* board = item->GetBoard(); |
|
|
|
EDA_RECT itemBBox; |
|
|
@ -364,23 +353,24 @@ static void insideFrontCourtyard( LIBEVAL::CONTEXT* aCtx, void* self ) |
|
|
|
|
|
|
|
if( arg->AsString() == "A" ) |
|
|
|
{ |
|
|
|
FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( context->GetItem( 0 ) ); |
|
|
|
return insideFootprint( item, itemBBox, itemShape, footprint ) ? 1.0 : 0.0; |
|
|
|
FOOTPRINT* fp = dynamic_cast<FOOTPRINT*>( context->GetItem( 0 ) ); |
|
|
|
|
|
|
|
if( isInsideCourtyard( item, itemBBox, itemShape, context, fp, F_Cu ) ) |
|
|
|
return 1.0; |
|
|
|
} |
|
|
|
else if( arg->AsString() == "B" ) |
|
|
|
{ |
|
|
|
FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( context->GetItem( 1 ) ); |
|
|
|
return insideFootprint( item, itemBBox, itemShape, footprint ) ? 1.0 : 0.0; |
|
|
|
FOOTPRINT* fp = dynamic_cast<FOOTPRINT*>( context->GetItem( 1 ) ); |
|
|
|
|
|
|
|
if( isInsideCourtyard( item, itemBBox, itemShape, context, fp, F_Cu ) ) |
|
|
|
return 1.0; |
|
|
|
} |
|
|
|
else |
|
|
|
else for( FOOTPRINT* fp : board->Footprints() ) |
|
|
|
{ |
|
|
|
for( FOOTPRINT* candidate : board->Footprints() ) |
|
|
|
if( fp->GetReference().Matches( arg->AsString() ) ) |
|
|
|
{ |
|
|
|
if( candidate->GetReference().Matches( arg->AsString() ) ) |
|
|
|
{ |
|
|
|
if( insideFootprint( item, itemBBox, itemShape, candidate ) ) |
|
|
|
return 1.0; |
|
|
|
} |
|
|
|
if( isInsideCourtyard( item, itemBBox, itemShape, context, fp, F_Cu ) ) |
|
|
|
return 1.0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -414,30 +404,8 @@ static void insideBackCourtyard( LIBEVAL::CONTEXT* aCtx, void* self ) |
|
|
|
if( !item ) |
|
|
|
return; |
|
|
|
|
|
|
|
auto insideFootprint = |
|
|
|
[context]( BOARD_ITEM* aItem, const EDA_RECT& aItemBBox, |
|
|
|
std::shared_ptr<SHAPE>& aItemShape, FOOTPRINT* aFootprint ) -> bool |
|
|
|
{ |
|
|
|
if( !aFootprint ) |
|
|
|
return false; |
|
|
|
|
|
|
|
BOARD* board = aItem->GetBoard(); |
|
|
|
std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex ); |
|
|
|
std::pair<BOARD_ITEM*, BOARD_ITEM*> key( aFootprint, aItem ); |
|
|
|
auto i = board->m_InsideBCourtyardCache.find( key ); |
|
|
|
|
|
|
|
if( i != board->m_InsideBCourtyardCache.end() ) |
|
|
|
return i->second; |
|
|
|
|
|
|
|
bool res = insideFootprintCourtyard( aItem, aItemBBox, aItemShape, context, |
|
|
|
aFootprint, B_Cu ); |
|
|
|
|
|
|
|
board->m_InsideBCourtyardCache[ key ] = res; |
|
|
|
return res; |
|
|
|
}; |
|
|
|
|
|
|
|
result->SetDeferredEval( |
|
|
|
[item, arg, context, &insideFootprint]() -> double |
|
|
|
[item, arg, context]() -> double |
|
|
|
{ |
|
|
|
BOARD* board = item->GetBoard(); |
|
|
|
EDA_RECT itemBBox; |
|
|
@ -450,23 +418,24 @@ static void insideBackCourtyard( LIBEVAL::CONTEXT* aCtx, void* self ) |
|
|
|
|
|
|
|
if( arg->AsString() == "A" ) |
|
|
|
{ |
|
|
|
FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( context->GetItem( 0 ) ); |
|
|
|
return insideFootprint( item, itemBBox, itemShape, footprint ) ? 1.0 : 0.0; |
|
|
|
FOOTPRINT* fp = dynamic_cast<FOOTPRINT*>( context->GetItem( 0 ) ); |
|
|
|
|
|
|
|
if( isInsideCourtyard( item, itemBBox, itemShape, context, fp, B_Cu ) ) |
|
|
|
return 1.0; |
|
|
|
} |
|
|
|
else if( arg->AsString() == "B" ) |
|
|
|
{ |
|
|
|
FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( context->GetItem( 1 ) ); |
|
|
|
return insideFootprint( item, itemBBox, itemShape, footprint ) ? 1.0 : 0.0; |
|
|
|
FOOTPRINT* fp = dynamic_cast<FOOTPRINT*>( context->GetItem( 1 ) ); |
|
|
|
|
|
|
|
if( isInsideCourtyard( item, itemBBox, itemShape, context, fp, B_Cu ) ) |
|
|
|
return 1.0; |
|
|
|
} |
|
|
|
else |
|
|
|
else for( FOOTPRINT* fp : board->Footprints() ) |
|
|
|
{ |
|
|
|
for( FOOTPRINT* candidate : board->Footprints() ) |
|
|
|
if( fp->GetReference().Matches( arg->AsString() ) ) |
|
|
|
{ |
|
|
|
if( candidate->GetReference().Matches( arg->AsString() ) ) |
|
|
|
{ |
|
|
|
if( insideFootprint( item, itemBBox, itemShape, candidate ) ) |
|
|
|
return 1.0; |
|
|
|
} |
|
|
|
if( isInsideCourtyard( item, itemBBox, itemShape, context, fp, B_Cu ) ) |
|
|
|
return 1.0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -475,173 +444,174 @@ static void insideBackCourtyard( LIBEVAL::CONTEXT* aCtx, void* self ) |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void insideArea( LIBEVAL::CONTEXT* aCtx, void* self ) |
|
|
|
bool calcIsInsideArea( BOARD_ITEM* aItem, const EDA_RECT& aItemBBox, PCB_EXPR_CONTEXT* aCtx, |
|
|
|
ZONE* aArea ) |
|
|
|
{ |
|
|
|
PCB_EXPR_CONTEXT* context = static_cast<PCB_EXPR_CONTEXT*>( aCtx ); |
|
|
|
LIBEVAL::VALUE* arg = aCtx->Pop(); |
|
|
|
LIBEVAL::VALUE* result = aCtx->AllocValue(); |
|
|
|
BOARD* board = aArea->GetBoard(); |
|
|
|
std::shared_ptr<SHAPE> shape; |
|
|
|
|
|
|
|
result->Set( 0.0 ); |
|
|
|
aCtx->Push( result ); |
|
|
|
if( !aArea->GetCachedBoundingBox().Intersects( aItemBBox ) ) |
|
|
|
return false; |
|
|
|
|
|
|
|
if( !arg ) |
|
|
|
// Collisions include touching, so we need to deflate outline by enough to
|
|
|
|
// exclude touching. This is particularly important for detecting copper fills
|
|
|
|
// as they will be exactly touching along the entire border.
|
|
|
|
SHAPE_POLY_SET areaOutline = *aArea->Outline(); |
|
|
|
areaOutline.Deflate( board->GetDesignSettings().GetDRCEpsilon(), 0, |
|
|
|
SHAPE_POLY_SET::ALLOW_ACUTE_CORNERS ); |
|
|
|
|
|
|
|
if( aItem->GetFlags() & HOLE_PROXY ) |
|
|
|
{ |
|
|
|
if( aCtx->HasErrorCallback() ) |
|
|
|
if( aItem->Type() == PCB_PAD_T ) |
|
|
|
{ |
|
|
|
aCtx->ReportError( wxString::Format( _( "Missing argument to '%s'" ), |
|
|
|
wxT( "insideArea()" ) ) ); |
|
|
|
PAD* pad = static_cast<PAD*>( aItem ); |
|
|
|
const SHAPE_SEGMENT* holeShape = pad->GetEffectiveHoleShape(); |
|
|
|
|
|
|
|
return areaOutline.Collide( holeShape ); |
|
|
|
} |
|
|
|
else if( aItem->Type() == PCB_VIA_T ) |
|
|
|
{ |
|
|
|
PCB_VIA* via = static_cast<PCB_VIA*>( aItem ); |
|
|
|
const SHAPE_CIRCLE holeShape( via->GetPosition(), via->GetDrillValue() ); |
|
|
|
|
|
|
|
return; |
|
|
|
} |
|
|
|
return areaOutline.Collide( &holeShape ); |
|
|
|
} |
|
|
|
|
|
|
|
PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self ); |
|
|
|
BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr; |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
if( !item ) |
|
|
|
return; |
|
|
|
if( aItem->Type() == PCB_FOOTPRINT_T ) |
|
|
|
{ |
|
|
|
FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem ); |
|
|
|
|
|
|
|
auto itemIsInsideArea = |
|
|
|
[context]( BOARD_ITEM* aItem, ZONE* aArea, const EDA_RECT& aItemBBox ) -> bool |
|
|
|
if( ( footprint->GetFlags() & MALFORMED_COURTYARDS ) != 0 ) |
|
|
|
{ |
|
|
|
if( aCtx->HasErrorCallback() ) |
|
|
|
{ |
|
|
|
BOARD* board = aArea->GetBoard(); |
|
|
|
std::shared_ptr<SHAPE> shape; |
|
|
|
aCtx->ReportError( _( "Footprint's courtyard is not a single, closed shape." ) ); |
|
|
|
} |
|
|
|
|
|
|
|
if( !aArea->GetCachedBoundingBox().Intersects( aItemBBox ) ) |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
// Collisions include touching, so we need to deflate outline by enough to
|
|
|
|
// exclude touching. This is particularly important for detecting copper fills
|
|
|
|
// as they will be exactly touching along the entire border.
|
|
|
|
SHAPE_POLY_SET areaOutline = *aArea->Outline(); |
|
|
|
areaOutline.Deflate( board->GetDesignSettings().GetDRCEpsilon(), 0, |
|
|
|
SHAPE_POLY_SET::ALLOW_ACUTE_CORNERS ); |
|
|
|
if( ( aArea->GetLayerSet() & LSET::FrontMask() ).any() ) |
|
|
|
{ |
|
|
|
SHAPE_POLY_SET courtyard = footprint->GetPolyCourtyard( F_CrtYd ); |
|
|
|
|
|
|
|
if( aItem->GetFlags() & HOLE_PROXY ) |
|
|
|
{ |
|
|
|
if( aItem->Type() == PCB_PAD_T ) |
|
|
|
{ |
|
|
|
PAD* pad = static_cast<PAD*>( aItem ); |
|
|
|
const SHAPE_SEGMENT* holeShape = pad->GetEffectiveHoleShape(); |
|
|
|
if( courtyard.OutlineCount() == 0 ) |
|
|
|
{ |
|
|
|
if( aCtx->HasErrorCallback() ) |
|
|
|
aCtx->ReportError( _( "Footprint has no front courtyard." ) ); |
|
|
|
|
|
|
|
return areaOutline.Collide( holeShape ); |
|
|
|
} |
|
|
|
else if( aItem->Type() == PCB_VIA_T ) |
|
|
|
{ |
|
|
|
PCB_VIA* via = static_cast<PCB_VIA*>( aItem ); |
|
|
|
const SHAPE_CIRCLE holeShape( via->GetPosition(), via->GetDrillValue() ); |
|
|
|
return false; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
return areaOutline.Collide( &courtyard.Outline( 0 ) ); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return areaOutline.Collide( &holeShape ); |
|
|
|
} |
|
|
|
if( ( aArea->GetLayerSet() & LSET::BackMask() ).any() ) |
|
|
|
{ |
|
|
|
SHAPE_POLY_SET courtyard = footprint->GetPolyCourtyard( B_CrtYd ); |
|
|
|
|
|
|
|
return false; |
|
|
|
} |
|
|
|
if( courtyard.OutlineCount() == 0 ) |
|
|
|
{ |
|
|
|
if( aCtx->HasErrorCallback() ) |
|
|
|
aCtx->ReportError( _( "Footprint has no back courtyard." ) ); |
|
|
|
|
|
|
|
if( aItem->Type() == PCB_FOOTPRINT_T ) |
|
|
|
{ |
|
|
|
FOOTPRINT* footprint = static_cast<FOOTPRINT*>( aItem ); |
|
|
|
return false; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
return areaOutline.Collide( &courtyard.Outline( 0 ) ); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if( ( footprint->GetFlags() & MALFORMED_COURTYARDS ) != 0 ) |
|
|
|
{ |
|
|
|
if( context->HasErrorCallback() ) |
|
|
|
{ |
|
|
|
context->ReportError( _( "Footprint's courtyard is not a single, " |
|
|
|
"closed shape." ) ); |
|
|
|
} |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
return false; |
|
|
|
} |
|
|
|
if( aItem->Type() == PCB_ZONE_T || aItem->Type() == PCB_FP_ZONE_T ) |
|
|
|
{ |
|
|
|
ZONE* zone = static_cast<ZONE*>( aItem ); |
|
|
|
|
|
|
|
if( ( aArea->GetLayerSet() & LSET::FrontMask() ).any() ) |
|
|
|
{ |
|
|
|
SHAPE_POLY_SET courtyard = footprint->GetPolyCourtyard( F_CrtYd ); |
|
|
|
if( !zone->IsFilled() ) |
|
|
|
return false; |
|
|
|
|
|
|
|
if( courtyard.OutlineCount() == 0 ) |
|
|
|
{ |
|
|
|
if( context->HasErrorCallback() ) |
|
|
|
context->ReportError( _( "Footprint has no front courtyard." ) ); |
|
|
|
DRC_RTREE* zoneRTree = board->m_CopperZoneRTrees[ zone ].get(); |
|
|
|
|
|
|
|
return false; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
return areaOutline.Collide( &courtyard.Outline( 0 ) ); |
|
|
|
} |
|
|
|
} |
|
|
|
std::vector<SHAPE*> shapes; |
|
|
|
|
|
|
|
if( ( aArea->GetLayerSet() & LSET::BackMask() ).any() ) |
|
|
|
{ |
|
|
|
SHAPE_POLY_SET courtyard = footprint->GetPolyCourtyard( B_CrtYd ); |
|
|
|
if( zoneRTree ) |
|
|
|
{ |
|
|
|
for( PCB_LAYER_ID layer : aArea->GetLayerSet().Seq() ) |
|
|
|
{ |
|
|
|
if( zoneRTree->QueryColliding( aItemBBox, &areaOutline, layer ) ) |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if( courtyard.OutlineCount() == 0 ) |
|
|
|
{ |
|
|
|
if( context->HasErrorCallback() ) |
|
|
|
context->ReportError( _( "Footprint has no back courtyard." ) ); |
|
|
|
return false; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
if( !shape ) |
|
|
|
shape = aItem->GetEffectiveShape( aCtx->GetLayer() ); |
|
|
|
|
|
|
|
return false; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
return areaOutline.Collide( &courtyard.Outline( 0 ) ); |
|
|
|
} |
|
|
|
} |
|
|
|
return areaOutline.Collide( shape.get() ); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
if( aItem->Type() == PCB_ZONE_T || aItem->Type() == PCB_FP_ZONE_T ) |
|
|
|
{ |
|
|
|
ZONE* zone = static_cast<ZONE*>( aItem ); |
|
|
|
bool isInsideArea( BOARD_ITEM* aItem, const EDA_RECT& aItemBBox, PCB_EXPR_CONTEXT* aCtx, |
|
|
|
ZONE* aArea ) |
|
|
|
{ |
|
|
|
if( !aArea || aArea == aItem || aArea->GetParent() == aItem ) |
|
|
|
return false; |
|
|
|
|
|
|
|
if( !zone->IsFilled() ) |
|
|
|
return false; |
|
|
|
BOARD* board = aArea->GetBoard(); |
|
|
|
std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex ); |
|
|
|
std::pair<BOARD_ITEM*, BOARD_ITEM*> key( aArea, aItem ); |
|
|
|
auto i = board->m_InsideAreaCache.find( key ); |
|
|
|
|
|
|
|
DRC_RTREE* zoneRTree = board->m_CopperZoneRTrees[ zone ].get(); |
|
|
|
if( i != board->m_InsideAreaCache.end() ) |
|
|
|
return i->second; |
|
|
|
|
|
|
|
std::vector<SHAPE*> shapes; |
|
|
|
bool isInside = calcIsInsideArea( aItem, aItemBBox, aCtx, aArea ); |
|
|
|
|
|
|
|
if( zoneRTree ) |
|
|
|
{ |
|
|
|
for( PCB_LAYER_ID layer : aArea->GetLayerSet().Seq() ) |
|
|
|
{ |
|
|
|
if( zoneRTree->QueryColliding( aItemBBox, &areaOutline, layer ) ) |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
board->m_InsideAreaCache[ key ] = isInside; |
|
|
|
return isInside; |
|
|
|
} |
|
|
|
|
|
|
|
return false; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
if( !shape ) |
|
|
|
shape = aItem->GetEffectiveShape( context->GetLayer() ); |
|
|
|
|
|
|
|
return areaOutline.Collide( shape.get() ); |
|
|
|
} |
|
|
|
}; |
|
|
|
static void insideArea( 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(); |
|
|
|
|
|
|
|
auto checkArea = |
|
|
|
[&itemIsInsideArea]( BOARD_ITEM* aItem, const EDA_RECT& aItemBBox, ZONE* aArea ) -> bool |
|
|
|
{ |
|
|
|
if( !aArea || aArea == aItem || aArea->GetParent() == aItem ) |
|
|
|
return false; |
|
|
|
result->Set( 0.0 ); |
|
|
|
aCtx->Push( result ); |
|
|
|
|
|
|
|
BOARD* board = aArea->GetBoard(); |
|
|
|
std::unique_lock<std::mutex> cacheLock( board->m_CachesMutex ); |
|
|
|
std::pair<BOARD_ITEM*, BOARD_ITEM*> key( aArea, aItem ); |
|
|
|
auto i = board->m_InsideAreaCache.find( key ); |
|
|
|
if( !arg ) |
|
|
|
{ |
|
|
|
if( aCtx->HasErrorCallback() ) |
|
|
|
{ |
|
|
|
aCtx->ReportError( wxString::Format( _( "Missing argument to '%s'" ), |
|
|
|
wxT( "insideArea()" ) ) ); |
|
|
|
} |
|
|
|
|
|
|
|
if( i != board->m_InsideAreaCache.end() ) |
|
|
|
return i->second; |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
bool isInside = itemIsInsideArea( aItem, aArea, aItemBBox ); |
|
|
|
PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self ); |
|
|
|
BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr; |
|
|
|
|
|
|
|
board->m_InsideAreaCache[ key ] = isInside; |
|
|
|
return isInside; |
|
|
|
}; |
|
|
|
if( !item ) |
|
|
|
return; |
|
|
|
|
|
|
|
result->SetDeferredEval( |
|
|
|
[item, arg, context, &checkArea]() -> double |
|
|
|
[item, arg, context]() -> double |
|
|
|
{ |
|
|
|
BOARD* board = item->GetBoard(); |
|
|
|
EDA_RECT itemBBox; |
|
|
@ -654,12 +624,12 @@ static void insideArea( LIBEVAL::CONTEXT* aCtx, void* self ) |
|
|
|
if( arg->AsString() == "A" ) |
|
|
|
{ |
|
|
|
ZONE* zone = dynamic_cast<ZONE*>( context->GetItem( 0 ) ); |
|
|
|
return checkArea( item, itemBBox, zone ) ? 1.0 : 0.0; |
|
|
|
return isInsideArea( item, itemBBox, context, zone ) ? 1.0 : 0.0; |
|
|
|
} |
|
|
|
else if( arg->AsString() == "B" ) |
|
|
|
{ |
|
|
|
ZONE* zone = dynamic_cast<ZONE*>( context->GetItem( 1 ) ); |
|
|
|
return checkArea( item, itemBBox, zone ) ? 1.0 : 0.0; |
|
|
|
return isInsideArea( item, itemBBox, context, zone ) ? 1.0 : 0.0; |
|
|
|
} |
|
|
|
else if( KIID::SniffTest( arg->AsString() ) ) |
|
|
|
{ |
|
|
@ -670,7 +640,7 @@ static void insideArea( LIBEVAL::CONTEXT* aCtx, void* self ) |
|
|
|
// Only a single zone can match the UUID; exit once we find a match whether
|
|
|
|
// "inside" or not
|
|
|
|
if( area->m_Uuid == target ) |
|
|
|
return checkArea( item, itemBBox, area ) ? 1.0 : 0.0; |
|
|
|
return isInsideArea( item, itemBBox, context, area ) ? 1.0 : 0.0; |
|
|
|
} |
|
|
|
|
|
|
|
for( FOOTPRINT* footprint : board->Footprints() ) |
|
|
@ -680,7 +650,7 @@ static void insideArea( LIBEVAL::CONTEXT* aCtx, void* self ) |
|
|
|
// Only a single zone can match the UUID; exit once we find a match
|
|
|
|
// whether "inside" or not
|
|
|
|
if( area->m_Uuid == target ) |
|
|
|
return checkArea( item, itemBBox, area ) ? 1.0 : 0.0; |
|
|
|
return isInsideArea( item, itemBBox, context, area ) ? 1.0 : 0.0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -693,7 +663,7 @@ static void insideArea( LIBEVAL::CONTEXT* aCtx, void* self ) |
|
|
|
if( area->GetZoneName().Matches( arg->AsString() ) ) |
|
|
|
{ |
|
|
|
// Many zones can match the name; exit only when we find an "inside"
|
|
|
|
if( checkArea( item, itemBBox, area ) ) |
|
|
|
if( isInsideArea( item, itemBBox, context, area ) ) |
|
|
|
return 1.0; |
|
|
|
} |
|
|
|
} |
|
|
@ -705,7 +675,7 @@ static void insideArea( LIBEVAL::CONTEXT* aCtx, void* self ) |
|
|
|
// Many zones can match the name; exit only when we find an "inside"
|
|
|
|
if( area->GetZoneName().Matches( arg->AsString() ) ) |
|
|
|
{ |
|
|
|
if( checkArea( item, itemBBox, area ) ) |
|
|
|
if( isInsideArea( item, itemBBox, context, area ) ) |
|
|
|
return 1.0; |
|
|
|
} |
|
|
|
} |
|
|
|