From 0eb8f92c690f4480e14b066536aa7483e4244f98 Mon Sep 17 00:00:00 2001
From: John Beard <john.j.beard@gmail.com>
Date: Wed, 15 May 2019 22:11:13 +0100
Subject: [PATCH] Pcbnew: add a step field to the array tool

This is useful, for example, when arraying only one side
of a connector.

Fixes: lp:1809580
* https://bugs.launchpad.net/kicad/+bug/1809580
---
 common/array_axis.cpp                       |  10 +-
 include/array_axis.h                        |   9 +
 pcbnew/dialogs/dialog_create_array.cpp      |  80 +++--
 pcbnew/dialogs/dialog_create_array_base.cpp |  51 ++-
 pcbnew/dialogs/dialog_create_array_base.fbp | 350 +++++++++++++++++++-
 pcbnew/dialogs/dialog_create_array_base.h   |   5 +
 qa/common/test_array_axis.cpp               |  17 +-
 7 files changed, 464 insertions(+), 58 deletions(-)

diff --git a/common/array_axis.cpp b/common/array_axis.cpp
index 8737f7b62c..507a2bfc4a 100644
--- a/common/array_axis.cpp
+++ b/common/array_axis.cpp
@@ -35,7 +35,7 @@ static bool schemeNonUnitColsStartAt0( ARRAY_AXIS::NUMBERING_TYPE type )
 }
 
 
-ARRAY_AXIS::ARRAY_AXIS() : m_type( NUMBERING_TYPE::NUMBERING_NUMERIC ), m_offset( 0 )
+ARRAY_AXIS::ARRAY_AXIS() : m_type( NUMBERING_TYPE::NUMBERING_NUMERIC ), m_offset( 0 ), m_step( 1 )
 {
 }
 
@@ -124,6 +124,12 @@ int ARRAY_AXIS::GetOffset() const
 }
 
 
+void ARRAY_AXIS::SetStep( int aStep )
+{
+    m_step = aStep;
+}
+
+
 wxString ARRAY_AXIS::GetItemNumber( int n ) const
 {
     wxString        itemNum;
@@ -134,7 +140,7 @@ wxString ARRAY_AXIS::GetItemNumber( int n ) const
     bool firstRound = true;
     int  radix = alphabet.Length();
 
-    n += m_offset;
+    n = m_offset + m_step * n;
 
     do
     {
diff --git a/include/array_axis.h b/include/array_axis.h
index 49388b2f95..0f05f25710 100644
--- a/include/array_axis.h
+++ b/include/array_axis.h
@@ -86,6 +86,12 @@ public:
      */
     int GetOffset() const;
 
+    /**
+     * Set the skip between consecutive numbers (useful when doing a partial
+     * array, e.g. only one side of a connector)
+     */
+    void SetStep( int aStep );
+
     /**
      * Get the position number (name) for the n'th axis point
      *
@@ -105,6 +111,9 @@ private:
 
     NUMBERING_TYPE m_type;
     int            m_offset;
+
+    ///< Skip every 'n' numbers
+    int m_step;
 };
 
 #endif // ARRAY_AXIS__H
\ No newline at end of file
diff --git a/pcbnew/dialogs/dialog_create_array.cpp b/pcbnew/dialogs/dialog_create_array.cpp
index cc4951d392..5979fa0b6c 100644
--- a/pcbnew/dialogs/dialog_create_array.cpp
+++ b/pcbnew/dialogs/dialog_create_array.cpp
@@ -60,12 +60,15 @@ struct CREATE_ARRAY_DIALOG_ENTRIES
               m_gridSecAxisNumScheme( 0 ),     // numeric
               m_gridPriNumberingOffset( "1" ), // numeric
               m_gridSecNumberingOffset( "1" ), // numeric
+              m_gridPriAxisStep( 1 ),
+              m_gridSecAxisStep( 1 ),
               m_circCentreX( 0 ),
               m_circCentreY( 0 ),
               m_circAngle( 0.0 ),
               m_circCount( 4 ),
               m_circNumberingStartSet( 1 ), // use specified start
               m_circNumberingOffset( "1" ),
+              m_circNumberingStep( 1 ),
               m_circRotate( false ),
               m_arrayTypeTab( 0 ) // start on grid view
     {
@@ -84,6 +87,7 @@ struct CREATE_ARRAY_DIALOG_ENTRIES
     long     m_grid2dArrayNumbering;
     long     m_gridPriAxisNumScheme, m_gridSecAxisNumScheme;
     wxString m_gridPriNumberingOffset, m_gridSecNumberingOffset;
+    long     m_gridPriAxisStep, m_gridSecAxisStep;
 
     long     m_circCentreX, m_circCentreY;
     long     m_circAngle;
@@ -91,6 +95,7 @@ struct CREATE_ARRAY_DIALOG_ENTRIES
     long     m_circNumberingStartSet;
     long     m_gridCircNumScheme;
     wxString m_circNumberingOffset;
+    long     m_circNumberingStep;
     bool     m_circRotate;
     long     m_arrayTypeTab;
 };
@@ -189,6 +194,8 @@ DIALOG_CREATE_ARRAY::DIALOG_CREATE_ARRAY( PCB_BASE_FRAME* aParent,
             *m_entryGridPriNumberingOffset, saved_array_options.m_gridPriNumberingOffset );
     m_cfg_persister.Add(
             *m_entryGridSecNumberingOffset, saved_array_options.m_gridSecNumberingOffset );
+    m_cfg_persister.Add( *m_entryGridPriNumberingStep, saved_array_options.m_gridPriAxisStep );
+    m_cfg_persister.Add( *m_entryGridSecNumberingStep, saved_array_options.m_gridSecAxisStep );
 
     // bind circular options to persister
     m_cfg_persister.Add( m_hCentre, saved_array_options.m_circCentreX );
@@ -200,6 +207,7 @@ DIALOG_CREATE_ARRAY::DIALOG_CREATE_ARRAY( PCB_BASE_FRAME* aParent,
     m_cfg_persister.Add( *m_rbCircStartNumberingOpt, saved_array_options.m_circNumberingStartSet );
     m_cfg_persister.Add( *m_choiceCircNumbering, saved_array_options.m_gridCircNumScheme );
     m_cfg_persister.Add( *m_entryCircNumberingStart, saved_array_options.m_circNumberingOffset );
+    m_cfg_persister.Add( *m_entryCircNumberingStep, saved_array_options.m_circNumberingStep );
 
     m_cfg_persister.Add( *m_gridTypeNotebook, saved_array_options.m_arrayTypeTab );
 
@@ -222,6 +230,32 @@ void DIALOG_CREATE_ARRAY::OnParameterChanged( wxCommandEvent& event )
 }
 
 
+/**
+ * Validate and save a long integer entry
+ *
+ * @param entry the text entry to read from
+ * @param dest the value destination
+ * @param description description of the field (used if the value is not OK)
+ * @param errors a list of errors to add any error to
+ * @return valid
+ */
+static bool validateLongEntry(
+        const wxTextEntry& entry, long& dest, const wxString& description, wxArrayString& errors )
+{
+    bool ok = true;
+
+    if( !entry.GetValue().ToLong( &dest ) )
+    {
+        wxString err;
+        err.Printf( _( "Bad numeric value for %s: %s" ), description, entry.GetValue() );
+        errors.Add( err );
+        ok = false;
+    }
+
+    return ok;
+}
+
+
 /**
  * Validates and saves (if valid) the type and offset of an array axis numbering
  *
@@ -232,8 +266,8 @@ void DIALOG_CREATE_ARRAY::OnParameterChanged( wxCommandEvent& event )
  * @param errors error string accumulator
  * @return if all valid
  */
-static bool validateNumberingTypeAndOffset( const wxTextCtrl& offsetEntry,
-        const wxChoice& typeEntry, ARRAY_AXIS& aAxis, wxArrayString& errors )
+static bool validateAxisOptions( const wxTextCtrl& offsetEntry, const wxChoice& typeEntry,
+        const wxTextCtrl& aStepEntry, ARRAY_AXIS& aAxis, wxArrayString& errors )
 {
     const auto* typeData = static_cast<NUMBERING_LIST_DATA*>(
             typeEntry.GetClientData( typeEntry.GetSelection() ) );
@@ -258,31 +292,11 @@ static bool validateNumberingTypeAndOffset( const wxTextCtrl& offsetEntry,
         return false;
     }
 
-    return ok;
-}
+    long step;
+    ok = validateLongEntry( aStepEntry, step, _( "step" ), errors );
 
-
-/**
- * Validate and save a long integer entry
- *
- * @param entry the text entry to read from
- * @param dest the value destination
- * @param description description of the field (used if the value is not OK)
- * @param errors a list of errors to add any error to
- * @return valid
- */
-static bool validateLongEntry( const wxTextEntry& entry, long& dest, const wxString& description,
-                               wxArrayString& errors )
-{
-    bool ok = true;
-
-    if( !entry.GetValue().ToLong( &dest ) )
-    {
-        wxString err;
-        err.Printf( _("Bad numeric value for %s: %s"), description, entry.GetValue() );
-        errors.Add( err );
-        ok = false;
-     }
+    if( ok )
+        aAxis.SetStep( step );
 
     return ok;
 }
@@ -328,13 +342,15 @@ bool DIALOG_CREATE_ARRAY::TransferDataFromWindow()
                 newGrid->m_2dArrayNumbering = m_radioBoxGridNumberingScheme->GetSelection() != 0;
 
                 // validate from the input fields
-                bool numOk = validateNumberingTypeAndOffset( *m_entryGridPriNumberingOffset,
-                        *m_choicePriAxisNumbering, newGrid->m_pri_axis, errors );
+                bool numOk = validateAxisOptions( *m_entryGridPriNumberingOffset,
+                        *m_choicePriAxisNumbering, *m_entryGridPriNumberingStep,
+                        newGrid->m_pri_axis, errors );
 
                 if( newGrid->m_2dArrayNumbering )
                 {
-                    numOk = validateNumberingTypeAndOffset( *m_entryGridSecNumberingOffset,
-                                    *m_choiceSecAxisNumbering, newGrid->m_sec_axis, errors )
+                    numOk = validateAxisOptions( *m_entryGridSecNumberingOffset,
+                                    *m_choiceSecAxisNumbering, *m_entryGridSecNumberingStep,
+                                    newGrid->m_sec_axis, errors )
                             && numOk;
                 }
 
@@ -374,8 +390,8 @@ bool DIALOG_CREATE_ARRAY::TransferDataFromWindow()
             if( newCirc->GetNumberingStartIsSpecified() )
             {
                 ok = ok
-                     && validateNumberingTypeAndOffset( *m_entryCircNumberingStart,
-                             *m_choiceCircNumbering, newCirc->m_axis, errors );
+                     && validateAxisOptions( *m_entryCircNumberingStart, *m_choiceCircNumbering,
+                             *m_entryCircNumberingStep, newCirc->m_axis, errors );
             }
             else
             {
diff --git a/pcbnew/dialogs/dialog_create_array_base.cpp b/pcbnew/dialogs/dialog_create_array_base.cpp
index 231fc0f144..66c6c7eea2 100644
--- a/pcbnew/dialogs/dialog_create_array_base.cpp
+++ b/pcbnew/dialogs/dialog_create_array_base.cpp
@@ -150,25 +150,42 @@ DIALOG_CREATE_ARRAY_BASE::DIALOG_CREATE_ARRAY_BASE( wxWindow* parent, wxWindowID
 
 	m_gridPadNumberingSizer->Add( m_choiceSecAxisNumbering, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
 
-	wxBoxSizer* bSizer5;
-	bSizer5 = new wxBoxSizer( wxHORIZONTAL );
+	wxFlexGridSizer* fgSizer1;
+	fgSizer1 = new wxFlexGridSizer( 2, 3, 0, 0 );
+	fgSizer1->AddGrowableCol( 0 );
+	fgSizer1->SetFlexibleDirection( wxBOTH );
+	fgSizer1->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
 
 	m_labelGridNumberingOffset = new wxStaticText( m_gridPanel, wxID_ANY, _("Pad numbering start:"), wxDefaultPosition, wxDefaultSize, 0 );
 	m_labelGridNumberingOffset->Wrap( -1 );
-	bSizer5->Add( m_labelGridNumberingOffset, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
+	fgSizer1->Add( m_labelGridNumberingOffset, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
 
 	m_entryGridPriNumberingOffset = new wxTextCtrl( m_gridPanel, wxID_ANY, _("1"), wxDefaultPosition, wxDefaultSize, 0 );
 	m_entryGridPriNumberingOffset->SetMinSize( wxSize( 72,-1 ) );
 
-	bSizer5->Add( m_entryGridPriNumberingOffset, 0, wxALL, 5 );
+	fgSizer1->Add( m_entryGridPriNumberingOffset, 0, wxALL, 5 );
 
 	m_entryGridSecNumberingOffset = new wxTextCtrl( m_gridPanel, wxID_ANY, _("1"), wxDefaultPosition, wxDefaultSize, 0 );
 	m_entryGridSecNumberingOffset->SetMinSize( wxSize( 72,-1 ) );
 
-	bSizer5->Add( m_entryGridSecNumberingOffset, 0, wxALL, 5 );
+	fgSizer1->Add( m_entryGridSecNumberingOffset, 0, wxALL, 5 );
+
+	m_labelGridNumberingStep = new wxStaticText( m_gridPanel, wxID_ANY, _("Pad numbering skip:"), wxDefaultPosition, wxDefaultSize, 0 );
+	m_labelGridNumberingStep->Wrap( -1 );
+	fgSizer1->Add( m_labelGridNumberingStep, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
+
+	m_entryGridPriNumberingStep = new wxTextCtrl( m_gridPanel, wxID_ANY, _("1"), wxDefaultPosition, wxDefaultSize, 0 );
+	m_entryGridPriNumberingStep->SetMinSize( wxSize( 72,-1 ) );
+
+	fgSizer1->Add( m_entryGridPriNumberingStep, 0, wxALL, 5 );
+
+	m_entryGridSecNumberingStep = new wxTextCtrl( m_gridPanel, wxID_ANY, _("1"), wxDefaultPosition, wxDefaultSize, 0 );
+	m_entryGridSecNumberingStep->SetMinSize( wxSize( 72,-1 ) );
+
+	fgSizer1->Add( m_entryGridSecNumberingStep, 0, wxALL, 5 );
 
 
-	m_gridPadNumberingSizer->Add( bSizer5, 0, wxEXPAND, 5 );
+	m_gridPadNumberingSizer->Add( fgSizer1, 1, wxEXPAND, 5 );
 
 
 	bSizer2->Add( m_gridPadNumberingSizer, 0, wxALL|wxEXPAND, 5 );
@@ -276,18 +293,28 @@ DIALOG_CREATE_ARRAY_BASE::DIALOG_CREATE_ARRAY_BASE( wxWindow* parent, wxWindowID
 	m_choiceCircNumbering->SetSelection( 0 );
 	m_circPadNumberingSizer->Add( m_choiceCircNumbering, 0, wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT, 5 );
 
-	wxBoxSizer* bSizer7;
-	bSizer7 = new wxBoxSizer( wxHORIZONTAL );
+	wxFlexGridSizer* fgSizer2;
+	fgSizer2 = new wxFlexGridSizer( 0, 2, 0, 0 );
+	fgSizer2->AddGrowableCol( 0 );
+	fgSizer2->SetFlexibleDirection( wxBOTH );
+	fgSizer2->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
 
-	m_labelCircNumStart = new wxStaticText( m_circPadNumberingSizer->GetStaticBox(), wxID_ANY, _("Pad numbering start value:"), wxDefaultPosition, wxDefaultSize, 0 );
+	m_labelCircNumStart = new wxStaticText( m_circPadNumberingSizer->GetStaticBox(), wxID_ANY, _("Pad numbering start:"), wxDefaultPosition, wxDefaultSize, 0 );
 	m_labelCircNumStart->Wrap( -1 );
-	bSizer7->Add( m_labelCircNumStart, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
+	fgSizer2->Add( m_labelCircNumStart, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
 
 	m_entryCircNumberingStart = new wxTextCtrl( m_circPadNumberingSizer->GetStaticBox(), wxID_ANY, _("1"), wxDefaultPosition, wxDefaultSize, 0 );
-	bSizer7->Add( m_entryCircNumberingStart, 1, wxALL, 5 );
+	fgSizer2->Add( m_entryCircNumberingStart, 1, wxALL, 5 );
+
+	m_labelCircNumStep = new wxStaticText( m_circPadNumberingSizer->GetStaticBox(), wxID_ANY, _("Pad numbering skip:"), wxDefaultPosition, wxDefaultSize, 0 );
+	m_labelCircNumStep->Wrap( -1 );
+	fgSizer2->Add( m_labelCircNumStep, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
+
+	m_entryCircNumberingStep = new wxTextCtrl( m_circPadNumberingSizer->GetStaticBox(), wxID_ANY, _("1"), wxDefaultPosition, wxDefaultSize, 0 );
+	fgSizer2->Add( m_entryCircNumberingStep, 0, wxALL, 5 );
 
 
-	m_circPadNumberingSizer->Add( bSizer7, 0, wxEXPAND, 5 );
+	m_circPadNumberingSizer->Add( fgSizer2, 1, wxEXPAND, 5 );
 
 
 	bSizer4->Add( m_circPadNumberingSizer, 0, wxEXPAND|wxALL, 5 );
diff --git a/pcbnew/dialogs/dialog_create_array_base.fbp b/pcbnew/dialogs/dialog_create_array_base.fbp
index 2b9c4d9bb1..c8c7fe732f 100644
--- a/pcbnew/dialogs/dialog_create_array_base.fbp
+++ b/pcbnew/dialogs/dialog_create_array_base.fbp
@@ -1977,15 +1977,22 @@
                                                     <property name="window_style"></property>
                                                 </object>
                                             </object>
-                                            <object class="sizeritem" expanded="0">
+                                            <object class="sizeritem" expanded="1">
                                                 <property name="border">5</property>
                                                 <property name="flag">wxEXPAND</property>
-                                                <property name="proportion">0</property>
-                                                <object class="wxBoxSizer" expanded="0">
+                                                <property name="proportion">1</property>
+                                                <object class="wxFlexGridSizer" expanded="1">
+                                                    <property name="cols">3</property>
+                                                    <property name="flexible_direction">wxBOTH</property>
+                                                    <property name="growablecols">0</property>
+                                                    <property name="growablerows"></property>
+                                                    <property name="hgap">0</property>
                                                     <property name="minimum_size"></property>
-                                                    <property name="name">bSizer5</property>
-                                                    <property name="orient">wxHORIZONTAL</property>
+                                                    <property name="name">fgSizer1</property>
+                                                    <property name="non_flexible_grow_mode">wxFLEX_GROWMODE_SPECIFIED</property>
                                                     <property name="permission">none</property>
+                                                    <property name="rows">2</property>
+                                                    <property name="vgap">0</property>
                                                     <object class="sizeritem" expanded="0">
                                                         <property name="border">5</property>
                                                         <property name="flag">wxALIGN_CENTER_VERTICAL|wxALL</property>
@@ -2175,6 +2182,195 @@
                                                             <property name="window_style"></property>
                                                         </object>
                                                     </object>
+                                                    <object class="sizeritem" expanded="1">
+                                                        <property name="border">5</property>
+                                                        <property name="flag">wxALIGN_CENTER_VERTICAL|wxALL</property>
+                                                        <property name="proportion">1</property>
+                                                        <object class="wxStaticText" expanded="1">
+                                                            <property name="BottomDockable">1</property>
+                                                            <property name="LeftDockable">1</property>
+                                                            <property name="RightDockable">1</property>
+                                                            <property name="TopDockable">1</property>
+                                                            <property name="aui_layer"></property>
+                                                            <property name="aui_name"></property>
+                                                            <property name="aui_position"></property>
+                                                            <property name="aui_row"></property>
+                                                            <property name="best_size"></property>
+                                                            <property name="bg"></property>
+                                                            <property name="caption"></property>
+                                                            <property name="caption_visible">1</property>
+                                                            <property name="center_pane">0</property>
+                                                            <property name="close_button">1</property>
+                                                            <property name="context_help"></property>
+                                                            <property name="context_menu">1</property>
+                                                            <property name="default_pane">0</property>
+                                                            <property name="dock">Dock</property>
+                                                            <property name="dock_fixed">0</property>
+                                                            <property name="docking">Left</property>
+                                                            <property name="enabled">1</property>
+                                                            <property name="fg"></property>
+                                                            <property name="floatable">1</property>
+                                                            <property name="font"></property>
+                                                            <property name="gripper">0</property>
+                                                            <property name="hidden">0</property>
+                                                            <property name="id">wxID_ANY</property>
+                                                            <property name="label">Pad numbering skip:</property>
+                                                            <property name="markup">0</property>
+                                                            <property name="max_size"></property>
+                                                            <property name="maximize_button">0</property>
+                                                            <property name="maximum_size"></property>
+                                                            <property name="min_size"></property>
+                                                            <property name="minimize_button">0</property>
+                                                            <property name="minimum_size"></property>
+                                                            <property name="moveable">1</property>
+                                                            <property name="name">m_labelGridNumberingStep</property>
+                                                            <property name="pane_border">1</property>
+                                                            <property name="pane_position"></property>
+                                                            <property name="pane_size"></property>
+                                                            <property name="permission">protected</property>
+                                                            <property name="pin_button">1</property>
+                                                            <property name="pos"></property>
+                                                            <property name="resize">Resizable</property>
+                                                            <property name="show">1</property>
+                                                            <property name="size"></property>
+                                                            <property name="style"></property>
+                                                            <property name="subclass"></property>
+                                                            <property name="toolbar_pane">0</property>
+                                                            <property name="tooltip"></property>
+                                                            <property name="window_extra_style"></property>
+                                                            <property name="window_name"></property>
+                                                            <property name="window_style"></property>
+                                                            <property name="wrap">-1</property>
+                                                        </object>
+                                                    </object>
+                                                    <object class="sizeritem" expanded="1">
+                                                        <property name="border">5</property>
+                                                        <property name="flag">wxALL</property>
+                                                        <property name="proportion">0</property>
+                                                        <object class="wxTextCtrl" expanded="1">
+                                                            <property name="BottomDockable">1</property>
+                                                            <property name="LeftDockable">1</property>
+                                                            <property name="RightDockable">1</property>
+                                                            <property name="TopDockable">1</property>
+                                                            <property name="aui_layer"></property>
+                                                            <property name="aui_name"></property>
+                                                            <property name="aui_position"></property>
+                                                            <property name="aui_row"></property>
+                                                            <property name="best_size"></property>
+                                                            <property name="bg"></property>
+                                                            <property name="caption"></property>
+                                                            <property name="caption_visible">1</property>
+                                                            <property name="center_pane">0</property>
+                                                            <property name="close_button">1</property>
+                                                            <property name="context_help"></property>
+                                                            <property name="context_menu">1</property>
+                                                            <property name="default_pane">0</property>
+                                                            <property name="dock">Dock</property>
+                                                            <property name="dock_fixed">0</property>
+                                                            <property name="docking">Left</property>
+                                                            <property name="enabled">1</property>
+                                                            <property name="fg"></property>
+                                                            <property name="floatable">1</property>
+                                                            <property name="font"></property>
+                                                            <property name="gripper">0</property>
+                                                            <property name="hidden">0</property>
+                                                            <property name="id">wxID_ANY</property>
+                                                            <property name="max_size"></property>
+                                                            <property name="maximize_button">0</property>
+                                                            <property name="maximum_size"></property>
+                                                            <property name="maxlength">0</property>
+                                                            <property name="min_size"></property>
+                                                            <property name="minimize_button">0</property>
+                                                            <property name="minimum_size">72,-1</property>
+                                                            <property name="moveable">1</property>
+                                                            <property name="name">m_entryGridPriNumberingStep</property>
+                                                            <property name="pane_border">1</property>
+                                                            <property name="pane_position"></property>
+                                                            <property name="pane_size"></property>
+                                                            <property name="permission">protected</property>
+                                                            <property name="pin_button">1</property>
+                                                            <property name="pos"></property>
+                                                            <property name="resize">Resizable</property>
+                                                            <property name="show">1</property>
+                                                            <property name="size"></property>
+                                                            <property name="style"></property>
+                                                            <property name="subclass"></property>
+                                                            <property name="toolbar_pane">0</property>
+                                                            <property name="tooltip"></property>
+                                                            <property name="validator_data_type"></property>
+                                                            <property name="validator_style">wxFILTER_NONE</property>
+                                                            <property name="validator_type">wxDefaultValidator</property>
+                                                            <property name="validator_variable"></property>
+                                                            <property name="value">1</property>
+                                                            <property name="window_extra_style"></property>
+                                                            <property name="window_name"></property>
+                                                            <property name="window_style"></property>
+                                                        </object>
+                                                    </object>
+                                                    <object class="sizeritem" expanded="1">
+                                                        <property name="border">5</property>
+                                                        <property name="flag">wxALL</property>
+                                                        <property name="proportion">0</property>
+                                                        <object class="wxTextCtrl" expanded="1">
+                                                            <property name="BottomDockable">1</property>
+                                                            <property name="LeftDockable">1</property>
+                                                            <property name="RightDockable">1</property>
+                                                            <property name="TopDockable">1</property>
+                                                            <property name="aui_layer"></property>
+                                                            <property name="aui_name"></property>
+                                                            <property name="aui_position"></property>
+                                                            <property name="aui_row"></property>
+                                                            <property name="best_size"></property>
+                                                            <property name="bg"></property>
+                                                            <property name="caption"></property>
+                                                            <property name="caption_visible">1</property>
+                                                            <property name="center_pane">0</property>
+                                                            <property name="close_button">1</property>
+                                                            <property name="context_help"></property>
+                                                            <property name="context_menu">1</property>
+                                                            <property name="default_pane">0</property>
+                                                            <property name="dock">Dock</property>
+                                                            <property name="dock_fixed">0</property>
+                                                            <property name="docking">Left</property>
+                                                            <property name="enabled">1</property>
+                                                            <property name="fg"></property>
+                                                            <property name="floatable">1</property>
+                                                            <property name="font"></property>
+                                                            <property name="gripper">0</property>
+                                                            <property name="hidden">0</property>
+                                                            <property name="id">wxID_ANY</property>
+                                                            <property name="max_size"></property>
+                                                            <property name="maximize_button">0</property>
+                                                            <property name="maximum_size"></property>
+                                                            <property name="maxlength">0</property>
+                                                            <property name="min_size"></property>
+                                                            <property name="minimize_button">0</property>
+                                                            <property name="minimum_size">72,-1</property>
+                                                            <property name="moveable">1</property>
+                                                            <property name="name">m_entryGridSecNumberingStep</property>
+                                                            <property name="pane_border">1</property>
+                                                            <property name="pane_position"></property>
+                                                            <property name="pane_size"></property>
+                                                            <property name="permission">protected</property>
+                                                            <property name="pin_button">1</property>
+                                                            <property name="pos"></property>
+                                                            <property name="resize">Resizable</property>
+                                                            <property name="show">1</property>
+                                                            <property name="size"></property>
+                                                            <property name="style"></property>
+                                                            <property name="subclass"></property>
+                                                            <property name="toolbar_pane">0</property>
+                                                            <property name="tooltip"></property>
+                                                            <property name="validator_data_type"></property>
+                                                            <property name="validator_style">wxFILTER_NONE</property>
+                                                            <property name="validator_type">wxDefaultValidator</property>
+                                                            <property name="validator_variable"></property>
+                                                            <property name="value">1</property>
+                                                            <property name="window_extra_style"></property>
+                                                            <property name="window_name"></property>
+                                                            <property name="window_style"></property>
+                                                        </object>
+                                                    </object>
                                                 </object>
                                             </object>
                                         </object>
@@ -3516,15 +3712,22 @@
                                                     <property name="window_style"></property>
                                                 </object>
                                             </object>
-                                            <object class="sizeritem" expanded="0">
+                                            <object class="sizeritem" expanded="1">
                                                 <property name="border">5</property>
                                                 <property name="flag">wxEXPAND</property>
-                                                <property name="proportion">0</property>
-                                                <object class="wxBoxSizer" expanded="0">
+                                                <property name="proportion">1</property>
+                                                <object class="wxFlexGridSizer" expanded="1">
+                                                    <property name="cols">2</property>
+                                                    <property name="flexible_direction">wxBOTH</property>
+                                                    <property name="growablecols">0</property>
+                                                    <property name="growablerows"></property>
+                                                    <property name="hgap">0</property>
                                                     <property name="minimum_size"></property>
-                                                    <property name="name">bSizer7</property>
-                                                    <property name="orient">wxHORIZONTAL</property>
+                                                    <property name="name">fgSizer2</property>
+                                                    <property name="non_flexible_grow_mode">wxFLEX_GROWMODE_SPECIFIED</property>
                                                     <property name="permission">none</property>
+                                                    <property name="rows">0</property>
+                                                    <property name="vgap">0</property>
                                                     <object class="sizeritem" expanded="0">
                                                         <property name="border">5</property>
                                                         <property name="flag">wxALIGN_CENTER_VERTICAL|wxALL</property>
@@ -3557,7 +3760,7 @@
                                                             <property name="gripper">0</property>
                                                             <property name="hidden">0</property>
                                                             <property name="id">wxID_ANY</property>
-                                                            <property name="label">Pad numbering start value:</property>
+                                                            <property name="label">Pad numbering start:</property>
                                                             <property name="markup">0</property>
                                                             <property name="max_size"></property>
                                                             <property name="maximize_button">0</property>
@@ -3650,6 +3853,131 @@
                                                             <property name="window_style"></property>
                                                         </object>
                                                     </object>
+                                                    <object class="sizeritem" expanded="1">
+                                                        <property name="border">5</property>
+                                                        <property name="flag">wxALIGN_CENTER_VERTICAL|wxALL</property>
+                                                        <property name="proportion">0</property>
+                                                        <object class="wxStaticText" expanded="1">
+                                                            <property name="BottomDockable">1</property>
+                                                            <property name="LeftDockable">1</property>
+                                                            <property name="RightDockable">1</property>
+                                                            <property name="TopDockable">1</property>
+                                                            <property name="aui_layer"></property>
+                                                            <property name="aui_name"></property>
+                                                            <property name="aui_position"></property>
+                                                            <property name="aui_row"></property>
+                                                            <property name="best_size"></property>
+                                                            <property name="bg"></property>
+                                                            <property name="caption"></property>
+                                                            <property name="caption_visible">1</property>
+                                                            <property name="center_pane">0</property>
+                                                            <property name="close_button">1</property>
+                                                            <property name="context_help"></property>
+                                                            <property name="context_menu">1</property>
+                                                            <property name="default_pane">0</property>
+                                                            <property name="dock">Dock</property>
+                                                            <property name="dock_fixed">0</property>
+                                                            <property name="docking">Left</property>
+                                                            <property name="enabled">1</property>
+                                                            <property name="fg"></property>
+                                                            <property name="floatable">1</property>
+                                                            <property name="font"></property>
+                                                            <property name="gripper">0</property>
+                                                            <property name="hidden">0</property>
+                                                            <property name="id">wxID_ANY</property>
+                                                            <property name="label">Pad numbering skip:</property>
+                                                            <property name="markup">0</property>
+                                                            <property name="max_size"></property>
+                                                            <property name="maximize_button">0</property>
+                                                            <property name="maximum_size"></property>
+                                                            <property name="min_size"></property>
+                                                            <property name="minimize_button">0</property>
+                                                            <property name="minimum_size"></property>
+                                                            <property name="moveable">1</property>
+                                                            <property name="name">m_labelCircNumStep</property>
+                                                            <property name="pane_border">1</property>
+                                                            <property name="pane_position"></property>
+                                                            <property name="pane_size"></property>
+                                                            <property name="permission">protected</property>
+                                                            <property name="pin_button">1</property>
+                                                            <property name="pos"></property>
+                                                            <property name="resize">Resizable</property>
+                                                            <property name="show">1</property>
+                                                            <property name="size"></property>
+                                                            <property name="style"></property>
+                                                            <property name="subclass"></property>
+                                                            <property name="toolbar_pane">0</property>
+                                                            <property name="tooltip"></property>
+                                                            <property name="window_extra_style"></property>
+                                                            <property name="window_name"></property>
+                                                            <property name="window_style"></property>
+                                                            <property name="wrap">-1</property>
+                                                        </object>
+                                                    </object>
+                                                    <object class="sizeritem" expanded="1">
+                                                        <property name="border">5</property>
+                                                        <property name="flag">wxALL</property>
+                                                        <property name="proportion">0</property>
+                                                        <object class="wxTextCtrl" expanded="1">
+                                                            <property name="BottomDockable">1</property>
+                                                            <property name="LeftDockable">1</property>
+                                                            <property name="RightDockable">1</property>
+                                                            <property name="TopDockable">1</property>
+                                                            <property name="aui_layer"></property>
+                                                            <property name="aui_name"></property>
+                                                            <property name="aui_position"></property>
+                                                            <property name="aui_row"></property>
+                                                            <property name="best_size"></property>
+                                                            <property name="bg"></property>
+                                                            <property name="caption"></property>
+                                                            <property name="caption_visible">1</property>
+                                                            <property name="center_pane">0</property>
+                                                            <property name="close_button">1</property>
+                                                            <property name="context_help"></property>
+                                                            <property name="context_menu">1</property>
+                                                            <property name="default_pane">0</property>
+                                                            <property name="dock">Dock</property>
+                                                            <property name="dock_fixed">0</property>
+                                                            <property name="docking">Left</property>
+                                                            <property name="enabled">1</property>
+                                                            <property name="fg"></property>
+                                                            <property name="floatable">1</property>
+                                                            <property name="font"></property>
+                                                            <property name="gripper">0</property>
+                                                            <property name="hidden">0</property>
+                                                            <property name="id">wxID_ANY</property>
+                                                            <property name="max_size"></property>
+                                                            <property name="maximize_button">0</property>
+                                                            <property name="maximum_size"></property>
+                                                            <property name="maxlength">0</property>
+                                                            <property name="min_size"></property>
+                                                            <property name="minimize_button">0</property>
+                                                            <property name="minimum_size"></property>
+                                                            <property name="moveable">1</property>
+                                                            <property name="name">m_entryCircNumberingStep</property>
+                                                            <property name="pane_border">1</property>
+                                                            <property name="pane_position"></property>
+                                                            <property name="pane_size"></property>
+                                                            <property name="permission">protected</property>
+                                                            <property name="pin_button">1</property>
+                                                            <property name="pos"></property>
+                                                            <property name="resize">Resizable</property>
+                                                            <property name="show">1</property>
+                                                            <property name="size"></property>
+                                                            <property name="style"></property>
+                                                            <property name="subclass"></property>
+                                                            <property name="toolbar_pane">0</property>
+                                                            <property name="tooltip"></property>
+                                                            <property name="validator_data_type"></property>
+                                                            <property name="validator_style">wxFILTER_NONE</property>
+                                                            <property name="validator_type">wxDefaultValidator</property>
+                                                            <property name="validator_variable"></property>
+                                                            <property name="value">1</property>
+                                                            <property name="window_extra_style"></property>
+                                                            <property name="window_name"></property>
+                                                            <property name="window_style"></property>
+                                                        </object>
+                                                    </object>
                                                 </object>
                                             </object>
                                         </object>
diff --git a/pcbnew/dialogs/dialog_create_array_base.h b/pcbnew/dialogs/dialog_create_array_base.h
index ed1418b4ec..d1a5a4aef0 100644
--- a/pcbnew/dialogs/dialog_create_array_base.h
+++ b/pcbnew/dialogs/dialog_create_array_base.h
@@ -79,6 +79,9 @@ class DIALOG_CREATE_ARRAY_BASE : public DIALOG_SHIM
 		wxStaticText* m_labelGridNumberingOffset;
 		wxTextCtrl* m_entryGridPriNumberingOffset;
 		wxTextCtrl* m_entryGridSecNumberingOffset;
+		wxStaticText* m_labelGridNumberingStep;
+		wxTextCtrl* m_entryGridPriNumberingStep;
+		wxTextCtrl* m_entryGridSecNumberingStep;
 		wxPanel* m_circularPanel;
 		wxStaticText* m_labelCentreX;
 		wxTextCtrl* m_entryCentreX;
@@ -102,6 +105,8 @@ class DIALOG_CREATE_ARRAY_BASE : public DIALOG_SHIM
 		wxChoice* m_choiceCircNumbering;
 		wxStaticText* m_labelCircNumStart;
 		wxTextCtrl* m_entryCircNumberingStart;
+		wxStaticText* m_labelCircNumStep;
+		wxTextCtrl* m_entryCircNumberingStep;
 		wxStdDialogButtonSizer* m_stdButtons;
 		wxButton* m_stdButtonsOK;
 		wxButton* m_stdButtonsCancel;
diff --git a/qa/common/test_array_axis.cpp b/qa/common/test_array_axis.cpp
index baf5b62174..5e2773bd09 100644
--- a/qa/common/test_array_axis.cpp
+++ b/qa/common/test_array_axis.cpp
@@ -131,6 +131,7 @@ struct ARRAY_AXIS_NAMING_PARAMS
 {
     ARRAY_AXIS::NUMBERING_TYPE m_axis_type;
     std::string                m_start_at;
+    int                        m_step;
 };
 
 struct ARRAY_AXIS_NAMING_CASE
@@ -145,10 +146,11 @@ struct ARRAY_AXIS_NAMING_CASE
 // clang-format off
 static const std::vector<ARRAY_AXIS_NAMING_CASE> axis_name_cases = {
     {
-        "Linear",
+        "Numeric",
         {
             ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_NUMERIC,
             "1",
+            1,
         },
         6,
         { "1", "2", "3", "4", "5", "6" },
@@ -159,6 +161,7 @@ static const std::vector<ARRAY_AXIS_NAMING_CASE> axis_name_cases = {
         {
             ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_ALPHA_FULL,
             "A",
+            1,
         },
         3,
         { "A", "B", "C" },
@@ -169,10 +172,21 @@ static const std::vector<ARRAY_AXIS_NAMING_CASE> axis_name_cases = {
         {
             ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_ALPHA_FULL,
             "Y",
+            1,
         },
         4,
         { "Y", "Z", "AA", "AB" },
     },
+    {
+        "Numeric skip",
+        {
+            ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_NUMERIC,
+            "11",
+            2,
+        },
+        6,
+        { "11", "13", "15", "17", "19", "21" },
+    },
 };
 // clang-format on
 
@@ -187,6 +201,7 @@ BOOST_AUTO_TEST_CASE( Numbering )
         {
             ARRAY_AXIS axis;
             axis.SetAxisType( c.m_prms.m_axis_type );
+            axis.SetStep( c.m_prms.m_step );
 
             bool start_ok = axis.SetOffset( c.m_prms.m_start_at );