MythTV  master
mythgesture.cpp
Go to the documentation of this file.
1 /* -*- myth -*- */
29 #include "mythgesture.h"
30 
31 #include <cmath>
32 #include <algorithm>
33 #include <complex>
34 
35 #include <QMutex>
36 #include <QMap>
37 
38 QEvent::Type MythGestureEvent::kEventType =
39  (QEvent::Type) QEvent::registerEventType();
40 
46 
47 public:
48  QMutex m_m;
49  QMap <QString, MythGestureEvent::Gesture> m_sequences;
50 };
51 
52 
53 
54 /* comments in header */
55 MythGesture::MythGesture(size_t max_points, size_t min_points,
56  size_t max_sequence, size_t scale_ratio,
57  float bin_percent) :
58  m_max_points(max_points), m_min_points(min_points), m_max_sequence(max_sequence),
59  m_scale_ratio(scale_ratio), m_bin_percent(bin_percent)
60 {
61  /* default to an invalid event */
63 
64  /* create new private information */
65  p = new MythGesturePrivate();
66 
67  /* Click */
69 
70  /* Lines */
71  p->m_sequences.insert("456", MythGestureEvent::Right);
72  p->m_sequences.insert("654", MythGestureEvent::Left);
73  p->m_sequences.insert("258", MythGestureEvent::Down);
74  p->m_sequences.insert("852", MythGestureEvent::Up);
75 
76  /* Diagonals */
77  p->m_sequences.insert("951", MythGestureEvent::UpLeft);
78  p->m_sequences.insert("753", MythGestureEvent::UpRight);
81 
82  /* Double Lines*/
95 }
96 
98 {
99  delete p;
100 }
101 
102 /* comments in header */
103 void MythGesture::adjustExtremes(int x, int y)
104 {
105  m_min_x = std::min(m_min_x, x);
106  m_max_x = std::max(m_max_x, x);
107  m_min_y = std::min(m_min_y, y);
108  m_max_y = std::max(m_max_y, y);
109 }
110 
111 bool MythGesture::recording(void) const
112 {
113  p->m_m.lock();
114  bool temp = m_recording;
115  p->m_m.unlock();
116  return temp;
117 }
118 
119 /* comments in header */
121 {
122  p->m_m.lock();
123  m_recording = true;
124  p->m_m.unlock();
125 }
126 
127 /* comments in header */
129 {
130  p->m_m.lock();
131 
132  if (m_recording)
133  {
134  m_recording = false;
135 
136  /* translate before resetting maximums */
138 
139  m_min_x = m_min_y = 10000;
140  m_max_x = m_max_y = -1;
141  }
142 
143  p->m_m.unlock();
144 }
145 
147 {
148  return new MythGestureEvent(m_last_gesture);
149 }
150 
151 /* comments in header */
152 static int determineBin (const QPoint & p, int x1, int x2, int y1, int y2)
153 {
154  int bin_num = 1;
155  if (p.x() > x1)
156  bin_num += 1;
157  if (p.x() > x2)
158  bin_num += 1;
159  if (p.y() > y1)
160  bin_num += 3;
161  if (p.y() > y2)
162  bin_num += 3;
163 
164  return bin_num;
165 }
166 
167 /* comments in header */
169 {
170  size_t total_points = m_points.count();
171 
172  if (total_points > m_max_points)
173  {
174  m_points.clear();
175  return "0";
176  }
177 
178  /* treat any stroke with less than the minimum number of points as
179  * a click (not a drag), which is the center bin */
180  if (total_points < m_min_points)
181  {
182  m_points.clear();
183  return "5";
184  }
185 
186  QString sequence;
187 
188  /* number of bins recorded in the stroke */
189  size_t sequence_count = 0;
190 
191  /* points-->sequence translation scratch variables */
192  int prev_bin = 0;
193  int current_bin = 0;
194  int bin_count = 0;
195 
196  /*flag indicating the start of a stroke - always count it in the sequence*/
197  bool first_bin = true;
198 
199  /* bin boundary and size variables */
200  int delta_x, delta_y;
201  int bound_x_1, bound_x_2;
202  int bound_y_1, bound_y_2;
203 
204  /* determine size of grid */
205  delta_x = m_max_x - m_min_x;
206  delta_y = m_max_y - m_min_y;
207 
208  /* calculate bin boundary positions */
209  bound_x_1 = m_min_x + (delta_x / 3);
210  bound_x_2 = m_min_x + 2 * (delta_x / 3);
211 
212  bound_y_1 = m_min_y + (delta_y / 3);
213  bound_y_2 = m_min_y + 2 * (delta_y / 3);
214 
215  if (delta_x > m_scale_ratio * delta_y)
216  {
217  bound_y_1 = (m_max_y + m_min_y - delta_x) / 2 + (delta_x / 3);
218  bound_y_2 = (m_max_y + m_min_y - delta_x) / 2 + 2 * (delta_x / 3);
219  }
220  else if (delta_y > m_scale_ratio * delta_x)
221  {
222  bound_x_1 = (m_max_x + m_min_x - delta_y) / 2 + (delta_y / 3);
223  bound_x_2 = (m_max_x + m_min_x - delta_y) / 2 + 2 * (delta_y / 3);
224  }
225 
226  /* build string by placing points in bins, collapsing bins and
227  discarding those with too few points... */
228 
229  while (!m_points.empty())
230  {
231  QPoint pt = m_points.front();
232  m_points.pop_front();
233 
234  /* figure out which bin the point falls in */
235  current_bin = determineBin(pt, bound_x_1, bound_x_2, bound_y_1,
236  bound_y_2);
237 
238  /* if this is the first point, consider it the previous bin, too. */
239  prev_bin = (prev_bin == 0) ? current_bin : prev_bin;
240 
241  if (prev_bin == current_bin)
242  bin_count++;
243  else
244  {
245 
246  /* we are moving to a new bin -- consider adding to the
247  sequence */
248  if ((bin_count > (total_points * m_bin_percent)) || first_bin)
249  {
250  first_bin = false;
251  sequence += '0' + prev_bin;
252  sequence_count ++;
253  }
254 
255  /* restart counting points in the new bin */
256  bin_count = 0;
257  prev_bin = current_bin;
258  }
259  }
260 
261  /* add the last run of points to the sequence */
262  sequence += '0' + current_bin;
263  sequence_count++;
264 
265  /* bail out on error cases */
266  if (sequence_count > m_max_sequence)
267  sequence = '0';
268 
269  return sequence;
270 }
271 
272 /* comments in header */
273 bool MythGesture::record(const QPoint & pt)
274 {
275  /* only record if we haven't exceeded the maximum points */
276  if (((uint)m_points.size() >= m_max_points) || !recording())
277  return false;
278 
279  if (m_points.empty())
280  {
281  m_points.push_back(pt);
282  return true;
283  }
284 
285  /* interpolate between last and current point */
286  int delx = pt.x() - m_points.back().x();
287  int dely = pt.y() - m_points.back().y();
288 
289  /* step by the greatest delta direction */
290  if (abs(delx) > abs(dely))
291  {
292  float iy = m_points.back().y();
293 
294  /* go from the last point to the current, whatever direction
295  * it may be */
296  for (float ix = m_points.back().x();
297  (delx > 0) ? (ix < pt.x()) : (ix > pt.x());
298  ix += (delx > 0) ? 1 : -1)
299  {
300  /* step the other axis by the correct increment */
301  iy += std::fabs(((float) dely / (float) delx))
302  * (float) ((dely < 0) ? -1.0 : 1.0);
303 
304  m_points.push_back(QPoint((int)ix, (int)iy));
305 
306  adjustExtremes((int)ix, (int)iy);
307  }
308  }
309  else /* same thing, but for dely larger than delx case... */
310  {
311  float ix = m_points.back().x();
312 
313  /* go from the last point to the current, whatever direction
314  it may be */
315  for (float iy = m_points.back().y();
316  (dely > 0) ? (iy < pt.y()) : (iy > pt.y());
317  iy += (dely > 0) ? 1 : -1)
318  {
319  /* step the other axis by the correct increment */
320  ix += std::fabs(((float) delx / (float) dely))
321  * (float) ((delx < 0) ? -1.0 : 1.0);
322 
323  /* add the interpolated point */
324  m_points.push_back(QPoint((int)ix, (int)iy));
325 
326  adjustExtremes((int)ix, (int)iy);
327  }
328  }
329 
330  m_points.push_back(pt);
331 
332  return true;
333 }
334 
335 
336 static const char *gesturename[] = {
337  "Unknown",
338  "Up",
339  "Down",
340  "Left",
341  "Right",
342  "UpLeft",
343  "UpRight",
344  "DownLeft",
345  "DownRight",
346  "UpThenLeft",
347  "UpThenRight",
348  "DownThenLeft",
349  "DownThenRight",
350  "LeftThenUp",
351  "LeftThenDown",
352  "RightThenUp",
353  "RightThenDown",
354  "RightThenLeft",
355  "LeftThenRight",
356  "UpThenDown",
357  "DownThenUp",
358  "Click",
359  "MaxGesture"
360 };
361 
362 /* comments in header */
363 MythGestureEvent::operator QString() const
364 {
365  return gesturename[m_gesture];
366 }
bool record(const QPoint &p)
Record a point.
QString translate(void)
Translate the stroke into a sequence.
bool recording(void) const
Determine if the stroke is being recorded.
unsigned int uint
Definition: compat.h:140
static int determineBin(const QPoint &p, int x1, int x2, int y1, int y2)
void stop(void)
Stop recording.
MythGesturePrivate * p
Definition: mythgesture.h:232
size_t m_max_points
Definition: mythgesture.h:224
Private information used only by a stroke class.
Definition: mythgesture.cpp:45
static const char * gesturename[]
size_t m_max_sequence
Definition: mythgesture.h:226
MythGestureEvent * gesture(void) const
Complete the gesture event of the last completed stroke.
static int x2
Definition: mythsocket.cpp:61
MythGestureEvent::Gesture m_last_gesture
Definition: mythgesture.h:229
A C++ ripoff of the stroke library for MythTV.
bool m_recording
Definition: mythgesture.h:219
MythGesture(size_t max_points=10000, size_t min_points=50, size_t max_sequence=20, size_t scale_ratio=4, float bin_percent=0.07)
Create a new stroke, specifying tuning values.
Definition: mythgesture.cpp:55
A custom event that represents a mouse gesture.
Definition: mythgesture.h:39
void start(void)
Start recording.
static Type kEventType
Definition: mythgesture.h:121
int m_scale_ratio
Definition: mythgesture.h:227
float m_bin_percent
Definition: mythgesture.h:228
QMap< QString, MythGestureEvent::Gesture > m_sequences
Definition: mythgesture.cpp:49
void adjustExtremes(int x, int y)
Adjust horizontal and vertical extremes.
QList< QPoint > m_points
Definition: mythgesture.h:230
size_t m_min_points
Definition: mythgesture.h:225
static int x1
Definition: mythsocket.cpp:60