1 /* ***** BEGIN LICENSE BLOCK *****
2 * This file is part of openfx-supportext <https://github.com/devernay/openfx-supportext>,
3 * Copyright (C) 2013-2016 INRIA
4 *
5 * openfx-supportext is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * openfx-supportext is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with openfx-supportext. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>
17 * ***** END LICENSE BLOCK ***** */
18
19 /*
20 * OFX TransformInteractCustom.
21 *
22 * Based on ofxTransformInteract from openfx-supportext, modified for OCL.ofx
23 *
24 */
25
26 #include "ofxsTransformInteractCustom.h"
27
28 #include <memory>
29 #include <cmath>
30 #include <cfloat> // DBL_MAX
31 #include <algorithm>
32
33 #ifdef __APPLE__
34 #include <OpenGL/gl.h>
35 #else
36 #include <GL/gl.h>
37 #endif
38
39 #include "ofxsMatrix2D.h"
40 #include "ofxsTransform3x3.h"
41
42 #define SCALE_MAX 10000.
43
44 #define CIRCLE_RADIUS_BASE 30.
45 #define CIRCLE_RADIUS_MIN 15.
46 #define CIRCLE_RADIUS_MAX 300.
47 #define POINT_SIZE 7.
48 #define ELLIPSE_N_POINTS 50.
49
50 namespace OFX {
51 /// add Transform params. page and group are optional
52 void
ofxsTransformDescribeParams(OFX::ImageEffectDescriptor & desc,OFX::PageParamDescriptor * page,OFX::GroupParamDescriptor * group,bool isOpen,bool oldParams,bool noTranslate,bool uniform,double rotateDefault)53 ofxsTransformDescribeParams(OFX::ImageEffectDescriptor &desc,
54 OFX::PageParamDescriptor *page,
55 OFX::GroupParamDescriptor *group,
56 bool isOpen,
57 bool oldParams,
58 bool noTranslate,
59 bool uniform,
60 double rotateDefault)
61 {
62 // translate
63 if (!noTranslate) {
64 Double2DParamDescriptor* param = desc.defineDouble2DParam(oldParams ? kParamTransformTranslateOld : kParamTransformTranslate);
65 param->setLabel(kParamTransformTranslateLabel);
66 //param->setDoubleType(eDoubleTypeNormalisedXY); // deprecated in OpenFX 1.2
67 param->setDoubleType(eDoubleTypeXYAbsolute);
68 param->setDefaultCoordinateSystem(eCoordinatesNormalised);
69 //param->setDimensionLabels("x","y");
70 param->setDefault(0, 0);
71 param->setRange(-DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
72 param->setDisplayRange(-10000, -10000, 10000, 10000); // Resolve requires display range or values are clamped to (-1,1)
73 param->setIncrement(10.);
74 if (group) {
75 param->setParent(*group);
76 }
77 if (page) {
78 page->addChild(*param);
79 }
80 }
81
82 // rotate
83 {
84 DoubleParamDescriptor* param = desc.defineDoubleParam(oldParams ? kParamTransformRotateOld : kParamTransformRotate);
85 param->setLabel(kParamTransformRotateLabel);
86 param->setDoubleType(eDoubleTypeAngle);
87 param->setDefault(rotateDefault);
88 param->setRange(-DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
89 param->setDisplayRange(-180, 180);
90 param->setIncrement(0.1);
91 if (group) {
92 param->setParent(*group);
93 }
94 if (page) {
95 page->addChild(*param);
96 }
97 }
98
99 // scale
100 {
101 Double2DParamDescriptor* param = desc.defineDouble2DParam(oldParams ? kParamTransformScaleOld : kParamTransformScale);
102 param->setLabel(kParamTransformScaleLabel);
103 param->setDoubleType(eDoubleTypeScale);
104 //param->setDimensionLabels("w","h");
105 param->setDefault(1, 1);
106 param->setRange(-SCALE_MAX, -SCALE_MAX, SCALE_MAX, SCALE_MAX);
107 param->setDisplayRange(0.1, 0.1, 10, 10);
108 param->setIncrement(0.01);
109 param->setLayoutHint(OFX::eLayoutHintNoNewLine, 1);
110 if (group) {
111 param->setParent(*group);
112 }
113 if (page) {
114 page->addChild(*param);
115 }
116 }
117
118 // scaleUniform
119 {
120 BooleanParamDescriptor* param = desc.defineBooleanParam(oldParams ? kParamTransformScaleUniformOld : kParamTransformScaleUniform);
121 param->setLabel(kParamTransformScaleUniformLabel);
122 param->setHint(kParamTransformScaleUniformHint);
123 param->setDefault(uniform);
124 param->setAnimates(true);
125 if (group) {
126 param->setParent(*group);
127 }
128 if (page) {
129 page->addChild(*param);
130 }
131 }
132
133 // center
134 {
135 Double2DParamDescriptor* param = desc.defineDouble2DParam(oldParams ? kParamTransformCenterOld : kParamTransformCenter);
136 param->setLabel(kParamTransformCenterLabel);
137 //param->setDoubleType(eDoubleTypeNormalisedXY); // deprecated in OpenFX 1.2
138 //param->setDimensionLabels("x","y");
139 param->setDoubleType(eDoubleTypeXYAbsolute);
140 param->setDefaultCoordinateSystem(eCoordinatesNormalised);
141 param->setDefault(0.5, 0.5);
142 param->setRange(-DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX); // Resolve requires range and display range or values are clamped to (-1,1)
143 param->setDisplayRange(-10000, -10000, 10000, 10000); // Resolve requires display range or values are clamped to (-1,1)
144 param->setIncrement(1.);
145 param->setLayoutHint(eLayoutHintNoNewLine, 1);
146 if (group) {
147 param->setParent(*group);
148 }
149 if (page) {
150 page->addChild(*param);
151 }
152 }
153
154 // resetcenter
155 {
156 PushButtonParamDescriptor* param = desc.definePushButtonParam(oldParams ? kParamTransformResetCenterOld : kParamTransformResetCenter);
157 param->setLabel(kParamTransformResetCenterLabel);
158 param->setHint(kParamTransformResetCenterHint);
159 if (group) {
160 param->setParent(*group);
161 }
162 if (page) {
163 page->addChild(*param);
164 }
165 }
166
167 // interactOpen
168 {
169 BooleanParamDescriptor* param = desc.defineBooleanParam(kParamTransformInteractCustomOpen);
170 param->setLabel(kParamTransformInteractCustomOpenLabel);
171 param->setHint(kParamTransformInteractCustomOpenHint);
172 param->setDefault(isOpen); // open by default
173 param->setIsSecret(true); // secret by default, but this can be changed for specific hosts
174 param->setAnimates(false);
175 if (group) {
176 param->setParent(*group);
177 }
178 if (page) {
179 page->addChild(*param);
180 }
181 }
182
183 // interactive
184 {
185 BooleanParamDescriptor* param = desc.defineBooleanParam(oldParams ? kParamTransformInteractCustomiveOld : kParamTransformInteractCustomive);
186 param->setLabel(kParamTransformInteractCustomiveLabel);
187 param->setHint(kParamTransformInteractCustomiveHint);
188 param->setDefault(true);
189 param->setEvaluateOnChange(false);
190 param->setLayoutHint(OFX::eLayoutHintDivider);
191 if (group) {
192 param->setParent(*group);
193 }
194 if (page) {
195 page->addChild(*param);
196 }
197 }
198 } // ofxsTransformDescribeParams
199
200 ////////////////////////////////////////////////////////////////////////////////
201 // stuff for the interact
202
TransformInteractCustomHelper(OFX::ImageEffect * effect,OFX::Interact * interact,bool oldParams)203 TransformInteractCustomHelper::TransformInteractCustomHelper(OFX::ImageEffect* effect,
204 OFX::Interact* interact,
205 bool oldParams)
206 : _drawState(eInActive)
207 , _mouseState(eReleased)
208 , _modifierStateCtrl(0)
209 , _modifierStateShift(0)
210 , _orientation(eOrientationAllDirections)
211 , _effect(effect)
212 , _interact(interact)
213 , _lastMousePos()
214 , _scaleUniformDrag(0)
215 , _rotateDrag(0)
216 , _invertedDrag(0)
217 , _interactiveDrag(false)
218 , _translate(0)
219 , _rotate(0)
220 , _scale(0)
221 , _scaleUniform(0)
222 , _center(0)
223 , _invert(0)
224 , _interactOpen(0)
225 , _interactive(0)
226 {
227 assert(_effect && _interact);
228 _lastMousePos.x = _lastMousePos.y = 0.;
229 // NON-GENERIC
230 if (oldParams) {
231 if ( _effect->paramExists(kParamTransformTranslateOld) ) {
232 _translate = _effect->fetchDouble2DParam(kParamTransformTranslateOld);
233 assert(_translate);
234 }
235 _rotate = _effect->fetchDoubleParam(kParamTransformRotateOld);
236 _scale = _effect->fetchDouble2DParam(kParamTransformScaleOld);
237 _scaleUniform = _effect->fetchBooleanParam(kParamTransformScaleUniformOld);
238 _center = _effect->fetchDouble2DParam(kParamTransformCenterOld);
239 _interactive = _effect->fetchBooleanParam(kParamTransformInteractCustomiveOld);
240 } else {
241 if ( _effect->paramExists(kParamTransformTranslate) ) {
242 _translate = _effect->fetchDouble2DParam(kParamTransformTranslate);
243 assert(_translate);
244 }
245 _rotate = _effect->fetchDoubleParam(kParamTransformRotate);
246 _scale = _effect->fetchDouble2DParam(kParamTransformScale);
247 _scaleUniform = _effect->fetchBooleanParam(kParamTransformScaleUniform);
248 _center = _effect->fetchDouble2DParam(kParamTransformCenter);
249 _interactive = _effect->fetchBooleanParam(kParamTransformInteractCustomive);
250 }
251 _interactOpen = _effect->fetchBooleanParam(kParamTransformInteractCustomOpen);
252 if ( _effect->paramExists(kParamTransform3x3Invert) ) {
253 _invert = _effect->fetchBooleanParam(kParamTransform3x3Invert);
254 }
255 assert(_rotate && _scale && _scaleUniform && _center && _interactive);
256 if (_translate) {
257 _interact->addParamToSlaveTo(_translate);
258 }
259 if (_rotate) {
260 _interact->addParamToSlaveTo(_rotate);
261 }
262 if (_scale) {
263 _interact->addParamToSlaveTo(_scale);
264 }
265 if (_center) {
266 _interact->addParamToSlaveTo(_center);
267 }
268 if (_invert) {
269 _interact->addParamToSlaveTo(_invert);
270 }
271 if (!_translate) {
272 _modifierStateCtrl = 1;
273 }
274 _centerDrag.x = _centerDrag.y = 0.;
275 _translateDrag.x = _translateDrag.y = 0.;
276 _scaleParamDrag.x = _scaleParamDrag.y = 0.;
277 }
278
279 static void
getTargetCenter(const OfxPointD & center,const OfxPointD & translate,OfxPointD * targetCenter)280 getTargetCenter(const OfxPointD ¢er,
281 const OfxPointD &translate,
282 OfxPointD *targetCenter)
283 {
284 targetCenter->x = center.x + translate.x;
285 targetCenter->y = center.y + translate.y;
286 }
287
288 static void
getTargetRadius(const OfxPointD & scale,const OfxPointD & pixelScale,OfxPointD * targetRadius)289 getTargetRadius(const OfxPointD& scale,
290 const OfxPointD& pixelScale,
291 OfxPointD* targetRadius)
292 {
293 targetRadius->x = scale.x * CIRCLE_RADIUS_BASE;
294 targetRadius->y = scale.y * CIRCLE_RADIUS_BASE;
295 // don't draw too small. 15 pixels is the limit
296 if ( (std::fabs(targetRadius->x) < CIRCLE_RADIUS_MIN) && (std::fabs(targetRadius->y) < CIRCLE_RADIUS_MIN) ) {
297 targetRadius->x = targetRadius->x >= 0 ? CIRCLE_RADIUS_MIN : -CIRCLE_RADIUS_MIN;
298 targetRadius->y = targetRadius->y >= 0 ? CIRCLE_RADIUS_MIN : -CIRCLE_RADIUS_MIN;
299 } else if ( (std::fabs(targetRadius->x) > CIRCLE_RADIUS_MAX) && (std::fabs(targetRadius->y) > CIRCLE_RADIUS_MAX) ) {
300 targetRadius->x = targetRadius->x >= 0 ? CIRCLE_RADIUS_MAX : -CIRCLE_RADIUS_MAX;
301 targetRadius->y = targetRadius->y >= 0 ? CIRCLE_RADIUS_MAX : -CIRCLE_RADIUS_MAX;
302 } else {
303 if (std::fabs(targetRadius->x) < CIRCLE_RADIUS_MIN) {
304 if ( (targetRadius->x == 0.) && (targetRadius->y != 0.) ) {
305 targetRadius->y = targetRadius->y > 0 ? CIRCLE_RADIUS_MAX : -CIRCLE_RADIUS_MAX;
306 } else {
307 targetRadius->y *= std::fabs(CIRCLE_RADIUS_MIN / targetRadius->x);
308 }
309 targetRadius->x = targetRadius->x >= 0 ? CIRCLE_RADIUS_MIN : -CIRCLE_RADIUS_MIN;
310 }
311 if (std::fabs(targetRadius->x) > CIRCLE_RADIUS_MAX) {
312 targetRadius->y *= std::fabs(CIRCLE_RADIUS_MAX / targetRadius->x);
313 targetRadius->x = targetRadius->x > 0 ? CIRCLE_RADIUS_MAX : -CIRCLE_RADIUS_MAX;
314 }
315 if (std::fabs(targetRadius->y) < CIRCLE_RADIUS_MIN) {
316 if ( (targetRadius->y == 0.) && (targetRadius->x != 0.) ) {
317 targetRadius->x = targetRadius->x > 0 ? CIRCLE_RADIUS_MAX : -CIRCLE_RADIUS_MAX;
318 } else {
319 targetRadius->x *= std::fabs(CIRCLE_RADIUS_MIN / targetRadius->y);
320 }
321 targetRadius->y = targetRadius->y >= 0 ? CIRCLE_RADIUS_MIN : -CIRCLE_RADIUS_MIN;
322 }
323 if (std::fabs(targetRadius->y) > CIRCLE_RADIUS_MAX) {
324 targetRadius->x *= std::fabs(CIRCLE_RADIUS_MAX / targetRadius->x);
325 targetRadius->y = targetRadius->y > 0 ? CIRCLE_RADIUS_MAX : -CIRCLE_RADIUS_MAX;
326 }
327 }
328 // the circle axes are not aligned with the images axes, so we cannot use the x and y scales separately
329 double meanPixelScale = (pixelScale.x + pixelScale.y) / 2.;
330 targetRadius->x *= meanPixelScale;
331 targetRadius->y *= meanPixelScale;
332 }
333
334 static void
getTargetPoints(const OfxPointD & targetCenter,const OfxPointD & targetRadius,OfxPointD * left,OfxPointD * bottom,OfxPointD * top,OfxPointD * right)335 getTargetPoints(const OfxPointD& targetCenter,
336 const OfxPointD& targetRadius,
337 OfxPointD *left,
338 OfxPointD *bottom,
339 OfxPointD *top,
340 OfxPointD *right)
341 {
342 left->x = targetCenter.x - targetRadius.x;
343 left->y = targetCenter.y;
344 right->x = targetCenter.x + targetRadius.x;
345 right->y = targetCenter.y;
346 top->x = targetCenter.x;
347 top->y = targetCenter.y + targetRadius.y;
348 bottom->x = targetCenter.x;
349 bottom->y = targetCenter.y - targetRadius.y;
350 }
351
352 static void
drawSquare(const OfxRGBColourD & color,const OfxPointD & center,const OfxPointD & pixelScale,bool hovered,bool althovered,int l)353 drawSquare(const OfxRGBColourD& color,
354 const OfxPointD& center,
355 const OfxPointD& pixelScale,
356 bool hovered,
357 bool althovered,
358 int l)
359 {
360 // we are not axis-aligned
361 double meanPixelScale = (pixelScale.x + pixelScale.y) / 2.;
362
363 if (hovered) {
364 if (althovered) {
365 glColor3f(0.f * l, 1.f * l, 0.f * l);
366 } else {
367 glColor3f(1.f * l, 0.f * l, 0.f * l);
368 }
369 } else {
370 glColor3f( (float)color.r * l, (float)color.g * l, (float)color.b * l );
371 }
372 double halfWidth = (POINT_SIZE / 2.) * meanPixelScale;
373 double halfHeight = (POINT_SIZE / 2.) * meanPixelScale;
374 glPushMatrix();
375 glTranslated(center.x, center.y, 0.);
376 glBegin(GL_POLYGON);
377 glVertex2d(-halfWidth, -halfHeight); // bottom left
378 glVertex2d(-halfWidth, +halfHeight); // top left
379 glVertex2d(+halfWidth, +halfHeight); // bottom right
380 glVertex2d(+halfWidth, -halfHeight); // top right
381 glEnd();
382 glPopMatrix();
383 }
384
385 static void
drawEllipse(const OfxRGBColourD & color,const OfxPointD & center,const OfxPointD & targetRadius,bool hovered,int l)386 drawEllipse(const OfxRGBColourD& color,
387 const OfxPointD& center,
388 const OfxPointD& targetRadius,
389 bool hovered,
390 int l)
391 {
392 if (hovered) {
393 glColor3f(1.f * l, 0.f * l, 0.f * l);
394 } else {
395 glColor3f( (float)color.r * l, (float)color.g * l, (float)color.b * l );
396 }
397
398 glPushMatrix();
399 // center the oval at x_center, y_center
400 glTranslatef( (float)center.x, (float)center.y, 0.f );
401 // draw the oval using line segments
402 glBegin(GL_LINE_LOOP);
403 // we don't need to be pixel-perfect here, it's just an interact!
404 // 40 segments is enough.
405 for (int i = 0; i < 40; ++i) {
406 double theta = i * 2 * OFX::ofxsPi() / 40.;
407 glVertex2d( targetRadius.x * std::cos(theta), targetRadius.y * std::sin(theta) );
408 }
409 glEnd();
410
411 glPopMatrix();
412 }
413
414 static void
drawRotationBar(const OfxRGBColourD & color,const OfxPointD & pixelScale,double targetRadiusX,bool hovered,bool inverted,int l)415 drawRotationBar(const OfxRGBColourD& color,
416 const OfxPointD& pixelScale,
417 double targetRadiusX,
418 bool hovered,
419 bool inverted,
420 int l)
421 {
422 // we are not axis-aligned
423 double meanPixelScale = (pixelScale.x + pixelScale.y) / 2.;
424
425 if (hovered) {
426 glColor3f(1.f * l, 0.f * l, 0.f * l);
427 } else {
428 glColor3f(color.r * l, color.g * l, color.b * l);
429 }
430
431 double barExtra = 30. * meanPixelScale;
432 glBegin(GL_LINES);
433 glVertex2d(0., 0.);
434 glVertex2d(0. + targetRadiusX + barExtra, 0.);
435 glEnd();
436
437 if (hovered) {
438 double arrowCenterX = targetRadiusX + barExtra / 2.;
439
440 ///draw an arrow slightly bended. This is an arc of circle of radius 5 in X, and 10 in Y.
441 OfxPointD arrowRadius;
442 arrowRadius.x = 5. * meanPixelScale;
443 arrowRadius.y = 10. * meanPixelScale;
444
445 glPushMatrix();
446 // center the oval at x_center, y_center
447 glTranslatef( (float)arrowCenterX, 0.f, 0 );
448 // draw the oval using line segments
449 glBegin(GL_LINE_STRIP);
450 glVertex2d(0, arrowRadius.y);
451 glVertex2d(arrowRadius.x, 0.);
452 glVertex2d(0, -arrowRadius.y);
453 glEnd();
454
455
456 glBegin(GL_LINES);
457 ///draw the top head
458 glVertex2d(0., arrowRadius.y);
459 glVertex2d(0., arrowRadius.y - 5. * meanPixelScale);
460
461 glVertex2d(0., arrowRadius.y);
462 glVertex2d(4. * meanPixelScale, arrowRadius.y - 3. * meanPixelScale); // 5^2 = 3^2+4^2
463
464 ///draw the bottom head
465 glVertex2d(0., -arrowRadius.y);
466 glVertex2d(0., -arrowRadius.y + 5. * meanPixelScale);
467
468 glVertex2d(0., -arrowRadius.y);
469 glVertex2d(4. * meanPixelScale, -arrowRadius.y + 3. * meanPixelScale); // 5^2 = 3^2+4^2
470
471 glEnd();
472
473 glPopMatrix();
474 }
475 if (inverted) {
476 double arrowXPosition = targetRadiusX + barExtra * 1.5;
477 double arrowXHalfSize = 10 * meanPixelScale;
478 double arrowHeadOffsetX = 3 * meanPixelScale;
479 double arrowHeadOffsetY = 3 * meanPixelScale;
480
481 glPushMatrix();
482 glTranslatef( (float)arrowXPosition, 0, 0 );
483
484 glBegin(GL_LINES);
485 ///draw the central bar
486 glVertex2d(-arrowXHalfSize, 0.);
487 glVertex2d(+arrowXHalfSize, 0.);
488
489 ///left triangle
490 glVertex2d(-arrowXHalfSize, 0.);
491 glVertex2d(-arrowXHalfSize + arrowHeadOffsetX, arrowHeadOffsetY);
492
493 glVertex2d(-arrowXHalfSize, 0.);
494 glVertex2d(-arrowXHalfSize + arrowHeadOffsetX, -arrowHeadOffsetY);
495
496 ///right triangle
497 glVertex2d(+arrowXHalfSize, 0.);
498 glVertex2d(+arrowXHalfSize - arrowHeadOffsetX, arrowHeadOffsetY);
499
500 glVertex2d(+arrowXHalfSize, 0.);
501 glVertex2d(+arrowXHalfSize - arrowHeadOffsetX, -arrowHeadOffsetY);
502 glEnd();
503
504 glRotated(90., 0., 0., 1.);
505
506 glBegin(GL_LINES);
507 ///draw the central bar
508 glVertex2d(-arrowXHalfSize, 0.);
509 glVertex2d(+arrowXHalfSize, 0.);
510
511 ///left triangle
512 glVertex2d(-arrowXHalfSize, 0.);
513 glVertex2d(-arrowXHalfSize + arrowHeadOffsetX, arrowHeadOffsetY);
514
515 glVertex2d(-arrowXHalfSize, 0.);
516 glVertex2d(-arrowXHalfSize + arrowHeadOffsetX, -arrowHeadOffsetY);
517
518 ///right triangle
519 glVertex2d(+arrowXHalfSize, 0.);
520 glVertex2d(+arrowXHalfSize - arrowHeadOffsetX, arrowHeadOffsetY);
521
522 glVertex2d(+arrowXHalfSize, 0.);
523 glVertex2d(+arrowXHalfSize - arrowHeadOffsetX, -arrowHeadOffsetY);
524 glEnd();
525
526 glPopMatrix();
527 }
528 } // drawRotationBar
529
530 // draw the interact
531 bool
draw(const OFX::DrawArgs & args)532 TransformInteractCustomHelper::draw(const OFX::DrawArgs &args)
533 {
534 if ( !_interactOpen->getValueAtTime(args.time) ) {
535 return false;
536 }
537 const OfxPointD &pscale = args.pixelScale;
538 const double time = args.time;
539 OfxRGBColourD color = { 0.8, 0.8, 0.8 };
540 _interact->getSuggestedColour(color);
541 GLdouble projection[16];
542 glGetDoublev( GL_PROJECTION_MATRIX, projection);
543 GLint viewport[4];
544 glGetIntegerv(GL_VIEWPORT, viewport);
545 OfxPointD shadow; // how much to translate GL_PROJECTION to get exactly one pixel on screen
546 shadow.x = 2. / (projection[0] * viewport[2]);
547 shadow.y = 2. / (projection[5] * viewport[3]);
548
549 OfxPointD center = { 0., 0. };
550 OfxPointD translate = { 0., 0. };
551 OfxPointD scaleParam = { 1., 1. };
552 bool scaleUniform = false;
553 double rotate = 0.;
554 bool inverted = false;
555
556 if (_mouseState == eReleased) {
557 if (_center) {
558 _center->getValueAtTime(time, center.x, center.y);
559 }
560 if (_translate) {
561 _translate->getValueAtTime(time, translate.x, translate.y);
562 }
563 if (_scale) {
564 _scale->getValueAtTime(time, scaleParam.x, scaleParam.y);
565 }
566 if (_scaleUniform) {
567 _scaleUniform->getValueAtTime(time, scaleUniform);
568 }
569 if (_rotate) {
570 _rotate->getValueAtTime(time, rotate);
571 }
572 if (_invert) {
573 _invert->getValueAtTime(time, inverted);
574 }
575 } else {
576 center = _centerDrag;
577 translate = _translateDrag;
578 scaleParam = _scaleParamDrag;
579 scaleUniform = _scaleUniformDrag;
580 rotate = _rotateDrag;
581 inverted = _invertedDrag;
582 }
583
584 OfxPointD targetCenter;
585 getTargetCenter(center, translate, &targetCenter);
586
587 OfxPointD scale;
588 ofxsTransformGetScale(scaleParam, scaleUniform, &scale);
589
590 OfxPointD targetRadius;
591 getTargetRadius(scale, pscale, &targetRadius);
592
593 OfxPointD left, right, bottom, top;
594 getTargetPoints(targetCenter, targetRadius, &left, &bottom, &top, &right);
595
596 //glPushAttrib(GL_ALL_ATTRIB_BITS); // caller is responsible for protecting attribs
597
598 glDisable(GL_LINE_STIPPLE);
599 glEnable(GL_LINE_SMOOTH);
600 glDisable(GL_POINT_SMOOTH);
601 glEnable(GL_BLEND);
602 glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
603 glLineWidth(1.5f);
604 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
605
606 // Draw everything twice
607 // l = 0: shadow
608 // l = 1: drawing
609 for (int l = 0; l < 2; ++l) {
610 // shadow (uses GL_PROJECTION)
611 glMatrixMode(GL_PROJECTION);
612 int direction = (l == 0) ? 1 : -1;
613 // translate (1,-1) pixels
614 glTranslated(direction * shadow.x, -direction * shadow.y, 0);
615 glMatrixMode(GL_MODELVIEW); // Modelview should be used on Nuke
616
617 glColor3f(color.r * l, color.g * l, color.b * l);
618
619 glPushMatrix();
620 glTranslated(targetCenter.x, targetCenter.y, 0.);
621
622 glRotated(rotate, 0, 0., 1.);
623 drawRotationBar(color, pscale, targetRadius.x, _mouseState == eDraggingRotationBar || _drawState == eRotationBarHovered, inverted, l);
624 //glMultMatrixd(skewMatrix);
625 glTranslated(-targetCenter.x, -targetCenter.y, 0.);
626
627 drawEllipse(color, targetCenter, targetRadius, _mouseState == eDraggingCircle || _drawState == eCircleHovered, l);
628
629 drawSquare(color, targetCenter, pscale, _mouseState == eDraggingTranslation || _mouseState == eDraggingCenter || _drawState == eCenterPointHovered, (!_translate || _modifierStateCtrl), l);
630
631 glPopMatrix();
632 }
633 //glPopAttrib();
634
635 return true;
636 } // TransformInteractCustomHelper::draw
637
638 static bool
squareContains(const OFX::Point3D & pos,const OfxRectD & rect,double toleranceX=0.,double toleranceY=0.)639 squareContains(const OFX::Point3D& pos,
640 const OfxRectD& rect,
641 double toleranceX = 0.,
642 double toleranceY = 0.)
643 {
644 return ( pos.x >= (rect.x1 - toleranceX) && pos.x < (rect.x2 + toleranceX)
645 && pos.y >= (rect.y1 - toleranceY) && pos.y < (rect.y2 + toleranceY) );
646 }
647
648 static bool
isOnEllipseBorder(const OFX::Point3D & pos,const OfxPointD & targetRadius,const OfxPointD & targetCenter,double epsilon=0.1)649 isOnEllipseBorder(const OFX::Point3D& pos,
650 const OfxPointD& targetRadius,
651 const OfxPointD& targetCenter,
652 double epsilon = 0.1)
653 {
654 double v = ( (pos.x - targetCenter.x) * (pos.x - targetCenter.x) / (targetRadius.x * targetRadius.x) +
655 (pos.y - targetCenter.y) * (pos.y - targetCenter.y) / (targetRadius.y * targetRadius.y) );
656
657 if ( ( v <= (1. + epsilon) ) && ( v >= (1. - epsilon) ) ) {
658 return true;
659 }
660
661 return false;
662 }
663
664 static bool
isOnRotationBar(const OFX::Point3D & pos,double targetRadiusX,const OfxPointD & center,const OfxPointD & pixelScale,double tolerance)665 isOnRotationBar(const OFX::Point3D& pos,
666 double targetRadiusX,
667 const OfxPointD& center,
668 const OfxPointD& pixelScale,
669 double tolerance)
670 {
671 // we are not axis-aligned
672 double meanPixelScale = (pixelScale.x + pixelScale.y) / 2.;
673 double barExtra = 30. * meanPixelScale;
674
675 if ( ( pos.x >= (center.x - tolerance) ) && ( pos.x <= (center.x + targetRadiusX + barExtra + tolerance) ) &&
676 ( pos.y >= (center.y - tolerance) ) && ( pos.y <= (center.y + tolerance) ) ) {
677 return true;
678 }
679
680 return false;
681 }
682
683 static OfxRectD
rectFromCenterPoint(const OfxPointD & center,const OfxPointD & pixelScale)684 rectFromCenterPoint(const OfxPointD& center,
685 const OfxPointD& pixelScale)
686 {
687 // we are not axis-aligned
688 double meanPixelScale = (pixelScale.x + pixelScale.y) / 2.;
689 OfxRectD ret;
690
691 ret.x1 = center.x - (POINT_SIZE / 2.) * meanPixelScale;
692 ret.x2 = center.x + (POINT_SIZE / 2.) * meanPixelScale;
693 ret.y1 = center.y - (POINT_SIZE / 2.) * meanPixelScale;
694 ret.y2 = center.y + (POINT_SIZE / 2.) * meanPixelScale;
695
696 return ret;
697 }
698
699 // round to the closest int, 1/10 int, etc
700 // this make parameter editing easier
701 // pscale is args.pixelScale.x / args.renderScale.x;
702 // pscale10 is the power of 10 below pscale
703 static double
fround(double val,double pscale)704 fround(double val,
705 double pscale)
706 {
707 double pscale10 = std::pow( 10., std::floor( std::log10(pscale) ) );
708
709 return pscale10 * std::floor(val / pscale10 + 0.5);
710 }
711
712 // overridden functions from OFX::Interact to do things
713 bool
penMotion(const OFX::PenArgs & args)714 TransformInteractCustomHelper::penMotion(const OFX::PenArgs &args)
715 {
716 if ( !_interactOpen->getValueAtTime(args.time) ) {
717 return false;
718 }
719 const OfxPointD &pscale = args.pixelScale;
720 const double time = args.time;
721 OfxPointD center = { 0., 0. };
722 OfxPointD translate = { 0., 0. };
723 OfxPointD scaleParam = { 1., 1. };
724 bool scaleUniform = false;
725 double rotate = 0.;
726 bool inverted = false;
727
728 if (_mouseState == eReleased) {
729 if (_center) {
730 _center->getValueAtTime(time, center.x, center.y);
731 }
732 if (_translate) {
733 _translate->getValueAtTime(time, translate.x, translate.y);
734 }
735 if (_scale) {
736 _scale->getValueAtTime(time, scaleParam.x, scaleParam.y);
737 }
738 if (_scaleUniform) {
739 _scaleUniform->getValueAtTime(time, scaleUniform);
740 }
741 if (_rotate) {
742 _rotate->getValueAtTime(time, rotate);
743 }
744 if (_invert) {
745 _invert->getValueAtTime(time, inverted);
746 }
747 } else {
748 center = _centerDrag;
749 translate = _translateDrag;
750 scaleParam = _scaleParamDrag;
751 scaleUniform = _scaleUniformDrag;
752 rotate = _rotateDrag;
753 inverted = _invertedDrag;
754 }
755
756 bool didSomething = false;
757 bool centerChanged = false;
758 bool translateChanged = false;
759 bool scaleChanged = false;
760 bool rotateChanged = false;
761
762 OfxPointD targetCenter;
763 getTargetCenter(center, translate, &targetCenter);
764
765 OfxPointD scale;
766 ofxsTransformGetScale(scaleParam, scaleUniform, &scale);
767
768 OfxPointD targetRadius;
769 getTargetRadius(scale, pscale, &targetRadius);
770
771 OfxPointD left, right, bottom, top;
772 getTargetPoints(targetCenter, targetRadius, &left, &bottom, &top, &right);
773
774 OfxRectD centerPoint = rectFromCenterPoint(targetCenter, pscale);
775 OfxRectD leftPoint = rectFromCenterPoint(left, pscale);
776 OfxRectD rightPoint = rectFromCenterPoint(right, pscale);
777 OfxRectD topPoint = rectFromCenterPoint(top, pscale);
778 OfxRectD bottomPoint = rectFromCenterPoint(bottom, pscale);
779
780
781 //double dx = args.penPosition.x - _lastMousePos.x;
782 //double dy = args.penPosition.y - _lastMousePos.y;
783 double rot = OFX::ofxsToRadians(rotate);
784 OFX::Point3D penPos, prevPenPos, rotationPos, transformedPos, previousPos, currentPos;
785 penPos.x = args.penPosition.x;
786 penPos.y = args.penPosition.y;
787 penPos.z = 1.;
788 prevPenPos.x = _lastMousePos.x;
789 prevPenPos.y = _lastMousePos.y;
790 prevPenPos.z = 1.;
791
792 OFX::Matrix3x3 rotation, transform, transformscale;
793 ////for the rotation bar/translation/center dragging we dont use the same transform, we don't want to undo the rotation transform
794 if ( (_mouseState != eDraggingTranslation) && (_mouseState != eDraggingCenter) ) {
795 ///undo skew + rotation to the current position
796 rotation = OFX::ofxsMatInverseTransformCanonical(0., 0., 1., 1., 0., 0., false, rot, targetCenter.x, targetCenter.y);
797 transform = OFX::ofxsMatInverseTransformCanonical(0., 0., 1., 1., 0, 0, false, rot, targetCenter.x, targetCenter.y);
798 transformscale = OFX::ofxsMatInverseTransformCanonical(0., 0., scale.x, scale.y, 0, 0, false, rot, targetCenter.x, targetCenter.y);
799 } else {
800 rotation = OFX::ofxsMatInverseTransformCanonical(0., 0., 1., 1., 0., 0., false, 0., targetCenter.x, targetCenter.y);
801 transform = OFX::ofxsMatInverseTransformCanonical(0., 0., 1., 1., 0, 0, false, 0., targetCenter.x, targetCenter.y);
802 transformscale = OFX::ofxsMatInverseTransformCanonical(0., 0., scale.x, scale.y, 0, 0, false, 0., targetCenter.x, targetCenter.y);
803 }
804
805 rotationPos = rotation * penPos;
806 if (rotationPos.z != 0) {
807 rotationPos.x /= rotationPos.z;
808 rotationPos.y /= rotationPos.z;
809 }
810
811 transformedPos = transform * penPos;
812 if (transformedPos.z != 0) {
813 transformedPos.x /= transformedPos.z;
814 transformedPos.y /= transformedPos.z;
815 }
816
817 previousPos = transformscale * prevPenPos;
818 if (previousPos.z != 0) {
819 previousPos.x /= previousPos.z;
820 previousPos.y /= previousPos.z;
821 }
822
823 currentPos = transformscale * penPos;
824 if (currentPos.z != 0) {
825 currentPos.x /= currentPos.z;
826 currentPos.y /= currentPos.z;
827 }
828
829 if (_mouseState == eReleased) {
830 // we are not axis-aligned
831 double meanPixelScale = (pscale.x + pscale.y) / 2.;
832 double hoverTolerance = (POINT_SIZE / 2.) * meanPixelScale;
833 if ( squareContains(transformedPos, centerPoint) ) {
834 _drawState = eCenterPointHovered;
835 didSomething = true;
836 } else if ( squareContains(transformedPos, leftPoint) ) {
837 _drawState = eLeftPointHovered;
838 didSomething = true;
839 } else if ( squareContains(transformedPos, rightPoint) ) {
840 _drawState = eRightPointHovered;
841 didSomething = true;
842 } else if ( squareContains(transformedPos, topPoint) ) {
843 _drawState = eTopPointHovered;
844 didSomething = true;
845 } else if ( squareContains(transformedPos, bottomPoint) ) {
846 _drawState = eBottomPointHovered;
847 didSomething = true;
848 } else if ( isOnEllipseBorder(transformedPos, targetRadius, targetCenter) ) {
849 _drawState = eCircleHovered;
850 didSomething = true;
851 } else if ( isOnRotationBar(rotationPos, targetRadius.x, targetCenter, pscale, hoverTolerance) ) {
852 _drawState = eRotationBarHovered;
853 didSomething = true;
854 } else {
855 _drawState = eInActive;
856 }
857 } else if (_mouseState == eDraggingCircle) {
858 double minX, minY, maxX, maxY;
859 _scale->getRange(minX, minY, maxX, maxY);
860
861 // we need to compute the backtransformed points with the scale
862
863 // the scale ratio is the ratio of distances to the center
864 double prevDistSq = (targetCenter.x - previousPos.x) * (targetCenter.x - previousPos.x) + (targetCenter.y - previousPos.y) * (targetCenter.y - previousPos.y);
865 if (prevDistSq != 0.) {
866 const double distSq = (targetCenter.x - currentPos.x) * (targetCenter.x - currentPos.x) + (targetCenter.y - currentPos.y) * (targetCenter.y - currentPos.y);
867 const double distRatio = std::sqrt(distSq / prevDistSq);
868 scale.x *= distRatio;
869 scale.y *= distRatio;
870 //_scale->setValue(scale.x, scale.y);
871 scaleChanged = true;
872 }
873 } else if ( (_mouseState == eDraggingLeftPoint) || (_mouseState == eDraggingRightPoint) ) {
874 // avoid division by zero
875 if (targetCenter.x != previousPos.x) {
876 double minX, minY, maxX, maxY;
877 _scale->getRange(minX, minY, maxX, maxY);
878 const double scaleRatio = (targetCenter.x - currentPos.x) / (targetCenter.x - previousPos.x);
879 OfxPointD newScale;
880 newScale.x = scale.x * scaleRatio;
881 newScale.x = (std::max)( minX, (std::min)(newScale.x, maxX) );
882 newScale.y = scaleUniform ? newScale.x : scale.y;
883 scale = newScale;
884 //_scale->setValue(scale.x, scale.y);
885 scaleChanged = true;
886 }
887 } else if ( (_mouseState == eDraggingTopPoint) || (_mouseState == eDraggingBottomPoint) ) {
888 // avoid division by zero
889 if (targetCenter.y != previousPos.y) {
890 double minX, minY, maxX, maxY;
891 _scale->getRange(minX, minY, maxX, maxY);
892 const double scaleRatio = (targetCenter.y - currentPos.y) / (targetCenter.y - previousPos.y);
893 OfxPointD newScale;
894 newScale.y = scale.y * scaleRatio;
895 newScale.y = (std::max)( minY, (std::min)(newScale.y, maxY) );
896 newScale.x = scaleUniform ? newScale.y : scale.x;
897 scale = newScale;
898 //_scale->setValue(scale.x, scale.y);
899 scaleChanged = true;
900 }
901 } else if (_mouseState == eDraggingTranslation) {
902 double dx = args.penPosition.x - _lastMousePos.x;
903 double dy = args.penPosition.y - _lastMousePos.y;
904
905 if ( (_orientation == eOrientationNotSet) && (_modifierStateShift > 0) ) {
906 _orientation = std::abs(dx) > std::abs(dy) ? eOrientationHorizontal : eOrientationVertical;
907 }
908
909 dx = _orientation == eOrientationVertical ? 0 : dx;
910 dy = _orientation == eOrientationHorizontal ? 0 : dy;
911 double newx = translate.x + dx;
912 double newy = translate.y + dy;
913 // round newx/y to the closest int, 1/10 int, etc
914 // this make parameter editing easier
915 newx = fround(newx, pscale.x);
916 newy = fround(newy, pscale.y);
917 translate.x = newx;
918 translate.y = newy;
919 //_translate->setValue(translate.x, translate.y);
920 translateChanged = true;
921 } else if (_mouseState == eDraggingCenter) {
922 OfxPointD currentCenter = center;
923 OFX::Matrix3x3 R = ofxsMatScale(1. / scale.x, 1. / scale.y) * ofxsMatRotation(rot);
924 double dx = args.penPosition.x - _lastMousePos.x;
925 double dy = args.penPosition.y - _lastMousePos.y;
926
927 if ( (_orientation == eOrientationNotSet) && (_modifierStateShift > 0) ) {
928 _orientation = std::abs(dx) > std::abs(dy) ? eOrientationHorizontal : eOrientationVertical;
929 }
930
931 dx = _orientation == eOrientationVertical ? 0 : dx;
932 dy = _orientation == eOrientationHorizontal ? 0 : dy;
933
934 double dxrot, dyrot;
935 if (!_translate) {
936 dxrot = dx;
937 dyrot = dy;
938 } else {
939 // if there is a _translate param (i.e. this is Transform/DirBlur and not GodRays),
940 // compensate the rotation, because the
941 // interact is visualized on the transformed image
942 OFX::Point3D dRot;
943 dRot.x = dx;
944 dRot.y = dy;
945 dRot.z = 1.;
946 dRot = R * dRot;
947 if (dRot.z != 0) {
948 dRot.x /= dRot.z;
949 dRot.y /= dRot.z;
950 }
951 dxrot = dRot.x;
952 dyrot = dRot.y;
953 }
954 double newx = currentCenter.x + dxrot;
955 double newy = currentCenter.y + dyrot;
956 // round newx/y to the closest int, 1/10 int, etc
957 // this make parameter editing easier
958 newx = fround(newx, pscale.x);
959 newy = fround(newy, pscale.y);
960 center.x = newx;
961 center.y = newy;
962 //_effect->beginEditBlock("setCenter");
963 //_center->setValue(center.x, center.y);
964 centerChanged = true;
965 if (_translate) {
966 // recompute dxrot,dyrot after rounding
967 OFX::Matrix3x3 Rinv;
968 if ( R.inverse(&Rinv) ) {
969 dxrot = newx - currentCenter.x;
970 dyrot = newy - currentCenter.y;
971 OFX::Point3D dRot;
972 dRot.x = dxrot;
973 dRot.y = dyrot;
974 dRot.z = 1;
975 dRot = Rinv * dRot;
976 if (dRot.z != 0) {
977 dRot.x /= dRot.z;
978 dRot.y /= dRot.z;
979 }
980 dx = dRot.x;
981 dy = dRot.y;
982 OfxPointD newTranslation;
983 newTranslation.x = translate.x + dx - dxrot;
984 newTranslation.y = translate.y + dy - dyrot;
985 translate = newTranslation;
986 //_translate->setValue(translate.x, translate.y);
987 translateChanged = true;
988 }
989 }
990 //_effect->endEditBlock();
991 } else if (_mouseState == eDraggingRotationBar) {
992 OfxPointD diffToCenter;
993 ///the current mouse position (untransformed) is doing has a certain angle relative to the X axis
994 ///which can be computed by : angle = arctan(opposite / adjacent)
995 diffToCenter.y = rotationPos.y - targetCenter.y;
996 diffToCenter.x = rotationPos.x - targetCenter.x;
997 double angle = std::atan2(diffToCenter.y, diffToCenter.x);
998 double angledegrees = rotate + OFX::ofxsToDegrees(angle);
999 double closest90 = 90. * std::floor( (angledegrees + 45.) / 90. );
1000 if (std::fabs(angledegrees - closest90) < 5.) {
1001 // snap to closest multiple of 90.
1002 angledegrees = closest90;
1003 }
1004 rotate = angledegrees;
1005 //_rotate->setValue(rotate);
1006 rotateChanged = true;
1007 } else {
1008 assert(false);
1009 }
1010
1011 _centerDrag = center;
1012 _translateDrag = translate;
1013 _scaleParamDrag = scale;
1014 _scaleUniformDrag = scaleUniform;
1015 _rotateDrag = rotate;
1016 _invertedDrag = inverted;
1017
1018 bool valuesChanged = (centerChanged || translateChanged || scaleChanged || rotateChanged);
1019
1020 if ( (_mouseState != eReleased) && _interactiveDrag && valuesChanged ) {
1021 // no need to redraw overlay since it is slave to the paramaters
1022 _effect->beginEditBlock("setTransform");
1023 if (centerChanged) {
1024 _center->setValue(center.x, center.y);
1025 }
1026 if (translateChanged) {
1027 _translate->setValue(translate.x, translate.y);
1028 }
1029 if (scaleChanged) {
1030 _scale->setValue(scale.x, scale.y);
1031 }
1032 if (rotateChanged) {
1033 _rotate->setValue(rotate);
1034 }
1035 _effect->endEditBlock();
1036 } else if (didSomething || valuesChanged) {
1037 _interact->requestRedraw();
1038 }
1039
1040 _lastMousePos = args.penPosition;
1041
1042 return didSomething || valuesChanged;
1043 } // TransformInteractCustomHelper::penMotion
1044
1045 bool
penDown(const OFX::PenArgs & args)1046 TransformInteractCustomHelper::penDown(const OFX::PenArgs &args)
1047 {
1048 if ( !_interactOpen->getValueAtTime(args.time) ) {
1049 return false;
1050 }
1051 using OFX::Matrix3x3;
1052
1053 const OfxPointD &pscale = args.pixelScale;
1054 const double time = args.time;
1055 OfxPointD center = { 0., 0. };
1056 OfxPointD translate = { 0., 0. };
1057 OfxPointD scaleParam = { 1., 1. };
1058 bool scaleUniform = false;
1059 double rotate = 0.;
1060 bool inverted = false;
1061
1062 if (_mouseState == eReleased) {
1063 if (_center) {
1064 _center->getValueAtTime(time, center.x, center.y);
1065 }
1066 if (_translate) {
1067 _translate->getValueAtTime(time, translate.x, translate.y);
1068 }
1069 if (_scale) {
1070 _scale->getValueAtTime(time, scaleParam.x, scaleParam.y);
1071 }
1072 if (_scaleUniform) {
1073 _scaleUniform->getValueAtTime(time, scaleUniform);
1074 }
1075 if (_rotate) {
1076 _rotate->getValueAtTime(time, rotate);
1077 }
1078 if (_invert) {
1079 _invert->getValueAtTime(time, inverted);
1080 }
1081 if (_interactive) {
1082 _interactive->getValueAtTime(args.time, _interactiveDrag);
1083 }
1084 } else {
1085 center = _centerDrag;
1086 translate = _translateDrag;
1087 scaleParam = _scaleParamDrag;
1088 scaleUniform = _scaleUniformDrag;
1089 rotate = _rotateDrag;
1090 inverted = _invertedDrag;
1091 }
1092
1093 OfxPointD targetCenter;
1094 getTargetCenter(center, translate, &targetCenter);
1095
1096 OfxPointD scale;
1097 ofxsTransformGetScale(scaleParam, scaleUniform, &scale);
1098
1099 OfxPointD targetRadius;
1100 getTargetRadius(scale, pscale, &targetRadius);
1101
1102 OfxPointD left, right, bottom, top;
1103 getTargetPoints(targetCenter, targetRadius, &left, &bottom, &top, &right);
1104
1105 OfxRectD centerPoint = rectFromCenterPoint(targetCenter, pscale);
1106 OfxRectD leftPoint = rectFromCenterPoint(left, pscale);
1107 OfxRectD rightPoint = rectFromCenterPoint(right, pscale);
1108 OfxRectD topPoint = rectFromCenterPoint(top, pscale);
1109 OfxRectD bottomPoint = rectFromCenterPoint(bottom, pscale);
1110 OFX::Point3D transformedPos, rotationPos;
1111 transformedPos.x = args.penPosition.x;
1112 transformedPos.y = args.penPosition.y;
1113 transformedPos.z = 1.;
1114
1115 double rot = OFX::ofxsToRadians(rotate);
1116
1117 ///now undo skew + rotation to the current position
1118 OFX::Matrix3x3 rotation, transform;
1119 rotation = OFX::ofxsMatInverseTransformCanonical(0., 0., 1., 1., 0., 0., false, rot, targetCenter.x, targetCenter.y);
1120 transform = OFX::ofxsMatInverseTransformCanonical(0., 0., 1., 1., 0, 0, false, rot, targetCenter.x, targetCenter.y);
1121
1122 rotationPos = rotation * transformedPos;
1123 if (rotationPos.z != 0) {
1124 rotationPos.x /= rotationPos.z;
1125 rotationPos.y /= rotationPos.z;
1126 }
1127 transformedPos = transform * transformedPos;
1128 if (transformedPos.z != 0) {
1129 transformedPos.x /= transformedPos.z;
1130 transformedPos.y /= transformedPos.z;
1131 }
1132
1133 _orientation = eOrientationAllDirections;
1134
1135 double pressToleranceX = 5 * pscale.x;
1136 double pressToleranceY = 5 * pscale.y;
1137 bool didSomething = false;
1138 if ( squareContains(transformedPos, centerPoint, pressToleranceX, pressToleranceY) ) {
1139 _mouseState = ( (!_translate || _modifierStateCtrl) ? eDraggingCenter : eDraggingTranslation );
1140 if (_modifierStateShift > 0) {
1141 _orientation = eOrientationNotSet;
1142 }
1143 didSomething = true;
1144 } else if ( squareContains(transformedPos, leftPoint, pressToleranceX, pressToleranceY) ) {
1145 _mouseState = eDraggingLeftPoint;
1146 didSomething = true;
1147 } else if ( squareContains(transformedPos, rightPoint, pressToleranceX, pressToleranceY) ) {
1148 _mouseState = eDraggingRightPoint;
1149 didSomething = true;
1150 } else if ( squareContains(transformedPos, topPoint, pressToleranceX, pressToleranceY) ) {
1151 _mouseState = eDraggingTopPoint;
1152 didSomething = true;
1153 } else if ( squareContains(transformedPos, bottomPoint, pressToleranceX, pressToleranceY) ) {
1154 _mouseState = eDraggingBottomPoint;
1155 didSomething = true;
1156 } else if ( isOnEllipseBorder(transformedPos, targetRadius, targetCenter) ) {
1157 _mouseState = eDraggingCircle;
1158 didSomething = true;
1159 } else if ( isOnRotationBar(rotationPos, targetRadius.x, targetCenter, pscale, pressToleranceY) ) {
1160 _mouseState = eDraggingRotationBar;
1161 didSomething = true;
1162 } else {
1163 _mouseState = eReleased;
1164 }
1165
1166 _lastMousePos = args.penPosition;
1167
1168 _centerDrag = center;
1169 _translateDrag = translate;
1170 _scaleParamDrag = scaleParam;
1171 _scaleUniformDrag = scaleUniform;
1172 _rotateDrag = rotate;
1173 _invertedDrag = inverted;
1174
1175 if (didSomething) {
1176 _interact->requestRedraw();
1177 }
1178
1179 return didSomething;
1180 } // TransformInteractCustomHelper::penDown
1181
1182 bool
penUp(const OFX::PenArgs & args)1183 TransformInteractCustomHelper::penUp(const OFX::PenArgs &args)
1184 {
1185 if ( !_interactOpen->getValueAtTime(args.time) ) {
1186 return false;
1187 }
1188 bool ret = _mouseState != eReleased;
1189
1190 if ( !_interactiveDrag && (_mouseState != eReleased) ) {
1191 // no need to redraw overlay since it is slave to the paramaters
1192 _effect->beginEditBlock("setTransform");
1193 if (_center) {
1194 _center->setValue(_centerDrag.x, _centerDrag.y);
1195 }
1196 if (_translate) {
1197 _translate->setValue(_translateDrag.x, _translateDrag.y);
1198 }
1199 if (_scale) {
1200 _scale->setValue(_scaleParamDrag.x, _scaleParamDrag.y);
1201 }
1202 if (_rotate) {
1203 _rotate->setValue(_rotateDrag);
1204 }
1205 _effect->endEditBlock();
1206 } else if (_mouseState != eReleased) {
1207 _interact->requestRedraw();
1208 }
1209
1210 _mouseState = eReleased;
1211 _lastMousePos = args.penPosition;
1212
1213 return ret;
1214 }
1215
1216 // keyDown just updates the modifier state
1217 bool
keyDown(const OFX::KeyArgs & args)1218 TransformInteractCustomHelper::keyDown(const OFX::KeyArgs &args)
1219 {
1220 // Always process, even if interact is not open, since this concerns modifiers
1221 //if (!_interactOpen->getValueAtTime(args.time)) {
1222 // return false;
1223 //}
1224
1225 // Note that on the Mac:
1226 // cmd/apple/cloverleaf is kOfxKey_Control_L
1227 // ctrl is kOfxKey_Meta_L
1228 // alt/option is kOfxKey_Alt_L
1229 bool mustRedraw = false;
1230
1231 // the two control keys may be pressed consecutively, be aware about this
1232 if ( _translate && ( (args.keySymbol == kOfxKey_Control_L) || (args.keySymbol == kOfxKey_Control_R) ) ) {
1233 mustRedraw = (_modifierStateCtrl == 0);
1234 ++_modifierStateCtrl;
1235 }
1236 if ( (args.keySymbol == kOfxKey_Shift_L) || (args.keySymbol == kOfxKey_Shift_R) ) {
1237 mustRedraw = (_modifierStateShift == 0);
1238 ++_modifierStateShift;
1239 if (_modifierStateShift > 0) {
1240 _orientation = eOrientationNotSet;
1241 }
1242 }
1243 if (mustRedraw) {
1244 _interact->requestRedraw();
1245 }
1246 //std::cout << std::hex << args.keySymbol << std::endl;
1247
1248 // modifiers are not "caught"
1249 return false;
1250 }
1251
1252 // keyUp just updates the modifier state
1253 bool
keyUp(const OFX::KeyArgs & args)1254 TransformInteractCustomHelper::keyUp(const OFX::KeyArgs &args)
1255 {
1256 // Always process, even if interact is not open, since this concerns modifiers
1257 //if (!_interactOpen->getValueAtTime(args.time)) {
1258 // return false;
1259 //}
1260
1261 bool mustRedraw = false;
1262
1263 if ( _translate && ( (args.keySymbol == kOfxKey_Control_L) || (args.keySymbol == kOfxKey_Control_R) ) ) {
1264 // we may have missed a keypress
1265 if (_modifierStateCtrl > 0) {
1266 --_modifierStateCtrl;
1267 mustRedraw = (_modifierStateCtrl == 0);
1268 }
1269 }
1270 if ( (args.keySymbol == kOfxKey_Shift_L) || (args.keySymbol == kOfxKey_Shift_R) ) {
1271 if (_modifierStateShift > 0) {
1272 --_modifierStateShift;
1273 mustRedraw = (_modifierStateShift == 0);
1274 }
1275 if (_modifierStateShift == 0) {
1276 _orientation = eOrientationAllDirections;
1277 }
1278 }
1279 if (mustRedraw) {
1280 _interact->requestRedraw();
1281 }
1282
1283 // modifiers are not "caught"
1284 return false;
1285 }
1286
1287 /** @brief Called when the interact is loses input focus */
1288 void
loseFocus(const FocusArgs &)1289 TransformInteractCustomHelper::loseFocus(const FocusArgs & /*args*/)
1290 {
1291 // reset the modifiers state
1292 if (_translate) {
1293 _modifierStateCtrl = 0;
1294 }
1295 _modifierStateShift = 0;
1296 _interactiveDrag = false;
1297 _mouseState = eReleased;
1298 _drawState = eInActive;
1299 }
1300 } // namespace OFX
1301