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 <pcb_edit_frame.h>
26 #include <base_units.h>
27 #include <boost/algorithm/string/join.hpp>
28 #include <widgets/text_ctrl_eval.h>
29 #include <class_board.h>
30 #include <class_module.h>
31 
32 #include "dialog_create_array.h"
33 
34 
35 // initialise statics
37 
38 
40  wxPoint aOrigPos ) :
41  DIALOG_CREATE_ARRAY_BASE( aParent ),
42  CONFIG_SAVE_RESTORE_WINDOW( m_options.m_optionsSet ),
43  m_settings( NULL ),
44  m_hSpacing( aParent, m_labelDx, m_entryDx, m_unitLabelDx, true ),
45  m_vSpacing( aParent, m_labelDy, m_entryDy, m_unitLabelDy, true ),
46  m_hOffset( aParent, m_labelOffsetX, m_entryOffsetX, m_unitLabelOffsetX, true ),
47  m_vOffset( aParent, m_labelOffsetY, m_entryOffsetY, m_unitLabelOffsetY, true ),
48  m_hCentre( aParent, m_labelCentreX, m_entryCentreX, m_unitLabelCentreX, true ),
49  m_vCentre( aParent, m_labelCentreY, m_entryCentreY, m_unitLabelCentreY, true ),
50  m_circRadius( aParent, m_labelCircRadius, m_valueCircRadius, m_unitLabelCircRadius, true ),
51  m_originalItemPosition( aOrigPos ),
52  m_numberingEnabled(enableNumbering)
53 {
54  // Set up numbering scheme drop downs
55  //
56  // character set
57  // NOTE: do not change the order of this relative to the NUMBERING_TYPE_T enum
58  const wxString charSetDescriptions[] =
59  {
60  _( "Numerals (0,1,2,...,9,10)" ),
61  _( "Hexadecimal (0,1,...,F,10,...)" ),
62  _( "Alphabet, minus IOSQXZ" ),
63  _( "Alphabet, full 26 characters" )
64  };
65  m_choicePriAxisNumbering->Set( DIM( charSetDescriptions ), charSetDescriptions );
66  m_choiceSecAxisNumbering->Set( DIM( charSetDescriptions ), charSetDescriptions );
67 
68  m_choicePriAxisNumbering->SetSelection( 0 );
69  m_choiceSecAxisNumbering->SetSelection( 0 );
70 
75 
79 
81 
84 
91 
93 
97 
100 
102 
103  // Run the callbacks once to process the dialog contents
106 
107  m_stdButtonsOK->SetDefault();
108  Fit();
109  SetMinSize( GetSize() );
110 }
111 
112 
114 {
115  if( m_settings != NULL )
116  delete m_settings;
117 }
118 
119 
120 void DIALOG_CREATE_ARRAY::OnParameterChanged( wxCommandEvent& event )
121 {
124 }
125 
126 
128 {
129  static const wxString alphaNumeric = "0123456789";
130  static const wxString alphaHex = "0123456789ABCDEF";
131  static const wxString alphaFull = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
132  static const wxString alphaNoIOSQXZ = "ABCDEFGHJKLMNPRTUVWY";
133 
134  switch( type )
135  {
136  default:
137  case DIALOG_CREATE_ARRAY::NUMBERING_NUMERIC: return alphaNumeric;
138  case DIALOG_CREATE_ARRAY::NUMBERING_HEX: return alphaHex;
139  case DIALOG_CREATE_ARRAY::NUMBERING_ALPHA_NO_IOSQXZ: return alphaNoIOSQXZ;
140  case DIALOG_CREATE_ARRAY::NUMBERING_ALPHA_FULL: return alphaFull;
141  }
142 }
143 
144 
150 {
153 }
154 
155 
157  int& offsetToFill )
158 {
159  const wxString& alphabet = alphabetFromNumberingScheme( type );
160 
161  int offset = 0;
162  const int radix = alphabet.length();
163 
164  for( unsigned i = 0; i < str.length(); i++ )
165  {
166  int chIndex = alphabet.Find( str[i], false );
167 
168  if( chIndex == wxNOT_FOUND )
169  return false;
170 
171  const bool start0 = schemeNonUnitColsStartAt0( type );
172 
173  // eg "AA" is actually index 27, not 26
174  if( start0 && i < str.length() - 1 )
175  chIndex++;
176 
177  offset *= radix;
178  offset += chIndex;
179  }
180 
181  offsetToFill = offset;
182  return true;
183 }
184 
185 
196 static bool validateNumberingTypeAndOffset( const wxTextCtrl& offsetEntry,
197  const wxChoice& typeEntry,
199  int& offset, wxArrayString& errors )
200 {
201  const int typeVal = typeEntry.GetSelection();
202  // mind undefined casts to enums (should not be able to happen)
203  bool ok = typeVal <= DIALOG_CREATE_ARRAY::NUMBERING_TYPE_MAX;
204 
205  if( ok )
206  {
207  type = (DIALOG_CREATE_ARRAY::NUMBERING_TYPE_T) typeVal;
208  }
209  else
210  {
211  wxString err;
212  err.Printf( _("Unrecognized numbering scheme: %d"), typeVal );
213  errors.Add( err );
214  // we can't proceed - we don't know the numbering type
215  return false;
216  }
217 
218  const wxString text = offsetEntry.GetValue();
219  ok = getNumberingOffset( text, type, offset );
220 
221  if( !ok )
222  {
223  const wxString& alphabet = alphabetFromNumberingScheme( type );
224 
225  wxString err;
226  err.Printf( _( "Could not determine numbering start from \"%s\": "
227  "expected value consistent with alphabet \"%s\"" ),
228  text, alphabet );
229  errors.Add(err);
230  }
231 
232  return ok;
233 }
234 
235 
245 static bool validateLongEntry( const wxTextEntry& entry, long& dest, const wxString& description,
246  wxArrayString& errors )
247 {
248  bool ok = true;
249 
250  if( !entry.GetValue().ToLong( &dest ) )
251  {
252  wxString err;
253  err.Printf( _("Bad numeric value for %s: %s"), description, entry.GetValue() );
254  errors.Add( err );
255  ok = false;
256  }
257 
258  return ok;
259 }
260 
261 
263 {
264  ARRAY_OPTIONS* newSettings = NULL;
265  wxArrayString errors;
266  const wxWindow* page = m_gridTypeNotebook->GetCurrentPage();
267 
268  if( page == m_gridPanel )
269  {
270  ARRAY_GRID_OPTIONS* newGrid = new ARRAY_GRID_OPTIONS();
271  bool ok = true;
272 
273  // ints
274  ok = ok && validateLongEntry(*m_entryNx, newGrid->m_nx, _("horizontal count"), errors);
275  ok = ok && validateLongEntry(*m_entryNy, newGrid->m_ny, _("vertical count"), errors);
276 
277  newGrid->m_delta.x = m_hSpacing.GetValue();
278  newGrid->m_delta.y = m_vSpacing.GetValue();
279 
280  newGrid->m_offset.x = m_hOffset.GetValue();
281  newGrid->m_offset.y = m_vOffset.GetValue();
282 
283  ok = ok && validateLongEntry(*m_entryStagger, newGrid->m_stagger, _("stagger"), errors);
284 
285  newGrid->m_stagger_rows = m_radioBoxGridStaggerType->GetSelection() == 0;
286 
287  newGrid->m_horizontalThenVertical = m_radioBoxGridNumberingAxis->GetSelection() == 0;
289 
291 
292  if ( m_numberingEnabled )
293  {
294  newGrid->m_2dArrayNumbering = m_radioBoxGridNumberingScheme->GetSelection() != 0;
295 
296  bool numOk = validateNumberingTypeAndOffset(
298  newGrid->m_priAxisNumType, newGrid->m_numberingOffsetX,
299  errors );
300 
301  if( newGrid->m_2dArrayNumbering )
302  {
305  newGrid->m_secAxisNumType, newGrid->m_numberingOffsetY,
306  errors ) && numOk;
307  }
308 
309  ok = ok && numOk;
310 
311  newGrid->m_numberingStartIsSpecified = m_rbGridStartNumberingOpt->GetSelection() == 1;
312  }
313 
314  // Only use settings if all values are good
315  if( ok )
316  newSettings = newGrid;
317  else
318  delete newGrid;
319  }
320  else if( page == m_circularPanel )
321  {
323  bool ok = true;
324 
325  newCirc->m_centre.x = m_hCentre.GetValue();
326  newCirc->m_centre.y = m_vCentre.GetValue();
327  newCirc->m_angle = DoubleValueFromString( DEGREES, m_entryCircAngle->GetValue() );
328 
329  ok = ok && validateLongEntry(*m_entryCircCount, newCirc->m_nPts, _("point count"), errors);
330 
331  newCirc->m_rotateItems = m_entryRotateItemsCb->GetValue();
333 
334  if ( m_numberingEnabled )
335  {
336  newCirc->m_numberingStartIsSpecified = m_rbCircStartNumberingOpt->GetSelection() == 1;
338 
340  _("numbering start"), errors);
341  }
342 
343  // Only use settings if all values are good
344  if( ok )
345  newSettings = newCirc;
346  else
347  delete newCirc;
348  }
349 
350  // If we got good settings, send them out and finish
351  if( newSettings )
352  {
353  delete m_settings;
354 
355  // assign pointer and ownership here
356  m_settings = newSettings;
358 
359  return true;
360  }
361  else
362  {
363  wxString errorStr;
364 
365  if( errors.IsEmpty() )
366  errorStr = _("Bad parameters");
367  else
368  errorStr = boost::algorithm::join( errors, "\n" );
369 
370  wxMessageBox( errorStr );
371  return false;
372  }
373 }
374 
375 
377 {
378  if ( m_numberingEnabled )
379  {
380  const bool renumber = m_rbGridStartNumberingOpt->GetSelection() == 1;
381 
382  // If we're not renumbering, we can't set the numbering scheme
383  // or axis numbering types
384  m_radioBoxGridNumberingScheme->Enable( renumber );
385  m_labelPriAxisNumbering->Enable( renumber );
386  m_choicePriAxisNumbering->Enable( renumber );
387 
388  // Disable the secondary axis numbering option if the
389  // numbering scheme doesn't have two axes
390  const bool num2d = m_radioBoxGridNumberingScheme->GetSelection() != 0;
391 
392  m_labelSecAxisNumbering->Enable( renumber && num2d );
393  m_choiceSecAxisNumbering->Enable( renumber && num2d );
394 
395  // We can only set an offset if we renumber
396  m_labelGridNumberingOffset->Enable( renumber );
397  m_entryGridPriNumberingOffset->Enable( renumber );
398  m_entryGridSecNumberingOffset->Enable( renumber && num2d );
399 
400  m_entryCircNumberingStart->Enable( m_rbCircStartNumberingOpt->GetSelection() == 1 );
401  }
402  else
403  {
404  // grid
405  m_rbGridStartNumberingOpt->Enable( false );
406  m_checkBoxGridReverseNumbering->Enable( false );
407  m_radioBoxGridNumberingAxis->Enable( false );
408  m_radioBoxGridNumberingScheme->Enable( false );
409  m_choiceSecAxisNumbering->Enable( false );
410  m_choicePriAxisNumbering->Enable( false );
411  m_entryGridPriNumberingOffset->Enable( false );
412  m_entryGridSecNumberingOffset->Enable( false );
413 
414  // circular
415  m_rbCircStartNumberingOpt->Enable( false );
416  m_entryCircNumberingStart->Enable( false );
417  }
418 }
419 
420 
422 {
424 
425  // Find the radius, etc of the circle
426  centre -= m_originalItemPosition;
427 
428  const double radius = VECTOR2I(centre.x, centre.y).EuclideanNorm();
429  m_circRadius.SetValue( int( radius ) );
430 }
431 
432 
433 // ARRAY OPTION implementation functions --------------------------------------
434 
436  NUMBERING_TYPE_T type )
437 {
438  wxString itemNum;
439  const wxString& alphabet = alphabetFromNumberingScheme( type );
440 
441  const bool nonUnitColsStartAt0 = schemeNonUnitColsStartAt0( type );
442 
443  bool firstRound = true;
444  int radix = alphabet.Length();
445 
446  do {
447  int modN = n % radix;
448 
449  if( nonUnitColsStartAt0 && !firstRound )
450  modN--; // Start the "tens/hundreds/etc column" at "Ax", not "Bx"
451 
452  itemNum.insert( 0, 1, alphabet[modN] );
453 
454  n /= radix;
455  firstRound = false;
456  } while( n );
457 
458  return itemNum;
459 }
460 
461 
463  int aN, const wxString& aPattern ) const
464 {
465  wxString newStr( aPattern );
466  newStr.Replace( "%s", GetItemNumber( aN ), false );
467 
468  return newStr;
469 }
470 
471 
473 {
474  return m_nx * m_ny;
475 }
476 
477 
479 {
480  const int axisSize = m_horizontalThenVertical ? m_nx : m_ny;
481 
482  int x = n % axisSize;
483  int y = n / axisSize;
484 
485  // reverse on this row/col?
486  if( m_reverseNumberingAlternate && ( y % 2 ) )
487  x = axisSize - x - 1;
488 
489  wxPoint coords( x, y );
490 
491  return coords;
492 }
493 
494 
496  const wxPoint& rotPoint ) const
497 {
498  wxPoint point;
499 
500  wxPoint coords = getGridCoords( n );
501 
502  // swap axes if needed
503  if( !m_horizontalThenVertical )
504  std::swap( coords.x, coords.y );
505 
506  point.x = coords.x * m_delta.x + coords.y * m_offset.x;
507  point.y = coords.y * m_delta.y + coords.x * m_offset.y;
508 
509  if( std::abs( m_stagger ) > 1 )
510  {
511  const int stagger = std::abs( m_stagger );
512  const bool sr = m_stagger_rows;
513  const int stagger_idx = ( ( sr ? coords.y : coords.x ) % stagger );
514 
515  wxPoint stagger_delta( ( sr ? m_delta.x : m_offset.x ),
516  ( sr ? m_offset.y : m_delta.y ) );
517 
518  // Stagger to the left/up if the sign of the stagger is negative
519  point += stagger_delta * copysign( stagger_idx, m_stagger ) / stagger;
520  }
521 
522  // this is already relative to the first array entry
523  item->Move( point );
524 }
525 
526 
528 {
529  wxString itemNum;
530 
531  if( m_2dArrayNumbering )
532  {
533  wxPoint coords = getGridCoords( n );
534 
535  itemNum += getCoordinateNumber( coords.x + m_numberingOffsetX, m_priAxisNumType );
536  itemNum += getCoordinateNumber( coords.y + m_numberingOffsetY, m_secAxisNumType );
537  }
538  else
539  {
540  itemNum += getCoordinateNumber( n + m_numberingOffsetX, m_priAxisNumType );
541  }
542 
543  return itemNum;
544 }
545 
546 
548 {
549  return m_nPts;
550 }
551 
552 
554  const wxPoint& rotPoint ) const
555 {
556  double angle;
557 
558  if( m_angle == 0 )
559  // angle is zero, divide evenly into m_nPts
560  angle = 3600.0 * n / double( m_nPts );
561  else
562  // n'th step
563  angle = m_angle * n;
564 
565  item->Rotate( m_centre, angle );
566 
567  // take off the rotation (but not the translation) if needed
568  if( !m_rotateItems )
569  item->Rotate( item->GetCenter(), -angle );
570 }
571 
572 
574 {
575  return getCoordinateNumber( aN + m_numberingOffset, m_numberingType );
576 }
static bool schemeNonUnitColsStartAt0(DIALOG_CREATE_ARRAY::NUMBERING_TYPE_T type)
#define DIM(x)
of elements in an array
Definition: macros.h:98
static const wxString & alphabetFromNumberingScheme(DIALOG_CREATE_ARRAY::NUMBERING_TYPE_T type)
DIALOG_CREATE_ARRAY(PCB_BASE_FRAME *aParent, bool enableNumbering, wxPoint aOrigPos)
wxString GetItemNumber(int n) const override
void OnParameterChanged(wxCommandEvent &event) override
Arabic numerals: 0,1,2,3,4,5,6,7,8,9,10,11...
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.
static bool validateLongEntry(const wxTextEntry &entry, long &dest, const wxString &description, wxArrayString &errors)
Validate and save a long integer entry.
VECTOR2< int > VECTOR2I
Definition: vector2d.h:587
#define abs(a)
Definition: auxiliary.h:84
const string & str
Definition: json11.cpp:596
static wxString getCoordinateNumber(int n, NUMBERING_TYPE_T type)
bool m_numberingStartIsSpecified
True if this array&#39;s number starts from the preset point False if the array numbering starts from som...
virtual int GetValue()
Function GetValue Returns the current value in Internal Units.
virtual const wxPoint GetCenter() const
Function GetCenter()
static bool validateNumberingTypeAndOffset(const wxTextCtrl &offsetEntry, const wxChoice &typeEntry, DIALOG_CREATE_ARRAY::NUMBERING_TYPE_T &type, int &offset, wxArrayString &errors)
Validates and saves (if valid) the type and offset of an array axis numbering.
T EuclideanNorm() const
Destructor.
Definition: vector2d.h:292
static bool getNumberingOffset(const wxString &str, DIALOG_CREATE_ARRAY::NUMBERING_TYPE_T type, int &offsetToFill)
#define NUMBERING_TYPE_MAX
virtual void Move(const wxPoint &aMoveVector)
Function Move move this object.
double DoubleValueFromString(EDA_UNITS_T aUnits, const wxString &aTextValue, bool aUseMils)
Function DoubleValueFromString converts aTextValue to a double.
Definition: base_units.cpp:302
bool m_shouldNumber
True if this array numbers the new items.
virtual void Rotate(const wxPoint &aRotCentre, double aAngle)
Function Rotate Rotate this object.
bool TransferDataFromWindow() override
string & err
Definition: json11.cpp:598
virtual wxString InterpolateNumberIntoString(int n, const wxString &pattern) const
static CREATE_ARRAY_DIALOG_ENTRIES m_options
virtual void SetValue(int aValue)
Function SetValue Sets new value (in Internal Units) for the text field, taking care of units convers...
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
size_t i
Definition: json11.cpp:597
Module description (excepted pads)
void TransformItem(int n, BOARD_ITEM *item, const wxPoint &rotPoint) const override
Class DIALOG_CREATE_ARRAY_BASE.
class PCB_BASE_FRAME basic PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer...
const wxPoint m_originalItemPosition