MythTV master
mythgesture.cpp
Go to the documentation of this file.
1/* -*- myth -*- */
29// Qt
30#include <QMetaEnum>
31
32// MythTV
33#include "mythgesture.h"
34
35// Std
36#include <array>
37#include <cmath>
38#include <algorithm>
39#include <complex>
40
41const QEvent::Type MythGestureEvent::kEventType = static_cast<QEvent::Type>(QEvent::registerEventType());
42
51MythGestureEvent::MythGestureEvent(Gesture gesture, Qt::MouseButton Button)
52 : QEvent(kEventType),
53 m_gesture(gesture),
54 m_button(Button)
55{
56}
57
58const std::map<QString, MythGestureEvent::Gesture>MythGesture::kSequences =
59{
62 { "456", MythGestureEvent::Right },
63 { "654", MythGestureEvent::Left },
64 { "258", MythGestureEvent::Down },
65 { "852", MythGestureEvent::Up },
66 { "951", MythGestureEvent::UpLeft },
82};
83
88{
89 return QMetaEnum::fromType<Gesture>().valueToKey(m_gesture);
90}
91
93{
94 return QMetaEnum::fromType<Qt::MouseButtons>().valueToKey(static_cast<int>(m_button));
95}
96
116MythGesture::MythGesture(size_t MaxPoints, size_t MinPoints,
117 size_t MaxSequence, int ScaleRatio,
118 float BinPercent)
119 : m_maxPoints(MaxPoints),
120 m_minPoints(MinPoints),
121 m_maxSequence(MaxSequence),
122 m_scaleRatio(ScaleRatio),
123 m_binPercent(BinPercent)
124{
125}
126
132{
133 m_minX = std::min(m_minX, X);
134 m_maxX = std::max(m_maxX, X);
135 m_minY = std::min(m_minY, Y);
136 m_maxY = std::max(m_maxY, Y);
137}
138
143{
144 m_lock.lock();
145 bool temp = m_recording;
146 m_lock.unlock();
147 return temp;
148}
149
152{
153 m_lock.lock();
154 m_recording = true;
155 m_lock.unlock();
156}
157
158
163void MythGesture::Stop(bool Timeout)
164{
165 m_lock.lock();
166
167 if (m_recording)
168 {
169 m_recording = false;
170
171 // translate before resetting maximums
172 const QString gesture = Translate(Timeout);
173 auto found = kSequences.find(gesture);
174 if (found != kSequences.cend())
175 m_lastGesture = found->second;
176 else
178
179 m_minX = m_minY = 10000;
180 m_maxX = m_maxY = -1;
181 }
182
183 m_lock.unlock();
184}
185
186
191{
193}
194
195/* comments in header */
196static uint8_t determineBin (QPoint p, int x1, int x2, int y1, int y2)
197{
198 uint8_t bin_num = 1;
199 if (p.x() > x1)
200 bin_num += 1;
201 if (p.x() > x2)
202 bin_num += 1;
203 if (p.y() > y1)
204 bin_num += 3;
205 if (p.y() > y2)
206 bin_num += 3;
207
208 return bin_num;
209}
210
211
217QString MythGesture::Translate(bool Timeout)
218{
219 auto total_points = static_cast<size_t>(m_points.count());
220
221 if (total_points > m_maxPoints)
222 {
223 m_points.clear();
224 return "0";
225 }
226
227 /* treat any stroke with less than the minimum number of points as
228 * a click (not a drag), which is the center bin */
229 if (total_points < m_minPoints)
230 {
231 m_points.clear();
232 if (Timeout)
233 return "1";
234 return "5";
235 }
236
237 QString sequence;
238
239 /* number of bins recorded in the stroke */
240 size_t sequence_count = 0;
241
242 /* points-->sequence translation scratch variables */
243 uint8_t prev_bin = 0;
244 uint8_t current_bin = 0;
245 int bin_count = 0;
246
247 /*flag indicating the start of a stroke - always count it in the sequence*/
248 bool first_bin = true;
249
250 /* determine size of grid */
251 int delta_x = m_maxX - m_minX;
252 int delta_y = m_maxY - m_minY;
253
254 /* calculate bin boundary positions */
255 int bound_x_1 = m_minX + (delta_x / 3);
256 int bound_x_2 = m_minX + (2 * (delta_x / 3));
257
258 int bound_y_1 = m_minY + (delta_y / 3);
259 int bound_y_2 = m_minY + (2 * (delta_y / 3));
260
261 if (delta_x > m_scaleRatio * delta_y)
262 {
263 bound_y_1 = ((m_maxY + m_minY - delta_x) / 2) + (delta_x / 3);
264 bound_y_2 = ((m_maxY + m_minY - delta_x) / 2) + (2 * (delta_x / 3));
265 }
266 else if (delta_y > m_scaleRatio * delta_x)
267 {
268 bound_x_1 = ((m_maxX + m_minX - delta_y) / 2) + (delta_y / 3);
269 bound_x_2 = ((m_maxX + m_minX - delta_y) / 2) + (2 * (delta_y / 3));
270 }
271
272 /* build string by placing points in bins, collapsing bins and
273 discarding those with too few points... */
274
275 while (!m_points.empty())
276 {
277 QPoint pt = m_points.front();
278 m_points.pop_front();
279
280 /* figure out which bin the point falls in */
281 current_bin = determineBin(pt, bound_x_1, bound_x_2, bound_y_1,
282 bound_y_2);
283
284 /* if this is the first point, consider it the previous bin, too. */
285 prev_bin = (prev_bin == 0) ? current_bin : prev_bin;
286
287 if (prev_bin == current_bin)
288 {
289 bin_count++;
290 }
291 else
292 {
293
294 /* we are moving to a new bin -- consider adding to the
295 sequence */
296 if ((bin_count > (total_points * m_binPercent)) || first_bin)
297 {
298 first_bin = false;
299 sequence += QChar{'0' + prev_bin};
300 sequence_count ++;
301 }
302
303 /* restart counting points in the new bin */
304 bin_count = 0;
305 prev_bin = current_bin;
306 }
307 }
308
309 /* add the last run of points to the sequence */
310 sequence += QChar{'0' + current_bin};
311 sequence_count++;
312
313 /* bail out on error cases */
314 if (sequence_count > m_maxSequence)
315 sequence = '0';
316
317 return sequence;
318}
319
320
325{
326 return static_cast<size_t>(m_points.size()) >= m_minPoints;
327}
328
333bool MythGesture::Record(QPoint Point, Qt::MouseButton Button)
334{
335 /* only record if we haven't exceeded the maximum points */
336 if ((static_cast<size_t>(m_points.size()) >= m_maxPoints) || !Recording())
337 return false;
338
339 m_lastButton = Button;
340
341 if (m_points.empty())
342 {
343 m_points.push_back(Point);
344 return true;
345 }
346
347 /* interpolate between last and current point */
348 int delx = Point.x() - m_points.back().x();
349 int dely = Point.y() - m_points.back().y();
350
351 /* step by the greatest delta direction */
352 if (abs(delx) > abs(dely))
353 {
354 float fy = m_points.back().y();
355
356 /* go from the last point to the current, whatever direction
357 * it may be */
358 for (int ix = m_points.back().x();
359 (delx > 0) ? (ix < Point.x()) : (ix > Point.x());
360 ix += (delx > 0) ? 1 : -1)
361 {
362 /* step the other axis by the correct increment */
363 fy += std::fabs(static_cast<float>(dely) / static_cast<float>(delx))
364 * ((dely < 0) ? -1.0F : 1.0F);
365 int iy = static_cast<int>(fy);
366
367 /* add the interpolated point */
368 m_points.push_back(QPoint(ix, iy));
369 AdjustExtremes(ix, iy);
370 }
371 }
372 else /* same thing, but for dely larger than delx case... */
373 {
374 float fx = m_points.back().x();
375
376 /* go from the last point to the current, whatever direction
377 it may be */
378 for (int iy = m_points.back().y();
379 (dely > 0) ? (iy < Point.y()) : (iy > Point.y());
380 iy += (dely > 0) ? 1 : -1)
381 {
382 /* step the other axis by the correct increment */
383 fx += std::fabs(static_cast<float>(delx) / static_cast<float>(dely))
384 * ((delx < 0) ? -1.0F : 1.0F);
385 int ix = static_cast<int>(fx);
386
387 /* add the interpolated point */
388 m_points.push_back(QPoint(ix, iy));
389 AdjustExtremes(ix, iy);
390 }
391 }
392
393 m_points.push_back(Point);
394
395 return true;
396}
A custom event that represents a mouse gesture.
Definition: mythgesture.h:40
static const Type kEventType
Definition: mythgesture.h:91
Gesture m_gesture
Definition: mythgesture.h:94
Qt::MouseButton m_button
Definition: mythgesture.h:96
MythGestureEvent(Gesture gesture, Qt::MouseButton Button)
Create a MythGesture.
Definition: mythgesture.cpp:51
QString GetButtonName() const
Definition: mythgesture.cpp:92
QString GetName() const
Get the symbolic name of the gesture.
Definition: mythgesture.cpp:87
float m_binPercent
Definition: mythgesture.h:128
QMutex m_lock
Definition: mythgesture.h:131
bool Record(QPoint Point, Qt::MouseButton Button)
Record a point.
bool HasMinimumPoints() const
Determine if the stroke has the minimum required points.
MythGestureEvent * GetGesture() const
Complete the gesture event of the last completed stroke.
bool Recording()
Determine if the stroke is being recorded.
size_t m_maxSequence
Definition: mythgesture.h:126
MythGestureEvent::Gesture m_lastGesture
Definition: mythgesture.h:129
size_t m_maxPoints
Definition: mythgesture.h:124
void Start()
Start recording.
static const std::map< QString, MythGestureEvent::Gesture > kSequences
Definition: mythgesture.h:132
size_t m_minPoints
Definition: mythgesture.h:125
MythGesture(size_t MaxPoints=10000, size_t MinPoints=50, size_t MaxSequence=20, int ScaleRatio=4, float BinPercent=0.07F)
Create a new stroke, specifying tuning values.
Qt::MouseButton m_lastButton
Definition: mythgesture.h:133
int m_scaleRatio
Definition: mythgesture.h:127
void Stop(bool Timeout=false)
Stop recording.
void AdjustExtremes(int X, int Y)
Adjust horizontal and vertical extremes.
QList< QPoint > m_points
Definition: mythgesture.h:130
QString Translate(bool Timeout)
Translate the stroke into a sequence.
bool m_recording
Definition: mythgesture.h:119
static uint8_t determineBin(QPoint p, int x1, int x2, int y1, int y2)
A C++ ripoff of the stroke library for MythTV.
static int x1
Definition: mythsocket.cpp:54
static int x2
Definition: mythsocket.cpp:55