KiCad PCB EDA Suite
dialog_create_array.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2014 John Beard, john.j.beard@gmail.com
5  * Copyright (C) 1992-2014 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 #include <wxPcbStruct.h>
26 #include <base_units.h>
27 #include <macros.h>
28 #include <boost/algorithm/string/join.hpp>
29 
30 #include <class_drawpanel.h>
31 #include <class_board.h>
32 #include <class_module.h>
33 
34 #include "dialog_create_array.h"
35 
36 
37 // initialise statics
39 
40 
42  bool enableNumbering,
43  wxPoint aOrigPos ) :
44  DIALOG_CREATE_ARRAY_BASE( aParent ),
45  CONFIG_SAVE_RESTORE_WINDOW( m_options.m_optionsSet ),
46  m_settings( NULL ),
47  m_originalItemPosition( aOrigPos ),
48  m_numberingEnabled(enableNumbering)
49 {
50  // Set up numbering scheme drop downs
51  //
52  // character set
53  // NOTE: do not change the order of this relative to the ARRAY_NUMBERING_TYPE_T enum
54  const wxString charSetDescriptions[] =
55  {
56  _( "Numerals (0,1,2,...,9,10)" ),
57  _( "Hexadecimal (0,1,...,F,10,...)" ),
58  _( "Alphabet, minus IOSQXZ" ),
59  _( "Alphabet, full 26 characters" )
60  };
61  m_choicePriAxisNumbering->Set( DIM( charSetDescriptions ), charSetDescriptions );
62  m_choiceSecAxisNumbering->Set( DIM( charSetDescriptions ), charSetDescriptions );
63 
64  m_choicePriAxisNumbering->SetSelection( 0 );
65  m_choiceSecAxisNumbering->SetSelection( 0 );
66 
71 
75 
77 
80 
87 
89 
93 
96 
98 
99  // Load units into labels
100  {
101  const wxString lengthUnit = GetAbbreviatedUnitsLabel( g_UserUnit );
102 
103  m_unitLabelCentreX->SetLabelText( lengthUnit );
104  m_unitLabelCentreY->SetLabelText( lengthUnit );
105  m_unitLabelDx->SetLabelText( lengthUnit );
106  m_unitLabelDy->SetLabelText( lengthUnit );
107  m_unitLabelOffsetX->SetLabelText( lengthUnit );
108  m_unitLabelOffsetY->SetLabelText( lengthUnit );
109  }
110 
111  // Run the callbacks once to process the dialog contents
114 
115  m_stdButtonsOK->SetDefault();
116  Fit();
117  SetMinSize( GetSize() );
118 }
119 
120 
122 {
123  if( m_settings != NULL )
124  delete m_settings;
125 }
126 
127 
128 void DIALOG_CREATE_ARRAY::OnParameterChanged( wxCommandEvent& event )
129 {
132 }
133 
134 
135 static const wxString& alphabetFromNumberingScheme(
137 {
138  static const wxString alphaNumeric = "0123456789";
139  static const wxString alphaHex = "0123456789ABCDEF";
140  static const wxString alphaFull = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
141  static const wxString alphaNoIOSQXZ = "ABCDEFGHJKLMNPRTUVWY";
142 
143  switch( type )
144  {
145  default:
147  break;
148 
150  return alphaHex;
151 
153  return alphaNoIOSQXZ;
154 
156  return alphaFull;
157  }
158 
159  return alphaNumeric;
160 }
161 
162 
168 {
171 }
172 
173 
174 static bool getNumberingOffset( const wxString& str,
176  int& offsetToFill )
177 {
178  const wxString alphabet = alphabetFromNumberingScheme( type );
179 
180  int offset = 0;
181  const int radix = alphabet.length();
182 
183  for( unsigned i = 0; i < str.length(); i++ )
184  {
185  int chIndex = alphabet.Find( str[i], false );
186 
187  if( chIndex == wxNOT_FOUND )
188  return false;
189 
190  const bool start0 = schemeNonUnitColsStartAt0( type );
191 
192  // eg "AA" is actually index 27, not 26
193  if( start0 && i < str.length() - 1 )
194  chIndex++;
195 
196  offset *= radix;
197  offset += chIndex;
198  }
199 
200  offsetToFill = offset;
201  return true;
202 }
203 
204 
215 static bool validateNumberingTypeAndOffset( const wxTextCtrl& offsetEntry,
216  const wxChoice& typeEntry,
218  int& offset,
219  wxArrayString& errors )
220 {
221  const int typeVal = typeEntry.GetSelection();
222  // mind undefined casts to enums (should not be able to happen)
223  bool ok = typeVal <= DIALOG_CREATE_ARRAY::NUMBERING_TYPE_MAX;
224 
225  if( ok )
226  {
228  }
229  else
230  {
231  wxString err;
232  err.Printf( _("Unrecognised numbering scheme: %d"), typeVal );
233  errors.Add( err );
234  // we can't proceed - we don't know the numbering type
235  return false;
236  }
237 
238  const wxString text = offsetEntry.GetValue();
239  ok = getNumberingOffset( text, type, offset );
240 
241  if( !ok )
242  {
243  const wxString& alphabet = alphabetFromNumberingScheme( type );
244 
245  wxString err;
246  err.Printf( _( "Could not determine numbering start from \"%s\": "
247  "expected value consistent with alphabet \"%s\"" ),
248  text, alphabet );
249  errors.Add(err);
250  }
251 
252  return ok;
253 }
254 
255 
265 static bool validateLongEntry( const wxTextEntry& entry,
266  long& dest,
267  const wxString description,
268  wxArrayString& errors )
269 {
270  bool ok = true;
271 
272  if( !entry.GetValue().ToLong( &dest ) )
273  {
274  wxString err;
275  err.Printf( _("Bad numeric value for %s: %s"), description, entry.GetValue() );
276  errors.Add( err );
277  ok = false;
278  }
279 
280  return ok;
281 }
282 
283 
284 void DIALOG_CREATE_ARRAY::OnOkClick( wxCommandEvent& event )
285 {
286  ARRAY_OPTIONS* newSettings = NULL;
287 
288  wxArrayString errorStrs;
289 
290  const wxWindow* page = m_gridTypeNotebook->GetCurrentPage();
291 
292  if( page == m_gridPanel )
293  {
294  ARRAY_GRID_OPTIONS* newGrid = new ARRAY_GRID_OPTIONS();
295  bool ok = true;
296 
297  // ints
298  ok = ok && validateLongEntry(*m_entryNx, newGrid->m_nx, _("horizontal count"),
299  errorStrs);
300  ok = ok && validateLongEntry(*m_entryNy, newGrid->m_ny, _("vertical count"),
301  errorStrs);
302 
303 
304  newGrid->m_delta.x = DoubleValueFromString( g_UserUnit, m_entryDx->GetValue() );
305  newGrid->m_delta.y = DoubleValueFromString( g_UserUnit, m_entryDy->GetValue() );
306 
307  newGrid->m_offset.x = DoubleValueFromString( g_UserUnit, m_entryOffsetX->GetValue() );
308  newGrid->m_offset.y = DoubleValueFromString( g_UserUnit, m_entryOffsetY->GetValue() );
309 
310  ok = ok && validateLongEntry(*m_entryStagger, newGrid->m_stagger, _("stagger"),
311  errorStrs);
312 
313  newGrid->m_stagger_rows = m_radioBoxGridStaggerType->GetSelection() == 0;
314 
315  newGrid->m_horizontalThenVertical = m_radioBoxGridNumberingAxis->GetSelection() == 0;
317 
319 
320  if ( m_numberingEnabled )
321  {
322  newGrid->m_2dArrayNumbering = m_radioBoxGridNumberingScheme->GetSelection() != 0;
323 
324  bool numOk = validateNumberingTypeAndOffset(
326  newGrid->m_priAxisNumType, newGrid->m_numberingOffsetX,
327  errorStrs );
328 
329  if( newGrid->m_2dArrayNumbering )
330  {
333  newGrid->m_secAxisNumType, newGrid->m_numberingOffsetY,
334  errorStrs ) && numOk;
335  }
336 
337  ok = ok && numOk;
338 
339  newGrid->m_numberingStartIsSpecified = m_rbGridStartNumberingOpt->GetSelection() == 1;
340  }
341 
342  // Only use settings if all values are good
343  if( ok )
344  newSettings = newGrid;
345  else
346  delete newGrid;
347  }
348  else if( page == m_circularPanel )
349  {
351  bool ok = true;
352 
353  newCirc->m_centre.x = DoubleValueFromString( g_UserUnit, m_entryCentreX->GetValue() );
354  newCirc->m_centre.y = DoubleValueFromString( g_UserUnit, m_entryCentreY->GetValue() );
355 
356  newCirc->m_angle = DoubleValueFromString( DEGREES, m_entryCircAngle->GetValue() );
357 
358  ok = ok && validateLongEntry(*m_entryCircCount, newCirc->m_nPts,
359  _("point count"), errorStrs);
360 
361  newCirc->m_rotateItems = m_entryRotateItemsCb->GetValue();
362 
364 
365  if ( m_numberingEnabled )
366  {
367  newCirc->m_numberingStartIsSpecified = m_rbCircStartNumberingOpt->GetSelection() == 1;
369 
371  newCirc->m_numberingOffset,
372  _("numbering start"), errorStrs);
373  }
374 
375  // Only use settings if all values are good
376  if( ok )
377  newSettings = newCirc;
378  else
379  delete newCirc;
380  }
381 
382  // If we got good settings, send them out and finish
383  if( newSettings )
384  {
385  delete m_settings;
386 
387  // assign pointer and ownership here
388  m_settings = newSettings;
390 
391  EndModal( wxID_OK );
392  }
393  else
394  {
395  wxString errorStr;
396 
397  if( errorStrs.IsEmpty() )
398  errorStr = _("Bad parameters");
399  else
400  errorStr = boost::algorithm::join( errorStrs, "\n" );
401 
402  wxMessageBox( errorStr );
403  }
404 }
405 
406 
408 {
409  if ( m_numberingEnabled )
410  {
411  const bool renumber = m_rbGridStartNumberingOpt->GetSelection() == 1;
412 
413  // If we're not renumbering, we can't set the numbering scheme
414  // or axis numbering types
415  m_radioBoxGridNumberingScheme->Enable( renumber );
416  m_labelPriAxisNumbering->Enable( renumber );
417  m_choicePriAxisNumbering->Enable( renumber );
418 
419  // Disable the secondary axis numbering option if the
420  // numbering scheme doesn't have two axes
421  const bool num2d = m_radioBoxGridNumberingScheme->GetSelection() != 0;
422 
423  m_labelSecAxisNumbering->Enable( renumber && num2d );
424  m_choiceSecAxisNumbering->Enable( renumber && num2d );
425 
426  // We can only set an offset if we renumber
427  m_labelGridNumberingOffset->Enable( renumber );
428  m_entryGridPriNumberingOffset->Enable( renumber );
429  m_entryGridSecNumberingOffset->Enable( renumber && num2d );
430 
431  m_entryCircNumberingStart->Enable( m_rbCircStartNumberingOpt->GetSelection() == 1 );
432  }
433  else
434  {
435  // grid
436  m_rbGridStartNumberingOpt->Enable( false );
437  m_checkBoxGridReverseNumbering->Enable( false );
438  m_radioBoxGridNumberingAxis->Enable( false );
439  m_radioBoxGridNumberingScheme->Enable( false );
440  m_choiceSecAxisNumbering->Enable( false );
441  m_choicePriAxisNumbering->Enable( false );
442  m_entryGridPriNumberingOffset->Enable( false );
443  m_entryGridSecNumberingOffset->Enable( false );
444 
445  // circular
446  m_rbCircStartNumberingOpt->Enable( false );
447  m_entryCircNumberingStart->Enable( false );
448  }
449 }
450 
451 
453 {
454  wxPoint centre;
455 
456  centre.x = DoubleValueFromString( g_UserUnit, m_entryCentreX->GetValue() );
457  centre.y = DoubleValueFromString( g_UserUnit, m_entryCentreY->GetValue() );
458 
459  // Find the radius, etc of the circle
460  centre -= m_originalItemPosition;
461 
462  const double radius = VECTOR2I(centre.x, centre.y).EuclideanNorm();
463  m_labelCircRadiusValue->SetLabelText( StringFromValue( g_UserUnit, int(radius), true ) );
464 }
465 
466 
467 // ARRAY OPTION implementation functions --------------------------------------
468 
471 {
472  wxString itemNum;
473  const wxString& alphabet = alphabetFromNumberingScheme( type );
474 
475  const bool nonUnitColsStartAt0 = schemeNonUnitColsStartAt0( type );
476 
477  bool firstRound = true;
478  int radix = alphabet.Length();
479 
480  do {
481  int modN = n % radix;
482 
483  if( nonUnitColsStartAt0 && !firstRound )
484  modN--; // Start the "tens/hundreds/etc column" at "Ax", not "Bx"
485 
486  itemNum.insert( 0, 1, alphabet[modN] );
487 
488  n /= radix;
489  firstRound = false;
490  } while( n );
491 
492  return itemNum;
493 }
494 
495 
497  int aN, const wxString& aPattern ) const
498 {
499  wxString newStr( aPattern );
500  newStr.Replace( "%s", GetItemNumber( aN ), false );
501 
502  return newStr;
503 }
504 
505 
507 {
508  return m_nx * m_ny;
509 }
510 
511 
513 {
514  const int axisSize = m_horizontalThenVertical ? m_nx : m_ny;
515 
516  int x = n % axisSize;
517  int y = n / axisSize;
518 
519  // reverse on this row/col?
520  if( m_reverseNumberingAlternate && ( y % 2 ) )
521  x = axisSize - x - 1;
522 
523  wxPoint coords( x, y );
524 
525  return coords;
526 }
527 
528 
530  const wxPoint& rotPoint ) const
531 {
532  wxPoint point;
533 
534  wxPoint coords = getGridCoords( n );
535 
536  // swap axes if needed
537  if( !m_horizontalThenVertical )
538  std::swap( coords.x, coords.y );
539 
540  point.x = coords.x * m_delta.x + coords.y * m_offset.x;
541  point.y = coords.y * m_delta.y + coords.x * m_offset.y;
542 
543  if( std::abs( m_stagger ) > 1 )
544  {
545  const int stagger = std::abs( m_stagger );
546  const bool sr = m_stagger_rows;
547  const int stagger_idx = ( ( sr ? coords.y : coords.x ) % stagger );
548 
549  wxPoint stagger_delta( ( sr ? m_delta.x : m_offset.x ),
550  ( sr ? m_offset.y : m_delta.y ) );
551 
552  // Stagger to the left/up if the sign of the stagger is negative
553  point += stagger_delta * copysign( stagger_idx, m_stagger ) / stagger;
554  }
555 
556  // this is already relative to the first array entry
557  item->Move( point );
558 }
559 
560 
562 {
563  wxString itemNum;
564 
565  if( m_2dArrayNumbering )
566  {
567  wxPoint coords = getGridCoords( n );
568 
569  itemNum += getCoordinateNumber( coords.x + m_numberingOffsetX, m_priAxisNumType );
570  itemNum += getCoordinateNumber( coords.y + m_numberingOffsetY, m_secAxisNumType );
571  }
572  else
573  {
574  itemNum += getCoordinateNumber( n + m_numberingOffsetX, m_priAxisNumType );
575  }
576 
577  return itemNum;
578 }
579 
580 
582 {
583  return m_nPts;
584 }
585 
586 
588  const wxPoint& rotPoint ) const
589 {
590  double angle;
591 
592  if( m_angle == 0 )
593  // angle is zero, divide evenly into m_nPts
594  angle = 3600.0 * n / double( m_nPts );
595  else
596  // n'th step
597  angle = m_angle * n;
598 
599  item->Rotate( m_centre, angle );
600 
601  // take off the rotation (but not the translation) if needed
602  if( !m_rotateItems )
603  item->Rotate( item->GetCenter(), -angle );
604 }
605 
606 
608 {
609  return getCoordinateNumber( aN + m_numberingOffset, m_numberingType );
610 }
#define DIM(x)
of elements in an array
Definition: macros.h:98
DIALOG_CREATE_ARRAY(PCB_BASE_FRAME *aParent, bool enableNumbering, wxPoint aOrigPos)
wxString GetItemNumber(int n) const override
void OnParameterChanged(wxCommandEvent &event) override
void TransformItem(int n, BOARD_ITEM *item, const wxPoint &rotPoint) const override
Implementation of conversion functions that require both schematic and board internal units...
Class BOARD_ITEM is a base class for any item which can be embedded within the BOARD container class...
ARRAY_OPTIONS * m_settings
The settings object returned to the caller.
void Add(wxRadioBox *ctrl, int &dest)
Class BOARD to handle a board.
wxString StringFromValue(EDA_UNITS_T aUnit, int aValue, bool aAddUnitSymbol)
Function StringFromValue returns the string from aValue according to units (inch, mm ...
Definition: base_units.cpp:203
static bool getNumberingOffset(const wxString &str, DIALOG_CREATE_ARRAY::ARRAY_NUMBERING_TYPE_T type, int &offsetToFill)
static wxString getCoordinateNumber(int n, ARRAY_NUMBERING_TYPE_T type)
VECTOR2< int > VECTOR2I
Definition: vector2d.h:589
#define abs(a)
Definition: auxiliary.h:84
bool m_numberingStartIsSpecified
True if this array's number starts from the preset point False if the array numbering starts from som...
This file contains miscellaneous commonly used macros and functions.
Arabic numerals: 0,1,2,3,4,5,6,7,8,9,10,11...
virtual const wxPoint GetCenter() const
Function GetCenter()
T EuclideanNorm() const
Destructor.
Definition: vector2d.h:294
#define NUMBERING_TYPE_MAX
virtual void Move(const wxPoint &aMoveVector)
Function Move move this object.
wxString GetAbbreviatedUnitsLabel(EDA_UNITS_T aUnit)
Definition: base_units.cpp:479
static bool schemeNonUnitColsStartAt0(DIALOG_CREATE_ARRAY::ARRAY_NUMBERING_TYPE_T type)
bool m_shouldNumber
True if this array numbers the new items.
virtual void Rotate(const wxPoint &aRotCentre, double aAngle)
Function Rotate Rotate this object.
virtual wxString InterpolateNumberIntoString(int n, const wxString &pattern) const
EDA_UNITS_T g_UserUnit
Global variables definitions.
Definition: common.cpp:56
static bool validateNumberingTypeAndOffset(const wxTextCtrl &offsetEntry, const wxChoice &typeEntry, DIALOG_CREATE_ARRAY::ARRAY_NUMBERING_TYPE_T &type, int &offset, wxArrayString &errors)
Validates and saves (if valid) the type and offset of an array axis numbering.
static CREATE_ARRAY_DIALOG_ENTRIES m_options
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
static const wxString & alphabetFromNumberingScheme(DIALOG_CREATE_ARRAY::ARRAY_NUMBERING_TYPE_T type)
Module description (excepted pads)
void OnOkClick(wxCommandEvent &event) override
void TransformItem(int n, BOARD_ITEM *item, const wxPoint &rotPoint) const override
Class DIALOG_CREATE_ARRAY_BASE.
double DoubleValueFromString(EDA_UNITS_T aUnits, const wxString &aTextValue)
Function DoubleValueFromString converts aTextValue to a double.
Definition: base_units.cpp:301
class PCB_BASE_FRAME basic PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer...
const wxPoint m_originalItemPosition
static bool validateLongEntry(const wxTextEntry &entry, long &dest, const wxString description, wxArrayString &errors)
Validate and save a long integer entry.