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 &paramName)
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 &center = _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