CHANGED: Copper zones can be on more than one layer

Fixes https://gitlab.com/kicad/code/kicad/-/issues/1963
This commit is contained in:
Jon Evans 2020-06-23 22:19:08 -04:00
parent 0b34cea3d5
commit 0d4ee39f75
36 changed files with 671 additions and 492 deletions

View File

@ -708,7 +708,7 @@ void BOARD_ADAPTER::AddSolidAreasShapesToContainer( const ZONE_CONTAINER* aZoneC
PCB_LAYER_ID aLayerId )
{
// Copy the polys list because we have to simplify it
SHAPE_POLY_SET polyList = SHAPE_POLY_SET( aZoneContainer->GetFilledPolysList() );
SHAPE_POLY_SET polyList = SHAPE_POLY_SET( aZoneContainer->GetFilledPolysList( aLayerId ) );
// This convert the poly in outline and holes
Convert_shape_line_polygon_to_triangles( polyList, *aDstContainer, m_biuTo3Dunits,

View File

@ -682,6 +682,16 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
if( aStatusReporter )
aStatusReporter->Report( _( "Create zones" ) );
std::vector<std::pair<const ZONE_CONTAINER*, PCB_LAYER_ID>> zones;
for( size_t i = 0; i < m_board->GetAreaCount(); i++ )
{
const ZONE_CONTAINER* zone = m_board->GetArea( i );
for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
zones.emplace_back( std::make_pair( zone, layer ) );
}
// Add zones objects
// /////////////////////////////////////////////////////////////////////
std::atomic<size_t> nextZone( 0 );
@ -693,19 +703,20 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
std::thread t = std::thread( [&]()
{
for( size_t areaId = nextZone.fetch_add( 1 );
areaId < static_cast<size_t>( m_board->GetAreaCount() );
areaId < zones.size();
areaId = nextZone.fetch_add( 1 ) )
{
const ZONE_CONTAINER* zone = m_board->GetArea( areaId );
const ZONE_CONTAINER* zone = zones[areaId].first;
if( zone == nullptr )
break;
auto layerContainer = m_layers_container2D.find( zone->GetLayer() );
PCB_LAYER_ID layer = zones[areaId].second;
auto layerContainer = m_layers_container2D.find( layer );
if( layerContainer != m_layers_container2D.end() )
AddSolidAreasShapesToContainer( zone, layerContainer->second,
zone->GetLayer() );
AddSolidAreasShapesToContainer( zone, layerContainer->second, layer );
}
threadsFinished++;
@ -733,10 +744,13 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
if( zone == nullptr )
break;
auto layerContainer = m_layers_poly.find( zone->GetLayer() );
for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
{
auto layerContainer = m_layers_poly.find( layer );
if( layerContainer != m_layers_poly.end() )
zone->TransformSolidAreasShapesToPolygonSet( *layerContainer->second );
if( layerContainer != m_layers_poly.end() )
zone->TransformSolidAreasShapesToPolygonSet( layer, *layerContainer->second );
}
}
}
@ -1010,7 +1024,7 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
if( !zone->IsOnLayer( curr_layer_id ) )
continue;
zone->TransformSolidAreasShapesToPolygonSet( *layerPoly );
zone->TransformSolidAreasShapesToPolygonSet( curr_layer_id, *layerPoly );
}
}

View File

@ -89,10 +89,9 @@ void BOARD::ConvertBrdLayerToPolygonalContours( PCB_LAYER_ID aLayer, SHAPE_POLY_
for( int ii = 0; ii < GetAreaCount(); ii++ )
{
ZONE_CONTAINER* zone = GetArea( ii );
PCB_LAYER_ID zonelayer = zone->GetLayer();
if( zonelayer == aLayer )
zone->TransformSolidAreasShapesToPolygonSet( aOutlines );
if( zone->GetLayerSet().test( aLayer ) )
zone->TransformSolidAreasShapesToPolygonSet( aLayer, aOutlines );
}
// convert graphic items on copper layers (texts)
@ -242,14 +241,17 @@ void MODULE::TransformGraphicShapesWithClearanceToPolygonSet( PCB_LAYER_ID aLaye
}
void ZONE_CONTAINER::TransformSolidAreasShapesToPolygonSet( SHAPE_POLY_SET& aCornerBuffer,
void ZONE_CONTAINER::TransformSolidAreasShapesToPolygonSet( PCB_LAYER_ID aLayer,
SHAPE_POLY_SET& aCornerBuffer,
int aError ) const
{
if( GetFilledPolysList().IsEmpty() )
if( !m_FilledPolysList.count( aLayer ) || m_FilledPolysList.at( aLayer ).IsEmpty() )
return;
const SHAPE_POLY_SET& polys = m_FilledPolysList.at( aLayer );
// add filled areas polygons
aCornerBuffer.Append( m_FilledPolysList );
aCornerBuffer.Append( polys );
auto board = GetBoard();
int maxError = ARC_HIGH_DEF;
@ -257,9 +259,9 @@ void ZONE_CONTAINER::TransformSolidAreasShapesToPolygonSet( SHAPE_POLY_SET& aCor
maxError = board->GetDesignSettings().m_MaxError;
// add filled areas outlines, which are drawn with thick lines
for( int i = 0; i < m_FilledPolysList.OutlineCount(); i++ )
for( int i = 0; i < polys.OutlineCount(); i++ )
{
const SHAPE_LINE_CHAIN& path = m_FilledPolysList.COutline( i );
const SHAPE_LINE_CHAIN& path = polys.COutline( i );
for( int j = 0; j < path.PointCount(); j++ )
{
@ -652,8 +654,16 @@ void ZONE_CONTAINER::TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCorn
int aClearanceValue, int aError,
bool ignoreLineWidth ) const
{
wxASSERT_MSG( !ignoreLineWidth, "IgnoreLineWidth has no meaning for zones." );
// Now that zones are multilayer, we cannot implement this without a layer argument.
// But, at the time of adding multilayer zones, this is never called for zones anyway
// so let's just disable it and fail.
wxFAIL_MSG( "TransformShapeWithClearanceToPolygon is not supported for zones" );
aCornerBuffer = m_FilledPolysList;
#if 0
if( !m_FilledPolysList.count( aLayer ) )
return;
aCornerBuffer = m_FilledPolysList.at( aLayer );
aCornerBuffer.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
#endif
}

View File

@ -1216,31 +1216,6 @@ int BOARD::SortedNetnamesList( wxArrayString& aNames, bool aSortbyPadsCount )
}
ZONE_CONTAINER* BOARD::HitTestForAnyFilledArea( const wxPoint& aRefPos, PCB_LAYER_ID aStartLayer,
PCB_LAYER_ID aEndLayer, int aNetCode )
{
if( aEndLayer < 0 )
aEndLayer = aStartLayer;
if( aEndLayer < aStartLayer )
std::swap( aEndLayer, aStartLayer );
for( ZONE_CONTAINER* area : m_ZoneDescriptorList )
{
if( area->GetLayer() < aStartLayer || area->GetLayer() > aEndLayer )
continue;
if( aNetCode >= 0 && area->GetNetCode() != aNetCode )
continue;
if( area->HitTestFilledArea( aRefPos ) )
return area;
}
return NULL;
}
int BOARD::SetAreasNetCodesFromNetNames()
{
int error_count = 0;

View File

@ -855,21 +855,6 @@ public:
/* Copper Areas handling */
/*************************/
/**
* Function HitTestForAnyFilledArea
* tests if the given wxPoint is within the bounds of a filled area of this zone.
* the test is made on zones on layer from aStartLayer to aEndLayer
* @param aRefPos A wxPoint to test
* @param aStartLayer the first layer to test
* @param aEndLayer the last layer to test
* @param aNetCode = the netcode used to filter zones (-1 to to test all zones)
* @return ZONE_CONTAINER* return a pointer to the ZONE_CONTAINER found, else NULL
*/
ZONE_CONTAINER* HitTestForAnyFilledArea( const wxPoint& aRefPos,
PCB_LAYER_ID aStartLayer,
PCB_LAYER_ID aEndLayer,
int aNetCode );
/**
* Function SetAreasNetCodesFromNetNames
* Set the .m_NetCode member of all copper areas, according to the area Net Name

View File

@ -30,6 +30,7 @@
#include <pcb_screen.h>
#include <class_board.h>
#include <class_zone.h>
#include <pcb_edit_frame.h> // current layer for msgpanel
#include <math_for_graphics.h>
#include <settings/color_settings.h>
#include <settings/settings_manager.h>
@ -63,6 +64,7 @@ ZONE_CONTAINER::ZONE_CONTAINER( BOARD_ITEM_CONTAINER* aParent, bool aInModule )
SetLocalFlags( 0 ); // flags tempoarry used in zone calculations
m_Poly = new SHAPE_POLY_SET(); // Outlines
m_FilledPolysUseThickness = true; // set the "old" way to build filled polygon areas (before 6.0.x)
m_removeIslands = true;
aParent->GetZoneSettings().ExportSetting( *this );
m_needRefill = false; // True only after some edition.
@ -192,10 +194,20 @@ EDA_ITEM* ZONE_CONTAINER::Clone() const
bool ZONE_CONTAINER::UnFill()
{
bool change = ( !m_FilledPolysList.IsEmpty() || m_FillSegmList.size() > 0 );
bool change = false;
for( std::pair<PCB_LAYER_ID, SHAPE_POLY_SET> pair : m_FilledPolysList )
{
change |= !pair.second.IsEmpty();
pair.second.RemoveAllContours();
}
for( std::pair<PCB_LAYER_ID, ZONE_SEGMENT_FILL> pair : m_FillSegmList )
{
change |= !pair.second.empty();
pair.second.clear();
}
m_FilledPolysList.RemoveAllContours();
m_FillSegmList.clear();
m_IsFilled = false;
return change;
@ -216,14 +228,7 @@ PCB_LAYER_ID ZONE_CONTAINER::GetLayer() const
bool ZONE_CONTAINER::IsOnCopperLayer() const
{
if( GetIsKeepout() )
{
return ( m_layerSet & LSET::AllCuMask() ).count() > 0;
}
else
{
return IsCopperLayer( GetLayer() );
}
return ( m_layerSet & LSET::AllCuMask() ).count() > 0;
}
@ -255,12 +260,24 @@ void ZONE_CONTAINER::SetLayerSet( LSET aLayerSet )
return;
if( m_layerSet != aLayerSet )
{
SetNeedRefill( true );
UnFill();
for( PCB_LAYER_ID layer : aLayerSet.Seq() )
{
m_FillSegmList[layer] = {};
m_FilledPolysList[layer] = {};
m_RawPolysList[layer] = {};
m_filledPolysHash[layer] = {};
}
}
m_layerSet = aLayerSet;
// Set the single layer parameter.
// For keepout zones that can be on many layers, this parameter does not have
// For zones that can be on many layers, this parameter does not have
// really meaning and is a bit arbitrary if more than one layer is set.
// But many functions are using it.
// So we need to initialize it to a reasonable value.
@ -274,43 +291,24 @@ void ZONE_CONTAINER::SetLayerSet( LSET aLayerSet )
LSET ZONE_CONTAINER::GetLayerSet() const
{
// TODO - Enable multi-layer zones for all zone types
// not just keepout zones
if( GetIsKeepout() )
{
return m_layerSet;
}
else
{
return LSET( m_Layer );
}
return m_layerSet;
}
void ZONE_CONTAINER::ViewGetLayers( int aLayers[], int& aCount ) const
{
if( GetIsKeepout() )
{
LSEQ layers = m_layerSet.Seq();
LSEQ layers = m_layerSet.Seq();
for( unsigned int idx = 0; idx < layers.size(); idx++ )
aLayers[idx] = layers[idx];
for( unsigned int idx = 0; idx < layers.size(); idx++ )
aLayers[idx] = layers[idx];
aCount = layers.size();
}
else
{
aLayers[0] = m_Layer;
aCount = 1;
}
aCount = layers.size();
}
bool ZONE_CONTAINER::IsOnLayer( PCB_LAYER_ID aLayer ) const
{
if( GetIsKeepout() )
return m_layerSet.test( aLayer );
return BOARD_ITEM::IsOnLayer( aLayer );
return m_layerSet.test( aLayer );
}
@ -538,9 +536,12 @@ int ZONE_CONTAINER::GetLocalClearance( wxString* aSource ) const
}
bool ZONE_CONTAINER::HitTestFilledArea( const wxPoint& aRefPos ) const
bool ZONE_CONTAINER::HitTestFilledArea( PCB_LAYER_ID aLayer, const wxPoint& aRefPos ) const
{
return m_FilledPolysList.Contains( VECTOR2I( aRefPos.x, aRefPos.y ) );
if( !m_FilledPolysList.count( aLayer ) )
return false;
return m_FilledPolysList.at( aLayer ).Contains( VECTOR2I( aRefPos.x, aRefPos.y ) );
}
@ -642,7 +643,21 @@ void ZONE_CONTAINER::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PA
aList.emplace_back( _( "Priority" ), msg, BLUE );
}
aList.emplace_back( _( "Layer" ), LayerMaskDescribe( GetBoard(), m_layerSet ), DARKGREEN );
wxString layerDesc;
int count = 0;
for( PCB_LAYER_ID layer : m_layerSet.Seq() )
{
if( count == 0 )
layerDesc = GetBoard()->GetLayerName( layer );
count++;
}
if( count > 1 )
layerDesc.Printf( _( "%s and %d more" ), layerDesc, count - 1 );
aList.emplace_back( _( "Layer" ), layerDesc, DARKGREEN );
if( !m_zoneName.empty() )
aList.emplace_back( _( "Name" ), m_zoneName, DARKMAGENTA );
@ -671,9 +686,19 @@ void ZONE_CONTAINER::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PA
msg.Printf( wxT( "%d" ), (int) m_HatchLines.size() );
aList.emplace_back( MSG_PANEL_ITEM( _( "Hatch Lines" ), msg, BLUE ) );
if( !m_FilledPolysList.IsEmpty() )
PCB_LAYER_ID layer = m_Layer;
// NOTE: This brings in dependence on PCB_EDIT_FRAME to the qa tests, which isn't ideal.
// TODO: Figure out a way for items to know the active layer without the whole edit frame?
#if 0
if( PCB_EDIT_FRAME* pcbframe = dynamic_cast<PCB_EDIT_FRAME*>( aFrame ) )
if( m_FilledPolysList.count( pcbframe->GetActiveLayer() ) )
layer = pcbframe->GetActiveLayer();
#endif
if( !m_FilledPolysList.at( layer ).IsEmpty() )
{
msg.Printf( wxT( "%d" ), m_FilledPolysList.TotalVertices() );
msg.Printf( wxT( "%d" ), m_FilledPolysList.at( layer ).TotalVertices() );
aList.emplace_back( MSG_PANEL_ITEM( _( "Corner Count" ), msg, BLUE ) );
}
}
@ -688,12 +713,16 @@ void ZONE_CONTAINER::Move( const wxPoint& offset )
Hatch();
m_FilledPolysList.Move( offset );
for( std::pair<PCB_LAYER_ID, SHAPE_POLY_SET> pair : m_FilledPolysList )
pair.second.Move( offset );
for( SEG& seg : m_FillSegmList )
for( std::pair<PCB_LAYER_ID, ZONE_SEGMENT_FILL> pair : m_FillSegmList )
{
seg.A += VECTOR2I( offset );
seg.B += VECTOR2I( offset );
for( SEG& seg : pair.second )
{
seg.A += VECTOR2I( offset );
seg.B += VECTOR2I( offset );
}
}
}
@ -723,16 +752,20 @@ void ZONE_CONTAINER::Rotate( const wxPoint& centre, double angle )
Hatch();
/* rotate filled areas: */
m_FilledPolysList.Rotate( angle, VECTOR2I( centre ) );
for( std::pair<PCB_LAYER_ID, SHAPE_POLY_SET> pair : m_FilledPolysList )
pair.second.Rotate( angle, VECTOR2I( centre ) );
for( unsigned ic = 0; ic < m_FillSegmList.size(); ic++ )
for( std::pair<PCB_LAYER_ID, ZONE_SEGMENT_FILL> pair : m_FillSegmList )
{
wxPoint a( m_FillSegmList[ic].A );
RotatePoint( &a, centre, angle );
m_FillSegmList[ic].A = a;
wxPoint b( m_FillSegmList[ic].B );
RotatePoint( &b, centre, angle );
m_FillSegmList[ic].B = a;
for( SEG& seg : pair.second )
{
wxPoint a( seg.A );
RotatePoint( &a, centre, angle );
seg.A = a;
wxPoint b( seg.B );
RotatePoint( &b, centre, angle );
seg.B = a;
}
}
}
@ -756,19 +789,23 @@ void ZONE_CONTAINER::Mirror( const wxPoint& aMirrorRef, bool aMirrorLeftRight )
Hatch();
m_FilledPolysList.Mirror( aMirrorLeftRight, !aMirrorLeftRight, VECTOR2I( aMirrorRef ) );
for( std::pair<PCB_LAYER_ID, SHAPE_POLY_SET> pair : m_FilledPolysList )
pair.second.Mirror( aMirrorLeftRight, !aMirrorLeftRight, VECTOR2I( aMirrorRef ) );
for( SEG& seg : m_FillSegmList )
for( std::pair<PCB_LAYER_ID, ZONE_SEGMENT_FILL> pair : m_FillSegmList )
{
if( aMirrorLeftRight )
for( SEG& seg : pair.second )
{
MIRROR( seg.A.x, aMirrorRef.x );
MIRROR( seg.B.x, aMirrorRef.x );
}
else
{
MIRROR( seg.A.y, aMirrorRef.y );
MIRROR( seg.B.y, aMirrorRef.y );
if( aMirrorLeftRight )
{
MIRROR( seg.A.x, aMirrorRef.x );
MIRROR( seg.B.x, aMirrorRef.x );
}
else
{
MIRROR( seg.A.y, aMirrorRef.y );
MIRROR( seg.B.y, aMirrorRef.y );
}
}
}
}
@ -862,7 +899,21 @@ wxString ZONE_CONTAINER::GetSelectMenuText( EDA_UNITS aUnits ) const
else
text << GetNetnameMsg();
return wxString::Format( _( "Zone Outline %s on %s" ), text, GetLayerName() );
wxString layerDesc;
int count = 0;
for( PCB_LAYER_ID layer : m_layerSet.Seq() )
{
if( count == 0 )
layerDesc = GetBoard()->GetLayerName( layer );
count++;
}
if( count > 1 )
layerDesc.Printf( _( "%s and %d more" ), layerDesc, count - 1 );
return wxString::Format( _( "Zone Outline %s on %s" ), text, layerDesc );
}
@ -1087,7 +1138,8 @@ void ZONE_CONTAINER::SwapData( BOARD_ITEM* aImage )
void ZONE_CONTAINER::CacheTriangulation()
{
m_FilledPolysList.CacheTriangulation();
for( std::pair<PCB_LAYER_ID, SHAPE_POLY_SET> pair : m_FilledPolysList )
pair.second.CacheTriangulation();
}
@ -1183,13 +1235,18 @@ double ZONE_CONTAINER::CalculateFilledArea()
// Iterate over each outline polygon in the zone and then iterate over
// each hole it has to compute the total area.
for( int i = 0; i < m_FilledPolysList.OutlineCount(); i++ )
for( std::pair<PCB_LAYER_ID, SHAPE_POLY_SET> pair : m_FilledPolysList )
{
m_area += m_FilledPolysList.Outline( i ).Area();
SHAPE_POLY_SET& poly = pair.second;
for( int j = 0; j < m_FilledPolysList.HoleCount( i ); j++ )
for( int i = 0; i < poly.OutlineCount(); i++ )
{
m_area -= m_FilledPolysList.Hole( i, j ).Area();
m_area += poly.Outline( i ).Area();
for( int j = 0; j < poly.HoleCount( i ); j++ )
{
m_area -= poly.Hole( i, j ).Area();
}
}
}

View File

@ -250,8 +250,17 @@ public:
int GetLocalFlags() const { return m_localFlgs; }
void SetLocalFlags( int aFlags ) { m_localFlgs = aFlags; }
ZONE_SEGMENT_FILL& FillSegments() { return m_FillSegmList; }
const ZONE_SEGMENT_FILL& FillSegments() const { return m_FillSegmList; }
ZONE_SEGMENT_FILL& FillSegments( PCB_LAYER_ID aLayer )
{
wxASSERT( m_FillSegmList.count( aLayer ) );
return m_FillSegmList.at( aLayer );
}
const ZONE_SEGMENT_FILL& FillSegments( PCB_LAYER_ID aLayer ) const
{
wxASSERT( m_FillSegmList.count( aLayer ) );
return m_FillSegmList.at( aLayer );
}
SHAPE_POLY_SET* Outline() { return m_Poly; }
const SHAPE_POLY_SET* Outline() const { return const_cast< SHAPE_POLY_SET* >( m_Poly ); }
@ -269,10 +278,11 @@ public:
/**
* Function HitTestFilledArea
* tests if the given wxPoint is within the bounds of a filled area of this zone.
* @param aLayer is the layer to test on
* @param aRefPos A wxPoint to test
* @return bool - true if a hit, else false
*/
bool HitTestFilledArea( const wxPoint& aRefPos ) const;
bool HitTestFilledArea( PCB_LAYER_ID aLayer, const wxPoint& aRefPos ) const;
/**
* Tests if the given point is contained within a cutout of the zone.
@ -305,10 +315,11 @@ public:
* (the full shape is the polygon area with a thick outline)
* Used in 3D view
* Arcs (ends of segments) are approximated by segments
* @param aLayer is the layer of the zone to retrieve
* @param aCornerBuffer = a buffer to store the polygons
* @param aError = Maximum error allowed between true arc and polygon approx
*/
void TransformSolidAreasShapesToPolygonSet(
void TransformSolidAreasShapesToPolygonSet( PCB_LAYER_ID aLayer,
SHAPE_POLY_SET& aCornerBuffer, int aError = ARC_HIGH_DEF ) const;
/**
@ -334,14 +345,16 @@ public:
* Convert the zone shape to a closed polygon
* Used in filling zones calculations
* Circles and arcs are approximated by segments
* @param aLayer is the layer of the filled zone to retrieve
* @param aCornerBuffer = a buffer to store the polygon
* @param aClearanceValue = the clearance around the pad
* @param aError = the maximum deviation from true circle
* @param ignoreLineWidth = used for edge cut items where the line width is only
* for visualization
*/
void TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer, int aClearanceValue,
int aError = ARC_HIGH_DEF, bool ignoreLineWidth = false ) const override;
void TransformShapeWithClearanceToPolygon( SHAPE_POLY_SET& aCornerBuffer,
int aClearanceValue, int aError = ARC_HIGH_DEF,
bool ignoreLineWidth = false ) const override;
/**
* Function HitTestForCorner
@ -568,7 +581,8 @@ public:
*/
void ClearFilledPolysList()
{
m_FilledPolysList.RemoveAllContours();
for( std::pair<const PCB_LAYER_ID, SHAPE_POLY_SET>& pair : m_FilledPolysList )
pair.second.RemoveAllContours();
}
/**
@ -576,9 +590,10 @@ public:
* returns a reference to the list of filled polygons.
* @return Reference to the list of filled polygons.
*/
const SHAPE_POLY_SET& GetFilledPolysList() const
const SHAPE_POLY_SET& GetFilledPolysList( PCB_LAYER_ID aLayer ) const
{
return m_FilledPolysList;
wxASSERT( m_FilledPolysList.count( aLayer ) );
return m_FilledPolysList.at( aLayer );
}
/** (re)create a list of triangles that "fill" the solid areas.
@ -590,18 +605,18 @@ public:
* Function SetFilledPolysList
* sets the list of filled polygons.
*/
void SetFilledPolysList( SHAPE_POLY_SET& aPolysList )
void SetFilledPolysList( PCB_LAYER_ID aLayer, SHAPE_POLY_SET& aPolysList )
{
m_FilledPolysList = aPolysList;
m_FilledPolysList[aLayer] = aPolysList;
}
/**
* Function SetFilledPolysList
* sets the list of filled polygons.
*/
void SetRawPolysList( SHAPE_POLY_SET& aPolysList )
void SetRawPolysList( PCB_LAYER_ID aLayer, SHAPE_POLY_SET& aPolysList )
{
m_RawPolysList = aPolysList;
m_RawPolysList[aLayer] = aPolysList;
}
@ -643,14 +658,15 @@ public:
void AddPolygon( const SHAPE_LINE_CHAIN& aPolygon );
void SetFillSegments( const ZONE_SEGMENT_FILL& aSegments )
void SetFillSegments( PCB_LAYER_ID aLayer, const ZONE_SEGMENT_FILL& aSegments )
{
m_FillSegmList = aSegments;
m_FillSegmList[aLayer] = aSegments;
}
SHAPE_POLY_SET& RawPolysList()
SHAPE_POLY_SET& RawPolysList( PCB_LAYER_ID aLayer )
{
return m_RawPolysList;
wxASSERT( m_RawPolysList.count( aLayer ) );
return m_RawPolysList.at( aLayer );
}
wxString GetSelectMenuText( EDA_UNITS aUnits ) const override;
@ -685,6 +701,9 @@ public:
void SetDoNotAllowPads( bool aEnable ) { m_doNotAllowPads = aEnable; }
void SetDoNotAllowFootprints( bool aEnable ) { m_doNotAllowFootprints = aEnable; }
bool GetRemoveIslands() const { return m_removeIslands; }
void SetRemoveIslands( bool aRemove ) { m_removeIslands = aRemove; }
/**
* Hatch related methods
*/
@ -740,13 +759,25 @@ public:
/** @return the hash value previously calculated by BuildHashValue().
* used in zone filling calculations
*/
MD5_HASH GetHashValue() { return m_filledPolysHash; }
MD5_HASH GetHashValue( PCB_LAYER_ID aLayer )
{
if( !m_filledPolysHash.count( aLayer ) )
return MD5_HASH();
return m_filledPolysHash.at( aLayer );
}
/** Build the hash value of m_FilledPolysList, and store it internally
* in m_filledPolysHash.
* Used in zone filling calculations, to know if m_FilledPolysList is up to date.
*/
void BuildHashValue() { m_filledPolysHash = m_FilledPolysList.GetHash(); }
void BuildHashValue( PCB_LAYER_ID aLayer )
{
if( !m_FilledPolysList.count( aLayer ) )
return;
m_filledPolysHash[aLayer] = m_FilledPolysList.at( aLayer ).GetHash();
}
@ -797,6 +828,9 @@ protected:
int m_ZoneMinThickness; ///< Minimum thickness value in filled areas.
bool m_FilledPolysUseThickness; ///< outline of filled polygons have thickness.
/// True if isolated copper (islands) should be removed after fill (default)
bool m_removeIslands;
/** True when a zone was filled, false after deleting the filled areas. */
bool m_IsFilled;
@ -845,7 +879,7 @@ protected:
/** Segments used to fill the zone (#m_FillMode ==1 ), when fill zone by segment is used.
* In this case the segments have #m_ZoneMinThickness width.
*/
ZONE_SEGMENT_FILL m_FillSegmList;
std::map<PCB_LAYER_ID, ZONE_SEGMENT_FILL> m_FillSegmList;
/* set of filled polygons used to draw a zone as a filled area.
* from outlines (m_Poly) but unlike m_Poly these filled polygons have no hole
@ -855,10 +889,11 @@ protected:
* connecting "holes" with external main outline. In complex cases an outline
* described by m_Poly can have many filled areas
*/
SHAPE_POLY_SET m_FilledPolysList;
SHAPE_POLY_SET m_RawPolysList;
MD5_HASH m_filledPolysHash; // A hash value used in zone filling calculations
// to see if the filled areas are up to date
std::map<PCB_LAYER_ID, SHAPE_POLY_SET> m_FilledPolysList;
std::map<PCB_LAYER_ID, SHAPE_POLY_SET> m_RawPolysList;
/// A hash value used in zone filling calculations to see if the filled areas are up to date
std::map<PCB_LAYER_ID, MD5_HASH> m_filledPolysHash;
ZONE_HATCH_STYLE m_hatchStyle; // hatch style, see enum above
int m_hatchPitch; // for DIAGONAL_EDGE, distance between 2 hatch lines

View File

@ -431,7 +431,7 @@ SEARCH_RESULT GENERAL_COLLECTOR::Inspect( EDA_ITEM* testItem, void* testData )
if( zone->HitTestForCorner( m_RefPos, accuracy * 2 )
|| zone->HitTestForEdge( m_RefPos, accuracy )
|| ( testFill && zone->HitTestFilledArea( m_RefPos ) ) )
|| ( testFill && zone->HitTestFilledArea( layer, m_RefPos ) ) )
{
Append( item );
goto exit;
@ -495,7 +495,7 @@ SEARCH_RESULT GENERAL_COLLECTOR::Inspect( EDA_ITEM* testItem, void* testData )
if( zone->HitTestForCorner( m_RefPos, accuracy * 2 )
|| zone->HitTestForEdge( m_RefPos, accuracy )
|| ( testFill && zone->HitTestFilledArea( m_RefPos ) ) )
|| ( testFill && zone->HitTestFilledArea( layer, m_RefPos ) ) )
{
Append2nd( item );
goto exit;

View File

@ -181,8 +181,9 @@ bool CN_CONNECTIVITY_ALGO::Add( BOARD_ITEM* aItem )
m_itemMap[zone] = ITEM_MAP_ENTRY();
for( auto zitem : m_itemList.Add( zone ) )
m_itemMap[zone].Link(zitem);
for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
for( auto zitem : m_itemList.Add( zone, layer ) )
m_itemMap[zone].Link( zitem );
break;
}
@ -518,9 +519,11 @@ void CN_CONNECTIVITY_ALGO::PropagateNets( BOARD_COMMIT* aCommit )
}
void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( ZONE_CONTAINER* aZone, std::vector<int>& aIslands )
void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( ZONE_CONTAINER* aZone,
PCB_LAYER_ID aLayer,
std::vector<int>& aIslands )
{
if( aZone->GetFilledPolysList().IsEmpty() )
if( aZone->GetFilledPolysList( aLayer ).IsEmpty() )
return;
aIslands.clear();
@ -536,7 +539,7 @@ void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( ZONE_CONTAINER* aZone, std
{
for( auto z : *cluster )
{
if( z->Parent() == aZone )
if( z->Parent() == aZone && z->Layer() == aLayer )
{
aIslands.push_back( static_cast<CN_ZONE*>(z)->SubpolyIndex() );
}
@ -554,15 +557,23 @@ void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLAT
for ( auto& z : aZones )
{
if( !z.m_zone->GetFilledPolysList().IsEmpty() )
Add( z.m_zone );
for( PCB_LAYER_ID layer : z.m_zone->GetLayerSet().Seq() )
{
if( !z.m_zone->GetFilledPolysList( layer ).IsEmpty() )
{
Add( z.m_zone );
break;
}
}
}
m_connClusters = SearchClusters( CSM_CONNECTIVITY_CHECK );
for ( auto& zone : aZones )
{
if( zone.m_zone->GetFilledPolysList().IsEmpty() )
PCB_LAYER_ID layer = zone.m_layer;
if( zone.m_zone->GetFilledPolysList( layer ).IsEmpty() )
continue;
for( const auto& cluster : m_connClusters )
@ -571,10 +582,8 @@ void CN_CONNECTIVITY_ALGO::FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLAT
{
for( auto z : *cluster )
{
if( z->Parent() == zone.m_zone )
{
zone.m_islands.push_back( static_cast<CN_ZONE*>(z)->SubpolyIndex() );
}
if( z->Parent() == zone.m_zone && z->Layer() == layer )
zone.m_islands.push_back( static_cast<CN_ZONE*>( z )->SubpolyIndex() );
}
}
}
@ -643,10 +652,15 @@ void CN_VISITOR::checkZoneZoneConnection( CN_ZONE* aZoneA, CN_ZONE* aZoneB )
if( aZoneB == aZoneA || refParent == testedParent )
return;
if( aZoneA->Layer() != aZoneB->Layer() )
return;
if( aZoneB->Net() != aZoneA->Net() )
return; // we only test zones belonging to the same net
const auto& outline = refParent->GetFilledPolysList().COutline( aZoneA->SubpolyIndex() );
PCB_LAYER_ID layer = static_cast<PCB_LAYER_ID>( aZoneA->Layer() );
const auto& outline = refParent->GetFilledPolysList( layer ).COutline( aZoneA->SubpolyIndex() );
for( int i = 0; i < outline.PointCount(); i++ )
{
@ -658,7 +672,8 @@ void CN_VISITOR::checkZoneZoneConnection( CN_ZONE* aZoneA, CN_ZONE* aZoneB )
}
}
const auto& outline2 = testedParent->GetFilledPolysList().COutline( aZoneB->SubpolyIndex() );
const auto& outline2 =
testedParent->GetFilledPolysList( layer ).COutline( aZoneB->SubpolyIndex() );
for( int i = 0; i < outline2.PointCount(); i++ )
{

View File

@ -243,7 +243,8 @@ public:
*/
void PropagateNets( BOARD_COMMIT* aCommit = nullptr );
void FindIsolatedCopperIslands( ZONE_CONTAINER* aZone, std::vector<int>& aIslands );
void FindIsolatedCopperIslands( ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer,
std::vector<int>& aIslands );
/**
* Finds the copper islands that are not connected to a net. These are added to

View File

@ -235,7 +235,10 @@ int CONNECTIVITY_DATA::GetNetCount() const
void CONNECTIVITY_DATA::FindIsolatedCopperIslands( ZONE_CONTAINER* aZone,
std::vector<int>& aIslands )
{
// TODO(JE) ZONES
#if 0
m_connAlgo->FindIsolatedCopperIslands( aZone, aIslands );
#endif
}
void CONNECTIVITY_DATA::FindIsolatedCopperIslands( std::vector<CN_ZONE_ISOLATED_ISLAND_LIST>& aZones )

View File

@ -60,14 +60,22 @@ struct CN_DISJOINT_NET_ENTRY
VECTOR2I anchorA, anchorB;
};
/**
* A structure used for filling a copper zone on one layer.
* Multilayer zones will have one of these for each active layer.
*/
struct CN_ZONE_ISOLATED_ISLAND_LIST
{
CN_ZONE_ISOLATED_ISLAND_LIST( ZONE_CONTAINER* aZone ) :
m_zone( aZone )
CN_ZONE_ISOLATED_ISLAND_LIST( ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer ) :
m_zone( aZone ),
m_layer( aLayer )
{}
ZONE_CONTAINER* m_zone;
std::vector<int> m_islands;
ZONE_CONTAINER* m_zone;
PCB_LAYER_ID m_layer;
std::vector<int> m_islands;
};
struct RN_DYNAMIC_LINE

View File

@ -161,8 +161,8 @@ int CN_ZONE::AnchorCount() const
if( !Valid() )
return 0;
const auto zone = static_cast<const ZONE_CONTAINER*>( Parent() );
const auto& outline = zone->GetFilledPolysList().COutline( m_subpolyIndex );
const auto zone = static_cast<const ZONE_CONTAINER*>( Parent() );
const auto& outline = zone->GetFilledPolysList( m_layer ).COutline( m_subpolyIndex );
return outline.PointCount() ? 1 : 0;
}
@ -173,8 +173,8 @@ const VECTOR2I CN_ZONE::GetAnchor( int n ) const
if( !Valid() )
return VECTOR2I();
const auto zone = static_cast<const ZONE_CONTAINER*> ( Parent() );
const auto& outline = zone->GetFilledPolysList().COutline( m_subpolyIndex );
const auto zone = static_cast<const ZONE_CONTAINER*>( Parent() );
const auto& outline = zone->GetFilledPolysList( m_layer ).COutline( m_subpolyIndex );
return outline.CPoint( 0 );
}
@ -265,22 +265,22 @@ CN_ITEM* CN_LIST::Add( ARC* aArc )
return item;
}
const std::vector<CN_ITEM*> CN_LIST::Add( ZONE_CONTAINER* zone )
const std::vector<CN_ITEM*> CN_LIST::Add( ZONE_CONTAINER* zone, PCB_LAYER_ID aLayer )
{
const auto& polys = zone->GetFilledPolysList();
const auto& polys = zone->GetFilledPolysList( aLayer );
std::vector<CN_ITEM*> rv;
for( int j = 0; j < polys.OutlineCount(); j++ )
{
CN_ZONE* zitem = new CN_ZONE( zone, false, j );
const auto& outline = zone->GetFilledPolysList().COutline( j );
CN_ZONE* zitem = new CN_ZONE( zone, aLayer, false, j );
const auto& outline = zone->GetFilledPolysList( aLayer ).COutline( j );
for( int k = 0; k < outline.PointCount(); k++ )
zitem->AddAnchor( outline.CPoint( k ) );
m_items.push_back( zitem );
zitem->SetLayer( zone->GetLayer() );
zitem->SetLayer( aLayer );
addItemtoTree( zitem );
rv.push_back( zitem );
SetDirty();
@ -360,7 +360,8 @@ bool CN_ANCHOR::IsDangling() const
{
ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item->Parent() );
if( zone->HitTestFilledArea( (wxPoint) Pos() ) )
if( zone->HitTestFilledArea( static_cast<PCB_LAYER_ID>( item->Layer() ),
static_cast<wxPoint>( Pos() ) ) )
connected_count++;
}
else if( item->Parent()->HitTest( (wxPoint) Pos() ) )
@ -384,7 +385,8 @@ int CN_ANCHOR::ConnectedItemsCount() const
{
ZONE_CONTAINER* zone = static_cast<ZONE_CONTAINER*>( item->Parent() );
if( zone->HitTestFilledArea( wxPoint( Pos().x, Pos().y ) ) )
if( zone->HitTestFilledArea( static_cast<PCB_LAYER_ID>( item->Layer() ),
wxPoint( Pos().x, Pos().y ) ) )
connected_count++;
}
else if( item->Parent()->HitTest( wxPoint( Pos().x, Pos().y ) ) )

View File

@ -352,11 +352,12 @@ typedef std::shared_ptr<CN_ITEM> CN_ITEM_PTR;
class CN_ZONE : public CN_ITEM
{
public:
CN_ZONE( ZONE_CONTAINER* aParent, bool aCanChangeNet, int aSubpolyIndex ) :
CN_ZONE( ZONE_CONTAINER* aParent, PCB_LAYER_ID aLayer, bool aCanChangeNet, int aSubpolyIndex ) :
CN_ITEM( aParent, aCanChangeNet ),
m_subpolyIndex( aSubpolyIndex )
m_subpolyIndex( aSubpolyIndex ),
m_layer( aLayer )
{
SHAPE_LINE_CHAIN outline = aParent->GetFilledPolysList().COutline( aSubpolyIndex );
SHAPE_LINE_CHAIN outline = aParent->GetFilledPolysList( aLayer ).COutline( aSubpolyIndex );
outline.SetClosed( true );
outline.Simplify();
@ -396,6 +397,7 @@ private:
std::vector<VECTOR2I> m_testOutlinePoints;
std::unique_ptr<POLY_GRID_PARTITION> m_cachedPoly;
int m_subpolyIndex;
PCB_LAYER_ID m_layer;
};
class CN_LIST
@ -500,7 +502,7 @@ public:
CN_ITEM* Add( VIA* via );
const std::vector<CN_ITEM*> Add( ZONE_CONTAINER* zone );
const std::vector<CN_ITEM*> Add( ZONE_CONTAINER* zone, PCB_LAYER_ID aLayer );
};
class CN_CLUSTER

View File

@ -444,19 +444,10 @@ void DIALOG_COPPER_ZONE::OnLayerSelection( wxDataViewEvent& event )
int row = m_layers->ItemToRow( event.GetItem() );
if( m_layers->GetToggleValue( row, 0 ) )
{
wxVariant layerID;
m_layers->GetValue( layerID, row, 2 );
m_settings.m_CurrentZone_Layer = ToLAYER_ID( layerID.GetInteger() );
// Turn all other checkboxes off.
for( int ii = 0; ii < m_layers->GetItemCount(); ++ii )
{
if( ii != row )
m_layers->SetToggleValue( false, ii, 0 );
}
}
wxVariant layerID;
m_layers->GetValue( layerID, row, 2 );
m_settings.m_Layers.set( ToLAYER_ID( layerID.GetInteger() ),
m_layers->GetToggleValue( row, 0 ) );
}

View File

@ -193,21 +193,12 @@ void DIALOG_NON_COPPER_ZONES_EDITOR::OnLayerSelection( wxDataViewEvent& event )
if( event.GetColumn() != 0 )
return;
int row = m_layers->ItemToRow( event.GetItem() );
int row = m_layers->ItemToRow( event.GetItem() );
bool val = m_layers->GetToggleValue( row, 0 );
if( m_layers->GetToggleValue( row, 0 ) )
{
wxVariant layerID;
m_layers->GetValue( layerID, row, 2 );
m_settings.m_CurrentZone_Layer = ToLAYER_ID( layerID.GetInteger() );
// Turn all other checkboxes off.
for( int ii = 0; ii < m_layers->GetItemCount(); ++ii )
{
if( ii != row )
m_layers->SetToggleValue( false, ii, 0 );
}
}
wxVariant layerID;
m_layers->GetValue( layerID, row, 2 );
m_settings.m_Layers.set( ToLAYER_ID( layerID.GetInteger() ), val );
}

View File

@ -1394,8 +1394,10 @@ wxPoint DRC::GetLocation( TRACK* aTrack, ZONE_CONTAINER* aConflictZone )
{
SHAPE_POLY_SET* conflictOutline;
PCB_LAYER_ID l = aTrack->GetLayer();
if( aConflictZone->IsFilled() )
conflictOutline = const_cast<SHAPE_POLY_SET*>( &aConflictZone->GetFilledPolysList() );
conflictOutline = const_cast<SHAPE_POLY_SET*>( &aConflictZone->GetFilledPolysList( l ) );
else
conflictOutline = aConflictZone->Outline();

View File

@ -574,42 +574,46 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS
for( ZONE_CONTAINER* zone : m_pcb->Zones() )
{
if( zone->GetFilledPolysList().IsEmpty() || zone->GetIsKeepout() )
if( !( refLayerSet & zone->GetLayerSet() ).any() || zone->GetIsKeepout() )
continue;
if( !( refLayerSet & zone->GetLayerSet() ).any() )
continue;
if( zone->GetNetCode() && zone->GetNetCode() == aRefSeg->GetNetCode() )
continue;
int minClearance = aRefSeg->GetClearance( zone, &m_clearanceSource );
int widths = refSegWidth / 2;
int center2centerAllowed = minClearance + widths;
SHAPE_POLY_SET* outline = const_cast<SHAPE_POLY_SET*>( &zone->GetFilledPolysList() );
SEG::ecoord center2center_squared = outline->SquaredDistance( testSeg );
// to avoid false positive, due to rounding issues and approxiamtions
// in distance and clearance calculations, use a small threshold for distance
// (1 micron)
#define THRESHOLD_DIST Millimeter2iu( 0.001 )
if( center2center_squared + THRESHOLD_DIST < SEG::Square( center2centerAllowed ) )
for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
{
int actual = std::max( 0.0, sqrt( center2center_squared ) - widths );
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_NEAR_ZONE );
if( zone->GetFilledPolysList( layer ).IsEmpty() )
continue;
m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
m_clearanceSource,
MessageTextFromValue( userUnits(), minClearance, true ),
MessageTextFromValue( userUnits(), actual, true ) );
if( zone->GetNetCode() && zone->GetNetCode() == aRefSeg->GetNetCode() )
continue;
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefSeg, zone );
int minClearance = aRefSeg->GetClearance( zone, &m_clearanceSource );
int widths = refSegWidth / 2;
int center2centerAllowed = minClearance + widths;
SHAPE_POLY_SET* outline =
const_cast<SHAPE_POLY_SET*>( &zone->GetFilledPolysList( layer ) );
MARKER_PCB* marker = new MARKER_PCB( drcItem, GetLocation( aRefSeg, zone ) );
addMarkerToPcb( aCommit, marker );
SEG::ecoord center2center_squared = outline->SquaredDistance( testSeg );
// to avoid false positive, due to rounding issues and approxiamtions
// in distance and clearance calculations, use a small threshold for distance
// (1 micron)
#define THRESHOLD_DIST Millimeter2iu( 0.001 )
if( center2center_squared + THRESHOLD_DIST < SEG::Square( center2centerAllowed ) )
{
int actual = std::max( 0.0, sqrt( center2center_squared ) - widths );
DRC_ITEM* drcItem = new DRC_ITEM( DRCE_TRACK_NEAR_ZONE );
m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
m_clearanceSource,
MessageTextFromValue( userUnits(), minClearance, true ),
MessageTextFromValue( userUnits(), actual, true ) );
drcItem->SetErrorMessage( m_msg );
drcItem->SetItems( aRefSeg, zone );
MARKER_PCB* marker = new MARKER_PCB( drcItem, GetLocation( aRefSeg, zone ) );
addMarkerToPcb( aCommit, marker );
}
}
}
}

View File

@ -271,7 +271,7 @@ bool DRC_KEEPOUT_TESTER::checkPads( MODULE* aModule )
slotEnd += pad->GetPosition();
SEG slotSeg( slotStart, slotEnd );
SHAPE_POLY_SET* outline = const_cast<SHAPE_POLY_SET*>( &m_zone->GetFilledPolysList() );
SHAPE_POLY_SET* outline = m_zone->Outline();
SEG::ecoord center2center_sq = outline->SquaredDistance( slotSeg );
if( center2center_sq <= SEG::Square( slotWidth) )

View File

@ -447,48 +447,53 @@ bool HYPERLYNX_EXPORTER::writeNetObjects( const std::vector<BOARD_ITEM*>& aObjec
}
else if( ZONE_CONTAINER* zone = dyn_cast<ZONE_CONTAINER*>( item ) )
{
const auto layerName = m_board->GetLayerName( zone->GetLayer() );
SHAPE_POLY_SET filledShape = zone->GetFilledPolysList();
filledShape.Simplify( SHAPE_POLY_SET::PM_FAST );
for( int i = 0; i < filledShape.OutlineCount(); i++ )
for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
{
const auto& outl = filledShape.COutline( i );
const auto layerName = m_board->GetLayerName( layer );
SHAPE_POLY_SET filledShape = zone->GetFilledPolysList( layer );
auto p0 = outl.CPoint( 0 );
m_out->Print( 1, "{POLYGON T=POUR L=\"%s\" ID=%d X=%.10f Y=%.10f\n",
(const char*) layerName.c_str(), m_polyId, iu2hyp( p0.x ), iu2hyp( p0.y ) );
filledShape.Simplify( SHAPE_POLY_SET::PM_FAST );
for( int v = 0; v < outl.PointCount(); v++ )
for( int i = 0; i < filledShape.OutlineCount(); i++ )
{
m_out->Print( 2, "(LINE X=%.10f Y=%.10f)\n", iu2hyp( outl.CPoint( v ).x ),
iu2hyp( outl.CPoint( v ).y ) );
}
const auto& outl = filledShape.COutline( i );
m_out->Print( 2, "(LINE X=%.10f Y=%.10f)\n", iu2hyp( p0.x ), iu2hyp( p0.y ) );
m_out->Print( 1, "}\n" );
auto p0 = outl.CPoint( 0 );
m_out->Print( 1, "{POLYGON T=POUR L=\"%s\" ID=%d X=%.10f Y=%.10f\n",
(const char*) layerName.c_str(), m_polyId, iu2hyp( p0.x ),
iu2hyp( p0.y ) );
for( int h = 0; h < filledShape.HoleCount( i ); h++ )
{
const auto& holeShape = filledShape.CHole( i, h );
auto ph0 = holeShape.CPoint( 0 );
m_out->Print( 1, "{POLYVOID ID=%d X=%.10f Y=%.10f\n", m_polyId, iu2hyp( ph0.x ),
iu2hyp( ph0.y ) );
for( int v = 0; v < holeShape.PointCount(); v++ )
for( int v = 0; v < outl.PointCount(); v++ )
{
m_out->Print( 2, "(LINE X=%.10f Y=%.10f)\n",
iu2hyp( holeShape.CPoint( v ).x ),
iu2hyp( holeShape.CPoint( v ).y ) );
m_out->Print( 2, "(LINE X=%.10f Y=%.10f)\n", iu2hyp( outl.CPoint( v ).x ),
iu2hyp( outl.CPoint( v ).y ) );
}
m_out->Print( 2, "(LINE X=%.10f Y=%.10f)\n", iu2hyp( ph0.x ), iu2hyp( ph0.y ) );
m_out->Print( 2, "(LINE X=%.10f Y=%.10f)\n", iu2hyp( p0.x ), iu2hyp( p0.y ) );
m_out->Print( 1, "}\n" );
}
m_polyId++;
for( int h = 0; h < filledShape.HoleCount( i ); h++ )
{
const auto& holeShape = filledShape.CHole( i, h );
auto ph0 = holeShape.CPoint( 0 );
m_out->Print( 1, "{POLYVOID ID=%d X=%.10f Y=%.10f\n", m_polyId,
iu2hyp( ph0.x ), iu2hyp( ph0.y ) );
for( int v = 0; v < holeShape.PointCount(); v++ )
{
m_out->Print( 2, "(LINE X=%.10f Y=%.10f)\n",
iu2hyp( holeShape.CPoint( v ).x ),
iu2hyp( holeShape.CPoint( v ).y ) );
}
m_out->Print(
2, "(LINE X=%.10f Y=%.10f)\n", iu2hyp( ph0.x ), iu2hyp( ph0.y ) );
m_out->Print( 1, "}\n" );
}
m_polyId++;
}
}
}
}

View File

@ -1023,37 +1023,39 @@ static void export_vrml_zones( MODEL_VRML& aModel, BOARD* aPcb )
{
ZONE_CONTAINER* zone = aPcb->GetArea( ii );
VRML_LAYER* vl;
if( !GetLayer( aModel, zone->GetLayer(), &vl ) )
continue;
// fixme: this modifies the board where it shouldn't, but I don't have the time
// to clean this up - TW
if( !zone->IsFilled() )
for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
{
ZONE_FILLER filler( aPcb );
zone->SetFillMode( ZONE_FILL_MODE::POLYGONS ); // use filled polygons
filler.Fill( { zone } );
}
VRML_LAYER* vl;
const SHAPE_POLY_SET& poly = zone->GetFilledPolysList();
if( !GetLayer( aModel, layer, &vl ) )
continue;
for( int i = 0; i < poly.OutlineCount(); i++ )
{
const SHAPE_LINE_CHAIN& outline = poly.COutline( i );
int seg = vl->NewContour();
for( int j = 0; j < outline.PointCount(); j++ )
// fixme: this modifies the board where it shouldn't, but I don't have the time
// to clean this up - TW
if( !zone->IsFilled() )
{
if( !vl->AddVertex( seg, (double)outline.CPoint( j ).x * BOARD_SCALE,
-((double)outline.CPoint( j ).y * BOARD_SCALE ) ) )
throw( std::runtime_error( vl->GetError() ) );
ZONE_FILLER filler( aPcb );
zone->SetFillMode( ZONE_FILL_MODE::POLYGONS ); // use filled polygons
filler.Fill( { zone } );
}
vl->EnsureWinding( seg, false );
const SHAPE_POLY_SET& poly = zone->GetFilledPolysList( layer );
for( int i = 0; i < poly.OutlineCount(); i++ )
{
const SHAPE_LINE_CHAIN& outline = poly.COutline( i );
int seg = vl->NewContour();
for( int j = 0; j < outline.PointCount(); j++ )
{
if( !vl->AddVertex( seg, (double) outline.CPoint( j ).x * BOARD_SCALE,
-( (double) outline.CPoint( j ).y * BOARD_SCALE ) ) )
throw( std::runtime_error( vl->GetError() ) );
}
vl->EnsureWinding( seg, false );
}
}
}
}

View File

@ -1981,76 +1981,82 @@ void PCB_IO::format( ZONE_CONTAINER* aZone, int aNestLevel ) const
}
// Save the PolysList (filled areas)
const SHAPE_POLY_SET& fv = aZone->GetFilledPolysList();
newLine = 0;
if( !fv.IsEmpty() )
for( PCB_LAYER_ID layer : aZone->GetLayerSet().Seq() )
{
bool new_polygon = true;
bool is_closed = false;
const SHAPE_POLY_SET& fv = aZone->GetFilledPolysList( layer );
newLine = 0;
for( auto it = fv.CIterate(); it; ++it )
if( !fv.IsEmpty() )
{
if( new_polygon )
{
newLine = 0;
m_out->Print( aNestLevel+1, "(filled_polygon\n" );
m_out->Print( aNestLevel+2, "(pts\n" );
new_polygon = false;
is_closed = false;
}
bool new_polygon = true;
bool is_closed = false;
if( newLine == 0 )
m_out->Print( aNestLevel+3, "(xy %s %s)",
FormatInternalUnits( it->x ).c_str(),
FormatInternalUnits( it->y ).c_str() );
else
m_out->Print( 0, " (xy %s %s)",
FormatInternalUnits( it->x ) .c_str(),
FormatInternalUnits( it->y ).c_str() );
if( newLine < 4 )
for( auto it = fv.CIterate(); it; ++it )
{
newLine += 1;
}
else
{
newLine = 0;
m_out->Print( 0, "\n" );
}
if( new_polygon )
{
newLine = 0;
m_out->Print( aNestLevel + 1, "(filled_polygon\n" );
m_out->Print( aNestLevel + 2, "(layer %s)\n",
TO_UTF8( BOARD::GetStandardLayerName( layer ) ) );
m_out->Print( aNestLevel + 2, "(pts\n" );
new_polygon = false;
is_closed = false;
}
if( it.IsEndContour() )
{
is_closed = true;
if( newLine == 0 )
m_out->Print( aNestLevel + 3, "(xy %s %s)",
FormatInternalUnits( it->x ).c_str(),
FormatInternalUnits( it->y ).c_str() );
else
m_out->Print( 0, " (xy %s %s)", FormatInternalUnits( it->x ).c_str(),
FormatInternalUnits( it->y ).c_str() );
if( newLine != 0 )
if( newLine < 4 )
{
newLine += 1;
}
else
{
newLine = 0;
m_out->Print( 0, "\n" );
}
m_out->Print( aNestLevel+2, ")\n" );
m_out->Print( aNestLevel+1, ")\n" );
new_polygon = true;
if( it.IsEndContour() )
{
is_closed = true;
if( newLine != 0 )
m_out->Print( 0, "\n" );
m_out->Print( aNestLevel + 2, ")\n" );
m_out->Print( aNestLevel + 1, ")\n" );
new_polygon = true;
}
}
if( !is_closed ) // Should not happen, but...
m_out->Print( aNestLevel + 1, ")\n" );
}
if( !is_closed ) // Should not happen, but...
m_out->Print( aNestLevel+1, ")\n" );
}
// Save the filling segments list
const auto& segs = aZone->FillSegments( layer );
// Save the filling segments list
const auto& segs = aZone->FillSegments();
if( segs.size() )
{
m_out->Print( aNestLevel+1, "(fill_segments\n" );
for( ZONE_SEGMENT_FILL::const_iterator it = segs.begin(); it != segs.end(); ++it )
if( segs.size() )
{
m_out->Print( aNestLevel+2, "(pts (xy %s) (xy %s))\n",
FormatInternalUnits( wxPoint( it->A ) ).c_str(),
FormatInternalUnits( wxPoint( it->B ) ).c_str() );
}
m_out->Print( aNestLevel + 1, "(fill_segments\n" );
m_out->Print( aNestLevel + 2, "(layer %s)\n",
TO_UTF8( BOARD::GetStandardLayerName( layer ) ) );
m_out->Print( aNestLevel+1, ")\n" );
for( ZONE_SEGMENT_FILL::const_iterator it = segs.begin(); it != segs.end(); ++it )
{
m_out->Print( aNestLevel + 2, "(pts (xy %s) (xy %s))\n",
FormatInternalUnits( wxPoint( it->A ) ).c_str(),
FormatInternalUnits( wxPoint( it->B ) ).c_str() );
}
m_out->Print( aNestLevel + 1, ")\n" );
}
}
m_out->Print( aNestLevel, ")\n" );

View File

@ -70,7 +70,7 @@ class TEXTE_PCB;
//#define SEXPR_BOARD_FILE_VERSION 20200512 // page -> paper
//#define SEXPR_BOARD_FILE_VERSION 20200518 // save hole_to_hole_min
//#define SEXPR_BOARD_FILE_VERSION 20200614 // Add support for fp_rects and gr_rects
#define SEXPR_BOARD_FILE_VERSION 20200623 // Add name property to zones
#define SEXPR_BOARD_FILE_VERSION 20200623 // Multilayer zones and zone name property
#define CTL_STD_LAYER_NAMES (1 << 0) ///< Use English Standard layer names
#define CTL_OMIT_NETS (1 << 1) ///< Omit pads net names (useless in library)

View File

@ -2657,7 +2657,7 @@ void LEGACY_PLUGIN::loadZONE_CONTAINER()
makeNewOutline = end_contour;
}
zc->SetFilledPolysList( polysList );
zc->SetFilledPolysList( zc->GetLayer(), polysList );
}
else if( TESTLINE( "$FILLSEGMENTS" ) )
@ -2673,7 +2673,8 @@ void LEGACY_PLUGIN::loadZONE_CONTAINER()
BIU ex = biuParse( data, &data );
BIU ey = biuParse( data );
zc->FillSegments().push_back( SEG( VECTOR2I( sx, sy ), VECTOR2I( ex, ey ) ) );
zc->FillSegments( zc->GetLayer() )
.push_back( SEG( VECTOR2I( sx, sy ), VECTOR2I( ex, ey ) ) );
}
}

View File

@ -1077,7 +1077,9 @@ void PCB_PAINTER::draw( const MODULE* aModule, int aLayer )
void PCB_PAINTER::draw( const ZONE_CONTAINER* aZone, int aLayer )
{
if( !aZone->IsOnLayer( (PCB_LAYER_ID) aLayer ) )
PCB_LAYER_ID layer = static_cast<PCB_LAYER_ID>( aLayer );
if( !aZone->IsOnLayer( layer ) )
return;
const COLOR4D& color = m_pcbSettings.GetColor( aZone, aLayer );
@ -1119,7 +1121,7 @@ void PCB_PAINTER::draw( const ZONE_CONTAINER* aZone, int aLayer )
// Draw the filling
if( displayMode != PCB_RENDER_SETTINGS::DZ_HIDE_FILLED )
{
const SHAPE_POLY_SET& polySet = aZone->GetFilledPolysList();
const SHAPE_POLY_SET& polySet = aZone->GetFilledPolysList( layer );
if( polySet.OutlineCount() == 0 ) // Nothing to draw
return;
@ -1143,7 +1145,6 @@ void PCB_PAINTER::draw( const ZONE_CONTAINER* aZone, int aLayer )
m_gal->DrawPolygon( polySet );
}
}

View File

@ -3723,8 +3723,10 @@ ZONE_CONTAINER* PCB_PARSER::parseZONE_CONTAINER( BOARD_ITEM_CONTAINER* aParent )
wxString netnameFromfile; // the zone net name find in file
// bigger scope since each filled_polygon is concatenated in here
SHAPE_POLY_SET pts;
std::map<PCB_LAYER_ID, SHAPE_POLY_SET> pts;
bool inModule = false;
PCB_LAYER_ID filledLayer;
bool addedFilledPolygons = false;
if( dynamic_cast<MODULE*>( aParent ) ) // The zone belongs a footprint
inModule = true;
@ -3771,8 +3773,7 @@ ZONE_CONTAINER* PCB_PARSER::parseZONE_CONTAINER( BOARD_ITEM_CONTAINER* aParent )
NeedRIGHT();
break;
case T_layers: // keyword for zones that can live on a set of layer
// currently: keepout zones
case T_layers: // keyword for zones that can live on a set of layers
zone->SetLayerSet( parseBoardItemLayersAsMask() );
break;
@ -4069,17 +4070,40 @@ ZONE_CONTAINER* PCB_PARSER::parseZONE_CONTAINER( BOARD_ITEM_CONTAINER* aParent )
NeedLEFT();
token = NextTok();
if( token == T_layer )
{
filledLayer = parseBoardItemLayer();
NeedRIGHT();
token = NextTok();
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
}
else
{
filledLayer = zone->GetLayer();
}
if( token != T_pts )
Expecting( T_pts );
pts.NewOutline();
if( !pts.count( filledLayer ) )
pts[filledLayer] = SHAPE_POLY_SET();
SHAPE_POLY_SET& poly = pts.at( filledLayer );
poly.NewOutline();
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
pts.Append( parseXY() );
poly.Append( parseXY() );
}
NeedRIGHT();
addedFilledPolygons |= !poly.IsEmpty();
}
break;
@ -4094,6 +4118,22 @@ ZONE_CONTAINER* PCB_PARSER::parseZONE_CONTAINER( BOARD_ITEM_CONTAINER* aParent )
token = NextTok();
if( token == T_layer )
{
filledLayer = parseBoardItemLayer();
NeedRIGHT();
token = NextTok();
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
}
else
{
filledLayer = zone->GetLayer();
}
if( token != T_pts )
Expecting( T_pts );
@ -4102,7 +4142,7 @@ ZONE_CONTAINER* PCB_PARSER::parseZONE_CONTAINER( BOARD_ITEM_CONTAINER* aParent )
segs.push_back( segment );
}
zone->SetFillSegments( segs );
zone->SetFillSegments( filledLayer, segs );
}
break;
@ -4132,9 +4172,11 @@ ZONE_CONTAINER* PCB_PARSER::parseZONE_CONTAINER( BOARD_ITEM_CONTAINER* aParent )
zone->SetHatch( hatchStyle, hatchPitch, true );
}
if( !pts.IsEmpty() )
if( addedFilledPolygons )
{
zone->SetFilledPolysList( pts );
for( auto& pair : pts )
zone->SetFilledPolysList( pair.first, pair.second );
zone->CalculateFilledArea();
}

View File

@ -479,53 +479,63 @@ void PlotStandardLayer( BOARD *aBoard, PLOTTER* aPlotter,
// Plot all zones of the same layer & net together so we don't end up with divots where
// zones touch each other.
std::set<ZONE_CONTAINER*> plotted;
std::set<std::pair<PCB_LAYER_ID, ZONE_CONTAINER*>> plotted;
for( ZONE_CONTAINER* zone : aBoard->Zones() )
{
if( !aLayerMask[ zone->GetLayer() ] || plotted.count( zone ) )
continue;
plotted.insert( zone );
SHAPE_POLY_SET aggregateArea = zone->GetFilledPolysList();
bool needFracture = false; // If 2 or more filled areas are combined, resulting
// aggregateArea will be simplified and fractured
// (Long calculation time)
for( ZONE_CONTAINER* candidate : aBoard->Zones() )
for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
{
if( !aLayerMask[ candidate->GetLayer() ] || plotted.count( candidate ) )
auto pair = std::make_pair( layer, zone );
if( !aLayerMask[layer] || plotted.count( pair ) )
continue;
if( candidate->GetNetCode() != zone->GetNetCode() )
continue;
plotted.insert( pair );
// Merging zones of the same net can be done only for areas
// having compatible settings for drawings:
// use or not outline thickness, and if using outline thickness,
// having the same thickness
// because after merging only one outline thickness is used
if( candidate->GetFilledPolysUseThickness() != zone->GetFilledPolysUseThickness() )
// Should not happens, because usually the same option is used for filling
continue;
SHAPE_POLY_SET aggregateArea = zone->GetFilledPolysList( layer );
bool needFracture = false; // If 2 or more filled areas are combined, resulting
// aggregateArea will be simplified and fractured
// (Long calculation time)
if( zone->GetFilledPolysUseThickness() &&
( candidate->GetMinThickness() != zone->GetMinThickness() ) )
continue;
for( ZONE_CONTAINER* candidate : aBoard->Zones() )
{
if( !candidate->IsOnLayer( layer ) )
continue;
plotted.insert( candidate );
aggregateArea.Append( candidate->GetFilledPolysList() );
needFracture = true;
auto candidate_pair = std::make_pair( layer, candidate );
if( plotted.count( candidate_pair ) )
continue;
if( candidate->GetNetCode() != zone->GetNetCode() )
continue;
// Merging zones of the same net can be done only for areas
// having compatible settings for drawings:
// use or not outline thickness, and if using outline thickness,
// having the same thickness
// because after merging only one outline thickness is used
if( candidate->GetFilledPolysUseThickness() != zone->GetFilledPolysUseThickness() )
// Should not happens, because usually the same option is used for filling
continue;
if( zone->GetFilledPolysUseThickness()
&& ( candidate->GetMinThickness() != zone->GetMinThickness() ) )
continue;
plotted.insert( candidate_pair );
aggregateArea.Append( candidate->GetFilledPolysList( layer ) );
needFracture = true;
}
if( needFracture )
{
aggregateArea.Unfracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
aggregateArea.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
}
itemplotter.PlotFilledAreas( zone, aggregateArea );
}
if( needFracture )
{
aggregateArea.Unfracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
aggregateArea.Fracture( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
}
itemplotter.PlotFilledAreas( zone, aggregateArea );
}
aPlotter->EndBlock( NULL );

View File

@ -1774,8 +1774,9 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
for( ZONE_CONTAINER* zone : m_board->Zones() )
{
if( zone->HitTestFilledArea( position ) )
foundZones.push_back( zone );
for( PCB_LAYER_ID layer : LSET( zone->GetLayerSet() & lset ).Seq() )
if( zone->HitTestFilledArea( layer, position ) )
foundZones.push_back( zone );
}
std::sort( foundZones.begin(), foundZones.end(),

View File

@ -1118,7 +1118,7 @@ int PCB_EDITOR_CONTROL::ZoneDuplicate( const TOOL_EVENT& aEvent )
// offset it a bit so it can more easily be picked.
if( oldZone->GetIsKeepout() && ( oldZone->GetLayerSet() == zoneSettings.m_Layers ) )
newZone->Move( wxPoint( IU_PER_MM, IU_PER_MM ) );
else if( !oldZone->GetIsKeepout() && ( oldZone->GetLayer() == zoneSettings.m_CurrentZone_Layer ) )
else if( !oldZone->GetIsKeepout() && zoneSettings.m_Layers.test( oldZone->GetLayer() ) )
newZone->Move( wxPoint( IU_PER_MM, IU_PER_MM ) );
commit.Add( newZone.release() );

View File

@ -1560,25 +1560,21 @@ bool SELECTION_TOOL::Selectable( const BOARD_ITEM* aItem, bool checkVisibilityOn
if( zoneInFootprint && !m_editModules && !checkVisibilityOnly )
return false;
// Keepout zones can exist on multiple layers!
// zones can exist on multiple layers!
{
auto* zone = static_cast<const ZONE_CONTAINER*>( aItem );
auto* zone = static_cast<const ZONE_CONTAINER*>( aItem );
auto zoneLayers = zone->GetLayerSet().Seq();
if( zone->GetIsKeepout() )
for( unsigned int i = 0; i < zoneLayers.size(); i++ )
{
auto zoneLayers = zone->GetLayerSet().Seq();
for( unsigned int i = 0; i < zoneLayers.size(); i++ )
if( board()->IsLayerVisible( zoneLayers[i] ) )
{
if( board()->IsLayerVisible( zoneLayers[i] ) )
{
return true;
}
return true;
}
// No active layers selected!
return false;
}
// No active layers selected!
return false;
}
}
break;

View File

@ -58,7 +58,7 @@ std::unique_ptr<ZONE_CONTAINER> ZONE_CREATE_HELPER::createNewZone( bool aKeepout
// Get the current default settings for zones
ZONE_SETTINGS zoneInfo = frame.GetZoneSettings();
zoneInfo.m_CurrentZone_Layer = m_params.m_layer;
zoneInfo.m_Layers.reset().set( m_params.m_layer ); // TODO(JE) multilayer defaults?
zoneInfo.m_NetcodeSelection =
board.GetHighLightNetCodes().empty() ? -1 : *board.GetHighLightNetCodes().begin();
zoneInfo.SetIsKeepout( m_params.m_keepout );
@ -87,7 +87,8 @@ std::unique_ptr<ZONE_CONTAINER> ZONE_CREATE_HELPER::createNewZone( bool aKeepout
dialogResult = InvokeKeepoutAreaEditor( &frame, &zoneInfo );
else
{
if( IsCopperLayer( zoneInfo.m_CurrentZone_Layer ) )
// TODO(JE) combine these dialogs?
if( ( zoneInfo.m_Layers & LSET::AllCuMask() ).any() )
dialogResult = InvokeCopperZonesEditor( &frame, &zoneInfo );
else
dialogResult = InvokeNonCopperZonesEditor( &frame, &zoneInfo );

View File

@ -130,10 +130,13 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
// calculate the hash value for filled areas. it will be used later
// to know if the current filled areas are up to date
zone->BuildHashValue();
for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
{
zone->BuildHashValue( layer );
// Add the zone to the list of zones to test or refill
toFill.emplace_back( CN_ZONE_ISOLATED_ISLAND_LIST(zone) );
// Add the zone to the list of zones to test or refill
toFill.emplace_back( CN_ZONE_ISOLATED_ISLAND_LIST( zone, layer ) );
}
// Remove existing fill first to prevent drawing invalid polygons
// on some platforms
@ -151,13 +154,16 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
for( size_t i = nextItem++; i < toFill.size(); i = nextItem++ )
{
ZONE_CONTAINER* zone = toFill[i].m_zone;
zone->SetFilledPolysUseThickness( filledPolyWithOutline );
SHAPE_POLY_SET rawPolys, finalPolys;
fillSingleZone( zone, rawPolys, finalPolys );
PCB_LAYER_ID layer = toFill[i].m_layer;
ZONE_CONTAINER* zone = toFill[i].m_zone;
zone->SetRawPolysList( rawPolys );
zone->SetFilledPolysList( finalPolys );
zone->SetFilledPolysUseThickness( filledPolyWithOutline );
SHAPE_POLY_SET rawPolys, finalPolys;
fillSingleZone( zone, layer, rawPolys, finalPolys );
zone->SetRawPolysList( layer, rawPolys );
zone->SetFilledPolysList( layer, finalPolys );
zone->SetIsFilled( true );
if( m_progressReporter )
@ -207,14 +213,13 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
for( auto& zone : toFill )
{
std::sort( zone.m_islands.begin(), zone.m_islands.end(), std::greater<int>() );
SHAPE_POLY_SET poly = zone.m_zone->GetFilledPolysList();
SHAPE_POLY_SET poly = zone.m_zone->GetFilledPolysList( zone.m_layer );
// 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( zone.m_zone->GetNetCode() > 0 && zone.m_zone->GetRemoveIslands() )
{
// solid areas outside the board cutouts are also removed, because they are usually
// insulated islands
// solid areas outside the board cutouts are also removed, because they are usually insulated islands
for( auto idx : zone.m_islands )
{
poly.DeletePolygon( idx );
@ -231,20 +236,20 @@ bool ZONE_FILLER::Fill( const std::vector<ZONE_CONTAINER*>& aZones, bool aCheck
{
for( int idx = 0; idx < poly.OutlineCount(); )
{
if( poly.Polygon( idx ).empty() ||
!m_boardOutline.Contains( poly.Polygon( idx ).front().CPoint( 0 ) ) )
if( poly.Polygon( idx ).empty()
|| !m_boardOutline.Contains( poly.Polygon( idx ).front().CPoint( 0 ) ) )
{
poly.DeletePolygon( idx );
}
else
idx++;
idx++;
}
}
zone.m_zone->SetFilledPolysList( poly );
zone.m_zone->SetFilledPolysList( zone.m_layer, poly );
zone.m_zone->CalculateFilledArea();
if( aCheck && zone.m_zone->GetHashValue() != poly.GetHash() )
if( aCheck && zone.m_zone->GetHashValue( zone.m_layer ) != poly.GetHash() )
outOfDate = true;
}
@ -468,7 +473,8 @@ void ZONE_FILLER::addKnockout( BOARD_ITEM* aItem, int aGap, bool aIgnoreLineWidt
* Removes thermal reliefs from the shape for any pads connected to the zone. Does NOT add
* in spokes, which must be done later.
*/
void ZONE_FILLER::knockoutThermalReliefs( const ZONE_CONTAINER* aZone, SHAPE_POLY_SET& aFill )
void ZONE_FILLER::knockoutThermalReliefs( const ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer,
SHAPE_POLY_SET& aFill )
{
SHAPE_POLY_SET holes;
@ -488,7 +494,7 @@ void ZONE_FILLER::knockoutThermalReliefs( const ZONE_CONTAINER* aZone, SHAPE_POL
// If the pad isn't on the current layer but has a hole, knock out a thermal relief
// for the hole.
if( !pad->IsOnLayer( aZone->GetLayer() ) )
if( !pad->IsOnLayer( aLayer ) )
{
if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 )
continue;
@ -510,7 +516,8 @@ void ZONE_FILLER::knockoutThermalReliefs( const ZONE_CONTAINER* aZone, SHAPE_POL
* Removes clearance from the shape for copper items which share the zone's layer but are
* not connected to it.
*/
void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, SHAPE_POLY_SET& aHoles )
void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer,
SHAPE_POLY_SET& aHoles )
{
static DRAWSEGMENT dummyEdge;
dummyEdge.SetLayer( Edge_Cuts );
@ -543,7 +550,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, SHAPE_
{
for( auto pad : module->Pads() )
{
if( !pad->IsOnLayer( aZone->GetLayer() ) )
if( !pad->IsOnLayer( aLayer ) )
{
if( pad->GetDrillSize().x == 0 && pad->GetDrillSize().y == 0 )
continue;
@ -576,7 +583,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, SHAPE_
//
for( auto track : m_board->Tracks() )
{
if( !track->IsOnLayer( aZone->GetLayer() ) )
if( !track->IsOnLayer( aLayer ) )
continue;
if( track->GetNetCode() == aZone->GetNetCode() && ( aZone->GetNetCode() != 0) )
@ -597,7 +604,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, SHAPE_
[&]( BOARD_ITEM* aItem )
{
// A item on the Edge_Cuts is always seen as on any layer:
if( !aItem->IsOnLayer( aZone->GetLayer() ) && !aItem->IsOnLayer( Edge_Cuts ) )
if( !aItem->IsOnLayer( aLayer ) && !aItem->IsOnLayer( Edge_Cuts ) )
return;
if( aItem->GetBoundingBox().Intersects( zone_boundingbox ) )
@ -627,7 +634,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, SHAPE_
{
// If the zones share no common layers
if( !aZone->CommonLayerExists( zone->GetLayerSet() ) )
if( !zone->GetLayerSet().test( aLayer ) )
continue;
if( !zone->GetIsKeepout() && zone->GetPriority() <= aZone->GetPriority() )
@ -668,7 +675,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, SHAPE_
* 5 - Removes unconnected copper islands, deleting any affected spokes
* 6 - Adds in the remaining spokes
*/
void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone,
void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer,
const SHAPE_POLY_SET& aSmoothedOutline,
std::set<VECTOR2I>* aPreserveCorners,
SHAPE_POLY_SET& aRawPolys,
@ -709,17 +716,17 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone,
if( s_DumpZonesWhenFilling )
dumper->BeginGroup( "clipper-zone" );
knockoutThermalReliefs( aZone, aRawPolys );
knockoutThermalReliefs( aZone, aLayer, aRawPolys );
if( s_DumpZonesWhenFilling )
dumper->Write( &aRawPolys, "solid-areas-minus-thermal-reliefs" );
buildCopperItemClearances( aZone, clearanceHoles );
buildCopperItemClearances( aZone, aLayer, clearanceHoles );
if( s_DumpZonesWhenFilling )
dumper->Write( &aRawPolys, "clearance holes" );
buildThermalSpokes( aZone, thermalSpokes );
buildThermalSpokes( aZone, aLayer, thermalSpokes );
// Create a temporary zone that we can hit-test spoke-ends against. It's only temporary
// because the "real" subtract-clearance-holes has to be done after the spokes are added.
@ -778,7 +785,7 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone,
// Now remove the non filled areas due to the hatch pattern
if( aZone->GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN )
addHatchFillTypeOnZone( aZone, aRawPolys );
addHatchFillTypeOnZone( aZone, aLayer, aRawPolys );
if( s_DumpZonesWhenFilling )
dumper->Write( &aRawPolys, "solid-areas-after-hatching" );
@ -817,8 +824,8 @@ void ZONE_FILLER::computeRawFilledArea( const ZONE_CONTAINER* aZone,
* The solid areas can be more than one on copper layers, and do not have holes
* ( holes are linked by overlapping segments to the main outline)
*/
bool ZONE_FILLER::fillSingleZone( ZONE_CONTAINER* aZone, SHAPE_POLY_SET& aRawPolys,
SHAPE_POLY_SET& aFinalPolys )
bool ZONE_FILLER::fillSingleZone( ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer,
SHAPE_POLY_SET& aRawPolys, SHAPE_POLY_SET& aFinalPolys )
{
SHAPE_POLY_SET smoothedPoly;
std::set<VECTOR2I> colinearCorners;
@ -834,7 +841,8 @@ bool ZONE_FILLER::fillSingleZone( ZONE_CONTAINER* aZone, SHAPE_POLY_SET& aRawPol
if( aZone->IsOnCopperLayer() )
{
computeRawFilledArea( aZone, smoothedPoly, &colinearCorners, aRawPolys, aFinalPolys );
computeRawFilledArea(
aZone, aLayer, smoothedPoly, &colinearCorners, aRawPolys, aFinalPolys );
}
else
{
@ -852,7 +860,7 @@ bool ZONE_FILLER::fillSingleZone( ZONE_CONTAINER* aZone, SHAPE_POLY_SET& aRawPol
// Remove the non filled areas due to the hatch pattern
if( aZone->GetFillMode() == ZONE_FILL_MODE::HATCH_PATTERN )
addHatchFillTypeOnZone( aZone, smoothedPoly );
addHatchFillTypeOnZone( aZone, aLayer, smoothedPoly );
// Re-inflate after pruning of areas that don't meet minimum-width criteria
if( aZone->GetFilledPolysUseThickness() )
@ -877,7 +885,7 @@ bool ZONE_FILLER::fillSingleZone( ZONE_CONTAINER* aZone, SHAPE_POLY_SET& aRawPol
/**
* Function buildThermalSpokes
*/
void ZONE_FILLER::buildThermalSpokes( const ZONE_CONTAINER* aZone,
void ZONE_FILLER::buildThermalSpokes( const ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer,
std::deque<SHAPE_LINE_CHAIN>& aSpokesList )
{
auto zoneBB = aZone->GetBoundingBox();
@ -898,7 +906,7 @@ void ZONE_FILLER::buildThermalSpokes( const ZONE_CONTAINER* aZone,
continue;
// We currently only connect to pads, not pad holes
if( !pad->IsOnLayer( aZone->GetLayer() ) )
if( !pad->IsOnLayer( aLayer ) )
continue;
int thermalReliefGap = aZone->GetThermalReliefGap( pad );
@ -995,7 +1003,8 @@ void ZONE_FILLER::buildThermalSpokes( const ZONE_CONTAINER* aZone,
}
void ZONE_FILLER::addHatchFillTypeOnZone( const ZONE_CONTAINER* aZone, SHAPE_POLY_SET& aRawPolys )
void ZONE_FILLER::addHatchFillTypeOnZone( const ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer,
SHAPE_POLY_SET& aRawPolys )
{
// Build grid:

View File

@ -51,9 +51,11 @@ private:
void addKnockout( BOARD_ITEM* aItem, int aGap, bool aIgnoreLineWidth, SHAPE_POLY_SET& aHoles );
void knockoutThermalReliefs( const ZONE_CONTAINER* aZone, SHAPE_POLY_SET& aFill );
void knockoutThermalReliefs( const ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer,
SHAPE_POLY_SET& aFill );
void buildCopperItemClearances( const ZONE_CONTAINER* aZone, SHAPE_POLY_SET& aHoles );
void buildCopperItemClearances( const ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer,
SHAPE_POLY_SET& aHoles );
/**
* Function computeRawFilledArea
@ -66,7 +68,7 @@ private:
* filled copper area polygon (without clearance areas
* @param aPcb: the current board
*/
void computeRawFilledArea( const ZONE_CONTAINER* aZone,
void computeRawFilledArea( const ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer,
const SHAPE_POLY_SET& aSmoothedOutline,
std::set<VECTOR2I>* aPreserveCorners,
SHAPE_POLY_SET& aRawPolys, SHAPE_POLY_SET& aFinalPolys );
@ -75,7 +77,8 @@ private:
* Function buildThermalSpokes
* Constructs a list of all thermal spokes for the given zone.
*/
void buildThermalSpokes( const ZONE_CONTAINER* aZone, std::deque<SHAPE_LINE_CHAIN>& aSpokes );
void buildThermalSpokes( const ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer,
std::deque<SHAPE_LINE_CHAIN>& aSpokes );
/**
* Build the filled solid areas polygons from zone outlines (stored in m_Poly)
@ -91,7 +94,7 @@ private:
* by aZone->GetMinThickness() / 2 to be drawn with a outline thickness = aZone->GetMinThickness()
* aFinalPolys are polygons that will be drawn on screen and plotted
*/
bool fillSingleZone( ZONE_CONTAINER* aZone, SHAPE_POLY_SET& aRawPolys,
bool fillSingleZone( ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer, SHAPE_POLY_SET& aRawPolys,
SHAPE_POLY_SET& aFinalPolys );
/**
@ -101,7 +104,8 @@ private:
* @param aRawPolys: A reference to a SHAPE_POLY_SET buffer containing the initial
* filled areas, and after adding the grid pattern, the modified filled areas with holes
*/
void addHatchFillTypeOnZone( const ZONE_CONTAINER* aZone, SHAPE_POLY_SET& aRawPolys );
void addHatchFillTypeOnZone( const ZONE_CONTAINER* aZone, PCB_LAYER_ID aLayer,
SHAPE_POLY_SET& aRawPolys );
BOARD* m_board;
SHAPE_POLY_SET m_boardOutline; // The board outlines, if exists

View File

@ -57,11 +57,12 @@ ZONE_SETTINGS::ZONE_SETTINGS()
m_HatchFillTypeSmoothingLevel = 0; // Grid pattern smoothing type. 0 = no smoothing
m_HatchFillTypeSmoothingValue = 0.1; // Grid pattern chamfer value relative to the gap value
m_NetcodeSelection = 0; // Net code selection for the current zone
m_CurrentZone_Layer = F_Cu; // Layer used to create the current zone
m_Zone_HatchingStyle =
ZONE_HATCH_STYLE::DIAGONAL_EDGE; // Option to show the zone area (outlines only,
//short hatches or full hatches
m_Layers.reset().set( F_Cu );
// thickness of the gap in thermal reliefs:
m_ThermalReliefGap = Mils2iu( ZONE_THERMAL_RELIEF_GAP_MIL );
// thickness of the copper bridge in thermal reliefs:
@ -74,6 +75,8 @@ ZONE_SETTINGS::ZONE_SETTINGS()
m_cornerSmoothingType = SMOOTHING_NONE;
m_cornerRadius = 0;
m_removeIslands = true;
SetIsKeepout( false );
SetDoNotAllowCopperPour( false );
SetDoNotAllowVias( true );
@ -108,8 +111,8 @@ ZONE_SETTINGS& ZONE_SETTINGS::operator << ( const ZONE_CONTAINER& aSource )
m_keepoutDoNotAllowPads = aSource.GetDoNotAllowPads();
m_keepoutDoNotAllowFootprints = aSource.GetDoNotAllowFootprints();
m_Zone_45_Only = aSource.GetHV45();
m_removeIslands = aSource.GetRemoveIslands();
m_CurrentZone_Layer = aSource.GetLayer();
m_Layers = aSource.GetLayerSet();
return *this;
@ -138,21 +141,15 @@ void ZONE_SETTINGS::ExportSetting( ZONE_CONTAINER& aTarget, bool aFullExport ) c
aTarget.SetDoNotAllowPads( GetDoNotAllowPads() );
aTarget.SetDoNotAllowFootprints( GetDoNotAllowFootprints() );
aTarget.SetHV45( m_Zone_45_Only );
aTarget.SetRemoveIslands( GetRemoveIslands() );
if( aFullExport )
{
aTarget.SetPriority( m_ZonePriority );
aTarget.SetLayerSet( m_Layers );
// Keepout zones can have multiple layers and have no net
if( m_isKeepout )
{
aTarget.SetLayerSet( m_Layers );
}
else
{
if( !m_isKeepout )
aTarget.SetNetCode( m_NetcodeSelection );
aTarget.SetLayer( m_CurrentZone_Layer );
}
}
// call SetHatch last, because hatch lines will be rebuilt,
@ -214,7 +211,7 @@ void ZONE_SETTINGS::SetupLayersList( wxDataViewListCtrl* aList, PCB_BASE_FRAME*
row.push_back( wxVariant( wxString::Format( "%i", layerID ) ) );
aList->AppendItem( row );
if( m_CurrentZone_Layer == layerID )
if( m_Layers.test( layerID ) )
aList->SetToggleValue( true, (unsigned) aList->GetItemCount() - 1, 0 );
}

View File

@ -83,9 +83,7 @@ public:
int m_NetcodeSelection; ///< Net code selection for the current zone
LSET m_Layers;
PCB_LAYER_ID m_CurrentZone_Layer; ///< Layer used to create the current zone
LSET m_Layers; ///< Layers that this zone exists on
/// Option to show the zone area (outlines only, short hatches or full hatches
ZONE_HATCH_STYLE m_Zone_HatchingStyle;
@ -114,6 +112,7 @@ private:
bool m_keepoutDoNotAllowPads;
bool m_keepoutDoNotAllowFootprints;
bool m_removeIslands;
public:
ZONE_SETTINGS();
@ -181,6 +180,9 @@ public:
void SetDoNotAllowTracks( bool aEnable ) { m_keepoutDoNotAllowTracks = aEnable; }
void SetDoNotAllowPads( bool aEnable ) { m_keepoutDoNotAllowPads = aEnable; }
void SetDoNotAllowFootprints( bool aEnable ) { m_keepoutDoNotAllowFootprints = aEnable; }
const bool GetRemoveIslands() const { return m_removeIslands; }
void SetRemoveIslands( bool aRemove ) { m_removeIslands = aRemove; }
};

View File

@ -236,12 +236,18 @@ int polygon_triangulation_main( int argc, char *argv[] )
areaId = zonesToTriangulate.fetch_add( 1 ) )
{
auto zone = brd->GetArea( areaId );
SHAPE_POLY_SET poly = zone->GetFilledPolysList();
poly.CacheTriangulation();
// NOTE: this could be refactored to do multiple layers from the same zone in
// parallel, but since the test case doesn't have any of these, I'm not bothering
// to do that right now.
for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
{
SHAPE_POLY_SET poly = zone->GetFilledPolysList( layer );
(void) poly;
printf("zone %zu/%d\n", ( areaId + 1 ), brd->GetAreaCount() );
poly.CacheTriangulation();
(void) poly;
printf( "zone %zu/%d\n", ( areaId + 1 ), brd->GetAreaCount() );
#if 0
PROF_COUNTER unfrac("unfrac");
poly.Unfracture( SHAPE_POLY_SET::PM_FAST );
@ -255,6 +261,7 @@ int polygon_triangulation_main( int argc, char *argv[] )
}
triangulate.Show();
#endif
}
}
threadsFinished++;