More caching for DRC.

Also fixes a bug where some physical clearance tests would be run even
if the clearance was 0 (or if the rule was set to IGNORE).
This commit is contained in:
Jeff Young 2022-06-17 22:50:59 +01:00
parent f20cb0fda8
commit 97b0005780
18 changed files with 391 additions and 293 deletions

View File

@ -511,6 +511,7 @@ set( PCB_COMMON_SRCS
${CMAKE_SOURCE_DIR}/pcbnew/connectivity/from_to_cache.cpp
${CMAKE_SOURCE_DIR}/pcbnew/convert_shape_list_to_polygon.cpp
${CMAKE_SOURCE_DIR}/pcbnew/drc/drc_engine.cpp
${CMAKE_SOURCE_DIR}/pcbnew/drc/drc_cache_generator.cpp
${CMAKE_SOURCE_DIR}/pcbnew/drc/drc_item.cpp
${CMAKE_SOURCE_DIR}/pcbnew/drc/drc_rule.cpp
${CMAKE_SOURCE_DIR}/pcbnew/drc/drc_rule_condition.cpp

View File

@ -216,9 +216,14 @@ void BOARD::IncrementTimeStamp()
m_InsideFCourtyardCache.clear();
m_InsideBCourtyardCache.clear();
m_LayerExpressionCache.clear();
}
m_CopperZoneRTrees.clear();
m_DRCMaxClearance = 0;
m_DRCMaxPhysicalClearance = 0;
m_DRCZones.clear();
m_DRCCopperZones.clear();
m_CopperZoneRTreeCache.clear();
m_CopperItemRTreeCache = std::make_unique<DRC_RTREE>();
}
}
std::vector<PCB_MARKER*> BOARD::ResolveDRCExclusions()

View File

@ -1077,10 +1077,15 @@ public:
std::map< std::pair<BOARD_ITEM*, BOARD_ITEM*>, bool > m_InsideBCourtyardCache;
std::map< std::tuple<BOARD_ITEM*, BOARD_ITEM*, PCB_LAYER_ID>, bool > m_InsideAreaCache;
std::map< wxString, LSET > m_LayerExpressionCache;
std::map< ZONE*, std::unique_ptr<DRC_RTREE> > m_CopperZoneRTreeCache;
std::unique_ptr<DRC_RTREE> m_CopperItemRTreeCache;
std::map< ZONE*, std::unique_ptr<DRC_RTREE> > m_CopperZoneRTrees;
ZONE* m_SolderMask;
// ------------ DRC caches -------------
std::vector<ZONE*> m_DRCZones;
std::vector<ZONE*> m_DRCCopperZones;
int m_DRCMaxClearance;
int m_DRCMaxPhysicalClearance;
ZONE* m_SolderMask;
private:
// The default copy constructor & operator= are inadequate,

View File

@ -0,0 +1,208 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2022 KiCad Developers.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <common.h>
#include <atomic>
#include <thread>
#include <board_design_settings.h>
#include <footprint.h>
#include <zone.h>
#include <drc/drc_engine.h>
#include <drc/drc_rtree.h>
#include <drc/drc_cache_generator.h>
bool DRC_CACHE_GENERATOR::Run()
{
m_board = m_drcEngine->GetBoard();
int& m_largestClearance = m_board->m_DRCMaxClearance;
int& m_largestPhysicalClearance = m_board->m_DRCMaxPhysicalClearance;
DRC_CONSTRAINT worstConstraint;
if( m_drcEngine->QueryWorstConstraint( CLEARANCE_CONSTRAINT, worstConstraint ) )
m_largestClearance = worstConstraint.GetValue().Min();
if( m_drcEngine->QueryWorstConstraint( HOLE_CLEARANCE_CONSTRAINT, worstConstraint ) )
m_largestClearance = std::max( m_largestClearance, worstConstraint.GetValue().Min() );
if( m_drcEngine->QueryWorstConstraint( PHYSICAL_CLEARANCE_CONSTRAINT, worstConstraint ) )
m_largestPhysicalClearance = worstConstraint.GetValue().Min();
if( m_drcEngine->QueryWorstConstraint( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, worstConstraint ) )
m_largestPhysicalClearance = std::max( m_largestPhysicalClearance, worstConstraint.GetValue().Min() );
std::vector<ZONE*> allZones;
for( ZONE* zone : m_board->Zones() )
{
allZones.push_back( zone );
if( !zone->GetIsRuleArea() )
{
m_board->m_DRCZones.push_back( zone );
if( ( zone->GetLayerSet() & LSET::AllCuMask() ).any() )
{
m_board->m_DRCCopperZones.push_back( zone );
m_largestClearance = std::max( m_largestClearance, zone->GetLocalClearance() );
}
}
}
for( FOOTPRINT* footprint : m_board->Footprints() )
{
for( PAD* pad : footprint->Pads() )
m_largestClearance = std::max( m_largestClearance, pad->GetLocalClearance() );
for( ZONE* zone : footprint->Zones() )
{
allZones.push_back( zone );
if( !zone->GetIsRuleArea() )
{
m_board->m_DRCZones.push_back( zone );
if( ( zone->GetLayerSet() & LSET::AllCuMask() ).any() )
{
m_board->m_DRCCopperZones.push_back( zone );
m_largestClearance = std::max( m_largestClearance, zone->GetLocalClearance() );
}
}
}
}
// This is the number of tests between 2 calls to the progress bar
size_t delta = 50;
size_t count = 0;
size_t ii = 0;
auto countItems =
[&]( BOARD_ITEM* item ) -> bool
{
++count;
return true;
};
auto addToCopperTree =
[&]( BOARD_ITEM* item ) -> bool
{
if( !reportProgress( ii++, count, delta ) )
return false;
LSET layers = item->GetLayerSet();
// Special-case pad holes which pierce all the copper layers
if( item->Type() == PCB_PAD_T )
{
PAD* pad = static_cast<PAD*>( item );
if( pad->GetDrillSizeX() > 0 && pad->GetDrillSizeY() > 0 )
layers |= LSET::AllCuMask();
}
for( PCB_LAYER_ID layer : layers.Seq() )
{
if( IsCopperLayer( layer ) )
m_board->m_CopperItemRTreeCache->Insert( item, layer, m_largestClearance );
}
return true;
};
if( !reportPhase( _( "Gathering copper items..." ) ) )
return false; // DRC cancelled
static const std::vector<KICAD_T> itemTypes = {
PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T,
PCB_PAD_T,
PCB_SHAPE_T, PCB_FP_SHAPE_T,
PCB_TEXT_T, PCB_FP_TEXT_T, PCB_TEXTBOX_T, PCB_FP_TEXTBOX_T,
PCB_DIMENSION_T
};
forEachGeometryItem( itemTypes, LSET::AllCuMask(), countItems );
forEachGeometryItem( itemTypes, LSET::AllCuMask(), addToCopperTree );
if( !reportPhase( _( "Tessellating copper zones..." ) ) )
return false; // DRC cancelled
// Cache zone bounding boxes, triangulation, copper zone rtrees, and footprint courtyards
// before we start.
//
for( FOOTPRINT* footprint : m_board->Footprints() )
footprint->BuildPolyCourtyards();
count = allZones.size();
std::atomic<size_t> next( 0 );
std::atomic<size_t> done( 0 );
std::atomic<size_t> threads_finished( 0 );
size_t parallelThreadCount = std::max<size_t>( std::thread::hardware_concurrency(), 2 );
for( ii = 0; ii < parallelThreadCount; ++ii )
{
std::thread t = std::thread(
[ this, &allZones, &done, &threads_finished, &next, count ]( )
{
for( size_t i = next.fetch_add( 1 ); i < count; i = next.fetch_add( 1 ) )
{
ZONE* zone = allZones[ i ];
zone->CacheBoundingBox();
zone->CacheTriangulation();
if( !zone->GetIsRuleArea() && zone->IsOnCopperLayer() )
{
std::unique_ptr<DRC_RTREE> rtree = std::make_unique<DRC_RTREE>();
for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
{
if( IsCopperLayer( layer ) )
rtree->Insert( zone, layer );
}
std::unique_lock<std::mutex> cacheLock( m_board->m_CachesMutex );
m_board->m_CopperZoneRTreeCache[ zone ] = std::move( rtree );
}
if( m_drcEngine->IsCancelled() )
break;
done.fetch_add( 1 );
}
threads_finished.fetch_add( 1 );
} );
t.detach();
}
while( threads_finished < parallelThreadCount )
{
reportProgress( done, count, 1 );
std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
}
return !m_drcEngine->IsCancelled();
}

View File

@ -0,0 +1,50 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2004-2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 2007 Dick Hollenbeck, dick@softplc.com
* Copyright (C) 2019-2020 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef DRC_CACHE_GENERATOR__H
#define DRC_CACHE_GENERATOR__H
#include <drc/drc_test_provider_clearance_base.h>
class DRC_CACHE_GENERATOR : public DRC_TEST_PROVIDER_CLEARANCE_BASE
{
public:
DRC_CACHE_GENERATOR() :
DRC_TEST_PROVIDER_CLEARANCE_BASE()
{
}
virtual ~DRC_CACHE_GENERATOR()
{
}
virtual bool Run() override;
};
#endif // DRC_CACHE_GENERATOR__H

View File

@ -36,6 +36,7 @@
#include <drc/drc_rule_condition.h>
#include <drc/drc_test_provider.h>
#include <drc/drc_item.h>
#include <drc/drc_cache_generator.h>
#include <footprint.h>
#include <pad.h>
#include <pcb_track.h>
@ -594,76 +595,14 @@ void DRC_ENGINE::RunTests( EDA_UNITS aUnits, bool aReportAllTrackErrors, bool aT
DRC_TEST_PROVIDER::Init();
m_board->IncrementTimeStamp(); // Invalidate all caches
m_board->IncrementTimeStamp(); // Invalidate all caches...
if( !ReportPhase( _( "Tessellating copper zones..." ) ) )
DRC_CACHE_GENERATOR cacheGenerator;
cacheGenerator.SetDRCEngine( this );
if( !cacheGenerator.Run() ) // ... and regenerate them.
return;
// Cache zone bounding boxes, triangulation, copper zone rtrees, and footprint courtyards
// before we start.
//
std::vector<ZONE*> allZones = m_board->Zones();
for( FOOTPRINT* footprint : m_board->Footprints() )
{
for( ZONE* zone : footprint->Zones() )
allZones.push_back( zone );
footprint->BuildPolyCourtyards();
}
size_t count = allZones.size();
std::atomic<size_t> next( 0 );
std::atomic<size_t> done( 0 );
std::atomic<size_t> threads_finished( 0 );
size_t parallelThreadCount = std::max<size_t>( std::thread::hardware_concurrency(), 2 );
for( size_t ii = 0; ii < parallelThreadCount; ++ii )
{
std::thread t = std::thread(
[ this, &allZones, &done, &threads_finished, &next, count ]( )
{
for( size_t i = next.fetch_add( 1 ); i < count; i = next.fetch_add( 1 ) )
{
ZONE* zone = allZones[ i ];
zone->CacheBoundingBox();
zone->CacheTriangulation();
if( !zone->GetIsRuleArea() && zone->IsOnCopperLayer() )
{
std::unique_ptr<DRC_RTREE> rtree = std::make_unique<DRC_RTREE>();
for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
{
if( IsCopperLayer( layer ) )
rtree->Insert( zone, layer );
}
std::unique_lock<std::mutex> cacheLock( m_board->m_CachesMutex );
m_board->m_CopperZoneRTrees[ zone ] = std::move( rtree );
}
if( IsCancelled() )
break;
done.fetch_add( 1 );
}
threads_finished.fetch_add( 1 );
} );
t.detach();
}
while( threads_finished < parallelThreadCount )
{
ReportProgress( (double) done / (double) count );
std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
}
// Now run the tests.
//
for( DRC_TEST_PROVIDER* provider : m_testProviders )
{
ReportAux( wxString::Format( wxT( "Run DRC provider: '%s'" ), provider->GetName() ) );

View File

@ -38,7 +38,6 @@ public:
DRC_TEST_PROVIDER_CLEARANCE_BASE () :
DRC_TEST_PROVIDER(),
m_board( nullptr ),
m_largestClearance( 0 ),
m_boardOutlineValid( false )
{
}
@ -49,7 +48,6 @@ public:
protected:
BOARD* m_board;
int m_largestClearance;
bool m_boardOutlineValid;
};

View File

@ -93,25 +93,15 @@ private:
void testItemAgainstZone( BOARD_ITEM* aItem, ZONE* aZone, PCB_LAYER_ID aLayer );
private:
DRC_RTREE m_copperTree;
int m_drcEpsilon;
std::vector<ZONE*> m_copperZones;
int m_drcEpsilon;
};
bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::Run()
{
m_board = m_drcEngine->GetBoard();
DRC_CONSTRAINT worstConstraint;
if( m_drcEngine->QueryWorstConstraint( CLEARANCE_CONSTRAINT, worstConstraint ) )
m_largestClearance = worstConstraint.GetValue().Min();
if( m_drcEngine->QueryWorstConstraint( HOLE_CLEARANCE_CONSTRAINT, worstConstraint ) )
m_largestClearance = std::max( m_largestClearance, worstConstraint.GetValue().Min() );
if( m_largestClearance <= 0 )
if( m_board->m_DRCMaxClearance <= 0 )
{
reportAux( wxT( "No Clearance constraints found. Tests not run." ) );
return true; // continue with other tests
@ -119,90 +109,6 @@ bool DRC_TEST_PROVIDER_COPPER_CLEARANCE::Run()
m_drcEpsilon = m_board->GetDesignSettings().GetDRCEpsilon();
m_copperZones.clear();
for( ZONE* zone : m_board->Zones() )
{
if( ( zone->GetLayerSet() & LSET::AllCuMask() ).any() && !zone->GetIsRuleArea() )
{
m_copperZones.push_back( zone );
m_largestClearance = std::max( m_largestClearance, zone->GetLocalClearance() );
}
}
for( FOOTPRINT* footprint : m_board->Footprints() )
{
for( PAD* pad : footprint->Pads() )
m_largestClearance = std::max( m_largestClearance, pad->GetLocalClearance() );
for( ZONE* zone : footprint->Zones() )
{
if( ( zone->GetLayerSet() & LSET::AllCuMask() ).any() && !zone->GetIsRuleArea() )
{
m_copperZones.push_back( zone );
m_largestClearance = std::max( m_largestClearance, zone->GetLocalClearance() );
}
}
}
reportAux( wxT( "Worst clearance : %d nm" ), m_largestClearance );
// This is the number of tests between 2 calls to the progress bar
size_t delta = 50;
size_t count = 0;
size_t ii = 0;
m_copperTree.clear();
auto countItems =
[&]( BOARD_ITEM* item ) -> bool
{
++count;
return true;
};
auto addToCopperTree =
[&]( BOARD_ITEM* item ) -> bool
{
if( !reportProgress( ii++, count, delta ) )
return false;
LSET layers = item->GetLayerSet();
// Special-case pad holes which pierce all the copper layers
if( item->Type() == PCB_PAD_T )
{
PAD* pad = static_cast<PAD*>( item );
if( pad->GetDrillSizeX() > 0 && pad->GetDrillSizeY() > 0 )
layers |= LSET::AllCuMask();
}
for( PCB_LAYER_ID layer : layers.Seq() )
{
if( IsCopperLayer( layer ) )
m_copperTree.Insert( item, layer, m_largestClearance );
}
return true;
};
if( !reportPhase( _( "Gathering copper items..." ) ) )
return false; // DRC cancelled
static const std::vector<KICAD_T> itemTypes = {
PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T,
PCB_PAD_T,
PCB_SHAPE_T, PCB_FP_SHAPE_T,
PCB_TEXT_T, PCB_FP_TEXT_T, PCB_TEXTBOX_T, PCB_FP_TEXTBOX_T,
PCB_DIMENSION_T
};
forEachGeometryItem( itemTypes, LSET::AllCuMask(), countItems );
forEachGeometryItem( itemTypes, LSET::AllCuMask(), addToCopperTree );
reportAux( wxT( "Testing %d copper items and %d zones..." ), count, m_copperZones.size() );
if( !m_drcEngine->IsErrorLimitExceeded( DRCE_CLEARANCE ) )
{
if( !reportPhase( _( "Checking track & via clearances..." ) ) )
@ -399,7 +305,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testItemAgainstZone( BOARD_ITEM* aItem,
if( !testClearance && !testHoles )
return;
DRC_RTREE* zoneTree = m_board->m_CopperZoneRTrees[ aZone ].get();
DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ aZone ].get();
EDA_RECT itemBBox = aItem->GetBoundingBox();
DRC_CONSTRAINT constraint;
DRC_CONSTRAINT_T constraintType = CLEARANCE_CONSTRAINT;
@ -538,7 +444,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testTrackClearances()
{
std::shared_ptr<SHAPE> trackShape = track->GetEffectiveShape( layer );
m_copperTree.QueryColliding( track, layer, layer,
m_board->m_CopperItemRTreeCache->QueryColliding( track, layer, layer,
// Filter:
[&]( BOARD_ITEM* other ) -> bool
{
@ -575,9 +481,9 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testTrackClearances()
{
return testTrackAgainstItem( track, trackShape.get(), layer, other );
},
m_largestClearance );
m_board->m_DRCMaxClearance );
for( ZONE* zone : m_copperZones )
for( ZONE* zone : m_board->m_DRCCopperZones )
{
testItemAgainstZone( track, zone, layer );
@ -819,7 +725,7 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testPadClearances( )
{
std::shared_ptr<SHAPE> padShape = pad->GetEffectiveShape( layer );
m_copperTree.QueryColliding( pad, layer, layer,
m_board->m_CopperItemRTreeCache->QueryColliding( pad, layer, layer,
// Filter:
[&]( BOARD_ITEM* other ) -> bool
{
@ -846,9 +752,9 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testPadClearances( )
{
return testPadAgainstItem( pad, padShape.get(), layer, other );
},
m_largestClearance );
m_board->m_DRCMaxClearance );
for( ZONE* zone : m_copperZones )
for( ZONE* zone : m_board->m_DRCCopperZones )
{
testItemAgainstZone( pad, zone, layer );
@ -883,32 +789,38 @@ void DRC_TEST_PROVIDER_COPPER_CLEARANCE::testZonesToZones()
{
PCB_LAYER_ID layer = static_cast<PCB_LAYER_ID>( layer_id );
std::vector<SHAPE_POLY_SET> smoothed_polys;
smoothed_polys.resize( m_copperZones.size() );
smoothed_polys.resize( m_board->m_DRCCopperZones.size() );
// Skip over layers not used on the current board
if( !m_board->IsLayerEnabled( layer ) )
continue;
for( size_t ii = 0; ii < m_copperZones.size(); ii++ )
for( size_t ii = 0; ii < m_board->m_DRCCopperZones.size(); ii++ )
{
if( m_copperZones[ii]->IsOnLayer( layer ) )
m_copperZones[ii]->BuildSmoothedPoly( smoothed_polys[ii], layer, boardOutline );
if( m_board->m_DRCCopperZones[ii]->IsOnLayer( layer ) )
{
m_board->m_DRCCopperZones[ii]->BuildSmoothedPoly( smoothed_polys[ii], layer,
boardOutline );
}
}
// iterate through all areas
for( size_t ia = 0; ia < m_copperZones.size(); ia++ )
for( size_t ia = 0; ia < m_board->m_DRCCopperZones.size(); ia++ )
{
if( !reportProgress( layer_id * m_copperZones.size() + ia, B_Cu * m_copperZones.size(), delta ) )
if( !reportProgress( layer_id * m_board->m_DRCCopperZones.size() + ia,
B_Cu * m_board->m_DRCCopperZones.size(), delta ) )
{
return; // DRC cancelled
}
ZONE* zoneA = m_copperZones[ia];
ZONE* zoneA = m_board->m_DRCCopperZones[ia];
if( !zoneA->IsOnLayer( layer ) )
continue;
for( size_t ia2 = ia + 1; ia2 < m_copperZones.size(); ia2++ )
for( size_t ia2 = ia + 1; ia2 < m_board->m_DRCCopperZones.size(); ia2++ )
{
ZONE* zoneB = m_copperZones[ia2];
ZONE* zoneB = m_board->m_DRCCopperZones[ia2];
// test for same layer
if( !zoneB->IsOnLayer( layer ) )

View File

@ -43,7 +43,9 @@
class DRC_TEST_PROVIDER_COURTYARD_CLEARANCE : public DRC_TEST_PROVIDER_CLEARANCE_BASE
{
public:
DRC_TEST_PROVIDER_COURTYARD_CLEARANCE ()
DRC_TEST_PROVIDER_COURTYARD_CLEARANCE () :
DRC_TEST_PROVIDER_CLEARANCE_BASE(),
m_largestCourtyardClearance( 0 )
{
m_isRuleDriven = false;
}
@ -68,6 +70,9 @@ private:
bool testFootprintCourtyardDefinitions();
bool testCourtyardClearances();
private:
int m_largestCourtyardClearance;
};
@ -177,8 +182,8 @@ bool DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::testCourtyardClearances()
BOX2I frontBBox = frontA.BBoxFromCaches();
BOX2I backBBox = backA.BBoxFromCaches();
frontBBox.Inflate( m_largestClearance );
backBBox.Inflate( m_largestClearance );
frontBBox.Inflate( m_largestCourtyardClearance );
backBBox.Inflate( m_largestCourtyardClearance );
EDA_RECT fpABBox = fpA->GetBoundingBox();
@ -315,9 +320,9 @@ bool DRC_TEST_PROVIDER_COURTYARD_CLEARANCE::Run()
DRC_CONSTRAINT constraint;
if( m_drcEngine->QueryWorstConstraint( COURTYARD_CLEARANCE_CONSTRAINT, constraint ) )
m_largestClearance = constraint.GetValue().Min();
m_largestCourtyardClearance = constraint.GetValue().Min();
reportAux( wxT( "Worst courtyard clearance : %d nm" ), m_largestClearance );
reportAux( wxT( "Worst courtyard clearance : %d nm" ), m_largestCourtyardClearance );
if( !testFootprintCourtyardDefinitions() )
return false;

View File

@ -169,6 +169,7 @@ struct DIFF_PAIR_COUPLED_SEGMENTS
{}
};
struct DIFF_PAIR_ITEMS
{
std::set<BOARD_CONNECTED_ITEM*> itemsP, itemsN;
@ -178,7 +179,8 @@ struct DIFF_PAIR_ITEMS
int totalLengthP;
};
static void extractDiffPairCoupledItems( DIFF_PAIR_ITEMS& aDp, DRC_RTREE& aTree )
static void extractDiffPairCoupledItems( DIFF_PAIR_ITEMS& aDp )
{
for( BOARD_CONNECTED_ITEM* itemP : aDp.itemsP )
{
@ -250,12 +252,13 @@ static void extractDiffPairCoupledItems( DIFF_PAIR_ITEMS& aDp, DRC_RTREE& aTree
SHAPE_SEGMENT checkSegStart( bestCoupled->coupledP.A, bestCoupled->coupledN.A );
SHAPE_SEGMENT checkSegEnd( bestCoupled->coupledP.B, bestCoupled->coupledN.B );
DRC_RTREE* tree = bestCoupled->parentP->GetBoard()->m_CopperItemRTreeCache.get();
// check if there's anything in between the segments suspected to be coupled. If
// there's nothing, assume they are really coupled.
if( !aTree.CheckColliding( &checkSegStart, sp->GetLayer(), 0, excludeSelf )
&& !aTree.CheckColliding( &checkSegEnd, sp->GetLayer(), 0, excludeSelf ) )
if( !tree->CheckColliding( &checkSegStart, sp->GetLayer(), 0, excludeSelf )
&& !tree->CheckColliding( &checkSegEnd, sp->GetLayer(), 0, excludeSelf ) )
{
aDp.coupled.push_back( *bestCoupled );
}
@ -319,25 +322,7 @@ bool test::DRC_TEST_PROVIDER_DIFF_PAIR_COUPLING::Run()
drc_dbg( 10, wxT( "dp rule matches %d\n" ), (int) dpRuleMatches.size() );
DRC_RTREE copperTree;
auto addToTree =
[&copperTree]( BOARD_ITEM *item ) -> bool
{
for( PCB_LAYER_ID layer : item->GetLayerSet().Seq() )
{
if( IsCopperLayer( layer ) )
copperTree.Insert( item, layer );
}
return true;
};
forEachGeometryItem( { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T, PCB_PAD_T, PCB_ZONE_T },
LSET::AllCuMask(), addToTree );
reportAux( wxString::Format( _("DPs evaluated:") ) );
reportAux( wxT( "DPs evaluated:" ) );
for( auto& it : dpRuleMatches )
{
@ -355,7 +340,7 @@ bool test::DRC_TEST_PROVIDER_DIFF_PAIR_COUPLING::Run()
nameP,
nameN ) );
extractDiffPairCoupledItems( it.second, copperTree );
extractDiffPairCoupledItems( it.second );
it.second.totalCoupled = 0;
it.second.totalLengthN = 0;

View File

@ -133,7 +133,7 @@ bool DRC_TEST_PROVIDER_DISALLOW::Run()
SHAPE_POLY_SET areaPoly = ruleArea->Outline()->CloneDropTriangulation();
areaPoly.Deflate( epsilon, 0, SHAPE_POLY_SET::ALLOW_ACUTE_CORNERS );
DRC_RTREE* zoneRTree = board->m_CopperZoneRTrees[ copperZone ].get();
DRC_RTREE* zoneRTree = board->m_CopperZoneRTreeCache[ copperZone ].get();
if( zoneRTree )
{

View File

@ -50,7 +50,8 @@ class DRC_TEST_PROVIDER_EDGE_CLEARANCE : public DRC_TEST_PROVIDER_CLEARANCE_BASE
{
public:
DRC_TEST_PROVIDER_EDGE_CLEARANCE () :
DRC_TEST_PROVIDER_CLEARANCE_BASE()
DRC_TEST_PROVIDER_CLEARANCE_BASE(),
m_largestEdgeClearance( 0 )
{
}
@ -76,6 +77,7 @@ private:
private:
std::vector<PAD*> m_castellatedPads;
int m_largestEdgeClearance;
};
@ -160,9 +162,9 @@ bool DRC_TEST_PROVIDER_EDGE_CLEARANCE::Run()
DRC_CONSTRAINT worstClearanceConstraint;
if( m_drcEngine->QueryWorstConstraint( EDGE_CLEARANCE_CONSTRAINT, worstClearanceConstraint ) )
m_largestClearance = worstClearanceConstraint.GetValue().Min();
m_largestEdgeClearance = worstClearanceConstraint.GetValue().Min();
reportAux( wxT( "Worst clearance : %d nm" ), m_largestClearance );
reportAux( wxT( "Worst clearance : %d nm" ), m_largestEdgeClearance );
std::vector<std::unique_ptr<PCB_SHAPE>> edges;
DRC_RTREE edgesTree;
@ -222,7 +224,7 @@ bool DRC_TEST_PROVIDER_EDGE_CLEARANCE::Run()
for( PCB_LAYER_ID layer : { Edge_Cuts, Margin } )
{
if( edge->IsOnLayer( layer ) )
edgesTree.Insert( edge.get(), layer, m_largestClearance );
edgesTree.Insert( edge.get(), layer, m_largestEdgeClearance );
}
}
@ -231,7 +233,7 @@ bool DRC_TEST_PROVIDER_EDGE_CLEARANCE::Run()
for( PAD* pad : footprint->Pads() )
{
if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
edgesTree.Insert( pad, Edge_Cuts, m_largestClearance );
edgesTree.Insert( pad, Edge_Cuts, m_largestEdgeClearance );
if( pad->GetProperty() == PAD_PROP::CASTELLATED )
m_castellatedPads.push_back( pad );
@ -278,25 +280,25 @@ bool DRC_TEST_PROVIDER_EDGE_CLEARANCE::Run()
if( testCopper && item->IsOnCopperLayer() )
{
edgesTree.QueryColliding( item, UNDEFINED_LAYER, testLayer, nullptr,
[&]( BOARD_ITEM* edge ) -> bool
[&]( BOARD_ITEM* edge ) -> bool
{
return testAgainstEdge( item, itemShape.get(), edge,
EDGE_CLEARANCE_CONSTRAINT,
DRCE_EDGE_CLEARANCE );
},
m_largestClearance );
m_largestEdgeClearance );
}
if( testSilk && ( item->IsOnLayer( F_SilkS ) || item->IsOnLayer( B_SilkS ) ) )
{
if( edgesTree.QueryColliding( item, UNDEFINED_LAYER, testLayer, nullptr,
[&]( BOARD_ITEM* edge ) -> bool
[&]( BOARD_ITEM* edge ) -> bool
{
return testAgainstEdge( item, itemShape.get(), edge,
SILK_CLEARANCE_CONSTRAINT,
DRCE_SILK_EDGE_CLEARANCE );
},
m_largestClearance ) )
m_largestEdgeClearance ) )
{
// violations reported during QueryColliding
}

View File

@ -45,8 +45,9 @@ class DRC_TEST_PROVIDER_HOLE_TO_HOLE : public DRC_TEST_PROVIDER_CLEARANCE_BASE
{
public:
DRC_TEST_PROVIDER_HOLE_TO_HOLE () :
DRC_TEST_PROVIDER_CLEARANCE_BASE(),
m_board( nullptr )
DRC_TEST_PROVIDER_CLEARANCE_BASE(),
m_board( nullptr ),
m_largestHoleToHoleClearance( 0 )
{
}
@ -71,6 +72,7 @@ private:
BOARD* m_board;
DRC_RTREE m_holeTree;
int m_largestHoleToHoleClearance;
};
@ -106,8 +108,8 @@ bool DRC_TEST_PROVIDER_HOLE_TO_HOLE::Run()
if( m_drcEngine->QueryWorstConstraint( HOLE_TO_HOLE_CONSTRAINT, worstClearanceConstraint ) )
{
m_largestClearance = worstClearanceConstraint.GetValue().Min();
reportAux( wxT( "Worst hole to hole : %d nm" ), m_largestClearance );
m_largestHoleToHoleClearance = worstClearanceConstraint.GetValue().Min();
reportAux( wxT( "Worst hole to hole : %d nm" ), m_largestHoleToHoleClearance );
}
else
{
@ -146,7 +148,7 @@ bool DRC_TEST_PROVIDER_HOLE_TO_HOLE::Run()
// We only care about drilled (ie: round) holes
if( pad->GetDrillSize().x && pad->GetDrillSize().x == pad->GetDrillSize().y )
m_holeTree.Insert( item, F_Cu, m_largestClearance );
m_holeTree.Insert( item, F_Cu, m_largestHoleToHoleClearance );
}
else if( item->Type() == PCB_VIA_T )
{
@ -154,7 +156,7 @@ bool DRC_TEST_PROVIDER_HOLE_TO_HOLE::Run()
// We only care about mechanically drilled (ie: non-laser) holes
if( via->GetViaType() == VIATYPE::THROUGH )
m_holeTree.Insert( item, F_Cu, m_largestClearance );
m_holeTree.Insert( item, F_Cu, m_largestHoleToHoleClearance );
}
return true;
@ -204,7 +206,7 @@ bool DRC_TEST_PROVIDER_HOLE_TO_HOLE::Run()
{
return testHoleAgainstHole( via, holeShape.get(), other );
},
m_largestClearance );
m_largestHoleToHoleClearance );
}
}
@ -249,7 +251,7 @@ bool DRC_TEST_PROVIDER_HOLE_TO_HOLE::Run()
{
return testHoleAgainstHole( pad, holeShape.get(), other );
},
m_largestClearance );
m_largestHoleToHoleClearance );
}
}

View File

@ -83,7 +83,6 @@ private:
private:
DRC_RTREE m_itemTree;
std::vector<ZONE*> m_zones;
};
@ -91,56 +90,23 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::Run()
{
m_board = m_drcEngine->GetBoard();
m_itemTree.clear();
m_zones.clear();
m_zones.reserve( m_board->Zones().size() );
int errorMax = m_board->GetDesignSettings().m_MaxError;
DRC_CONSTRAINT worstConstraint;
int errorMax = m_board->GetDesignSettings().m_MaxError;
if( m_drcEngine->QueryWorstConstraint( PHYSICAL_CLEARANCE_CONSTRAINT, worstConstraint ) )
m_largestClearance = worstConstraint.GetValue().Min();
if( m_drcEngine->QueryWorstConstraint( PHYSICAL_HOLE_CLEARANCE_CONSTRAINT, worstConstraint ) )
m_largestClearance = std::max( m_largestClearance, worstConstraint.GetValue().Min() );
if( m_largestClearance <= 0 )
if( m_board->m_DRCMaxPhysicalClearance <= 0 )
{
reportAux( wxT( "No Clearance constraints found. Tests not run." ) );
reportAux( wxT( "No physical clearance constraints found. Tests not run." ) );
return true; // continue with other tests
}
for( ZONE* zone : m_board->Zones() )
{
if( !zone->GetIsRuleArea() )
{
m_zones.push_back( zone );
m_largestClearance = std::max( m_largestClearance, zone->GetLocalClearance() );
}
}
for( FOOTPRINT* footprint : m_board->Footprints() )
{
for( PAD* pad : footprint->Pads() )
m_largestClearance = std::max( m_largestClearance, pad->GetLocalClearance() );
for( ZONE* zone : footprint->Zones() )
{
if( !zone->GetIsRuleArea() )
{
m_zones.push_back( zone );
m_largestClearance = std::max( m_largestClearance, zone->GetLocalClearance() );
}
}
}
reportAux( wxT( "Worst clearance : %d nm" ), m_largestClearance );
reportAux( wxT( "Largest physical clearance : %d nm" ), m_board->m_DRCMaxPhysicalClearance );
// This is the number of tests between 2 calls to the progress bar
size_t delta = 100;
size_t count = 0;
size_t ii = 0;
if( !reportPhase( _( "Gathering items..." ) ) )
if( !reportPhase( _( "Gathering physical items..." ) ) )
return false; // DRC cancelled
static const std::vector<KICAD_T> itemTypes = {
@ -194,7 +160,7 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::Run()
}
for( PCB_LAYER_ID layer : layers.Seq() )
m_itemTree.Insert( item, layer, m_largestClearance );
m_itemTree.Insert( item, layer, m_board->m_DRCMaxPhysicalClearance );
return true;
} );
@ -251,7 +217,7 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::Run()
return testItemAgainstItem( item, itemShape.get(), layer,
other );
},
m_largestClearance );
m_board->m_DRCMaxPhysicalClearance );
testItemAgainstZones( item, layer );
}
@ -391,6 +357,9 @@ void DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testShapeLineChain( const SHAPE_LINE_
int count = aOutline.SegmentCount();
int clearance = aConstraint.GetValue().Min();
if( aConstraint.GetSeverity() == RPT_SEVERITY_IGNORE || clearance - epsilon <= 0 )
return;
// Trigonometry is not cheap; cache seg angles
std::vector<double> angles;
angles.reserve( count );
@ -508,17 +477,21 @@ void DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testShapeLineChain( const SHAPE_LINE_
{
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_CLEARANCE );
wxString msg;
VECTOR2I pt = collision.first;
msg.Printf( _( "(%s clearance %s; actual %s)" ),
if( aParentItem->GetParentFootprint() )
pt += aParentItem->GetParentFootprint()->GetPosition();
msg.Printf( _( "Internal clearance violation (%s clearance %s; actual %s)" ),
aConstraint.GetName(),
MessageTextFromValue( userUnits(), clearance ),
MessageTextFromValue( userUnits(), collision.second ) );
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
drce->SetErrorMessage( msg );
drce->SetItems( aParentItem );
drce->SetViolatingRule( aConstraint.GetParentRule() );
reportViolation( drce, collision.first, aLayer );
reportViolation( drce, pt, aLayer );
}
}
@ -526,13 +499,14 @@ void DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testShapeLineChain( const SHAPE_LINE_
void DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testZoneLayer( ZONE* aZone, PCB_LAYER_ID aLayer,
DRC_CONSTRAINT& aConstraint )
{
int epsilon = m_board->GetDesignSettings().GetDRCEpsilon();
int clearance = aConstraint.GetValue().Min();
SHAPE_POLY_SET fill = aZone->GetFilledPolysList( aLayer )->CloneDropTriangulation();
int epsilon = m_board->GetDesignSettings().GetDRCEpsilon();
int clearance = aConstraint.GetValue().Min();
if( aConstraint.GetSeverity() == RPT_SEVERITY_IGNORE || clearance - epsilon <= 0 )
return;
SHAPE_POLY_SET fill = aZone->GetFilledPolysList( aLayer )->CloneDropTriangulation();
// Turn fractured fill into outlines and holes
fill.Simplify( SHAPE_POLY_SET::PM_FAST );
@ -717,7 +691,7 @@ bool DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstItem( BOARD_ITEM* item
void DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstZones( BOARD_ITEM* aItem,
PCB_LAYER_ID aLayer )
{
for( ZONE* zone : m_zones )
for( ZONE* zone : m_board->m_DRCZones )
{
if( !zone->GetLayerSet().test( aLayer ) )
continue;
@ -730,7 +704,7 @@ void DRC_TEST_PROVIDER_PHYSICAL_CLEARANCE::testItemAgainstZones( BOARD_ITEM* aIt
if( !testClearance && !testHoles )
return;
DRC_RTREE* zoneTree = m_board->m_CopperZoneRTrees[ zone ].get();
DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ zone ].get();
EDA_RECT itemBBox = aItem->GetBoundingBox();
DRC_CONSTRAINT constraint;
bool colliding;

View File

@ -512,7 +512,7 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::testMaskItemAgainstZones( BOARD_ITEM* aItem,
if( aItem->GetBoundingBox().Intersects( zone->GetCachedBoundingBox() ) )
{
DRC_RTREE* zoneTree = m_board->m_CopperZoneRTrees[ zone ].get();
DRC_RTREE* zoneTree = m_board->m_CopperZoneRTreeCache[ zone ].get();
int clearance = m_board->GetDesignSettings().m_SolderMaskToCopperClearance;
int actual;
VECTOR2I pos;
@ -659,7 +659,7 @@ bool DRC_TEST_PROVIDER_SOLDER_MASK::Run()
m_largestClearance = std::max( m_largestClearance, pad->GetSolderMaskExpansion() );
}
// Order is important here: m_webWidth must be added in before m_largestClearance is maxed
// Order is important here: m_webWidth must be added in before m_largestCourtyardClearance is maxed
// with the various SILK_CLEARANCE_CONSTRAINTS.
m_largestClearance += m_largestClearance + m_webWidth;

View File

@ -547,7 +547,7 @@ bool calcIsInsideArea( BOARD_ITEM* aItem, const EDA_RECT& aItemBBox, PCB_EXPR_CO
if( !zone->IsFilled() )
return false;
DRC_RTREE* zoneRTree = board->m_CopperZoneRTrees[ zone ].get();
DRC_RTREE* zoneRTree = board->m_CopperZoneRTreeCache[ zone ].get();
if( zoneRTree )
{

View File

@ -74,8 +74,18 @@ HANDLE_EXCEPTIONS(BOARD::TracksInNetBetweenPoints)
// Do not wrap internal-only structures
%ignore BOARD::m_CachesMutex;
%ignore BOARD::m_InsideCourtyardCache;
%ignore BOARD::m_InsideFCourtyardCache;
%ignore BOARD::m_InsideBCourtyardCache;
%ignore BOARD::m_InsideAreaCache;
%ignore BOARD::m_CopperZoneRTrees;
%ignore BOARD::m_LayerExpressionCache;
%ignore BOARD::m_CopperZoneRTreeCache;
%ignore BOARD::m_CopperItemRTreeCache;
%ignore BOARD::m_DRCZones;
%ignore BOARD::m_DRCCopperZones;
%ignore BOARD::m_DRCMaxClearance;
%ignore BOARD::m_DRCMaxPhysicalClearance;
%ignore BOARD::m_SolderMask;
%include board.h
%{

View File

@ -77,7 +77,9 @@ using namespace std::placeholders;
class DRC_TEST_PROVIDER_COURTYARD_CLEARANCE_ON_MOVE : public DRC_TEST_PROVIDER_CLEARANCE_BASE
{
public:
DRC_TEST_PROVIDER_COURTYARD_CLEARANCE_ON_MOVE()
DRC_TEST_PROVIDER_COURTYARD_CLEARANCE_ON_MOVE() :
DRC_TEST_PROVIDER_CLEARANCE_BASE(),
m_largestCourtyardClearance( 0 )
{
m_isRuleDriven = false;
}
@ -108,6 +110,9 @@ public:
private:
void testCourtyardClearances();
private:
int m_largestCourtyardClearance;
};
@ -129,8 +134,8 @@ void DRC_TEST_PROVIDER_COURTYARD_CLEARANCE_ON_MOVE::testCourtyardClearances()
BOX2I frontBBox = frontA.BBoxFromCaches();
BOX2I backBBox = backA.BBoxFromCaches();
frontBBox.Inflate( m_largestClearance );
backBBox.Inflate( m_largestClearance );
frontBBox.Inflate( m_largestCourtyardClearance );
backBBox.Inflate( m_largestCourtyardClearance );
EDA_RECT fpABBox = fpA->GetBoundingBox();
@ -256,15 +261,12 @@ void DRC_TEST_PROVIDER_COURTYARD_CLEARANCE_ON_MOVE::Init( BOARD* aBoard )
bool DRC_TEST_PROVIDER_COURTYARD_CLEARANCE_ON_MOVE::Run()
{
m_FpInConflict.clear();
m_largestClearance = 0;
m_largestCourtyardClearance = 0;
// Currently, do not use DRC engine for calculation time reasons
#if 0
DRC_CONSTRAINT constraint;
if( m_drcEngine->QueryWorstConstraint( COURTYARD_CLEARANCE_CONSTRAINT, constraint ) )
m_largestClearance = constraint.GetValue().Min();
#endif
m_largestCourtyardClearance = constraint.GetValue().Min();
testCourtyardClearances();