Keep track of single-pad-islands so we can discount spokes to them.

This commit is contained in:
Jeff Young 2023-03-25 10:44:46 +00:00
parent 4e27e91b2f
commit d6dd58fff9
13 changed files with 174 additions and 174 deletions

View File

@ -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>();
} }

View File

@ -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,

View File

@ -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() );
} }
} }
} }

View File

@ -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();

View File

@ -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 );
} }

View File

@ -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()

View File

@ -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 */ )

View File

@ -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;

View File

@ -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();
} }

View File

@ -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 );
} }
} }
} }

View File

@ -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();
}
} }
} }

View File

@ -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.
* *

View File

@ -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 );