7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2025-04-21 21:43:43 +00:00

Pcbnew array: allow selecting the center point interactively

This involves recasting the array tool from the slightly
eccentric ARRAY_CREATOR class into a full-blown TOOL, which
allows it to usefuly store state asd the dialog hides. This
is modelled on the POSITION_RELATIVE_TOOL strategy.

The by-radius-and-point mechanism is removed entirely,
as it's very fiddly and hard to describe in the UI,
and what you probably really want to do is get your
item onto the circle with tools like "move exact",
and then define the circle origin (now using the picker
tool)

Fixes: https://gitlab.com/kicad/code/kicad/-/issues/16783
This commit is contained in:
John Beard 2024-10-28 21:49:09 +08:00
parent 9c0e4a3f66
commit 2391e5ecdc
14 changed files with 602 additions and 1525 deletions

View File

@ -319,7 +319,6 @@ set( PCBNEW_CLASS_SRCS
autorouter/autoplace_tool.cpp
action_plugin.cpp
array_creator.cpp
array_pad_number_provider.cpp
build_BOM_from_board.cpp
cleanup_item.cpp
@ -372,6 +371,7 @@ set( PCBNEW_CLASS_SRCS
ratsnest/ratsnest.cpp
tools/array_tool.cpp
tools/board_editor_control.cpp
tools/board_inspection_tool.cpp
tools/board_reannotate_tool.cpp

View File

@ -1,66 +0,0 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Created on: 11 Mar 2016, author John Beard
* Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef ARRAY_CREATOR_H_
#define ARRAY_CREATOR_H_
#include <dialogs/dialog_create_array.h>
#include <board.h>
#include <footprint.h>
#include <board_item.h>
#include <tools/pcb_selection.h>
class TOOL_MANAGER;
/*!
* Class that performs array creation by producing a dialog to gather parameters and then
* creating and laying out the items.
*/
class ARRAY_CREATOR
{
public:
ARRAY_CREATOR( PCB_BASE_FRAME& aParent, bool aIsFootprintEditor,
const PCB_SELECTION& aSelection, TOOL_MANAGER* aToolManager ) :
m_parent( aParent ),
m_isFootprintEditor( aIsFootprintEditor ),
m_selection( aSelection ),
m_toolMgr( aToolManager )
{}
virtual ~ARRAY_CREATOR() {}
/*!
* Open the dialog, gather parameters and create the array
*/
void Invoke();
private:
PCB_BASE_FRAME& m_parent;
bool m_isFootprintEditor;
const PCB_SELECTION m_selection;
TOOL_MANAGER* m_toolMgr;
};
#endif /* ARRAY_CREATOR_H_ */

View File

@ -27,6 +27,7 @@
#include <array_options.h>
#include <footprint.h>
#include <board_commit.h>
/**
* Simple class that sequentially provides numbers from an #ARRAY_OPTIONS object, making sure

View File

@ -23,13 +23,16 @@
#include "dialogs/dialog_create_array.h"
#include <base_units.h>
#include <widgets/text_ctrl_eval.h>
#include <footprint.h>
#include <pcb_edit_frame.h>
#include <wx/msgdlg.h>
#include <boost/algorithm/string/join.hpp>
#include <base_units.h>
#include <footprint.h>
#include <pcb_edit_frame.h>
#include <tools/pcb_actions.h>
#include <tools/pcb_picker_tool.h>
#include <tool/tool_manager.h>
#include <widgets/text_ctrl_eval.h>
/**
* Struct containing the last-entered values for the dialog.
@ -63,6 +66,7 @@ struct CREATE_ARRAY_DIALOG_ENTRIES
m_GridSecondaryAxisStep( 1 ),
m_CircCentreX( 0 ),
m_CircCentreY( 0 ),
m_CircAngle( ANGLE_90 ),
m_CircCount( 4 ),
m_CircNumStartSet( 1 ), // use specified start
m_GridCircNumScheme( 0 ),
@ -101,6 +105,7 @@ struct CREATE_ARRAY_DIALOG_ENTRIES
long m_CircCentreY;
EDA_ANGLE m_CircAngle;
long m_CircCount;
bool m_CircFullCircle;
long m_CircNumStartSet;
long m_GridCircNumScheme;
wxString m_CircNumberingOffset;
@ -146,23 +151,19 @@ static const std::vector<NUMBERING_LIST_DATA> numberingTypeData {
},
};
DIALOG_CREATE_ARRAY::DIALOG_CREATE_ARRAY( PCB_BASE_FRAME* aParent,
DIALOG_CREATE_ARRAY::DIALOG_CREATE_ARRAY( PCB_BASE_FRAME* aParent,
std::unique_ptr<ARRAY_OPTIONS>& aSettings,
bool aIsFootprintEditor, const VECTOR2I& aOrigPos ) :
DIALOG_CREATE_ARRAY_BASE( aParent ),
m_frame( aParent ),
m_settings( aSettings ),
m_originalItemPosition( aOrigPos ),
m_isFootprintEditor( aIsFootprintEditor ),
m_originalItemPosition( aOrigPos ), m_isFootprintEditor( aIsFootprintEditor ),
m_hSpacing( aParent, m_labelDx, m_entryDx, m_unitLabelDx ),
m_vSpacing( aParent, m_labelDy, m_entryDy, m_unitLabelDy ),
m_hOffset( aParent, m_labelOffsetX, m_entryOffsetX, m_unitLabelOffsetX ),
m_vOffset( aParent, m_labelOffsetY, m_entryOffsetY, m_unitLabelOffsetY ),
m_refPosX( aParent, m_stRefPosXTxt, m_tcRefPosX, m_stRefPosXUnit ),
m_refPosY( aParent, m_stRefPosYTxt, m_tcRefPosY, m_stRefPosYUnit ),
m_hCentre( aParent, m_labelCentreX, m_entryCentreX, m_unitLabelCentreX ),
m_vCentre( aParent, m_labelCentreY, m_entryCentreY, m_unitLabelCentreY ),
m_circRadius( aParent, m_labelCircRadius, m_tcValueCircRadius, m_unitLabelCircRadius ),
m_circCenterAngle( aParent, m_labelCircCenterAngle, m_tcValueCircCenterAngle, m_unitLabelCircCenterAngle ),
m_circAngle( aParent, m_labelCircAngle, m_entryCircAngle, m_unitLabelCircAngle ),
m_cfg_persister( pcbIUScale, s_arrayOptions.m_OptionsSet )
{
@ -189,7 +190,6 @@ DIALOG_CREATE_ARRAY::DIALOG_CREATE_ARRAY( PCB_BASE_FRAME* aParent,
m_choiceSecAxisNumbering->SetSelection( 0 );
m_choiceCircNumbering->SetSelection( 0 );
m_circCenterAngle.SetUnits( EDA_UNITS::DEGREES );
m_circAngle.SetUnits( EDA_UNITS::DEGREES );
// bind grid options to persister
@ -222,6 +222,8 @@ DIALOG_CREATE_ARRAY::DIALOG_CREATE_ARRAY( PCB_BASE_FRAME* aParent,
// bind circular options to persister
m_cfg_persister.Add( m_hCentre, s_arrayOptions.m_CircCentreX );
m_cfg_persister.Add( m_vCentre, s_arrayOptions.m_CircCentreY );
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_entryCircCount, s_arrayOptions.m_CircCount );
m_cfg_persister.Add( *m_entryRotateItemsCb, s_arrayOptions.m_CircRotatationStep );
@ -240,7 +242,6 @@ DIALOG_CREATE_ARRAY::DIALOG_CREATE_ARRAY( PCB_BASE_FRAME* aParent,
// Run the callbacks once to process the dialog contents
setControlEnablement();
setCircularArrayEnablement();
calculateCircularArrayProperties();
SetupStandardButtons();
@ -249,57 +250,74 @@ DIALOG_CREATE_ARRAY::DIALOG_CREATE_ARRAY( PCB_BASE_FRAME* aParent,
}
void DIALOG_CREATE_ARRAY::OnButtonPosition( wxCommandEvent& event )
DIALOG_CREATE_ARRAY::~DIALOG_CREATE_ARRAY()
{
setCircularArrayEnablement();
}
void DIALOG_CREATE_ARRAY::OnButtonRadius( wxCommandEvent& event )
{
setCircularArrayEnablement();
}
void DIALOG_CREATE_ARRAY::setCircularArrayEnablement()
{
if( m_radioBtnSetByRadius->GetValue() )
{
m_entryCentreX->Disable();
m_entryCentreY->Disable();
m_tcValueCircRadius->Enable();
m_tcValueCircCenterAngle->Enable();
}
else
{
m_entryCentreX->Enable();
m_entryCentreY->Enable();
m_tcValueCircRadius->Disable();
m_tcValueCircCenterAngle->Disable();
}
}
void DIALOG_CREATE_ARRAY::OnParameterChanged( wxCommandEvent& event )
{
setCircularArrayEnablement();
if( m_radioBtnSetByPos->GetValue() )
if( m_checkBoxFullCircle->GetValue() && m_entryCircAngle == event.GetEventObject() )
{
setControlEnablement();
calculateCircularArrayProperties();
return;
}
setControlEnablement();
calculateCircularArrayProperties();
}
void DIALOG_CREATE_ARRAY::OnRadiusChanged( wxCommandEvent& event )
{
setCircularArrayEnablement();
if( m_radioBtnSetByRadius->GetValue() )
void DIALOG_CREATE_ARRAY::OnSelectCenterButton( wxCommandEvent& event )
{
event.Skip();
PCB_PICKER_TOOL* pickerTool = m_frame->GetToolManager()->GetTool<PCB_PICKER_TOOL>();
wxCHECK( pickerTool, /* void */ );
if( event.GetEventObject() == m_btnSelectCenterItem )
{
setControlEnablement();
calculateCircularArrayProperties();
m_frame->GetToolManager()->RunAction(
PCB_ACTIONS::selectItemInteractively,
PCB_PICKER_TOOL::INTERACTIVE_PARAMS{ this, _( "Select center item..." ) } );
}
else if( event.GetEventObject() == m_btnSelectCenterPoint )
{
m_frame->GetToolManager()->RunAction(
PCB_ACTIONS::selectPointInteractively,
PCB_PICKER_TOOL::INTERACTIVE_PARAMS{ this, _( "Select center point..." ) } );
}
else
{
wxFAIL_MSG( "Unknown event source" );
}
// Hide, but do not close, the dialog
Hide();
}
// Implement the RECEIVER interface for the callback from the TOOL
void DIALOG_CREATE_ARRAY::UpdatePickedItem( const EDA_ITEM* aItem )
{
if( aItem )
{
m_hCentre.SetValue( aItem->GetPosition().x );
m_vCentre.SetValue( aItem->GetPosition().y );
}
Show( true );
}
void DIALOG_CREATE_ARRAY::UpdatePickedPoint( const std::optional<VECTOR2I>& aPoint )
{
if( aPoint )
{
m_hCentre.SetValue( aPoint->x );
m_vCentre.SetValue( aPoint->y );
}
Show( true );
}
@ -375,6 +393,7 @@ static bool validateAxisOptions( const wxTextCtrl& offsetEntry, const wxChoice&
bool DIALOG_CREATE_ARRAY::TransferDataFromWindow()
{
std::cout << "DIALOG_CREATE_ARRAY::TransferDataFromWindow()" << std::endl;
std::unique_ptr<ARRAY_OPTIONS> newSettings;
wxArrayString errors;
@ -480,6 +499,8 @@ bool DIALOG_CREATE_ARRAY::TransferDataFromWindow()
newSettings = std::move( newCirc );
}
bool ret = false;
// If we got good settings, send them out and finish
if( newSettings )
{
@ -491,7 +512,7 @@ bool DIALOG_CREATE_ARRAY::TransferDataFromWindow()
// persist the control state for next time
m_cfg_persister.ReadConfigFromControls();
return true;
ret = true;
}
else
{
@ -500,16 +521,29 @@ bool DIALOG_CREATE_ARRAY::TransferDataFromWindow()
if( errors.IsEmpty() )
errorStr = _("Bad parameters");
else
errorStr = boost::algorithm::join( errors, wxT( "\n" ) );
errorStr = wxJoin( errors, '\n' );
wxMessageBox( errorStr );
return false;
ret = false;
}
// This dialog is not modal, so close it now
Close();
return ret;
}
void DIALOG_CREATE_ARRAY::setControlEnablement()
{
if( m_checkBoxFullCircle->GetValue() )
{
m_entryCircAngle->Disable();
}
else
{
m_entryCircAngle->Enable();
}
if( m_isFootprintEditor )
{
m_footprintReannotatePanel->Show( false );
@ -573,29 +607,14 @@ void DIALOG_CREATE_ARRAY::setControlEnablement()
void DIALOG_CREATE_ARRAY::calculateCircularArrayProperties()
{
if( m_radioBtnSetByPos->GetValue() )
// In full circle mode, the division angle is computed from the number of points
if( m_checkBoxFullCircle->GetValue() )
{
VECTOR2I centre( m_hCentre.GetIntValue(), m_vCentre.GetIntValue() );
// Find the radius, etc of the circle
centre -= m_originalItemPosition;
EDA_ANGLE angle( centre );
m_circRadius.SetValue( int( centre.EuclideanNorm() ) );
m_circCenterAngle.SetAngleValue( angle.Round( 4 ) );
m_refPosX.SetValue( m_originalItemPosition.x );
m_refPosY.SetValue( m_originalItemPosition.y );
}
else
{
m_refPosX.SetValue( m_originalItemPosition.x );
m_refPosY.SetValue( m_originalItemPosition.y );
double radius = m_circRadius.GetIntValue();
EDA_ANGLE angle = m_circCenterAngle.GetAngleValue();
m_hCentre.SetValue( m_originalItemPosition.x + radius * angle.Cos() );
m_vCentre.SetValue( m_originalItemPosition.y + radius * angle.Sin() );
long nPts;
if( m_entryCircCount->GetValue().ToLong( &nPts ) )
{
EDA_ANGLE division = EDA_ANGLE( 360, DEGREES_T ) / nPts;
m_circAngle.SetAngleValue( division );
}
}
}

View File

@ -22,22 +22,22 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef DIALOG_CREATE_ARRAY__H_
#define DIALOG_CREATE_ARRAY__H_
#pragma once
// Include the wxFormBuider header base:
#include <dialog_create_array_base.h>
#include <memory>
#include <array_options.h>
#include <board_item.h>
#include <pcb_base_frame.h>
#include <tools/pcb_picker_tool.h>
#include <widgets/unit_binder.h>
#include <widgets/widget_save_restore.h>
#include <memory>
class DIALOG_CREATE_ARRAY : public DIALOG_CREATE_ARRAY_BASE
class DIALOG_CREATE_ARRAY : public DIALOG_CREATE_ARRAY_BASE, public PCB_PICKER_TOOL::RECEIVER
{
public:
/**
@ -50,22 +50,27 @@ public:
*/
DIALOG_CREATE_ARRAY( PCB_BASE_FRAME* aParent, std::unique_ptr<ARRAY_OPTIONS>& aOptions,
bool enableNumbering, const VECTOR2I& aOrigPos );
~DIALOG_CREATE_ARRAY();
// Implement the RECEIVER interface for the callback from the TOOL
void UpdatePickedItem( const EDA_ITEM* aItem ) override;
void UpdatePickedPoint( const std::optional<VECTOR2I>& aPoint ) override;
private:
// Event callbacks
void OnButtonPosition( wxCommandEvent& event ) override;
void OnButtonRadius( wxCommandEvent& event ) override;
void OnParameterChanged( wxCommandEvent& event ) override;
void OnRadiusChanged( wxCommandEvent& event ) override;
// Center select buttons
void OnSelectCenterButton( wxCommandEvent& event ) override;
// Internal callback handlers
void setControlEnablement();
void setCircularArrayEnablement();
void calculateCircularArrayProperties();
bool TransferDataFromWindow() override;
PCB_BASE_FRAME* m_frame;
/**
* The settings to re-seat on dialog OK.
*/
@ -81,13 +86,8 @@ private:
UNIT_BINDER m_hSpacing, m_vSpacing;
UNIT_BINDER m_hOffset, m_vOffset;
UNIT_BINDER m_refPosX, m_refPosY;
UNIT_BINDER m_hCentre, m_vCentre;
UNIT_BINDER m_circRadius;
UNIT_BINDER m_circCenterAngle;
UNIT_BINDER m_circAngle;
WIDGET_SAVE_RESTORE m_cfg_persister;
};
#endif // DIALOG_CREATE_ARRAY__H_

View File

@ -287,7 +287,7 @@ DIALOG_CREATE_ARRAY_BASE::DIALOG_CREATE_ARRAY_BASE( wxWindow* parent, wxWindowID
m_gridPanel->SetSizer( bSizerGridArray );
m_gridPanel->Layout();
bSizerGridArray->Fit( m_gridPanel );
m_gridTypeNotebook->AddPage( m_gridPanel, _("Grid Array"), true );
m_gridTypeNotebook->AddPage( m_gridPanel, _("Grid Array"), false );
m_circularPanel = new wxPanel( m_gridTypeNotebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxBoxSizer* bSizer4;
bSizer4 = new wxBoxSizer( wxHORIZONTAL );
@ -296,52 +296,7 @@ DIALOG_CREATE_ARRAY_BASE::DIALOG_CREATE_ARRAY_BASE( wxWindow* parent, wxWindowID
bSizerCircLeft = new wxBoxSizer( wxVERTICAL );
wxStaticBoxSizer* sbSizerInfo;
sbSizerInfo = new wxStaticBoxSizer( new wxStaticBox( m_circularPanel, wxID_ANY, _("Reference Position") ), wxVERTICAL );
m_stInfoItems = new wxStaticText( sbSizerInfo->GetStaticBox(), wxID_ANY, _("Position of the selected item (or group) to duplicate"), wxDefaultPosition, wxDefaultSize, 0 );
m_stInfoItems->Wrap( -1 );
sbSizerInfo->Add( m_stInfoItems, 0, wxBOTTOM|wxRIGHT|wxLEFT, 5 );
wxFlexGridSizer* fgSizer4;
fgSizer4 = new wxFlexGridSizer( 0, 3, 5, 5 );
fgSizer4->AddGrowableCol( 1 );
fgSizer4->SetFlexibleDirection( wxBOTH );
fgSizer4->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_stRefPosXTxt = new wxStaticText( sbSizerInfo->GetStaticBox(), wxID_ANY, _("Ref point pos X:"), wxDefaultPosition, wxDefaultSize, 0 );
m_stRefPosXTxt->Wrap( -1 );
fgSizer4->Add( m_stRefPosXTxt, 0, wxALIGN_CENTER_VERTICAL, 5 );
m_tcRefPosX = new wxTextCtrl( sbSizerInfo->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY );
fgSizer4->Add( m_tcRefPosX, 0, wxEXPAND, 5 );
m_stRefPosXUnit = new wxStaticText( sbSizerInfo->GetStaticBox(), wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 );
m_stRefPosXUnit->Wrap( -1 );
fgSizer4->Add( m_stRefPosXUnit, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
m_stRefPosYTxt = new wxStaticText( sbSizerInfo->GetStaticBox(), wxID_ANY, _("Ref point pos Y:"), wxDefaultPosition, wxDefaultSize, 0 );
m_stRefPosYTxt->Wrap( -1 );
fgSizer4->Add( m_stRefPosYTxt, 0, wxALIGN_CENTER_VERTICAL, 5 );
m_tcRefPosY = new wxTextCtrl( sbSizerInfo->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY );
fgSizer4->Add( m_tcRefPosY, 0, wxEXPAND, 5 );
m_stRefPosYUnit = new wxStaticText( sbSizerInfo->GetStaticBox(), wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 );
m_stRefPosYUnit->Wrap( -1 );
fgSizer4->Add( m_stRefPosYUnit, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
sbSizerInfo->Add( fgSizer4, 0, wxEXPAND|wxTOP|wxBOTTOM|wxLEFT, 5 );
bSizerCircLeft->Add( sbSizerInfo, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 10 );
wxStaticBoxSizer* sbSizerCircParams;
sbSizerCircParams = new wxStaticBoxSizer( new wxStaticBox( m_circularPanel, wxID_ANY, _("Array Settings") ), wxVERTICAL );
m_radioBtnSetByPos = new wxRadioButton( sbSizerCircParams->GetStaticBox(), wxID_ANY, _("Set center by position"), wxDefaultPosition, wxDefaultSize, 0 );
m_radioBtnSetByPos->SetValue( true );
sbSizerCircParams->Add( m_radioBtnSetByPos, 0, wxALL, 5 );
sbSizerInfo = new wxStaticBoxSizer( new wxStaticBox( m_circularPanel, wxID_ANY, _("Center position") ), wxVERTICAL );
wxFlexGridSizer* fgSizerArrayPrms;
fgSizerArrayPrms = new wxFlexGridSizer( 0, 3, 5, 5 );
@ -349,109 +304,67 @@ DIALOG_CREATE_ARRAY_BASE::DIALOG_CREATE_ARRAY_BASE( wxWindow* parent, wxWindowID
fgSizerArrayPrms->SetFlexibleDirection( wxBOTH );
fgSizerArrayPrms->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_labelCentreX = new wxStaticText( sbSizerCircParams->GetStaticBox(), wxID_ANY, _("Center pos X:"), wxDefaultPosition, wxDefaultSize, 0 );
m_labelCentreX = new wxStaticText( sbSizerInfo->GetStaticBox(), wxID_ANY, _("Center pos X:"), wxDefaultPosition, wxDefaultSize, 0 );
m_labelCentreX->Wrap( -1 );
fgSizerArrayPrms->Add( m_labelCentreX, 0, wxALIGN_CENTER_VERTICAL, 5 );
m_entryCentreX = new wxTextCtrl( sbSizerCircParams->GetStaticBox(), wxID_ANY, _("0"), wxDefaultPosition, wxDefaultSize, 0 );
fgSizerArrayPrms->Add( m_entryCentreX, 0, wxEXPAND, 5 );
m_entryCentreX = new wxTextCtrl( sbSizerInfo->GetStaticBox(), wxID_ANY, _("0"), wxDefaultPosition, wxDefaultSize, 0 );
fgSizerArrayPrms->Add( m_entryCentreX, 0, wxEXPAND|wxTOP, 5 );
m_unitLabelCentreX = new wxStaticText( sbSizerCircParams->GetStaticBox(), wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 );
m_unitLabelCentreX = new wxStaticText( sbSizerInfo->GetStaticBox(), wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 );
m_unitLabelCentreX->Wrap( -1 );
fgSizerArrayPrms->Add( m_unitLabelCentreX, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
m_labelCentreY = new wxStaticText( sbSizerCircParams->GetStaticBox(), wxID_ANY, _("Center pos Y:"), wxDefaultPosition, wxDefaultSize, 0 );
m_labelCentreY = new wxStaticText( sbSizerInfo->GetStaticBox(), wxID_ANY, _("Center pos Y:"), wxDefaultPosition, wxDefaultSize, 0 );
m_labelCentreY->Wrap( -1 );
fgSizerArrayPrms->Add( m_labelCentreY, 0, wxALIGN_CENTER_VERTICAL, 5 );
m_entryCentreY = new wxTextCtrl( sbSizerCircParams->GetStaticBox(), wxID_ANY, _("0"), wxDefaultPosition, wxDefaultSize, 0 );
m_entryCentreY = new wxTextCtrl( sbSizerInfo->GetStaticBox(), wxID_ANY, _("0"), wxDefaultPosition, wxDefaultSize, 0 );
fgSizerArrayPrms->Add( m_entryCentreY, 0, wxEXPAND, 5 );
m_unitLabelCentreY = new wxStaticText( sbSizerCircParams->GetStaticBox(), wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 );
m_unitLabelCentreY = new wxStaticText( sbSizerInfo->GetStaticBox(), wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 );
m_unitLabelCentreY->Wrap( -1 );
fgSizerArrayPrms->Add( m_unitLabelCentreY, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
sbSizerCircParams->Add( fgSizerArrayPrms, 0, wxEXPAND|wxLEFT, 25 );
fgSizerArrayPrms->Add( 0, 0, 1, wxEXPAND, 5 );
sbSizerCircParams->Add( 0, 15, 1, wxEXPAND, 5 );
sbSizerInfo->Add( fgSizerArrayPrms, 0, wxEXPAND|wxLEFT, 25 );
m_radioBtnSetByRadius = new wxRadioButton( sbSizerCircParams->GetStaticBox(), wxID_ANY, _("Set center by radius"), wxDefaultPosition, wxDefaultSize, 0 );
sbSizerCircParams->Add( m_radioBtnSetByRadius, 0, wxALL, 5 );
wxBoxSizer* bSizer12;
bSizer12 = new wxBoxSizer( wxHORIZONTAL );
wxBoxSizer* bSizerRadius;
bSizerRadius = new wxBoxSizer( wxVERTICAL );
m_btnSelectCenterPoint = new wxButton( sbSizerInfo->GetStaticBox(), wxID_ANY, _("Select Point..."), wxDefaultPosition, wxDefaultSize, 0 );
bSizer12->Add( m_btnSelectCenterPoint, 1, wxALL, 5 );
m_labelCircRadius = new wxStaticText( sbSizerCircParams->GetStaticBox(), wxID_ANY, _("Radius of circular array:"), wxDefaultPosition, wxDefaultSize, 0 );
m_labelCircRadius->Wrap( -1 );
bSizerRadius->Add( m_labelCircRadius, 0, wxTOP|wxRIGHT, 5 );
wxBoxSizer* bSizerRadiusValue;
bSizerRadiusValue = new wxBoxSizer( wxHORIZONTAL );
m_tcValueCircRadius = new wxTextCtrl( sbSizerCircParams->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
m_tcValueCircRadius->SetToolTip( _("Distance between Ref point and Center pos.") );
bSizerRadiusValue->Add( m_tcValueCircRadius, 1, wxALIGN_CENTER_VERTICAL|wxTOP|wxRIGHT, 3 );
m_unitLabelCircRadius = new wxStaticText( sbSizerCircParams->GetStaticBox(), wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 );
m_unitLabelCircRadius->Wrap( -1 );
bSizerRadiusValue->Add( m_unitLabelCircRadius, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
m_btnSelectCenterItem = new wxButton( sbSizerInfo->GetStaticBox(), wxID_ANY, _("Select Item..."), wxDefaultPosition, wxDefaultSize, 0 );
bSizer12->Add( m_btnSelectCenterItem, 1, wxALL, 5 );
bSizerRadius->Add( bSizerRadiusValue, 0, wxEXPAND, 5 );
m_labelCircCenterAngle = new wxStaticText( sbSizerCircParams->GetStaticBox(), wxID_ANY, _("Angle from center to reference position:"), wxDefaultPosition, wxDefaultSize, 0 );
m_labelCircCenterAngle->Wrap( -1 );
bSizerRadius->Add( m_labelCircCenterAngle, 0, wxTOP|wxRIGHT, 5 );
wxBoxSizer* bSizerRadiusValue1;
bSizerRadiusValue1 = new wxBoxSizer( wxHORIZONTAL );
m_tcValueCircCenterAngle = new wxTextCtrl( sbSizerCircParams->GetStaticBox(), wxID_ANY, _("0"), wxDefaultPosition, wxDefaultSize, 0 );
#ifdef __WXGTK__
if ( !m_tcValueCircCenterAngle->HasFlag( wxTE_MULTILINE ) )
{
m_tcValueCircCenterAngle->SetMaxLength( 8 );
}
#else
m_tcValueCircCenterAngle->SetMaxLength( 8 );
#endif
m_tcValueCircCenterAngle->SetToolTip( _("Angle between Ref point and Center pos.") );
bSizerRadiusValue1->Add( m_tcValueCircCenterAngle, 1, wxALIGN_CENTER_VERTICAL|wxTOP|wxRIGHT, 3 );
m_unitLabelCircCenterAngle = new wxStaticText( sbSizerCircParams->GetStaticBox(), wxID_ANY, _("deg"), wxDefaultPosition, wxDefaultSize, 0 );
m_unitLabelCircCenterAngle->Wrap( -1 );
bSizerRadiusValue1->Add( m_unitLabelCircCenterAngle, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
sbSizerInfo->Add( bSizer12, 1, wxEXPAND, 5 );
bSizerRadius->Add( bSizerRadiusValue1, 0, wxEXPAND, 5 );
sbSizerCircParams->Add( bSizerRadius, 0, wxEXPAND|wxLEFT, 25 );
sbSizerCircParams->Add( 0, 5, 0, 0, 5 );
bSizerCircLeft->Add( sbSizerCircParams, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 10 );
bSizerCircLeft->Add( sbSizerInfo, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 10 );
wxStaticBoxSizer* sbSizerDupPrms;
sbSizerDupPrms = new wxStaticBoxSizer( new wxStaticBox( m_circularPanel, wxID_ANY, _("Duplication Settings") ), wxVERTICAL );
m_checkBoxFullCircle = new wxCheckBox( sbSizerDupPrms->GetStaticBox(), wxID_ANY, _("Full circle"), wxDefaultPosition, wxDefaultSize, 0 );
m_checkBoxFullCircle->SetValue(true);
sbSizerDupPrms->Add( m_checkBoxFullCircle, 0, wxALL, 5 );
wxFlexGridSizer* fgSizerDupPrms;
fgSizerDupPrms = new wxFlexGridSizer( 0, 3, 5, 5 );
fgSizerDupPrms->AddGrowableCol( 1 );
fgSizerDupPrms->SetFlexibleDirection( wxBOTH );
fgSizerDupPrms->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_labelCircAngle = new wxStaticText( sbSizerDupPrms->GetStaticBox(), wxID_ANY, _("Angle between new items:"), wxDefaultPosition, wxDefaultSize, 0 );
m_labelCircAngle = new wxStaticText( sbSizerDupPrms->GetStaticBox(), wxID_ANY, _("Angle between items:"), wxDefaultPosition, wxDefaultSize, 0 );
m_labelCircAngle->Wrap( -1 );
fgSizerDupPrms->Add( m_labelCircAngle, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 );
m_entryCircAngle = new wxTextCtrl( sbSizerDupPrms->GetStaticBox(), wxID_ANY, _("0"), wxDefaultPosition, wxDefaultSize, 0 );
m_entryCircAngle = new wxTextCtrl( sbSizerDupPrms->GetStaticBox(), wxID_ANY, _("90"), wxDefaultPosition, wxDefaultSize, 0 );
m_entryCircAngle->SetToolTip( _("Positive angles represent an anti-clockwise rotation. An angle of 0 will produce a full circle divided evenly into \"Count\" portions.") );
fgSizerDupPrms->Add( m_entryCircAngle, 0, wxEXPAND, 5 );
@ -470,20 +383,13 @@ DIALOG_CREATE_ARRAY_BASE::DIALOG_CREATE_ARRAY_BASE( wxWindow* parent, wxWindowID
fgSizerDupPrms->Add( m_entryCircCount, 0, wxEXPAND, 5 );
fgSizerDupPrms->Add( 0, 0, 0, wxEXPAND, 5 );
sbSizerDupPrms->Add( fgSizerDupPrms, 0, wxBOTTOM|wxEXPAND, 5 );
m_labelCircRotate = new wxStaticText( sbSizerDupPrms->GetStaticBox(), wxID_ANY, _("Rotate items:"), wxDefaultPosition, wxDefaultSize, 0 );
m_labelCircRotate->Wrap( -1 );
fgSizerDupPrms->Add( m_labelCircRotate, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 );
m_entryRotateItemsCb = new wxCheckBox( sbSizerDupPrms->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
m_entryRotateItemsCb = new wxCheckBox( sbSizerDupPrms->GetStaticBox(), wxID_ANY, _("Rotate items"), wxDefaultPosition, wxDefaultSize, 0 );
m_entryRotateItemsCb->SetValue(true);
m_entryRotateItemsCb->SetToolTip( _("Rotate the item as well as move it - multi-selections will be rotated together") );
fgSizerDupPrms->Add( m_entryRotateItemsCb, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM, 5 );
sbSizerDupPrms->Add( fgSizerDupPrms, 0, wxBOTTOM|wxEXPAND, 5 );
sbSizerDupPrms->Add( m_entryRotateItemsCb, 0, wxALL, 5 );
bSizerCircLeft->Add( sbSizerDupPrms, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 10 );
@ -553,7 +459,7 @@ DIALOG_CREATE_ARRAY_BASE::DIALOG_CREATE_ARRAY_BASE( wxWindow* parent, wxWindowID
m_circularPanel->SetSizer( bSizer4 );
m_circularPanel->Layout();
bSizer4->Fit( m_circularPanel );
m_gridTypeNotebook->AddPage( m_circularPanel, _("Circular Array"), false );
m_gridTypeNotebook->AddPage( m_circularPanel, _("Circular Array"), true );
bSizer7->Add( m_gridTypeNotebook, 1, wxALL|wxEXPAND, 10 );
@ -610,12 +516,11 @@ DIALOG_CREATE_ARRAY_BASE::DIALOG_CREATE_ARRAY_BASE( wxWindow* parent, wxWindowID
m_entryStagger->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnParameterChanged ), NULL, this );
m_rbGridStartNumberingOpt->Connect( wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnParameterChanged ), NULL, this );
m_radioBoxGridNumberingScheme->Connect( wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnParameterChanged ), NULL, this );
m_radioBtnSetByPos->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnButtonPosition ), NULL, this );
m_entryCentreX->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnParameterChanged ), NULL, this );
m_entryCentreY->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnParameterChanged ), NULL, this );
m_radioBtnSetByRadius->Connect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnButtonRadius ), NULL, this );
m_tcValueCircRadius->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnRadiusChanged ), NULL, this );
m_tcValueCircCenterAngle->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnRadiusChanged ), NULL, this );
m_btnSelectCenterPoint->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnSelectCenterButton ), NULL, this );
m_btnSelectCenterItem->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnSelectCenterButton ), NULL, this );
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_rbCircStartNumberingOpt->Connect( wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnParameterChanged ), NULL, this );
@ -634,12 +539,11 @@ DIALOG_CREATE_ARRAY_BASE::~DIALOG_CREATE_ARRAY_BASE()
m_entryStagger->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnParameterChanged ), NULL, this );
m_rbGridStartNumberingOpt->Disconnect( wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnParameterChanged ), NULL, this );
m_radioBoxGridNumberingScheme->Disconnect( wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnParameterChanged ), NULL, this );
m_radioBtnSetByPos->Disconnect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnButtonPosition ), NULL, this );
m_entryCentreX->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnParameterChanged ), NULL, this );
m_entryCentreY->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnParameterChanged ), NULL, this );
m_radioBtnSetByRadius->Disconnect( wxEVT_COMMAND_RADIOBUTTON_SELECTED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnButtonRadius ), NULL, this );
m_tcValueCircRadius->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnRadiusChanged ), NULL, this );
m_tcValueCircCenterAngle->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnRadiusChanged ), NULL, this );
m_btnSelectCenterPoint->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnSelectCenterButton ), NULL, this );
m_btnSelectCenterItem->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnSelectCenterButton ), NULL, this );
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_rbCircStartNumberingOpt->Disconnect( wxEVT_COMMAND_RADIOBOX_SELECTED, wxCommandEventHandler( DIALOG_CREATE_ARRAY_BASE::OnParameterChanged ), NULL, this );

File diff suppressed because it is too large Load Diff

View File

@ -30,8 +30,8 @@ class TEXT_CTRL_EVAL;
#include <wx/bitmap.h>
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/notebook.h>
#include <wx/button.h>
#include <wx/notebook.h>
#include <wx/dialog.h>
///////////////////////////////////////////////////////////////////////////
@ -87,33 +87,20 @@ class DIALOG_CREATE_ARRAY_BASE : public DIALOG_SHIM
wxTextCtrl* m_entryGridPriNumberingStep;
wxTextCtrl* m_entryGridSecNumberingStep;
wxPanel* m_circularPanel;
wxStaticText* m_stInfoItems;
wxStaticText* m_stRefPosXTxt;
wxTextCtrl* m_tcRefPosX;
wxStaticText* m_stRefPosXUnit;
wxStaticText* m_stRefPosYTxt;
wxTextCtrl* m_tcRefPosY;
wxStaticText* m_stRefPosYUnit;
wxRadioButton* m_radioBtnSetByPos;
wxStaticText* m_labelCentreX;
wxTextCtrl* m_entryCentreX;
wxStaticText* m_unitLabelCentreX;
wxStaticText* m_labelCentreY;
wxTextCtrl* m_entryCentreY;
wxStaticText* m_unitLabelCentreY;
wxRadioButton* m_radioBtnSetByRadius;
wxStaticText* m_labelCircRadius;
wxTextCtrl* m_tcValueCircRadius;
wxStaticText* m_unitLabelCircRadius;
wxStaticText* m_labelCircCenterAngle;
wxTextCtrl* m_tcValueCircCenterAngle;
wxStaticText* m_unitLabelCircCenterAngle;
wxButton* m_btnSelectCenterPoint;
wxButton* m_btnSelectCenterItem;
wxCheckBox* m_checkBoxFullCircle;
wxStaticText* m_labelCircAngle;
wxTextCtrl* m_entryCircAngle;
wxStaticText* m_unitLabelCircAngle;
wxStaticText* m_labelCircCount;
TEXT_CTRL_EVAL* m_entryCircCount;
wxStaticText* m_labelCircRotate;
wxCheckBox* m_entryRotateItemsCb;
wxPanel* m_circularPadNumberingPanel;
wxStaticBoxSizer* m_circPadNumberingSizer;
@ -134,9 +121,7 @@ class DIALOG_CREATE_ARRAY_BASE : public DIALOG_SHIM
// Virtual event handlers, override them in your derived class
virtual void OnClose( wxCloseEvent& event ) { event.Skip(); }
virtual void OnParameterChanged( wxCommandEvent& event ) { event.Skip(); }
virtual void OnButtonPosition( wxCommandEvent& event ) { event.Skip(); }
virtual void OnButtonRadius( wxCommandEvent& event ) { event.Skip(); }
virtual void OnRadiusChanged( wxCommandEvent& event ) { event.Skip(); }
virtual void OnSelectCenterButton( wxCommandEvent& event ) { event.Skip(); }
public:

View File

@ -67,6 +67,7 @@
#include <tool/properties_tool.h>
#include <tool/selection.h>
#include <tool/zoom_tool.h>
#include <tools/array_tool.h>
#include <tools/pcb_grid_helper.h>
#include <tools/pcb_selection_tool.h>
#include <tools/pcb_picker_tool.h>
@ -730,6 +731,7 @@ void PCB_EDIT_FRAME::setupTools()
m_toolManager->RegisterTool( new ALIGN_DISTRIBUTE_TOOL );
m_toolManager->RegisterTool( new MICROWAVE_TOOL );
m_toolManager->RegisterTool( new POSITION_RELATIVE_TOOL );
m_toolManager->RegisterTool( new ARRAY_TOOL );
m_toolManager->RegisterTool( new ZONE_FILLER_TOOL );
m_toolManager->RegisterTool( new AUTOPLACE_TOOL );
m_toolManager->RegisterTool( new DRC_TOOL );
@ -1056,6 +1058,7 @@ void PCB_EDIT_FRAME::setupUIConditions()
CURRENT_EDIT_TOOL( PCB_ACTIONS::drawLeader );
CURRENT_EDIT_TOOL( PCB_ACTIONS::drillOrigin );
CURRENT_EDIT_TOOL( PCB_ACTIONS::gridSetOrigin );
CURRENT_EDIT_TOOL( PCB_ACTIONS::createArray );
CURRENT_EDIT_TOOL( PCB_ACTIONS::microwaveCreateLine );
CURRENT_EDIT_TOOL( PCB_ACTIONS::microwaveCreateGap );

View File

@ -1,8 +1,7 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Created on: 11 Mar 2016, author John Beard
* Copyright (C) 2016-2024 KiCad Developers, see AUTHORS.txt for contributors.
* Copyright (C) 2017-2022 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -22,17 +21,17 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "array_creator.h"
#include "tools/array_tool.h"
#include <array_options.h>
#include <array_pad_number_provider.h>
#include <board_commit.h>
#include <pcb_group.h>
#include <pad.h>
#include <dialogs/dialog_create_array.h>
#include <tool/tool_manager.h>
#include <pad.h>
#include <pcb_group.h>
#include <tools/board_reannotate_tool.h>
#include <tools/pcb_selection_tool.h>
/**
* Transform a #BOARD_ITEM from the given #ARRAY_OPTIONS and an index into the array.
*
@ -44,58 +43,110 @@ static void TransformItem( const ARRAY_OPTIONS& aArrOpts, int aIndex, BOARD_ITEM
{
const ARRAY_OPTIONS::TRANSFORM transform = aArrOpts.GetTransform( aIndex, aItem.GetPosition() );
std::cout << "Offset for item " << aIndex << ": " << transform.m_offset << std::endl;
aItem.Move( transform.m_offset );
aItem.Rotate( aItem.GetPosition(), transform.m_rotation );
}
void ARRAY_CREATOR::Invoke()
ARRAY_TOOL::ARRAY_TOOL() : PCB_TOOL_BASE( "pcbnew.Array" )
{
// bail out if no items
if( m_selection.Size() == 0 )
return;
}
FOOTPRINT* const fp = m_isFootprintEditor ? m_parent.GetBoard()->GetFirstFootprint() : nullptr;
ARRAY_TOOL::~ARRAY_TOOL()
{
}
void ARRAY_TOOL::Reset( RESET_REASON aReason )
{
}
bool ARRAY_TOOL::Init()
{
return true;
}
int ARRAY_TOOL::CreateArray( const TOOL_EVENT& aEvent )
{
PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
// Be sure that there is at least one item that we can modify
const PCB_SELECTION& selection = selectionTool->RequestSelection(
[]( const VECTOR2I&, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
{
sTool->FilterCollectorForMarkers( aCollector );
sTool->FilterCollectorForHierarchy( aCollector, true );
} );
if( selection.Empty() )
return 0;
m_selection = std::make_unique<PCB_SELECTION>( selection );
// we have a selection to work on now, so start the tool process
PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
const bool enableArrayNumbering = m_isFootprintEditor;
VECTOR2I origin;
if( m_selection.Size() == 1 )
origin = m_selection.Items()[0]->GetPosition();
if( m_selection->Size() == 1 )
origin = m_selection->Items()[0]->GetPosition();
else
origin = m_selection.GetCenter();
origin = m_selection->GetCenter();
std::unique_ptr<ARRAY_OPTIONS> array_opts;
m_array_opts.reset();
m_dialog = new DIALOG_CREATE_ARRAY( editFrame, m_array_opts, enableArrayNumbering, origin );
DIALOG_CREATE_ARRAY dialog( &m_parent, array_opts, enableArrayNumbering, origin );
m_dialog->Bind( wxEVT_CLOSE_WINDOW, &ARRAY_TOOL::onDialogClosed, this );
int ret = dialog.ShowModal();
// Show the dialog, but it's not modal - the user might ask to select a point, in which case
// we'll exit and do to that.
m_dialog->Show( true );
if( ret != wxID_OK || array_opts == nullptr )
return 0;
}
void ARRAY_TOOL::onDialogClosed( wxCloseEvent& aEvent )
{
// Now that the dialog has served it's purpose, we can get rid of it
m_dialog->Destroy();
// This means the dialog failed somehow
if( m_array_opts == nullptr )
return;
BOARD_COMMIT commit( &m_parent );
wxCHECK( m_selection, /* void */ );
ARRAY_PAD_NUMBER_PROVIDER pad_number_provider( fp, *array_opts );
PCB_SELECTION& selection = *m_selection;
PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
BOARD_COMMIT commit( editFrame );
FOOTPRINT* const fp =
m_isFootprintEditor ? editFrame->GetBoard()->GetFirstFootprint() : nullptr;
ARRAY_PAD_NUMBER_PROVIDER pad_number_provider( fp, *m_array_opts );
EDA_ITEMS all_added_items;
int arraySize = array_opts->GetArraySize();
int arraySize = m_array_opts->GetArraySize();
// Iterate in reverse so the original items go last, and we can
// use them for the positions of the clones.
for( int ptN = arraySize - 1; ptN >= 0; --ptN )
{
PCB_SELECTION items_for_this_block;
PCB_SELECTION items_for_this_block;
std::set<FOOTPRINT*> fpDeDupe;
for ( int i = 0; i < m_selection.Size(); ++i )
for( int i = 0; i < m_selection->Size(); ++i )
{
if( !m_selection[i]->IsBOARD_ITEM() )
if( !selection[i]->IsBOARD_ITEM() )
continue;
BOARD_ITEM* item = static_cast<BOARD_ITEM*>( m_selection[ i ] );
BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection[i] );
FOOTPRINT* parentFootprint = item->GetParentFootprint();
@ -127,7 +178,7 @@ void ARRAY_CREATOR::Invoke()
commit.Modify( this_item );
TransformItem( *array_opts, arraySize - 1, *this_item );
TransformItem( *m_array_opts, arraySize - 1, *this_item );
}
else
{
@ -162,9 +213,7 @@ void ARRAY_CREATOR::Invoke()
case PCB_DIM_ORTHOGONAL_T:
case PCB_DIM_LEADER_T:
case PCB_TARGET_T:
case PCB_ZONE_T:
this_item = item->Duplicate();
break;
case PCB_ZONE_T: this_item = item->Duplicate(); break;
case PCB_GROUP_T:
this_item = static_cast<PCB_GROUP*>( item )->DeepDuplicate();
@ -192,7 +241,7 @@ void ARRAY_CREATOR::Invoke()
} );
// We're iterating backwards, so the first item is the last in the array
TransformItem( *array_opts, arraySize - ptN - 1, *this_item );
TransformItem( *m_array_opts, arraySize - ptN - 1, *this_item );
// If a group is duplicated, add also created members to the board
if( this_item->Type() == PCB_GROUP_T )
@ -211,7 +260,7 @@ void ARRAY_CREATOR::Invoke()
// attempt to renumber items if the array parameters define
// a complete numbering scheme to number by (as opposed to
// implicit numbering by incrementing the items during creation
if( this_item && array_opts->ShouldNumberItems() )
if( this_item && m_array_opts->ShouldNumberItems() )
{
// Renumber non-aperture pads.
if( this_item->Type() == PCB_PAD_T )
@ -227,7 +276,7 @@ void ARRAY_CREATOR::Invoke()
}
}
if( !m_isFootprintEditor && array_opts->ShouldReannotateFootprints() )
if( !m_isFootprintEditor && m_array_opts->ShouldReannotateFootprints() )
{
m_toolMgr->GetTool<BOARD_REANNOTATE_TOOL>()->ReannotateDuplicates( items_for_this_block,
all_added_items );
@ -241,4 +290,14 @@ void ARRAY_CREATOR::Invoke()
m_toolMgr->RunAction<EDA_ITEMS*>( PCB_ACTIONS::selectItems, &all_added_items );
commit.Push( _( "Create Array" ) );
m_selection.reset();
}
void ARRAY_TOOL::setTransitions()
{
// clang-format off
Go( &ARRAY_TOOL::CreateArray, PCB_ACTIONS::createArray.MakeEvent() );
// clang-format on
}

78
pcbnew/tools/array_tool.h Normal file
View File

@ -0,0 +1,78 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2024 KiCad Developers, see AUTHORS.txt for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#pragma once
#include <memory>
#include <wx/event.h>
#include <math/vector2d.h>
#include <tools/pcb_tool_base.h>
#include <tools/pcb_selection.h>
class ARRAY_OPTIONS;
class BOARD_COMMIT;
class DIALOG_CREATE_ARRAY;
/**
* The array tool.
*
* Handles actions and cross-action state for creating arrays.
*
* The cross-action persistence is for cases such as hiding a dialog
* to give the user a chance to choose a point or item manually.
*/
class ARRAY_TOOL : public PCB_TOOL_BASE, public wxEvtHandler
{
public:
ARRAY_TOOL();
~ARRAY_TOOL();
void Reset( RESET_REASON aReason ) override;
/// @copydoc TOOL_BASE::Init()
bool Init() override;
/**
* Invoke a dialog box to allow positioning of the item relative to another by an exact amount.
*/
int CreateArray( const TOOL_EVENT& aEvent );
/**
* Position the m_position_relative_selection selection relative to anchor position using
* the given translation.
*/
// int DoCreateArray( const VECTOR2I& anchor, const VECTOR2I& translation );
///< Set up handlers for various events.
void setTransitions() override;
private:
void onDialogClosed( wxCloseEvent& aEvent );
DIALOG_CREATE_ARRAY* m_dialog;
std::unique_ptr<ARRAY_OPTIONS> m_array_opts;
std::unique_ptr<PCB_SELECTION> m_selection;
};

View File

@ -43,7 +43,6 @@
#include <pcb_edit_frame.h>
#include <drawing_sheet/ds_proxy_view_item.h>
#include <kiway.h>
#include <array_creator.h>
#include <status_popup.h>
#include <tool/selection_conditions.h>
#include <tool/tool_manager.h>
@ -2934,34 +2933,6 @@ int EDIT_TOOL::Duplicate( const TOOL_EVENT& aEvent )
}
int EDIT_TOOL::CreateArray( const TOOL_EVENT& aEvent )
{
if( isRouterActive() )
{
wxBell();
return 0;
}
// Be sure that there is at least one item that we can modify
const PCB_SELECTION& selection = m_selectionTool->RequestSelection(
[]( const VECTOR2I&, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
{
sTool->FilterCollectorForMarkers( aCollector );
sTool->FilterCollectorForHierarchy( aCollector, true );
} );
if( selection.Empty() )
return 0;
// we have a selection to work on now, so start the tool process
PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
ARRAY_CREATOR array_creator( *editFrame, m_isFootprintEditor, selection, m_toolMgr );
array_creator.Invoke();
return 0;
}
int EDIT_TOOL::Increment( const TOOL_EVENT& aEvent )
{
const auto incrementableFilter =
@ -3440,7 +3411,6 @@ void EDIT_TOOL::setTransitions()
Go( &EDIT_TOOL::Move, PCB_ACTIONS::moveWithReference.MakeEvent() );
Go( &EDIT_TOOL::Duplicate, ACTIONS::duplicate.MakeEvent() );
Go( &EDIT_TOOL::Duplicate, PCB_ACTIONS::duplicateIncrement.MakeEvent() );
Go( &EDIT_TOOL::CreateArray, PCB_ACTIONS::createArray.MakeEvent() );
Go( &EDIT_TOOL::Mirror, PCB_ACTIONS::mirrorH.MakeEvent() );
Go( &EDIT_TOOL::Mirror, PCB_ACTIONS::mirrorV.MakeEvent() );
Go( &EDIT_TOOL::Swap, PCB_ACTIONS::swap.MakeEvent() );

View File

@ -170,11 +170,6 @@ public:
*/
int MoveExact( const TOOL_EVENT& aEvent );
/**
* Create an array of the selected items, invoking the array editor dialog to set the options.
*/
int CreateArray( const TOOL_EVENT& aEvent );
/**
* Increment some aspect of the selected items.q
*/

View File

@ -512,15 +512,6 @@ TOOL_ACTION PCB_ACTIONS::pointEditorMoveMidpoint( TOOL_ACTION_ARGS()
.Tooltip( _( "Move the active midpoint to an exact location" ) )
.Icon( BITMAPS::move_exactly ) );
TOOL_ACTION PCB_ACTIONS::createArray( TOOL_ACTION_ARGS()
.Name( "pcbnew.InteractiveEdit.createArray" )
.Scope( AS_GLOBAL )
.DefaultHotkey( MD_CTRL + 'T' )
.LegacyHotkeyName( "Create Array" )
.FriendlyName( _( "Create Array..." ) )
.Icon( BITMAPS::array )
.Flags( AF_ACTIVATE ) );
TOOL_ACTION PCB_ACTIONS::rotateCw( TOOL_ACTION_ARGS()
.Name( "pcbnew.InteractiveEdit.rotateCw" )
.Scope( AS_GLOBAL )
@ -680,6 +671,16 @@ TOOL_ACTION PCB_ACTIONS::properties( TOOL_ACTION_ARGS()
.FriendlyName( _( "Properties..." ) )
.Icon( BITMAPS::edit ) );
// ARRAY
//
TOOL_ACTION PCB_ACTIONS::createArray( TOOL_ACTION_ARGS()
.Name( "pcbnew.Array.createArray" )
.Scope( AS_GLOBAL )
.DefaultHotkey( MD_CTRL + 'T' )
.LegacyHotkeyName( "Create Array" )
.FriendlyName( _( "Create Array..." ) )
.Icon( BITMAPS::array )
.Flags( AF_ACTIVATE ) );
// FOOTPRINT_EDITOR_CONTROL
//