1 #define _USE_MATH_DEFINES
3 #include <sigc++/bind_return.h>
4 #include "gesturedetector.h"
5 #include "touchscreen.h"
12 GestureDetector::GestureDetector(Touchscreen &ts):
14 current_gesture(GESTURE_NONE),
16 pending_tap(GESTURE_NONE),
17 invalid_gesture(false)
21 touchscreen.signal_button_press.connect(sigc::bind_return(sigc::mem_fun(this, &GestureDetector::touch_down), false));
22 touchscreen.signal_button_release.connect(sigc::bind_return(sigc::mem_fun(this, &GestureDetector::touch_up), false));
23 touchscreen.signal_axis_motion.connect(sigc::bind_return(sigc::mem_fun(this, &GestureDetector::touch_move), false));
24 Graphics::Window &window = touchscreen.get_window();
25 window.signal_resize.connect(sigc::mem_fun(this, &GestureDetector::window_resized));
26 window_resized(window.get_width(), window.get_height());
29 string GestureDetector::get_button_name(unsigned btn) const
33 else if(btn==GESTURE_TAP_2)
34 return "Two-finger tap";
35 else if(btn==GESTURE_DRAG)
37 else if(btn==GESTURE_DRAG_2)
38 return "Two-finger drag";
39 else if(btn==GESTURE_PINCH)
41 else if(btn==GESTURE_ROTATE)
44 return Device::get_button_name(btn);
47 string GestureDetector::get_axis_name(unsigned axis) const
58 return Device::get_axis_name(axis);
61 void GestureDetector::touch_down(unsigned btn)
66 TouchPoint &p = points[btn];
72 p.threshold_exceeded = false;
74 if(current_gesture==GESTURE_NONE && !invalid_gesture)
77 pending_tap = GESTURE_TAP;
79 pending_tap = GESTURE_TAP_2;
83 void GestureDetector::touch_up(unsigned btn)
88 TouchPoint &p = points[btn];
93 if(active_points&(1<<btn))
96 if(current_gesture==GESTURE_NONE)
98 // New gesture can't start until all points have been released.
99 invalid_gesture = false;
100 for(unsigned i=0; (i<MAX_POINTS && !invalid_gesture); ++i)
101 invalid_gesture = points[i].down;
103 if(!invalid_gesture && pending_tap!=GESTURE_NONE)
105 unsigned n_points = min<unsigned>((pending_tap-GESTURE_TAP)+1, MAX_POINTS);
106 set_gesture_location((1<<n_points)-1);
107 set_button_state(pending_tap, true, true);
108 set_button_state(pending_tap, false, true);
110 pending_tap = GESTURE_NONE;
115 void GestureDetector::touch_move(unsigned axis, float value, float)
117 if(axis>=MAX_POINTS*2)
120 unsigned i = axis>>1;
121 TouchPoint &p = points[i];
122 // Track relative position when pressed, absolute when not.
124 p.y = (p.down ? value-p.down_y : value);
126 p.x = (p.down ? value-p.down_x : value);
130 if(p.x*p.x/threshold_x_sq+p.y*p.y/threshold_y_sq>=1)
132 p.threshold_exceeded = true;
133 pending_tap = GESTURE_NONE;
136 if(current_gesture==GESTURE_NONE && !invalid_gesture)
138 else if(active_points&(1<<i))
143 void GestureDetector::start_gesture()
145 TouchPoint &p = points[0];
149 /* At least one point needs to have moved more than the threshold to start
151 bool threshold_exceeded = false;
152 for(unsigned i=0; (i<MAX_POINTS && !threshold_exceeded); ++i)
153 threshold_exceeded = (points[i].down && points[i].threshold_exceeded);
154 if(!threshold_exceeded)
157 invalid_gesture = false;
160 TouchPoint &p2 = points[1];
161 float ddx = p.down_x-p2.down_x;
162 float ddy = p.down_y-p2.down_y;
163 float away = p.x*ddx+p.y*ddy;
164 float turn = p.y*ddx-p.x*ddy;
165 float away2 = -(p2.x*ddx+p2.y*ddy);
166 float turn2 = -(p2.y*ddx-p2.x*ddy);
167 if(away*away2>0 && abs(away)>abs(turn) && abs(away2)>abs(turn2))
168 /* If the points moved away from or towards each other without rotating
169 significantly, it's a pinch gesture. */
170 current_gesture = GESTURE_PINCH;
171 else if(turn*turn2>0 && abs(turn)>abs(away) && abs(turn2)>abs(away2))
172 /* If the points both turned in the same direction without significant
173 changes in distance, it's a rotate gesture. */
174 current_gesture = GESTURE_ROTATE;
175 else if((p.x*p2.x+p.y*p2.y)>2*abs(p.x*p2.y-p.y*p2.x))
176 // If both points moved in the same direction, it's a two-finger drag.
177 current_gesture = GESTURE_DRAG_2;
179 if(current_gesture!=GESTURE_NONE)
184 current_gesture = GESTURE_DRAG;
189 if(current_gesture!=GESTURE_NONE)
191 set_gesture_location(active_points);
193 set_button_state(current_gesture, true, true);
196 invalid_gesture = true;
199 void GestureDetector::set_gesture_location(unsigned mask)
204 for(unsigned i=0; i<MAX_POINTS; ++i)
207 x += points[i].down_x;
208 y += points[i].down_y;
212 set_axis_value(0, x/count, true);
213 set_axis_value(1, y/count, true);
216 void GestureDetector::update_progress()
218 TouchPoint &p = points[0];
220 if(current_gesture==GESTURE_DRAG)
222 set_axis_value(2, p.x, true);
223 set_axis_value(3, p.y, true);
225 else if(current_gesture==GESTURE_DRAG_2)
227 TouchPoint &p2 = points[1];
228 set_axis_value(2, (p.x+p2.x)/2, true);
229 set_axis_value(3, (p.y+p2.y)/2, true);
231 else if(current_gesture==GESTURE_PINCH || current_gesture==GESTURE_ROTATE)
233 TouchPoint &p2 = points[1];
234 /* Pinch progress is the ratio between the current distance of the points
235 and their distance when they were pressed. */
236 float ddx = p.down_x-p2.down_x;
237 float ddy = p.down_y-p2.down_y;
238 float dx = ddx+p.x-p2.x;
239 float dy = ddy+p.y-p2.y;
240 if(current_gesture==GESTURE_PINCH)
242 set_axis_value(2, sqrt(dx*dx+dy*dy)/sqrt(ddx*ddx+ddy*ddy)-1, true);
243 set_axis_value(3, 0, true);
245 else if(current_gesture==GESTURE_ROTATE)
247 set_axis_value(2, atan2(dy*ddx-dx*ddy, dx*ddx+dy*ddy)/M_PI/2, true);
248 set_axis_value(3, 0, true);
253 void GestureDetector::end_gesture()
255 set_button_state(current_gesture, false, true);
256 set_axis_value(2, 0, false);
257 current_gesture = GESTURE_NONE;
259 pending_tap = GESTURE_NONE;
262 void GestureDetector::window_resized(unsigned w, unsigned h)
264 threshold_x_sq = 2500.0/(w*w);
265 threshold_y_sq = 2500.0/(h*h);
269 GestureDetector::TouchPoint::TouchPoint():
275 threshold_exceeded(false)