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
virtual int GetValue() const
Function GetValue Returns the current value in Internal Units.
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 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