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 int determineBin (QPoint p, int x1, int x2, int y1, int y2)
197{
198 int 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 int prev_bin = 0;
244 int 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 bin_count++;
289 else
290 {
291
292 /* we are moving to a new bin -- consider adding to the
293 sequence */
294 if ((bin_count > (total_points * m_binPercent)) || first_bin)
295 {
296 first_bin = false;
297 sequence += '0' + QChar(prev_bin);
298 sequence_count ++;
299 }
300
301 /* restart counting points in the new bin */
302 bin_count = 0;
303 prev_bin = current_bin;
304 }
305 }
306
307 /* add the last run of points to the sequence */
308 sequence += '0' + QChar(current_bin);
309 sequence_count++;
310
311 /* bail out on error cases */
312 if (sequence_count > m_maxSequence)
313 sequence = '0';
314
315 return sequence;
316}
317
318
323{
324 return static_cast<size_t>(m_points.size()) >= m_minPoints;
325}
326
331bool MythGesture::Record(QPoint Point, Qt::MouseButton Button)
332{
333 /* only record if we haven't exceeded the maximum points */
334 if ((static_cast<size_t>(m_points.size()) >= m_maxPoints) || !Recording())
335 return false;
336
337 m_lastButton = Button;
338
339 if (m_points.empty())
340 {
341 m_points.push_back(Point);
342 return true;
343 }
344
345 /* interpolate between last and current point */
346 int delx = Point.x() - m_points.back().x();
347 int dely = Point.y() - m_points.back().y();
348
349 /* step by the greatest delta direction */
350 if (abs(delx) > abs(dely))
351 {
352 float fy = m_points.back().y();
353
354 /* go from the last point to the current, whatever direction
355 * it may be */
356 for (int ix = m_points.back().x();
357 (delx > 0) ? (ix < Point.x()) : (ix > Point.x());
358 ix += (delx > 0) ? 1 : -1)
359 {
360 /* step the other axis by the correct increment */
361 fy += std::fabs(static_cast<float>(dely) / static_cast<float>(delx))
362 * ((dely < 0) ? -1.0F : 1.0F);
363 int iy = static_cast<int>(fy);
364
365 /* add the interpolated point */
366 m_points.push_back(QPoint(ix, iy));
367 AdjustExtremes(ix, iy);
368 }
369 }
370 else /* same thing, but for dely larger than delx case... */
371 {
372 float fx = m_points.back().x();
373
374 /* go from the last point to the current, whatever direction
375 it may be */
376 for (int iy = m_points.back().y();
377 (dely > 0) ? (iy < Point.y()) : (iy > Point.y());
378 iy += (dely > 0) ? 1 : -1)
379 {
380 /* step the other axis by the correct increment */
381 fx += std::fabs(static_cast<float>(delx) / static_cast<float>(dely))
382 * ((delx < 0) ? -1.0F : 1.0F);
383 int ix = static_cast<int>(fx);
384
385 /* add the interpolated point */
386 m_points.push_back(QPoint(ix, iy));
387 AdjustExtremes(ix, iy);
388 }
389 }
390
391 m_points.push_back(Point);
392
393 return true;
394}
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 int 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