]> git.tdb.fi Git - libs/gui.git/commitdiff
Refactor GestureDetector and add some comments to it
authorMikko Rasa <tdb@tdb.fi>
Thu, 19 Nov 2015 21:12:38 +0000 (23:12 +0200)
committerMikko Rasa <tdb@tdb.fi>
Fri, 20 Nov 2015 07:25:56 +0000 (09:25 +0200)
source/input/gesturedetector.cpp
source/input/gesturedetector.h

index c43e65a0d61c5be930e2672ae356f702c8059bf9..112121ee456a86736876b01e23c09a394045c498 100644 (file)
@@ -54,59 +54,62 @@ string GestureDetector::get_axis_name(unsigned axis) const
 
 void GestureDetector::touch_down(unsigned btn)
 {
-       if(btn<3)
-       {
-               TouchPoint &p = points[btn];
-               p.down = true;
-               p.down_x = p.x;
-               p.down_y = p.y;
-       }
+       if(btn>=MAX_POINTS)
+               return;
+
+       TouchPoint &p = points[btn];
+       p.down = true;
+       p.down_x = p.x;
+       p.down_y = p.y;
+       p.x = 0;
+       p.y = 0;
+       p.threshold_exceeded = false;
 }
 
 void GestureDetector::touch_up(unsigned btn)
 {
-       if(btn<3)
-       {
-               TouchPoint &p = points[btn];
-               p.down = false;
+       if(btn>=MAX_POINTS)
+               return;
 
-               if(active_points&(1<<btn))
-               {
-                       set_button_state(current_gesture, false, true);
-                       set_axis_value(2, 0, false);
-                       current_gesture = GESTURE_NONE;
-                       active_points = 0;
-                       invalid_gesture = true;
-               }
+       TouchPoint &p = points[btn];
+       p.x += p.down_x;
+       p.y += p.down_y;
+       p.down = false;
 
-               if(invalid_gesture)
-               {
-                       invalid_gesture = false;
-                       for(unsigned i=0; i<3; ++i)
-                               if(points[i].down)
-                                       invalid_gesture = true;
-               }
+       if(active_points&(1<<btn))
+               end_gesture();
+
+       if(current_gesture==GESTURE_NONE)
+       {
+               // New gesture can't start until all points have been released.
+               invalid_gesture = false;
+               for(unsigned i=0; (i<MAX_POINTS && !invalid_gesture); ++i)
+                       invalid_gesture = points[i].down;
        }
 }
 
 void GestureDetector::touch_move(unsigned axis, float value, float)
 {
-       if(axis<6)
+       if(axis>=MAX_POINTS*2)
+               return;
+
+       unsigned i = axis>>1;
+       TouchPoint &p = points[i];
+       // Track relative position when pressed, absolute when not.
+       if(axis&1)
+               p.y = (p.down ? value-p.down_y : value);
+       else
+               p.x = (p.down ? value-p.down_x : value);
+
+       if(p.down)
        {
-               unsigned i = axis>>1;
-               TouchPoint &p = points[i];
-               if(axis&1)
-                       p.y = value;
-               else
-                       p.x = value;
+               if(p.x*p.x/threshold_x_sq+p.y*p.y/threshold_y_sq>=1)
+                       p.threshold_exceeded = true;
 
-               if(p.down)
-               {
-                       if(current_gesture==GESTURE_NONE && !invalid_gesture)
-                               start_gesture();
-                       else if(active_points&(1<<i))
-                               update_progress();
-               }
+               if(current_gesture==GESTURE_NONE && !invalid_gesture)
+                       start_gesture();
+               else if(active_points&(1<<i))
+                       update_progress();
        }
 }
 
@@ -116,47 +119,47 @@ void GestureDetector::start_gesture()
        if(!p.down)
                return;
 
-       float dx = p.x-p.down_x;
-       float dy = p.y-p.down_y;
-       if(dx*dx/threshold_x_sq+dy*dy/threshold_y_sq<1)
+       /* At least one point needs to have moved more than the threshold to start
+       the gesture. */
+       bool threshold_exceeded = false;
+       for(unsigned i=0; (i<MAX_POINTS && !threshold_exceeded); ++i)
+               threshold_exceeded = (points[i].down && points[i].threshold_exceeded);
+       if(!threshold_exceeded)
                return;
 
        invalid_gesture = false;
        if(points[1].down)
        {
                TouchPoint &p2 = points[1];
-               float dx2 = p2.x-p2.down_x;
-               float dy2 = p2.y-p2.down_y;
                float ddx = p.down_x-p2.down_x;
                float ddy = p.down_y-p2.down_y;
-               /* TODO Should the second point be also required to exceeded the
-               threshold? */
-               if(dx*dx2+dy*dy2<0 && (dx*ddx+dy*ddy)*(dx2*ddx+dy2*ddy)<0)
-               {
+               if(p.x*p2.x+p.y*p2.y<0 && (p.x*ddx+p.y*ddy)*(p2.x*ddx+p2.y*ddy)<0)
+                       /* If the points moved in different directions and also both away from
+                       or towards the other, it's a pinch gesture. */
                        current_gesture = GESTURE_PINCH;
-                       active_points = 3;
-               }
                else
                        invalid_gesture = true;
 
                if(current_gesture!=GESTURE_NONE)
                {
+                       active_points = 3;
                        set_axis_value(0, (p.down_x+p2.down_x)/2, true);
                        set_axis_value(1, (p.down_y+p2.down_y)/2, true);
                }
        }
        else
        {
-               active_points = 1;
-               if(abs(dx)>2*abs(dy))
-                       current_gesture = (dx>0 ? GESTURE_SWIPE_RIGHT : GESTURE_SWIPE_LEFT);
-               else if(abs(dy)>2*abs(dx))
-                       current_gesture = (dy>0 ? GESTURE_SWIPE_UP : GESTURE_SWIPE_DOWN);
+               // Allow a maximum deviation of about 26° to recognize a swipe gesture.
+               if(abs(p.x)>2*abs(p.y))
+                       current_gesture = (p.x>0 ? GESTURE_SWIPE_RIGHT : GESTURE_SWIPE_LEFT);
+               else if(abs(p.y)>2*abs(p.x))
+                       current_gesture = (p.y>0 ? GESTURE_SWIPE_UP : GESTURE_SWIPE_DOWN);
                else
                        invalid_gesture = true;
 
                if(current_gesture!=GESTURE_NONE)
                {
+                       active_points = 1;
                        set_axis_value(0, p.down_x, true);
                        set_axis_value(1, p.down_y, true);
                }
@@ -173,24 +176,34 @@ void GestureDetector::update_progress()
        TouchPoint &p = points[0];
 
        if(current_gesture==GESTURE_SWIPE_DOWN)
-               set_axis_value(2, p.down_y-p.y, true);
+               set_axis_value(2, -p.y, true);
        else if(current_gesture==GESTURE_SWIPE_UP)
-               set_axis_value(2, p.y-p.down_y, true);
+               set_axis_value(2, p.y, true);
        else if(current_gesture==GESTURE_SWIPE_LEFT)
-               set_axis_value(2, p.down_x-p.x, true);
+               set_axis_value(2, -p.x, true);
        else if(current_gesture==GESTURE_SWIPE_RIGHT)
-               set_axis_value(2, p.x-p.down_x, true);
+               set_axis_value(2, p.x, true);
        else if(current_gesture==GESTURE_PINCH)
        {
                TouchPoint &p2 = points[1];
-               float dx = p.x-p2.x;
-               float dy = p.y-p2.y;
+               /* Pinch progress is the ratio between the current distance of the points
+               and their distance when they were pressed. */
                float ddx = p.down_x-p2.down_x;
                float ddy = p.down_y-p2.down_y;
+               float dx = ddx+p.x-p2.x;
+               float dy = ddy+p.y-p2.y;
                set_axis_value(2, sqrt(dx*dx+dy*dy)/sqrt(ddx*ddx+ddy*ddy)-1, true);
        }
 }
 
+void GestureDetector::end_gesture()
+{
+       set_button_state(current_gesture, false, true);
+       set_axis_value(2, 0, false);
+       current_gesture = GESTURE_NONE;
+       active_points = 0;
+}
+
 void GestureDetector::window_resized(unsigned w, unsigned h)
 {
        threshold_x_sq = 2500.0/(w*w);
index 2b5ea3c3203219d2153b78cd22e19c65aed3d291..b2d0d4fdd36139411c3fb61e8b1062e575ea3ba4 100644 (file)
@@ -27,6 +27,11 @@ absolute values greater than one.
 class GestureDetector: public Device
 {
 private:
+       enum
+       {
+               MAX_POINTS = 3
+       };
+
        struct TouchPoint
        {
                bool down;
@@ -34,12 +39,13 @@ private:
                float down_y;
                float x;
                float y;
+               bool threshold_exceeded;
 
                TouchPoint();
        };
 
        Touchscreen &touchscreen;
-       TouchPoint points[3];
+       TouchPoint points[MAX_POINTS];
        Gesture current_gesture;
        unsigned active_points;
        bool invalid_gesture;
@@ -58,6 +64,7 @@ private:
        void touch_move(unsigned, float, float);
        void start_gesture();
        void update_progress();
+       void end_gesture();
        void window_resized(unsigned, unsigned);
 };