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