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 <macros.h>
28 #include <boost/algorithm/string/join.hpp>
29 #include <widgets/text_ctrl_eval.h>
30 
31 #include <class_drawpanel.h>
32 #include <class_board.h>
33 #include <class_module.h>
34 
35 #include "dialog_create_array.h"
36 
37 
38 // initialise statics
40 
41 
43  bool enableNumbering,
44  wxPoint aOrigPos ) :
45  DIALOG_CREATE_ARRAY_BASE( aParent ),
46  CONFIG_SAVE_RESTORE_WINDOW( m_options.m_optionsSet ),
47  m_settings( NULL ),
48  m_originalItemPosition( aOrigPos ),
49  m_numberingEnabled(enableNumbering)
50 {
51  // Set up numbering scheme drop downs
52  //
53  // character set
54  // NOTE: do not change the order of this relative to the ARRAY_NUMBERING_TYPE_T enum
55  const wxString charSetDescriptions[] =
56  {
57  _( "Numerals (0,1,2,...,9,10)" ),
58  _( "Hexadecimal (0,1,...,F,10,...)" ),
59  _( "Alphabet, minus IOSQXZ" ),
60  _( "Alphabet, full 26 characters" )
61  };
62  m_choicePriAxisNumbering->Set( DIM( charSetDescriptions ), charSetDescriptions );
63  m_choiceSecAxisNumbering->Set( DIM( charSetDescriptions ), charSetDescriptions );
64 
65  m_choicePriAxisNumbering->SetSelection( 0 );
66  m_choiceSecAxisNumbering->SetSelection( 0 );
67 
72 
76 
78 
81 
88 
90 
94 
97 
99 
100  // Load units into labels
101  {
102  const wxString lengthUnit = GetAbbreviatedUnitsLabel( g_UserUnit );
103 
104  m_unitLabelCentreX->SetLabelText( lengthUnit );
105  m_unitLabelCentreY->SetLabelText( lengthUnit );
106  m_unitLabelDx->SetLabelText( lengthUnit );
107  m_unitLabelDy->SetLabelText( lengthUnit );
108  m_unitLabelOffsetX->SetLabelText( lengthUnit );
109  m_unitLabelOffsetY->SetLabelText( lengthUnit );
110  }
111 
112  // Run the callbacks once to process the dialog contents
115 
116  m_stdButtonsOK->SetDefault();
117  Fit();
118  SetMinSize( GetSize() );
119 }
120 
121 
123 {
124  if( m_settings != NULL )
125  delete m_settings;
126 }
127 
128 
129 void DIALOG_CREATE_ARRAY::OnParameterChanged( wxCommandEvent& event )
130 {
133 }
134 
135 
136 static const wxString& alphabetFromNumberingScheme(
138 {
139  static const wxString alphaNumeric = "0123456789";
140  static const wxString alphaHex = "0123456789ABCDEF";
141  static const wxString alphaFull = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
142  static const wxString alphaNoIOSQXZ = "ABCDEFGHJKLMNPRTUVWY";
143 
144  switch( type )
145  {
146  default:
148  break;
149 
151  return alphaHex;
152 
154  return alphaNoIOSQXZ;
155 
157  return alphaFull;
158  }
159 
160  return alphaNumeric;
161 }
162 
163 
169 {
172 }
173 
174 
175 static bool getNumberingOffset( const wxString& str,
177  int& offsetToFill )
178 {
179  const wxString& alphabet = alphabetFromNumberingScheme( type );
180 
181  int offset = 0;
182  const int radix = alphabet.length();
183 
184  for( unsigned i = 0; i < str.length(); i++ )
185  {
186  int chIndex = alphabet.Find( str[i], false );
187 
188  if( chIndex == wxNOT_FOUND )
189  return false;
190 
191  const bool start0 = schemeNonUnitColsStartAt0( type );
192 
193  // eg "AA" is actually index 27, not 26
194  if( start0 && i < str.length() - 1 )
195  chIndex++;
196 
197  offset *= radix;
198  offset += chIndex;
199  }
200 
201  offsetToFill = offset;
202  return true;
203 }
204 
205 
216 static bool validateNumberingTypeAndOffset( const wxTextCtrl& offsetEntry,
217  const wxChoice& typeEntry,
219  int& offset,
220  wxArrayString& errors )
221 {
222  const int typeVal = typeEntry.GetSelection();
223  // mind undefined casts to enums (should not be able to happen)
224  bool ok = typeVal <= DIALOG_CREATE_ARRAY::NUMBERING_TYPE_MAX;
225 
226  if( ok )
227  {
229  }
230  else
231  {
232  wxString err;
233  err.Printf( _("Unrecognized numbering scheme: %d"), typeVal );
234  errors.Add( err );
235  // we can't proceed - we don't know the numbering type
236  return false;
237  }
238 
239  const wxString text = offsetEntry.GetValue();
240  ok = getNumberingOffset( text, type, offset );
241 
242  if( !ok )
243  {
244  const wxString& alphabet = alphabetFromNumberingScheme( type );
245 
246  wxString err;
247  err.Printf( _( "Could not determine numbering start from \"%s\": "
248  "expected value consistent with alphabet \"%s\"" ),
249  text, alphabet );
250  errors.Add(err);
251  }
252 
253  return ok;
254 }
255 
256 
266 static bool validateLongEntry( const wxTextEntry& entry,
267  long& dest,
268  const wxString& description,
269  wxArrayString& errors )
270 {
271  bool ok = true;
272 
273  if( !entry.GetValue().ToLong( &dest ) )
274  {
275  wxString err;
276  err.Printf( _("Bad numeric value for %s: %s"), description, entry.GetValue() );
277  errors.Add( err );
278  ok = false;
279  }
280 
281  return ok;
282 }
283 
284 
286 {
287  ARRAY_OPTIONS* newSettings = NULL;
288 
289  wxArrayString errorStrs;
290 
291  const wxWindow* page = m_gridTypeNotebook->GetCurrentPage();
292 
293  if( page == m_gridPanel )
294  {
295  ARRAY_GRID_OPTIONS* newGrid = new ARRAY_GRID_OPTIONS();
296  bool ok = true;
297 
298  // ints
299  ok = ok && validateLongEntry(*m_entryNx, newGrid->m_nx, _("horizontal count"),
300  errorStrs);
301  ok = ok && validateLongEntry(*m_entryNy, newGrid->m_ny, _("vertical count"),
302  errorStrs);
303 
304 
305  newGrid->m_delta.x = DoubleValueFromString( g_UserUnit, m_entryDx->GetValue() );
306  newGrid->m_delta.y = DoubleValueFromString( g_UserUnit, m_entryDy->GetValue() );
307 
308  newGrid->m_offset.x = DoubleValueFromString( g_UserUnit, m_entryOffsetX->GetValue() );
309  newGrid->m_offset.y = DoubleValueFromString( g_UserUnit, m_entryOffsetY->GetValue() );
310 
311  ok = ok && validateLongEntry(*m_entryStagger, newGrid->m_stagger, _("stagger"),
312  errorStrs);
313 
314  newGrid->m_stagger_rows = m_radioBoxGridStaggerType->GetSelection() == 0;
315 
316  newGrid->m_horizontalThenVertical = m_radioBoxGridNumberingAxis->GetSelection() == 0;
318 
320 
321  if ( m_numberingEnabled )
322  {
323  newGrid->m_2dArrayNumbering = m_radioBoxGridNumberingScheme->GetSelection() != 0;
324 
325  bool numOk = validateNumberingTypeAndOffset(
327  newGrid->m_priAxisNumType, newGrid->m_numberingOffsetX,
328  errorStrs );
329 
330  if( newGrid->m_2dArrayNumbering )
331  {
334  newGrid->m_secAxisNumType, newGrid->m_numberingOffsetY,
335  errorStrs ) && numOk;
336  }
337 
338  ok = ok && numOk;
339 
340  newGrid->m_numberingStartIsSpecified = m_rbGridStartNumberingOpt->GetSelection() == 1;
341  }
342 
343  // Only use settings if all values are good
344  if( ok )
345  newSettings = newGrid;
346  else
347  delete newGrid;
348  }
349  else if( page == m_circularPanel )
350  {
352  bool ok = true;
353 
354  newCirc->m_centre.x = DoubleValueFromString( g_UserUnit, m_entryCentreX->GetValue() );
355  newCirc->m_centre.y = DoubleValueFromString( g_UserUnit, m_entryCentreY->GetValue() );
356 
357  newCirc->m_angle = DoubleValueFromString( DEGREES, m_entryCircAngle->GetValue() );
358 
359  ok = ok && validateLongEntry(*m_entryCircCount, newCirc->m_nPts,
360  _("point count"), errorStrs);
361 
362  newCirc->m_rotateItems = m_entryRotateItemsCb->GetValue();
363 
365 
366  if ( m_numberingEnabled )
367  {
368  newCirc->m_numberingStartIsSpecified = m_rbCircStartNumberingOpt->GetSelection() == 1;
370 
372  newCirc->m_numberingOffset,
373  _("numbering start"), errorStrs);
374  }
375 
376  // Only use settings if all values are good
377  if( ok )
378  newSettings = newCirc;
379  else
380  delete newCirc;
381  }
382 
383  // If we got good settings, send them out and finish
384  if( newSettings )
385  {
386  delete m_settings;
387 
388  // assign pointer and ownership here
389  m_settings = newSettings;
391 
392  return true;
393  }
394  else
395  {
396  wxString errorStr;
397 
398  if( errorStrs.IsEmpty() )
399  errorStr = _("Bad parameters");
400  else
401  errorStr = boost::algorithm::join( errorStrs, "\n" );
402 
403  wxMessageBox( errorStr );
404  return false;
405  }
406 }
407 
408 
410 {
411  if ( m_numberingEnabled )
412  {
413  const bool renumber = m_rbGridStartNumberingOpt->GetSelection() == 1;
414 
415  // If we're not renumbering, we can't set the numbering scheme
416  // or axis numbering types
417  m_radioBoxGridNumberingScheme->Enable( renumber );
418  m_labelPriAxisNumbering->Enable( renumber );
419  m_choicePriAxisNumbering->Enable( renumber );
420 
421  // Disable the secondary axis numbering option if the
422  // numbering scheme doesn't have two axes
423  const bool num2d = m_radioBoxGridNumberingScheme->GetSelection() != 0;
424 
425  m_labelSecAxisNumbering->Enable( renumber && num2d );
426  m_choiceSecAxisNumbering->Enable( renumber && num2d );
427 
428  // We can only set an offset if we renumber
429  m_labelGridNumberingOffset->Enable( renumber );
430  m_entryGridPriNumberingOffset->Enable( renumber );
431  m_entryGridSecNumberingOffset->Enable( renumber && num2d );
432 
433  m_entryCircNumberingStart->Enable( m_rbCircStartNumberingOpt->GetSelection() == 1 );
434  }
435  else
436  {
437  // grid
438  m_rbGridStartNumberingOpt->Enable( false );
439  m_checkBoxGridReverseNumbering->Enable( false );
440  m_radioBoxGridNumberingAxis->Enable( false );
441  m_radioBoxGridNumberingScheme->Enable( false );
442  m_choiceSecAxisNumbering->Enable( false );
443  m_choicePriAxisNumbering->Enable( false );
444  m_entryGridPriNumberingOffset->Enable( false );
445  m_entryGridSecNumberingOffset->Enable( false );
446 
447  // circular
448  m_rbCircStartNumberingOpt->Enable( false );
449  m_entryCircNumberingStart->Enable( false );
450  }
451 }
452 
453 
455 {
456  wxPoint centre;
457 
458  centre.x = DoubleValueFromString( g_UserUnit, m_entryCentreX->GetValue() );
459  centre.y = DoubleValueFromString( g_UserUnit, m_entryCentreY->GetValue() );
460 
461  // Find the radius, etc of the circle
462  centre -= m_originalItemPosition;
463 
464  const double radius = VECTOR2I(centre.x, centre.y).EuclideanNorm();
465  m_labelCircRadiusValue->SetLabelText( StringFromValue( g_UserUnit, int(radius), true ) );
466 }
467 
468 
469 // ARRAY OPTION implementation functions --------------------------------------
470 
473 {
474  wxString itemNum;
475  const wxString& alphabet = alphabetFromNumberingScheme( type );
476 
477  const bool nonUnitColsStartAt0 = schemeNonUnitColsStartAt0( type );
478 
479  bool firstRound = true;
480  int radix = alphabet.Length();
481 
482  do {
483  int modN = n % radix;
484 
485  if( nonUnitColsStartAt0 && !firstRound )
486  modN--; // Start the "tens/hundreds/etc column" at "Ax", not "Bx"
487 
488  itemNum.insert( 0, 1, alphabet[modN] );
489 
490  n /= radix;
491  firstRound = false;
492  } while( n );
493 
494  return itemNum;
495 }
496 
497 
499  int aN, const wxString& aPattern ) const
500 {
501  wxString newStr( aPattern );
502  newStr.Replace( "%s", GetItemNumber( aN ), false );
503 
504  return newStr;
505 }
506 
507 
509 {
510  return m_nx * m_ny;
511 }
512 
513 
515 {
516  const int axisSize = m_horizontalThenVertical ? m_nx : m_ny;
517 
518  int x = n % axisSize;
519  int y = n / axisSize;
520 
521  // reverse on this row/col?
522  if( m_reverseNumberingAlternate && ( y % 2 ) )
523  x = axisSize - x - 1;
524 
525  wxPoint coords( x, y );
526 
527  return coords;
528 }
529 
530 
532  const wxPoint& rotPoint ) const
533 {
534  wxPoint point;
535 
536  wxPoint coords = getGridCoords( n );
537 
538  // swap axes if needed
539  if( !m_horizontalThenVertical )
540  std::swap( coords.x, coords.y );
541 
542  point.x = coords.x * m_delta.x + coords.y * m_offset.x;
543  point.y = coords.y * m_delta.y + coords.x * m_offset.y;
544 
545  if( std::abs( m_stagger ) > 1 )
546  {
547  const int stagger = std::abs( m_stagger );
548  const bool sr = m_stagger_rows;
549  const int stagger_idx = ( ( sr ? coords.y : coords.x ) % stagger );
550 
551  wxPoint stagger_delta( ( sr ? m_delta.x : m_offset.x ),
552  ( sr ? m_offset.y : m_delta.y ) );
553 
554  // Stagger to the left/up if the sign of the stagger is negative
555  point += stagger_delta * copysign( stagger_idx, m_stagger ) / stagger;
556  }
557 
558  // this is already relative to the first array entry
559  item->Move( point );
560 }
561 
562 
564 {
565  wxString itemNum;
566 
567  if( m_2dArrayNumbering )
568  {
569  wxPoint coords = getGridCoords( n );
570 
571  itemNum += getCoordinateNumber( coords.x + m_numberingOffsetX, m_priAxisNumType );
572  itemNum += getCoordinateNumber( coords.y + m_numberingOffsetY, m_secAxisNumType );
573  }
574  else
575  {
576  itemNum += getCoordinateNumber( n + m_numberingOffsetX, m_priAxisNumType );
577  }
578 
579  return itemNum;
580 }
581 
582 
584 {
585  return m_nPts;
586 }
587 
588 
590  const wxPoint& rotPoint ) const
591 {
592  double angle;
593 
594  if( m_angle == 0 )
595  // angle is zero, divide evenly into m_nPts
596  angle = 3600.0 * n / double( m_nPts );
597  else
598  // n'th step
599  angle = m_angle * n;
600 
601  item->Rotate( m_centre, angle );
602 
603  // take off the rotation (but not the translation) if needed
604  if( !m_rotateItems )
605  item->Rotate( item->GetCenter(), -angle );
606 }
607 
608 
610 {
611  return getCoordinateNumber( aN + m_numberingOffset, m_numberingType );
612 }
#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:205
static bool getNumberingOffset(const wxString &str, DIALOG_CREATE_ARRAY::ARRAY_NUMBERING_TYPE_T type, int &offsetToFill)
static bool validateLongEntry(const wxTextEntry &entry, long &dest, const wxString &description, wxArrayString &errors)
Validate and save a long integer entry.
static wxString getCoordinateNumber(int n, ARRAY_NUMBERING_TYPE_T type)
VECTOR2< int > VECTOR2I
Definition: vector2d.h:589
#define abs(a)
Definition: auxiliary.h:84
const string & str
Definition: json11.cpp:596
bool m_numberingStartIsSpecified
True if this array&#39;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:485
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.
bool TransferDataFromWindow() override
string & err
Definition: json11.cpp:598
virtual wxString InterpolateNumberIntoString(int n, const wxString &pattern) const
EDA_UNITS_T g_UserUnit
Global variables definitions.
Definition: common.cpp:57
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)
size_t i
Definition: json11.cpp:597
static const wxString & alphabetFromNumberingScheme(DIALOG_CREATE_ARRAY::ARRAY_NUMBERING_TYPE_T type)
Module description (excepted pads)
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:303
class PCB_BASE_FRAME basic PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer...
const wxPoint m_originalItemPosition