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( _("Unrecognised 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 
285 void DIALOG_CREATE_ARRAY::OnOkClick( wxCommandEvent& event )
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  EndModal( wxID_OK );
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  }
405 }
406 
407 
409 {
410  if ( m_numberingEnabled )
411  {
412  const bool renumber = m_rbGridStartNumberingOpt->GetSelection() == 1;
413 
414  // If we're not renumbering, we can't set the numbering scheme
415  // or axis numbering types
416  m_radioBoxGridNumberingScheme->Enable( renumber );
417  m_labelPriAxisNumbering->Enable( renumber );
418  m_choicePriAxisNumbering->Enable( renumber );
419 
420  // Disable the secondary axis numbering option if the
421  // numbering scheme doesn't have two axes
422  const bool num2d = m_radioBoxGridNumberingScheme->GetSelection() != 0;
423 
424  m_labelSecAxisNumbering->Enable( renumber && num2d );
425  m_choiceSecAxisNumbering->Enable( renumber && num2d );
426 
427  // We can only set an offset if we renumber
428  m_labelGridNumberingOffset->Enable( renumber );
429  m_entryGridPriNumberingOffset->Enable( renumber );
430  m_entryGridSecNumberingOffset->Enable( renumber && num2d );
431 
432  m_entryCircNumberingStart->Enable( m_rbCircStartNumberingOpt->GetSelection() == 1 );
433  }
434  else
435  {
436  // grid
437  m_rbGridStartNumberingOpt->Enable( false );
438  m_checkBoxGridReverseNumbering->Enable( false );
439  m_radioBoxGridNumberingAxis->Enable( false );
440  m_radioBoxGridNumberingScheme->Enable( false );
441  m_choiceSecAxisNumbering->Enable( false );
442  m_choicePriAxisNumbering->Enable( false );
443  m_entryGridPriNumberingOffset->Enable( false );
444  m_entryGridSecNumberingOffset->Enable( false );
445 
446  // circular
447  m_rbCircStartNumberingOpt->Enable( false );
448  m_entryCircNumberingStart->Enable( false );
449  }
450 }
451 
452 
454 {
455  wxPoint centre;
456 
457  centre.x = DoubleValueFromString( g_UserUnit, m_entryCentreX->GetValue() );
458  centre.y = DoubleValueFromString( g_UserUnit, m_entryCentreY->GetValue() );
459 
460  // Find the radius, etc of the circle
461  centre -= m_originalItemPosition;
462 
463  const double radius = VECTOR2I(centre.x, centre.y).EuclideanNorm();
464  m_labelCircRadiusValue->SetLabelText( StringFromValue( g_UserUnit, int(radius), true ) );
465 }
466 
467 
468 // ARRAY OPTION implementation functions --------------------------------------
469 
472 {
473  wxString itemNum;
474  const wxString& alphabet = alphabetFromNumberingScheme( type );
475 
476  const bool nonUnitColsStartAt0 = schemeNonUnitColsStartAt0( type );
477 
478  bool firstRound = true;
479  int radix = alphabet.Length();
480 
481  do {
482  int modN = n % radix;
483 
484  if( nonUnitColsStartAt0 && !firstRound )
485  modN--; // Start the "tens/hundreds/etc column" at "Ax", not "Bx"
486 
487  itemNum.insert( 0, 1, alphabet[modN] );
488 
489  n /= radix;
490  firstRound = false;
491  } while( n );
492 
493  return itemNum;
494 }
495 
496 
498  int aN, const wxString& aPattern ) const
499 {
500  wxString newStr( aPattern );
501  newStr.Replace( "%s", GetItemNumber( aN ), false );
502 
503  return newStr;
504 }
505 
506 
508 {
509  return m_nx * m_ny;
510 }
511 
512 
514 {
515  const int axisSize = m_horizontalThenVertical ? m_nx : m_ny;
516 
517  int x = n % axisSize;
518  int y = n / axisSize;
519 
520  // reverse on this row/col?
521  if( m_reverseNumberingAlternate && ( y % 2 ) )
522  x = axisSize - x - 1;
523 
524  wxPoint coords( x, y );
525 
526  return coords;
527 }
528 
529 
531  const wxPoint& rotPoint ) const
532 {
533  wxPoint point;
534 
535  wxPoint coords = getGridCoords( n );
536 
537  // swap axes if needed
538  if( !m_horizontalThenVertical )
539  std::swap( coords.x, coords.y );
540 
541  point.x = coords.x * m_delta.x + coords.y * m_offset.x;
542  point.y = coords.y * m_delta.y + coords.x * m_offset.y;
543 
544  if( std::abs( m_stagger ) > 1 )
545  {
546  const int stagger = std::abs( m_stagger );
547  const bool sr = m_stagger_rows;
548  const int stagger_idx = ( ( sr ? coords.y : coords.x ) % stagger );
549 
550  wxPoint stagger_delta( ( sr ? m_delta.x : m_offset.x ),
551  ( sr ? m_offset.y : m_delta.y ) );
552 
553  // Stagger to the left/up if the sign of the stagger is negative
554  point += stagger_delta * copysign( stagger_idx, m_stagger ) / stagger;
555  }
556 
557  // this is already relative to the first array entry
558  item->Move( point );
559 }
560 
561 
563 {
564  wxString itemNum;
565 
566  if( m_2dArrayNumbering )
567  {
568  wxPoint coords = getGridCoords( n );
569 
570  itemNum += getCoordinateNumber( coords.x + m_numberingOffsetX, m_priAxisNumType );
571  itemNum += getCoordinateNumber( coords.y + m_numberingOffsetY, m_secAxisNumType );
572  }
573  else
574  {
575  itemNum += getCoordinateNumber( n + m_numberingOffsetX, m_priAxisNumType );
576  }
577 
578  return itemNum;
579 }
580 
581 
583 {
584  return m_nPts;
585 }
586 
587 
589  const wxPoint& rotPoint ) const
590 {
591  double angle;
592 
593  if( m_angle == 0 )
594  // angle is zero, divide evenly into m_nPts
595  angle = 3600.0 * n / double( m_nPts );
596  else
597  // n'th step
598  angle = m_angle * n;
599 
600  item->Rotate( m_centre, angle );
601 
602  // take off the rotation (but not the translation) if needed
603  if( !m_rotateItems )
604  item->Rotate( item->GetCenter(), -angle );
605 }
606 
607 
609 {
610  return getCoordinateNumber( aN + m_numberingOffset, m_numberingType );
611 }
#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:204
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
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:484
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:302
class PCB_BASE_FRAME basic PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer...
const wxPoint m_originalItemPosition