Ticket #791: mythstroke.cpp

File mythstroke.cpp, 8.7 KB (added by mfgalizi@…, 18 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 */