diff options
Diffstat (limited to 'pcbnew/dialogs/dialog_create_array.cpp')
-rw-r--r-- | pcbnew/dialogs/dialog_create_array.cpp | 488 |
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 ); +} |