Vuo  2.4.0
VuoInputEditorTransform2d.cc
Go to the documentation of this file.
1
11
12extern "C"
13{
14 #include "VuoTransform2d.h"
15 #include "VuoReal.h"
16}
17
18#define DEG2RAD 0.0174532925f
19#define RAD2DEG 57.295779513f
20
25{
26 return new VuoInputEditorTransform2d();
27}
28
32QLabel* makeLabel(const char* text)
33{
34 QLabel* label = new QLabel(text);
35 label->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
36 return label;
37}
38
42void 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
54VuoDoubleSpinBox* 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
71QSlider* 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
94void 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
253void 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
267VuoInputEditorTransform2d::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
286void 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
302}
303
307void 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
322}