Vuo  2.4.0
VuoInputEditorTransform.cc
Go to the documentation of this file.
1
11
12#define DEG2RAD 0.0174532925f
13#define RAD2DEG 57.295779513f
14
19{
20 return new VuoInputEditorTransform();
21}
22
26QLabel* makeLabel(const char* text)
27{
28 QLabel* label = new QLabel(text);
29 label->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
30 return label;
31}
32
36void VuoInputEditorTransform::setCoordMap(map<coord, double>* coordMap, VuoTransform transform)
37{
38 (*coordMap)[xTranslation] = transform.translation.x;
39 (*coordMap)[yTranslation] = transform.translation.y;
40 (*coordMap)[zTranslation] = transform.translation.z;
41
42 VuoPoint3d rotation = VuoTransform_getEuler(transform);
43 (*coordMap)[xRotation] = RAD2DEG * rotation.x;
44 (*coordMap)[yRotation] = RAD2DEG * rotation.y;
45 (*coordMap)[zRotation] = RAD2DEG * rotation.z;
46
47 (*coordMap)[xScale] = transform.scale.x;
48 (*coordMap)[yScale] = transform.scale.y;
49 (*coordMap)[zScale] = transform.scale.z;
50}
51
55VuoDoubleSpinBox* VuoInputEditorTransform::initSpinBox(coord whichCoord, QDialog& dialog, double initialValue)
56{
57 VuoDoubleSpinBox* spin = new VuoDoubleSpinBox(&dialog, 7);
58 const int decimalPrecision = DBL_MAX_10_EXP + DBL_DIG;
59 spin->setDecimals(decimalPrecision);
60 spin->setButtonMinimum(suggestedMinForCoord[whichCoord]);
61 spin->setButtonMaximum(suggestedMaxForCoord[whichCoord]);
62 spin->setSingleStep(suggestedStepForCoord[whichCoord]);
63 spin->setValue(initialValue);
64 return spin;
65}
66
70QSlider* VuoInputEditorTransform::initSlider(coord whichCoord, QDialog& dialog, double initialValue)
71{
72 double min = suggestedMinForCoord[whichCoord];
73 double max = suggestedMaxForCoord[whichCoord];
74 double step = suggestedStepForCoord[whichCoord];
75 double range = max - min;
76
77 QSlider* slider = new QSlider(&dialog);
78 // slider->setAttribute(Qt::WA_MacSmallSize);
79 slider->setOrientation(Qt::Horizontal);
80 slider->setFocusPolicy(Qt::NoFocus);
81 slider->setMinimum(0);
82 slider->setMaximum(slider->width());
83 slider->setSingleStep( fmax(1, step * (slider->maximum() - slider->minimum()) / range ) );
84 slider->setValue( VuoDoubleSpinBox::doubleToSlider(slider->minimum(), slider->maximum(), min, max, initialValue) );
85
86 return slider;
87}
88
93void VuoInputEditorTransform::setUpDialog(QDialog& dialog, json_object *originalValue, json_object *details)
94{
95 currentTransform = VuoTransform_makeFromJson(originalValue);
96
97 bool tabCycleForward = true;
98
99 VuoTransform default_min = VuoTransform_makeEuler( VuoPoint3d_make(-1, -1, -1), VuoPoint3d_make(-180. * DEG2RAD, -180. * DEG2RAD, -180. * DEG2RAD), VuoPoint3d_make(0, 0, 0));
100 VuoTransform default_max = VuoTransform_makeEuler( VuoPoint3d_make(1, 1, 1), VuoPoint3d_make(180. * DEG2RAD, 180. * DEG2RAD, 180. * DEG2RAD), VuoPoint3d_make(2, 2, 2));
101 VuoTransform default_step = VuoTransform_makeEuler( VuoPoint3d_make(.01, .01, .01), VuoPoint3d_make(5. * DEG2RAD, 5. * DEG2RAD, 5. * DEG2RAD), VuoPoint3d_make(.01, .01, .01));
102
103 setCoordMap(&suggestedMinForCoord, default_min);
104 setCoordMap(&suggestedMaxForCoord, default_max);
105 setCoordMap(&suggestedStepForCoord, default_step);
106
107 // Parse supported port annotations from the port's "details" JSON object:
108 if (details)
109 {
110 json_object *forwardTabTraversal = NULL;
111
112 if (json_object_object_get_ex(details, "forwardTabTraversal", &forwardTabTraversal))
113 tabCycleForward = json_object_get_boolean(forwardTabTraversal);
114
115 // "suggestedMin"
116 json_object *suggestedMinValue = NULL;
117 if (json_object_object_get_ex(details, "suggestedMin", &suggestedMinValue))
118 {
119 VuoTransform minTransform = VuoTransform_makeFromJson(suggestedMinValue);
120 setCoordMap(&suggestedMinForCoord, minTransform);
121 }
122
123 // "suggestedMax"
124 json_object *suggestedMaxValue = NULL;
125 if (json_object_object_get_ex(details, "suggestedMax", &suggestedMaxValue))
126 {
127 VuoTransform maxTransform = VuoTransform_makeFromJson(suggestedMaxValue);
128 setCoordMap(&suggestedMaxForCoord, maxTransform);
129 }
130
131 // "suggestedStep"
132 json_object *suggestedStepValue = NULL;
133 if (json_object_object_get_ex(details, "suggestedStep", &suggestedStepValue))
134 {
135 VuoTransform stepTransform = VuoTransform_makeFromJson(suggestedStepValue);
136 setCoordMap(&suggestedStepForCoord, stepTransform);
137 }
138 }
139
140 // x-translation
141 spinboxForCoord[xTranslation] = initSpinBox(xTranslation, dialog, currentTransform.translation.x);
142 sliderForCoord[xTranslation] = initSlider(xTranslation, dialog, currentTransform.translation.x);
143 connect(sliderForCoord[xTranslation], &QSlider::valueChanged, this, &VuoInputEditorTransform::onSliderUpdate);
144 connect(spinboxForCoord[xTranslation], static_cast<void (QDoubleSpinBox::*)(const QString &)>(&QDoubleSpinBox::valueChanged), this, &VuoInputEditorTransform::onSpinboxUpdate);
145
146 // y-translation
147 spinboxForCoord[yTranslation] = initSpinBox(yTranslation, dialog, currentTransform.translation.y);
148 sliderForCoord[yTranslation] = initSlider(yTranslation, dialog, currentTransform.translation.y);
149 connect(sliderForCoord[yTranslation], &QSlider::valueChanged, this, &VuoInputEditorTransform::onSliderUpdate);
150 connect(spinboxForCoord[yTranslation], static_cast<void (QDoubleSpinBox::*)(const QString &)>(&QDoubleSpinBox::valueChanged), this, &VuoInputEditorTransform::onSpinboxUpdate);
151
152 // z-translation
153 spinboxForCoord[zTranslation] = initSpinBox(zTranslation, dialog, currentTransform.translation.z);
154 sliderForCoord[zTranslation] = initSlider(zTranslation, dialog, currentTransform.translation.z);
155 connect(sliderForCoord[zTranslation], &QSlider::valueChanged, this, &VuoInputEditorTransform::onSliderUpdate);
156 connect(spinboxForCoord[zTranslation], static_cast<void (QDoubleSpinBox::*)(const QString &)>(&QDoubleSpinBox::valueChanged), this, &VuoInputEditorTransform::onSpinboxUpdate);
157
158 // x-rotation
159 VuoPoint3d rotation = VuoTransform_getEuler(currentTransform);
160 spinboxForCoord[xRotation] = initSpinBox(xRotation, dialog, rotation.x * RAD2DEG);
161 sliderForCoord[xRotation] = initSlider(xRotation, dialog, rotation.x * RAD2DEG);
162 connect(sliderForCoord[xRotation], &QSlider::valueChanged, this, &VuoInputEditorTransform::onSliderUpdate);
163 connect(spinboxForCoord[xRotation], static_cast<void (QDoubleSpinBox::*)(const QString &)>(&QDoubleSpinBox::valueChanged), this, &VuoInputEditorTransform::onSpinboxUpdate);
164
165 // y-rotation
166 spinboxForCoord[yRotation] = initSpinBox(yRotation, dialog, rotation.y * RAD2DEG);
167 sliderForCoord[yRotation] = initSlider(yRotation, dialog, rotation.y * RAD2DEG);
168 connect(sliderForCoord[yRotation], &QSlider::valueChanged, this, &VuoInputEditorTransform::onSliderUpdate);
169 connect(spinboxForCoord[yRotation], static_cast<void (QDoubleSpinBox::*)(const QString &)>(&QDoubleSpinBox::valueChanged), this, &VuoInputEditorTransform::onSpinboxUpdate);
170
171 // z-rotation
172 spinboxForCoord[zRotation] = initSpinBox(zRotation, dialog, rotation.z * RAD2DEG);
173 sliderForCoord[zRotation] = initSlider(zRotation, dialog, rotation.z * RAD2DEG);
174 connect(sliderForCoord[zRotation], &QSlider::valueChanged, this, &VuoInputEditorTransform::onSliderUpdate);
175 connect(spinboxForCoord[zRotation], static_cast<void (QDoubleSpinBox::*)(const QString &)>(&QDoubleSpinBox::valueChanged), this, &VuoInputEditorTransform::onSpinboxUpdate);
176
177 // x-scale
178 spinboxForCoord[xScale] = initSpinBox(xScale, dialog, currentTransform.scale.x);
179 sliderForCoord[xScale] = initSlider(xScale, dialog, currentTransform.scale.x);
180 connect(sliderForCoord[xScale], &QSlider::valueChanged, this, &VuoInputEditorTransform::onSliderUpdate);
181 connect(spinboxForCoord[xScale], static_cast<void (QDoubleSpinBox::*)(const QString &)>(&QDoubleSpinBox::valueChanged), this, &VuoInputEditorTransform::onSpinboxUpdate);
182
183 // y-scale
184 spinboxForCoord[yScale] = initSpinBox(yScale, dialog, currentTransform.scale.y);
185 sliderForCoord[yScale] = initSlider(yScale, dialog, currentTransform.scale.y);
186 connect(sliderForCoord[yScale], &QSlider::valueChanged, this, &VuoInputEditorTransform::onSliderUpdate);
187 connect(spinboxForCoord[yScale], static_cast<void (QDoubleSpinBox::*)(const QString &)>(&QDoubleSpinBox::valueChanged), this, &VuoInputEditorTransform::onSpinboxUpdate);
188
189 // z-scale
190 spinboxForCoord[zScale] = initSpinBox(zScale, dialog, currentTransform.scale.z);
191 sliderForCoord[zScale] = initSlider(zScale, dialog, currentTransform.scale.z);
192 connect(sliderForCoord[zScale], &QSlider::valueChanged, this, &VuoInputEditorTransform::onSliderUpdate);
193 connect(spinboxForCoord[zScale], static_cast<void (QDoubleSpinBox::*)(const QString &)>(&QDoubleSpinBox::valueChanged), this, &VuoInputEditorTransform::onSpinboxUpdate);
194
195 // layout dialog
196 QGridLayout* layout = new QGridLayout;
197 dialog.setLayout(layout);
198
199 // left, top, right, bottom
200 // when showing sliders, add a little extra margin on the bottom since QSlider takes up the
201 // entire vertical spacing
202 layout->setContentsMargins(4, 4, 16, 4);
203 layout->setSpacing(8);
204
205 unsigned int row = 0;
206
207 layout->addWidget(makeLabel("X"), row, 1, Qt::AlignHCenter);
208 layout->addWidget(makeLabel("Y"), row, 2, Qt::AlignHCenter);
209 layout->addWidget(makeLabel("Z"), row, 3, Qt::AlignHCenter);
210
211 row++;
212
213 layout->addWidget(makeLabel("Translation"), row, 0);
214 layout->addWidget( spinboxForCoord[xTranslation], row, 1);
215 layout->addWidget( spinboxForCoord[yTranslation], row, 2);
216 layout->addWidget( spinboxForCoord[zTranslation], row, 3);
217
218 row++;
219
220 layout->addWidget( sliderForCoord[xTranslation], row, 1);
221 layout->addWidget( sliderForCoord[yTranslation], row, 2);
222 layout->addWidget( sliderForCoord[zTranslation], row, 3);
223
224 row++;
225
226 layout->addWidget(makeLabel("Rotation"), row, 0);
227 layout->addWidget( spinboxForCoord[xRotation], row, 1);
228 layout->addWidget( spinboxForCoord[yRotation], row, 2);
229 layout->addWidget( spinboxForCoord[zRotation], row, 3);
230
231 row++;
232
233 layout->addWidget( sliderForCoord[xRotation], row, 1);
234 layout->addWidget( sliderForCoord[yRotation], row, 2);
235 layout->addWidget( sliderForCoord[zRotation], row, 3);
236
237 row++;
238
239 row++;
240
241 layout->addWidget(makeLabel("Scale"), row, 0);
242 layout->addWidget( spinboxForCoord[xScale], row, 1);
243 layout->addWidget( spinboxForCoord[yScale], row, 2);
244 layout->addWidget( spinboxForCoord[zScale], row, 3);
245
246 row++;
247
248 layout->addWidget( sliderForCoord[xScale], row, 1);
249 layout->addWidget( sliderForCoord[yScale], row, 2);
250 layout->addWidget( sliderForCoord[zScale], row, 3);
251
252 row++;
253
254 dialog.setMaximumWidth(1);
255 dialog.setMaximumHeight(1);
256
257 dialog.adjustSize();
258
259 // Layout details
260 setFirstWidgetInTabOrder(spinboxForCoord[xTranslation]);
261 setLastWidgetInTabOrder(spinboxForCoord[zScale]);
262
263 // Return focus to the topmost line edit by default, or to the bottommost
264 // line edit if tab-cycling backwards.
265 // To be handled properly for https://b33p.net/kosada/node/6365 .
266 (tabCycleForward ? spinboxForCoord[xTranslation] : spinboxForCoord[zScale])->setFocus();
267 (tabCycleForward ? spinboxForCoord[xTranslation] : spinboxForCoord[zScale])->selectAll();
268}
269
274{
275 return VuoTransform_getJson(currentTransform);
276}
277
278void VuoInputEditorTransform::setTransformProperty(coord whichCoord, double value)
279{
280 if(whichCoord == xTranslation)
281 currentTransform.translation.x = value;
282 else if(whichCoord == yTranslation)
283 currentTransform.translation.y = value;
284 else if(whichCoord == zTranslation)
285 currentTransform.translation.z = value;
286 else if(whichCoord == xRotation || whichCoord == yRotation || whichCoord == zRotation)
287 {
288 VuoPoint3d euler = VuoTransform_getEuler(currentTransform);
289
290 if(whichCoord == xRotation)
291 euler.x = value * DEG2RAD;
292 else if(whichCoord == yRotation)
293 euler.y = value * DEG2RAD;
294 else if(whichCoord == zRotation)
295 euler.z = value * DEG2RAD;
296
297 currentTransform = VuoTransform_makeEuler(currentTransform.translation, euler, currentTransform.scale);
298 }
299 else if(whichCoord == xScale)
300 currentTransform.scale.x = value;
301 else if(whichCoord == yScale)
302 currentTransform.scale.y = value;
303 else if(whichCoord == zScale)
304 currentTransform.scale.z = value;
305}
306
307VuoInputEditorTransform::coord VuoInputEditorTransform::getCoordFromQObject(QObject* sender)
308{
309 if( sender == sliderForCoord[xTranslation] || sender == spinboxForCoord[xTranslation] )
310 return xTranslation;
311 else if( sender == sliderForCoord[yTranslation] || sender == spinboxForCoord[yTranslation] )
312 return yTranslation;
313 else if( sender == sliderForCoord[zTranslation] || sender == spinboxForCoord[zTranslation] )
314 return zTranslation;
315 else if( sender == sliderForCoord[xRotation] || sender == spinboxForCoord[xRotation] )
316 return xRotation;
317 else if( sender == sliderForCoord[yRotation] || sender == spinboxForCoord[yRotation] )
318 return yRotation;
319 else if( sender == sliderForCoord[zRotation] || sender == spinboxForCoord[zRotation] )
320 return zRotation;
321 else if( sender == sliderForCoord[xScale] || sender == spinboxForCoord[xScale] )
322 return xScale;
323 else if( sender == sliderForCoord[yScale] || sender == spinboxForCoord[yScale] )
324 return yScale;
325 else if( sender == sliderForCoord[zScale] || sender == spinboxForCoord[zScale] )
326 return zScale;
327
328 return xTranslation;
329}
330
334void VuoInputEditorTransform::onSliderUpdate(int sliderValue)
335{
336 coord whichCoord = getCoordFromQObject(QObject::sender());
337 QSlider *targetSlider = sliderForCoord[whichCoord];
338 double value = VuoDoubleSpinBox::sliderToDouble(targetSlider->minimum(), targetSlider->maximum(), suggestedMinForCoord[whichCoord], suggestedMaxForCoord[whichCoord], sliderValue);
339 setTransformProperty(whichCoord, value);
340
341 // disconnect before setting spinbox value otherwise onSpinboxUpdate is called and the whole thing just cycles
342 disconnect(spinboxForCoord[whichCoord], static_cast<void (QDoubleSpinBox::*)(const QString &)>(&QDoubleSpinBox::valueChanged), this, &VuoInputEditorTransform::onSpinboxUpdate);
343 spinboxForCoord[whichCoord]->setValue( value );
344 connect(spinboxForCoord[whichCoord], static_cast<void (QDoubleSpinBox::*)(const QString &)>(&QDoubleSpinBox::valueChanged), this, &VuoInputEditorTransform::onSpinboxUpdate);
345
346 spinboxForCoord[whichCoord]->setFocus();
347 spinboxForCoord[whichCoord]->selectAll();
348
350}
351
355void VuoInputEditorTransform::onSpinboxUpdate(QString spinboxValue)
356{
357 coord whichCoord = getCoordFromQObject(QObject::sender());
358
359 double value = QLocale().toDouble(spinboxValue);
360 setTransformProperty(whichCoord, value);
361
362 QSlider *targetSlider = sliderForCoord[whichCoord];
363 int sliderValue = VuoDoubleSpinBox::doubleToSlider(targetSlider->minimum(), targetSlider->maximum(), suggestedMinForCoord[whichCoord], suggestedMaxForCoord[whichCoord], value);
364
365 disconnect(targetSlider, &QSlider::valueChanged, this, &VuoInputEditorTransform::onSliderUpdate);
366 targetSlider->setValue( sliderValue );
367 connect(targetSlider, &QSlider::valueChanged, this, &VuoInputEditorTransform::onSliderUpdate);
368
370}