KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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) 1992-2023 KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
25
26#include <base_units.h>
28#include <board.h>
29#include <footprint.h>
30#include <pcb_edit_frame.h>
31#include <wx/msgdlg.h>
32
33#include <boost/algorithm/string/join.hpp>
34
39{
45 m_OptionsSet( true ),
46 m_GridNx( 5 ),
47 m_GridNy( 5 ),
48 m_GridDx( pcbIUScale.mmToIU( 2.54 ) ),
49 m_GridDy( pcbIUScale.mmToIU( 2.54 ) ),
50 m_GridOffsetX( 0 ),
51 m_GridOffsetY( 0 ),
52 m_GridStagger( 1 ),
53 m_GridStaggerType( 0 ), // rows
54 m_GridNumberingAxis( 0 ), // h then v
55 m_GridNumReverseAlt( false ),
56 m_GridNumStartSet( 1 ), // use specified start
57 m_Grid2dArrayNumbering( 0 ), // linear numbering
58 m_GridPrimaryAxisScheme( 0 ), // numeric
59 m_GridSecondaryAxisScheme( 0 ), // numeric
60 m_GridPrimaryNumOffset( wxT( "1" ) ), // numeric
61 m_GridSecondaryNumOffset( wxT( "1" ) ), // numeric
64 m_CircCentreX( 0 ),
65 m_CircCentreY( 0 ),
66 m_CircCount( 4 ),
67 m_CircNumStartSet( 1 ), // use specified start
69 m_CircNumberingOffset( wxT( "1" ) ),
71 m_CircRotatationStep( false ),
72 m_ArrayTypeTab( 0 ), // start on grid view
74 m_FootprintReannotate( true ), // Assign unique by default
75 m_CenterByRadius( false ),
76 m_CenterByPosition( true )
77 {
78 }
79
81
89
101
116};
117
118// Persistent options settings
120
125{
127 wxString m_label;
128};
129
134static const std::vector<NUMBERING_LIST_DATA> numberingTypeData {
135 {
137 _( "Numerals (0,1,2,...,9,10)" ),
138 },
139 {
141 _( "Hexadecimal (0,1,...,F,10,...)" ),
142 },
143 {
145 _( "Alphabet, minus IOSQXZ" ),
146 },
147 {
149 _( "Alphabet, full 26 characters" ),
150 },
151};
152
154 std::unique_ptr<ARRAY_OPTIONS>& aSettings,
155 bool aIsFootprintEditor, const VECTOR2I& aOrigPos ) :
156 DIALOG_CREATE_ARRAY_BASE( aParent ),
157 m_settings( aSettings ),
158 m_originalItemPosition( aOrigPos ),
159 m_isFootprintEditor( aIsFootprintEditor ),
160 m_hSpacing( aParent, m_labelDx, m_entryDx, m_unitLabelDx ),
161 m_vSpacing( aParent, m_labelDy, m_entryDy, m_unitLabelDy ),
162 m_hOffset( aParent, m_labelOffsetX, m_entryOffsetX, m_unitLabelOffsetX ),
163 m_vOffset( aParent, m_labelOffsetY, m_entryOffsetY, m_unitLabelOffsetY ),
164 m_refPosX( aParent, m_stRefPosXTxt, m_tcRefPosX, m_stRefPosXUnit ),
165 m_refPosY( aParent, m_stRefPosYTxt, m_tcRefPosY, m_stRefPosYUnit ),
166 m_hCentre( aParent, m_labelCentreX, m_entryCentreX, m_unitLabelCentreX ),
167 m_vCentre( aParent, m_labelCentreY, m_entryCentreY, m_unitLabelCentreY ),
168 m_circRadius( aParent, m_labelCircRadius, m_tcValueCircRadius, m_unitLabelCircRadius ),
169 m_circCenterAngle( aParent, m_labelCircCenterAngle, m_tcValueCircCenterAngle, m_unitLabelCircCenterAngle ),
170 m_circAngle( aParent, m_labelCircAngle, m_entryCircAngle, m_unitLabelCircAngle ),
171 m_cfg_persister( pcbIUScale, s_arrayOptions.m_OptionsSet )
172{
173 // Configure display origin transforms
180
181 // Set up numbering scheme drop downs character set strings
182 for( const auto& numData : numberingTypeData )
183 {
184 const wxString label = wxGetTranslation( numData.m_label );
185 void* clientData = (void*) &numData;
186
187 m_choicePriAxisNumbering->Append( label, clientData );
188 m_choiceSecAxisNumbering->Append( label, clientData );
189 m_choiceCircNumbering->Append( label, clientData );
190 }
191
192 m_choicePriAxisNumbering->SetSelection( 0 );
193 m_choiceSecAxisNumbering->SetSelection( 0 );
194 m_choiceCircNumbering->SetSelection( 0 );
195
196 m_circCenterAngle.SetUnits( EDA_UNITS::DEGREES );
197 m_circAngle.SetUnits( EDA_UNITS::DEGREES );
198
199 // bind grid options to persister
204
208
210
213
218
223
224 // bind circular options to persister
230
235
237
240
243
245
246 // Run the callbacks once to process the dialog contents
250
252 Fit();
253 SetMinSize( GetSize() );
254}
255
256
257void DIALOG_CREATE_ARRAY::OnButtonPosition( wxCommandEvent& event )
258{
260}
261
262
263void DIALOG_CREATE_ARRAY::OnButtonRadius( wxCommandEvent& event )
264{
266}
267
268
270{
271 if( m_radioBtnSetByRadius->GetValue() )
272 {
273 m_entryCentreX->Disable();
274 m_entryCentreY->Disable();
275 m_tcValueCircRadius->Enable();
276 m_tcValueCircCenterAngle->Enable();
277 }
278 else
279 {
280 m_entryCentreX->Enable();
281 m_entryCentreY->Enable();
282 m_tcValueCircRadius->Disable();
283 m_tcValueCircCenterAngle->Disable();
284 }
285}
286
287
288void DIALOG_CREATE_ARRAY::OnParameterChanged( wxCommandEvent& event )
289{
291
292 if( m_radioBtnSetByPos->GetValue() )
293 {
296 }
297}
298
299void DIALOG_CREATE_ARRAY::OnRadiusChanged( wxCommandEvent& event )
300{
302
303 if( m_radioBtnSetByRadius->GetValue() )
304 {
307 }
308}
309
310
320static bool validateLongEntry( const wxTextEntry& entry, long& dest, const wxString& description,
321 wxArrayString& errors )
322{
323 bool ok = true;
324
325 if( !entry.GetValue().ToLong( &dest ) )
326 {
327 wxString err;
328 err.Printf( _( "Bad numeric value for %s: %s" ), description, entry.GetValue() );
329 errors.Add( err );
330 ok = false;
331 }
332
333 return ok;
334}
335
336
347static bool validateAxisOptions( const wxTextCtrl& offsetEntry, const wxChoice& typeEntry,
348 const wxTextCtrl& aStepEntry, ARRAY_AXIS& aAxis,
349 wxArrayString& errors )
350{
351 void* clientData = typeEntry.GetClientData( typeEntry.GetSelection() );
352 const NUMBERING_LIST_DATA* numberingData = static_cast<NUMBERING_LIST_DATA*>( clientData );
353
354 wxCHECK_MSG( numberingData, false, wxT( "Failed to get client data from list control." ) );
355
356 aAxis.SetAxisType( numberingData->m_numbering_type );
357
358 const wxString text = offsetEntry.GetValue();
359
360 bool ok = aAxis.SetOffset( text );
361
362 if( !ok )
363 {
364 errors.Add( wxString::Format( _( "Could not determine numbering start from '%s': "
365 "expected value consistent with alphabet '%s'." ),
366 text,
367 aAxis.GetAlphabet() ) );
368 return false;
369 }
370
371 long step;
372 ok = validateLongEntry( aStepEntry, step, _( "step value" ), errors );
373
374 if( ok )
375 aAxis.SetStep( step );
376
377 return ok;
378}
379
380
382{
383 std::unique_ptr<ARRAY_OPTIONS> newSettings;
384
385 wxArrayString errors;
386 const wxWindow* page = m_gridTypeNotebook->GetCurrentPage();
387
388 if( page == m_gridPanel )
389 {
390 auto newGrid = std::make_unique<ARRAY_GRID_OPTIONS>();
391 bool ok = true;
392
393 // ints
394 ok &= validateLongEntry(*m_entryNx, newGrid->m_nx, _("horizontal count"), errors);
395 ok &= validateLongEntry(*m_entryNy, newGrid->m_ny, _("vertical count"), errors);
396
397 newGrid->m_delta.x = m_hSpacing.GetValue();
398 newGrid->m_delta.y = m_vSpacing.GetValue();
399
400 newGrid->m_offset.x = m_hOffset.GetValue();
401 newGrid->m_offset.y = m_vOffset.GetValue();
402
403 ok &= validateLongEntry(*m_entryStagger, newGrid->m_stagger, _("stagger"), errors);
404
405 newGrid->m_stagger_rows = m_radioBoxGridStaggerType->GetSelection() == 0;
406
407 newGrid->m_horizontalThenVertical = m_radioBoxGridNumberingAxis->GetSelection() == 0;
408 newGrid->m_reverseNumberingAlternate = m_checkBoxGridReverseNumbering->GetValue();
409
410 newGrid->SetShouldNumber( m_isFootprintEditor );
411
413 {
414 newGrid->SetNumberingStartIsSpecified( m_rbGridStartNumberingOpt->GetSelection() == 1 );
415
416 if( newGrid->GetNumberingStartIsSpecified() )
417 {
418 newGrid->m_2dArrayNumbering = m_radioBoxGridNumberingScheme->GetSelection() != 0;
419
420 // validate from the input fields
424 newGrid->m_pri_axis, errors );
425
426 if( newGrid->m_2dArrayNumbering )
427 {
431 newGrid->m_sec_axis, errors );
432 }
433
434 ok &= numOk;
435 }
436 else
437 {
438 // artificial linear numeric scheme from 1
439 newGrid->m_2dArrayNumbering = false;
440 newGrid->m_pri_axis.SetAxisType( ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_NUMERIC );
441 newGrid->m_pri_axis.SetOffset( 1 );
442 }
443 }
444
445 // Only use settings if all values are good
446 if( ok )
447 newSettings = std::move( newGrid );
448 }
449 else if( page == m_circularPanel )
450 {
451 auto newCirc = std::make_unique<ARRAY_CIRCULAR_OPTIONS>();
452 bool ok = true;
454
455 newCirc->m_centre.x = m_hCentre.GetValue();
456 newCirc->m_centre.y = m_vCentre.GetValue();
457 newCirc->m_angle = EDA_ANGLE( angle, DEGREES_T );
458
459 ok = validateLongEntry(*m_entryCircCount, newCirc->m_nPts, _("point count"), errors);
460
461 newCirc->m_rotateItems = m_entryRotateItemsCb->GetValue();
462 newCirc->SetShouldNumber( m_isFootprintEditor );
463
465 {
466 newCirc->SetNumberingStartIsSpecified( m_rbCircStartNumberingOpt->GetSelection() == 1 );
467
468 if( newCirc->GetNumberingStartIsSpecified() )
469 {
471 *m_entryCircNumberingStep, newCirc->m_axis, errors );
472 }
473 else
474 {
475 // artificial linear numeric scheme from 1
476 newCirc->m_axis.SetAxisType( ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_NUMERIC );
477 newCirc->m_axis.SetOffset( 1 ); // Start at "1"
478 }
479 }
480
481 // Only use settings if all values are good
482 if( ok )
483 newSettings = std::move( newCirc );
484 }
485
486 // If we got good settings, send them out and finish
487 if( newSettings )
488 {
489 // assign pointer and ownership here
490 m_settings = std::move( newSettings );
491
492 m_settings->SetSShouldReannotateFootprints( m_radioBtnUniqueRefs->GetValue() );
493
494 // persist the control state for next time
496
497 return true;
498 }
499 else
500 {
501 wxString errorStr;
502
503 if( errors.IsEmpty() )
504 errorStr = _("Bad parameters");
505 else
506 errorStr = boost::algorithm::join( errors, wxT( "\n" ) );
507
508 wxMessageBox( errorStr );
509 return false;
510 }
511}
512
513
515{
517 {
518 m_footprintReannotatePanel->Show( false );
519
520 m_gridPadNumberingPanel->Show( true );
521 m_circularPadNumberingPanel->Show( true );
522
523 // If we set the start number, we can set the other options,
524 // otherwise it's a hardcoded linear array
525 const bool use_set_start_grid = m_rbGridStartNumberingOpt->GetSelection() == 1;
526
527 m_radioBoxGridNumberingScheme->Enable( use_set_start_grid );
528 m_labelPriAxisNumbering->Enable( use_set_start_grid );
529 m_choicePriAxisNumbering->Enable( use_set_start_grid );
530
531 // Disable the secondary axis numbering option if the
532 // numbering scheme doesn't have two axes
533 const bool num2d = m_radioBoxGridNumberingScheme->GetSelection() != 0;
534
535 m_labelSecAxisNumbering->Enable( use_set_start_grid && num2d );
536 m_choiceSecAxisNumbering->Enable( use_set_start_grid && num2d );
537
538 // We can only set an offset if we're setting the start number
539 m_labelGridNumberingOffset->Enable( use_set_start_grid );
540 m_entryGridPriNumberingOffset->Enable( use_set_start_grid );
541 m_entryGridSecNumberingOffset->Enable( use_set_start_grid && num2d );
542
543 // disable the circular number offset in the same way
544 const bool use_set_start_circ = m_rbCircStartNumberingOpt->GetSelection() == 1;
545 m_entryCircNumberingStart->Enable( use_set_start_circ );
546 }
547 else
548 {
549 // grid
550 m_rbGridStartNumberingOpt->Enable( false );
551 m_radioBoxGridNumberingScheme->Enable( false );
552
553 m_labelPriAxisNumbering->Enable( false );
554 m_labelSecAxisNumbering->Enable( false );
555
556 m_choiceSecAxisNumbering->Enable( false );
557 m_choicePriAxisNumbering->Enable( false );
558
559 m_labelGridNumberingOffset->Enable( false );
560 m_entryGridPriNumberingOffset->Enable( false );
561 m_entryGridSecNumberingOffset->Enable( false );
562
563 m_gridPadNumberingPanel->Show( false );
564
565 // circular
566 m_rbCircStartNumberingOpt->Enable( false );
567 m_entryCircNumberingStart->Enable( false );
568
569 m_circularPadNumberingPanel->Show( false );
570
571 m_footprintReannotatePanel->Show( true );
572 }
573}
574
575
577{
578 if( m_radioBtnSetByPos->GetValue() )
579 {
581
582 // Find the radius, etc of the circle
583 centre -= m_originalItemPosition;
584 EDA_ANGLE angle( centre );
585
586 m_circRadius.SetValue( int( centre.EuclideanNorm() ) );
588
591 }
592 else
593 {
596
597 double radius = m_circRadius.GetValue();
599
600 m_hCentre.SetValue( m_originalItemPosition.x + radius * angle.Cos() );
601 m_vCentre.SetValue( m_originalItemPosition.y + radius * angle.Sin() );
602 }
603}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
Class that contains information about a single array axis and the numbering of items along that axis.
Definition: array_axis.h:40
bool SetOffset(const wxString &aOffsetName)
Set the axis start (as a string, which should decode to a valid index in the alphabet)
Definition: array_axis.cpp:102
const wxString & GetAlphabet() const
Get the alphabet for the current numbering scheme.
Definition: array_axis.cpp:43
void SetAxisType(NUMBERING_TYPE aType)
Set the axis numbering type.
Definition: array_axis.cpp:96
@ NUMBERING_NUMERIC
Arabic numerals: 0,1,2,3,4,5,6,7,8,9,10,11...
Definition: array_axis.h:44
@ NUMBERING_HEX
Definition: array_axis.h:45
@ NUMBERING_ALPHA_NO_IOSQXZ
Definition: array_axis.h:46
@ NUMBERING_ALPHA_FULL
Full 26-character alphabet.
Definition: array_axis.h:52
void SetStep(int aStep)
Set the skip between consecutive numbers (useful when doing a partial array, e.g.
Definition: array_axis.cpp:127
Class DIALOG_CREATE_ARRAY_BASE.
WIDGET_SAVE_RESTORE m_cfg_persister
DIALOG_CREATE_ARRAY(PCB_BASE_FRAME *aParent, std::unique_ptr< ARRAY_OPTIONS > &aOptions, bool enableNumbering, const VECTOR2I &aOrigPos)
Construct a new dialog.
void OnButtonRadius(wxCommandEvent &event) override
bool TransferDataFromWindow() override
void OnButtonPosition(wxCommandEvent &event) override
void OnParameterChanged(wxCommandEvent &event) override
void OnRadiusChanged(wxCommandEvent &event) override
const VECTOR2I m_originalItemPosition
std::unique_ptr< ARRAY_OPTIONS > & m_settings
The settings to re-seat on dialog OK.
void SetupStandardButtons(std::map< int, wxString > aLabels={})
double Sin() const
Definition: eda_angle.h:206
EDA_ANGLE Round(int digits) const
Definition: eda_angle.h:312
double Cos() const
Definition: eda_angle.h:221
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
virtual long long int GetValue()
Return the current value in Internal Units.
virtual void SetUnits(EDA_UNITS aUnits)
Normally not needed (as the UNIT_BINDER inherits from the parent frame), but can be used to set to DE...
virtual EDA_ANGLE GetAngleValue()
virtual void SetAngleValue(const EDA_ANGLE &aValue)
virtual void SetValue(long long int aValue)
Set new value (in Internal Units) for the text field, taking care of units conversion.
void SetCoordType(ORIGIN_TRANSFORMS::COORD_TYPES_T aCoordType)
Set the current origin transform mode.
Definition: unit_binder.h:189
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:265
void Add(wxRadioBox &ctrl, long &dest)
Bind a radiobox to a choice.
void RestoreConfigToControls()
Restore the values from the internally-stored references to the underlying data to each bound control...
void ReadConfigFromControls()
Read values of all bound controls into the internally-stored references to the underlying data.
static const std::vector< NUMBERING_LIST_DATA > numberingTypeData
List of type <--> name mappings (in order) for the numbering type list boxes.
static bool validateAxisOptions(const wxTextCtrl &offsetEntry, const wxChoice &typeEntry, const wxTextCtrl &aStepEntry, ARRAY_AXIS &aAxis, wxArrayString &errors)
Validates and saves (if valid) the type and offset of an array axis numbering.
static bool validateLongEntry(const wxTextEntry &entry, long &dest, const wxString &description, wxArrayString &errors)
Validate and save a long integer entry.
static CREATE_ARRAY_DIALOG_ENTRIES s_arrayOptions
#define _(s)
@ DEGREES_T
Definition: eda_angle.h:31
KICOMMON_API double DoubleValueFromString(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Function DoubleValueFromString converts aTextValue to a double.
Definition: eda_units.cpp:565
Struct containing the last-entered values for the dialog.
CREATE_ARRAY_DIALOG_ENTRIES()
Construct with some sensible defaults.
Local mapping for list-box <-> numbering type.
ARRAY_AXIS::NUMBERING_TYPE m_numbering_type