KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_move_exact.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, [email protected]
5 * Copyright The 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, see <https://www.gnu.org/licenses/>.
19 */
20
22#include <math/box2.h>
23#include <math/util.h> // for KiROUND
25#include <pcb_edit_frame.h>
26#include <trigo.h>
27
28
29
31 ROTATION_ANCHOR& aAnchor, const BOX2I& aBbox ) :
32 DIALOG_MOVE_EXACT_BASE( aParent ),
33 m_translation( aTranslate ),
34 m_rotation( aRotate ),
35 m_rotationAnchor( aAnchor ),
36 m_bbox( aBbox ),
37 m_moveX( aParent, m_xLabel, m_xEntry, m_xUnit ),
38 m_moveY( aParent, m_yLabel, m_yEntry, m_yUnit ),
40 m_stateX( 0.0 ),
41 m_stateY( 0.0 ),
42 m_stateRadius( 0.0 )
43{
44 // We can't set the tab order through wxWidgets due to shortcomings in their mnemonics
45 // implementation on MSW
46 m_tabOrder = {
53 };
54
55 // Configure display origin transforms
58
59 m_menuIDs.push_back( aAnchor );
61
62 if( aParent->IsType( FRAME_PCB_EDITOR ) )
64
65 // DIALOG_SHIM needs a title- and anchor-options-specific hash_key so we don't save/restore state
66 // between usage cases.
67 m_hash_key = GetTitle().ToStdString();
68
69 if( aAnchor == ROTATE_AROUND_SEL_CENTER )
70 m_hash_key += "|with_selection";
71
72 if( aParent->IsType( FRAME_PCB_EDITOR ) )
73 m_hash_key += "|pcb_editor";
74
76 m_rotate.SetUnits( EDA_UNITS::DEGREES );
77
79
81}
82
83
85{
86 wxArrayString menuItems;
87
88 for( const ROTATION_ANCHOR& anchorID : m_menuIDs )
89 {
90 switch( anchorID )
91 {
92 case ROTATE_AROUND_ITEM_ANCHOR: menuItems.push_back( _( "Rotate around item anchor" ) ); break;
93 case ROTATE_AROUND_SEL_CENTER: menuItems.push_back( _( "Rotate around selection center" ) ); break;
94 case ROTATE_AROUND_USER_ORIGIN: menuItems.push_back( _( "Rotate around local coordinates origin" ) ); break;
95 case ROTATE_AROUND_AUX_ORIGIN: menuItems.push_back( _( "Rotate around drill/place origin" ) ); break;
96 }
97 }
98
99 m_anchorOptions->Set( menuItems );
100
101 // This can be -1 if uninitialized
102 const int currSelection = m_anchorOptions->GetSelection();
103 if( currSelection < 0 || currSelection >= static_cast<int>( m_menuIDs.size() ) )
104 m_anchorOptions->SetSelection( 0 );
105}
106
107
108void DIALOG_MOVE_EXACT::ToPolarDeg( double x, double y, double& r, EDA_ANGLE& q )
109{
110 // convert to polar coordinates
111 r = hypot( x, y );
112
113 q = ( r != 0) ? EDA_ANGLE( VECTOR2D( x, y ) ) : ANGLE_0;
114}
115
116
117bool DIALOG_MOVE_EXACT::GetTranslationInIU( wxRealPoint& val, bool polar )
118{
119 if( polar )
120 {
121 const double r = m_moveX.GetDoubleValue();
122 const EDA_ANGLE q = m_moveY.GetAngleValue();
123
124 val.x = r * q.Cos();
125 val.y = r * q.Sin();
126 }
127 else
128 {
129 // direct read
130 val.x = m_moveX.GetDoubleValue();
131 val.y = m_moveY.GetDoubleValue();
132 }
133
134 // no validation to do here, but in future, you could return false here
135 return true;
136}
137
138
139void DIALOG_MOVE_EXACT::OnPolarChanged( wxCommandEvent& event )
140{
141 bool newPolar = m_polarCoords->IsChecked();
142 double moveX = m_moveX.GetDoubleValue();
143 double moveY = m_moveY.GetDoubleValue();
144 updateDialogControls( newPolar );
145
146 if( newPolar )
147 {
148 if( moveX != m_stateX || moveY != m_stateY )
149 {
150 m_stateX = moveX;
151 m_stateY = moveY;
153
154 m_moveX.SetDoubleValue( m_stateRadius );
155 m_stateRadius = m_moveX.GetDoubleValue();
156 m_moveY.SetAngleValue( m_stateTheta );
157 m_stateTheta = m_moveY.GetAngleValue();
158 }
159 else
160 {
161 m_moveX.SetDoubleValue( m_stateRadius );
162 m_moveY.SetAngleValue( m_stateTheta );
163 }
164 }
165 else
166 {
167 if( moveX != m_stateRadius || moveY != m_stateTheta.AsDegrees() )
168 {
169 m_stateRadius = moveX;
170 m_stateTheta = EDA_ANGLE( moveY, DEGREES_T );
173
174 m_moveX.SetDoubleValue( m_stateX );
175 m_stateX = m_moveX.GetDoubleValue();
176 m_moveY.SetDoubleValue( m_stateY );
177 m_stateY = m_moveY.GetDoubleValue();
178 }
179 else
180 {
181 m_moveX.SetDoubleValue( m_stateX );
182 m_moveY.SetDoubleValue( m_stateY );
183 }
184 }
185}
186
187
189{
190 if( aPolar )
191 {
192 m_moveX.SetLabel( _( "Distance:" ) ); // Polar radius
193 m_moveY.SetLabel( _( "Angle:" ) ); // Polar theta or angle
194 m_moveY.SetUnits( EDA_UNITS::DEGREES );
195 }
196 else
197 {
198 m_moveX.SetLabel( _( "Move X:" ) );
199 m_moveY.SetLabel( _( "Move Y:" ) );
200 m_moveY.SetUnits( GetUserUnits() );
201 }
202
203 Layout();
204}
205
206
207void DIALOG_MOVE_EXACT::OnClear( wxCommandEvent& event )
208{
209 wxObject* obj = event.GetEventObject();
210
211 if( obj == m_clearX )
212 {
213 m_moveX.SetValue( 0 );
214 }
215 else if( obj == m_clearY )
216 {
217 m_moveY.SetValue( 0 );
218 }
219 else if( obj == m_clearRot )
220 {
221 m_rotate.SetAngleValue( ANGLE_0 );
222 }
223
224 // Keep m_stdButtonsOK focused to allow enter key activate the OK button
225 m_stdButtonsOK->SetFocus();
226}
227
228
230{
231 updateDialogControls( m_polarCoords->GetValue() );
232
233 // Force the evaluation when setting previous values
234 m_moveX.RequireEval();
235 m_moveY.RequireEval();
236 m_rotate.RequireEval();
237
238 return true;
239}
240
241
243{
244 // for the output, we only deliver a Cartesian vector
245 wxRealPoint translation;
246 bool ok = GetTranslationInIU( translation, m_polarCoords->IsChecked() );
247 m_translation.x = KiROUND(translation.x);
248 m_translation.y = KiROUND(translation.y);
249 m_rotation = m_rotate.GetAngleValue();
250
251 const int anchorSelection = m_anchorOptions->GetSelection();
252 wxCHECK_MSG( anchorSelection >= 0 && anchorSelection < static_cast<int>( m_menuIDs.size() ), false,
253 wxString::Format( "Invalid rotation anchor selection: %d", anchorSelection ) );
254
255 m_rotationAnchor = m_menuIDs[anchorSelection];
256
257 return ok;
258}
259
260
261void DIALOG_MOVE_EXACT::OnTextFocusLost( wxFocusEvent& event )
262{
263 wxTextCtrl* obj = static_cast<wxTextCtrl*>( event.GetEventObject() );
264
265 if( obj->GetValue().IsEmpty() )
266 obj->SetValue( "0" );
267
268 event.Skip();
269}
270
271
272void DIALOG_MOVE_EXACT::OnTextChanged( wxCommandEvent& event )
273{
274 double delta_x = m_moveX.GetDoubleValue();
275 double delta_y = m_moveY.GetDoubleValue();
276 double max_border = std::numeric_limits<int>::max() * M_SQRT1_2;
277
278 if( m_bbox.GetLeft() + delta_x < -max_border ||
279 m_bbox.GetRight() + delta_x > max_border ||
280 m_bbox.GetTop() + delta_y < -max_border ||
281 m_bbox.GetBottom() + delta_y > max_border )
282 {
283 const wxString invalid_length = _( "Invalid movement values. Movement would place selection "
284 "outside of the maximum board area." );
285
286 m_xEntry->SetToolTip( invalid_length );
287 m_xEntry->SetForegroundColour( *wxRED );
288 m_yEntry->SetToolTip( invalid_length );
289 m_yEntry->SetForegroundColour( *wxRED );
290 m_stdButtons->GetAffirmativeButton()->Disable();
291 }
292 else
293 {
294 m_xEntry->SetToolTip( "" );
295 m_xEntry->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT ) );
296 m_yEntry->SetToolTip( "" );
297 m_yEntry->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT ) );
298 m_stdButtons->GetAffirmativeButton()->Enable();
299 event.Skip();
300 }
301}
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
DIALOG_MOVE_EXACT_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Move Item"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(-1,-1), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
wxStdDialogButtonSizer * m_stdButtons
bool TransferDataToWindow() override
bool TransferDataFromWindow() override
ROTATION_ANCHOR & m_rotationAnchor
void ToPolarDeg(double x, double y, double &r, EDA_ANGLE &q)
Convert a given Cartesian point into a polar representation.
void updateDialogControls(bool aPolar)
void OnClear(wxCommandEvent &event) override
DIALOG_MOVE_EXACT(PCB_BASE_FRAME *aParent, VECTOR2I &aTranslate, EDA_ANGLE &aRotate, ROTATION_ANCHOR &aAnchor, const BOX2I &aBbox)
void OnTextFocusLost(wxFocusEvent &event) override
Reset a text field to be 0 if it was exited while blank.
void OnPolarChanged(wxCommandEvent &event) override
void OnTextChanged(wxCommandEvent &event) override
bool GetTranslationInIU(wxRealPoint &val, bool polar)
Get the (Cartesian) translation described by the text entries.
std::vector< ROTATION_ANCHOR > m_menuIDs
std::vector< wxWindow * > m_tabOrder
void SetupStandardButtons(std::map< int, wxString > aLabels={})
std::string m_hash_key
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
EDA_UNITS GetUserUnits() const
double Sin() const
Definition eda_angle.h:178
double Cos() const
Definition eda_angle.h:197
bool IsType(FRAME_T aType) const
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
ROTATION_ANCHOR
@ ROTATE_AROUND_USER_ORIGIN
@ ROTATE_AROUND_SEL_CENTER
@ ROTATE_AROUND_AUX_ORIGIN
@ ROTATE_AROUND_ITEM_ANCHOR
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
@ DEGREES_T
Definition eda_angle.h:31
@ FRAME_PCB_EDITOR
Definition frame_type.h:38
Functions for manipulating tab traversal in forms and dialogs.
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682