00001
00030 #include "mythgesture.h"
00031
00032 #include <cmath>
00033 #include <algorithm>
00034
00035 #include <QMutex>
00036 #include <QMap>
00037
00038 QEvent::Type MythGestureEvent::kEventType =
00039 (QEvent::Type) QEvent::registerEventType();
00040
00045 class MythGesturePrivate {
00046
00047 public:
00048 QMutex m;
00049 QMap <QString, MythGestureEvent::Gesture> sequences;
00050 };
00051
00052
00053
00054
00055 MythGesture::MythGesture(size_t max_points, size_t min_points,
00056 size_t max_sequence, size_t scale_ratio,
00057 float bin_percent) :
00058 m_recording(false), min_x(10000), max_x(-1), min_y(10000), max_y(-1),
00059 max_points(max_points), min_points(min_points), max_sequence(max_sequence),
00060 scale_ratio(scale_ratio), bin_percent(bin_percent)
00061 {
00062
00063 last_gesture = MythGestureEvent::MaxGesture;
00064
00065
00066 p = new MythGesturePrivate();
00067
00068
00069 p->sequences.insert("5", MythGestureEvent::Click);
00070
00071
00072 p->sequences.insert("456", MythGestureEvent::Right);
00073 p->sequences.insert("654", MythGestureEvent::Left);
00074 p->sequences.insert("258", MythGestureEvent::Down);
00075 p->sequences.insert("852", MythGestureEvent::Up);
00076
00077
00078 p->sequences.insert("951", MythGestureEvent::UpLeft);
00079 p->sequences.insert("753", MythGestureEvent::UpRight);
00080 p->sequences.insert("159", MythGestureEvent::DownRight);
00081 p->sequences.insert("357", MythGestureEvent::DownLeft);
00082
00083
00084 p->sequences.insert("96321",MythGestureEvent::UpThenLeft);
00085 p->sequences.insert("74123",MythGestureEvent::UpThenRight);
00086 p->sequences.insert("36987",MythGestureEvent::DownThenLeft);
00087 p->sequences.insert("14789",MythGestureEvent::DownThenRight);
00088 p->sequences.insert("32147",MythGestureEvent::LeftThenDown);
00089 p->sequences.insert("98741",MythGestureEvent::LeftThenUp);
00090 p->sequences.insert("12369",MythGestureEvent::RightThenDown);
00091 p->sequences.insert("78963",MythGestureEvent::RightThenUp);
00092 }
00093
00094 MythGesture::~MythGesture()
00095 {
00096 delete p;
00097 }
00098
00099
00100 void MythGesture::adjustExtremes(int x, int y)
00101 {
00102 min_x = std::min(min_x, x);
00103 max_x = std::max(max_y, x);
00104 min_y = std::min(min_y, y);
00105 max_y = std::max(max_y, y);
00106 }
00107
00108 bool MythGesture::recording(void) const
00109 {
00110 p->m.lock();
00111 bool temp = m_recording;
00112 p->m.unlock();
00113 return temp;
00114 }
00115
00116
00117 void MythGesture::start(void)
00118 {
00119 p->m.lock();
00120 m_recording = true;
00121 p->m.unlock();
00122 }
00123
00124
00125 void MythGesture::stop(void)
00126 {
00127 p->m.lock();
00128
00129 if (m_recording)
00130 {
00131 m_recording = false;
00132
00133
00134 last_gesture = p->sequences[translate()];
00135
00136 min_x = min_y = 10000;
00137 max_x = max_y = -1;
00138 }
00139
00140 p->m.unlock();
00141 }
00142
00143 MythGestureEvent *MythGesture::gesture(void) const
00144 {
00145 return new MythGestureEvent(last_gesture);
00146 }
00147
00148
00149 static int determineBin (const QPoint & p, int x1, int x2, int y1, int y2)
00150 {
00151 int bin_num = 1;
00152 if (p.x() > x1)
00153 bin_num += 1;
00154 if (p.x() > x2)
00155 bin_num += 1;
00156 if (p.y() > y1)
00157 bin_num += 3;
00158 if (p.y() > y2)
00159 bin_num += 3;
00160
00161 return bin_num;
00162 }
00163
00164
00165 QString MythGesture::translate(void)
00166 {
00167 size_t total_points = points.count();
00168
00169 if (total_points > max_points)
00170 {
00171 points.clear();
00172 return "0";
00173 }
00174
00175
00176
00177 if (total_points < min_points)
00178 {
00179 points.clear();
00180 return "5";
00181 }
00182
00183 QString sequence;
00184
00185
00186 size_t sequence_count = 0;
00187
00188
00189 int prev_bin = 0;
00190 int current_bin = 0;
00191 int bin_count = 0;
00192
00193
00194 bool first_bin = true;
00195
00196
00197 int delta_x, delta_y;
00198 int bound_x_1, bound_x_2;
00199 int bound_y_1, bound_y_2;
00200
00201
00202 delta_x = max_x - min_x;
00203 delta_y = max_y - min_y;
00204
00205
00206 bound_x_1 = min_x + (delta_x / 3);
00207 bound_x_2 = min_x + 2 * (delta_x / 3);
00208
00209 bound_y_1 = min_y + (delta_y / 3);
00210 bound_y_2 = min_y + 2 * (delta_y / 3);
00211
00212 if (delta_x > scale_ratio * delta_y)
00213 {
00214 bound_y_1 = (max_y + min_y - delta_x) / 2 + (delta_x / 3);
00215 bound_y_2 = (max_y + min_y - delta_x) / 2 + 2 * (delta_x / 3);
00216 }
00217 else if (delta_y > scale_ratio * delta_x)
00218 {
00219 bound_x_1 = (max_x + min_x - delta_y) / 2 + (delta_y / 3);
00220 bound_x_2 = (max_x + min_x - delta_y) / 2 + 2 * (delta_y / 3);
00221 }
00222
00223
00224
00225
00226 while (!points.empty())
00227 {
00228 QPoint p = points.front();
00229 points.pop_front();
00230
00231
00232 current_bin = determineBin(p, bound_x_1, bound_x_2, bound_y_1,
00233 bound_y_2);
00234
00235
00236 prev_bin = (prev_bin == 0) ? current_bin : prev_bin;
00237
00238 if (prev_bin == current_bin)
00239 bin_count++;
00240 else
00241 {
00242
00243
00244
00245 if ((bin_count > (total_points * bin_percent)) || first_bin)
00246 {
00247 first_bin = false;
00248 sequence += '0' + prev_bin;
00249 sequence_count ++;
00250 }
00251
00252
00253 bin_count = 0;
00254 prev_bin = current_bin;
00255 }
00256 }
00257
00258
00259 sequence += '0' + current_bin;
00260 sequence_count++;
00261
00262
00263 if (sequence_count > max_sequence)
00264 sequence = '0';
00265
00266 return sequence;
00267 }
00268
00269
00270 bool MythGesture::record(const QPoint & p)
00271 {
00272
00273 if (((uint)points.size() >= max_points) || !recording())
00274 return false;
00275
00276 if (points.size() == 0)
00277 {
00278 points.push_back(p);
00279 return true;
00280 }
00281
00282
00283 int delx = p.x() - points.back().x();
00284 int dely = p.y() - points.back().y();
00285
00286
00287 if (abs(delx) > abs(dely))
00288 {
00289 float iy = points.back().y();
00290
00291
00292
00293 for (float ix = points.back().x();
00294 (delx > 0) ? (ix < p.x()) : (ix > p.x());
00295 ix += (delx > 0) ? 1 : -1)
00296 {
00297
00298 iy += std::fabs(((float) dely / (float) delx))
00299 * (float) ((dely < 0) ? -1.0 : 1.0);
00300
00301 points.push_back(QPoint((int)ix, (int)iy));
00302
00303 adjustExtremes((int)ix, (int)iy);
00304 }
00305 }
00306 else
00307 {
00308 float ix = points.back().x();
00309
00310
00311
00312 for (float iy = points.back().y();
00313 (dely > 0) ? (iy < p.y()) : (iy > p.y());
00314 iy += (dely > 0) ? 1 : -1)
00315 {
00316
00317 ix += std::fabs(((float) delx / (float) dely))
00318 * (float) ((delx < 0) ? -1.0 : 1.0);
00319
00320
00321 points.push_back(QPoint((int)ix, (int)iy));
00322
00323 adjustExtremes((int)ix, (int)iy);
00324 }
00325 }
00326
00327 points.push_back(p);
00328
00329 return true;
00330 }
00331
00332
00333 static const char *gesturename[] = {
00334 "Up",
00335 "Down",
00336 "Left",
00337 "Right",
00338 "UpLeft",
00339 "UpRight",
00340 "DownLeft",
00341 "DownRight",
00342 "UpThenLeft",
00343 "UpThenRight",
00344 "DownThenLeft",
00345 "DownThenRight",
00346 "LeftThenUp",
00347 "LeftThenDown",
00348 "RightThenUp",
00349 "RightThenDown",
00350 "Click",
00351 "MaxGesture"
00352 };
00353
00354
00355 MythGestureEvent::operator QString() const
00356 {
00357 return gesturename[m_gesture];
00358 }