1 /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * This file is part of openfx-supportext <https://github.com/devernay/openfx-supportext>,
4 * Copyright (C) 2013-2018 INRIA
5 *
6 * openfx-supportext is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * openfx-supportext is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with openfx-supportext. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>
18 * ***** END LICENSE BLOCK ***** */
19
20 /*
21 * OFX utilities for tracking.
22 */
23
24 #include "ofxsTracking.h"
25 #include <cmath>
26
27 #include "ofxsOGLTextRenderer.h"
28
29 #ifdef __APPLE__
30 #include <OpenGL/gl.h>
31 #else
32 #include <GL/gl.h>
33 #endif
34
35 #define kSupportsTiles 1
36 #define kSupportsMultiResolution 1
37 #define kSupportsRenderScale 0 // we need full-res images
38 #define kRenderThreadSafety eRenderFullySafe
39
40 #define POINT_SIZE 5
41 #define POINT_TOLERANCE 6
42 #define HANDLE_SIZE 6
43
44 using namespace OFX;
45 using std::string;
46
47 namespace OFX {
GenericTrackerPlugin(OfxImageEffectHandle handle)48 GenericTrackerPlugin::GenericTrackerPlugin(OfxImageEffectHandle handle)
49 : ImageEffect(handle)
50 , _dstClip(NULL)
51 , _srcClip(NULL)
52 , _backwardButton(NULL)
53 , _prevButton(NULL)
54 , _nextButton(NULL)
55 , _forwardButton(NULL)
56 , _instanceName(NULL)
57 {
58 _dstClip = fetchClip(kOfxImageEffectOutputClipName);
59 assert(_dstClip->getPixelComponents() == ePixelComponentAlpha ||
60 _dstClip->getPixelComponents() == ePixelComponentRGB ||
61 _dstClip->getPixelComponents() == ePixelComponentRGBA);
62 _srcClip = getContext() == eContextGenerator ? NULL : fetchClip(kOfxImageEffectSimpleSourceClipName);
63 assert( (!_srcClip && getContext() == eContextGenerator) ||
64 (_srcClip->getPixelComponents() == ePixelComponentAlpha ||
65 _srcClip->getPixelComponents() == ePixelComponentRGB ||
66 _srcClip->getPixelComponents() == ePixelComponentRGBA) );
67
68
69 _backwardButton = fetchPushButtonParam(kParamTrackingBackward);
70 _prevButton = fetchPushButtonParam(kParamTrackingPrevious);
71 _nextButton = fetchPushButtonParam(kParamTrackingNext);
72 _forwardButton = fetchPushButtonParam(kParamTrackingForward);
73 _instanceName = fetchStringParam(kNatronOfxParamStringSublabelName);
74 assert(_backwardButton && _prevButton && _nextButton && _forwardButton && _instanceName);
75 }
76
77 bool
isIdentity(const IsIdentityArguments & args,Clip * & identityClip,double & identityTime,int &,std::string &)78 GenericTrackerPlugin::isIdentity(const IsIdentityArguments &args,
79 Clip * &identityClip,
80 double &identityTime, int& /*view*/, std::string& /*plane*/)
81 {
82 if ( !kSupportsRenderScale && ( (args.renderScale.x != 1.) || (args.renderScale.y != 1.) ) ) {
83 throwSuiteStatusException(kOfxStatFailed);
84
85 return false;
86 }
87
88 identityClip = _srcClip;
89 identityTime = args.time;
90
91 return true;
92 }
93
94 void
changedParam(const InstanceChangedArgs & args,const string & paramName)95 GenericTrackerPlugin::changedParam(const InstanceChangedArgs &args,
96 const string ¶mName)
97 {
98 if ( !kSupportsRenderScale && ( (args.renderScale.x != 1.) || (args.renderScale.y != 1.) ) ) {
99 throwSuiteStatusException(kOfxStatFailed);
100
101 return;
102 }
103
104 TrackArguments trackArgs;
105 trackArgs.renderScale = args.renderScale;
106 if ( (paramName == kParamTrackingBackward) && _srcClip && _srcClip->isConnected() ) {
107 trackArgs.first = args.time;
108 //double first,last; timeLineGetBounds(first, last); trackArgs.last = first + 1;// wrong: we want the srcClip range
109 OfxRangeD range = _srcClip->getFrameRange();
110 trackArgs.last = range.min + 1;
111 if (trackArgs.last <= trackArgs.first) {
112 trackArgs.forward = false;
113 trackArgs.reason = args.reason;
114 trackRange(trackArgs);
115 }
116 } else if ( (paramName == kParamTrackingPrevious) && _srcClip && _srcClip->isConnected() ) {
117 trackArgs.first = args.time;
118 trackArgs.last = trackArgs.first;
119 trackArgs.forward = false;
120 trackArgs.reason = args.reason;
121 trackRange(trackArgs);
122 } else if ( (paramName == kParamTrackingNext) && _srcClip && _srcClip->isConnected() ) {
123 trackArgs.first = args.time;
124 trackArgs.last = trackArgs.first;
125 trackArgs.forward = true;
126 trackArgs.reason = args.reason;
127 trackRange(trackArgs);
128 } else if ( (paramName == kParamTrackingForward) && _srcClip && _srcClip->isConnected() ) {
129 trackArgs.first = args.time;
130 //double first,last; timeLineGetBounds(first, last); trackArgs.last = last - 1; // wrong: we want the srcClip range
131 OfxRangeD range = _srcClip->getFrameRange();
132 trackArgs.last = range.max - 1;
133 if (trackArgs.last >= trackArgs.first) {
134 trackArgs.forward = true;
135 trackArgs.reason = args.reason;
136 trackRange(trackArgs);
137 }
138 }
139 }
140
141 bool
getRegionOfDefinition(const RegionOfDefinitionArguments & args,OfxRectD &)142 GenericTrackerPlugin::getRegionOfDefinition(const RegionOfDefinitionArguments &args,
143 OfxRectD & /*rod*/)
144 {
145 if ( !kSupportsRenderScale && ( (args.renderScale.x != 1.) || (args.renderScale.y != 1.) ) ) {
146 throwSuiteStatusException(kOfxStatFailed);
147 }
148
149 return false;
150 }
151
152 void
genericTrackerDescribe(ImageEffectDescriptor & desc)153 genericTrackerDescribe(ImageEffectDescriptor &desc)
154 {
155 desc.addSupportedContext(eContextGeneral);
156 desc.addSupportedContext(eContextFilter);
157 desc.addSupportedContext(eContextTracker);
158 // supported bit depths depend on the tracking algorithm.
159 //desc.addSupportedBitDepth(eBitDepthUByte);
160 //desc.addSupportedBitDepth(eBitDepthUShort);
161 //desc.addSupportedBitDepth(eBitDepthFloat);
162
163 // single instance depends on the algorithm
164 //desc.setSingleInstance(false);
165
166 // no host frame threading (anyway, the tracker always returns identity)
167 desc.setHostFrameThreading(false);
168
169 ///We do temporal clip access
170 desc.setTemporalClipAccess(true);
171
172 // rendertwicealways must be set to true if the tracker cannot handle interlaced content (most don't)
173 //desc.setRenderTwiceAlways(true);
174
175 desc.setSupportsMultipleClipPARs(false);
176
177 // support multithread (anyway, the tracker always returns identity)
178 desc.setRenderThreadSafety(kRenderThreadSafety);
179
180 // support tiles (anyway, the tracker always returns identity)
181 desc.setSupportsTiles(kSupportsTiles);
182
183 // in order to support render scale, render() must take into account the pixelaspectratio and the renderscale
184 // and scale the transform appropriately.
185 // All other functions are usually in canonical coordinates.
186
187 ///We support multi-resolution (which does not mean we support render scale)
188 desc.setSupportsMultiResolution(kSupportsMultiResolution);
189 #ifdef OFX_EXTENSIONS_NATRON
190 desc.setChannelSelector(ePixelComponentNone);
191 #endif
192 }
193
194 PageParamDescriptor*
genericTrackerDescribeInContextBegin(ImageEffectDescriptor & desc,ContextEnum)195 genericTrackerDescribeInContextBegin(ImageEffectDescriptor &desc,
196 ContextEnum /*context*/)
197 {
198 // Source clip only in the filter context
199 // create the mandated source clip
200 // always declare the source clip first, because some hosts may consider
201 // it as the default input clip (e.g. Nuke)
202 ClipDescriptor *srcClip = desc.defineClip(kOfxImageEffectSimpleSourceClipName);
203
204 srcClip->addSupportedComponent(ePixelComponentRGBA);
205 srcClip->addSupportedComponent(ePixelComponentRGB);
206 srcClip->addSupportedComponent(ePixelComponentAlpha);
207
208 ///we do temporal clip access
209 srcClip->setTemporalClipAccess(true);
210 srcClip->setSupportsTiles(kSupportsTiles);
211 srcClip->setIsMask(false);
212 srcClip->setOptional(false);
213
214 // create the mandated output clip
215 ClipDescriptor *dstClip = desc.defineClip(kOfxImageEffectOutputClipName);
216 dstClip->addSupportedComponent(ePixelComponentRGBA);
217 dstClip->addSupportedComponent(ePixelComponentRGB);
218 dstClip->addSupportedComponent(ePixelComponentAlpha);
219 dstClip->setSupportsTiles(kSupportsTiles);
220
221
222 // make some pages and to things in
223 PageParamDescriptor *page = desc.definePageParam("Controls");
224
225 return page;
226 }
227
228 void
genericTrackerDescribePointParameters(ImageEffectDescriptor & desc,PageParamDescriptor * page)229 genericTrackerDescribePointParameters(ImageEffectDescriptor &desc,
230 PageParamDescriptor* page)
231 {
232 ///Declare the name first so that in Natron it appears as the first column in the multi instance
233 // name
234 {
235 StringParamDescriptor* param = desc.defineStringParam(kParamTrackingLabel);
236 param->setLabel(kParamTrackingLabelLabel);
237 param->setHint(kParamTrackingLabelHint);
238 param->setDefault(kParamTrackingLabelDefault);
239 param->setInstanceSpecific(true);
240 ////param->setIsSecret(false); // it has to be user-editable
241 ////param->setEnabled(true); // it has to be user-editable
242 ////param->setIsPersistent(true); // it has to be saved with the instance parameters
243 param->setEvaluateOnChange(false); // it is meaningless
244 if (page) {
245 page->addChild(*param);
246 }
247 }
248
249
250 // backward
251 {
252 PushButtonParamDescriptor* param = desc.definePushButtonParam(kParamTrackingBackward);
253 param->setLabel(kParamTrackingBackwardLabel);
254 param->setHint(kParamTrackingBackwardHint);
255 param->setLayoutHint(eLayoutHintNoNewLine, 1);
256 if (page) {
257 page->addChild(*param);
258 }
259 }
260
261 // prev
262 {
263 PushButtonParamDescriptor* param = desc.definePushButtonParam(kParamTrackingPrevious);
264 param->setLabel(kParamTrackingPreviousLabel);
265 param->setHint(kParamTrackingPreviousHint);
266 param->setLayoutHint(eLayoutHintNoNewLine, 1);
267 if (page) {
268 page->addChild(*param);
269 }
270 }
271
272 // next
273 {
274 PushButtonParamDescriptor* param = desc.definePushButtonParam(kParamTrackingNext);
275 param->setLabel(kParamTrackingNextLabel);
276 param->setHint(kParamTrackingNextHint);
277 param->setLayoutHint(eLayoutHintNoNewLine, 1);
278 if (page) {
279 page->addChild(*param);
280 }
281 }
282
283 // forward
284 {
285 PushButtonParamDescriptor* param = desc.definePushButtonParam(kParamTrackingForward);
286 param->setLabel(kParamTrackingForwardLabel);
287 param->setHint(kParamTrackingForwardHint);
288 if (page) {
289 page->addChild(*param);
290 }
291 }
292 } // genericTrackerDescribePointParameters
293
294 //////////////////// INTERACT ////////////////////
295
296 static bool
isNearby(const OfxPointD & p,double x,double y,double tolerance,const OfxPointD & pscale)297 isNearby(const OfxPointD & p,
298 double x,
299 double y,
300 double tolerance,
301 const OfxPointD & pscale)
302 {
303 return std::fabs(p.x - x) <= tolerance * pscale.x && std::fabs(p.y - y) <= tolerance * pscale.y;
304 }
305
306 bool
draw(const DrawArgs & args)307 TrackerRegionInteract::draw(const DrawArgs &args)
308 {
309 OfxRGBColourD color = { 0.8, 0.8, 0.8 };
310
311 getSuggestedColour(color);
312 const OfxPointD& pscale = args.pixelScale;
313 GLdouble projection[16];
314 glGetDoublev( GL_PROJECTION_MATRIX, projection);
315 GLint viewport[4];
316 glGetIntegerv(GL_VIEWPORT, viewport);
317 OfxPointD shadow; // how much to translate GL_PROJECTION to get exactly one pixel on screen
318 shadow.x = 2. / (projection[0] * viewport[2]);
319 shadow.y = 2. / (projection[5] * viewport[3]);
320
321 double xi1, xi2, yi1, yi2, xo1, xo2, yo1, yo2, xc, yc, xoff, yoff;
322
323 if (_ms != eMouseStateIdle) {
324 xi1 = _innerBtmLeftDragPos.x;
325 yi1 = _innerBtmLeftDragPos.y;
326 xi2 = _innerTopRightDragPos.x;
327 yi2 = _innerTopRightDragPos.y;
328 xo1 = _outerBtmLeftDragPos.x;
329 yo1 = _outerBtmLeftDragPos.y;
330 xo2 = _outerTopRightDragPos.x;
331 yo2 = _outerTopRightDragPos.y;
332 xc = _centerDragPos.x;
333 yc = _centerDragPos.y;
334 xoff = _offsetDragPos.x;
335 yoff = _offsetDragPos.y;
336 } else {
337 _innerBtmLeft->getValueAtTime( args.time, xi1, yi1);
338 _innerTopRight->getValueAtTime(args.time, xi2, yi2);
339 _outerBtmLeft->getValueAtTime( args.time, xo1, yo1);
340 _outerTopRight->getValueAtTime(args.time, xo2, yo2);
341 _center->getValueAtTime(args.time, xc, yc);
342 _offset->getValueAtTime(args.time, xoff, yoff);
343 ///innerBtmLeft and outerBtmLeft are relative to the center, make them absolute
344 xi1 += (xc + xoff);
345 yi1 += (yc + yoff);
346 xi2 += (xc + xoff);
347 yi2 += (yc + yoff);
348 xo1 += (xc + xoff);
349 yo1 += (yc + yoff);
350 xo2 += (xc + xoff);
351 yo2 += (yc + yoff);
352 }
353
354
355 //glPushAttrib(GL_ALL_ATTRIB_BITS); // caller is responsible for protecting attribs
356
357 glDisable(GL_LINE_STIPPLE);
358 glEnable(GL_LINE_SMOOTH);
359 glDisable(GL_POINT_SMOOTH);
360 glEnable(GL_BLEND);
361 glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
362 glLineWidth(1.5f);
363 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
364
365 // Draw everything twice
366 // l = 0: shadow
367 // l = 1: drawing
368 for (int l = 0; l < 2; ++l) {
369 // shadow (uses GL_PROJECTION)
370 glMatrixMode(GL_PROJECTION);
371 int direction = (l == 0) ? 1 : -1;
372 // translate (1,-1) pixels
373 glTranslated(direction * shadow.x, -direction * shadow.y, 0);
374 glMatrixMode(GL_MODELVIEW); // Modelview should be used on Nuke
375
376 glColor3f( (float)color.r * l, (float)color.g * l, (float)color.b * l );
377 glBegin(GL_LINE_LOOP);
378 glVertex2d(xi1, yi1);
379 glVertex2d(xi1, yi2);
380 glVertex2d(xi2, yi2);
381 glVertex2d(xi2, yi1);
382 glEnd();
383
384 glBegin(GL_LINE_LOOP);
385 glVertex2d(xo1, yo1);
386 glVertex2d(xo1, yo2);
387 glVertex2d(xo2, yo2);
388 glVertex2d(xo2, yo1);
389 glEnd();
390
391 glPointSize(POINT_SIZE);
392 glBegin(GL_POINTS);
393
394 ///draw center
395 if ( (_ds == eDrawStateHoveringCenter) || (_ms == eMouseStateDraggingCenter) ) {
396 glColor3f(0.f * l, 1.f * l, 0.f * l);
397 } else {
398 glColor3f( (float)color.r * l, (float)color.g * l, (float)color.b * l );
399 }
400 glVertex2d(xc, yc);
401
402 if ( (xoff != 0) || (yoff != 0) ) {
403 glVertex2d(xc + xoff, yc + yoff);
404 }
405
406 //////DRAWING INNER POINTS
407 if ( (_ds == eDrawStateHoveringInnerBtmLeft) || (_ms == eMouseStateDraggingInnerBtmLeft) ) {
408 glColor3f(0.f * l, 1.f * l, 0.f * l);
409 glVertex2d(xi1, yi1);
410 }
411 if ( (_ds == eDrawStateHoveringInnerBtmMid) || (_ms == eMouseStateDraggingInnerBtmMid) ) {
412 glColor3f(0.f * l, 1.f * l, 0.f * l);
413 glVertex2d(xc + xoff, yi1);
414 }
415 if ( (_ds == eDrawStateHoveringInnerBtmRight) || (_ms == eMouseStateDraggingInnerBtmRight) ) {
416 glColor3f(0.f * l, 1.f * l, 0.f * l);
417 glVertex2d(xi2, yi1);
418 }
419 if ( (_ds == eDrawStateHoveringInnerMidLeft) || (_ms == eMouseStateDraggingInnerMidLeft) ) {
420 glColor3f(0.f * l, 1.f * l, 0.f * l);
421 glVertex2d(xi1, yc + yoff);
422 }
423 if ( (_ds == eDrawStateHoveringInnerMidRight) || (_ms == eMouseStateDraggingInnerMidRight) ) {
424 glColor3f(0.f * l, 1.f * l, 0.f * l);
425 glVertex2d(xi2, yc + yoff);
426 }
427 if ( (_ds == eDrawStateHoveringInnerTopLeft) || (_ms == eMouseStateDraggingInnerTopLeft) ) {
428 glColor3f(0.f * l, 1.f * l, 0.f * l);
429 glVertex2d(xi1, yi2);
430 }
431
432 if ( (_ds == eDrawStateHoveringInnerTopMid) || (_ms == eMouseStateDraggingInnerTopMid) ) {
433 glColor3f(0.f * l, 1.f * l, 0.f * l);
434 glVertex2d(xc + xoff, yi2);
435 }
436
437 if ( (_ds == eDrawStateHoveringInnerTopRight) || (_ms == eMouseStateDraggingInnerTopRight) ) {
438 glColor3f(0.f * l, 1.f * l, 0.f * l);
439 glVertex2d(xi2, yi2);
440 }
441
442
443 //////DRAWING OUTTER POINTS
444
445 if ( (_ds == eDrawStateHoveringOuterBtmLeft) || (_ms == eMouseStateDraggingOuterBtmLeft) ) {
446 glColor3f(0.f * l, 1.f * l, 0.f * l);
447 glVertex2d(xo1, yo1);
448 }
449 if ( (_ds == eDrawStateHoveringOuterBtmMid) || (_ms == eMouseStateDraggingOuterBtmMid) ) {
450 glColor3f(0.f * l, 1.f * l, 0.f * l);
451 glVertex2d(xc + xoff, yo1);
452 }
453 if ( (_ds == eDrawStateHoveringOuterBtmRight) || (_ms == eMouseStateDraggingOuterBtmRight) ) {
454 glColor3f(0.f * l, 1.f * l, 0.f * l);
455 glVertex2d(xo2, yo1);
456 }
457 if ( (_ds == eDrawStateHoveringOuterMidLeft) || (_ms == eMouseStateDraggingOuterMidLeft) ) {
458 glColor3f(0.f * l, 1.f * l, 0.f * l);
459 glVertex2d(xo1, yc + yoff);
460 }
461 if ( (_ds == eDrawStateHoveringOuterMidRight) || (_ms == eMouseStateDraggingOuterMidRight) ) {
462 glColor3f(0.f * l, 1.f * l, 0.f * l);
463 glVertex2d(xo2, yc + yoff);
464 }
465
466 if ( (_ds == eDrawStateHoveringOuterTopLeft) || (_ms == eMouseStateDraggingOuterTopLeft) ) {
467 glColor3f(0.f * l, 1.f * l, 0.f * l);
468 glVertex2d(xo1, yo2);
469 }
470 if ( (_ds == eDrawStateHoveringOuterTopMid) || (_ms == eMouseStateDraggingOuterTopMid) ) {
471 glColor3f(0.f * l, 1.f * l, 0.f * l);
472 glVertex2d(xc + xoff, yo2);
473 }
474 if ( (_ds == eDrawStateHoveringOuterTopRight) || (_ms == eMouseStateDraggingOuterTopRight) ) {
475 glColor3f(0.f * l, 1.f * l, 0.f * l);
476 glVertex2d(xo2, yo2);
477 }
478
479 glEnd();
480
481 if ( (xoff != 0) || (yoff != 0) ) {
482 glBegin(GL_LINES);
483 glColor3f( (float)color.r * l, (float)color.g * l, (float)color.b * l );
484 glVertex2d(xc, yc);
485 glVertex2d(xc + xoff, yc + yoff);
486 glEnd();
487 }
488
489 double handleSizeX = HANDLE_SIZE * pscale.x;
490 double handleSizeY = HANDLE_SIZE * pscale.y;
491
492 ///now show small lines at handle positions
493 glBegin(GL_LINES);
494
495 if ( (_ds == eDrawStateHoveringInnerMidLeft) || (_ms == eMouseStateDraggingInnerMidLeft) ) {
496 glColor3f(0.f * l, 1.f * l, 0.f * l);
497 } else {
498 glColor3f( (float)color.r * l, (float)color.g * l, (float)color.b * l );
499 }
500 glVertex2d(xi1, yc + yoff);
501 glVertex2d(xi1 - handleSizeX, yc + yoff);
502
503 if ( (_ds == eDrawStateHoveringInnerTopMid) || (_ms == eMouseStateDraggingInnerTopMid) ) {
504 glColor3f(0.f * l, 1.f * l, 0.f * l);
505 } else {
506 glColor3f( (float)color.r * l, (float)color.g * l, (float)color.b * l );
507 }
508 glVertex2d(xc + xoff, yi2);
509 glVertex2d(xc + xoff, yi2 + handleSizeY);
510
511 if ( (_ds == eDrawStateHoveringInnerMidRight) || (_ms == eMouseStateDraggingInnerMidRight) ) {
512 glColor3f(0.f * l, 1.f * l, 0.f * l);
513 } else {
514 glColor3f( (float)color.r * l, (float)color.g * l, (float)color.b * l );
515 }
516 glVertex2d(xi2, yc + yoff);
517 glVertex2d(xi2 + handleSizeX, yc + yoff);
518
519 if ( (_ds == eDrawStateHoveringInnerBtmMid) || (_ms == eMouseStateDraggingInnerBtmMid) ) {
520 glColor3f(0.f * l, 1.f * l, 0.f * l);
521 } else {
522 glColor3f( (float)color.r * l, (float)color.g * l, (float)color.b * l );
523 }
524 glVertex2d(xc + xoff, yi1);
525 glVertex2d(xc + xoff, yi1 - handleSizeY);
526
527 //////DRAWING OUTTER HANDLES
528
529 if ( (_ds == eDrawStateHoveringOuterMidLeft) || (_ms == eMouseStateDraggingOuterMidLeft) ) {
530 glColor3f(0.f * l, 1.f * l, 0.f * l);
531 } else {
532 glColor3f( (float)color.r * l, (float)color.g * l, (float)color.b * l );
533 }
534 glVertex2d(xo1, yc + yoff);
535 glVertex2d(xo1 - handleSizeX, yc + yoff);
536
537 if ( (_ds == eDrawStateHoveringOuterTopMid) || (_ms == eMouseStateDraggingOuterTopMid) ) {
538 glColor3f(0.f * l, 1.f * l, 0.f * l);
539 } else {
540 glColor3f( (float)color.r * l, (float)color.g * l, (float)color.b * l );
541 }
542 glVertex2d(xc + xoff, yo2);
543 glVertex2d(xc + xoff, yo2 + handleSizeY);
544
545 if ( (_ds == eDrawStateHoveringOuterMidRight) || (_ms == eMouseStateDraggingOuterMidRight) ) {
546 glColor3f(0.f * l, 1.f * l, 0.f * l);
547 } else {
548 glColor3f( (float)color.r * l, (float)color.g * l, (float)color.b * l );
549 }
550 glVertex2d(xo2 + handleSizeX, yc + yoff);
551 glVertex2d(xo2, yc + yoff);
552
553 if ( (_ds == eDrawStateHoveringOuterBtmMid) || (_ms == eMouseStateDraggingOuterBtmMid) ) {
554 glColor3f(0.f * l, 1.f * l, 0.f * l);
555 } else {
556 glColor3f( (float)color.r * l, (float)color.g * l, (float)color.b * l );
557 }
558 glVertex2d(xc + xoff, yo1);
559 glVertex2d(xc + xoff, yo1 - handleSizeY);
560 glEnd();
561
562
563 glColor3f( (float)color.r * l, (float)color.g * l, (float)color.b * l );
564 string name;
565 _name->getValue(name);
566 TextRenderer::bitmapString( xc, yc, name.c_str() );
567 }
568
569 //glPopAttrib();
570
571 return true;
572 } // draw
573
574 bool
penMotion(const PenArgs & args)575 TrackerRegionInteract::penMotion(const PenArgs &args)
576 {
577 const OfxPointD& pscale = args.pixelScale;
578 bool didSomething = false;
579 bool valuesChanged = false;
580 OfxPointD delta;
581
582 delta.x = args.penPosition.x - _lastMousePos.x;
583 delta.y = args.penPosition.y - _lastMousePos.y;
584
585 double xi1, xi2, yi1, yi2, xo1, xo2, yo1, yo2, xc, yc, xoff, yoff;
586 if (_ms == eMouseStateIdle) {
587 _innerBtmLeft->getValueAtTime( args.time, xi1, yi1);
588 _innerTopRight->getValueAtTime(args.time, xi2, yi2);
589 _outerBtmLeft->getValueAtTime( args.time, xo1, yo1);
590 _outerTopRight->getValueAtTime(args.time, xo2, yo2);
591 _center->getValueAtTime(args.time, xc, yc);
592 _offset->getValueAtTime(args.time, xoff, yoff);
593
594 ///innerBtmLeft and outerBtmLeft are relative to the center, make them absolute
595 xi1 += (xc + xoff);
596 yi1 += (yc + yoff);
597 xi2 += (xc + xoff);
598 yi2 += (yc + yoff);
599 xo1 += (xc + xoff);
600 yo1 += (yc + yoff);
601 xo2 += (xc + xoff);
602 yo2 += (yc + yoff);
603 } else {
604 xi1 = _innerBtmLeftDragPos.x;
605 yi1 = _innerBtmLeftDragPos.y;
606 xi2 = _innerTopRightDragPos.x;
607 yi2 = _innerTopRightDragPos.y;
608 xo1 = _outerBtmLeftDragPos.x;
609 yo1 = _outerBtmLeftDragPos.y;
610 xo2 = _outerTopRightDragPos.x;
611 yo2 = _outerTopRightDragPos.y;
612 xc = _centerDragPos.x;
613 yc = _centerDragPos.y;
614 xoff = _offsetDragPos.x;
615 yoff = _offsetDragPos.y;
616 }
617
618
619 bool lastStateWasHovered = _ds != eDrawStateInactive;
620
621 if (_ms == eMouseStateIdle) {
622 // test center first
623 if ( isNearby(args.penPosition, xc, yc, POINT_TOLERANCE, pscale) ) {
624 _ds = eDrawStateHoveringCenter;
625 didSomething = true;
626 } else if ( isNearby(args.penPosition, xi1, yi1, POINT_TOLERANCE, pscale) ) {
627 _ds = eDrawStateHoveringInnerBtmLeft;
628 didSomething = true;
629 } else if ( isNearby(args.penPosition, xi2, yi1, POINT_TOLERANCE, pscale) ) {
630 _ds = eDrawStateHoveringInnerBtmRight;
631 didSomething = true;
632 } else if ( isNearby(args.penPosition, xi1, yi2, POINT_TOLERANCE, pscale) ) {
633 _ds = eDrawStateHoveringInnerTopLeft;
634 didSomething = true;
635 } else if ( isNearby(args.penPosition, xi2, yi2, POINT_TOLERANCE, pscale) ) {
636 _ds = eDrawStateHoveringInnerTopRight;
637 didSomething = true;
638 } else if ( isNearby(args.penPosition, xc + xoff, yi1, POINT_TOLERANCE, pscale) ) {
639 _ds = eDrawStateHoveringInnerBtmMid;
640 didSomething = true;
641 } else if ( isNearby(args.penPosition, xi1, yc + yoff, POINT_TOLERANCE, pscale) ) {
642 _ds = eDrawStateHoveringInnerMidLeft;
643 didSomething = true;
644 } else if ( isNearby(args.penPosition, xc + xoff, yi2, POINT_TOLERANCE, pscale) ) {
645 _ds = eDrawStateHoveringInnerTopMid;
646 didSomething = true;
647 } else if ( isNearby(args.penPosition, xi2, yc + yoff, POINT_TOLERANCE, pscale) ) {
648 _ds = eDrawStateHoveringInnerMidRight;
649 didSomething = true;
650 } else if ( isNearby(args.penPosition, xo1, yo1, POINT_TOLERANCE, pscale) ) {
651 _ds = eDrawStateHoveringOuterBtmLeft;
652 didSomething = true;
653 } else if ( isNearby(args.penPosition, xo2, yo1, POINT_TOLERANCE, pscale) ) {
654 _ds = eDrawStateHoveringOuterBtmRight;
655 didSomething = true;
656 } else if ( isNearby(args.penPosition, xo1, yo2, POINT_TOLERANCE, pscale) ) {
657 _ds = eDrawStateHoveringOuterTopLeft;
658 didSomething = true;
659 } else if ( isNearby(args.penPosition, xo2, yo2, POINT_TOLERANCE, pscale) ) {
660 _ds = eDrawStateHoveringOuterTopRight;
661 didSomething = true;
662 } else if ( isNearby(args.penPosition, xc + xoff, yo1, POINT_TOLERANCE, pscale) ) {
663 _ds = eDrawStateHoveringOuterBtmMid;
664 didSomething = true;
665 } else if ( isNearby(args.penPosition, xo1, yc + yoff, POINT_TOLERANCE, pscale) ) {
666 _ds = eDrawStateHoveringOuterMidLeft;
667 didSomething = true;
668 } else if ( isNearby(args.penPosition, xc + xoff, yo2, POINT_TOLERANCE, pscale) ) {
669 _ds = eDrawStateHoveringOuterTopMid;
670 didSomething = true;
671 } else if ( isNearby(args.penPosition, xo2, yc + yoff, POINT_TOLERANCE, pscale) ) {
672 _ds = eDrawStateHoveringOuterMidRight;
673 didSomething = true;
674 } else {
675 _ds = eDrawStateInactive;
676 }
677 }
678
679 double multiplier = _controlDown ? 0 : 1;
680 if (_ms == eMouseStateDraggingInnerBtmLeft) {
681 xi1 += delta.x;
682 yi1 += delta.y;
683
684 xi2 -= delta.x;
685 yi2 -= delta.y;
686
687 ///also move the outer rect
688 xo1 += delta.x;
689 yo1 += delta.y;
690
691 xo2 -= delta.x;
692 yo2 -= delta.y;
693
694 valuesChanged = true;
695 } else if (_ms == eMouseStateDraggingInnerTopLeft) {
696 xi1 += delta.x;
697 yi1 -= delta.y;
698
699 yi2 += delta.y;
700 xi2 -= delta.x;
701
702 xo1 += delta.x;
703 yo1 -= delta.y;
704
705 yo2 += delta.y;
706 xo2 -= delta.x;
707
708 valuesChanged = true;
709 } else if (_ms == eMouseStateDraggingInnerTopRight) {
710 xi1 -= delta.x;
711 yi1 -= delta.y;
712
713 yi2 += delta.y;
714 xi2 += delta.x;
715
716 xo1 -= delta.x;
717 yo1 -= delta.y;
718
719 yo2 += delta.y;
720 xo2 += delta.x;
721
722 valuesChanged = true;
723 } else if (_ms == eMouseStateDraggingInnerBtmRight) {
724 yi1 += delta.y;
725 xi1 -= delta.x;
726
727 yi2 -= delta.y;
728 xi2 += delta.x;
729
730 yo1 += delta.y;
731 xo1 -= delta.x;
732
733 yo2 -= delta.y;
734 xo2 += delta.x;
735
736 valuesChanged = true;
737 } else if (_ms == eMouseStateDraggingInnerTopMid) {
738 yi1 -= delta.y;
739
740 yi2 += delta.y;
741
742 yo1 -= delta.y;
743
744 yo2 += delta.y;
745
746 valuesChanged = true;
747 } else if (_ms == eMouseStateDraggingInnerMidRight) {
748 xi1 -= delta.x;
749
750 xi2 += delta.x;
751
752 xo1 -= delta.x;
753
754 xo2 += delta.x;
755
756 valuesChanged = true;
757 } else if (_ms == eMouseStateDraggingInnerBtmMid) {
758 yi1 += delta.y;
759
760 yi2 -= delta.y;
761
762 yo1 += delta.y;
763
764 yo2 -= delta.y;
765
766 valuesChanged = true;
767 } else if (_ms == eMouseStateDraggingInnerMidLeft) {
768 xi1 += delta.x;
769
770 xi2 -= delta.x;
771
772 xo1 += delta.x;
773
774 xo2 -= delta.x;
775
776 valuesChanged = true;
777 } else if (_ms == eMouseStateDraggingOuterBtmLeft) {
778 xo1 += delta.x;
779 yo1 += delta.y;
780
781 xo2 -= multiplier * delta.x;
782 yo2 -= multiplier * delta.y;
783
784 valuesChanged = true;
785 } else if (_ms == eMouseStateDraggingOuterTopLeft) {
786 xo1 += delta.x;
787 if (!_controlDown) {
788 yo1 -= delta.y;
789 }
790
791 yo2 += delta.y;
792 xo2 -= multiplier * delta.x;
793
794 valuesChanged = true;
795 } else if (_ms == eMouseStateDraggingOuterTopRight) {
796 if (!_controlDown) {
797 xo1 -= delta.x;
798 yo1 -= delta.y;
799 }
800
801 yo2 += delta.y;
802 xo2 += delta.x;
803
804 valuesChanged = true;
805 } else if (_ms == eMouseStateDraggingOuterBtmRight) {
806 yo1 += delta.y;
807 if (!_controlDown) {
808 xo1 -= delta.x;
809 }
810
811 yo2 -= multiplier * delta.y;
812 xo2 += delta.x;
813
814 valuesChanged = true;
815 } else if (_ms == eMouseStateDraggingOuterTopMid) {
816 if (!_controlDown) {
817 yo1 -= delta.y;
818 }
819
820 yo2 += delta.y;
821
822 valuesChanged = true;
823 } else if (_ms == eMouseStateDraggingOuterMidRight) {
824 if (!_controlDown) {
825 xo1 -= delta.x;
826 }
827
828 xo2 += delta.x;
829
830 valuesChanged = true;
831 } else if (_ms == eMouseStateDraggingOuterBtmMid) {
832 yo1 += delta.y;
833
834 yo2 -= multiplier * delta.y;
835
836 valuesChanged = true;
837 } else if (_ms == eMouseStateDraggingOuterMidLeft) {
838 xo1 += delta.x;
839
840 xo2 -= multiplier * delta.x;
841
842 valuesChanged = true;
843 } else if ( (_ms == eMouseStateDraggingCenter) || (_ms == eMouseStateDraggingOffset) ) {
844 xi1 += delta.x;
845 yi1 += delta.y;
846
847 xi2 += delta.x;
848 yi2 += delta.y;
849
850 xo1 += delta.x;
851 yo1 += delta.y;
852
853 xo2 += delta.x;
854 yo2 += delta.y;
855
856 if (_ms == eMouseStateDraggingCenter) {
857 xc += delta.x;
858 yc += delta.y;
859 } else {
860 assert(_ms == eMouseStateDraggingOffset);
861 xoff += delta.x;
862 yoff += delta.y;
863 }
864
865 valuesChanged = true;
866 }
867
868
869 if ( isDraggingOuterPoint() ) {
870 /// outer rect must at least contain the inner rect
871
872 if (xo1 > xi1) {
873 xo1 = xi1;
874 valuesChanged = true;
875 }
876
877 if (yo1 > yi1) {
878 yo1 = yi1;
879 valuesChanged = true;
880 }
881
882 if (xo2 < xi2) {
883 xo2 = xi2;
884 valuesChanged = true;
885 }
886 if (yo2 < yi2) {
887 yo2 = yi2;
888 valuesChanged = true;
889 }
890 }
891
892 if ( isDraggingInnerPoint() ) {
893 /// inner rect must contain center point
894 if ( xi1 > (xc + xoff) ) {
895 double diffX = xi1 - xc - xoff;
896 xi1 = xc + xoff;
897 xo1 -= diffX;
898 xo2 += multiplier * diffX;
899 xi2 += multiplier * diffX;
900 valuesChanged = true;
901 }
902 if ( yi1 > (yc + yoff) ) {
903 double diffY = yi1 - yc - yoff;
904 yi1 = yc + yoff;
905 yo1 -= diffY;
906 yo2 += multiplier * diffY;
907 yi2 += multiplier * diffY;
908 valuesChanged = true;
909 }
910 if ( xi2 <= (xc + xoff) ) {
911 double diffX = xi2 - xc - xoff;
912 xi2 = xc + xoff;
913 xo2 += diffX;
914 xo1 -= multiplier * diffX;
915 xi1 -= multiplier * diffX;
916 valuesChanged = true;
917 }
918 if ( yi2 <= (yc + yoff) ) {
919 double diffY = yi2 - yc - yoff;
920 yi2 = yc + yoff;
921 yo2 -= diffY;
922 yo1 -= multiplier * diffY;
923 yi1 -= multiplier * diffY;
924 valuesChanged = true;
925 }
926 }
927
928 ///forbid 0 pixels wide rectangles
929 if (xi2 <= xi1) {
930 xi1 = (xi2 + xi1) / 2;
931 xi2 = xi1 + 1;
932 valuesChanged = true;
933 }
934 if (yi2 <= yi1) {
935 yi1 = (yi2 + yi1) / 2;
936 yi2 = yi1 + 1;
937 valuesChanged = true;
938 }
939 if (xo2 <= xo1) {
940 xo1 = (xo2 + xo1) / 2;
941 xo2 = xo1 + 1;
942 valuesChanged = true;
943 }
944 if (yo2 <= yo1) {
945 yo1 = (yo2 + yo1) / 2;
946 yo2 = yo1 + 1;
947 valuesChanged = true;
948 }
949
950
951 ///repaint if we toggled off a hovered handle
952 if (lastStateWasHovered) {
953 didSomething = true;
954 }
955
956
957 if (valuesChanged) {
958 ///Keep the points in absolute coordinates
959 _innerBtmLeftDragPos.x = xi1;
960 _innerBtmLeftDragPos.y = yi1;
961 _innerTopRightDragPos.x = xi2;
962 _innerTopRightDragPos.y = yi2;
963 _outerBtmLeftDragPos.x = xo1;
964 _outerBtmLeftDragPos.y = yo1;
965 _outerTopRightDragPos.x = xo2;
966 _outerTopRightDragPos.y = yo2;
967 _centerDragPos.x = xc;
968 _centerDragPos.y = yc;
969 _offsetDragPos.x = xoff;
970 _offsetDragPos.y = yoff;
971 }
972
973 if (didSomething || valuesChanged) {
974 requestRedraw();
975 }
976
977 _lastMousePos = args.penPosition;
978
979 return didSomething || valuesChanged;
980 } // penMotion
981
982 bool
penDown(const PenArgs & args)983 TrackerRegionInteract::penDown(const PenArgs &args)
984 {
985 const OfxPointD& pscale = args.pixelScale;
986 bool didSomething = false;
987 double xi1, xi2, yi1, yi2, xo1, xo2, yo1, yo2, xc, yc, xoff, yoff;
988
989 _innerBtmLeft->getValueAtTime( args.time, xi1, yi1);
990 _innerTopRight->getValueAtTime(args.time, xi2, yi2);
991 _outerBtmLeft->getValueAtTime( args.time, xo1, yo1);
992 _outerTopRight->getValueAtTime(args.time, xo2, yo2);
993 _center->getValueAtTime(args.time, xc, yc);
994 _offset->getValueAtTime(args.time, xoff, yoff);
995
996 ///innerBtmLeft and outerBtmLeft are relative to the center, make them absolute
997 xi1 += (xc + xoff);
998 yi1 += (yc + yoff);
999 xi2 += (xc + xoff);
1000 yi2 += (yc + yoff);
1001 xo1 += (xc + xoff);
1002 yo1 += (yc + yoff);
1003 xo2 += (xc + xoff);
1004 yo2 += (yc + yoff);
1005
1006 // test center first
1007 if ( isNearby(args.penPosition, xc, yc, POINT_TOLERANCE, pscale) ) {
1008 if (_controlDown > 0) {
1009 _ms = eMouseStateDraggingOffset;
1010 } else {
1011 _ms = eMouseStateDraggingCenter;
1012 }
1013 didSomething = true;
1014 } else if ( (xoff != 0) && (yoff != 0) && isNearby(args.penPosition, xc + xoff, yc + yoff, POINT_TOLERANCE, pscale) ) {
1015 _ms = eMouseStateDraggingOffset;
1016 didSomething = true;
1017 } else if ( isNearby(args.penPosition, xi1, yi1, POINT_TOLERANCE, pscale) ) {
1018 _ms = eMouseStateDraggingInnerBtmLeft;
1019 didSomething = true;
1020 } else if ( isNearby(args.penPosition, xi2, yi1, POINT_TOLERANCE, pscale) ) {
1021 _ms = eMouseStateDraggingInnerBtmRight;
1022 didSomething = true;
1023 } else if ( isNearby(args.penPosition, xi1, yi2, POINT_TOLERANCE, pscale) ) {
1024 _ms = eMouseStateDraggingInnerTopLeft;
1025 didSomething = true;
1026 } else if ( isNearby(args.penPosition, xi2, yi2, POINT_TOLERANCE, pscale) ) {
1027 _ms = eMouseStateDraggingInnerTopRight;
1028 didSomething = true;
1029 } else if ( isNearby(args.penPosition, xc + xoff, yi1, POINT_TOLERANCE, pscale) ) {
1030 _ms = eMouseStateDraggingInnerBtmMid;
1031 didSomething = true;
1032 } else if ( isNearby(args.penPosition, xi1, yc + yoff, POINT_TOLERANCE, pscale) ) {
1033 _ms = eMouseStateDraggingInnerMidLeft;
1034 didSomething = true;
1035 } else if ( isNearby(args.penPosition, xc + xoff, yi2, POINT_TOLERANCE, pscale) ) {
1036 _ms = eMouseStateDraggingInnerTopMid;
1037 didSomething = true;
1038 } else if ( isNearby(args.penPosition, xi2, yc + yoff, POINT_TOLERANCE, pscale) ) {
1039 _ms = eMouseStateDraggingInnerMidRight;
1040 didSomething = true;
1041 } else if ( isNearby(args.penPosition, xo1, yo1, POINT_TOLERANCE, pscale) ) {
1042 _ms = eMouseStateDraggingOuterBtmLeft;
1043 didSomething = true;
1044 } else if ( isNearby(args.penPosition, xo2, yo1, POINT_TOLERANCE, pscale) ) {
1045 _ms = eMouseStateDraggingOuterBtmRight;
1046 didSomething = true;
1047 } else if ( isNearby(args.penPosition, xo1, yo2, POINT_TOLERANCE, pscale) ) {
1048 _ms = eMouseStateDraggingOuterTopLeft;
1049 didSomething = true;
1050 } else if ( isNearby(args.penPosition, xo2, yo2, POINT_TOLERANCE, pscale) ) {
1051 _ms = eMouseStateDraggingOuterTopRight;
1052 didSomething = true;
1053 } else if ( isNearby(args.penPosition, xc + xoff, yo1, POINT_TOLERANCE, pscale) ) {
1054 _ms = eMouseStateDraggingOuterBtmMid;
1055 didSomething = true;
1056 } else if ( isNearby(args.penPosition, xo1, yc + yoff, POINT_TOLERANCE, pscale) ) {
1057 _ms = eMouseStateDraggingOuterMidLeft;
1058 didSomething = true;
1059 } else if ( isNearby(args.penPosition, xc + xoff, yo2, POINT_TOLERANCE, pscale) ) {
1060 _ms = eMouseStateDraggingOuterTopMid;
1061 didSomething = true;
1062 } else if ( isNearby(args.penPosition, xo2, yc + yoff, POINT_TOLERANCE, pscale) ) {
1063 _ms = eMouseStateDraggingOuterMidRight;
1064 didSomething = true;
1065 } else {
1066 _ms = eMouseStateIdle;
1067 }
1068
1069
1070 ///Keep the points in absolute coordinates
1071 _innerBtmLeftDragPos.x = xi1;
1072 _innerBtmLeftDragPos.y = yi1;
1073 _innerTopRightDragPos.x = xi2;
1074 _innerTopRightDragPos.y = yi2;
1075 _outerBtmLeftDragPos.x = xo1;
1076 _outerBtmLeftDragPos.y = yo1;
1077 _outerTopRightDragPos.x = xo2;
1078 _outerTopRightDragPos.y = yo2;
1079 _centerDragPos.x = xc;
1080 _centerDragPos.y = yc;
1081 _offsetDragPos.x = xoff;
1082 _offsetDragPos.y = yoff;
1083
1084 _lastMousePos = args.penPosition;
1085
1086 if (didSomething) {
1087 requestRedraw();
1088 }
1089
1090 return didSomething;
1091 } // penDown
1092
1093 bool
isDraggingInnerPoint() const1094 TrackerRegionInteract::isDraggingInnerPoint() const
1095 {
1096 return (_ms == eMouseStateDraggingInnerTopLeft ||
1097 _ms == eMouseStateDraggingInnerTopRight ||
1098 _ms == eMouseStateDraggingInnerBtmLeft ||
1099 _ms == eMouseStateDraggingInnerBtmRight ||
1100 _ms == eMouseStateDraggingInnerTopMid ||
1101 _ms == eMouseStateDraggingInnerMidRight ||
1102 _ms == eMouseStateDraggingInnerBtmMid ||
1103 _ms == eMouseStateDraggingInnerMidLeft);
1104 }
1105
1106 bool
isDraggingOuterPoint() const1107 TrackerRegionInteract::isDraggingOuterPoint() const
1108 {
1109 return (_ms == eMouseStateDraggingOuterTopLeft ||
1110 _ms == eMouseStateDraggingOuterTopRight ||
1111 _ms == eMouseStateDraggingOuterBtmLeft ||
1112 _ms == eMouseStateDraggingOuterBtmRight ||
1113 _ms == eMouseStateDraggingOuterTopMid ||
1114 _ms == eMouseStateDraggingOuterMidRight ||
1115 _ms == eMouseStateDraggingOuterBtmMid ||
1116 _ms == eMouseStateDraggingOuterMidLeft);
1117 }
1118
1119 bool
penUp(const PenArgs & args)1120 TrackerRegionInteract::penUp(const PenArgs &args)
1121 {
1122 if (_ms == eMouseStateIdle) {
1123 return false;
1124 }
1125
1126 const OfxPointD ¢er = _centerDragPos;
1127 const OfxPointD &offset = _offsetDragPos;
1128 _effect->beginEditBlock("setTrackerRegion");
1129 {
1130 OfxPointD btmLeft;
1131 btmLeft.x = _innerBtmLeftDragPos.x - center.x - offset.x;
1132 btmLeft.y = _innerBtmLeftDragPos.y - center.y - offset.y;
1133
1134 _innerBtmLeft->setValue(btmLeft.x, btmLeft.y);
1135
1136 OfxPointD topRight;
1137 topRight.x = _innerTopRightDragPos.x - center.x - offset.x;
1138 topRight.y = _innerTopRightDragPos.y - center.y - offset.y;
1139
1140 _innerTopRight->setValue(topRight.x, topRight.y);
1141 }
1142 {
1143 OfxPointD btmLeft;
1144 btmLeft.x = _outerBtmLeftDragPos.x - center.x - offset.x;
1145 btmLeft.y = _outerBtmLeftDragPos.y - center.y - offset.y;
1146 _outerBtmLeft->setValue(btmLeft.x, btmLeft.y);
1147
1148 OfxPointD topRight;
1149 topRight.x = _outerTopRightDragPos.x - center.x - offset.x;
1150 topRight.y = _outerTopRightDragPos.y - center.y - offset.y;
1151 _outerTopRight->setValue(topRight.x, topRight.y);
1152 }
1153
1154 if (_ms == eMouseStateDraggingCenter) {
1155 _center->setValueAtTime(args.time, _centerDragPos.x, _centerDragPos.y);
1156 } else if (_ms == eMouseStateDraggingOffset) {
1157 _offset->setValueAtTime(args.time, _offsetDragPos.x, _offsetDragPos.y);
1158 }
1159 _effect->endEditBlock();
1160
1161 _ms = eMouseStateIdle;
1162
1163 requestRedraw();
1164
1165 return true;
1166 }
1167
1168 bool
keyDown(const KeyArgs & args)1169 TrackerRegionInteract::keyDown(const KeyArgs &args)
1170 {
1171 if ( (args.keySymbol == kOfxKey_Control_L) || (args.keySymbol == kOfxKey_Control_R) ) {
1172 ++_controlDown;
1173 } else if ( (args.keySymbol == kOfxKey_Alt_L) || (args.keySymbol == kOfxKey_Alt_R) ) {
1174 ++_altDown;
1175 }
1176
1177 return false;
1178 }
1179
1180 bool
keyUp(const KeyArgs & args)1181 TrackerRegionInteract::keyUp(const KeyArgs &args)
1182 {
1183 if ( (args.keySymbol == kOfxKey_Control_L) || (args.keySymbol == kOfxKey_Control_R) ) {
1184 if (_controlDown > 0) {
1185 --_controlDown;
1186 }
1187 } else if ( (args.keySymbol == kOfxKey_Alt_L) || (args.keySymbol == kOfxKey_Alt_R) ) {
1188 if (_altDown > 0) {
1189 --_altDown;
1190 }
1191 }
1192
1193 return false;
1194 }
1195
1196 /** @brief Called when the interact is loses input focus */
1197 void
loseFocus(const FocusArgs &)1198 TrackerRegionInteract::loseFocus(const FocusArgs & /*args*/)
1199 {
1200 // reset the modifiers state
1201 _controlDown = 0;
1202 _altDown = 0;
1203 _ds = eDrawStateInactive;
1204 _ms = eMouseStateIdle;
1205 }
1206 } // namespace OFX
1207