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