KiCad PCB EDA Suite
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 (C) 2018-2020 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
26#include <math/util.h> // for KiROUND
28#include <pcb_edit_frame.h>
29#include <trigo.h>
30
31// initialise statics
33
34
36 EDA_ANGLE& aRotate, ROTATION_ANCHOR& aAnchor,
37 const BOX2I& aBbox ) :
38 DIALOG_MOVE_EXACT_BASE( aParent ),
39 m_translation( aTranslate ),
40 m_rotation( aRotate ),
41 m_rotationAnchor( aAnchor ),
42 m_bbox( aBbox ),
43 m_moveX( aParent, m_xLabel, m_xEntry, m_xUnit ),
44 m_moveY( aParent, m_yLabel, m_yEntry, m_yUnit ),
45 m_rotate( aParent, m_rotLabel, m_rotEntry, m_rotUnit ),
46 m_stateX( 0.0 ),
47 m_stateY( 0.0 ),
48 m_stateRadius( 0.0 )
49{
50 // We can't set the tab order through wxWidgets due to shortcomings in their mnemonics
51 // implementation on MSW
52 m_tabOrder = {
59 };
60
61 // Configure display origin transforms
64
66
67 m_menuIDs.push_back( aAnchor );
69
70 if( aParent->IsType( FRAME_PCB_EDITOR ) )
72
74
75 // and set up the entries according to the saved options
77 m_xEntry->ChangeValue( m_options.entry1 );
78 m_yEntry->ChangeValue( m_options.entry2 );
79
80 // Force the evaluation when setting previous values
84
87 m_anchorOptions->SetSelection( std::min( m_options.entryAnchorSelection, m_menuIDs.size() ) );
88
90
92}
93
94
96{
97 wxArrayString menuItems;
98
99 for( auto anchorID : m_menuIDs )
100 {
101 switch( anchorID )
102 {
104 menuItems.push_back( _( "Rotate around item anchor" ) );
105 break;
107 menuItems.push_back( _( "Rotate around selection center" ) );
108 break;
110 menuItems.push_back( _( "Rotate around local coordinates origin" ) );
111 break;
113 menuItems.push_back( _( "Rotate around drill/place origin" ) );
114 break;
115 }
116 }
117
118 m_anchorOptions->Set( menuItems );
119}
120
121
122void DIALOG_MOVE_EXACT::ToPolarDeg( double x, double y, double& r, EDA_ANGLE& q )
123{
124 // convert to polar coordinates
125 r = hypot( x, y );
126
127 q = ( r != 0) ? EDA_ANGLE( VECTOR2D( x, y ) ) : ANGLE_0;
128}
129
130
131bool DIALOG_MOVE_EXACT::GetTranslationInIU( wxRealPoint& val, bool polar )
132{
133 if( polar )
134 {
135 const double r = m_moveX.GetDoubleValue();
136 const EDA_ANGLE q = m_moveY.GetAngleValue();
137
138 val.x = r * q.Cos();
139 val.y = r * q.Sin();
140 }
141 else
142 {
143 // direct read
144 val.x = m_moveX.GetDoubleValue();
145 val.y = m_moveY.GetDoubleValue();
146 }
147
148 // no validation to do here, but in future, you could return false here
149 return true;
150}
151
152
153void DIALOG_MOVE_EXACT::OnPolarChanged( wxCommandEvent& event )
154{
155 bool newPolar = m_polarCoords->IsChecked();
156 double moveX = m_moveX.GetDoubleValue();
157 double moveY = m_moveY.GetDoubleValue();
158 updateDialogControls( newPolar );
159
160 if( newPolar )
161 {
162 if( moveX != m_stateX || moveY != m_stateY )
163 {
164 m_stateX = moveX;
165 m_stateY = moveY;
167
172 }
173 else
174 {
177 }
178 }
179 else
180 {
181 if( moveX != m_stateRadius || moveY != m_stateTheta.AsDegrees() )
182 {
183 m_stateRadius = moveX;
184 m_stateTheta = EDA_ANGLE( moveY, DEGREES_T );
187
192 }
193 else
194 {
197 }
198 }
199}
200
201
203{
204 if( aPolar )
205 {
206 m_moveX.SetLabel( _( "Distance:" ) ); // Polar radius
207 m_moveY.SetLabel( _( "Angle:" ) ); // Polar theta or angle
209 }
210 else
211 {
212 m_moveX.SetLabel( _( "Move X:" ) );
213 m_moveY.SetLabel( _( "Move Y:" ) );
215 }
216
217 Layout();
218}
219
220
221void DIALOG_MOVE_EXACT::OnClear( wxCommandEvent& event )
222{
223 wxObject* obj = event.GetEventObject();
224
225 if( obj == m_clearX )
226 {
227 m_moveX.SetValue( 0 );
228 }
229 else if( obj == m_clearY )
230 {
231 m_moveY.SetValue( 0 );
232 }
233 else if( obj == m_clearRot )
234 {
236 }
237
238 // Keep m_stdButtonsOK focused to allow enter key activate the OK button
239 m_stdButtonsOK->SetFocus();
240}
241
242
244{
245 // for the output, we only deliver a Cartesian vector
246 wxRealPoint translation;
247 bool ok = GetTranslationInIU( translation, m_polarCoords->IsChecked() );
248 m_translation.x = KiROUND(translation.x);
249 m_translation.y = KiROUND(translation.y);
251 m_rotationAnchor = m_menuIDs[ m_anchorOptions->GetSelection() ];
252
253 // save the settings
254 m_options.polarCoords = m_polarCoords->GetValue();
255 m_options.entry1 = m_xEntry->GetValue();
256 m_options.entry2 = m_yEntry->GetValue();
257 m_options.entryRotation = m_rotEntry->GetValue();
258 m_options.entryAnchorSelection = (size_t) std::max( m_anchorOptions->GetSelection(), 0 );
259
260 return ok;
261}
262
263
264void DIALOG_MOVE_EXACT::OnTextFocusLost( wxFocusEvent& event )
265{
266 wxTextCtrl* obj = static_cast<wxTextCtrl*>( event.GetEventObject() );
267
268 if( obj->GetValue().IsEmpty() )
269 obj->SetValue( "0" );
270
271 event.Skip();
272}
273
274
275void DIALOG_MOVE_EXACT::OnTextChanged( wxCommandEvent& event )
276{
277 double delta_x = m_moveX.GetDoubleValue();
278 double delta_y = m_moveY.GetDoubleValue();
279 double max_border = std::numeric_limits<int>::max() * M_SQRT1_2;
280
281 if( m_bbox.GetLeft() + delta_x < -max_border ||
282 m_bbox.GetRight() + delta_x > max_border ||
283 m_bbox.GetTop() + delta_y < -max_border ||
284 m_bbox.GetBottom() + delta_y > max_border )
285 {
286 const wxString invalid_length = _( "Invalid movement values. Movement would place selection "
287 "outside of the maximum board area." );
288
289 m_xEntry->SetToolTip( invalid_length );
290 m_xEntry->SetForegroundColour( *wxRED );
291 m_yEntry->SetToolTip( invalid_length );
292 m_yEntry->SetForegroundColour( *wxRED );
293 m_stdButtons->GetAffirmativeButton()->Disable();
294 }
295 else
296 {
297 m_xEntry->SetToolTip( "" );
298 m_xEntry->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT ) );
299 m_yEntry->SetToolTip( "" );
300 m_yEntry->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT ) );
301 m_stdButtons->GetAffirmativeButton()->Enable();
302 event.Skip();
303 }
304
305}
coord_type GetTop() const
Definition: box2.h:194
coord_type GetRight() const
Definition: box2.h:189
coord_type GetLeft() const
Definition: box2.h:193
coord_type GetBottom() const
Definition: box2.h:190
Class DIALOG_MOVE_EXACT_BASE.
wxStdDialogButtonSizer * m_stdButtons
static MOVE_EXACT_OPTIONS m_options
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.
const BOX2I & m_bbox
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
Definition: dialog_shim.h:222
void SetupStandardButtons(std::map< int, wxString > aLabels={})
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
Definition: dialog_shim.h:121
double Sin() const
Definition: eda_angle.h:206
double AsDegrees() const
Definition: eda_angle.h:149
double Cos() const
Definition: eda_angle.h:221
bool IsType(FRAME_T aType) const
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
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 double GetDoubleValue()
Return the current value in Internal Units.
void RequireEval()
Force the binder to evaluate the text.
Definition: unit_binder.h:194
virtual void SetAngleValue(const EDA_ANGLE &aValue)
void SetLabel(const wxString &aLabel)
virtual void SetDoubleValue(double aValue)
Set new value (in Internal Units) for the text field, taking care of units conversion.
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:186
ROTATION_ANCHOR
@ ROTATE_AROUND_USER_ORIGIN
@ ROTATE_AROUND_SEL_CENTER
@ ROTATE_AROUND_AUX_ORIGIN
@ ROTATE_AROUND_ITEM_ANCHOR
#define _(s)
@ DEGREES_T
Definition: eda_angle.h:31
static constexpr EDA_ANGLE & ANGLE_0
Definition: eda_angle.h:412
E_SERIE r
Definition: eserie.cpp:41
@ FRAME_PCB_EDITOR
Definition: frame_type.h:40
Functions for manipulating tab traversal in forms and dialogs.
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:80
VECTOR2< double > VECTOR2D
Definition: vector2d.h:617