]> git.tdb.fi Git - libs/gui.git/blob - source/input/gesturedetector.cpp
Add a gesture detector input device
[libs/gui.git] / source / input / gesturedetector.cpp
1 #include <cmath>
2 #include <sigc++/bind_return.h>
3 #include "gesturedetector.h"
4 #include "touchscreen.h"
5
6 using namespace std;
7
8 namespace Msp {
9 namespace Input {
10
11 GestureDetector::GestureDetector(Touchscreen &ts):
12         touchscreen(ts),
13         current_gesture(GESTURE_NONE),
14         active_points(0),
15         invalid_gesture(false)
16 {
17         name = "Gesture";
18
19         touchscreen.signal_button_press.connect(sigc::bind_return(sigc::mem_fun(this, &GestureDetector::touch_down), false));
20         touchscreen.signal_button_release.connect(sigc::bind_return(sigc::mem_fun(this, &GestureDetector::touch_up), false));
21         touchscreen.signal_axis_motion.connect(sigc::bind_return(sigc::mem_fun(this, &GestureDetector::touch_move), false));
22         Graphics::Window &window = touchscreen.get_window();
23         window.signal_resize.connect(sigc::mem_fun(this, &GestureDetector::window_resized));
24         window_resized(window.get_width(), window.get_height());
25 }
26
27 string GestureDetector::get_button_name(unsigned btn) const
28 {
29         if(btn==GESTURE_SWIPE_DOWN)
30                 return "Swipe down";
31         else if(btn==GESTURE_SWIPE_UP)
32                 return "Swipe up";
33         else if(btn==GESTURE_SWIPE_LEFT)
34                 return "Swipe left";
35         else if(btn==GESTURE_SWIPE_RIGHT)
36                 return "Swipe right";
37         else if(btn==GESTURE_PINCH)
38                 return "Pinch";
39         else
40                 return Device::get_button_name(btn);
41 }
42
43 string GestureDetector::get_axis_name(unsigned axis) const
44 {
45         if(axis==0)
46                 return "X";
47         else if(axis==1)
48                 return "Y";
49         else if(axis==2)
50                 return "Progress";
51         else
52                 return Device::get_axis_name(axis);
53 }
54
55 void GestureDetector::touch_down(unsigned btn)
56 {
57         if(btn<3)
58         {
59                 TouchPoint &p = points[btn];
60                 p.down = true;
61                 p.down_x = p.x;
62                 p.down_y = p.y;
63         }
64 }
65
66 void GestureDetector::touch_up(unsigned btn)
67 {
68         if(btn<3)
69         {
70                 TouchPoint &p = points[btn];
71                 p.down = false;
72
73                 if(active_points&(1<<btn))
74                 {
75                         set_button_state(current_gesture, false, true);
76                         set_axis_value(2, 0, false);
77                         current_gesture = GESTURE_NONE;
78                         active_points = 0;
79                         invalid_gesture = true;
80                 }
81
82                 if(invalid_gesture)
83                 {
84                         invalid_gesture = false;
85                         for(unsigned i=0; i<3; ++i)
86                                 if(points[i].down)
87                                         invalid_gesture = true;
88                 }
89         }
90 }
91
92 void GestureDetector::touch_move(unsigned axis, float value, float)
93 {
94         if(axis<6)
95         {
96                 unsigned i = axis>>1;
97                 TouchPoint &p = points[i];
98                 if(axis&1)
99                         p.y = value;
100                 else
101                         p.x = value;
102
103                 if(p.down)
104                 {
105                         if(current_gesture==GESTURE_NONE && !invalid_gesture)
106                                 start_gesture();
107                         else if(active_points&(1<<i))
108                                 update_progress();
109                 }
110         }
111 }
112
113 void GestureDetector::start_gesture()
114 {
115         TouchPoint &p = points[0];
116         if(!p.down)
117                 return;
118
119         float dx = p.x-p.down_x;
120         float dy = p.y-p.down_y;
121         if(dx*dx/threshold_x_sq+dy*dy/threshold_y_sq<1)
122                 return;
123
124         invalid_gesture = false;
125         if(points[1].down)
126         {
127                 TouchPoint &p2 = points[1];
128                 float dx2 = p2.x-p2.down_x;
129                 float dy2 = p2.y-p2.down_y;
130                 float ddx = p.down_x-p2.down_x;
131                 float ddy = p.down_y-p2.down_y;
132                 /* TODO Should the second point be also required to exceeded the
133                 threshold? */
134                 if(dx*dx2+dy*dy2<0 && (dx*ddx+dy*ddy)*(dx2*ddx+dy2*ddy)<0)
135                 {
136                         current_gesture = GESTURE_PINCH;
137                         active_points = 3;
138                 }
139                 else
140                         invalid_gesture = true;
141
142                 if(current_gesture!=GESTURE_NONE)
143                 {
144                         set_axis_value(0, (p.down_x+p2.down_x)/2, true);
145                         set_axis_value(1, (p.down_y+p2.down_y)/2, true);
146                 }
147         }
148         else
149         {
150                 active_points = 1;
151                 if(abs(dx)>2*abs(dy))
152                         current_gesture = (dx>0 ? GESTURE_SWIPE_RIGHT : GESTURE_SWIPE_LEFT);
153                 else if(abs(dy)>2*abs(dx))
154                         current_gesture = (dy>0 ? GESTURE_SWIPE_UP : GESTURE_SWIPE_DOWN);
155                 else
156                         invalid_gesture = true;
157
158                 if(current_gesture!=GESTURE_NONE)
159                 {
160                         set_axis_value(0, p.down_x, true);
161                         set_axis_value(1, p.down_y, true);
162                 }
163         }
164
165         update_progress();
166
167         if(current_gesture!=GESTURE_NONE)
168                 set_button_state(current_gesture, true, true);
169 }
170
171 void GestureDetector::update_progress()
172 {
173         TouchPoint &p = points[0];
174
175         if(current_gesture==GESTURE_SWIPE_DOWN)
176                 set_axis_value(2, p.down_y-p.y, true);
177         else if(current_gesture==GESTURE_SWIPE_UP)
178                 set_axis_value(2, p.y-p.down_y, true);
179         else if(current_gesture==GESTURE_SWIPE_LEFT)
180                 set_axis_value(2, p.down_x-p.x, true);
181         else if(current_gesture==GESTURE_SWIPE_RIGHT)
182                 set_axis_value(2, p.x-p.down_x, true);
183         else if(current_gesture==GESTURE_PINCH)
184         {
185                 TouchPoint &p2 = points[1];
186                 float dx = p.x-p2.x;
187                 float dy = p.y-p2.y;
188                 float ddx = p.down_x-p2.down_x;
189                 float ddy = p.down_y-p2.down_y;
190                 set_axis_value(2, sqrt(dx*dx+dy*dy)/sqrt(ddx*ddx+ddy*ddy)-1, true);
191         }
192 }
193
194 void GestureDetector::window_resized(unsigned w, unsigned h)
195 {
196         threshold_x_sq = 2500.0/(w*w);
197         threshold_y_sq = 2500.0/(h*h);
198 }
199
200
201 GestureDetector::TouchPoint::TouchPoint():
202         down(false),
203         down_x(0),
204         down_y(0),
205         x(0),
206         y(0)
207 { }
208
209 } // namespace Input
210 } // namespace Msp