From ac3b56a5601492d0bd50097d0de5bc476c808a1c Mon Sep 17 00:00:00 2001 From: John Beard Date: Sun, 4 May 2025 21:13:29 +0800 Subject: [PATCH] Pcbnew: circular array tool: add angle offset and direction options --- common/array_options.cpp | 5 + include/array_options.h | 4 + pcbnew/dialogs/dialog_create_array.cpp | 9 + pcbnew/dialogs/dialog_create_array.h | 2 +- pcbnew/dialogs/dialog_create_array_base.cpp | 30 ++ pcbnew/dialogs/dialog_create_array_base.fbp | 348 +++++++++++++++++++- pcbnew/dialogs/dialog_create_array_base.h | 5 + 7 files changed, 394 insertions(+), 9 deletions(-) diff --git a/common/array_options.cpp b/common/array_options.cpp index fcf34585c7..abdc894917 100644 --- a/common/array_options.cpp +++ b/common/array_options.cpp @@ -133,6 +133,11 @@ ARRAY_OPTIONS::TRANSFORM ARRAY_CIRCULAR_OPTIONS::GetTransform( int n, const VECT // n'th step angle = EDA_ANGLE( m_angle.AsDegrees() * n, DEGREES_T ); + angle += m_angleOffset; + + if( m_clockwise ) + angle = -angle; + VECTOR2I new_pos = aPos; RotatePoint( new_pos, m_centre, angle ); diff --git a/include/array_options.h b/include/array_options.h index 720dcec890..3a18137858 100644 --- a/include/array_options.h +++ b/include/array_options.h @@ -203,6 +203,8 @@ struct KICOMMON_API ARRAY_CIRCULAR_OPTIONS : public ARRAY_OPTIONS : ARRAY_OPTIONS( ARRAY_CIRCULAR ), m_nPts( 0 ), m_angle( ANGLE_0 ), + m_angleOffset( ANGLE_0 ), + m_clockwise( false ), m_rotateItems( false ) { } @@ -212,6 +214,8 @@ struct KICOMMON_API ARRAY_CIRCULAR_OPTIONS : public ARRAY_OPTIONS /// angle between points, or 0 for each point separated by this value (decideg) EDA_ANGLE m_angle; + EDA_ANGLE m_angleOffset; + bool m_clockwise; VECTOR2I m_centre; bool m_rotateItems; ARRAY_AXIS m_axis; diff --git a/pcbnew/dialogs/dialog_create_array.cpp b/pcbnew/dialogs/dialog_create_array.cpp index f2b6097a61..2d8884cfca 100644 --- a/pcbnew/dialogs/dialog_create_array.cpp +++ b/pcbnew/dialogs/dialog_create_array.cpp @@ -72,6 +72,8 @@ struct CREATE_ARRAY_DIALOG_ENTRIES long m_CircCentreX = 0; long m_CircCentreY = 0; EDA_ANGLE m_CircAngle = ANGLE_90; + long m_CircleDirection = 0; // clockwise + EDA_ANGLE m_CircOffsetAngle = ANGLE_0; long m_CircCount = 4; bool m_CircFullCircle = 0; long m_CircNumStartSet = 1; // use specified start @@ -135,6 +137,7 @@ DIALOG_CREATE_ARRAY::DIALOG_CREATE_ARRAY( PCB_BASE_FRAME* aParen m_hCentre( aParent, m_labelCentreX, m_entryCentreX, m_unitLabelCentreX ), m_vCentre( aParent, m_labelCentreY, m_entryCentreY, m_unitLabelCentreY ), m_circAngle( aParent, m_labelCircAngle, m_entryCircAngle, m_unitLabelCircAngle ), + m_circOffset( aParent, m_labelCircOffset, m_entryCircOffset, m_unitLabelCircOffset ), m_cfg_persister( pcbIUScale, s_arrayOptions.m_OptionsSet ) { // Configure display origin transforms @@ -161,6 +164,7 @@ DIALOG_CREATE_ARRAY::DIALOG_CREATE_ARRAY( PCB_BASE_FRAME* aParen m_choiceCircNumbering->SetSelection( 0 ); m_circAngle.SetUnits( EDA_UNITS::DEGREES ); + m_circOffset.SetUnits( EDA_UNITS::DEGREES ); // bind grid options to persister m_cfg_persister.Add( *m_entryNx, s_arrayOptions.m_GridNx ); @@ -196,6 +200,8 @@ DIALOG_CREATE_ARRAY::DIALOG_CREATE_ARRAY( PCB_BASE_FRAME* aParen m_cfg_persister.Add( *m_checkBoxFullCircle, s_arrayOptions.m_CircFullCircle ); m_cfg_persister.Add( m_circAngle, s_arrayOptions.m_CircAngle ); + m_cfg_persister.Add( m_circOffset, s_arrayOptions.m_CircOffsetAngle ); + m_cfg_persister.Add( *m_rbCircDirection, s_arrayOptions.m_CircleDirection ); m_cfg_persister.Add( *m_entryCircCount, s_arrayOptions.m_CircCount ); m_cfg_persister.Add( *m_entryRotateItemsCb, s_arrayOptions.m_CircRotatationStep ); @@ -479,10 +485,13 @@ bool DIALOG_CREATE_ARRAY::TransferDataFromWindow() auto newCirc = std::make_unique(); bool ok = true; double angle = EDA_UNIT_UTILS::UI::DoubleValueFromString( m_entryCircAngle->GetValue() ); + double offset = EDA_UNIT_UTILS::UI::DoubleValueFromString( m_entryCircOffset->GetValue() ); newCirc->m_centre.x = m_hCentre.GetIntValue(); newCirc->m_centre.y = m_vCentre.GetIntValue(); newCirc->m_angle = EDA_ANGLE( angle, DEGREES_T ); + newCirc->m_angleOffset = EDA_ANGLE( offset, DEGREES_T ); + newCirc->m_clockwise = m_rbCircDirection->GetSelection() == 0; ok = validateLongEntry(*m_entryCircCount, newCirc->m_nPts, _("point count"), errors); diff --git a/pcbnew/dialogs/dialog_create_array.h b/pcbnew/dialogs/dialog_create_array.h index ecb0dea093..48a20efb64 100644 --- a/pcbnew/dialogs/dialog_create_array.h +++ b/pcbnew/dialogs/dialog_create_array.h @@ -90,7 +90,7 @@ private: UNIT_BINDER m_hSpacing, m_vSpacing; UNIT_BINDER m_hOffset, m_vOffset; UNIT_BINDER m_hCentre, m_vCentre; - UNIT_BINDER m_circAngle; + UNIT_BINDER m_circAngle, m_circOffset; WIDGET_SAVE_RESTORE m_cfg_persister; }; diff --git a/pcbnew/dialogs/dialog_create_array_base.cpp b/pcbnew/dialogs/dialog_create_array_base.cpp index 2197e578eb..898f8fe1bc 100644 --- a/pcbnew/dialogs/dialog_create_array_base.cpp +++ b/pcbnew/dialogs/dialog_create_array_base.cpp @@ -357,6 +357,18 @@ DIALOG_CREATE_ARRAY_BASE::DIALOG_CREATE_ARRAY_BASE( wxWindow* parent, wxWindowID m_checkBoxFullCircle->SetValue(true); sbSizerDupPrms->Add( m_checkBoxFullCircle, 0, wxALL, 5 ); + wxString m_rbCircDirectionChoices[] = { _("Clockwise"), _("Anti-clockwise") }; + int m_rbCircDirectionNChoices = sizeof( m_rbCircDirectionChoices ) / sizeof( wxString ); + m_rbCircDirection = new wxRadioBox( sbSizerDupPrms->GetStaticBox(), wxID_ANY, _("Direction"), wxDefaultPosition, wxDefaultSize, m_rbCircDirectionNChoices, m_rbCircDirectionChoices, 1, wxRA_SPECIFY_COLS ); + m_rbCircDirection->SetSelection( 0 ); + sbSizerDupPrms->Add( m_rbCircDirection, 0, wxEXPAND|wxLEFT|wxRIGHT, 5 ); + + m_entryRotateItemsCb1 = new wxCheckBox( sbSizerDupPrms->GetStaticBox(), wxID_ANY, _("Rotate items"), wxDefaultPosition, wxDefaultSize, 0 ); + m_entryRotateItemsCb1->SetValue(true); + m_entryRotateItemsCb1->SetToolTip( _("Rotate the item as well as move it - multi-selections will be rotated together") ); + + sbSizerDupPrms->Add( m_entryRotateItemsCb1, 0, wxALL, 5 ); + wxFlexGridSizer* fgSizerDupPrms; fgSizerDupPrms = new wxFlexGridSizer( 0, 3, 5, 5 ); fgSizerDupPrms->AddGrowableCol( 1 ); @@ -386,6 +398,22 @@ DIALOG_CREATE_ARRAY_BASE::DIALOG_CREATE_ARRAY_BASE( wxWindow* parent, wxWindowID fgSizerDupPrms->Add( m_entryCircCount, 0, wxEXPAND, 5 ); + fgSizerDupPrms->Add( 0, 0, 1, wxEXPAND, 5 ); + + m_labelCircOffset = new wxStaticText( sbSizerDupPrms->GetStaticBox(), wxID_ANY, _("First item angle:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_labelCircOffset->Wrap( -1 ); + fgSizerDupPrms->Add( m_labelCircOffset, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 ); + + m_entryCircOffset = new TEXT_CTRL_EVAL( sbSizerDupPrms->GetStaticBox(), wxID_ANY, _("0"), wxDefaultPosition, wxDefaultSize, 0 ); + m_entryCircOffset->SetToolTip( _("Angle offset of the first item in the array") ); + + fgSizerDupPrms->Add( m_entryCircOffset, 0, wxEXPAND, 5 ); + + m_unitLabelCircOffset = new wxStaticText( sbSizerDupPrms->GetStaticBox(), wxID_ANY, _("deg"), wxDefaultPosition, wxDefaultSize, 0 ); + m_unitLabelCircOffset->Wrap( -1 ); + fgSizerDupPrms->Add( m_unitLabelCircOffset, 0, wxALIGN_CENTER_VERTICAL, 5 ); + + sbSizerDupPrms->Add( fgSizerDupPrms, 0, wxBOTTOM|wxEXPAND, 5 ); m_entryRotateItemsCb = new wxCheckBox( sbSizerDupPrms->GetStaticBox(), wxID_ANY, _("Rotate items"), wxDefaultPosition, wxDefaultSize, 0 ); @@ -552,6 +580,7 @@ DIALOG_CREATE_ARRAY_BASE::DIALOG_CREATE_ARRAY_BASE( wxWindow* parent, wxWindowID m_checkBoxFullCircle->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnParameterChanged ), NULL, this ); m_entryCircAngle->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnParameterChanged ), NULL, this ); m_entryCircCount->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnParameterChanged ), NULL, this ); + m_entryCircOffset->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnParameterChanged ), NULL, this ); m_rbCircStartNumberingOpt->Connect( wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnParameterChanged ), NULL, this ); m_choiceCircNumbering->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnAxisNumberingChange ), NULL, this ); m_radioBtnDuplicateSelection->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnParameterChanged ), NULL, this ); @@ -581,6 +610,7 @@ DIALOG_CREATE_ARRAY_BASE::~DIALOG_CREATE_ARRAY_BASE() m_checkBoxFullCircle->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnParameterChanged ), NULL, this ); m_entryCircAngle->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnParameterChanged ), NULL, this ); m_entryCircCount->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnParameterChanged ), NULL, this ); + m_entryCircOffset->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnParameterChanged ), NULL, this ); m_rbCircStartNumberingOpt->Disconnect( wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnParameterChanged ), NULL, this ); m_choiceCircNumbering->Disconnect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnAxisNumberingChange ), NULL, this ); m_radioBtnDuplicateSelection->Disconnect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnParameterChanged ), NULL, this ); diff --git a/pcbnew/dialogs/dialog_create_array_base.fbp b/pcbnew/dialogs/dialog_create_array_base.fbp index f2986c22ff..9949ea9e92 100644 --- a/pcbnew/dialogs/dialog_create_array_base.fbp +++ b/pcbnew/dialogs/dialog_create_array_base.fbp @@ -2815,7 +2815,7 @@ Circular Array 0 - + 1 1 1 @@ -2867,16 +2867,16 @@ wxTAB_TRAVERSAL - + bSizer4 wxHORIZONTAL none - + 5 wxEXPAND 1 - + bSizerCircLeft wxVERTICAL @@ -3464,11 +3464,11 @@ - + 10 wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT 0 - + wxID_ANY Duplication Settings @@ -3542,11 +3542,143 @@ OnParameterChanged - + + 5 + wxEXPAND|wxLEFT|wxRIGHT + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + "Clockwise" "Anti-clockwise" + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Direction + 1 + + 0 + + + 0 + + 1 + m_rbCircDirection + 1 + + + protected + 1 + + Resizable + 0 + 1 + + wxRA_SPECIFY_COLS + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + Rotate items + + 0 + + + 0 + + 1 + m_entryRotateItemsCb1 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + Rotate the item as well as move it - multi-selections will be rotated together + + wxFILTER_NONE + wxDefaultValidator + + + + + + + 5 wxBOTTOM|wxEXPAND 0 - + 3 wxBOTH 1 @@ -3876,6 +4008,206 @@ OnParameterChanged + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + 5 + wxALIGN_CENTER_VERTICAL|wxLEFT + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + First item angle: + 0 + + 0 + + + 0 + + 1 + m_labelCircOffset + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + 5 + wxEXPAND + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + 0 + + 0 + + 1 + m_entryCircOffset + 1 + + + protected + 1 + + Resizable + 1 + + + TEXT_CTRL_EVAL; widgets/text_ctrl_eval.h + 0 + Angle offset of the first item in the array + + wxFILTER_NONE + wxDefaultValidator + + 0 + + + + OnParameterChanged + + + + 5 + wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + 0 + + 0 + 0 + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + 1 + + 1 + + 0 + 0 + wxID_ANY + deg + 0 + + 0 + + + 0 + + 1 + m_unitLabelCircOffset + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + diff --git a/pcbnew/dialogs/dialog_create_array_base.h b/pcbnew/dialogs/dialog_create_array_base.h index 09aafb1147..316249883f 100644 --- a/pcbnew/dialogs/dialog_create_array_base.h +++ b/pcbnew/dialogs/dialog_create_array_base.h @@ -97,11 +97,16 @@ class DIALOG_CREATE_ARRAY_BASE : public DIALOG_SHIM wxButton* m_btnSelectCenterPoint; wxButton* m_btnSelectCenterItem; wxCheckBox* m_checkBoxFullCircle; + wxRadioBox* m_rbCircDirection; + wxCheckBox* m_entryRotateItemsCb1; wxStaticText* m_labelCircAngle; wxTextCtrl* m_entryCircAngle; wxStaticText* m_unitLabelCircAngle; wxStaticText* m_labelCircCount; TEXT_CTRL_EVAL* m_entryCircCount; + wxStaticText* m_labelCircOffset; + TEXT_CTRL_EVAL* m_entryCircOffset; + wxStaticText* m_unitLabelCircOffset; wxCheckBox* m_entryRotateItemsCb; wxPanel* m_circularPadNumberingPanel; wxStaticBoxSizer* m_circPadNumberingSizer;