summaryrefslogtreecommitdiff
path: root/pcbnew/dialogs/dialog_create_array.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'pcbnew/dialogs/dialog_create_array.cpp')
-rw-r--r--pcbnew/dialogs/dialog_create_array.cpp488
1 files changed, 488 insertions, 0 deletions
diff --git a/pcbnew/dialogs/dialog_create_array.cpp b/pcbnew/dialogs/dialog_create_array.cpp
new file mode 100644
index 0000000..ee63520
--- /dev/null
+++ b/pcbnew/dialogs/dialog_create_array.cpp
@@ -0,0 +1,488 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 John Beard, john.j.beard@gmail.com
+ * Copyright (C) 1992-2014 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 2
+ * 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, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <wxPcbStruct.h>
+#include <base_units.h>
+#include <macros.h>
+
+#include <class_drawpanel.h>
+#include <class_board.h>
+#include <class_module.h>
+
+#include "dialog_create_array.h"
+
+
+// initialise statics
+DIALOG_CREATE_ARRAY::CREATE_ARRAY_DIALOG_ENTRIES DIALOG_CREATE_ARRAY::m_options;
+
+
+DIALOG_CREATE_ARRAY::DIALOG_CREATE_ARRAY( PCB_BASE_FRAME* aParent, wxPoint aOrigPos,
+ ARRAY_OPTIONS** aSettings ) :
+ DIALOG_CREATE_ARRAY_BASE( aParent ),
+ CONFIG_SAVE_RESTORE_WINDOW( m_options.m_optionsSet ),
+ m_settings( aSettings ),
+ m_originalItemPosition( aOrigPos )
+{
+ // Set up numbering scheme drop downs
+ //
+ // character set
+ // NOTE: do not change the order of this relative to the ARRAY_NUMBERING_TYPE_T enum
+ const wxString charSetDescriptions[] =
+ {
+ _( "Numerals (0,1,2,...,9,10)" ),
+ _( "Hexadecimal (0,1,...,F,10,...)" ),
+ _( "Alphabet, minus IOSQXZ" ),
+ _( "Alphabet, full 26 characters" )
+ };
+ m_choicePriAxisNumbering->Set( DIM( charSetDescriptions ), charSetDescriptions );
+ m_choiceSecAxisNumbering->Set( DIM( charSetDescriptions ), charSetDescriptions );
+
+ m_choicePriAxisNumbering->SetSelection( 0 );
+ m_choiceSecAxisNumbering->SetSelection( 0 );
+
+ Add( m_entryNx, m_options.m_gridNx );
+ Add( m_entryNy, m_options.m_gridNy );
+ Add( m_entryDx, m_options.m_gridDx );
+ Add( m_entryDy, m_options.m_gridDy );
+
+ Add( m_entryOffsetX, m_options.m_gridOffsetX );
+ Add( m_entryOffsetY, m_options.m_gridOffsetY );
+ Add( m_entryStagger, m_options.m_gridStagger );
+
+ Add( m_radioBoxGridStaggerType, m_options.m_gridStaggerType );
+
+ Add( m_radioBoxGridNumberingAxis, m_options.m_gridNumberingAxis );
+ Add( m_checkBoxGridReverseNumbering, m_options.m_gridNumberingReverseAlternate );
+
+ Add( m_entryCentreX, m_options.m_circCentreX );
+ Add( m_entryCentreY, m_options.m_circCentreY );
+ Add( m_entryCircAngle, m_options.m_circAngle );
+ Add( m_entryCircCount, m_options.m_circCount );
+ Add( m_entryRotateItemsCb, m_options.m_circRotate );
+ Add( m_entryCircNumberingStart, m_options.m_circNumberingOffset );
+
+ Add( m_gridTypeNotebook, m_options.m_arrayTypeTab );
+
+ Add( m_radioBoxGridNumberingScheme, m_options.m_grid2dArrayNumbering );
+ Add( m_choicePriAxisNumbering, m_options.m_gridPriAxisNumScheme );
+ Add( m_choiceSecAxisNumbering, m_options.m_gridSecAxisNumScheme );
+
+ Add( m_entryGridPriNumberingOffset, m_options.m_gridPriNumberingOffset );
+ Add( m_entryGridSecNumberingOffset, m_options.m_gridSecNumberingOffset );
+
+ Add( m_rbGridStartNumberingOpt, m_options.m_gridNumberingScheme );
+ Add( m_rbCircStartNumberingOpt, m_options.m_circNumberingScheme );
+
+ RestoreConfigToControls();
+
+ // Load units into labels
+ {
+ const wxString lengthUnit = GetAbbreviatedUnitsLabel( g_UserUnit );
+
+ m_unitLabelCentreX->SetLabelText( lengthUnit );
+ m_unitLabelCentreY->SetLabelText( lengthUnit );
+ m_unitLabelDx->SetLabelText( lengthUnit );
+ m_unitLabelDy->SetLabelText( lengthUnit );
+ m_unitLabelOffsetX->SetLabelText( lengthUnit );
+ m_unitLabelOffsetY->SetLabelText( lengthUnit );
+ }
+
+ // Run the callbacks once to process the dialog contents
+ setControlEnablement();
+ calculateCircularArrayProperties();
+
+ m_stdButtonsOK->SetDefault();
+ Fit();
+ SetMinSize( GetSize() );
+}
+
+
+void DIALOG_CREATE_ARRAY::OnParameterChanged( wxCommandEvent& event )
+{
+ setControlEnablement();
+ calculateCircularArrayProperties();
+}
+
+
+static const std::string& alphabetFromNumberingScheme(
+ DIALOG_CREATE_ARRAY::ARRAY_NUMBERING_TYPE_T type )
+{
+ static const std::string alphaNumeric = "0123456789";
+ static const std::string alphaHex = "0123456789ABCDEF";
+ static const std::string alphaFull = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ static const std::string alphaNoIOSQXZ = "ABCDEFGHJKLMNPRTUVWY";
+
+ switch( type )
+ {
+ default:
+ case DIALOG_CREATE_ARRAY::NUMBERING_NUMERIC:
+ break;
+
+ case DIALOG_CREATE_ARRAY::NUMBERING_HEX:
+ return alphaHex;
+
+ case DIALOG_CREATE_ARRAY::NUMBERING_ALPHA_NO_IOSQXZ:
+ return alphaNoIOSQXZ;
+
+ case DIALOG_CREATE_ARRAY::NUMBERING_ALPHA_FULL:
+ return alphaFull;
+ }
+
+ return alphaNumeric;
+}
+
+
+/**
+ * @return False for schemes like 0,1...9,10
+ * True for schemes like A,B..Z,AA (where the tens column starts with char 0)
+ */
+static bool schemeNonUnitColsStartAt0( DIALOG_CREATE_ARRAY::ARRAY_NUMBERING_TYPE_T type )
+{
+ return type == DIALOG_CREATE_ARRAY::NUMBERING_ALPHA_FULL
+ || type == DIALOG_CREATE_ARRAY::NUMBERING_ALPHA_NO_IOSQXZ;
+}
+
+
+static bool getNumberingOffset( const std::string& str,
+ DIALOG_CREATE_ARRAY::ARRAY_NUMBERING_TYPE_T type,
+ int& offsetToFill )
+{
+ const std::string alphabet = alphabetFromNumberingScheme( type );
+
+ int offset = 0;
+ const int radix = alphabet.length();
+
+ for( unsigned i = 0; i < str.length(); i++ )
+ {
+ int chIndex = alphabet.find( str[i], 0 );
+
+ if( chIndex == wxNOT_FOUND )
+ return false;
+
+ const bool start0 = schemeNonUnitColsStartAt0( type );
+
+ // eg "AA" is actually index 27, not 26
+ if( start0 && i < str.length() - 1 )
+ chIndex++;
+
+ offset *= radix;
+ offset += chIndex;
+ }
+
+ offsetToFill = offset;
+ return true;
+}
+
+
+void DIALOG_CREATE_ARRAY::OnOkClick( wxCommandEvent& event )
+{
+ ARRAY_OPTIONS* newSettings = NULL;
+
+ const wxWindow* page = m_gridTypeNotebook->GetCurrentPage();
+
+ if( page == m_gridPanel )
+ {
+ ARRAY_GRID_OPTIONS* newGrid = new ARRAY_GRID_OPTIONS();
+ bool ok = true;
+
+ // ints
+ ok = ok && m_entryNx->GetValue().ToLong( &newGrid->m_nx );
+ ok = ok && m_entryNy->GetValue().ToLong( &newGrid->m_ny );
+
+ newGrid->m_delta.x = DoubleValueFromString( g_UserUnit, m_entryDx->GetValue() );
+ newGrid->m_delta.y = DoubleValueFromString( g_UserUnit, m_entryDy->GetValue() );
+
+ newGrid->m_offset.x = DoubleValueFromString( g_UserUnit, m_entryOffsetX->GetValue() );
+ newGrid->m_offset.y = DoubleValueFromString( g_UserUnit, m_entryOffsetY->GetValue() );
+
+ ok = ok && m_entryStagger->GetValue().ToLong( &newGrid->m_stagger );
+
+ newGrid->m_stagger_rows = m_radioBoxGridStaggerType->GetSelection() == 0;
+
+ newGrid->m_horizontalThenVertical = m_radioBoxGridNumberingAxis->GetSelection() == 0;
+ newGrid->m_reverseNumberingAlternate = m_checkBoxGridReverseNumbering->GetValue();
+
+ newGrid->m_2dArrayNumbering = m_radioBoxGridNumberingScheme->GetSelection() != 0;
+
+ // this is only correct if you set the choice up according to the enum size and order
+ ok = ok && m_choicePriAxisNumbering->GetSelection() <= NUMBERING_TYPE_MAX
+ && m_choiceSecAxisNumbering->GetSelection() <= NUMBERING_TYPE_MAX;
+
+ // mind undefined casts to enums (should not be able to happen)
+ if( ok )
+ {
+ newGrid->m_priAxisNumType =
+ (ARRAY_NUMBERING_TYPE_T) m_choicePriAxisNumbering->GetSelection();
+ newGrid->m_secAxisNumType =
+ (ARRAY_NUMBERING_TYPE_T) m_choiceSecAxisNumbering->GetSelection();
+ }
+
+ // Work out the offsets for the numbering
+ ok = ok && getNumberingOffset(
+ m_entryGridPriNumberingOffset->GetValue().ToStdString(),
+ newGrid->m_priAxisNumType, newGrid->m_numberingOffsetX );
+
+ if( newGrid->m_2dArrayNumbering )
+ ok = ok && getNumberingOffset(
+ m_entryGridSecNumberingOffset->GetValue().ToStdString(),
+ newGrid->m_secAxisNumType, newGrid->m_numberingOffsetY );
+
+ newGrid->m_shouldRenumber = m_rbGridStartNumberingOpt->GetSelection() == 1;
+
+ // Only use settings if all values are good
+ if( ok )
+ newSettings = newGrid;
+ else
+ delete newGrid;
+ }
+ else if( page == m_circularPanel )
+ {
+ ARRAY_CIRCULAR_OPTIONS* newCirc = new ARRAY_CIRCULAR_OPTIONS();
+ bool ok = true;
+
+ newCirc->m_centre.x = DoubleValueFromString( g_UserUnit, m_entryCentreX->GetValue() );
+ newCirc->m_centre.y = DoubleValueFromString( g_UserUnit, m_entryCentreY->GetValue() );
+
+ newCirc->m_angle = DoubleValueFromString( DEGREES, m_entryCircAngle->GetValue() );
+ ok = ok && m_entryCircCount->GetValue().ToLong( &newCirc->m_nPts );
+
+ newCirc->m_rotateItems = m_entryRotateItemsCb->GetValue();
+ newCirc->m_shouldRenumber = m_rbCircStartNumberingOpt->GetSelection() == 1;
+ newCirc->m_numberingType = NUMBERING_NUMERIC;
+
+ ok = ok && m_entryCircNumberingStart->GetValue().ToLong( &newCirc->m_numberingOffset );
+
+ // Only use settings if all values are good
+ if( ok )
+ newSettings = newCirc;
+ else
+ delete newCirc;
+ }
+
+ // If we got good settings, send them out and finish
+ if( newSettings )
+ {
+ delete *m_settings;
+
+ // assign pointer and ownership here
+ *m_settings = newSettings;
+ ReadConfigFromControls();
+
+ EndModal( wxID_OK );
+ }
+
+ else
+ wxMessageBox( _("Bad parameters" ) );
+}
+
+
+void DIALOG_CREATE_ARRAY::setControlEnablement()
+{
+ const bool renumber = m_rbGridStartNumberingOpt->GetSelection() == 1;
+
+ // If we're not renumbering, we can't set the numbering scheme
+ // or axis numbering types
+ m_radioBoxGridNumberingScheme->Enable( renumber );
+ m_labelPriAxisNumbering->Enable( renumber );
+ m_choicePriAxisNumbering->Enable( renumber );
+
+ // Disable the secondary axis numbering option if the
+ // numbering scheme doesn't have two axes
+ const bool num2d = m_radioBoxGridNumberingScheme->GetSelection() != 0;
+
+ m_labelSecAxisNumbering->Enable( renumber && num2d );
+ m_choiceSecAxisNumbering->Enable( renumber && num2d );
+
+ // We can only set an offset if we renumber
+ m_labelGridNumberingOffset->Enable( renumber );
+ m_entryGridPriNumberingOffset->Enable( renumber );
+ m_entryGridSecNumberingOffset->Enable( renumber && num2d );
+
+ m_entryCircNumberingStart->Enable( m_rbCircStartNumberingOpt->GetSelection() == 1 );
+}
+
+
+void DIALOG_CREATE_ARRAY::calculateCircularArrayProperties()
+{
+ wxPoint centre;
+
+ centre.x = DoubleValueFromString( g_UserUnit, m_entryCentreX->GetValue() );
+ centre.y = DoubleValueFromString( g_UserUnit, m_entryCentreY->GetValue() );
+
+ // Find the radius, etc of the circle
+ centre -= m_originalItemPosition;
+
+ const double radius = VECTOR2I(centre.x, centre.y).EuclideanNorm();
+ m_labelCircRadiusValue->SetLabelText( StringFromValue( g_UserUnit, int(radius), true ) );
+}
+
+
+// ARRAY OPTION implementation functions --------------------------------------
+
+std::string DIALOG_CREATE_ARRAY::ARRAY_OPTIONS::getCoordinateNumber( int n,
+ ARRAY_NUMBERING_TYPE_T type )
+{
+ std::string itemNum;
+ const std::string& alphabet = alphabetFromNumberingScheme( type );
+
+ const bool nonUnitColsStartAt0 = schemeNonUnitColsStartAt0( type );
+
+ bool firstRound = true;
+ int radix = alphabet.length();
+
+ do {
+ int modN = n % radix;
+
+ if( nonUnitColsStartAt0 && !firstRound )
+ modN--; // Start the "tens/hundreds/etc column" at "Ax", not "Bx"
+
+ itemNum.insert( 0, 1, alphabet[modN] );
+
+ n /= radix;
+ firstRound = false;
+ } while( n );
+
+ return itemNum;
+}
+
+
+wxString DIALOG_CREATE_ARRAY::ARRAY_OPTIONS::InterpolateNumberIntoString(
+ int aN, const wxString& aPattern ) const
+{
+ wxString newStr( aPattern );
+ newStr.Replace( "%s", GetItemNumber( aN ), false );
+
+ return newStr;
+}
+
+
+int DIALOG_CREATE_ARRAY::ARRAY_GRID_OPTIONS::GetArraySize() const
+{
+ return m_nx * m_ny;
+}
+
+
+wxPoint DIALOG_CREATE_ARRAY::ARRAY_GRID_OPTIONS::getGridCoords( int n ) const
+{
+ const int axisSize = m_horizontalThenVertical ? m_nx : m_ny;
+
+ int x = n % axisSize;
+ int y = n / axisSize;
+
+ // reverse on this row/col?
+ if( m_reverseNumberingAlternate && ( y % 2 ) )
+ x = axisSize - x - 1;
+
+ wxPoint coords( x, y );
+
+ return coords;
+}
+
+
+void DIALOG_CREATE_ARRAY::ARRAY_GRID_OPTIONS::TransformItem( int n, BOARD_ITEM* item,
+ const wxPoint& rotPoint ) const
+{
+ wxPoint point;
+
+ wxPoint coords = getGridCoords( n );
+
+ // swap axes if needed
+ if( !m_horizontalThenVertical )
+ std::swap( coords.x, coords.y );
+
+ point.x = coords.x * m_delta.x + coords.y * m_offset.x;
+ point.y = coords.y * m_delta.y + coords.x * m_offset.y;
+
+ if( std::abs( m_stagger ) > 1 )
+ {
+ const int stagger = std::abs( m_stagger );
+ const bool sr = m_stagger_rows;
+ const int stagger_idx = ( ( sr ? coords.y : coords.x ) % stagger );
+
+ wxPoint stagger_delta( ( sr ? m_delta.x : m_offset.x ),
+ ( sr ? m_offset.y : m_delta.y ) );
+
+ // Stagger to the left/up if the sign of the stagger is negative
+ point += stagger_delta * copysign( stagger_idx, m_stagger ) / stagger;
+ }
+
+ // this is already relative to the first array entry
+ item->Move( point );
+}
+
+
+wxString DIALOG_CREATE_ARRAY::ARRAY_GRID_OPTIONS::GetItemNumber( int n ) const
+{
+ wxString itemNum;
+
+ if( m_2dArrayNumbering )
+ {
+ wxPoint coords = getGridCoords( n );
+
+ itemNum += getCoordinateNumber( coords.x + m_numberingOffsetX, m_priAxisNumType );
+ itemNum += getCoordinateNumber( coords.y + m_numberingOffsetY, m_secAxisNumType );
+ }
+ else
+ {
+ itemNum += getCoordinateNumber( n + m_numberingOffsetX, m_priAxisNumType );
+ }
+
+ return itemNum;
+}
+
+
+int DIALOG_CREATE_ARRAY::ARRAY_CIRCULAR_OPTIONS::GetArraySize() const
+{
+ return m_nPts;
+}
+
+
+void DIALOG_CREATE_ARRAY::ARRAY_CIRCULAR_OPTIONS::TransformItem( int n, BOARD_ITEM* item,
+ const wxPoint& rotPoint ) const
+{
+ double angle;
+
+ if( m_angle == 0 )
+ // angle is zero, divide evenly into m_nPts
+ angle = 3600.0 * n / double( m_nPts );
+ else
+ // n'th step
+ angle = m_angle * n;
+
+ item->Rotate( m_centre, angle );
+
+ // take off the rotation (but not the translation) if needed
+ if( !m_rotateItems )
+ item->Rotate( item->GetCenter(), -angle );
+}
+
+
+wxString DIALOG_CREATE_ARRAY::ARRAY_CIRCULAR_OPTIONS::GetItemNumber( int aN ) const
+{
+ // The first new pad has aN number == 1, not 0
+ if( m_shouldRenumber ) // numbering pad from initial user value
+ return getCoordinateNumber( aN - 1 + m_numberingOffset, m_numberingType );
+ else // numbering pad from inital pad number
+ return getCoordinateNumber( aN + m_numberingOffset, m_numberingType );
+}