Add cache for insideArea() and insideCourtyard().

Fixes https://gitlab.com/kicad/code/kicad/issues/7720
This commit is contained in:
Jeff Young 2021-02-26 19:19:56 +00:00
parent 451d7c117a
commit f226373324
3 changed files with 69 additions and 22 deletions

View File

@ -267,7 +267,13 @@ public:
*/
BOARD_USE GetBoardUse() const { return m_boardUse; }
void IncrementTimeStamp() { m_timeStamp++; }
void IncrementTimeStamp()
{
m_timeStamp++;
m_InsideAreaCache.clear();
m_InsideCourtyardCache.clear();
}
int GetTimeStamp() { return m_timeStamp; }
/**
@ -1130,5 +1136,10 @@ public:
* @return bit field of legal ops.
*/
GroupLegalOpsField GroupLegalOps( const PCB_SELECTION& selection ) const;
public:
// While this is a significant encapsulation leak, it's also a significant performance win.
std::map< std::pair<BOARD_ITEM*, BOARD_ITEM*>, bool> m_InsideCourtyardCache;
std::map< std::pair<BOARD_ITEM*, BOARD_ITEM*>, bool> m_InsideAreaCache;
};
#endif // CLASS_BOARD_H_

View File

@ -678,6 +678,8 @@ void DRC_ENGINE::RunTests( EDA_UNITS aUnits, bool aReportAllTrackErrors, bool aT
m_errorLimits[ ii ] = INT_MAX;
}
m_board->IncrementTimeStamp(); // Invalidate all caches
for( ZONE* zone : m_board->Zones() )
{
zone->CacheBoundingBox();

View File

@ -151,27 +151,26 @@ static void insideCourtyard( LIBEVAL::CONTEXT* aCtx, void* self )
return;
}
PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
EDA_RECT itemBBox;
std::shared_ptr<SHAPE> shape;
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 =
auto realInsideFootprint =
[&]( FOOTPRINT* footprint ) -> bool
{
SHAPE_POLY_SET footprintCourtyard;
if( !footprint )
return false;
if( footprint->IsFlipped() )
footprintCourtyard = footprint->GetPolyCourtyardBack();
else
@ -195,6 +194,24 @@ static void insideCourtyard( LIBEVAL::CONTEXT* aCtx, void* self )
return footprintCourtyard.Collide( shape.get() );
};
auto insideFootprint =
[&]( FOOTPRINT* footprint ) -> bool
{
if( !footprint )
return false;
std::pair<BOARD_ITEM*, BOARD_ITEM*> key( footprint, item );
auto i = board->m_InsideCourtyardCache.find( key );
if( i != board->m_InsideCourtyardCache.end() )
return i->second;
bool result = realInsideFootprint( footprint );
board->m_InsideCourtyardCache[ key ] = result;
return result;
};
if( arg->AsString() == "A" )
{
if( insideFootprint( dynamic_cast<FOOTPRINT*>( context->GetItem( 0 ) ) ) )
@ -207,7 +224,7 @@ static void insideCourtyard( LIBEVAL::CONTEXT* aCtx, void* self )
}
else
{
for( FOOTPRINT* candidate : item->GetBoard()->Footprints() )
for( FOOTPRINT* candidate : board->Footprints() )
{
if( candidate->GetReference().Matches( arg->AsString() ) )
{
@ -238,25 +255,24 @@ static void insideArea( LIBEVAL::CONTEXT* aCtx, void* self )
return;
}
PCB_EXPR_VAR_REF* vref = static_cast<PCB_EXPR_VAR_REF*>( self );
BOARD_ITEM* item = vref ? vref->GetObject( aCtx ) : nullptr;
EDA_RECT itemBBox;
std::shared_ptr<SHAPE> shape;
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 insideZone =
auto realInsideZone =
[&]( ZONE* zone ) -> bool
{
if( !zone || zone == item || zone->GetParent() == item )
return false;
if( !zone->GetCachedBoundingBox().Intersects( itemBBox ) )
return false;
@ -355,6 +371,24 @@ static void insideArea( LIBEVAL::CONTEXT* aCtx, void* self )
}
};
auto insideZone =
[&]( ZONE* zone ) -> bool
{
if( !zone || zone == item || zone->GetParent() == item )
return false;
std::pair<BOARD_ITEM*, BOARD_ITEM*> key( zone, item );
auto i = board->m_InsideAreaCache.find( key );
if( i != board->m_InsideAreaCache.end() )
return i->second;
bool result = realInsideZone( zone );
board->m_InsideCourtyardCache[ key ] = result;
return result;
};
if( arg->AsString() == "A" )
{
if( insideZone( dynamic_cast<ZONE*>( context->GetItem( 0 ) ) ) )
@ -369,7 +403,7 @@ static void insideArea( LIBEVAL::CONTEXT* aCtx, void* self )
{
KIID target( arg->AsString() );
for( ZONE* candidate : item->GetBoard()->Zones() )
for( ZONE* candidate : board->Zones() )
{
// Only a single zone can match the UUID; exit once we find a match whether
// "inside" or not
@ -382,7 +416,7 @@ static void insideArea( LIBEVAL::CONTEXT* aCtx, void* self )
}
}
for( FOOTPRINT* footprint : item->GetBoard()->Footprints() )
for( FOOTPRINT* footprint : board->Footprints() )
{
for( ZONE* candidate : footprint->Zones() )
{
@ -400,7 +434,7 @@ static void insideArea( LIBEVAL::CONTEXT* aCtx, void* self )
}
else // Match on zone name
{
for( ZONE* candidate : item->GetBoard()->Zones() )
for( ZONE* candidate : board->Zones() )
{
if( candidate->GetZoneName().Matches( arg->AsString() ) )
{
@ -413,7 +447,7 @@ static void insideArea( LIBEVAL::CONTEXT* aCtx, void* self )
}
}
for( FOOTPRINT* footprint : item->GetBoard()->Footprints() )
for( FOOTPRINT* footprint : board->Footprints() )
{
for( ZONE* candidate : footprint->Zones() )
{