Fix several issues with multilayer zones
- Properly refill if layer set is modified - Fix some threading issues with island removal - Fix copy constructor Fixes https://gitlab.com/kicad/code/kicad/-/issues/4765
This commit is contained in:
parent
e320a3f112
commit
f8bfb2bc16
|
@ -87,6 +87,10 @@ ZONE_CONTAINER& ZONE_CONTAINER::operator=( const ZONE_CONTAINER& aOther )
|
|||
m_Poly = new SHAPE_POLY_SET( *aOther.m_Poly );
|
||||
|
||||
m_isKeepout = aOther.m_isKeepout;
|
||||
SetLayerSet( aOther.GetLayerSet() );
|
||||
|
||||
m_zoneName = aOther.m_zoneName;
|
||||
m_IsFilled = aOther.m_IsFilled;
|
||||
m_CornerSelection = nullptr; // for corner moving, corner index to (null if no selection)
|
||||
m_ZoneClearance = aOther.m_ZoneClearance; // clearance value
|
||||
m_ZoneMinThickness = aOther.m_ZoneMinThickness;
|
||||
|
@ -99,11 +103,14 @@ ZONE_CONTAINER& ZONE_CONTAINER::operator=( const ZONE_CONTAINER& aOther )
|
|||
SetHatchPitch( aOther.GetHatchPitch() );
|
||||
m_HatchLines = aOther.m_HatchLines; // copy vector <SEG>
|
||||
|
||||
m_FilledPolysList = aOther.m_FilledPolysList;
|
||||
m_RawPolysList = aOther.m_RawPolysList;
|
||||
m_filledPolysHash = aOther.m_filledPolysHash;
|
||||
m_FillSegmList = aOther.m_FillSegmList; // vector <> copy
|
||||
m_insulatedIslands = aOther.m_insulatedIslands;
|
||||
for( PCB_LAYER_ID layer : aOther.GetLayerSet().Seq() )
|
||||
{
|
||||
m_FilledPolysList[layer] = aOther.m_FilledPolysList.at( layer );
|
||||
m_RawPolysList[layer] = aOther.m_RawPolysList.at( layer );
|
||||
m_filledPolysHash[layer] = aOther.m_filledPolysHash.at( layer );
|
||||
m_FillSegmList[layer] = aOther.m_FillSegmList.at( layer ); // vector <> copy
|
||||
m_insulatedIslands[layer] = aOther.m_insulatedIslands.at( layer );
|
||||
}
|
||||
|
||||
m_HatchFillTypeThickness = aOther.m_HatchFillTypeThickness;
|
||||
m_HatchFillTypeGap = aOther.m_HatchFillTypeGap;
|
||||
|
@ -111,8 +118,6 @@ ZONE_CONTAINER& ZONE_CONTAINER::operator=( const ZONE_CONTAINER& aOther )
|
|||
m_HatchFillTypeSmoothingLevel = aOther.m_HatchFillTypeSmoothingLevel;
|
||||
m_HatchFillTypeSmoothingValue = aOther.m_HatchFillTypeSmoothingValue;
|
||||
|
||||
SetLayerSet( aOther.GetLayerSet() );
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -137,6 +142,8 @@ void ZONE_CONTAINER::initDataFromSrcInCopyCtor( const ZONE_CONTAINER& aZone )
|
|||
m_isKeepout = aZone.m_isKeepout;
|
||||
SetLayerSet( aZone.GetLayerSet() );
|
||||
|
||||
m_zoneName = aZone.m_zoneName;
|
||||
|
||||
m_Poly = new SHAPE_POLY_SET( *aZone.m_Poly );
|
||||
|
||||
// For corner moving, corner index to drag, or nullptr if no selection
|
||||
|
@ -151,11 +158,15 @@ void ZONE_CONTAINER::initDataFromSrcInCopyCtor( const ZONE_CONTAINER& aZone )
|
|||
m_PadConnection = aZone.m_PadConnection;
|
||||
m_ThermalReliefGap = aZone.m_ThermalReliefGap;
|
||||
m_ThermalReliefCopperBridge = aZone.m_ThermalReliefCopperBridge;
|
||||
m_FilledPolysList = aZone.m_FilledPolysList;
|
||||
m_RawPolysList = aZone.m_RawPolysList;
|
||||
m_filledPolysHash = aZone.m_filledPolysHash;
|
||||
m_FillSegmList = aZone.m_FillSegmList; // vector <> copy
|
||||
m_insulatedIslands = aZone.m_insulatedIslands;
|
||||
|
||||
for( PCB_LAYER_ID layer : aZone.GetLayerSet().Seq() )
|
||||
{
|
||||
m_FilledPolysList[layer] = aZone.m_FilledPolysList.at( layer );
|
||||
m_RawPolysList[layer] = aZone.m_RawPolysList.at( layer );
|
||||
m_filledPolysHash[layer] = aZone.m_filledPolysHash.at( layer );
|
||||
m_FillSegmList[layer] = aZone.m_FillSegmList.at( layer ); // vector <> copy
|
||||
m_insulatedIslands[layer] = aZone.m_insulatedIslands.at( layer );
|
||||
}
|
||||
|
||||
m_doNotAllowCopperPour = aZone.m_doNotAllowCopperPour;
|
||||
m_doNotAllowVias = aZone.m_doNotAllowVias;
|
||||
|
@ -267,6 +278,12 @@ void ZONE_CONTAINER::SetLayerSet( LSET aLayerSet )
|
|||
|
||||
UnFill();
|
||||
|
||||
m_FillSegmList.clear();
|
||||
m_FilledPolysList.clear();
|
||||
m_RawPolysList.clear();
|
||||
m_filledPolysHash.clear();
|
||||
m_insulatedIslands.clear();
|
||||
|
||||
for( PCB_LAYER_ID layer : aLayerSet.Seq() )
|
||||
{
|
||||
m_FillSegmList[layer] = {};
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#define CLASS_ZONE_H_
|
||||
|
||||
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <gr_basic.h>
|
||||
#include <class_board_item.h>
|
||||
|
@ -176,6 +177,11 @@ public:
|
|||
return m_area;
|
||||
}
|
||||
|
||||
std::mutex& GetLock()
|
||||
{
|
||||
return m_lock;
|
||||
}
|
||||
|
||||
bool IsFilled() const { return m_IsFilled; }
|
||||
void SetIsFilled( bool isFilled ) { m_IsFilled = isFilled; }
|
||||
|
||||
|
@ -929,6 +935,9 @@ protected:
|
|||
bool m_hv45; // constrain edges to horizontal, vertical or 45º
|
||||
|
||||
double m_area; // The filled zone area
|
||||
|
||||
/// Lock used for multi-threaded filling on multi-layer zones
|
||||
std::mutex m_lock;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -552,38 +552,33 @@ void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( ZONE_CONTAINER* aZone,
|
|||
|
||||
void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones )
|
||||
{
|
||||
for ( auto& z : aZones )
|
||||
Remove( z.m_zone );
|
||||
|
||||
for ( auto& z : aZones )
|
||||
for( auto& z : aZones )
|
||||
{
|
||||
for( PCB_LAYER_ID layer : z.m_zone->GetLayerSet().Seq() )
|
||||
{
|
||||
if( !z.m_zone->GetFilledPolysList( layer ).IsEmpty() )
|
||||
{
|
||||
Add( z.m_zone );
|
||||
break;
|
||||
}
|
||||
}
|
||||
Remove( z.m_zone );
|
||||
Add( z.m_zone );
|
||||
}
|
||||
|
||||
m_connClusters = SearchClusters( CSM_CONNECTIVITY_CHECK );
|
||||
|
||||
for ( auto& zone : aZones )
|
||||
for( auto& zone : aZones )
|
||||
{
|
||||
PCB_LAYER_ID layer = zone.m_layer;
|
||||
|
||||
if( zone.m_zone->GetFilledPolysList( layer ).IsEmpty() )
|
||||
continue;
|
||||
|
||||
for( const auto& cluster : m_connClusters )
|
||||
for( PCB_LAYER_ID layer : zone.m_zone->GetLayerSet().Seq() )
|
||||
{
|
||||
if( cluster->Contains( zone.m_zone ) && cluster->IsOrphaned() )
|
||||
if( zone.m_zone->GetFilledPolysList( layer ).IsEmpty() )
|
||||
continue;
|
||||
|
||||
for( const auto& cluster : m_connClusters )
|
||||
{
|
||||
for( auto z : *cluster )
|
||||
if( cluster->Contains( zone.m_zone ) && cluster->IsOrphaned() )
|
||||
{
|
||||
if( z->Parent() == zone.m_zone && z->Layer() == layer )
|
||||
zone.m_islands.push_back( static_cast<CN_ZONE*>( z )->SubpolyIndex() );
|
||||
for( auto z : *cluster )
|
||||
{
|
||||
if( z->Parent() == zone.m_zone && z->Layer() == layer )
|
||||
{
|
||||
zone.m_islands[layer].push_back(
|
||||
static_cast<CN_ZONE*>( z )->SubpolyIndex() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,21 +61,17 @@ struct CN_DISJOINT_NET_ENTRY
|
|||
};
|
||||
|
||||
/**
|
||||
* A structure used for filling a copper zone on one layer.
|
||||
* Multilayer zones will have one of these for each active layer.
|
||||
* 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_CONTAINER* aZone, PCB_LAYER_ID aLayer ) :
|
||||
m_zone( aZone ),
|
||||
m_layer( aLayer )
|
||||
CN_ZONE_ISOLATED_ISLAND_LIST( ZONE_CONTAINER* aZone ) :
|
||||
m_zone( aZone )
|
||||
{}
|
||||
|
||||
ZONE_CONTAINER* m_zone;
|
||||
|
||||
PCB_LAYER_ID m_layer;
|
||||
|
||||
std::vector<int> m_islands;
|
||||
std::map<PCB_LAYER_ID, std::vector<int>> m_islands;
|
||||
};
|
||||
|
||||
struct RN_DYNAMIC_LINE
|
||||
|
|
|
@ -100,7 +100,9 @@ void ZONE_FILLER::InstallNewProgressReporter( wxWindow* aParent, const wxString&
|
|||
|
||||
bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck )
|
||||
{
|
||||
std::vector<CN_ZONE_ISOLATED_ISLAND_LIST> toFill;
|
||||
std::vector<std::pair<ZONE_CONTAINER*, PCB_LAYER_ID>> toFill;
|
||||
std::vector<CN_ZONE_ISOLATED_ISLAND_LIST> islandsList;
|
||||
|
||||
auto connectivity = m_board->GetConnectivity();
|
||||
bool filledPolyWithOutline = not m_board->GetDesignSettings().m_ZoneUseNoOutlineInFill;
|
||||
|
||||
|
@ -112,7 +114,7 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
|
|||
if( m_progressReporter )
|
||||
{
|
||||
m_progressReporter->Report( aCheck ? _( "Checking zone fills..." ) : _( "Building zone fills..." ) );
|
||||
m_progressReporter->SetMaxProgress( toFill.size() );
|
||||
m_progressReporter->SetMaxProgress( aZones.size() );
|
||||
}
|
||||
|
||||
// The board outlines is used to clip solid areas inside the board (when outlines are valid)
|
||||
|
@ -145,9 +147,11 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
|
|||
zone->BuildHashValue( layer );
|
||||
|
||||
// Add the zone to the list of zones to test or refill
|
||||
toFill.emplace_back( CN_ZONE_ISOLATED_ISLAND_LIST( zone, layer ) );
|
||||
toFill.emplace_back( std::make_pair( zone, layer ) );
|
||||
}
|
||||
|
||||
islandsList.emplace_back( CN_ZONE_ISOLATED_ISLAND_LIST( zone ) );
|
||||
|
||||
// Remove existing fill first to prevent drawing invalid polygons
|
||||
// on some platforms
|
||||
zone->UnFill();
|
||||
|
@ -164,14 +168,16 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
|
|||
|
||||
for( size_t i = nextItem++; i < toFill.size(); i = nextItem++ )
|
||||
{
|
||||
PCB_LAYER_ID layer = toFill[i].m_layer;
|
||||
ZONE_CONTAINER* zone = toFill[i].m_zone;
|
||||
PCB_LAYER_ID layer = toFill[i].second;
|
||||
ZONE_CONTAINER* zone = toFill[i].first;
|
||||
|
||||
zone->SetFilledPolysUseThickness( filledPolyWithOutline );
|
||||
|
||||
SHAPE_POLY_SET rawPolys, finalPolys;
|
||||
fillSingleZone( zone, layer, rawPolys, finalPolys );
|
||||
|
||||
std::unique_lock<std::mutex> zoneLock( zone->GetLock() );
|
||||
|
||||
zone->SetRawPolysList( layer, rawPolys );
|
||||
zone->SetFilledPolysList( layer, finalPolys );
|
||||
zone->SetIsFilled( true );
|
||||
|
@ -215,68 +221,70 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
|
|||
}
|
||||
|
||||
connectivity->SetProgressReporter( m_progressReporter );
|
||||
connectivity->FindIsolatedCopperIslands( toFill );
|
||||
connectivity->FindIsolatedCopperIslands( islandsList );
|
||||
|
||||
// Now remove insulated copper islands and islands outside the board edge
|
||||
bool outOfDate = false;
|
||||
|
||||
for( auto& zone : toFill )
|
||||
for( auto& zone : islandsList )
|
||||
{
|
||||
std::sort( zone.m_islands.begin(), zone.m_islands.end(), std::greater<int>() );
|
||||
SHAPE_POLY_SET poly = zone.m_zone->GetFilledPolysList( zone.m_layer );
|
||||
|
||||
long long int minArea = zone.m_zone->GetMinIslandArea();
|
||||
ISLAND_REMOVAL_MODE mode = zone.m_zone->GetIslandRemovalMode();
|
||||
|
||||
// Remove solid areas outside the board cutouts and the insulated islands
|
||||
// only zones with net code > 0 can have insulated islands by definition
|
||||
if( zone.m_zone->GetNetCode() > 0 )
|
||||
for( PCB_LAYER_ID layer : zone.m_zone->GetLayerSet().Seq() )
|
||||
{
|
||||
// solid areas outside the board cutouts are also removed, because they are usually
|
||||
// insulated islands
|
||||
for( auto idx : zone.m_islands )
|
||||
{
|
||||
#if 0 // for tests only
|
||||
double metricMin = minArea * ( MM_PER_IU * MM_PER_IU );
|
||||
double metricArea = poly.Outline( idx ).Area() * ( MM_PER_IU * MM_PER_IU );
|
||||
std::cout << ( metricArea < metricMin ) << std::endl;
|
||||
#endif
|
||||
if( !zone.m_islands.count( layer ) )
|
||||
continue;
|
||||
|
||||
if( mode == ISLAND_REMOVAL_MODE::ALWAYS
|
||||
|| ( mode == ISLAND_REMOVAL_MODE::AREA
|
||||
&& poly.Outline( idx ).Area() < minArea )
|
||||
|| !m_boardOutline.Contains( poly.Polygon( idx ).front().CPoint( 0 ) ) )
|
||||
poly.DeletePolygon( idx );
|
||||
else
|
||||
zone.m_zone->SetIsIsland( zone.m_layer, idx );
|
||||
}
|
||||
}
|
||||
// Zones with no net can have areas outside the board cutouts.
|
||||
// By definition, Zones with no net have no isolated island
|
||||
// (in fact all filled areas are isolated islands)
|
||||
// but they can have some areas outside the board cutouts.
|
||||
// A filled area outside the board cutouts has all points outside cutouts,
|
||||
// so we only need to check one point for each filled polygon.
|
||||
// Note also non copper zones are already clipped
|
||||
else if( m_brdOutlinesValid && zone.m_zone->IsOnCopperLayer() )
|
||||
{
|
||||
for( int idx = 0; idx < poly.OutlineCount(); )
|
||||
std::vector<int>& islands = zone.m_islands.at( layer );
|
||||
|
||||
std::sort( islands.begin(), islands.end(), std::greater<int>() );
|
||||
SHAPE_POLY_SET poly = zone.m_zone->GetFilledPolysList( layer );
|
||||
|
||||
long long int minArea = zone.m_zone->GetMinIslandArea();
|
||||
ISLAND_REMOVAL_MODE mode = zone.m_zone->GetIslandRemovalMode();
|
||||
|
||||
// Remove solid areas outside the board cutouts and the insulated islands
|
||||
// only zones with net code > 0 can have insulated islands by definition
|
||||
if( zone.m_zone->GetNetCode() > 0 )
|
||||
{
|
||||
if( poly.Polygon( idx ).empty()
|
||||
|| !m_boardOutline.Contains( poly.Polygon( idx ).front().CPoint( 0 ) ) )
|
||||
// solid areas outside the board cutouts are also removed, because they are usually
|
||||
// insulated islands
|
||||
for( auto idx : islands )
|
||||
{
|
||||
poly.DeletePolygon( idx );
|
||||
if( mode == ISLAND_REMOVAL_MODE::ALWAYS
|
||||
|| ( mode == ISLAND_REMOVAL_MODE::AREA
|
||||
&& poly.Outline( idx ).Area() < minArea )
|
||||
|| !m_boardOutline.Contains( poly.Polygon( idx ).front().CPoint( 0 ) ) )
|
||||
poly.DeletePolygon( idx );
|
||||
else
|
||||
zone.m_zone->SetIsIsland( layer, idx );
|
||||
}
|
||||
else
|
||||
idx++;
|
||||
}
|
||||
// Zones with no net can have areas outside the board cutouts.
|
||||
// By definition, Zones with no net have no isolated island
|
||||
// (in fact all filled areas are isolated islands)
|
||||
// but they can have some areas outside the board cutouts.
|
||||
// A filled area outside the board cutouts has all points outside cutouts,
|
||||
// so we only need to check one point for each filled polygon.
|
||||
// Note also non copper zones are already clipped
|
||||
else if( m_brdOutlinesValid && zone.m_zone->IsOnCopperLayer() )
|
||||
{
|
||||
for( int idx = 0; idx < poly.OutlineCount(); )
|
||||
{
|
||||
if( poly.Polygon( idx ).empty()
|
||||
|| !m_boardOutline.Contains( poly.Polygon( idx ).front().CPoint( 0 ) ) )
|
||||
{
|
||||
poly.DeletePolygon( idx );
|
||||
}
|
||||
else
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
zone.m_zone->SetFilledPolysList( layer, poly );
|
||||
zone.m_zone->CalculateFilledArea();
|
||||
|
||||
if( aCheck && zone.m_zone->GetHashValue( layer ) != poly.GetHash() )
|
||||
outOfDate = true;
|
||||
}
|
||||
|
||||
zone.m_zone->SetFilledPolysList( zone.m_layer, poly );
|
||||
zone.m_zone->CalculateFilledArea();
|
||||
|
||||
if( aCheck && zone.m_zone->GetHashValue( zone.m_layer ) != poly.GetHash() )
|
||||
outOfDate = true;
|
||||
}
|
||||
|
||||
if( aCheck && outOfDate )
|
||||
|
@ -312,9 +320,9 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
|
|||
{
|
||||
size_t num = 0;
|
||||
|
||||
for( size_t i = nextItem++; i < toFill.size(); i = nextItem++ )
|
||||
for( size_t i = nextItem++; i < islandsList.size(); i = nextItem++ )
|
||||
{
|
||||
toFill[i].m_zone->CacheTriangulation();
|
||||
islandsList[i].m_zone->CacheTriangulation();
|
||||
num++;
|
||||
|
||||
if( m_progressReporter )
|
||||
|
@ -361,7 +369,7 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
|
|||
else
|
||||
{
|
||||
for( auto& i : toFill )
|
||||
connectivity->Update( i.m_zone );
|
||||
connectivity->Update( i.first );
|
||||
|
||||
connectivity->RecalculateRatsnest();
|
||||
}
|
||||
|
|
|
@ -129,7 +129,8 @@ void PCB_EDIT_FRAME::Edit_Zone_Params( ZONE_CONTAINER* aZone )
|
|||
continue;
|
||||
}
|
||||
|
||||
if( zone->IsFilled() )
|
||||
// aZone won't be filled if the layer set was modified, but it needs to be updated
|
||||
if( zone->IsFilled() || zone == aZone )
|
||||
zones_to_refill.push_back( zone );
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
bool ZONE_CONTAINER::IsSame( const ZONE_CONTAINER& aZoneToCompare )
|
||||
{
|
||||
// compare basic parameters:
|
||||
if( GetLayer() != aZoneToCompare.GetLayer() )
|
||||
if( GetLayerSet() != aZoneToCompare.GetLayerSet() )
|
||||
return false;
|
||||
|
||||
if( GetNetCode() != aZoneToCompare.GetNetCode() )
|
||||
|
@ -111,6 +111,15 @@ bool ZONE_CONTAINER::IsSame( const ZONE_CONTAINER& aZoneToCompare )
|
|||
if( m_ThermalReliefCopperBridge != aZoneToCompare.m_ThermalReliefCopperBridge )
|
||||
return false;
|
||||
|
||||
if( m_zoneName != aZoneToCompare.m_zoneName )
|
||||
return false;
|
||||
|
||||
if( m_islandRemovalMode != aZoneToCompare.m_islandRemovalMode )
|
||||
return false;
|
||||
|
||||
if( m_minIslandArea != aZoneToCompare.m_minIslandArea )
|
||||
return false;
|
||||
|
||||
|
||||
// Compare outlines
|
||||
wxASSERT( m_Poly ); // m_Poly == NULL Should never happen
|
||||
|
@ -149,7 +158,7 @@ int SaveCopyOfZones( PICKED_ITEMS_LIST& aPickList, BOARD* aPcb, int aNetCode, LA
|
|||
if( aNetCode >= 0 && aNetCode != zone->GetNetCode() )
|
||||
continue;
|
||||
|
||||
if( aLayer >= 0 && aLayer != zone->GetLayer() )
|
||||
if( aLayer >= 0 && !zone->GetLayerSet().test( aLayer ) )
|
||||
continue;
|
||||
|
||||
ZONE_CONTAINER* zoneDup = new ZONE_CONTAINER( *zone );
|
||||
|
|
|
@ -127,7 +127,7 @@ bool BOARD::CombineAllAreasInNet( PICKED_ITEMS_LIST* aDeletedList, int aNetCode,
|
|||
if( curr_area->GetIsKeepout() != area2->GetIsKeepout() )
|
||||
continue;
|
||||
|
||||
if( curr_area->GetLayer() != area2->GetLayer() )
|
||||
if( curr_area->GetLayerSet() != area2->GetLayerSet() )
|
||||
continue;
|
||||
|
||||
BOX2I b2 = area2->Outline()->BBox();
|
||||
|
|
Loading…
Reference in New Issue