From 59a4cc2e4b6bfebb32208f6dbf781217070f29c4 Mon Sep 17 00:00:00 2001
From: Ian McInerney <i.mcinerney17@imperial.ac.uk>
Date: Sun, 2 Feb 2025 00:43:03 +0000
Subject: [PATCH] Default to 4 user defined layers

---
 common/lset.cpp                               | 29 ++++++++++++++-----
 include/board_design_settings.h               | 17 +++++++++++
 include/layer_ids.h                           |  3 +-
 include/lset.h                                | 11 +++++--
 pcbnew/board.cpp                              | 11 +++++++
 pcbnew/board.h                                |  3 ++
 pcbnew/board_design_settings.cpp              | 27 +++++++++++++----
 pcbnew/dialogs/panel_setup_layers.cpp         | 12 ++++----
 pcbnew/initpcb.cpp                            |  3 ++
 pcbnew/pcb_io/altium/altium_pcb.cpp           |  2 +-
 .../cadstar/cadstar_pcb_archive_loader.cpp    |  6 ++--
 11 files changed, 98 insertions(+), 26 deletions(-)

diff --git a/common/lset.cpp b/common/lset.cpp
index 3ecd6033be..3593c2f545 100644
--- a/common/lset.cpp
+++ b/common/lset.cpp
@@ -654,17 +654,21 @@ LSET LSET::PhysicalLayersMask()
 }
 
 
-LSET LSET::UserDefinedLayers()
+LSET LSET::UserDefinedLayersMask( int aUserDefinedLayerCount )
 {
-    static const LSET saved(
-            { User_1,  User_2,  User_3,  User_4,  User_5,  User_6,  User_7,  User_8,  User_9,
-              User_10, User_11, User_12, User_13, User_14, User_15, User_16, User_17, User_18,
-              User_19, User_20, User_21, User_22, User_23, User_24, User_25, User_26, User_27,
-              User_28, User_29, User_30, User_31, User_32, User_33, User_34, User_35, User_36,
-              User_37, User_38, User_39, User_40, User_41, User_42, User_43, User_44, User_45 } );
+    LSET   ret;
+    size_t layer = User_1;
 
+    for( int ulayer = 1; ulayer <= aUserDefinedLayerCount; ulayer++ )
+    {
+        if( layer > ret.size() )
+            break;
 
-    return saved;
+        ret.set( layer );
+        layer += 2;
+    }
+
+    return ret;
 }
 
 
@@ -910,4 +914,13 @@ LSET& LSET::ClearNonCopperLayers()
     return *this;
 }
 
+
+LSET& LSET::ClearUserDefinedLayers()
+{
+    for( size_t ii = User_1; ii < size(); ii += 2 )
+        reset( ii );
+
+    return *this;
+}
+
 #endif
diff --git a/include/board_design_settings.h b/include/board_design_settings.h
index fa8bf2b7d1..0a7de517ff 100644
--- a/include/board_design_settings.h
+++ b/include/board_design_settings.h
@@ -612,6 +612,21 @@ public:
      */
     void SetCopperLayerCount( int aNewLayerCount );
 
+    /**
+     * @return the number of enabled user defined layers.
+     */
+    inline int GetUserDefinedLayerCount() const
+    {
+        return m_userDefinedLayerCount;
+    }
+
+    /**
+     * Set the number of user defined layers to \a aNewLayerCount.
+     *
+     * @param aNewLayerCount The new number of enabled user defined layers.
+     */
+    void SetUserDefinedLayerCount( int aNewLayerCount );
+
     /**
      * The full thickness of the board including copper and masks.
      * @return
@@ -796,6 +811,8 @@ private:
 
     int        m_copperLayerCount; ///< Number of copper layers for this design
 
+    int        m_userDefinedLayerCount; ///< Number of user defined layers for this design
+
     LSET       m_enabledLayers;    ///< Bit-mask for layer enabling
 
     int        m_boardThickness;   ///< Board thickness for 3D viewer
diff --git a/include/layer_ids.h b/include/layer_ids.h
index bcb38ba58e..af2108cd3c 100644
--- a/include/layer_ids.h
+++ b/include/layer_ids.h
@@ -173,7 +173,8 @@ enum PCB_LAYER_ID: int
 
 constexpr PCB_LAYER_ID PCBNEW_LAYER_ID_START = F_Cu;
 
-#define MAX_CU_LAYERS       32
+#define MAX_CU_LAYERS           32
+#define MAX_USER_DEFINED_LAYERS 45
 
 /**
  * Enum used during connectivity building to ensure we do not query connectivity while building
diff --git a/include/lset.h b/include/lset.h
index 390b2c7103..79072ddc2a 100644
--- a/include/lset.h
+++ b/include/lset.h
@@ -185,9 +185,11 @@ public:
     static LSET PhysicalLayersMask();
 
     /**
-     * Return a mask with all of the allowable user defined layers.
+     * Return a mask with the requested number of user defined layers.
+     *
+     * @param aUserDefinedLayerCount The number of user defined layers
      */
-    static LSET UserDefinedLayers();
+    static LSET UserDefinedLayersMask( int aUserDefinedLayerCount = MAX_USER_DEFINED_LAYERS );
 
     /**
      * Layers which are not allowed within footprint definitions.
@@ -294,6 +296,11 @@ public:
      */
     LSET& ClearNonCopperLayers();
 
+    /**
+     * Clear the user defined layers in this set.
+     */
+    LSET& ClearUserDefinedLayers();
+
 #ifndef SWIG
     // Custom iterator to iterate over all set bits
     class KICOMMON_API all_set_layers_iterator : public BASE_SET::set_bits_iterator
diff --git a/pcbnew/board.cpp b/pcbnew/board.cpp
index 64a7215960..b26bf6d080 100644
--- a/pcbnew/board.cpp
+++ b/pcbnew/board.cpp
@@ -792,6 +792,17 @@ void BOARD::SetCopperLayerCount( int aCount )
 }
 
 
+int BOARD::GetUserDefinedLayerCount() const
+{
+    return GetDesignSettings().GetUserDefinedLayerCount();
+}
+
+
+void BOARD::SetUserDefinedLayerCount( int aCount )
+{
+    return GetDesignSettings().SetUserDefinedLayerCount( aCount );
+}
+
 PCB_LAYER_ID BOARD::GetCopperLayerStackMaxId() const
 {
     int imax = GetCopperLayerCount();
diff --git a/pcbnew/board.h b/pcbnew/board.h
index b5695487ca..b1a8ad09bb 100644
--- a/pcbnew/board.h
+++ b/pcbnew/board.h
@@ -578,6 +578,9 @@ public:
     int  GetCopperLayerCount() const;
     void SetCopperLayerCount( int aCount );
 
+    int GetUserDefinedLayerCount() const;
+    void SetUserDefinedLayerCount( int aCount );
+
     /**
      * @return The copper layer max PCB_LAYER_ID in the BOARD.
      * similar to GetCopperLayerCount(), but returns the max PCB_LAYER_ID
diff --git a/pcbnew/board_design_settings.cpp b/pcbnew/board_design_settings.cpp
index ca48334a7f..f78499c439 100644
--- a/pcbnew/board_design_settings.cpp
+++ b/pcbnew/board_design_settings.cpp
@@ -57,10 +57,13 @@ BOARD_DESIGN_SETTINGS::BOARD_DESIGN_SETTINGS( JSON_SETTINGS* aParent, const std:
     SetDefaultMasterPad();
 
     LSET all_set = LSET().set();
-    m_enabledLayers = all_set;              // All layers enabled at first.
-                                            // SetCopperLayerCount() will adjust this.
+    m_enabledLayers = all_set;     // All layers enabled at first.
+                                   // SetCopperLayerCount() will adjust this.
+
+    // Default design is a double layer board with 4 user defined layers
+    SetCopperLayerCount( 2 );
+    SetUserDefinedLayerCount( 4 );
 
-    SetCopperLayerCount( 2 );               // Default design is a double sided board
     m_CurrentViaType = VIATYPE::THROUGH;
 
     // if true, when creating a new track starting on an existing track, use this track width
@@ -1020,6 +1023,7 @@ void BOARD_DESIGN_SETTINGS::initFromOther( const BOARD_DESIGN_SETTINGS& aOther )
     m_useCustomDiffPair   = aOther.m_useCustomDiffPair;
     m_customDiffPair      = aOther.m_customDiffPair;
     m_copperLayerCount    = aOther.m_copperLayerCount;
+    m_userDefinedLayerCount = aOther.m_userDefinedLayerCount;
     m_enabledLayers       = aOther.m_enabledLayers;
     m_boardThickness      = aOther.m_boardThickness;
     m_currentNetClassName = aOther.m_currentNetClassName;
@@ -1115,6 +1119,7 @@ bool BOARD_DESIGN_SETTINGS::operator==( const BOARD_DESIGN_SETTINGS& aOther ) co
     if( m_useCustomDiffPair        != aOther.m_useCustomDiffPair ) return false;
     if( m_customDiffPair           != aOther.m_customDiffPair ) return false;
     if( m_copperLayerCount         != aOther.m_copperLayerCount ) return false;
+    if( m_userDefinedLayerCount    != aOther.m_userDefinedLayerCount ) return false;
     if( m_enabledLayers            != aOther.m_enabledLayers ) return false;
     if( m_boardThickness           != aOther.m_boardThickness ) return false;
     if( m_currentNetClassName      != aOther.m_currentNetClassName ) return false;
@@ -1446,6 +1451,17 @@ void BOARD_DESIGN_SETTINGS::SetCopperLayerCount( int aNewLayerCount )
 }
 
 
+void BOARD_DESIGN_SETTINGS::SetUserDefinedLayerCount( int aNewLayerCount )
+{
+    m_userDefinedLayerCount = aNewLayerCount;
+
+    m_enabledLayers.ClearUserDefinedLayers();
+
+    if( aNewLayerCount > 0 )
+        m_enabledLayers |= LSET::UserDefinedLayersMask( aNewLayerCount );
+}
+
+
 void BOARD_DESIGN_SETTINGS::SetEnabledLayers( LSET aMask )
 {
     // Ensures mandatory back and front layers are always enabled regardless of board file
@@ -1457,8 +1473,9 @@ void BOARD_DESIGN_SETTINGS::SetEnabledLayers( LSET aMask )
 
     m_enabledLayers = aMask;
 
-    // update m_CopperLayerCount to ensure its consistency with m_EnabledLayers
-    m_copperLayerCount = (int) aMask.ClearNonCopperLayers().count();
+    // update layer counts to ensure their consistency with m_EnabledLayers
+    m_copperLayerCount      = (int) aMask.ClearNonCopperLayers().count();
+    m_userDefinedLayerCount = (int) ( aMask & LSET::UserDefinedLayersMask() ).count();
 }
 
 
diff --git a/pcbnew/dialogs/panel_setup_layers.cpp b/pcbnew/dialogs/panel_setup_layers.cpp
index 86c8213bab..b57fa57ef8 100644
--- a/pcbnew/dialogs/panel_setup_layers.cpp
+++ b/pcbnew/dialogs/panel_setup_layers.cpp
@@ -450,7 +450,7 @@ void PANEL_SETUP_LAYERS::initialize_layers_controls()
     m_layersControls[Cmts_User] = PANEL_SETUP_LAYERS_CTLs( m_CommentsName, m_CommentsCheckBox, m_CommentsStaticText );
     m_layersControls[Dwgs_User] = PANEL_SETUP_LAYERS_CTLs( m_DrawingsName, m_DrawingsCheckBox, m_DrawingsStaticText );
 
-    layers &= LSET::UserDefinedLayers();
+    layers &= LSET::UserDefinedLayersMask();
 
     for( auto it = layers.non_copper_layers_begin(); it != layers.non_copper_layers_end(); ++it )
     {
@@ -554,7 +554,7 @@ void PANEL_SETUP_LAYERS::SyncCopperLayers( int aNumCopperLayers )
 
 void PANEL_SETUP_LAYERS::setUserDefinedLayerCheckBoxes()
 {
-    LSET layers = m_enabledLayers & LSET::UserDefinedLayers();
+    LSET layers = m_enabledLayers & LSET::UserDefinedLayersMask();
 
     for( PCB_LAYER_ID layer : layers )
         setLayerCheckBox( layer, m_pcb->IsLayerEnabled( layer ) );
@@ -609,7 +609,7 @@ void PANEL_SETUP_LAYERS::showLayerTypes()
 
     }
 
-    layers = m_enabledLayers & LSET::UserDefinedLayers();
+    layers = m_enabledLayers & LSET::UserDefinedLayersMask();
 
     for( PCB_LAYER_ID layer : layers )
     {
@@ -891,7 +891,7 @@ bool PANEL_SETUP_LAYERS::TransferDataFromWindow()
         }
     }
 
-    LSET layers = m_enabledLayers & LSET::UserDefinedLayers();
+    LSET layers = m_enabledLayers & LSET::UserDefinedLayersMask();
 
     for( PCB_LAYER_ID layer : layers )
     {
@@ -1127,7 +1127,7 @@ void PANEL_SETUP_LAYERS::addUserDefinedLayer( wxCommandEvent& aEvent )
     // Build the available user-defined layers list:
     std::vector<wxArrayString> list;
 
-    for( PCB_LAYER_ID layer : LSET::UserDefinedLayers().Seq() )
+    for( PCB_LAYER_ID layer : LSET::UserDefinedLayersMask().Seq() )
     {
         wxCheckBox* checkBox = getCheckBox( layer );
 
@@ -1157,7 +1157,7 @@ void PANEL_SETUP_LAYERS::addUserDefinedLayer( wxCommandEvent& aEvent )
 
     PCB_LAYER_ID layer = UNDEFINED_LAYER;
 
-    for( PCB_LAYER_ID layer2 : LSET::UserDefinedLayers().Seq() )
+    for( PCB_LAYER_ID layer2 : LSET::UserDefinedLayersMask().Seq() )
     {
         if( LayerName( layer2 ) == dlg.GetTextSelection() )
         {
diff --git a/pcbnew/initpcb.cpp b/pcbnew/initpcb.cpp
index e00647e314..6aeff8c45e 100644
--- a/pcbnew/initpcb.cpp
+++ b/pcbnew/initpcb.cpp
@@ -79,6 +79,9 @@ bool PCB_EDIT_FRAME::Clear_Pcb( bool doAskAboutUnsavedChanges, bool aFinal )
         // Default copper layers count set to 2: double layer board
         GetBoard()->SetCopperLayerCount( 2 );
 
+        // Default user defined layers count set to 4
+        GetBoard()->SetUserDefinedLayerCount( 4 );
+
         // Update display (some options depend on the board setup)
         GetBoard()->SetVisibleLayers( LSET().set() );
         ReCreateLayerBox();
diff --git a/pcbnew/pcb_io/altium/altium_pcb.cpp b/pcbnew/pcb_io/altium/altium_pcb.cpp
index f98e8cd47f..1af55e9e73 100644
--- a/pcbnew/pcb_io/altium/altium_pcb.cpp
+++ b/pcbnew/pcb_io/altium/altium_pcb.cpp
@@ -1121,7 +1121,7 @@ void ALTIUM_PCB::remapUnsureLayers( std::vector<ABOARD6_LAYER_STACKUP>& aStackup
 {
     LSET enabledLayers        = m_board->GetEnabledLayers();
     LSET validRemappingLayers = enabledLayers    | LSET::AllBoardTechMask() |
-                                LSET::UserMask() | LSET::UserDefinedLayers();
+                                LSET::UserMask() | LSET::UserDefinedLayersMask();
 
     if( aStackup.size() == 0 )
         return;
diff --git a/pcbnew/pcb_io/cadstar/cadstar_pcb_archive_loader.cpp b/pcbnew/pcb_io/cadstar/cadstar_pcb_archive_loader.cpp
index b3e0ea4ae2..f4266db6f0 100644
--- a/pcbnew/pcb_io/cadstar/cadstar_pcb_archive_loader.cpp
+++ b/pcbnew/pcb_io/cadstar/cadstar_pcb_archive_loader.cpp
@@ -687,7 +687,7 @@ void CADSTAR_PCB_ARCHIVE_LOADER::remapUnsureLayers()
 {
     LSET enabledLayers        = m_board->GetEnabledLayers();
     LSET validRemappingLayers = enabledLayers    | LSET::AllBoardTechMask() |
-                                LSET::UserMask() | LSET::UserDefinedLayers();
+                                LSET::UserMask() | LSET::UserDefinedLayersMask();
 
     std::vector<INPUT_LAYER_DESC> inputLayers;
     std::map<wxString, LAYER_ID>  cadstarLayerNameMap;
@@ -4167,7 +4167,7 @@ LSET CADSTAR_PCB_ARCHIVE_LOADER::getKiCadLayerSet( const LAYER_ID& aCadstarLayer
                        PCB_LAYER_ID::Cmts_User,
                        PCB_LAYER_ID::Eco1_User,
                        PCB_LAYER_ID::Eco2_User } )
-                | LSET::UserDefinedLayers();
+                | LSET::UserDefinedLayersMask();
 
     case LAYER_TYPE::ALLELEC:
         return LSET::AllCuMask( m_numCopperLayers );
@@ -4178,7 +4178,7 @@ LSET CADSTAR_PCB_ARCHIVE_LOADER::getKiCadLayerSet( const LAYER_ID& aCadstarLayer
                           PCB_LAYER_ID::Cmts_User,
                           PCB_LAYER_ID::Eco1_User,
                           PCB_LAYER_ID::Eco2_User } )
-                | LSET::UserDefinedLayers()
+                | LSET::UserDefinedLayersMask()
                 | LSET::AllBoardTechMask();
 
     default: