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();
}
}
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);
}
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);