7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2025-04-18 20:59:17 +00:00

Reorganize layer numbering

F_Cu = 0
B_Cu = 2
Remaining internal copper layers are even and incrementing

Non-copper layers are odd and incrementing.

This means that we can no longer do things like:
for( PCB_LAYER_ID layer = F_Cu; layer <= B_Cu; ++layer)
Instead, we have the class LAYER_RANGE:
for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu) )

Similarly, gt/lt tests should not refer to the integer value of the
layer.  We have functions such as IsCopperLayer to test whether a layer
is copper or not.

When using the connectivity RTree, the third dimension is layer, so we
provide B_Cu with the special INT_MAX value, ensuring that elements
between F_Cu and B_Cu will be identified.  There is a new, special
function GetBoardLayer() for interfacing with CN_ITEMS

Similarly, PNS layers remain unchanged and sequential.  A set of
interface functions is provided to map PNS layers to Board layers and
back.  This allows the PNS_LAYER_RANGE to function as expected
This commit is contained in:
Seth Hillbrand 2024-07-21 10:49:18 -07:00
parent b99a43bec2
commit 5e0abadb23
107 changed files with 6682 additions and 1105 deletions
3d-viewer
common
gerbview
include
kicad/cli
pcbnew
qa
thirdparty

View File

@ -31,6 +31,7 @@
#include <3d_rendering/raytracing/shapes2D/polygon_2d.h>
#include <board.h>
#include <dialogs/dialog_color_picker.h>
#include <layer_range.h>
#include <3d_math.h>
#include "3d_fastmath.h"
#include <geometry/geometry_utils.h>
@ -434,14 +435,16 @@ void BOARD_ADAPTER::InitSettings( REPORTER* aStatusReporter, REPORTER* aWarningR
// Top = Bottom - m_copperThickness
unsigned int layer;
LSET copperLayers = LSET::AllCuMask();
for( layer = 0; layer < m_copperLayersCount; ++layer )
for( auto layer : LAYER_RANGE( F_Cu, B_Cu, m_copperLayersCount ) )
{
// This approximates internal layer positions (because we're treating all the dielectric
// layers as having the same thickness). But we don't render them anyway so it doesn't
// really matter.
m_layerZcoordBottom[layer] = m_boardBodyThickness3DU / 2.0f -
(m_boardBodyThickness3DU * layer / (m_copperLayersCount - 1) );
m_layerZcoordBottom[layer] = m_boardBodyThickness3DU / 2.0f
- ( m_boardBodyThickness3DU * static_cast<int>( layer )
/ ( 2 * ( m_copperLayersCount - 1 ) ) );
if( layer < (m_copperLayersCount / 2) )
m_layerZcoordTop[layer] = m_layerZcoordBottom[layer] + m_frontCopperThickness3DU;
@ -452,20 +455,13 @@ void BOARD_ADAPTER::InitSettings( REPORTER* aStatusReporter, REPORTER* aWarningR
#define layerThicknessMargin 1.1
const float zpos_offset = m_nonCopperLayerThickness3DU * layerThicknessMargin;
// Fill remaining unused copper layers and back layer zpos with -m_boardBodyThickness / 2.0
for( ; layer < MAX_CU_LAYERS; layer++ )
{
m_layerZcoordBottom[layer] = -( m_boardBodyThickness3DU / 2.0f );
m_layerZcoordTop[layer] = m_layerZcoordBottom[layer] - m_backCopperThickness3DU;
}
// This is the top of the copper layer thickness.
const float zpos_copperTop_back = m_layerZcoordTop[B_Cu];
const float zpos_copperTop_front = m_layerZcoordTop[F_Cu];
// calculate z position for each non copper layer
// Solder mask and Solder paste have the same Z position
for( int layer_id = MAX_CU_LAYERS; layer_id < PCB_LAYER_ID_COUNT; ++layer_id )
for( PCB_LAYER_ID layer_id : { B_Adhes, B_Mask, B_Paste, F_Adhes, F_Mask, F_Paste, B_SilkS, F_SilkS } )
{
float zposTop;
float zposBottom;
@ -513,12 +509,10 @@ void BOARD_ADAPTER::InitSettings( REPORTER* aStatusReporter, REPORTER* aWarningR
break;
default:
zposTop = zpos_copperTop_front + (layer_id - MAX_CU_LAYERS + 3.0f) * zpos_offset;
zposBottom = zposTop - m_nonCopperLayerThickness3DU;
break;
}
m_layerZcoordTop[layer_id] = zposTop;
m_layerZcoordTop[layer_id] = zposTop;
m_layerZcoordBottom[layer_id] = zposBottom;
}
@ -957,9 +951,17 @@ bool BOARD_ADAPTER::createBoardPolygon( wxString* aErrorMsg )
float BOARD_ADAPTER::GetFootprintZPos( bool aIsFlipped ) const
{
if( aIsFlipped )
return m_layerZcoordBottom[B_Paste];
{
if( auto it = m_layerZcoordBottom.find( B_Paste ); it != m_layerZcoordBottom.end() )
return it->second;
}
else
return m_layerZcoordTop[F_Paste];
{
if( auto it = m_layerZcoordTop.find( F_Paste ); it != m_layerZcoordTop.end() )
return it->second;
}
return 0.0;
}

View File

@ -236,7 +236,12 @@ public:
*/
float GetLayerTopZPos( PCB_LAYER_ID aLayerId ) const noexcept
{
return m_layerZcoordTop[aLayerId];
auto it = m_layerZcoordTop.find( aLayerId );
if( it != m_layerZcoordTop.end() )
return it->second;
else
return -( m_boardBodyThickness3DU / 2.0f );
}
/**
@ -247,7 +252,12 @@ public:
*/
float GetLayerBottomZPos( PCB_LAYER_ID aLayerId ) const noexcept
{
return m_layerZcoordBottom[aLayerId];
auto it = m_layerZcoordBottom.find( aLayerId );
if( it != m_layerZcoordBottom.end() )
return it->second;
else
return -( m_boardBodyThickness3DU / 2.0f ) - m_backCopperThickness3DU;
}
/**
@ -488,11 +498,11 @@ private:
double m_biuTo3Dunits; ///< Scale factor to convert board internal units
///< to 3D units normalized between -1.0 and 1.0.
std::array<float, PCB_LAYER_ID_COUNT> m_layerZcoordTop; ///< Top (End) Z position of each
///< layer in 3D units.
std::map<PCB_LAYER_ID, float> m_layerZcoordTop; ///< Top (End) Z position of each
///< layer in 3D units.
std::array<float, PCB_LAYER_ID_COUNT> m_layerZcoordBottom; ///< Bottom (Start) Z position of
///< each layer in 3D units.
std::map<PCB_LAYER_ID, float> m_layerZcoordBottom; ///< Bottom (Start) Z position of
///< each layer in 3D units.
float m_frontCopperThickness3DU;
float m_backCopperThickness3DU;

View File

@ -38,6 +38,7 @@
#include <board_design_settings.h>
#include <board.h>
#include <footprint.h>
#include <layer_range.h>
#include <lset.h>
#include <pad.h>
#include <pcb_text.h>
@ -162,9 +163,6 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
int64_t start_Time = stats_startCopperLayersTime;
#endif
PCB_LAYER_ID cu_seq[MAX_CU_LAYERS];
LSET cu_set = LSET::AllCuMask( m_copperLayersCount );
EDA_3D_VIEWER_SETTINGS::RENDER_SETTINGS& cfg = m_Cfg->m_Render;
std::bitset<LAYER_3D_END> visibilityFlags = GetVisibleLayers();
@ -220,10 +218,7 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
layer_ids.clear();
layer_ids.reserve( m_copperLayersCount );
for( unsigned i = 0; i < arrayDim( cu_seq ); ++i )
cu_seq[i] = ToLAYER_ID( B_Cu - i );
for( PCB_LAYER_ID layer : cu_set.Seq( cu_seq, arrayDim( cu_seq ) ) )
for( PCB_LAYER_ID layer : LAYER_RANGE( B_Cu, F_Cu, m_copperLayersCount ) )
{
if( !Is3dLayerEnabled( layer, visibilityFlags ) ) // Skip non enabled layers
continue;
@ -795,7 +790,7 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
// draw graphic items, on technical layers
static const PCB_LAYER_ID techLayerList[] = {
LSEQ techLayerList = LSET::AllNonCuMask().Seq( {
B_Adhes,
F_Adhes,
B_Paste,
@ -810,7 +805,7 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
Cmts_User,
Eco1_User,
Eco2_User
};
} );
std::bitset<LAYER_3D_END> enabledFlags = visibilityFlags;
@ -820,7 +815,7 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
enabledFlags.set( LAYER_3D_SOLDERMASK_BOTTOM );
}
for( PCB_LAYER_ID layer : LSET::AllNonCuMask().Seq( techLayerList, arrayDim( techLayerList ) ) )
for( PCB_LAYER_ID layer : techLayerList )
{
if( aStatusReporter )
aStatusReporter->Report( wxString::Format( _( "Build Tech layer %d" ), (int) layer ) );

View File

@ -604,7 +604,8 @@ bool RENDER_3D_OPENGL::Redraw( bool aIsMoving, REPORTER* aStatusReporter,
if( layerFlags.test( LAYER_3D_BOARD ) && m_boardAdapter.m_BoardBodyColor.a > opacity_min )
{
if( layer > F_Cu && layer < B_Cu )
// B_Cu is layer 2 and all inner layers are higher values
if( layer > B_Cu )
continue;
}

View File

@ -280,6 +280,7 @@ target_include_directories( kicommon
PUBLIC
.
${CMAKE_BINARY_DIR}
$<TARGET_PROPERTY:dynamic_bitset,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:magic_enum,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:pegtl,INTERFACE_INCLUDE_DIRECTORIES>
$<TARGET_PROPERTY:expected,INTERFACE_INCLUDE_DIRECTORIES>

View File

@ -102,6 +102,10 @@ wxString LayerName( int aLayer )
case PCB_LAYER_ID::User_7: return wxT( "User.7" );
case PCB_LAYER_ID::User_8: return wxT( "User.8" );
case PCB_LAYER_ID::User_9: return wxT( "User.9" );
case 57: return wxT( "User.10" );
case 59: return wxT( "User.11" );
case 61: return wxT( "User.12" );
case 63: return wxT( "User.13" );
// Rescue
case PCB_LAYER_ID::Rescue: return _( "Rescue" );

View File

@ -28,9 +28,11 @@
#include <cstdarg>
#include <iostream> // for string, endl, basic_ost...
#include <cstddef> // for size_t
#include <map>
#include <core/arraydim.h>
#include <layer_ids.h> // for PCB_LAYER_ID
#include <layer_range.h>
#include <lseq.h>
#include <macros.h> // for arrayDim
#include <wx/debug.h> // for wxASSERT, wxASSERT_MSG
@ -55,49 +57,128 @@ LSET::LSET( const LSEQ& aSeq ) :
}
LSET::LSET( const LAYER_RANGE& aRange )
{
for( PCB_LAYER_ID layer : aRange )
set( layer );
}
int LSET::LayerCount( PCB_LAYER_ID aStart, PCB_LAYER_ID aEnd, int aCopperLayerCount )
{
int start = aStart;
int end = aEnd;
// Both layers need to be copper
wxCHECK( IsCopperLayer( aStart ) && IsCopperLayer( aEnd ), aCopperLayerCount );
if( aStart == B_Cu )
std::swap( start, end );
if( aStart == aEnd )
return 1;
if( aStart == F_Cu )
{
if ( aEnd == B_Cu )
return aCopperLayerCount;
else
return ( end - start ) / 2 - 1;
}
else if ( aEnd == B_Cu )
{
// Add 1 for the B_Cu layer
return aCopperLayerCount - start / 2 + 1;
}
return ( end - start ) / 2;
}
int LSET::NameToLayer( wxString& aName )
{
std::map<wxString, PCB_LAYER_ID> layerMap = {
{ "F.Cu", F_Cu },
{ "B.Cu", B_Cu },
{ "F.Adhes", F_Adhes },
{ "B.Adhes", B_Adhes },
{ "F.Paste", F_Paste },
{ "B.Paste", B_Paste },
{ "F.SilkS", F_SilkS },
{ "B.SilkS", B_SilkS },
{ "F.Mask", F_Mask },
{ "B.Mask", B_Mask },
{ "Dwgs.User", Dwgs_User },
{ "Cmts.User", Cmts_User },
{ "Eco1.User", Eco1_User },
{ "Eco2.User", Eco2_User },
{ "Edge.Cuts", Edge_Cuts },
{ "Margin", Margin },
{ "F.CrtYd", F_CrtYd },
{ "B.CrtYd", B_CrtYd },
{ "F.Fab", F_Fab },
{ "B.Fab", B_Fab },
{ "Rescue", Rescue },
{ "B.Cu", B_Cu },
};
if( auto it = layerMap.find( aName ); it != layerMap.end() )
return static_cast<int>( it->second );
if( aName.StartsWith( "User." ) )
{
long offset;
if( aName.Mid( 5 ).ToLong( &offset ) && offset > 0 )
return static_cast<int>( User_1 ) + ( offset - 1 ) * 2;
}
if( aName.StartsWith( "In" ) )
{
long offset;
wxString str_num = aName.Mid( 2 );
str_num.RemoveLast( 3 ); // Removes .Cu
if( str_num.ToLong( &offset ) && offset > 0 )
return static_cast<int>( In1_Cu ) + ( offset - 1 ) * 2;
}
return -1;
}
bool LSET::IsBetween( PCB_LAYER_ID aStart, PCB_LAYER_ID aEnd, PCB_LAYER_ID aLayer )
{
if( aLayer == aStart || aLayer == aEnd )
return true;
int start = std::min( aStart, aEnd );
int end = std::max( aStart, aEnd );
int layer = aLayer;
if( end == B_Cu )
{
//Reassign the end layer to the largest possible positive even number
end = std::numeric_limits<PCB_LAYER_ID>::max() & ~1;
}
return !( layer & 1 ) && ( layer >= start ) && ( layer <= end );
}
/**
* NOTE: These names must not be translated or changed. They are used as tokens in the board
* file format because the ordinal value of the PCB_LAYER_ID enum was not stable over time.
* @see LayerName() for what should be used to display the default name of a layer in the GUI.
*/
const wxChar* LSET::Name( PCB_LAYER_ID aLayerId )
wxString LSET::Name( PCB_LAYER_ID aLayerId )
{
const wxChar* txt;
wxString txt;
// using a switch to explicitly show the mapping more clearly
switch( aLayerId )
{
case F_Cu: txt = wxT( "F.Cu" ); break;
case In1_Cu: txt = wxT( "In1.Cu" ); break;
case In2_Cu: txt = wxT( "In2.Cu" ); break;
case In3_Cu: txt = wxT( "In3.Cu" ); break;
case In4_Cu: txt = wxT( "In4.Cu" ); break;
case In5_Cu: txt = wxT( "In5.Cu" ); break;
case In6_Cu: txt = wxT( "In6.Cu" ); break;
case In7_Cu: txt = wxT( "In7.Cu" ); break;
case In8_Cu: txt = wxT( "In8.Cu" ); break;
case In9_Cu: txt = wxT( "In9.Cu" ); break;
case In10_Cu: txt = wxT( "In10.Cu" ); break;
case In11_Cu: txt = wxT( "In11.Cu" ); break;
case In12_Cu: txt = wxT( "In12.Cu" ); break;
case In13_Cu: txt = wxT( "In13.Cu" ); break;
case In14_Cu: txt = wxT( "In14.Cu" ); break;
case In15_Cu: txt = wxT( "In15.Cu" ); break;
case In16_Cu: txt = wxT( "In16.Cu" ); break;
case In17_Cu: txt = wxT( "In17.Cu" ); break;
case In18_Cu: txt = wxT( "In18.Cu" ); break;
case In19_Cu: txt = wxT( "In19.Cu" ); break;
case In20_Cu: txt = wxT( "In20.Cu" ); break;
case In21_Cu: txt = wxT( "In21.Cu" ); break;
case In22_Cu: txt = wxT( "In22.Cu" ); break;
case In23_Cu: txt = wxT( "In23.Cu" ); break;
case In24_Cu: txt = wxT( "In24.Cu" ); break;
case In25_Cu: txt = wxT( "In25.Cu" ); break;
case In26_Cu: txt = wxT( "In26.Cu" ); break;
case In27_Cu: txt = wxT( "In27.Cu" ); break;
case In28_Cu: txt = wxT( "In28.Cu" ); break;
case In29_Cu: txt = wxT( "In29.Cu" ); break;
case In30_Cu: txt = wxT( "In30.Cu" ); break;
case B_Cu: txt = wxT( "B.Cu" ); break;
// Technicals
@ -124,24 +205,23 @@ const wxChar* LSET::Name( PCB_LAYER_ID aLayerId )
case F_Fab: txt = wxT( "F.Fab" ); break;
case B_Fab: txt = wxT( "B.Fab" ); break;
// User definable layers.
case User_1: txt = wxT( "User.1" ); break;
case User_2: txt = wxT( "User.2" ); break;
case User_3: txt = wxT( "User.3" ); break;
case User_4: txt = wxT( "User.4" ); break;
case User_5: txt = wxT( "User.5" ); break;
case User_6: txt = wxT( "User.6" ); break;
case User_7: txt = wxT( "User.7" ); break;
case User_8: txt = wxT( "User.8" ); break;
case User_9: txt = wxT( "User.9" ); break;
// Rescue
case Rescue: txt = wxT( "Rescue" ); break;
default:
std::cout << aLayerId << std::endl;
wxASSERT_MSG( 0, wxT( "aLayerId out of range" ) );
txt = wxT( "BAD INDEX!" ); break;
if( static_cast<int>( aLayerId ) & 1 )
{
int offset = ( aLayerId - Rescue ) / 2;
txt = wxString::Format( wxT( "User.%d" ), offset );
}
else
{
int offset = ( aLayerId - B_Cu ) / 2;
txt = wxString::Format( wxT( "In%d.Cu" ), offset );
}
}
return txt;
@ -150,98 +230,24 @@ const wxChar* LSET::Name( PCB_LAYER_ID aLayerId )
LSEQ LSET::CuStack() const
{
// desired sequence
static const PCB_LAYER_ID sequence[] = {
F_Cu,
In1_Cu,
In2_Cu,
In3_Cu,
In4_Cu,
In5_Cu,
In6_Cu,
In7_Cu,
In8_Cu,
In9_Cu,
In10_Cu,
In11_Cu,
In12_Cu,
In13_Cu,
In14_Cu,
In15_Cu,
In16_Cu,
In17_Cu,
In18_Cu,
In19_Cu,
In20_Cu,
In21_Cu,
In22_Cu,
In23_Cu,
In24_Cu,
In25_Cu,
In26_Cu,
In27_Cu,
In28_Cu,
In29_Cu,
In30_Cu,
B_Cu, // 31
};
LSEQ ret;
return Seq( sequence, arrayDim( sequence ) );
}
ret.reserve( 32 );
for( auto it = copper_layers_begin(); it != copper_layers_end(); ++it )
ret.push_back( *it );
LSEQ LSET::Technicals( LSET aSetToOmit ) const
{
// desired sequence
static const PCB_LAYER_ID sequence[] = {
F_Adhes,
B_Adhes,
F_Paste,
B_Paste,
F_SilkS,
B_SilkS,
F_Mask,
B_Mask,
F_CrtYd,
B_CrtYd,
F_Fab,
B_Fab,
};
LSET subset = ~aSetToOmit & *this;
return subset.Seq( sequence, arrayDim( sequence ) );
}
LSEQ LSET::Users() const
{
// desired
static const PCB_LAYER_ID sequence[] = {
Dwgs_User,
Cmts_User,
Eco1_User,
Eco2_User,
Edge_Cuts,
Margin,
User_1,
User_2,
User_3,
User_4,
User_5,
User_6,
User_7,
User_8,
User_9
};
return Seq( sequence, arrayDim( sequence ) );
return ret;
}
LSEQ LSET::TechAndUserUIOrder() const
{
static const PCB_LAYER_ID sequence[] = {
LSEQ ret;
ret.reserve( 32 );
ret = Seq( {
F_Adhes,
B_Adhes,
F_Paste,
@ -259,19 +265,16 @@ LSEQ LSET::TechAndUserUIOrder() const
F_CrtYd,
B_CrtYd,
F_Fab,
B_Fab,
User_1,
User_2,
User_3,
User_4,
User_5,
User_6,
User_7,
User_8,
User_9
};
B_Fab
} );
return Seq( sequence, arrayDim( sequence ) );
for( auto it = non_copper_layers_begin(); it != non_copper_layers_end(); ++it )
{
if( *it >= User_1 )
ret.push_back( *it );
}
return ret;
}
@ -389,40 +392,6 @@ int LSET::ParseHex( const char* aStart, int aCount )
}
LSEQ LSET::Seq( const PCB_LAYER_ID* aWishListSequence, unsigned aCount ) const
{
LSEQ ret;
#if defined(DEBUG) && 0
LSET dup_detector;
for( unsigned i=0; i<aCount; ++i )
{
PCB_LAYER_ID id = aWishListSequence[i];
if( test( id ) )
{
wxASSERT_MSG( !dup_detector[id], wxT( "Duplicate in aWishListSequence" ) );
dup_detector[id] = true;
ret.push_back( id );
}
}
#else
for( unsigned i=0; i<aCount; ++i )
{
PCB_LAYER_ID id = aWishListSequence[i];
if( test( id ) )
ret.push_back( id );
}
#endif
return ret;
}
LSEQ LSET::Seq( const LSEQ& aSequence ) const
{
LSEQ ret;
@ -455,69 +424,48 @@ LSEQ LSET::Seq() const
LSEQ LSET::SeqStackupTop2Bottom( PCB_LAYER_ID aSelectedLayer ) const
{
static const PCB_LAYER_ID sequence[] = {
LSEQ base_sequence = Seq( {
Edge_Cuts,
Margin,
Dwgs_User,
Cmts_User,
Eco1_User,
Eco2_User,
User_1,
User_2,
User_3,
User_4,
User_5,
User_6,
User_7,
User_8,
User_9,
Eco2_User
} );
LSEQ top_tech_sequence = Seq( {
F_Fab,
F_SilkS,
F_Paste,
F_Adhes,
F_Mask,
F_CrtYd,
F_Cu,
In1_Cu,
In2_Cu,
In3_Cu,
In4_Cu,
In5_Cu,
In6_Cu,
In7_Cu,
In8_Cu,
In9_Cu,
In10_Cu,
In11_Cu,
In12_Cu,
In13_Cu,
In14_Cu,
In15_Cu,
In16_Cu,
In17_Cu,
In18_Cu,
In19_Cu,
In20_Cu,
In21_Cu,
In22_Cu,
In23_Cu,
In24_Cu,
In25_Cu,
In26_Cu,
In27_Cu,
In28_Cu,
In29_Cu,
In30_Cu,
B_Cu,
} );
LSEQ bottom_tech_sequence = Seq( {
B_CrtYd,
B_Mask,
B_Adhes,
B_Paste,
B_SilkS,
B_Fab,
};
} );
LSEQ seq = Seq( sequence, arrayDim( sequence ) );
LSEQ seq = Seq( base_sequence );
for( auto it = non_copper_layers_begin(); it != non_copper_layers_end(); ++it )
{
if( *it >= User_1 )
seq.push_back( *it );
}
std::copy( top_tech_sequence.begin(), top_tech_sequence.end(), std::back_inserter( seq ) );
for( auto it = copper_layers_begin(); it != copper_layers_end(); ++it )
seq.push_back( *it );
std::copy( bottom_tech_sequence.begin(), bottom_tech_sequence.end(), std::back_inserter( seq ) );
if( aSelectedLayer != UNDEFINED_LAYER )
{
@ -539,7 +487,7 @@ LSEQ LSET::SeqStackupForPlotting() const
// bottom-to-top stack-up layers
// Note that the bottom technical layers are flipped so that when plotting a bottom-side view,
// they appear in the correct sequence.
static const PCB_LAYER_ID sequence[] = {
LSEQ bottom_tech_sequence = Seq( {
B_Cu,
B_Mask,
B_Paste,
@ -547,61 +495,71 @@ LSEQ LSET::SeqStackupForPlotting() const
B_Adhes,
B_CrtYd,
B_Fab,
In30_Cu,
In29_Cu,
In28_Cu,
In27_Cu,
In26_Cu,
In25_Cu,
In24_Cu,
In23_Cu,
In22_Cu,
In21_Cu,
In20_Cu,
In19_Cu,
In18_Cu,
In17_Cu,
In16_Cu,
In15_Cu,
In14_Cu,
In13_Cu,
In12_Cu,
In11_Cu,
In10_Cu,
In9_Cu,
In8_Cu,
In7_Cu,
In6_Cu,
In5_Cu,
In4_Cu,
In3_Cu,
In2_Cu,
In1_Cu,
F_Cu,
} );
// Copper layers go here
LSEQ top_tech_sequence = Seq( {
F_Mask,
F_Paste,
F_SilkS,
F_Adhes,
F_CrtYd,
F_Fab,
} );
LSEQ user_sequence = Seq( {
Dwgs_User,
Cmts_User,
Eco1_User,
Eco2_User,
User_1,
User_2,
User_3,
User_4,
User_5,
User_6,
User_7,
User_8,
User_9,
} );
// User layers go here
LSEQ base_sequence = Seq( {
Margin,
Edge_Cuts,
};
} );
return Seq( sequence, arrayDim( sequence ) );
LSEQ seq = Seq( bottom_tech_sequence );
std::vector<PCB_LAYER_ID> temp_layers;
// We are going to reverse the copper layers and then add them to the sequence
// because the plotting order is bottom-to-top
for( auto it = copper_layers_begin(); it != copper_layers_end(); ++it )
{
// Skip B_Cu because it is already in the sequence (if it exists)
if( *it != B_Cu )
temp_layers.push_back( *it );
}
for( auto it = temp_layers.rbegin(); it != temp_layers.rend(); ++it )
seq.push_back( *it );
std::copy( top_tech_sequence.begin(), top_tech_sequence.end(), std::back_inserter( seq ) );
std::copy( user_sequence.begin(), user_sequence.end(), std::back_inserter( seq ) );
temp_layers.clear();
for( auto it = non_copper_layers_begin(); it != non_copper_layers_end(); ++it )
{
if( *it >= User_1 )
temp_layers.push_back( *it );
}
for( auto it = temp_layers.rbegin(); it != temp_layers.rend(); ++it )
{
seq.push_back( *it );
}
std::copy( base_sequence.begin(), base_sequence.end(), std::back_inserter( seq ) );
return seq;
}
@ -611,70 +569,41 @@ LSET& LSET::Flip( int aCopperLayersCount )
reset();
if( oldMask.test( B_Cu ) )
set( F_Cu );
// Mapping for Copper and Non-Copper layers
const std::map<PCB_LAYER_ID, PCB_LAYER_ID> flip_map =
{
{F_Cu, B_Cu},
{B_Cu, F_Cu},
{F_SilkS, B_SilkS},
{B_SilkS, F_SilkS},
{F_Adhes, B_Adhes},
{B_Adhes, F_Adhes},
{F_Mask, B_Mask},
{B_Mask, F_Mask},
{F_Paste, B_Paste},
{B_Paste, F_Paste},
{F_CrtYd, B_CrtYd},
{B_CrtYd, F_CrtYd},
{F_Fab, B_Fab},
{B_Fab, F_Fab}
};
if( oldMask.test( F_Cu ) )
set( B_Cu );
for( const auto& pair : flip_map )
{
if( oldMask.test( pair.first ) )
set( pair.second );
}
if( oldMask.test( B_SilkS ) )
set( F_SilkS );
if( oldMask.test( F_SilkS ) )
set( B_SilkS );
if( oldMask.test( B_Adhes ) )
set( F_Adhes );
if( oldMask.test( F_Adhes ) )
set( B_Adhes );
if( oldMask.test( B_Mask ) )
set( F_Mask );
if( oldMask.test( F_Mask ) )
set( B_Mask );
if( oldMask.test( B_Paste ) )
set( F_Paste );
if( oldMask.test( F_Paste ) )
set( B_Paste );
if( oldMask.test( B_Adhes ) )
set( F_Adhes );
if( oldMask.test( F_Adhes ) )
set( B_Adhes );
if( oldMask.test( B_CrtYd ) )
set( F_CrtYd );
if( oldMask.test( F_CrtYd ) )
set( B_CrtYd );
if( oldMask.test( B_Fab ) )
set( F_Fab );
if( oldMask.test( F_Fab ) )
set( B_Fab );
if( aCopperLayersCount >= 4 ) // Internal layers exist
if( aCopperLayersCount >= 4 )
{
LSET internalMask = oldMask & InternalCuMask();
int innerLayerCnt = aCopperLayersCount - 2;
int innerLayerCount = aCopperLayersCount - 2;
// the flipped mask is the innerLayerCnt bits rewritten in reverse order
// ( bits innerLayerCnt to 1 rewritten in bits 1 to innerLayerCnt )
for( int ii = 0; ii < innerLayerCnt; ii++ )
for( int ii = 0; ii < innerLayerCount; ii++ )
{
if( internalMask[innerLayerCnt - ii] )
if( internalMask.test( innerLayerCount - ii * 2 + In1_Cu ) )
{
set( ii + In1_Cu );
}
else
{
reset( ii + In1_Cu );
set( ii * 2 + In1_Cu );
}
}
}
@ -731,20 +660,10 @@ LSET LSET::InternalCuMask()
LSET LSET::AllCuMask( int aCuLayerCount )
{
// retain all in static as the full set, which is a common case.
static const LSET all = InternalCuMask().set( F_Cu ).set( B_Cu );
LSET ret;
if( aCuLayerCount == MAX_CU_LAYERS )
return all;
// subtract out some Cu layers not wanted in the mask.
LSET ret = all;
int clear_count = MAX_CU_LAYERS - aCuLayerCount;
clear_count = std::clamp( clear_count, 0, MAX_CU_LAYERS - 2 );
for( int elem = In30_Cu; clear_count; --elem, --clear_count )
ret.set( elem, false );
for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, aCuLayerCount ) )
ret.set( layer );
return ret;
}
@ -866,6 +785,7 @@ LSEQ LSET::UIOrder() const
{
LSEQ order = CuStack();
LSEQ techuser = TechAndUserUIOrder();
order.insert( order.end(), techuser.begin(), techuser.end() );
return order;
@ -874,6 +794,10 @@ LSEQ LSET::UIOrder() const
PCB_LAYER_ID ToLAYER_ID( int aLayer )
{
// We use std::numeric_limits<int>::max() to represent B_Cu for the connectivity_rtree
if( aLayer == std::numeric_limits<int>::max() )
return B_Cu;
wxASSERT( aLayer < GAL_LAYER_ID_END );
return PCB_LAYER_ID( aLayer );
}
@ -946,3 +870,100 @@ GAL_SET GAL_SET::DefaultVisible()
static const GAL_SET saved( visible, arrayDim( visible ) );
return saved;
}
#ifndef SWIG // Skip SWIG generators for the iterators because it requires a default constructor
// Custom iterators for Copper and Non-Copper layers
LSET::copper_layers_iterator::copper_layers_iterator( const BASE_SET& set, size_t index ) :
BASE_SET::set_bits_iterator( set, index )
{
m_index = ( index + 1 ) & ~1;
advance_to_next_set_copper_bit();
}
PCB_LAYER_ID LSET::copper_layers_iterator::operator*() const
{
return static_cast<PCB_LAYER_ID>( m_index );
}
LSET::copper_layers_iterator& LSET::copper_layers_iterator::operator++()
{
next_copper_layer();
advance_to_next_set_copper_bit();
return *this;
}
void LSET::copper_layers_iterator::next_copper_layer()
{
if( m_index == F_Cu )
{
m_index += 4;
}
else if( m_index == B_Cu )
{
m_index = m_baseSet.size();
return;
}
else
{
m_index += 2;
if( m_index >= m_baseSet.size() )
m_index = B_Cu;
}
}
void LSET::copper_layers_iterator::advance_to_next_set_copper_bit()
{
while( m_index < m_baseSet.size() && !m_baseSet.test( m_index ) )
next_copper_layer();
}
LSET::non_copper_layers_iterator::non_copper_layers_iterator( const BASE_SET& set, size_t index ) :
BASE_SET::set_bits_iterator( set, index )
{
advance_to_next_set_non_copper_bit();
}
PCB_LAYER_ID LSET::non_copper_layers_iterator::operator*() const
{
return static_cast<PCB_LAYER_ID>( m_index );
}
LSET::non_copper_layers_iterator& LSET::non_copper_layers_iterator::operator++()
{
++m_index;
advance_to_next_set_non_copper_bit();
return *this;
}
void LSET::non_copper_layers_iterator::advance_to_next_set_non_copper_bit()
{
while( m_index < m_baseSet.size() && ( m_index % 2 != 1 || !m_baseSet.test( m_index ) ) )
{
++m_index;
}
}
LSET::copper_layers_iterator LSET::copper_layers_begin() const
{
return copper_layers_iterator( *this, 0 );
}
LSET::copper_layers_iterator LSET::copper_layers_end() const
{
return copper_layers_iterator( *this, size() );
}
LSET::non_copper_layers_iterator LSET::non_copper_layers_begin() const
{
return non_copper_layers_iterator( *this, 0 );
}
LSET::non_copper_layers_iterator LSET::non_copper_layers_end() const
{
return non_copper_layers_iterator( *this, size() );
}
#endif

View File

@ -147,6 +147,10 @@ static const std::map<int, COLOR4D> s_defaultTheme =
{ GERBVIEW_LAYER_ID_START + 57, CSS_COLOR( 127, 200, 127, 1 ) },
{ GERBVIEW_LAYER_ID_START + 58, CSS_COLOR( 206, 125, 44, 1 ) },
{ GERBVIEW_LAYER_ID_START + 59, CSS_COLOR( 79, 203, 203, 1 ) },
{ GERBVIEW_LAYER_ID_START + 60, CSS_COLOR( 219, 98, 139, 1 ) },
{ GERBVIEW_LAYER_ID_START + 61, CSS_COLOR( 167, 165, 198, 1 ) },
{ GERBVIEW_LAYER_ID_START + 62, CSS_COLOR( 40, 204, 217, 1 ) },
{ GERBVIEW_LAYER_ID_START + 63, CSS_COLOR( 232, 178, 167, 1 ) },
{ LAYER_ANCHOR, CSS_COLOR( 255, 38, 226, 1 ) },
{ LAYER_LOCKED_ITEM_SHADOW, CSS_COLOR( 255, 38, 226, 0.5 ) },

View File

@ -59,7 +59,7 @@ bool GERBVIEW_PRINTOUT::OnPrintPage( int aPage )
// objects when using only one page is tricky
// Enable only one layer to create a printout
m_settings.m_LayerSet = LSET( layerId );
m_settings.m_LayerSet = LSET( { layerId } );
GERBER_FILE_IMAGE_LIST& gbrImgList = GERBER_FILE_IMAGE_LIST::GetImagesList();
GERBER_FILE_IMAGE* gbrImage = gbrImgList.GetGbrImage( layerId );

View File

@ -24,7 +24,7 @@
#include <limits>
#include <ostream>
#include <stdexcept>
#include <vector>
#include <dynamic_bitset.h>
#include <core/kicad_algo.h>
#include <import_export.h>
@ -37,176 +37,182 @@
typedef SSIZE_T ssize_t;
#endif
class APIEXPORT BASE_SET
class APIEXPORT BASE_SET : public sul::dynamic_bitset<uint64_t>
{
public:
using iterator = std::vector<int>::iterator;
using const_iterator = std::vector<int>::const_iterator;
BASE_SET( size_t size ) : m_bits( size, 0 ) {}
bool test( size_t pos ) const
class iterator
{
if( pos >= m_bits.size() )
return false;
public:
using iterator_category = std::random_access_iterator_tag;
using value_type = bool;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = bool;
return m_bits[pos] == 1;
}
bool any() const { return std::any_of( m_bits.begin(), m_bits.end(), []( int bit ) { return bit == 1; } ); }
bool all() const { return std::all_of( m_bits.begin(), m_bits.end(), []( int bit ) { return bit == 1; } ); }
bool none() const { return std::none_of( m_bits.begin(), m_bits.end(), []( int bit ) { return bit == 1; } ); }
BASE_SET& set( size_t pos = std::numeric_limits<size_t>::max(), bool value = true )
{
if( pos == std::numeric_limits<size_t>::max() )
iterator( BASE_SET* set, size_t pos ) : m_set( set ), m_pos( pos ) {}
bool operator*() const { return m_set->test( m_pos ); }
iterator& operator++()
{
std::fill( m_bits.begin(), m_bits.end(), value ? 1 : 0 );
++m_pos;
return *this;
}
iterator operator+( difference_type n ) const
{
return iterator( m_set, m_pos + n );
}
difference_type operator-( const iterator& other ) const
{
return static_cast<difference_type>(m_pos) - static_cast<difference_type>(other.m_pos);
}
auto operator<=>( const iterator& ) const = default;
if( pos >= m_bits.size() )
m_bits.resize( pos + 1, 0 );
private:
BASE_SET* m_set;
size_t m_pos;
};
m_bits[pos] = value ? 1 : 0;
class const_iterator
{
public:
using iterator_category = std::random_access_iterator_tag;
using value_type = bool;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = bool;
const_iterator( const BASE_SET* set, size_t pos ) : m_set( set ), m_pos( pos ) {}
bool operator*() const { return m_set->test( m_pos ); }
const_iterator& operator++()
{
++m_pos;
return *this;
}
const_iterator operator+( difference_type n ) const
{
return const_iterator( m_set, m_pos + n );
}
difference_type operator-( const const_iterator& other ) const
{
return static_cast<difference_type>(m_pos) - static_cast<difference_type>(other.m_pos);
}
auto operator<=>( const const_iterator& ) const = default;
private:
const BASE_SET* m_set;
size_t m_pos;
};
iterator begin() { return iterator(this, 0); }
iterator end() { return iterator(this, size()); }
const_iterator begin() const { return const_iterator(this, 0); }
const_iterator end() const { return const_iterator(this, size()); }
BASE_SET( size_t size = 64 ) : sul::dynamic_bitset<uint64_t>( size ) {}
// Overloads for set, reset, and flip operations
// Set a bit at the specified position
BASE_SET& set(size_t pos)
{
if( pos >= size() )
sul::dynamic_bitset<uint64_t>::resize( pos + 1 );
sul::dynamic_bitset<uint64_t>::set(pos);
return *this;
}
BASE_SET& reset( size_t pos = std::numeric_limits<size_t>::max() )
// Set a bit at the specified position to a given value
BASE_SET& set(size_t pos, bool value)
{
if( pos == std::numeric_limits<size_t>::max() )
{
std::fill( m_bits.begin(), m_bits.end(), 0 );
return *this;
}
if( pos >= size() )
sul::dynamic_bitset<uint64_t>::resize( pos + 1 );
if( pos >= m_bits.size() )
m_bits.resize( pos + 1, 0 );
m_bits[pos] = 0;
sul::dynamic_bitset<uint64_t>::set(pos, value);
return *this;
}
BASE_SET& flip( size_t pos = std::numeric_limits<size_t>::max() )
// Set all bits to 1
BASE_SET& set()
{
if( pos == std::numeric_limits<size_t>::max() )
{
std::transform( m_bits.begin(), m_bits.end(), m_bits.begin(), []( int bit ) { return bit ^ 1; } );
return *this;
}
if( pos >= m_bits.size() )
m_bits.resize( pos + 1, 0 );
m_bits[pos] ^= 1;
sul::dynamic_bitset<uint64_t>::set();
return *this;
}
size_t count() const { return std::count( m_bits.begin(), m_bits.end(), 1 ); }
// Reset (clear) a bit at the specified position
BASE_SET& reset(size_t pos)
{
if( pos >= size() )
sul::dynamic_bitset<uint64_t>::resize( pos + 1 );
size_t size() const { return m_bits.size(); }
sul::dynamic_bitset<uint64_t>::reset(pos);
return *this;
}
void resize( size_t newSize ) { m_bits.resize( newSize, 0 ); }
// Reset (clear) all bits
BASE_SET& reset()
{
sul::dynamic_bitset<uint64_t>::reset();
return *this;
}
int& operator[]( size_t pos ) { return m_bits[pos]; }
// Flip a bit at the specified position
BASE_SET& flip(size_t pos)
{
if( pos >= size() )
sul::dynamic_bitset<uint64_t>::resize( pos + 1 );
const int& operator[]( size_t pos ) const { return m_bits[pos]; }
sul::dynamic_bitset<uint64_t>::flip(pos);
return *this;
}
// Flip all bits
BASE_SET& flip()
{
sul::dynamic_bitset<uint64_t>::flip();
return *this;
}
// Overloads for boolean operators
// Bitwise NOT operator
BASE_SET operator~() const
{
BASE_SET result(*this);
result.flip();
return result;
}
// Compound assignment AND operator
BASE_SET& operator&=(const BASE_SET& other)
{
sul::dynamic_bitset<uint64_t>::operator&=(other);
return *this;
}
// Compound assignment OR operator
BASE_SET& operator|=(const BASE_SET& other)
{
sul::dynamic_bitset<uint64_t>::operator|=(other);
return *this;
}
// Compound assignment XOR operator
BASE_SET& operator^=(const BASE_SET& other)
{
sul::dynamic_bitset<uint64_t>::operator^=(other);
return *this;
}
int compare( const BASE_SET& other ) const
{
return alg::lexicographical_compare_3way( m_bits, other.m_bits );
}
iterator begin() { return m_bits.begin(); }
iterator end() { return m_bits.end(); }
const_iterator begin() const { return m_bits.begin(); }
const_iterator end() const { return m_bits.end(); }
// Define equality operator
bool operator==( const BASE_SET& other ) const
{
std::size_t minSize = std::min( size(), other.size() );
if( !std::equal( m_bits.begin(), m_bits.begin() + minSize, other.m_bits.begin() ) )
return false;
if( std::any_of( m_bits.begin() + minSize, m_bits.end(), []( int bit ) { return bit != 0; } ) )
return false;
if( std::any_of( other.m_bits.begin() + minSize, other.m_bits.end(), []( int bit ) { return bit != 0; } ) )
return false;
return true;
auto result = std::lexicographical_compare_three_way( begin(), end(), other.begin(), other.end() );
return result == std::strong_ordering::equal ? 0 : ( result == std::strong_ordering::less ? -1 : 1 );
}
// Define less-than operator for comparison
bool operator<( const BASE_SET& other ) const
{
return std::lexicographical_compare( m_bits.begin(), m_bits.end(), other.m_bits.begin(), other.m_bits.end() );
}
// Define output operator
friend std::ostream& operator<<( std::ostream& os, const BASE_SET& set )
{
return os << set.to_string();
}
// to_string method
template <typename CharT = char>
std::basic_string<CharT> to_string( CharT zero = CharT( '0' ), CharT one = CharT( '1' ) ) const
{
std::basic_string<CharT> result( size(), zero );
for( size_t i = 0; i < size(); ++i )
{
if( test( i ) )
{
result[size() - 1 - i] = one; // Reverse order to match std::bitset behavior
}
}
return result;
}
// Boolean operators
BASE_SET& operator&=( const BASE_SET& rhs )
{
assert( m_bits.size() == rhs.m_bits.size() );
for( size_t i = 0; i < m_bits.size(); ++i )
m_bits[i] &= rhs.m_bits[i];
return *this;
}
BASE_SET& operator|=( const BASE_SET& rhs )
{
assert( m_bits.size() == rhs.m_bits.size() );
for( size_t i = 0; i < m_bits.size(); ++i )
m_bits[i] |= rhs.m_bits[i];
return *this;
}
BASE_SET& operator^=( const BASE_SET& rhs )
{
assert( m_bits.size() == rhs.m_bits.size() );
for( size_t i = 0; i < m_bits.size(); ++i )
m_bits[i] ^= rhs.m_bits[i];
return *this;
}
BASE_SET operator~() const
{
BASE_SET result = *this;
for( size_t i = 0; i < m_bits.size(); ++i )
result.m_bits[i] = !m_bits[i];
return result;
return std::lexicographical_compare( begin(), end(), other.begin(), other.end() );
}
// Custom iterator to iterate over set bits
@ -238,7 +244,7 @@ public:
bool operator==( const set_bits_iterator& other ) const { return m_index == other.m_index; }
private:
protected:
void advance_to_next_set_bit()
{
while( m_index < m_baseSet.size() && !m_baseSet.test( m_index ) )
@ -284,7 +290,7 @@ public:
return m_index == other.m_index;
}
private:
protected:
void advance_to_previous_set_bit()
{
while( m_index >= 0 && !m_baseSet.test( m_index ) )
@ -298,19 +304,17 @@ public:
};
set_bits_iterator set_bits_begin() const { return set_bits_iterator( *this, 0 ); }
set_bits_iterator set_bits_end() const { return set_bits_iterator( *this, m_bits.size() ); }
set_bits_iterator set_bits_end() const { return set_bits_iterator( *this, size() ); }
set_bits_reverse_iterator set_bits_rbegin() const
{
return set_bits_reverse_iterator( *this, m_bits.size() - 1 );
return set_bits_reverse_iterator( *this, size() - 1 );
}
set_bits_reverse_iterator set_bits_rend() const
{
return set_bits_reverse_iterator( *this, -1 );
}
private:
std::vector<int> m_bits;
};
inline BASE_SET operator&( const BASE_SET& lhs, const BASE_SET& rhs )

View File

@ -239,6 +239,21 @@ public:
*/
virtual PCB_LAYER_ID GetLayer() const { return m_layer; }
/**
* Return the total number of layers for the board that this item resides on.
*/
virtual int BoardLayerCount() const;
/**
* Return the total number of copper layers for the board that this item resides on.
*/
virtual int BoardCopperLayerCount() const;
/**
* Return the LSET for the board that this item resides on.
*/
virtual LSET BoardLayerSet() const;
/**
* Return a std::bitset of all layers on which the item physically resides.
*/
@ -247,7 +262,7 @@ public:
if( m_layer == UNDEFINED_LAYER )
return LSET();
else
return LSET( m_layer );
return LSET( { m_layer } );
}
virtual void SetLayerSet( const LSET& aLayers )

View File

@ -62,84 +62,82 @@ enum PCB_LAYER_ID: int
UNSELECTED_LAYER = -2,
F_Cu = 0,
In1_Cu,
In2_Cu,
In3_Cu,
In4_Cu,
In5_Cu,
In6_Cu,
In7_Cu,
In8_Cu,
In9_Cu,
In10_Cu,
In11_Cu,
In12_Cu,
In13_Cu,
In14_Cu,
In15_Cu,
In16_Cu,
In17_Cu,
In18_Cu,
In19_Cu,
In20_Cu,
In21_Cu,
In22_Cu,
In23_Cu,
In24_Cu,
In25_Cu,
In26_Cu,
In27_Cu,
In28_Cu,
In29_Cu,
In30_Cu,
B_Cu, // 31
B_Cu = 2,
In1_Cu = 4,
In2_Cu = 6,
In3_Cu = 8,
In4_Cu = 10,
In5_Cu = 12,
In6_Cu = 14,
In7_Cu = 16,
In8_Cu = 18,
In9_Cu = 20,
In10_Cu = 22,
In11_Cu = 24,
In12_Cu = 26,
In13_Cu = 28,
In14_Cu = 30,
In15_Cu = 32,
In16_Cu = 34,
In17_Cu = 36,
In18_Cu = 38,
In19_Cu = 40,
In20_Cu = 42,
In21_Cu = 44,
In22_Cu = 46,
In23_Cu = 48,
In24_Cu = 50,
In25_Cu = 52,
In26_Cu = 54,
In27_Cu = 56,
In28_Cu = 58,
In29_Cu = 60,
In30_Cu = 62,
B_Adhes,
F_Adhes,
F_Mask = 1,
B_Mask = 3,
B_Paste,
F_Paste,
F_SilkS = 5,
B_SilkS = 7,
F_Adhes = 9,
B_Adhes = 11,
F_Paste = 13,
B_Paste = 15,
B_SilkS,
F_SilkS,
Dwgs_User = 17,
Cmts_User = 19,
Eco1_User = 21,
Eco2_User = 23,
B_Mask,
F_Mask, // 39
Edge_Cuts = 25,
Margin = 27,
Dwgs_User,
Cmts_User,
Eco1_User,
Eco2_User,
Edge_Cuts,
Margin, // 45
B_CrtYd = 29,
F_CrtYd = 31,
B_CrtYd,
F_CrtYd,
B_Fab = 33,
F_Fab = 35,
B_Fab,
F_Fab, // 49
Rescue = 37,
// User definable layers.
User_1,
User_2,
User_3,
User_4,
User_5,
User_6,
User_7,
User_8,
User_9,
User_1 = 39,
User_2 = 41,
User_3 = 43,
User_4 = 45,
User_5 = 47,
User_6 = 49,
User_7 = 51,
User_8 = 53,
User_9 = 55,
Rescue, // 59
// Four reserved layers (60 - 63) for future expansion within the 64 bit integer limit.
PCB_LAYER_ID_COUNT
PCB_LAYER_ID_COUNT = 64
};
constexpr PCB_LAYER_ID PCBNEW_LAYER_ID_START = F_Cu;
#define MAX_CU_LAYERS (B_Cu - F_Cu + 1)
#define MAX_CU_LAYERS 32
/**
* Enum used during connectivity building to ensure we do not query connectivity while building
@ -531,7 +529,7 @@ inline bool IsPcbLayer( int aLayer )
*/
inline bool IsCopperLayer( int aLayerId )
{
return aLayerId >= F_Cu && aLayerId <= B_Cu;
return !( aLayerId & 1 ) && aLayerId <= PCB_LAYER_ID_COUNT;
}
/**
@ -542,7 +540,7 @@ inline bool IsCopperLayer( int aLayerId )
*/
inline bool IsNonCopperLayer( int aLayerId )
{
return aLayerId > B_Cu && aLayerId <= PCB_LAYER_ID_COUNT;
return ( aLayerId & 1 ) && aLayerId <= PCB_LAYER_ID_COUNT;
}
/**

143
include/layer_range.h Normal file
View File

@ -0,0 +1,143 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <layer_ids.h>
#ifndef LAYER_RANGE_H
#define LAYER_RANGE_H
class LAYER_RANGE
{
private:
PCB_LAYER_ID m_start;
PCB_LAYER_ID m_stop;
int m_layer_count;
class LAYER_RANGE_ITERATOR
{
private:
int m_current;
int m_stop;
int m_layer_count;
bool m_reverse;
int next_layer( int aLayer )
{
if( m_reverse )
{
if( aLayer == B_Cu )
aLayer = m_layer_count == 2 ? F_Cu : static_cast<int>( F_Cu ) + 2 * ( m_layer_count - 2 ) + 2;
else if( aLayer == m_stop || aLayer == UNDEFINED_LAYER )
aLayer = UNDEFINED_LAYER;
else if( aLayer == In1_Cu )
aLayer = F_Cu;
else
aLayer = static_cast<int>( aLayer ) - 2;
}
else
{
if( aLayer == F_Cu && m_layer_count == 2 )
aLayer = B_Cu;
else if( aLayer == m_stop || aLayer == UNDEFINED_LAYER )
aLayer = UNDEFINED_LAYER;
else if( aLayer == static_cast<int>( F_Cu ) + 2 * ( m_layer_count - 2 ) + 2)
aLayer = B_Cu;
else if( aLayer == F_Cu )
aLayer = In1_Cu;
else
aLayer = static_cast<int>( aLayer ) + 2;
}
return aLayer;
}
public:
using iterator_category = std::bidirectional_iterator_tag;
using value_type = PCB_LAYER_ID;
using difference_type = std::ptrdiff_t;
using pointer = PCB_LAYER_ID*;
using reference = PCB_LAYER_ID&;
LAYER_RANGE_ITERATOR( PCB_LAYER_ID start, PCB_LAYER_ID stop, int layer_count ) :
m_current( start ), m_stop( stop ), m_layer_count( layer_count )
{
if( start & 1 || stop & 1 )
throw std::invalid_argument( "Only works for copper layers" );
m_layer_count = m_layer_count & ~1;
if( stop == B_Cu || m_stop >= m_current )
m_reverse = false;
else
m_reverse = true;
}
PCB_LAYER_ID operator*() const { return static_cast<PCB_LAYER_ID>( m_current ); }
LAYER_RANGE_ITERATOR& operator++()
{
m_current = next_layer( m_current );
return *this;
}
LAYER_RANGE_ITERATOR operator++( int )
{
LAYER_RANGE_ITERATOR tmp = *this;
++( *this );
return tmp;
}
bool operator==( const LAYER_RANGE_ITERATOR& other ) const
{
return m_current == other.m_current;
}
bool operator!=( const LAYER_RANGE_ITERATOR& other ) const { return !( *this == other ); }
};
public:
LAYER_RANGE( PCB_LAYER_ID start, PCB_LAYER_ID stop, int layer_count ) :
m_start( start ), m_stop( stop ), m_layer_count( layer_count )
{
if( start & 1 || stop & 1 )
throw std::invalid_argument( "Only works for copper layers" );
}
LAYER_RANGE_ITERATOR begin() const { return LAYER_RANGE_ITERATOR( m_start, m_stop, m_layer_count ); }
LAYER_RANGE_ITERATOR end() const { auto it = LAYER_RANGE_ITERATOR( m_stop, m_stop, m_layer_count ); return ++it; }
static bool Contains( int aStart_layer, int aEnd_layer, int aTest_layer )
{
if( aStart_layer == B_Cu )
aStart_layer = INT_MAX;
if( aEnd_layer == B_Cu )
aEnd_layer = INT_MAX;
return aTest_layer >= aStart_layer && aTest_layer <= aEnd_layer;
}
bool Contains( int aTest_layer )
{
return Contains( m_start, m_stop, aTest_layer );
}
};
#endif // LAYER_RANGE_H

View File

@ -24,6 +24,7 @@
#include <base_set.h>
class LSEQ;
class LAYER_RANGE;
/**
* LSET is a set of PCB_LAYER_IDs. It can be converted to numerous purpose LSEQs using
@ -35,39 +36,19 @@ class KICOMMON_API LSET : public BASE_SET
{
public:
// The constructor flavors are carefully chosen to prevent LSET( int ) from compiling.
// That excludes "LSET s = 0;" and excludes "LSET s = -1;", etc.
// LSET s = 0; needs to be removed from the code, this accomplishes that.
// Remember LSET( PCB_LAYER_ID(0) ) sets bit 0, so "LSET s = 0;" is illegal
// to prevent that surprise. Therefore LSET's constructor suite is significantly
// different than the base class from which it is derived.
// Other member functions (non-constructor functions) are identical to the base
// class's and therefore are re-used from the base class.
/**
* Create an empty (cleared) set.
*/
LSET() :
BASE_SET( PCB_LAYER_ID_COUNT ) // all bits are set to zero in BASE_SET()
{
}
LSET() : BASE_SET( PCB_LAYER_ID_COUNT ) {} // all bits are set to zero in BASE_SET()
LSET( const BASE_SET& aOther ) :
BASE_SET( aOther )
{
}
LSET( PCB_LAYER_ID aLayer ) :
BASE_SET( PCB_LAYER_ID_COUNT )
{
set( aLayer );
}
LSET( const BASE_SET& aOther ) : BASE_SET( aOther ) {}
LSET( std::initializer_list<PCB_LAYER_ID> aList );
LSET( const LSEQ& aSeq );
LSET( const LAYER_RANGE& aRange );
LSET( unsigned long __val ) = delete;
/**
@ -91,7 +72,18 @@ public:
/**
* Return the fixed name association with aLayerId.
*/
static const wxChar* Name( PCB_LAYER_ID aLayerId );
static wxString Name( PCB_LAYER_ID aLayerId );
/**
* Return the layer number from a layer name.
*/
static int NameToLayer( wxString& aName );
/**
* Return true if aLayer is between aStart and aEnd, inclusive. Takes into
* account the direction of the layers and the fact that B_Cu comes before In*_Cu
*/
static bool IsBetween( PCB_LAYER_ID aStart, PCB_LAYER_ID aEnd, PCB_LAYER_ID aLayer );
/**
* Return a complete set of internal copper layers which is all Cu layers
@ -192,24 +184,20 @@ public:
/**
* Return a sequence of copper layers in starting from the front/top
* and extending to the back/bottom. This specific sequence is depended upon
* in numerous places.
* and extending to the back/bottom.
*/
LSEQ CuStack() const;
/**
* Return a sequence of technical layers. A sequence provides a certain order.
* Returns the technical and user layers in the order shown in layer widget
*
* @param aSubToOmit is the subset of the technical layers to omit, defaults to none.
*/
LSEQ Technicals( LSET aSubToOmit = LSET() ) const;
/// *_User layers.
LSEQ Users() const;
/// Returns the technical and user layers in the order shown in layer widget
LSEQ TechAndUserUIOrder() const;
/**
* Returns the copper, technical and user layers in the order shown in layer widget
*
*/
LSEQ UIOrder() const;
/**
@ -217,10 +205,7 @@ public:
* element will be in the same sequence as aWishListSequence if they are present.
* @param aWishListSequence establishes the order of the returned LSEQ, and the LSEQ will only
* contain PCB_LAYER_IDs which are present in this set.
* @param aCount is the length of aWishListSequence array.
*/
LSEQ Seq( const PCB_LAYER_ID* aWishListSequence, unsigned aCount ) const;
LSEQ Seq( const LSEQ& aSequence ) const;
/**
@ -295,5 +280,63 @@ public:
*/
LSET& Flip( int aCopperLayersCount = 0 );
/**
* Return the number of layers between aStart and aEnd, inclusive.
*/
static int LayerCount( PCB_LAYER_ID aStart, PCB_LAYER_ID aEnd, int aCopperLayerCount );
#ifndef SWIG
// Custom iterator to iterate over all set bits
class all_set_layers_iterator : public BASE_SET::set_bits_iterator
{
public:
all_set_layers_iterator( const BASE_SET& set, size_t index ) :
BASE_SET::set_bits_iterator( set, index )
{
}
PCB_LAYER_ID operator*() const { return PCB_LAYER_ID( BASE_SET::set_bits_iterator::operator*() ); }
all_set_layers_iterator& operator++()
{
BASE_SET::set_bits_iterator::operator++();
return *this;
}
};
all_set_layers_iterator begin() const { return all_set_layers_iterator( *this, 0 ); }
all_set_layers_iterator end() const { return all_set_layers_iterator( *this, size() ); }
// Custom iterators for Copper and Non-Copper layers
class copper_layers_iterator : public BASE_SET::set_bits_iterator
{
public:
copper_layers_iterator( const BASE_SET& set, size_t index );
PCB_LAYER_ID operator*() const;
copper_layers_iterator& operator++();
private:
void advance_to_next_set_copper_bit();
void next_copper_layer();
};
class non_copper_layers_iterator : public BASE_SET::set_bits_iterator
{
public:
non_copper_layers_iterator( const BASE_SET& set, size_t index );
PCB_LAYER_ID operator*() const;
non_copper_layers_iterator& operator++();
private:
void advance_to_next_set_non_copper_bit();
};
copper_layers_iterator copper_layers_begin() const;
copper_layers_iterator copper_layers_end() const;
non_copper_layers_iterator non_copper_layers_begin() const;
non_copper_layers_iterator non_copper_layers_end() const;
#endif
};
#endif // LSET_H

View File

@ -50,9 +50,9 @@ CLI::PCB_EXPORT_BASE_COMMAND::PCB_EXPORT_BASE_COMMAND( const std::string& aName,
//m_layerIndices[untranslated] = PCB_LAYER_ID( layer );
// Add layer name used in pcb files
m_layerMasks[untranslated] = LSET( PCB_LAYER_ID( layer ) );
m_layerMasks[untranslated] = LSET( { PCB_LAYER_ID( layer ) } );
// Add layer name using GUI canonical layer name
m_layerGuiMasks[ TO_UTF8(LayerName( layer ) ) ] = LSET( PCB_LAYER_ID( layer ) );
m_layerGuiMasks[ TO_UTF8(LayerName( layer ) ) ] = LSET( { PCB_LAYER_ID( layer ) } );
}
// Add list of grouped layer names used in pcb files

View File

@ -571,7 +571,7 @@ int AR_AUTOPLACER::getOptimalFPPlacement( FOOTPRINT* aFootprint )
if( m_matrix.m_RoutingLayersCount > 1 )
{
LSET other( aFootprint->GetLayer() == B_Cu ? F_Cu : B_Cu );
LSET other( { aFootprint->GetLayer() == B_Cu ? F_Cu : B_Cu } );
for( PAD* pad : aFootprint->Pads() )
{

View File

@ -2121,7 +2121,7 @@ PAD* BOARD::GetPad( const PCB_TRACK* aTrace, ENDPOINT_T aEndPoint ) const
{
const VECTOR2I& aPosition = aTrace->GetEndPoint( aEndPoint );
LSET lset( aTrace->GetLayer() );
LSET lset( { aTrace->GetLayer() } );
return GetPad( aPosition, lset );
}

View File

@ -103,6 +103,39 @@ const KIFONT::METRICS& BOARD_ITEM::GetFontMetrics() const
}
int BOARD_ITEM::BoardLayerCount() const
{
const BOARD* board = GetBoard();
if( board )
return board->GetLayerSet().count();
return 64;
}
int BOARD_ITEM::BoardCopperLayerCount() const
{
const BOARD* board = GetBoard();
if( board )
return board->GetCopperLayerCount();
return 32;
}
LSET BOARD_ITEM::BoardLayerSet() const
{
const BOARD* board = GetBoard();
if( board )
return board->GetLayerSet();
return LSET::AllLayersMask();
}
wxString BOARD_ITEM::GetLayerName() const
{
if( const BOARD* board = GetBoard() )

View File

@ -703,7 +703,7 @@ void CN_CONNECTIVITY_ALGO::FillIsolatedIslandsMap(
{
for( CN_ITEM* item : *cluster )
{
if( item->Parent() == zone && item->Layer() == layer )
if( item->Parent() == zone && item->GetBoardLayer() == layer )
{
CN_ZONE_LAYER* z = static_cast<CN_ZONE_LAYER*>( item );

View File

@ -447,7 +447,7 @@ bool CONNECTIVITY_DATA::IsConnectedOnLayer( const BOARD_CONNECTED_ITEM *aItem, i
CN_ZONE_LAYER* zoneLayer = dynamic_cast<CN_ZONE_LAYER*>( connected );
if( connected->Valid()
&& connected->Layers().Overlaps( aLayer )
&& connected->StartLayer() <= aLayer && connected->EndLayer() >= aLayer
&& matchType( connected->Parent()->Type() )
&& connected->Net() == aItem->GetNetCode() )
{

View File

@ -152,7 +152,7 @@ CN_ITEM* CN_LIST::Add( PAD* pad )
auto item = new CN_ITEM( pad, false, 1 );
item->AddAnchor( pad->ShapePos() );
item->SetLayers( LAYER_RANGE( F_Cu, B_Cu ) );
item->SetLayers( F_Cu, B_Cu );
switch( pad->GetAttribute() )
{
@ -160,16 +160,11 @@ CN_ITEM* CN_LIST::Add( PAD* pad )
case PAD_ATTRIB::NPTH:
case PAD_ATTRIB::CONN:
{
LSET lmsk = pad->GetLayerSet();
LSEQ lmsk = pad->GetLayerSet().CuStack();
if( !lmsk.empty() )
item->SetLayer( lmsk.front() );
for( int i = 0; i <= MAX_CU_LAYERS; i++ )
{
if( lmsk[i] )
{
item->SetLayer( i );
break;
}
}
break;
}
default:
@ -216,7 +211,7 @@ CN_ITEM* CN_LIST::Add( PCB_VIA* via )
m_items.push_back( item );
item->AddAnchor( via->GetStart() );
item->SetLayers( LAYER_RANGE( via->TopLayer(), via->BottomLayer() ) );
item->SetLayers( via->TopLayer(), via->BottomLayer() );
addItemtoTree( item );
SetDirty();
return item;
@ -358,7 +353,7 @@ bool CN_ANCHOR::IsDangling() const
{
ZONE* zone = static_cast<ZONE*>( item->Parent() );
if( zone->HitTestFilledArea( ToLAYER_ID( item->Layer() ), Pos(), accuracy ) )
if( zone->HitTestFilledArea( item->GetBoardLayer(), Pos(), accuracy ) )
connected_count++;
}
else if( item->Parent()->HitTest( Pos(), accuracy ) )
@ -384,7 +379,7 @@ int CN_ANCHOR::ConnectedItemsCount() const
{
ZONE* zone = static_cast<ZONE*>( item->Parent() );
if( zone->HitTestFilledArea( ToLAYER_ID( item->Layer() ), Pos() ) )
if( zone->HitTestFilledArea( item->GetBoardLayer(), Pos() ) )
connected_count++;
}
else if( item->Parent()->HitTest( Pos() ) )

View File

@ -142,7 +142,8 @@ public:
m_valid = true;
m_dirty = true;
m_anchors.reserve( std::max( 6, aAnchorCount ) );
m_layers = LAYER_RANGE( 0, PCB_LAYER_ID_COUNT );
m_start_layer = 0;
m_end_layer = std::numeric_limits<int>::max();
m_connected.reserve( 8 );
}
@ -167,26 +168,55 @@ public:
bool Dirty() const { return m_dirty; }
/**
* Set the layers spanned by the item to aLayers.
* Set the layers spanned by the item to aStartLayer and aEndLayer.
*/
void SetLayers( const LAYER_RANGE& aLayers ) { m_layers = aLayers; }
void SetLayers( int aStartLayer, int aEndLayer )
{
// B_Cu is nominally layer 2 but we reset it to INT_MAX to ensure that it is
// always greater than any other layer in the RTree
if( aStartLayer == B_Cu )
aStartLayer = std::numeric_limits<int>::max();
if( aEndLayer == B_Cu )
aEndLayer = std::numeric_limits<int>::max();
m_start_layer = aStartLayer;
m_end_layer = aEndLayer;
}
/**
* Set the layers spanned by the item to a single layer aLayer.
*/
void SetLayer( int aLayer ) { m_layers = LAYER_RANGE( aLayer, aLayer ); }
void SetLayer( int aLayer ) { SetLayers( aLayer, aLayer ); }
/**
* Return the contiguous set of layers spanned by the item.
*/
const LAYER_RANGE& Layers() const { return m_layers; }
int StartLayer() const { return m_start_layer; }
int EndLayer() const { return m_end_layer; }
/**
* Return the item's layer, for single-layered items only.
* N.B. This should only be used inside connectivity as B_Cu
* is mapped to a large int
*/
virtual int Layer() const
{
return Layers().Start();
return StartLayer();
}
/**
* When using CN_ITEM layers to compare against board items,
* use this function which correctly remaps the B_Cu layer
*/
PCB_LAYER_ID GetBoardLayer() const
{
int layer = Layer();
if( layer == std::numeric_limits<int>::max() )
layer = B_Cu;
return ToLAYER_ID( layer );
}
const BOX2I& BBox()
@ -232,7 +262,8 @@ public:
protected:
bool m_dirty; ///< used to identify recently added item not yet
///< scanned into the connectivity search
LAYER_RANGE m_layers; ///< layer range over which the item exists
int m_start_layer; ///< start layer of the item N.B. B_Cu is set to INT_MAX
int m_end_layer; ///< end layer of the item N.B. B_Cu is set to INT_MAX
BOX2I m_bbox; ///< bounding box for the item
private:
@ -264,7 +295,7 @@ public:
m_layer( aLayer )
{
m_fillPoly = aParent->GetFilledPolysList( aLayer );
SetLayers( aLayer );
SetLayers( aLayer, aLayer );
}
void BuildRTree()
@ -399,7 +430,7 @@ public:
template <class T>
void FindNearby( CN_ITEM* aItem, T aFunc )
{
m_index.Query( aItem->BBox(), aItem->Layers(), aFunc );
m_index.Query( aItem->BBox(), aItem->StartLayer(), aItem->EndLayer(), aFunc );
}
void SetHasInvalid( bool aInvalid = true ) { m_hasInvalid = aInvalid; }

View File

@ -56,11 +56,10 @@ public:
*/
void Insert( T aItem )
{
const BOX2I& bbox = aItem->BBox();
const LAYER_RANGE layers = aItem->Layers();
const BOX2I& bbox = aItem->BBox();
const int mmin[3] = { layers.Start(), bbox.GetX(), bbox.GetY() };
const int mmax[3] = { layers.End(), bbox.GetRight(), bbox.GetBottom() };
const int mmin[3] = { aItem->StartLayer(), bbox.GetX(), bbox.GetY() };
const int mmax[3] = { aItem->EndLayer(), bbox.GetRight(), bbox.GetBottom() };
m_tree->Insert( mmin, mmax, aItem );
}
@ -75,9 +74,9 @@ public:
// First, attempt to remove the item using its given BBox
const BOX2I& bbox = aItem->BBox();
const LAYER_RANGE layers = aItem->Layers();
const int mmin[3] = { layers.Start(), bbox.GetX(), bbox.GetY() };
const int mmax[3] = { layers.End(), bbox.GetRight(), bbox.GetBottom() };
const int mmin[3] = { aItem->StartLayer(), bbox.GetX(), bbox.GetY() };
const int mmax[3] = { aItem->EndLayer(), bbox.GetRight(), bbox.GetBottom() };
// If we are not successful ( 1 == not found ), then we expand
// the search to the full tree
@ -107,10 +106,13 @@ public:
* with aBounds.
*/
template <class Visitor>
void Query( const BOX2I& aBounds, const LAYER_RANGE& aRange, Visitor& aVisitor ) const
void Query( const BOX2I& aBounds, int aStartLayer, int aEndLayer, Visitor& aVisitor ) const
{
const int mmin[3] = { aRange.Start(), aBounds.GetX(), aBounds.GetY() };
const int mmax[3] = { aRange.End(), aBounds.GetRight(), aBounds.GetBottom() };
int start_layer = aStartLayer == B_Cu ? INT_MAX : aStartLayer;
int end_layer = aEndLayer == B_Cu ? INT_MAX : aEndLayer;
const int mmin[3] = { start_layer, aBounds.GetX(), aBounds.GetY() };
const int mmax[3] = { end_layer, aBounds.GetRight(), aBounds.GetBottom() };
m_tree->Search( mmin, mmax, aVisitor );
}

View File

@ -510,7 +510,7 @@ void DIALOG_DRC::OnDRCItemSelected( wxDataViewEvent& aEvent )
WINDOW_THAWER thawer( m_frame );
if( principalLayer > UNDEFINED_LAYER && ( violationLayers & board->GetVisibleLayers() ) == 0 )
if( principalLayer > UNDEFINED_LAYER && ( violationLayers & board->GetVisibleLayers() ).none() )
m_frame->GetAppearancePanel()->SetLayerVisible( principalLayer, true );
if( principalLayer > UNDEFINED_LAYER && board->GetVisibleLayers().test( principalLayer ) )

View File

@ -292,7 +292,7 @@ void DIALOG_FOOTPRINT_CHECKER::OnSelectItem( wxDataViewEvent& aEvent )
m_frame->FocusOnItem( item );
m_frame->GetCanvas()->Refresh();
if( ( violationLayers & board->GetVisibleLayers() ) == 0 )
if( ( violationLayers & board->GetVisibleLayers() ).none() )
{
m_frame->GetAppearancePanel()->SetLayerVisible( principalLayer, true );
m_frame->GetCanvas()->Refresh();

Some files were not shown because too many files have changed in this diff Show More