Implement progress reporting for DRC RTree.

Also fixes a bug in silk-to-mask checking which wasn't checking
zones on the mask layer.

Also a perfomance fix for the DRC RTree to use a hash table (std::map)
instead of a std::set for keeping track of known collisions.

Fixes https://gitlab.com/kicad/code/kicad/issues/5851
This commit is contained in:
Jeff Young 2020-10-12 23:59:01 +01:00
parent 5f6d309d36
commit cd1a5ed6fb
4 changed files with 69 additions and 41 deletions

View File

@ -322,24 +322,35 @@ public:
typedef std::pair<PCB_LAYER_ID, PCB_LAYER_ID> LAYER_PAIR; typedef std::pair<PCB_LAYER_ID, PCB_LAYER_ID> LAYER_PAIR;
struct PAIR_INFO
{
PAIR_INFO( LAYER_PAIR aPair, ITEM_WITH_SHAPE* aRef, ITEM_WITH_SHAPE* aTest ) :
layerPair( aPair ),
refItem( aRef ),
testItem( aTest )
{ };
LAYER_PAIR layerPair;
ITEM_WITH_SHAPE* refItem;
ITEM_WITH_SHAPE* testItem;
};
int QueryCollidingPairs( DRC_RTREE* aRefTree, int QueryCollidingPairs( DRC_RTREE* aRefTree,
std::vector<LAYER_PAIR> aLayers, std::vector<LAYER_PAIR> aLayers,
std::function<bool( const LAYER_PAIR&, std::function<bool( const LAYER_PAIR&,
ITEM_WITH_SHAPE*, ITEM_WITH_SHAPE*, ITEM_WITH_SHAPE*, ITEM_WITH_SHAPE*,
bool* aCollision )> aVisitor, bool* aCollision )> aVisitor,
int aMaxClearance ) int aMaxClearance,
std::function<bool(int, int )> aProgressReporter )
{ {
// keep track of BOARD_ITEMs pairs that have been already found to collide (some items std::vector< PAIR_INFO > pairsToVisit;
// might be build of COMPOUND/triangulated shapes and a single subshape collision
// means we have a hit)
std::set< std::pair<BOARD_ITEM*, BOARD_ITEM*>> collidingCompounds;
for( auto refLayerIter : aLayers ) for( LAYER_PAIR& refLayerIter : aLayers )
{ {
const PCB_LAYER_ID refLayer = refLayerIter.first; const PCB_LAYER_ID refLayer = refLayerIter.first;
const PCB_LAYER_ID targetLayer = refLayerIter.second; const PCB_LAYER_ID targetLayer = refLayerIter.second;
for( auto refItem : aRefTree->OnLayer( refLayer ) ) for( ITEM_WITH_SHAPE* refItem : aRefTree->OnLayer( refLayer ) )
{ {
BOX2I box = refItem->shape->BBox(); BOX2I box = refItem->shape->BBox();
box.Inflate( aMaxClearance ); box.Inflate( aMaxClearance );
@ -350,30 +361,44 @@ public:
auto visit = auto visit =
[&]( ITEM_WITH_SHAPE* aItemToTest ) -> bool [&]( ITEM_WITH_SHAPE* aItemToTest ) -> bool
{ {
const std::pair<BOARD_ITEM*, BOARD_ITEM*>
chkCompoundPair( refItem->parent, aItemToTest->parent );
// don't report multiple collisions for compound or triangulated shapes
if( alg::contains( collidingCompounds, chkCompoundPair ) )
return true;
// don't collide items against themselves // don't collide items against themselves
if( refLayer == targetLayer && aItemToTest->parent == refItem->parent ) if( refLayer == targetLayer && aItemToTest->parent == refItem->parent )
return true; return true;
bool collisionDetected = false; pairsToVisit.emplace_back( refLayerIter, refItem, aItemToTest );
bool continueSearch = aVisitor( refLayerIter, refItem, aItemToTest, return true;
&collisionDetected );
if( collisionDetected )
collidingCompounds.insert( chkCompoundPair );
return continueSearch;
}; };
this->m_tree[targetLayer]->Search( min, max, visit ); this->m_tree[targetLayer]->Search( min, max, visit );
}; };
} }
// keep track of BOARD_ITEMs pairs that have been already found to collide (some items
// might be build of COMPOUND/triangulated shapes and a single subshape collision
// means we have a hit)
std::map< std::pair<BOARD_ITEM*, BOARD_ITEM*>, int> collidingCompounds;
int progress = 0;
int count = pairsToVisit.size();
for( PAIR_INFO& pair : pairsToVisit )
{
if( !aProgressReporter( progress++, count ) )
break;
// don't report multiple collisions for compound or triangulated shapes
if( collidingCompounds.count( { pair.refItem->parent, pair.testItem->parent } ) )
continue;
bool collisionDetected = false;
if( !aVisitor( pair.layerPair, pair.refItem, pair.testItem, &collisionDetected ) )
break;
if( collisionDetected )
collidingCompounds[ { pair.refItem->parent, pair.testItem->parent } ] = 1;
}
return 0; return 0;
} }

View File

@ -314,7 +314,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testCopperDrawItem( BOARD_ITEM* aItem )
void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testTrackClearances() void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testTrackClearances()
{ {
// This is the number of tests between 2 calls to the progress bar // This is the number of tests between 2 calls to the progress bar
const int delta = m_drcEngine->GetTestTracksAgainstZones() ? 25 : 100; const int delta = 25;
int count = m_board->Tracks().size(); int count = m_board->Tracks().size();
reportAux( "Testing %d tracks...", count ); reportAux( "Testing %d tracks...", count );

View File

@ -179,8 +179,7 @@ bool DRC_TEST_PROVIDER_SILK_CLEARANCE::Run()
return true; return true;
}; };
forEachGeometryItem( { PCB_SHAPE_T, PCB_FP_SHAPE_T, PCB_TEXT_T, PCB_FP_TEXT_T }, forEachGeometryItem( {}, LSET( 2, F_SilkS, B_SilkS ), addToSilkTree );
LSET( 2, F_SilkS, B_SilkS ), addToSilkTree );
forEachGeometryItem( {}, LSET::FrontMask() | LSET::BackMask(), addToTargetTree ); forEachGeometryItem( {}, LSET::FrontMask() | LSET::BackMask(), addToTargetTree );
reportAux( _("Testing %d silkscreen features against %d board items."), reportAux( _("Testing %d silkscreen features against %d board items."),
@ -204,10 +203,17 @@ bool DRC_TEST_PROVIDER_SILK_CLEARANCE::Run()
DRC_RTREE::LAYER_PAIR( B_SilkS, B_CrtYd ), DRC_RTREE::LAYER_PAIR( B_SilkS, B_CrtYd ),
DRC_RTREE::LAYER_PAIR( B_SilkS, B_Fab ), DRC_RTREE::LAYER_PAIR( B_SilkS, B_Fab ),
DRC_RTREE::LAYER_PAIR( B_SilkS, B_Cu ), DRC_RTREE::LAYER_PAIR( B_SilkS, B_Cu ),
DRC_RTREE::LAYER_PAIR( B_SilkS, Edge_Cuts ), DRC_RTREE::LAYER_PAIR( B_SilkS, Edge_Cuts )
}; };
targetTree.QueryCollidingPairs( &silkTree, layerPairs, checkClearance, m_largestClearance ); // This is the number of tests between 2 calls to the progress bar
const int delta = 250;
targetTree.QueryCollidingPairs( &silkTree, layerPairs, checkClearance, m_largestClearance,
[this]( int aCount, int aSize ) -> bool
{
return reportProgress( aCount, aSize, delta );
} );
reportRuleStatistics(); reportRuleStatistics();

View File

@ -161,20 +161,10 @@ bool DRC_TEST_PROVIDER_SILK_TO_MASK::Run()
return true; return true;
}; };
int numPads = forEachGeometryItem( { PCB_PAD_T, int numMask = forEachGeometryItem( {}, LSET( 2, F_Mask, B_Mask ), addMaskToTree );
PCB_SHAPE_T, int numSilk = forEachGeometryItem( {}, LSET( 2, F_SilkS, B_SilkS ), addSilkToTree );
PCB_FP_SHAPE_T,
PCB_TEXT_T,
PCB_FP_TEXT_T },
LSET( 2, F_Mask, B_Mask ), addMaskToTree );
int numSilk = forEachGeometryItem( { PCB_SHAPE_T, reportAux( _("Testing %d mask apertures against %d silkscreen features."), numMask, numSilk );
PCB_FP_SHAPE_T,
PCB_TEXT_T,
PCB_FP_TEXT_T },
LSET( 2, F_SilkS, B_SilkS ), addSilkToTree );
reportAux( _("Testing %d exposed copper against %d silkscreen features."), numPads, numSilk );
const std::vector<DRC_RTREE::LAYER_PAIR> layerPairs = const std::vector<DRC_RTREE::LAYER_PAIR> layerPairs =
{ {
@ -182,7 +172,14 @@ bool DRC_TEST_PROVIDER_SILK_TO_MASK::Run()
DRC_RTREE::LAYER_PAIR( B_SilkS, B_Mask ) DRC_RTREE::LAYER_PAIR( B_SilkS, B_Mask )
}; };
maskTree.QueryCollidingPairs( &silkTree, layerPairs, checkClearance, m_largestClearance ); // This is the number of tests between 2 calls to the progress bar
const int delta = 250;
maskTree.QueryCollidingPairs( &silkTree, layerPairs, checkClearance, m_largestClearance,
[this]( int aCount, int aSize ) -> bool
{
return reportProgress( aCount, aSize, delta );
} );
reportRuleStatistics(); reportRuleStatistics();