Ticket #791: mythstroke.cpp

File mythstroke.cpp, 8.7 KB (added by mfgalizi@…, 20 years ago)

Gesture stuff

Line 
1/**
2 * @file mythstroke.cpp
3 * @author Micah F. Galizia <mfgalizi@csd.uwo.ca>
4 * @brief A C++ ripoff of the stroke library, modified for MythTV.
5 *
6 * Copyright (C) 2005 Micah Galizia
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2, or (at
11 * your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
21 * 02111-1307, USA
22 *
23 * This library contains code originally obtained from the libstroke
24 * library, which was written by Mark F. Willey. If I am in offense
25 * of any copyright or anything, please let me know and I will make
26 * the appropriate fixes.
27 */
28#ifndef MYTHSTROKE_CPP
29#define MYTHSTROKE_CPP
30
31using namespace std;
32
33#include "mythstroke.h"
34#include <math.h>
35
36
37MythStroke::MythStroke(size_t max_points, size_t min_points,
38 size_t max_sequence, size_t scale_ratio,
39 float bin_percent): recording(false),
40 min_x(10000),
41 max_x(-1),
42 min_y(10000),
43 max_y(-1),
44 max_points(max_points),
45 min_points(min_points),
46 max_sequence(max_sequence),
47 scale_ratio(scale_ratio),
48 bin_percent(bin_percent)
49{
50 /* Click */
51 sequences.insert("5", MythGestureEvent::Click);
52
53 /* Lines */
54 sequences.insert("456", MythGestureEvent::Right);
55 sequences.insert("654", MythGestureEvent::Left);
56 sequences.insert("258", MythGestureEvent::Down);
57 sequences.insert("852", MythGestureEvent::Up);
58
59 /* Diagonals */
60 sequences.insert("951", MythGestureEvent::UpLeft);
61 sequences.insert("753", MythGestureEvent::UpRight);
62 sequences.insert("159", MythGestureEvent::DownRight);
63 sequences.insert("357", MythGestureEvent::DownLeft);
64
65 /* Double Lines*/
66 sequences.insert("96321",MythGestureEvent::UpThenLeft);
67 sequences.insert("74123",MythGestureEvent::UpThenRight);
68 sequences.insert("36987",MythGestureEvent::DownThenLeft);
69 sequences.insert("14789",MythGestureEvent::DownThenRight);
70 sequences.insert("32147",MythGestureEvent::LeftThenDown);
71 sequences.insert("98741",MythGestureEvent::LeftThenUp);
72 sequences.insert("12369",MythGestureEvent::RightThenDown);
73 sequences.insert("78963",MythGestureEvent::RightThenUp);
74
75}
76
77
78
79
80/* comments in header */
81void MythStroke::adjustExtremes(int x, int y)
82{
83 if (x < min_x) min_x = x;
84 if (x > max_x) max_x = x;
85 if (y < min_y) min_y = y;
86 if (y > max_y) max_y = y;
87}
88
89
90
91/* comments in header */
92int determineBin (const QPoint & p, int x1, int x2, int y1, int y2)
93{
94 int bin_num = 1;
95 if (p.x() > x1) bin_num += 1;
96 if (p.x() > x2) bin_num += 1;
97 if (p.y() > y1) bin_num += 3;
98 if (p.y() > y2) bin_num += 3;
99
100 return bin_num;
101}
102
103
104
105/* comments in header */
106QString MythStroke::translate(void)
107{
108 size_t total_points = points.count();
109
110 if (total_points > max_points)
111 {
112 points.clear();
113 return "0";
114 }
115
116 /* treat any stroke with less than the minimum number of points as
117 * a click (not a drag), which is the center bin */
118 if (total_points < min_points)
119 {
120 points.clear();
121 return "5";
122 }
123
124 QString sequence;
125
126 /* number of bins recorded in the stroke */
127 size_t sequence_count = 0;
128
129 /* points-->sequence translation scratch variables */
130 int prev_bin = 0;
131 int current_bin = 0;
132 int bin_count = 0;
133
134 /*flag indicating the start of a stroke - always count it in the sequence*/
135 bool first_bin = true;
136
137 /* bin boundary and size variables */
138 int delta_x, delta_y;
139 int bound_x_1, bound_x_2;
140 int bound_y_1, bound_y_2;
141
142 /* determine size of grid */
143 delta_x = max_x - min_x;
144 delta_y = max_y - min_y;
145
146 /* calculate bin boundary positions */
147 bound_x_1 = min_x + (delta_x / 3);
148 bound_x_2 = min_x + 2 * (delta_x / 3);
149
150 bound_y_1 = min_y + (delta_y / 3);
151 bound_y_2 = min_y + 2 * (delta_y / 3);
152
153 if (delta_x > scale_ratio * delta_y)
154 {
155 bound_y_1 = (max_y + min_y - delta_x) / 2 + (delta_x / 3);
156 bound_y_2 = (max_y + min_y - delta_x) / 2 + 2 * (delta_x / 3);
157 } else if (delta_y > scale_ratio * delta_x)
158 {
159 bound_x_1 = (max_x + min_x - delta_y) / 2 + (delta_y / 3);
160 bound_x_2 = (max_x + min_x - delta_y) / 2 + 2 * (delta_y / 3);
161 }
162
163 /* build string by placing points in bins, collapsing bins and
164 discarding those with too few points... */
165
166 while (!points.empty())
167 {
168
169 QPoint p = points.front();
170 points.pop_front();
171
172 /* figure out which bin the point falls in */
173 current_bin = determineBin(p, bound_x_1, bound_x_2, bound_y_1,
174 bound_y_2);
175
176 /* if this is the first point, consider it the previous bin, too. */
177 prev_bin = (prev_bin == 0) ? current_bin : prev_bin;
178
179 if (prev_bin == current_bin) bin_count++;
180 else {
181
182 /* we are moving to a new bin -- consider adding to the
183 sequence */
184 if ((bin_count > (total_points * bin_percent)) || first_bin)
185 {
186 first_bin = false;
187 sequence += '0' + prev_bin;
188 sequence_count ++;
189 }
190
191 /* restart counting points in the new bin */
192 bin_count = 0;
193 prev_bin = current_bin;
194 }
195 }
196
197 /* add the last run of points to the sequence */
198 sequence += '0' + current_bin;
199 sequence_count++;
200
201 /* bail out on error cases */
202 if (sequence_count > max_sequence) sequence = "0";
203
204 return sequence;
205}
206
207
208
209/* comments in header */
210bool MythStroke::record(const QPoint & p)
211{
212 /* only record if we haven't exceeded the maximum points */
213 if ((points.size() >= max_points) || !recording)
214 return false;
215
216
217 if (points.size() == 0)
218 {
219 points.push_back(p);
220 return true;
221 }
222
223 /* interpolate between last and current point */
224 int delx = p.x() - points.back().x();
225 int dely = p.y() - points.back().y();
226
227 /* step by the greatest delta direction */
228 if (abs(delx) > abs(dely))
229 {
230 float iy = points.back().y();
231
232 /* go from the last point to the current, whatever direction
233 * it may be */
234 for (float ix = points.back().x();
235 (delx > 0) ? (ix < p.x()) : (ix > p.x());
236 ix += (delx > 0) ? 1 : -1)
237 {
238 /* step the other axis by the correct increment */
239 iy += fabs(((float) dely / (float) delx))
240 * (float) ((dely < 0) ? -1.0 : 1.0);
241
242 points.push_back(QPoint((int)ix, (int)iy));
243
244 adjustExtremes((int)ix, (int)iy);
245 }
246 }
247 else /* same thing, but for dely larger than delx case... */
248 {
249 float ix = points.back().x();
250
251 /* go from the last point to the current, whatever direction
252 it may be */
253 for (float iy = points.back().y();
254 (dely > 0) ? (iy < p.y()) : (iy > p.y());
255 iy += (dely > 0) ? 1 : -1)
256 {
257 /* step the other axis by the correct increment */
258 ix += fabs(((float) delx / (float) dely))
259 * (float) ((delx < 0) ? -1.0 : 1.0);
260
261 /* add the interpolated point */
262 points.push_back(QPoint((int)ix, (int)iy));
263
264 adjustExtremes((int)ix, (int)iy);
265 }
266 }
267
268 points.push_back(p);
269
270 return true;
271}
272
273
274static char *gesturename[] = {
275 "Unknown",
276 "Click",
277 "Up",
278 "Down",
279 "Left",
280 "Right",
281 "UpLeft",
282 "UpRight",
283 "DownLeft",
284 "DownRight",
285 "UpThenLeft",
286 "UpThenRight",
287 "DownThenLeft",
288 "DownThenRight",
289 "LeftThenUp",
290 "LeftThenDown",
291 "RightThenUp",
292 "RightThenDown",
293 "MaxGesture"
294};
295
296
297
298
299MythGestureEvent *MythStroke::complete()
300{
301 QString seq;
302 this->recording = false;
303 seq = translate();
304
305 min_x = min_y = 10000;
306 max_x = max_y = -1;
307
308 return new MythGestureEvent(sequences[seq]);
309}
310
311
312
313
314/* comments in header */
315MythGestureEvent::operator QString() const
316{
317 return gesturename[_gesture];
318}
319
320
321
322#endif /* MYTHSTROKE_CPP */