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