Vuo  2.0.0
VuoInputEditorTransform.cc
Go to the documentation of this file.
1 
11 
12 #define DEG2RAD 0.0174532925f
13 #define RAD2DEG 57.295779513f
14 
15 
19 {
20  return new VuoInputEditorTransform();
21 }
22 
26 QLabel* makeLabel(const char* text)
27 {
28  QLabel* label = new QLabel(text);
29  label->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
30  return label;
31 }
32 
36 void 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 
55 VuoDoubleSpinBox* 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 
70 QSlider* 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 
93 void 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 
278 void 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 
307 VuoInputEditorTransform::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 
334 void 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 
349  emit valueChanged( getAcceptedValue() );
350 }
351 
355 void 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 
369  emit valueChanged( getAcceptedValue() );
370 }