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