More performance enhancements for DRC.
This commit is contained in:
parent
5da817649b
commit
82ebc247b8
|
@ -476,7 +476,17 @@ static inline bool Collide( const SHAPE_LINE_CHAIN_BASE& aA, const SHAPE_SEGMENT
|
||||||
static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_RECT& aB, int aClearance,
|
static inline bool Collide( const SHAPE_RECT& aA, const SHAPE_RECT& aB, int aClearance,
|
||||||
int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
|
int* aActual, VECTOR2I* aLocation, VECTOR2I* aMTV )
|
||||||
{
|
{
|
||||||
return Collide( aA.Outline(), aB.Outline(), aClearance, aActual, aLocation, aMTV );
|
if( aClearance || aActual || aLocation || aMTV )
|
||||||
|
{
|
||||||
|
return Collide( aA.Outline(), aB.Outline(), aClearance, aActual, aLocation, aMTV );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BOX2I bboxa = aA.BBox();
|
||||||
|
BOX2I bboxb = aB.BBox();
|
||||||
|
|
||||||
|
return bboxa.Intersects( bboxb );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||||
*
|
*
|
||||||
* Copyright (C) 2015 CERN
|
* Copyright (C) 2015 CERN
|
||||||
|
* Copyright (C) 2015-2022 KiCad Developers, see AUTHORS.txt for contributors.
|
||||||
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
|
@ -25,10 +26,11 @@
|
||||||
|
|
||||||
#include <geometry/shape_rect.h>
|
#include <geometry/shape_rect.h>
|
||||||
|
|
||||||
bool SHAPE_RECT::Collide( const SEG& aSeg, int aClearance, int* aActual,
|
bool SHAPE_RECT::Collide( const SEG& aSeg, int aClearance, int* aActual, VECTOR2I* aLocation ) const
|
||||||
VECTOR2I* aLocation ) const
|
|
||||||
{
|
{
|
||||||
if( BBox( 0 ).Contains( aSeg.A ) )
|
BOX2I bbox( BBox() );
|
||||||
|
|
||||||
|
if( bbox.Contains( aSeg.A ) )
|
||||||
{
|
{
|
||||||
if( aLocation )
|
if( aLocation )
|
||||||
*aLocation = aSeg.A;
|
*aLocation = aSeg.A;
|
||||||
|
@ -39,7 +41,7 @@ bool SHAPE_RECT::Collide( const SEG& aSeg, int aClearance, int* aActual,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( BBox( 0 ).Contains( aSeg.B ) )
|
if( bbox.Contains( aSeg.B ) )
|
||||||
{
|
{
|
||||||
if( aLocation )
|
if( aLocation )
|
||||||
*aLocation = aSeg.B;
|
*aLocation = aSeg.B;
|
||||||
|
|
|
@ -664,25 +664,29 @@ void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( ZONE* aZone, PCB_LAYER_ID
|
||||||
wxLogTrace( wxT( "CN" ), wxT( "Found %u isolated islands\n" ), (unsigned) aIslands.size() );
|
wxLogTrace( wxT( "CN" ), wxT( "Found %u isolated islands\n" ), (unsigned) aIslands.size() );
|
||||||
}
|
}
|
||||||
|
|
||||||
void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones )
|
void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones,
|
||||||
|
bool aConnectivityAlreadyRebuilt )
|
||||||
{
|
{
|
||||||
int delta = 10; // Number of additions between 2 calls to the progress bar
|
int delta = 10; // Number of additions between 2 calls to the progress bar
|
||||||
int ii = 0;
|
int ii = 0;
|
||||||
|
|
||||||
for( CN_ZONE_ISOLATED_ISLAND_LIST& z : aZones )
|
if( !aConnectivityAlreadyRebuilt )
|
||||||
{
|
{
|
||||||
Remove( z.m_zone );
|
for( CN_ZONE_ISOLATED_ISLAND_LIST& z : aZones )
|
||||||
Add( z.m_zone );
|
|
||||||
ii++;
|
|
||||||
|
|
||||||
if( m_progressReporter && ( ii % delta ) == 0 )
|
|
||||||
{
|
{
|
||||||
m_progressReporter->SetCurrentProgress( (double) ii / (double) aZones.size() );
|
Remove( z.m_zone );
|
||||||
m_progressReporter->KeepRefreshing( false );
|
Add( z.m_zone );
|
||||||
}
|
ii++;
|
||||||
|
|
||||||
if( m_progressReporter && m_progressReporter->IsCancelled() )
|
if( m_progressReporter && ( ii % delta ) == 0 )
|
||||||
return;
|
{
|
||||||
|
m_progressReporter->SetCurrentProgress( (double) ii / (double) aZones.size() );
|
||||||
|
m_progressReporter->KeepRefreshing( false );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_progressReporter && m_progressReporter->IsCancelled() )
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_connClusters = SearchClusters( CSM_CONNECTIVITY_CHECK );
|
m_connClusters = SearchClusters( CSM_CONNECTIVITY_CHECK );
|
||||||
|
|
|
@ -224,7 +224,8 @@ public:
|
||||||
*
|
*
|
||||||
* @param: aZones is the set of zones to search for islands.
|
* @param: aZones is the set of zones to search for islands.
|
||||||
*/
|
*/
|
||||||
void FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones );
|
void FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones,
|
||||||
|
bool aConnectivityAlreadyRebuilt );
|
||||||
|
|
||||||
const CLUSTERS& GetClusters();
|
const CLUSTERS& GetClusters();
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#include <ratsnest/ratsnest_data.h>
|
#include <ratsnest/ratsnest_data.h>
|
||||||
#include <progress_reporter.h>
|
#include <progress_reporter.h>
|
||||||
#include <trigo.h>
|
#include <trigo.h>
|
||||||
|
#include <drc/drc_rtree.h>
|
||||||
|
|
||||||
CONNECTIVITY_DATA::CONNECTIVITY_DATA()
|
CONNECTIVITY_DATA::CONNECTIVITY_DATA()
|
||||||
{
|
{
|
||||||
|
@ -312,9 +313,10 @@ void CONNECTIVITY_DATA::FindIsolatedCopperIslands( ZONE* aZone, std::vector<int>
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void CONNECTIVITY_DATA::FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones )
|
void CONNECTIVITY_DATA::FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones,
|
||||||
|
bool aConnectivityAlreadyRebuilt )
|
||||||
{
|
{
|
||||||
m_connAlgo->FindIsolatedCopperIslands( aZones );
|
m_connAlgo->FindIsolatedCopperIslands( aZones, aConnectivityAlreadyRebuilt );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -740,14 +742,32 @@ bool CONNECTIVITY_DATA::TestTrackEndpointDangling( PCB_TRACK* aTrack, VECTOR2I*
|
||||||
for( CN_ITEM* connected : citem->ConnectedItems() )
|
for( CN_ITEM* connected : citem->ConnectedItems() )
|
||||||
{
|
{
|
||||||
BOARD_CONNECTED_ITEM* item = connected->Parent();
|
BOARD_CONNECTED_ITEM* item = connected->Parent();
|
||||||
|
ZONE* zone = dynamic_cast<ZONE*>( item );
|
||||||
|
DRC_RTREE* rtree = nullptr;
|
||||||
|
bool hitStart = false;
|
||||||
|
bool hitEnd = false;
|
||||||
|
|
||||||
if( item->GetFlags() & IS_DELETED )
|
if( item->GetFlags() & IS_DELETED )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::shared_ptr<SHAPE> shape = item->GetEffectiveShape( layer );
|
if( zone )
|
||||||
|
rtree = zone->GetBoard()->m_CopperZoneRTreeCache[ zone ].get();
|
||||||
|
|
||||||
bool hitStart = shape->Collide( aTrack->GetStart(), accuracy );
|
if( rtree )
|
||||||
bool hitEnd = shape->Collide( aTrack->GetEnd(), accuracy );
|
{
|
||||||
|
SHAPE_CIRCLE start( aTrack->GetStart(), accuracy );
|
||||||
|
SHAPE_CIRCLE end( aTrack->GetEnd(), accuracy );
|
||||||
|
|
||||||
|
hitStart = rtree->QueryColliding( start.BBox(), &start, layer );
|
||||||
|
hitEnd = rtree->QueryColliding( end.BBox(), &end, layer );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::shared_ptr<SHAPE> shape = item->GetEffectiveShape( layer );
|
||||||
|
|
||||||
|
hitStart = shape->Collide( aTrack->GetStart(), accuracy );
|
||||||
|
hitEnd = shape->Collide( aTrack->GetEnd(), accuracy );
|
||||||
|
}
|
||||||
|
|
||||||
if( hitStart && hitEnd )
|
if( hitStart && hitEnd )
|
||||||
{
|
{
|
||||||
|
|
|
@ -185,7 +185,8 @@ public:
|
||||||
* @param aIslands list of islands that have no connections (outline indices in the polygon set)
|
* @param aIslands list of islands that have no connections (outline indices in the polygon set)
|
||||||
*/
|
*/
|
||||||
void FindIsolatedCopperIslands( ZONE* aZone, std::vector<int>& aIslands );
|
void FindIsolatedCopperIslands( ZONE* aZone, std::vector<int>& aIslands );
|
||||||
void FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones );
|
void FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones,
|
||||||
|
bool aConnectivityAlreadyRebuilt = false );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function RecalculateRatsnest()
|
* Function RecalculateRatsnest()
|
||||||
|
|
|
@ -85,7 +85,7 @@ bool DRC_TEST_PROVIDER_CONNECTIVITY::Run()
|
||||||
// Rebuild just in case. This really needs to be reliable.
|
// Rebuild just in case. This really needs to be reliable.
|
||||||
connectivity->Clear();
|
connectivity->Clear();
|
||||||
connectivity->Build( board, m_drcEngine->GetProgressReporter() );
|
connectivity->Build( board, m_drcEngine->GetProgressReporter() );
|
||||||
connectivity->FindIsolatedCopperIslands( islandsList );
|
connectivity->FindIsolatedCopperIslands( islandsList, true );
|
||||||
|
|
||||||
int delta = 100; // This is the number of tests between 2 calls to the progress bar
|
int delta = 100; // This is the number of tests between 2 calls to the progress bar
|
||||||
int ii = 0;
|
int ii = 0;
|
||||||
|
|
|
@ -94,7 +94,6 @@ private:
|
||||||
|
|
||||||
std::unique_ptr<DRC_RTREE> m_tesselatedTree;
|
std::unique_ptr<DRC_RTREE> m_tesselatedTree;
|
||||||
std::unique_ptr<DRC_RTREE> m_itemTree;
|
std::unique_ptr<DRC_RTREE> m_itemTree;
|
||||||
std::vector<ZONE*> m_copperZones;
|
|
||||||
|
|
||||||
std::map< std::tuple<BOARD_ITEM*, BOARD_ITEM*, PCB_LAYER_ID>, int> m_checkedPairs;
|
std::map< std::tuple<BOARD_ITEM*, BOARD_ITEM*, PCB_LAYER_ID>, int> m_checkedPairs;
|
||||||
|
|
||||||
|
@ -121,9 +120,6 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::addItemToRTrees( BOARD_ITEM* item )
|
||||||
SHAPE_POLY_SET::PM_FAST );
|
SHAPE_POLY_SET::PM_FAST );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( zone->IsOnCopperLayer() && !zone->GetIsRuleArea() )
|
|
||||||
m_copperZones.push_back( zone );
|
|
||||||
}
|
}
|
||||||
else if( item->Type() == PCB_PAD_T )
|
else if( item->Type() == PCB_PAD_T )
|
||||||
{
|
{
|
||||||
|
@ -188,10 +184,6 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::buildRTrees()
|
||||||
|
|
||||||
m_tesselatedTree = std::make_unique<DRC_RTREE>();
|
m_tesselatedTree = std::make_unique<DRC_RTREE>();
|
||||||
m_itemTree = std::make_unique<DRC_RTREE>();
|
m_itemTree = std::make_unique<DRC_RTREE>();
|
||||||
m_copperZones.clear();
|
|
||||||
|
|
||||||
// Unlikely to be correct, but better than starting at 0
|
|
||||||
m_copperZones.reserve( m_board->Zones().size() );
|
|
||||||
|
|
||||||
forEachGeometryItem( s_allBasicItems, layers,
|
forEachGeometryItem( s_allBasicItems, layers,
|
||||||
[&]( BOARD_ITEM* item ) -> bool
|
[&]( BOARD_ITEM* item ) -> bool
|
||||||
|
@ -495,7 +487,7 @@ void DRC_TEST_PROVIDER_SOLDER_MASK::testMaskItemAgainstZones( BOARD_ITEM* aItem,
|
||||||
PCB_LAYER_ID aMaskLayer,
|
PCB_LAYER_ID aMaskLayer,
|
||||||
PCB_LAYER_ID aTargetLayer )
|
PCB_LAYER_ID aTargetLayer )
|
||||||
{
|
{
|
||||||
for( ZONE* zone : m_copperZones )
|
for( ZONE* zone : m_board->m_DRCCopperZones )
|
||||||
{
|
{
|
||||||
if( !zone->GetLayerSet().test( aTargetLayer ) )
|
if( !zone->GetLayerSet().test( aTargetLayer ) )
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <thread>
|
||||||
#include <board.h>
|
#include <board.h>
|
||||||
#include <board_design_settings.h>
|
#include <board_design_settings.h>
|
||||||
#include <connectivity/connectivity_data.h>
|
#include <connectivity/connectivity_data.h>
|
||||||
|
@ -65,132 +67,163 @@ public:
|
||||||
{
|
{
|
||||||
return wxT( "Checks thermal reliefs for a sufficient number of connecting spokes" );
|
return wxT( "Checks thermal reliefs for a sufficient number of connecting spokes" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void testZoneLayer( ZONE* aZone, PCB_LAYER_ID aLayer );
|
||||||
};
|
};
|
||||||
|
|
||||||
bool DRC_TEST_PROVIDER_ZONE_CONNECTIONS::Run()
|
|
||||||
{
|
|
||||||
const int delta = 5; // This is the number of tests between 2 calls to the progress bar
|
|
||||||
int ii = 0;
|
|
||||||
|
|
||||||
|
void DRC_TEST_PROVIDER_ZONE_CONNECTIONS::testZoneLayer( ZONE* aZone, PCB_LAYER_ID aLayer )
|
||||||
|
{
|
||||||
|
BOARD* board = m_drcEngine->GetBoard();
|
||||||
|
BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
|
||||||
|
std::shared_ptr<CONNECTIVITY_DATA> connectivity = board->GetConnectivity();
|
||||||
|
DRC_CONSTRAINT constraint;
|
||||||
|
|
||||||
|
const std::shared_ptr<SHAPE_POLY_SET>& zoneFill = aZone->GetFilledPolysList( aLayer );
|
||||||
|
|
||||||
|
for( FOOTPRINT* footprint : board->Footprints() )
|
||||||
|
{
|
||||||
|
for( PAD* pad : footprint->Pads() )
|
||||||
|
{
|
||||||
|
if( m_drcEngine->IsErrorLimitExceeded( DRCE_STARVED_THERMAL ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
if( m_drcEngine->IsCancelled() )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Quick tests for "connected":
|
||||||
|
//
|
||||||
|
if( !pad->FlashLayer( aLayer ) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( pad->GetNetCode() != aZone->GetNetCode() || pad->GetNetCode() <= 0 )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
EDA_RECT item_boundingbox = pad->GetBoundingBox();
|
||||||
|
|
||||||
|
if( !item_boundingbox.Intersects( aZone->GetCachedBoundingBox() ) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// If those passed, do a thorough test:
|
||||||
|
//
|
||||||
|
constraint = bds.m_DRCEngine->EvalZoneConnection( pad, aZone, aLayer );
|
||||||
|
ZONE_CONNECTION conn = constraint.m_ZoneConnection;
|
||||||
|
|
||||||
|
if( conn != ZONE_CONNECTION::THERMAL )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
constraint = bds.m_DRCEngine->EvalRules( MIN_RESOLVED_SPOKES_CONSTRAINT, pad, aZone,
|
||||||
|
aLayer );
|
||||||
|
int minCount = constraint.m_Value.Min();
|
||||||
|
|
||||||
|
if( constraint.GetSeverity() == RPT_SEVERITY_IGNORE || minCount <= 0 )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
SHAPE_POLY_SET padPoly;
|
||||||
|
pad->TransformShapeWithClearanceToPolygon( padPoly, aLayer, 0, ARC_LOW_DEF,
|
||||||
|
ERROR_OUTSIDE );
|
||||||
|
|
||||||
|
SHAPE_LINE_CHAIN& padOutline = padPoly.Outline( 0 );
|
||||||
|
std::vector<SHAPE_LINE_CHAIN::INTERSECTION> intersections;
|
||||||
|
int spokes = 0;
|
||||||
|
|
||||||
|
for( int jj = 0; jj < zoneFill->OutlineCount(); ++jj )
|
||||||
|
padOutline.Intersect( zoneFill->Outline( jj ), intersections, true );
|
||||||
|
|
||||||
|
spokes += intersections.size() / 2;
|
||||||
|
|
||||||
|
if( spokes <= 0 )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Now we know we're connected, so see if there are any other manual spokes
|
||||||
|
// added:
|
||||||
|
//
|
||||||
|
for( PCB_TRACK* track : connectivity->GetConnectedTracks( pad ) )
|
||||||
|
{
|
||||||
|
if( padOutline.PointInside( track->GetStart() ) )
|
||||||
|
{
|
||||||
|
if( aZone->GetFilledPolysList( aLayer )->Collide( track->GetEnd() ) )
|
||||||
|
spokes++;
|
||||||
|
}
|
||||||
|
else if( padOutline.PointInside( track->GetEnd() ) )
|
||||||
|
{
|
||||||
|
if( aZone->GetFilledPolysList( aLayer )->Collide( track->GetStart() ) )
|
||||||
|
spokes++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// And finally report it if there aren't enough:
|
||||||
|
//
|
||||||
|
if( spokes < minCount )
|
||||||
|
{
|
||||||
|
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_STARVED_THERMAL );
|
||||||
|
wxString msg;
|
||||||
|
|
||||||
|
msg.Printf( _( "(%s min spoke count %d; actual %d)" ),
|
||||||
|
constraint.GetName(),
|
||||||
|
minCount,
|
||||||
|
spokes );
|
||||||
|
|
||||||
|
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
|
||||||
|
drce->SetItems( aZone, pad );
|
||||||
|
drce->SetViolatingRule( constraint.GetParentRule() );
|
||||||
|
|
||||||
|
reportViolation( drce, pad->GetPosition(), UNDEFINED_LAYER );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool DRC_TEST_PROVIDER_ZONE_CONNECTIONS::Run()
|
||||||
|
{
|
||||||
BOARD* board = m_drcEngine->GetBoard();
|
BOARD* board = m_drcEngine->GetBoard();
|
||||||
BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
|
BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
|
||||||
std::shared_ptr<CONNECTIVITY_DATA> connectivity = board->GetConnectivity();
|
std::shared_ptr<CONNECTIVITY_DATA> connectivity = board->GetConnectivity();
|
||||||
DRC_CONSTRAINT constraint;
|
DRC_CONSTRAINT constraint;
|
||||||
std::vector<ZONE*> zones;
|
|
||||||
|
|
||||||
if( !reportPhase( _( "Checking thermal reliefs..." ) ) )
|
if( !reportPhase( _( "Checking thermal reliefs..." ) ) )
|
||||||
return false; // DRC cancelled
|
return false; // DRC cancelled
|
||||||
|
|
||||||
for( ZONE* zone : board->Zones() )
|
std::vector< std::pair<ZONE*, PCB_LAYER_ID> > zoneLayers;
|
||||||
zones.push_back( zone );
|
|
||||||
|
|
||||||
for( FOOTPRINT* footprint : board->Footprints() )
|
for( ZONE* zone : board->m_DRCCopperZones )
|
||||||
{
|
{
|
||||||
for( ZONE* zone : footprint->Zones() )
|
for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
|
||||||
zones.push_back( zone );
|
zoneLayers.push_back( { zone, layer } );
|
||||||
}
|
}
|
||||||
|
|
||||||
for( ZONE* zone : zones )
|
int zoneLayerCount = zoneLayers.size();
|
||||||
|
std::atomic<size_t> next( 0 );
|
||||||
|
std::atomic<size_t> done( 1 );
|
||||||
|
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 )
|
||||||
{
|
{
|
||||||
if( !reportProgress( ii++, zones.size(), delta ) )
|
std::thread t = std::thread(
|
||||||
return false;
|
[&]( )
|
||||||
|
|
||||||
for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
|
|
||||||
{
|
|
||||||
const std::shared_ptr<SHAPE_POLY_SET>& zoneFill = zone->GetFilledPolysList( layer );
|
|
||||||
|
|
||||||
for( FOOTPRINT* footprint : board->Footprints() )
|
|
||||||
{
|
|
||||||
for( PAD* pad : footprint->Pads() )
|
|
||||||
{
|
{
|
||||||
if( m_drcEngine->IsErrorLimitExceeded( DRCE_STARVED_THERMAL ) )
|
for( int i = next.fetch_add( 1 ); i < zoneLayerCount; i = next.fetch_add( 1 ) )
|
||||||
return true;
|
|
||||||
|
|
||||||
if( m_drcEngine->IsCancelled() )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Quick tests for "connected":
|
|
||||||
//
|
|
||||||
if( !pad->FlashLayer( layer ) )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if( pad->GetNetCode() != zone->GetNetCode() || pad->GetNetCode() <= 0 )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
EDA_RECT item_boundingbox = pad->GetBoundingBox();
|
|
||||||
|
|
||||||
if( !item_boundingbox.Intersects( zone->GetCachedBoundingBox() ) )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// If those passed, do a thorough test:
|
|
||||||
//
|
|
||||||
constraint = bds.m_DRCEngine->EvalZoneConnection( pad, zone, layer );
|
|
||||||
ZONE_CONNECTION conn = constraint.m_ZoneConnection;
|
|
||||||
|
|
||||||
if( conn != ZONE_CONNECTION::THERMAL )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
constraint = bds.m_DRCEngine->EvalRules( MIN_RESOLVED_SPOKES_CONSTRAINT,
|
|
||||||
pad, zone, layer );
|
|
||||||
int minCount = constraint.m_Value.Min();
|
|
||||||
|
|
||||||
if( constraint.GetSeverity() == RPT_SEVERITY_IGNORE || minCount <= 0 )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
SHAPE_POLY_SET padPoly;
|
|
||||||
pad->TransformShapeWithClearanceToPolygon( padPoly, layer, 0, ARC_LOW_DEF,
|
|
||||||
ERROR_OUTSIDE );
|
|
||||||
|
|
||||||
SHAPE_LINE_CHAIN& padOutline = padPoly.Outline( 0 );
|
|
||||||
std::vector<SHAPE_LINE_CHAIN::INTERSECTION> intersections;
|
|
||||||
int spokes = 0;
|
|
||||||
|
|
||||||
for( int jj = 0; jj < zoneFill->OutlineCount(); ++jj )
|
|
||||||
padOutline.Intersect( zoneFill->Outline( jj ), intersections, true );
|
|
||||||
|
|
||||||
spokes += intersections.size() / 2;
|
|
||||||
|
|
||||||
if( spokes <= 0 )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Now we know we're connected, so see if there are any other manual spokes
|
|
||||||
// added:
|
|
||||||
//
|
|
||||||
for( PCB_TRACK* track : connectivity->GetConnectedTracks( pad ) )
|
|
||||||
{
|
{
|
||||||
if( padOutline.PointInside( track->GetStart() ) )
|
testZoneLayer( zoneLayers[i].first, zoneLayers[i].second );
|
||||||
{
|
done.fetch_add( 1 );
|
||||||
if( zone->GetFilledPolysList( layer )->Collide( track->GetEnd() ) )
|
|
||||||
spokes++;
|
if( m_drcEngine->IsCancelled() )
|
||||||
}
|
break;
|
||||||
else if( padOutline.PointInside( track->GetEnd() ) )
|
|
||||||
{
|
|
||||||
if( zone->GetFilledPolysList( layer )->Collide( track->GetStart() ) )
|
|
||||||
spokes++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// And finally report it if there aren't enough:
|
threads_finished.fetch_add( 1 );
|
||||||
//
|
} );
|
||||||
if( spokes < minCount )
|
|
||||||
{
|
|
||||||
std::shared_ptr<DRC_ITEM> drce = DRC_ITEM::Create( DRCE_STARVED_THERMAL );
|
|
||||||
wxString msg;
|
|
||||||
|
|
||||||
msg.Printf( _( "(%s min spoke count %d; actual %d)" ),
|
t.detach();
|
||||||
constraint.GetName(),
|
}
|
||||||
minCount,
|
|
||||||
spokes );
|
|
||||||
|
|
||||||
drce->SetErrorMessage( drce->GetErrorText() + wxS( " " ) + msg );
|
while( threads_finished < parallelThreadCount )
|
||||||
drce->SetItems( zone, pad );
|
{
|
||||||
drce->SetViolatingRule( constraint.GetParentRule() );
|
m_drcEngine->ReportProgress( (double) done / (double) zoneLayerCount );
|
||||||
|
std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) );
|
||||||
reportViolation( drce, pad->GetPosition(), UNDEFINED_LAYER );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return !m_drcEngine->IsCancelled();
|
return !m_drcEngine->IsCancelled();
|
||||||
|
|
|
@ -2343,13 +2343,17 @@ void FOOTPRINT::CheckOverlappingPads( const std::function<void( const PAD*, cons
|
||||||
{
|
{
|
||||||
checkedPairs[ { a, b } ] = 1;
|
checkedPairs[ { a, b } ] = 1;
|
||||||
|
|
||||||
VECTOR2I pos;
|
if( pad->GetBoundingBox().Intersects( other->GetBoundingBox() ) )
|
||||||
SHAPE* padShape = pad->GetEffectiveShape().get();
|
|
||||||
SHAPE* otherShape = other->GetEffectiveShape().get();
|
|
||||||
|
|
||||||
if( padShape->Collide( otherShape, 0, nullptr, &pos ) )
|
|
||||||
{
|
{
|
||||||
(aErrorHandler)( pad, other, pos );
|
|
||||||
|
VECTOR2I pos;
|
||||||
|
SHAPE* padShape = pad->GetEffectiveShape().get();
|
||||||
|
SHAPE* otherShape = other->GetEffectiveShape().get();
|
||||||
|
|
||||||
|
if( padShape->Collide( otherShape, 0, nullptr, &pos ) )
|
||||||
|
{
|
||||||
|
(aErrorHandler)( pad, other, pos );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1284,18 +1284,10 @@ double FP_ZONE::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const
|
||||||
|
|
||||||
std::shared_ptr<SHAPE> ZONE::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
|
std::shared_ptr<SHAPE> ZONE::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
|
||||||
{
|
{
|
||||||
std::shared_ptr<SHAPE> shape;
|
|
||||||
|
|
||||||
if( m_FilledPolysList.find( aLayer ) == m_FilledPolysList.end() )
|
if( m_FilledPolysList.find( aLayer ) == m_FilledPolysList.end() )
|
||||||
{
|
return std::make_shared<SHAPE_NULL>();
|
||||||
shape = std::make_shared<SHAPE_NULL>();
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
return m_FilledPolysList.at( aLayer );
|
||||||
shape.reset( m_FilledPolysList.at( aLayer )->Clone() );
|
|
||||||
}
|
|
||||||
|
|
||||||
return shape;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue