Keep track of single-pad-islands so we can discount spokes to them.
This commit is contained in:
parent
4e27e91b2f
commit
d6dd58fff9
|
@ -263,6 +263,7 @@ void BOARD::IncrementTimeStamp()
|
||||||
m_DRCMaxPhysicalClearance = 0;
|
m_DRCMaxPhysicalClearance = 0;
|
||||||
m_DRCZones.clear();
|
m_DRCZones.clear();
|
||||||
m_DRCCopperZones.clear();
|
m_DRCCopperZones.clear();
|
||||||
|
m_ZoneIsolatedIslandsMap.clear();
|
||||||
m_CopperZoneRTreeCache.clear();
|
m_CopperZoneRTreeCache.clear();
|
||||||
m_CopperItemRTreeCache = std::make_unique<DRC_RTREE>();
|
m_CopperItemRTreeCache = std::make_unique<DRC_RTREE>();
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,6 +60,7 @@ class CONNECTIVITY_DATA;
|
||||||
class COMPONENT;
|
class COMPONENT;
|
||||||
class PROJECT;
|
class PROJECT;
|
||||||
class PROGRESS_REPORTER;
|
class PROGRESS_REPORTER;
|
||||||
|
struct ISOLATED_ISLANDS;
|
||||||
|
|
||||||
// The default value for m_outlinesChainingEpsilon to convert a board outlines to polygons
|
// The default value for m_outlinesChainingEpsilon to convert a board outlines to polygons
|
||||||
// It is the max dist between 2 end points to see them connected
|
// It is the max dist between 2 end points to see them connected
|
||||||
|
@ -1177,6 +1178,7 @@ public:
|
||||||
int m_DRCMaxClearance;
|
int m_DRCMaxClearance;
|
||||||
int m_DRCMaxPhysicalClearance;
|
int m_DRCMaxPhysicalClearance;
|
||||||
ZONE* m_SolderMask;
|
ZONE* m_SolderMask;
|
||||||
|
std::map<ZONE*, std::map<PCB_LAYER_ID, ISOLATED_ISLANDS>> m_ZoneIsolatedIslandsMap;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// The default copy constructor & operator= are inadequate,
|
// The default copy constructor & operator= are inadequate,
|
||||||
|
|
|
@ -613,53 +613,26 @@ void CN_CONNECTIVITY_ALGO::PropagateNets( BOARD_COMMIT* aCommit )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( ZONE* aZone, PCB_LAYER_ID aLayer,
|
void CN_CONNECTIVITY_ALGO::FillIsolatedIslandsMap(
|
||||||
std::vector<int>& aIslands )
|
std::map<ZONE*, std::map<PCB_LAYER_ID, ISOLATED_ISLANDS>>& aMap,
|
||||||
{
|
bool aConnectivityAlreadyRebuilt )
|
||||||
if( aZone->GetFilledPolysList( aLayer )->IsEmpty() )
|
|
||||||
return;
|
|
||||||
|
|
||||||
aIslands.clear();
|
|
||||||
|
|
||||||
Remove( aZone );
|
|
||||||
Add( aZone );
|
|
||||||
|
|
||||||
m_connClusters = SearchClusters( CSM_CONNECTIVITY_CHECK );
|
|
||||||
|
|
||||||
for( const std::shared_ptr<CN_CLUSTER>& cluster : m_connClusters )
|
|
||||||
{
|
|
||||||
if( cluster->Contains( aZone ) && cluster->IsOrphaned() )
|
|
||||||
{
|
|
||||||
for( CN_ITEM* z : *cluster )
|
|
||||||
{
|
|
||||||
if( z->Parent() == aZone && z->Layer() == aLayer )
|
|
||||||
aIslands.push_back( static_cast<CN_ZONE_LAYER*>(z)->SubpolyIndex() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
bool aConnectivityAlreadyRebuilt )
|
|
||||||
{
|
{
|
||||||
int progressDelta = 50;
|
int progressDelta = 50;
|
||||||
int ii = 0;
|
int ii = 0;
|
||||||
|
|
||||||
progressDelta = std::max( progressDelta, (int) aZones.size() / 4 );
|
progressDelta = std::max( progressDelta, (int) aMap.size() / 4 );
|
||||||
|
|
||||||
if( !aConnectivityAlreadyRebuilt )
|
if( !aConnectivityAlreadyRebuilt )
|
||||||
{
|
{
|
||||||
for( CN_ZONE_ISOLATED_ISLAND_LIST& z : aZones )
|
for( const auto& [ zone, islands ] : aMap )
|
||||||
{
|
{
|
||||||
Remove( z.m_zone );
|
Remove( zone );
|
||||||
Add( z.m_zone );
|
Add( zone );
|
||||||
ii++;
|
ii++;
|
||||||
|
|
||||||
if( m_progressReporter && ( ii % progressDelta ) == 0 )
|
if( m_progressReporter && ( ii % progressDelta ) == 0 )
|
||||||
{
|
{
|
||||||
m_progressReporter->SetCurrentProgress( (double) ii / (double) aZones.size() );
|
m_progressReporter->SetCurrentProgress( (double) ii / (double) aMap.size() );
|
||||||
m_progressReporter->KeepRefreshing( false );
|
m_progressReporter->KeepRefreshing( false );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -670,24 +643,25 @@ void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLAT
|
||||||
|
|
||||||
m_connClusters = SearchClusters( CSM_CONNECTIVITY_CHECK );
|
m_connClusters = SearchClusters( CSM_CONNECTIVITY_CHECK );
|
||||||
|
|
||||||
for( CN_ZONE_ISOLATED_ISLAND_LIST& zone : aZones )
|
for( auto& [ zone, zoneIslands ] : aMap )
|
||||||
{
|
{
|
||||||
for( PCB_LAYER_ID layer : zone.m_zone->GetLayerSet().Seq() )
|
for( auto& [ layer, layerIslands ] : zoneIslands )
|
||||||
{
|
{
|
||||||
if( zone.m_zone->GetFilledPolysList( layer )->IsEmpty() )
|
if( zone->GetFilledPolysList( layer )->IsEmpty() )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for( const std::shared_ptr<CN_CLUSTER>& cluster : m_connClusters )
|
for( const std::shared_ptr<CN_CLUSTER>& cluster : m_connClusters )
|
||||||
{
|
{
|
||||||
if( cluster->Contains( zone.m_zone ) && cluster->IsOrphaned() )
|
for( CN_ITEM* item : *cluster )
|
||||||
{
|
{
|
||||||
for( CN_ITEM* z : *cluster )
|
if( item->Parent() == zone && item->Layer() == layer )
|
||||||
{
|
{
|
||||||
if( z->Parent() == zone.m_zone && z->Layer() == layer )
|
CN_ZONE_LAYER* z = static_cast<CN_ZONE_LAYER*>( item );
|
||||||
{
|
|
||||||
zone.m_islands[layer].push_back(
|
if( cluster->IsOrphaned() )
|
||||||
static_cast<CN_ZONE_LAYER*>( z )->SubpolyIndex() );
|
layerIslands.m_IsolatedOutlines.push_back( z->SubpolyIndex() );
|
||||||
}
|
else if( z->HasSingleConnection() )
|
||||||
|
layerIslands.m_SingleConnectionOutlines.push_back( z->SubpolyIndex() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,18 +221,11 @@ public:
|
||||||
*/
|
*/
|
||||||
void PropagateNets( BOARD_COMMIT* aCommit = nullptr );
|
void PropagateNets( BOARD_COMMIT* aCommit = nullptr );
|
||||||
|
|
||||||
void FindIsolatedCopperIslands( ZONE* aZone, PCB_LAYER_ID aLayer, std::vector<int>& aIslands );
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the copper islands that are not connected to a net.
|
* Fill in the isolated islands map with copper islands that are not connected to a net.
|
||||||
*
|
|
||||||
* These are added to the m_islands vector.
|
|
||||||
* N.B. This must be called after aZones has been refreshed.
|
|
||||||
*
|
|
||||||
* @param: aZones is the set of zones to search for islands.
|
|
||||||
*/
|
*/
|
||||||
void FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones,
|
void FillIsolatedIslandsMap( std::map<ZONE*, std::map<PCB_LAYER_ID, ISOLATED_ISLANDS>>& aMap,
|
||||||
bool aConnectivityAlreadyRebuilt );
|
bool aConnectivityAlreadyRebuilt );
|
||||||
|
|
||||||
const CLUSTERS& GetClusters();
|
const CLUSTERS& GetClusters();
|
||||||
|
|
||||||
|
|
|
@ -309,18 +309,10 @@ int CONNECTIVITY_DATA::GetNetCount() const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CONNECTIVITY_DATA::FindIsolatedCopperIslands( ZONE* aZone, std::vector<int>& aIslands )
|
void CONNECTIVITY_DATA::FillIsolatedIslandsMap( std::map<ZONE*, std::map<PCB_LAYER_ID, ISOLATED_ISLANDS>>& aMap,
|
||||||
|
bool aConnectivityAlreadyRebuilt )
|
||||||
{
|
{
|
||||||
// TODO(JE) ZONES
|
m_connAlgo->FillIsolatedIslandsMap( aMap, aConnectivityAlreadyRebuilt );
|
||||||
#if 0
|
|
||||||
m_connAlgo->FindIsolatedCopperIslands( aZone, aIslands );
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void CONNECTIVITY_DATA::FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones,
|
|
||||||
bool aConnectivityAlreadyRebuilt )
|
|
||||||
{
|
|
||||||
m_connAlgo->FindIsolatedCopperIslands( aZones, aConnectivityAlreadyRebuilt );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -64,21 +64,6 @@ struct CN_DISJOINT_NET_ENTRY
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A structure used for calculating isolated islands on a given zone across all its layers
|
|
||||||
*/
|
|
||||||
struct CN_ZONE_ISOLATED_ISLAND_LIST
|
|
||||||
{
|
|
||||||
CN_ZONE_ISOLATED_ISLAND_LIST( ZONE* aZone ) :
|
|
||||||
m_zone( aZone )
|
|
||||||
{}
|
|
||||||
|
|
||||||
ZONE* m_zone;
|
|
||||||
|
|
||||||
std::map<PCB_LAYER_ID, std::vector<int>> m_islands;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct RN_DYNAMIC_LINE
|
struct RN_DYNAMIC_LINE
|
||||||
{
|
{
|
||||||
int netCode;
|
int netCode;
|
||||||
|
@ -176,14 +161,11 @@ public:
|
||||||
void PropagateNets( BOARD_COMMIT* aCommit = nullptr );
|
void PropagateNets( BOARD_COMMIT* aCommit = nullptr );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function FindIsolatedCopperIslands()
|
* Fill the isolate islands list for each layer of each zone. Isolated islands are individual
|
||||||
* Searches for copper islands in zone aZone that are not connected to any pad.
|
* polygons in a zone fill that don't connect to a net.
|
||||||
* @param aZone zone to test
|
|
||||||
* @param aIslands list of islands that have no connections (outline indices in the polygon set)
|
|
||||||
*/
|
*/
|
||||||
void FindIsolatedCopperIslands( ZONE* aZone, std::vector<int>& aIslands );
|
void FillIsolatedIslandsMap( std::map<ZONE*, std::map<PCB_LAYER_ID, ISOLATED_ISLANDS>>& aMap,
|
||||||
void FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones,
|
bool aConnectivityAlreadyRebuilt = false );
|
||||||
bool aConnectivityAlreadyRebuilt = false );
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function RecalculateRatsnest()
|
* Function RecalculateRatsnest()
|
||||||
|
|
|
@ -109,6 +109,23 @@ const VECTOR2I CN_ZONE_LAYER::GetAnchor( int n ) const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool CN_ZONE_LAYER::HasSingleConnection()
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for( CN_ITEM* item : ConnectedItems() )
|
||||||
|
{
|
||||||
|
if( item->Valid() )
|
||||||
|
count++;
|
||||||
|
|
||||||
|
if( count > 1 )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void CN_ITEM::RemoveInvalidRefs()
|
void CN_ITEM::RemoveInvalidRefs()
|
||||||
{
|
{
|
||||||
for( auto it = m_connected.begin(); it != m_connected.end(); /* increment in loop */ )
|
for( auto it = m_connected.begin(); it != m_connected.end(); /* increment in loop */ )
|
||||||
|
|
|
@ -387,6 +387,8 @@ public:
|
||||||
return collision;
|
return collision;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HasSingleConnection();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int m_subpolyIndex;
|
int m_subpolyIndex;
|
||||||
PCB_LAYER_ID m_layer;
|
PCB_LAYER_ID m_layer;
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
#include <footprint.h>
|
#include <footprint.h>
|
||||||
#include <thread_pool.h>
|
#include <thread_pool.h>
|
||||||
#include <zone.h>
|
#include <zone.h>
|
||||||
|
#include <connectivity/connectivity_data.h>
|
||||||
#include <drc/drc_engine.h>
|
#include <drc/drc_engine.h>
|
||||||
#include <drc/drc_rtree.h>
|
#include <drc/drc_rtree.h>
|
||||||
#include <drc/drc_cache_generator.h>
|
#include <drc/drc_cache_generator.h>
|
||||||
|
@ -201,6 +201,23 @@ bool DRC_CACHE_GENERATOR::Run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_board->m_ZoneIsolatedIslandsMap.clear();
|
||||||
|
|
||||||
|
for( ZONE* zone : m_board->Zones() )
|
||||||
|
{
|
||||||
|
if( !zone->GetIsRuleArea() )
|
||||||
|
{
|
||||||
|
for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
|
||||||
|
m_board->m_ZoneIsolatedIslandsMap[ zone ][ layer ] = ISOLATED_ISLANDS();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
|
||||||
|
|
||||||
|
connectivity->ClearRatsnest();
|
||||||
|
connectivity->Build( m_board, m_drcEngine->GetProgressReporter() );
|
||||||
|
connectivity->FillIsolatedIslandsMap( m_board->m_ZoneIsolatedIslandsMap, true );
|
||||||
|
|
||||||
return !m_drcEngine->IsCancelled();
|
return !m_drcEngine->IsCancelled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
|
|
||||||
#include <connectivity/connectivity_data.h>
|
#include <connectivity/connectivity_data.h>
|
||||||
#include <connectivity/connectivity_algo.h>
|
#include <connectivity/connectivity_algo.h>
|
||||||
|
#include <zone.h>
|
||||||
#include <drc/drc_engine.h>
|
#include <drc/drc_engine.h>
|
||||||
#include <drc/drc_item.h>
|
#include <drc/drc_item.h>
|
||||||
#include <drc/drc_rule.h>
|
#include <drc/drc_rule.h>
|
||||||
|
@ -71,25 +71,12 @@ bool DRC_TEST_PROVIDER_CONNECTIVITY::Run()
|
||||||
if( !reportPhase( _( "Checking pad, via and zone connections..." ) ) )
|
if( !reportPhase( _( "Checking pad, via and zone connections..." ) ) )
|
||||||
return false; // DRC cancelled
|
return false; // DRC cancelled
|
||||||
|
|
||||||
BOARD* board = m_drcEngine->GetBoard();
|
BOARD* board = m_drcEngine->GetBoard();
|
||||||
|
std::shared_ptr<CONNECTIVITY_DATA> connectivity = board->GetConnectivity();
|
||||||
std::shared_ptr<CONNECTIVITY_DATA> connectivity = board->GetConnectivity();
|
|
||||||
std::vector<CN_ZONE_ISOLATED_ISLAND_LIST> islandsList;
|
|
||||||
|
|
||||||
for( ZONE* zone : board->Zones() )
|
|
||||||
{
|
|
||||||
if( !zone->GetIsRuleArea() )
|
|
||||||
islandsList.emplace_back( CN_ZONE_ISOLATED_ISLAND_LIST( zone ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rebuild (from scratch, ignoring dirty flags) just in case. This really needs to be reliable.
|
|
||||||
connectivity->ClearRatsnest();
|
|
||||||
connectivity->Build( board, m_drcEngine->GetProgressReporter() );
|
|
||||||
connectivity->FindIsolatedCopperIslands( islandsList, true );
|
|
||||||
|
|
||||||
int progressDelta = 250;
|
int progressDelta = 250;
|
||||||
int ii = 0;
|
int ii = 0;
|
||||||
int count = board->Tracks().size() + islandsList.size();
|
int count = board->Tracks().size() + board->m_ZoneIsolatedIslandsMap.size();
|
||||||
|
|
||||||
ii += count; // We gave half of this phase to CONNECTIVITY_DATA::Build()
|
ii += count; // We gave half of this phase to CONNECTIVITY_DATA::Build()
|
||||||
count += count;
|
count += count;
|
||||||
|
@ -122,7 +109,7 @@ bool DRC_TEST_PROVIDER_CONNECTIVITY::Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
/* test starved zones */
|
/* test starved zones */
|
||||||
for( CN_ZONE_ISOLATED_ISLAND_LIST& zone : islandsList )
|
for( const auto& [ zone, zoneIslands ] : board->m_ZoneIsolatedIslandsMap )
|
||||||
{
|
{
|
||||||
if( m_drcEngine->IsErrorLimitExceeded( DRCE_ISOLATED_COPPER ) )
|
if( m_drcEngine->IsErrorLimitExceeded( DRCE_ISOLATED_COPPER ) )
|
||||||
break;
|
break;
|
||||||
|
@ -130,21 +117,18 @@ bool DRC_TEST_PROVIDER_CONNECTIVITY::Run()
|
||||||
if( !reportProgress( ii++, count, progressDelta ) )
|
if( !reportProgress( ii++, count, progressDelta ) )
|
||||||
return false; // DRC cancelled
|
return false; // DRC cancelled
|
||||||
|
|
||||||
for( PCB_LAYER_ID layer : zone.m_zone->GetLayerSet().Seq() )
|
for( const auto& [ layer, layerIslands ] : zoneIslands )
|
||||||
{
|
{
|
||||||
if( !zone.m_islands.count( layer ) )
|
for( int polyIdx : layerIslands.m_IsolatedOutlines )
|
||||||
continue;
|
|
||||||
|
|
||||||
std::shared_ptr<SHAPE_POLY_SET> poly = zone.m_zone->GetFilledPolysList( layer );
|
|
||||||
|
|
||||||
for( int idx : zone.m_islands.at( layer ) )
|
|
||||||
{
|
{
|
||||||
if( m_drcEngine->IsErrorLimitExceeded( DRCE_ISOLATED_COPPER ) )
|
if( m_drcEngine->IsErrorLimitExceeded( DRCE_ISOLATED_COPPER ) )
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
std::shared_ptr<SHAPE_POLY_SET> poly = zone->GetFilledPolysList( layer );
|
||||||
|
|
||||||
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_ISOLATED_COPPER );
|
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_ISOLATED_COPPER );
|
||||||
drcItem->SetItems( zone.m_zone );
|
drcItem->SetItems( zone );
|
||||||
reportViolation( drcItem, poly->Outline( idx ).CPoint( 0 ), layer );
|
reportViolation( drcItem, poly->Outline( polyIdx ).CPoint( 0 ), layer );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,6 +81,17 @@ void DRC_TEST_PROVIDER_ZONE_CONNECTIONS::testZoneLayer( ZONE* aZone, PCB_LAYER_I
|
||||||
DRC_CONSTRAINT constraint;
|
DRC_CONSTRAINT constraint;
|
||||||
|
|
||||||
const std::shared_ptr<SHAPE_POLY_SET>& zoneFill = aZone->GetFilledPolysList( aLayer );
|
const std::shared_ptr<SHAPE_POLY_SET>& zoneFill = aZone->GetFilledPolysList( aLayer );
|
||||||
|
ISOLATED_ISLANDS isolatedIslands;
|
||||||
|
|
||||||
|
auto zoneIter = board->m_ZoneIsolatedIslandsMap.find( aZone );
|
||||||
|
|
||||||
|
if( zoneIter != board->m_ZoneIsolatedIslandsMap.end() )
|
||||||
|
{
|
||||||
|
auto layerIter = zoneIter->second.find( aLayer );
|
||||||
|
|
||||||
|
if( layerIter != zoneIter->second.end() )
|
||||||
|
isolatedIslands = layerIter->second;
|
||||||
|
}
|
||||||
|
|
||||||
for( FOOTPRINT* footprint : board->Footprints() )
|
for( FOOTPRINT* footprint : board->Footprints() )
|
||||||
{
|
{
|
||||||
|
@ -132,7 +143,15 @@ void DRC_TEST_PROVIDER_ZONE_CONNECTIONS::testZoneLayer( ZONE* aZone, PCB_LAYER_I
|
||||||
std::vector<SHAPE_LINE_CHAIN::INTERSECTION> intersections;
|
std::vector<SHAPE_LINE_CHAIN::INTERSECTION> intersections;
|
||||||
|
|
||||||
for( int jj = 0; jj < zoneFill->OutlineCount(); ++jj )
|
for( int jj = 0; jj < zoneFill->OutlineCount(); ++jj )
|
||||||
|
{
|
||||||
|
// If we connect to an island that only connects to a single item then we *are*
|
||||||
|
// that item. Thermal spokes to this (otherwise isolated) island don't provide
|
||||||
|
// electrical connectivity to anything, so we don't count them.
|
||||||
|
if( alg::contains( isolatedIslands.m_SingleConnectionOutlines, jj ) )
|
||||||
|
continue;
|
||||||
|
|
||||||
zoneFill->Outline( jj ).Intersect( padOutline, intersections, true, &padBBox );
|
zoneFill->Outline( jj ).Intersect( padOutline, intersections, true, &padBBox );
|
||||||
|
}
|
||||||
|
|
||||||
int spokes = intersections.size() / 2;
|
int spokes = intersections.size() / 2;
|
||||||
|
|
||||||
|
@ -198,10 +217,13 @@ bool DRC_TEST_PROVIDER_ZONE_CONNECTIONS::Run()
|
||||||
|
|
||||||
for( ZONE* zone : board->m_DRCCopperZones )
|
for( ZONE* zone : board->m_DRCCopperZones )
|
||||||
{
|
{
|
||||||
for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
|
if( !zone->IsTeardropArea() )
|
||||||
{
|
{
|
||||||
zoneLayers.push_back( { zone, layer } );
|
for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
|
||||||
total_effort += zone->GetFilledPolysList( layer )->FullPointCount();
|
{
|
||||||
|
zoneLayers.push_back( { zone, layer } );
|
||||||
|
total_effort += zone->GetFilledPolysList( layer )->FullPointCount();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,24 @@ class ZONE;
|
||||||
class MSG_PANEL_ITEM;
|
class MSG_PANEL_ITEM;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A struct recording the isolated and single-pad islands within a zone. Each array holds
|
||||||
|
* indexes into the outlines of a SHAPE_POLY_SET for a zone fill on a particular layer.
|
||||||
|
*
|
||||||
|
* Isolated outlines are those whose *connectivity cluster* contains no pads. These generate
|
||||||
|
* DRC violations.
|
||||||
|
*
|
||||||
|
* Single-connection outlines are those with a *direct* connection to only a single item. These
|
||||||
|
* participate in thermal spoke counting as a pad spoke to an *otherwise* unconnected island
|
||||||
|
* provides no connectivity to the pad.
|
||||||
|
*/
|
||||||
|
struct ISOLATED_ISLANDS
|
||||||
|
{
|
||||||
|
std::vector<int> m_IsolatedOutlines;
|
||||||
|
std::vector<int> m_SingleConnectionOutlines;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle a list of polygons defining a copper zone.
|
* Handle a list of polygons defining a copper zone.
|
||||||
*
|
*
|
||||||
|
|
|
@ -90,9 +90,9 @@ bool ZONE_FILLER::Fill( std::vector<ZONE*>& aZones, bool aCheck, wxWindow* aPare
|
||||||
{
|
{
|
||||||
std::lock_guard<KISPINLOCK> lock( m_board->GetConnectivity()->GetLock() );
|
std::lock_guard<KISPINLOCK> lock( m_board->GetConnectivity()->GetLock() );
|
||||||
|
|
||||||
std::vector<std::pair<ZONE*, PCB_LAYER_ID>> toFill;
|
std::vector<std::pair<ZONE*, PCB_LAYER_ID>> toFill;
|
||||||
std::map<std::pair<ZONE*, PCB_LAYER_ID>, MD5_HASH> oldFillHashes;
|
std::map<std::pair<ZONE*, PCB_LAYER_ID>, MD5_HASH> oldFillHashes;
|
||||||
std::vector<CN_ZONE_ISOLATED_ISLAND_LIST> islandsList;
|
std::map<ZONE*, std::map<PCB_LAYER_ID, ISOLATED_ISLANDS>> isolatedIslandsMap;
|
||||||
|
|
||||||
std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
|
std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
|
||||||
|
|
||||||
|
@ -328,9 +328,9 @@ bool ZONE_FILLER::Fill( std::vector<ZONE*>& aZones, bool aCheck, wxWindow* aPare
|
||||||
|
|
||||||
// Add the zone to the list of zones to test or refill
|
// Add the zone to the list of zones to test or refill
|
||||||
toFill.emplace_back( std::make_pair( zone, layer ) );
|
toFill.emplace_back( std::make_pair( zone, layer ) );
|
||||||
}
|
|
||||||
|
|
||||||
islandsList.emplace_back( CN_ZONE_ISOLATED_ISLAND_LIST( zone ) );
|
isolatedIslandsMap[ zone ][ layer ] = ISOLATED_ISLANDS();
|
||||||
|
}
|
||||||
|
|
||||||
// Remove existing fill first to prevent drawing invalid polygons on some platforms
|
// Remove existing fill first to prevent drawing invalid polygons on some platforms
|
||||||
zone->UnFill();
|
zone->UnFill();
|
||||||
|
@ -534,7 +534,7 @@ bool ZONE_FILLER::Fill( std::vector<ZONE*>& aZones, bool aCheck, wxWindow* aPare
|
||||||
}
|
}
|
||||||
|
|
||||||
connectivity->SetProgressReporter( m_progressReporter );
|
connectivity->SetProgressReporter( m_progressReporter );
|
||||||
connectivity->FindIsolatedCopperIslands( islandsList );
|
connectivity->FillIsolatedIslandsMap( isolatedIslandsMap );
|
||||||
connectivity->SetProgressReporter( nullptr );
|
connectivity->SetProgressReporter( nullptr );
|
||||||
|
|
||||||
if( m_progressReporter && m_progressReporter->IsCancelled() )
|
if( m_progressReporter && m_progressReporter->IsCancelled() )
|
||||||
|
@ -552,26 +552,15 @@ bool ZONE_FILLER::Fill( std::vector<ZONE*>& aZones, bool aCheck, wxWindow* aPare
|
||||||
// Now remove isolated copper islands according to the isolated islands strategy assigned
|
// Now remove isolated copper islands according to the isolated islands strategy assigned
|
||||||
// by the user (always, never, below-certain-size).
|
// by the user (always, never, below-certain-size).
|
||||||
//
|
//
|
||||||
for( CN_ZONE_ISOLATED_ISLAND_LIST& zone : islandsList )
|
for( const auto& [ zone, zoneIslands ] : isolatedIslandsMap )
|
||||||
{
|
{
|
||||||
// If *all* the polygons are islands, do not remove any of them
|
// If *all* the polygons are islands, do not remove any of them
|
||||||
bool allIslands = true;
|
bool allIslands = true;
|
||||||
|
|
||||||
for( PCB_LAYER_ID layer : zone.m_zone->GetLayerSet().Seq() )
|
for( const auto& [ layer, layerIslands ] : zoneIslands )
|
||||||
{
|
{
|
||||||
std::shared_ptr<SHAPE_POLY_SET> poly = zone.m_zone->GetFilledPolysList( layer );
|
if( layerIslands.m_IsolatedOutlines.size()
|
||||||
|
!= static_cast<size_t>( zone->GetFilledPolysList( layer )->OutlineCount() ) )
|
||||||
if( !zone.m_islands.count( layer ) )
|
|
||||||
{
|
|
||||||
if( poly->OutlineCount() > 0 )
|
|
||||||
allIslands = false;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<int>& islands = zone.m_islands.at( layer );
|
|
||||||
|
|
||||||
if( islands.size() != static_cast<size_t>( poly->OutlineCount() ) )
|
|
||||||
{
|
{
|
||||||
allIslands = false;
|
allIslands = false;
|
||||||
break;
|
break;
|
||||||
|
@ -581,23 +570,23 @@ bool ZONE_FILLER::Fill( std::vector<ZONE*>& aZones, bool aCheck, wxWindow* aPare
|
||||||
if( allIslands )
|
if( allIslands )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for( PCB_LAYER_ID layer : zone.m_zone->GetLayerSet().Seq() )
|
for( const auto& [ layer, layerIslands ] : zoneIslands )
|
||||||
{
|
{
|
||||||
if( m_debugZoneFiller && LSET::InternalCuMask().Contains( layer ) )
|
if( m_debugZoneFiller && LSET::InternalCuMask().Contains( layer ) )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if( !zone.m_islands.count( layer ) )
|
if( layerIslands.m_IsolatedOutlines.empty() )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::vector<int>& islands = zone.m_islands.at( layer );
|
std::vector<int> islands = layerIslands.m_IsolatedOutlines;
|
||||||
|
|
||||||
// The list of polygons to delete must be explored from last to first in list,
|
// The list of polygons to delete must be explored from last to first in list,
|
||||||
// to allow deleting a polygon from list without breaking the remaining of the list
|
// to allow deleting a polygon from list without breaking the remaining of the list
|
||||||
std::sort( islands.begin(), islands.end(), std::greater<int>() );
|
std::sort( islands.begin(), islands.end(), std::greater<int>() );
|
||||||
|
|
||||||
std::shared_ptr<SHAPE_POLY_SET> poly = zone.m_zone->GetFilledPolysList( layer );
|
std::shared_ptr<SHAPE_POLY_SET> poly = zone->GetFilledPolysList( layer );
|
||||||
long long int minArea = zone.m_zone->GetMinIslandArea();
|
long long int minArea = zone->GetMinIslandArea();
|
||||||
ISLAND_REMOVAL_MODE mode = zone.m_zone->GetIslandRemovalMode();
|
ISLAND_REMOVAL_MODE mode = zone->GetIslandRemovalMode();
|
||||||
|
|
||||||
for( int idx : islands )
|
for( int idx : islands )
|
||||||
{
|
{
|
||||||
|
@ -608,11 +597,11 @@ bool ZONE_FILLER::Fill( std::vector<ZONE*>& aZones, bool aCheck, wxWindow* aPare
|
||||||
else if ( mode == ISLAND_REMOVAL_MODE::AREA && outline.Area( true ) < minArea )
|
else if ( mode == ISLAND_REMOVAL_MODE::AREA && outline.Area( true ) < minArea )
|
||||||
poly->DeletePolygonAndTriangulationData( idx, false );
|
poly->DeletePolygonAndTriangulationData( idx, false );
|
||||||
else
|
else
|
||||||
zone.m_zone->SetIsIsland( layer, idx );
|
zone->SetIsIsland( layer, idx );
|
||||||
}
|
}
|
||||||
|
|
||||||
poly->UpdateTriangulationDataHash();
|
poly->UpdateTriangulationDataHash();
|
||||||
zone.m_zone->CalculateFilledArea();
|
zone->CalculateFilledArea();
|
||||||
|
|
||||||
if( m_progressReporter && m_progressReporter->IsCancelled() )
|
if( m_progressReporter && m_progressReporter->IsCancelled() )
|
||||||
return false;
|
return false;
|
||||||
|
@ -646,39 +635,42 @@ bool ZONE_FILLER::Fill( std::vector<ZONE*>& aZones, bool aCheck, wxWindow* aPare
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto island_lambda = [&]( int aStart, int aEnd ) -> island_check_return
|
auto island_lambda =
|
||||||
{
|
[&]( int aStart, int aEnd ) -> island_check_return
|
||||||
island_check_return retval;
|
|
||||||
|
|
||||||
for( int ii = aStart; ii < aEnd && !cancelled; ++ii )
|
|
||||||
{
|
{
|
||||||
auto [poly, minArea] = polys_to_check[ii];
|
island_check_return retval;
|
||||||
|
|
||||||
for( int jj = poly->OutlineCount() - 1; jj >= 0; jj-- )
|
for( int ii = aStart; ii < aEnd && !cancelled; ++ii )
|
||||||
{
|
{
|
||||||
SHAPE_POLY_SET island;
|
auto [poly, minArea] = polys_to_check[ii];
|
||||||
SHAPE_POLY_SET intersection;
|
|
||||||
const SHAPE_LINE_CHAIN& test_poly = poly->Polygon( jj ).front();
|
|
||||||
double island_area = test_poly.Area();
|
|
||||||
|
|
||||||
if( island_area < minArea )
|
for( int jj = poly->OutlineCount() - 1; jj >= 0; jj-- )
|
||||||
continue;
|
{
|
||||||
|
SHAPE_POLY_SET island;
|
||||||
|
SHAPE_POLY_SET intersection;
|
||||||
|
const SHAPE_LINE_CHAIN& test_poly = poly->Polygon( jj ).front();
|
||||||
|
double island_area = test_poly.Area();
|
||||||
|
|
||||||
|
if( island_area < minArea )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
|
||||||
island.AddOutline( test_poly );
|
island.AddOutline( test_poly );
|
||||||
intersection.BooleanIntersection( m_boardOutline, island, SHAPE_POLY_SET::POLYGON_MODE::PM_FAST );
|
intersection.BooleanIntersection( m_boardOutline, island,
|
||||||
|
SHAPE_POLY_SET::POLYGON_MODE::PM_FAST );
|
||||||
|
|
||||||
// Nominally, all of these areas should be either inside or outside the board outline. So this test
|
// Nominally, all of these areas should be either inside or outside the
|
||||||
// should be able to just compare areas (if they are equal, you are inside). But in practice,
|
// board outline. So this test should be able to just compare areas (if
|
||||||
// we sometimes can have slight overlap at the edges. So testing against half-size area is
|
// they are equal, you are inside). But in practice, we sometimes have
|
||||||
// a fail-safe
|
// slight overlap at the edges, so testing against half-size area acts as
|
||||||
if( intersection.Area() < island_area / 2.0 )
|
// a fail-safe.
|
||||||
retval.emplace_back( poly, jj );
|
if( intersection.Area() < island_area / 2.0 )
|
||||||
|
retval.emplace_back( poly, jj );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto island_returns = tp.parallelize_loop( 0, polys_to_check.size(), island_lambda );
|
auto island_returns = tp.parallelize_loop( 0, polys_to_check.size(), island_lambda );
|
||||||
cancelled = false;
|
cancelled = false;
|
||||||
|
@ -1575,10 +1567,14 @@ bool ZONE_FILLER::fillCopperZone( const ZONE* aZone, PCB_LAYER_ID aLayer, PCB_LA
|
||||||
DUMP_POLYS_TO_COPPER_LAYER( aFillPolys, In15_Cu, wxT( "after-reinflating" ) );
|
DUMP_POLYS_TO_COPPER_LAYER( aFillPolys, In15_Cu, wxT( "after-reinflating" ) );
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------------------
|
/* -------------------------------------------------------------------------------------
|
||||||
* Ensure additive changes (thermal stubs and particularly inflating acute corners) do not
|
* Ensure additive changes (thermal stubs and inflating acute corners) do not add copper
|
||||||
* add copper outside the zone boundary or inside the clearance holes
|
* outside the zone boundary, inside the clearance holes, or between otherwise isolated
|
||||||
|
* islands
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
for( PAD* pad : thermalConnectionPads )
|
||||||
|
addHoleKnockout( pad, 0, clearanceHoles );
|
||||||
|
|
||||||
aFillPolys.BooleanIntersection( aMaxExtents, SHAPE_POLY_SET::PM_FAST );
|
aFillPolys.BooleanIntersection( aMaxExtents, SHAPE_POLY_SET::PM_FAST );
|
||||||
DUMP_POLYS_TO_COPPER_LAYER( aFillPolys, In16_Cu, wxT( "after-trim-to-outline" ) );
|
DUMP_POLYS_TO_COPPER_LAYER( aFillPolys, In16_Cu, wxT( "after-trim-to-outline" ) );
|
||||||
aFillPolys.BooleanSubtract( clearanceHoles, SHAPE_POLY_SET::PM_FAST );
|
aFillPolys.BooleanSubtract( clearanceHoles, SHAPE_POLY_SET::PM_FAST );
|
||||||
|
|
Loading…
Reference in New Issue