/
TransformDialog.cpp
325 lines (249 loc) · 9.6 KB
/
TransformDialog.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
#include "TransformDialog.h"
#include "i18n.h"
#include "icommandsystem.h"
#include "iuimanager.h"
#include "imainframe.h"
#include "itextstream.h"
#include "selectionlib.h"
#include "string/string.h"
#include "registry/Widgets.h"
#include "wxutil/ControlButton.h"
#include "wxutil/dialog/MessageBox.h"
#include <wx/panel.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/artprov.h>
#include <functional>
namespace ui
{
namespace
{
const char* const WINDOW_TITLE = N_("Arbitrary Transformation");
const char* const LABEL_ROTATION = N_("Rotation");
const char* const LABEL_SCALE = N_("Scale");
const char* const LABEL_ROTX = N_("X-Axis Rotate:");
const char* const LABEL_ROTY = N_("Y-Axis Rotate:");
const char* const LABEL_ROTZ = N_("Z-Axis Rotate:");
const char* const LABEL_SCALEX = N_("X-Axis Scale:");
const char* const LABEL_SCALEY = N_("Y-Axis Scale:");
const char* const LABEL_SCALEZ = N_("Z-Axis Scale:");
const char* const LABEL_STEP = N_("Step:");
const std::string RKEY_ROOT = "user/ui/transformDialog/";
const std::string RKEY_WINDOW_STATE = RKEY_ROOT + "window";
const std::string RKEY_ROTX_STEP = RKEY_ROOT + "rotXStep";
const std::string RKEY_ROTY_STEP = RKEY_ROOT + "rotYStep";
const std::string RKEY_ROTZ_STEP = RKEY_ROOT + "rotZStep";
const std::string RKEY_SCALEX_STEP = RKEY_ROOT + "scaleXStep";
const std::string RKEY_SCALEY_STEP = RKEY_ROOT + "scaleYStep";
const std::string RKEY_SCALEZ_STEP = RKEY_ROOT + "scaleZStep";
}
TransformDialog::TransformDialog()
: TransientWindow(_(WINDOW_TITLE), GlobalMainFrame().getWxTopLevelWindow(), true),
_selectionInfo(GlobalSelectionSystem().getSelectionInfo())
{
// Create all the widgets and pack them into the window
populateWindow();
// Update the widget sensitivity
update();
InitialiseWindowPosition(260, 314, RKEY_WINDOW_STATE);
}
void TransformDialog::onMainFrameShuttingDown()
{
rMessage() << "TransformDialog shutting down." << std::endl;
if (IsShownOnScreen())
{
Hide();
}
// Destroy the window
SendDestroyEvent();
InstancePtr().reset();
}
TransformDialogPtr& TransformDialog::InstancePtr()
{
static TransformDialogPtr _instancePtr;
if (_instancePtr == NULL)
{
// Not yet instantiated, do it now
_instancePtr = TransformDialogPtr(new TransformDialog);
// Pre-destruction cleanup
GlobalMainFrame().signal_MainFrameShuttingDown().connect(
sigc::mem_fun(*_instancePtr, &TransformDialog::onMainFrameShuttingDown)
);
}
return _instancePtr;
}
TransformDialog& TransformDialog::Instance()
{
return *InstancePtr();
}
// The command target
void TransformDialog::toggle(const cmd::ArgumentList& args)
{
Instance().ToggleVisibility();
}
void TransformDialog::populateWindow()
{
wxPanel* dialogPanel = new wxPanel(this, wxID_ANY);
dialogPanel->SetSizer(new wxBoxSizer(wxVERTICAL));
wxBoxSizer* overallVBox = new wxBoxSizer(wxVERTICAL);
dialogPanel->GetSizer()->Add(overallVBox, 1, wxEXPAND | wxALL, 12);
_rotatePanel = new wxPanel(dialogPanel, wxID_ANY);
_scalePanel = new wxPanel(dialogPanel, wxID_ANY);
overallVBox->Add(_rotatePanel, 0, wxEXPAND);
overallVBox->Add(_scalePanel, 1, wxEXPAND);
_rotatePanel->SetSizer(new wxBoxSizer(wxVERTICAL));
_scalePanel->SetSizer(new wxBoxSizer(wxVERTICAL));
// Create the rotation label (bold font)
wxStaticText* rotateLabel = new wxStaticText(_rotatePanel, wxID_ANY, _(LABEL_ROTATION));
rotateLabel->SetFont(rotateLabel->GetFont().Bold());
// Create the scale label (bold font)
wxStaticText* scaleLabel = new wxStaticText(_scalePanel, wxID_ANY, _(LABEL_SCALE));
scaleLabel->SetFont(scaleLabel->GetFont().Bold());
// Arrange label and control rows
wxFlexGridSizer* rotateTable = new wxFlexGridSizer(3, 4, 6, 6);
wxFlexGridSizer* scaleTable = new wxFlexGridSizer(3, 4, 6, 6);
_rotatePanel->GetSizer()->Add(rotateLabel, 0);
_rotatePanel->GetSizer()->Add(rotateTable, 1, wxEXPAND | wxLEFT, 6);
_scalePanel->GetSizer()->Add(scaleLabel, 0, wxTOP | wxBOTTOM, 6);
_scalePanel->GetSizer()->Add(scaleTable, 1, wxEXPAND | wxLEFT, 6);
_entries["rotateX"] = createEntryRow(_(LABEL_ROTX), rotateTable, true, 0);
_entries["rotateY"] = createEntryRow(_(LABEL_ROTY), rotateTable, true, 1);
_entries["rotateZ"] = createEntryRow(_(LABEL_ROTZ), rotateTable, true, 2);
_entries["scaleX"] = createEntryRow(_(LABEL_SCALEX), scaleTable, false, 0);
_entries["scaleY"] = createEntryRow(_(LABEL_SCALEY), scaleTable, false, 1);
_entries["scaleZ"] = createEntryRow(_(LABEL_SCALEZ), scaleTable, false, 2);
// Connect the step values to the according registry values
registry::bindWidget(_entries["rotateX"].stepEntry, RKEY_ROTX_STEP);
registry::bindWidget(_entries["rotateY"].stepEntry, RKEY_ROTY_STEP);
registry::bindWidget(_entries["rotateZ"].stepEntry, RKEY_ROTZ_STEP);
registry::bindWidget(_entries["scaleX"].stepEntry, RKEY_SCALEX_STEP);
registry::bindWidget(_entries["scaleY"].stepEntry, RKEY_SCALEY_STEP);
registry::bindWidget(_entries["scaleZ"].stepEntry, RKEY_SCALEZ_STEP);
// Connect all the arrow buttons
for (EntryRowMap::iterator i = _entries.begin(); i != _entries.end(); ++i)
{
EntryRow& row = i->second;
// Pass a EntryRow pointer to the callback, that's all it will need to update
row.smaller->Bind(wxEVT_BUTTON, std::bind(&TransformDialog::onClickSmaller, this, std::placeholders::_1, &row));
row.larger->Bind(wxEVT_BUTTON, std::bind(&TransformDialog::onClickLarger, this, std::placeholders::_1, &row));
}
}
TransformDialog::EntryRow TransformDialog::createEntryRow(
const std::string& label, wxSizer* table, bool isRotator, int axis)
{
EntryRow entryRow;
entryRow.isRotator = isRotator;
entryRow.axis = axis;
// greebo: The rotation direction is reversed for X and Z rotations
// This has no mathematical meaning, it's just for looking right.
entryRow.direction = (isRotator && axis != 1) ? -1 : 1;
wxWindow* parent = table->GetContainingWindow();
// Create the label
wxStaticText* labelWidget = new wxStaticText(parent, wxID_ANY, label);
table->Add(labelWidget, 0, wxALIGN_CENTER_VERTICAL);
// Create the control buttons (zero spacing hbox)
{
wxBoxSizer* controlButtonBox = new wxBoxSizer(wxHORIZONTAL);
controlButtonBox->SetMinSize(30, 30);
entryRow.smaller = new wxutil::ControlButton(parent,
wxArtProvider::GetBitmap(GlobalUIManager().ArtIdPrefix() + "arrow_left.png"));
entryRow.smaller->SetMinSize(wxSize(15, 24));
controlButtonBox->Add(entryRow.smaller, 0);
entryRow.larger = new wxutil::ControlButton(parent,
wxArtProvider::GetBitmap(GlobalUIManager().ArtIdPrefix() + "arrow_right.png"));
entryRow.larger->SetMinSize(wxSize(15, 24));
controlButtonBox->Add(entryRow.larger, 0);
table->Add(controlButtonBox, 0, wxLEFT, 6);
}
// Create the label
wxStaticText* stepLabel = new wxStaticText(parent, wxID_ANY, _(LABEL_STEP));
table->Add(stepLabel, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 6);
// Create the entry field
entryRow.stepEntry = new wxTextCtrl(parent, wxID_ANY);
entryRow.stepEntry->SetMinClientSize(wxSize(entryRow.stepEntry->GetCharWidth() * 5, -1));
table->Add(entryRow.stepEntry, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 6);
// Return the filled structure
return entryRow;
}
// Pre-hide callback
void TransformDialog::_preHide()
{
TransientWindow::_preHide();
_selectionChanged.disconnect();
}
// Pre-show callback
void TransformDialog::_preShow()
{
TransientWindow::_preShow();
_selectionChanged.disconnect();
// Register self to the SelSystem to get notified upon selection changes.
_selectionChanged = GlobalSelectionSystem().signal_selectionChanged().connect(
[this](const ISelectable&) { update(); });
// Update the widget values
update();
}
void TransformDialog::update()
{
// Check if there is anything selected
bool rotSensitive = (_selectionInfo.totalCount > 0);
bool scaleSensitive = (_selectionInfo.totalCount > 0 && _selectionInfo.entityCount == 0);
// set the sensitivity of the scale/rotation widgets
_rotatePanel->Enable(rotSensitive);
_rotatePanel->Enable(rotSensitive);
_scalePanel->Enable(scaleSensitive);
_scalePanel->Enable(scaleSensitive);
}
void TransformDialog::onClickLarger(wxCommandEvent& ev, EntryRow* row)
{
// Get the current step increment
float step = string::convert<float>(row->stepEntry->GetValue().ToStdString());
// Determine the action
if (row->isRotator)
{
// Do a rotation
Vector3 eulerXYZ;
// Store the value into the right axis
eulerXYZ[row->axis] = step * row->direction;
// Pass the call to the algorithm functions
GlobalCommandSystem().executeCommand("RotateSelectedEulerXYZ", eulerXYZ);
}
else
{
// Do a scale
Vector3 scaleXYZ(1,1,1);
// Store the value into the right axis
scaleXYZ[row->axis] = step;
// Pass the call to the algorithm functions
GlobalCommandSystem().executeCommand("ScaleSelected", scaleXYZ);
}
}
void TransformDialog::onClickSmaller(wxCommandEvent& ev, EntryRow* row)
{
// Get the current value and the step increment
float step = string::convert<float>(row->stepEntry->GetValue().ToStdString());
// Determine the action
if (row->isRotator)
{
// Do a rotation
Vector3 eulerXYZ;
// Store the value into the right axis
eulerXYZ[row->axis] = -step * row->direction;
// Pass the call to the algorithm functions
GlobalCommandSystem().executeCommand("RotateSelectedEulerXYZ", eulerXYZ);
}
else
{
// Do a scale
if (float_equal_epsilon(step, 0.0f, 0.0001f))
{
wxutil::Messagebox::ShowError(_("Cannot scale by zero or near-zero values"));
return;
}
Vector3 scaleXYZ(1,1,1);
// Store the value into the right axis
scaleXYZ[row->axis] = 1/step;
// Pass the call to the algorithm functions
GlobalCommandSystem().executeCommand("ScaleSelected", scaleXYZ);
}
}
} // namespace ui