X-Git-Url: http://git.tdb.fi/?a=blobdiff_plain;f=source%2Finput%2Fgesturedetector.cpp;h=97e3d56f0fc53a3e4a7eb0e271a2a0c060303603;hb=81a662b025809d71d8dc67a2108ad7d54ba7c645;hp=c43e65a0d61c5be930e2672ae356f702c8059bf9;hpb=f9ebab54c3debe98e108790be9c21f43f4de116f;p=libs%2Fgui.git diff --git a/source/input/gesturedetector.cpp b/source/input/gesturedetector.cpp index c43e65a..97e3d56 100644 --- a/source/input/gesturedetector.cpp +++ b/source/input/gesturedetector.cpp @@ -1,3 +1,4 @@ +#define _USE_MATH_DEFINES #include #include #include "gesturedetector.h" @@ -11,7 +12,7 @@ namespace Input { GestureDetector::GestureDetector(Touchscreen &ts): touchscreen(ts), current_gesture(GESTURE_NONE), - active_points(0), + pending_tap(GESTURE_NONE), invalid_gesture(false) { name = "Gesture"; @@ -26,16 +27,22 @@ GestureDetector::GestureDetector(Touchscreen &ts): string GestureDetector::get_button_name(unsigned btn) const { - if(btn==GESTURE_SWIPE_DOWN) - return "Swipe down"; - else if(btn==GESTURE_SWIPE_UP) - return "Swipe up"; - else if(btn==GESTURE_SWIPE_LEFT) - return "Swipe left"; - else if(btn==GESTURE_SWIPE_RIGHT) - return "Swipe right"; + if(btn==GESTURE_TAP) + return "Tap"; + else if(btn==GESTURE_TAP_2) + return "Two-finger tap"; + else if(btn==GESTURE_TAP_3) + return "Three-finger tap"; + else if(btn==GESTURE_DRAG) + return "Drag"; + else if(btn==GESTURE_DRAG_2) + return "Two-finger drag"; + else if(btn==GESTURE_DRAG_3) + return "Three-finger drag"; else if(btn==GESTURE_PINCH) return "Pinch"; + else if(btn==GESTURE_ROTATE) + return "Rotate"; else return Device::get_button_name(btn); } @@ -47,66 +54,93 @@ string GestureDetector::get_axis_name(unsigned axis) const else if(axis==1) return "Y"; else if(axis==2) - return "Progress"; + return "Delta X"; + else if(axis==3) + return "Delta Y"; else return Device::get_axis_name(axis); } void GestureDetector::touch_down(unsigned btn) { - if(btn<3) + 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; + + if(current_gesture==GESTURE_NONE && !invalid_gesture) { - TouchPoint &p = points[btn]; - p.down = true; - p.down_x = p.x; - p.down_y = p.y; + if(btn==0) + pending_tap = GESTURE_TAP; + else if(btn==1) + pending_tap = GESTURE_TAP_2; + else if(btn==2) + pending_tap = GESTURE_TAP_3; } } void GestureDetector::touch_up(unsigned btn) { - if(btn<3) + if(btn>=MAX_POINTS) + return; + + TouchPoint &p = points[btn]; + p.x += p.down_x; + p.y += p.down_y; + p.down = false; + + if(btn=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.down) + if(p.x*p.x/threshold_x_sq+p.y*p.y/threshold_y_sq>=1) { - if(current_gesture==GESTURE_NONE && !invalid_gesture) - start_gesture(); - else if(active_points&(1<2*abs(pi.x*pj.y-pi.y*pj.x)); + } + + if(same_direction) + current_gesture = GESTURE_DRAG_3; + } + else 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) - { + float away = p.x*ddx+p.y*ddy; + float turn = p.y*ddx-p.x*ddy; + float away2 = -(p2.x*ddx+p2.y*ddy); + float turn2 = -(p2.y*ddx-p2.x*ddy); + if(away*away2>0 && abs(away)>abs(turn) && abs(away2)>abs(turn2)) + /* If the points moved away from or towards each other without rotating + significantly, it's a pinch gesture. */ current_gesture = GESTURE_PINCH; - active_points = 3; - } - else - invalid_gesture = true; + else if(turn*turn2>0 && abs(turn)>abs(away) && abs(turn2)>abs(away2)) + /* If the points both turned in the same direction without significant + changes in distance, it's a rotate gesture. */ + current_gesture = GESTURE_ROTATE; + else if((p.x*p2.x+p.y*p2.y)>2*abs(p.x*p2.y-p.y*p2.x)) + // If both points moved in the same direction, it's a two-finger drag. + current_gesture = GESTURE_DRAG_2; + } + else + current_gesture = GESTURE_DRAG; - if(current_gesture!=GESTURE_NONE) - { - set_axis_value(0, (p.down_x+p2.down_x)/2, true); - set_axis_value(1, (p.down_y+p2.down_y)/2, true); - } + if(current_gesture!=GESTURE_NONE) + { + set_gesture_location(gesture_points(current_gesture)); + update_progress(); + set_button_state(current_gesture, true, true); } else + invalid_gesture = true; +} + +void GestureDetector::set_gesture_location(unsigned n_points) +{ + float x = 0; + float y = 0; + for(unsigned i=0; i2*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); - else - invalid_gesture = true; - - if(current_gesture!=GESTURE_NONE) - { - set_axis_value(0, p.down_x, true); - set_axis_value(1, p.down_y, true); - } + x += points[i].down_x; + y += points[i].down_y; } - update_progress(); + set_axis_value(0, x/n_points, true); + set_axis_value(1, y/n_points, true); +} - if(current_gesture!=GESTURE_NONE) - set_button_state(current_gesture, true, true); +void GestureDetector::set_gesture_delta(unsigned n_points) +{ + float x = 0; + float y = 0; + for(unsigned i=0; i=GESTURE_DRAG && current_gesture<=GESTURE_DRAG_3) + set_gesture_delta(gesture_points(current_gesture)); + else if(current_gesture==GESTURE_PINCH || current_gesture==GESTURE_ROTATE) { + TouchPoint &p = points[0]; 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; - set_axis_value(2, sqrt(dx*dx+dy*dy)/sqrt(ddx*ddx+ddy*ddy)-1, true); + float dx = ddx+p.x-p2.x; + float dy = ddy+p.y-p2.y; + if(current_gesture==GESTURE_PINCH) + { + set_axis_value(2, sqrt(dx*dx+dy*dy)/sqrt(ddx*ddx+ddy*ddy)-1, true); + set_axis_value(3, 0, true); + } + else if(current_gesture==GESTURE_ROTATE) + { + set_axis_value(2, atan2(dy*ddx-dx*ddy, dx*ddx+dy*ddy)/M_PI/2, true); + set_axis_value(3, 0, true); + } } } +void GestureDetector::end_gesture() +{ + set_button_state(current_gesture, false, true); + set_axis_value(2, 0, false); + current_gesture = GESTURE_NONE; + pending_tap = GESTURE_NONE; +} + void GestureDetector::window_resized(unsigned w, unsigned h) { threshold_x_sq = 2500.0/(w*w); @@ -203,8 +283,27 @@ GestureDetector::TouchPoint::TouchPoint(): down_x(0), down_y(0), x(0), - y(0) + y(0), + threshold_exceeded(false) { } + +unsigned gesture_points(Gesture gesture) +{ + switch(gesture) + { + case GESTURE_NONE: return 0; + case GESTURE_TAP: return 1; + case GESTURE_TAP_2: return 2; + case GESTURE_TAP_3: return 3; + case GESTURE_DRAG: return 1; + case GESTURE_DRAG_2: return 2; + case GESTURE_DRAG_3: return 3; + case GESTURE_PINCH: return 2; + case GESTURE_ROTATE: return 2; + default: throw invalid_argument("gesture_points"); + } +} + } // namespace Input } // namespace Msp