00001 #include "disp/colors.H"
00002 #include "geom/gl_view.H"
00003 #include "geom/world.H"
00004 #include "gest_int.H"
00005 #include "mlib/point2.H"
00006 #include "std/config.H"
00007 #include "std/run_avg.H"
00008
00009 using namespace mlib;
00010
00011 const double MIN_GESTURE_LENGTH=15;
00012 const double MIN_GESTURE_SPREAD=10;
00013
00014 void
00015 GESTURE::init(CPIXEL& p, CEvent& down, double pressure)
00016 {
00017 _down = down;
00018 _start_frame = VIEW::stamp();
00019 _bbox.set(Wpt(XYpt(p)),Wpt(XYpt(p)));
00020 _pix_bbox.set(p, p);
00021 add(p, 1, pressure);
00022 }
00023
00024 void
00025 GESTURE::add(CPIXEL& p, double min_dist, double pressure)
00026 {
00027 if (_pts.empty() || _pts.last().dist(p) >= min_dist) {
00028 _pts += p;
00029 _pressures += pressure;
00030 _times += stop_watch::sys_time();
00031
00032 _bbox.update(Wpt(XYpt(p)));
00033 _pix_bbox.update(p);
00034
00035
00036 _pts.update_length();
00037 }
00038 _end_frame = VIEW::stamp();
00039 }
00040
00041 void
00042 GESTURE::smooth_points()
00043 {
00044 PIXEL_list s = _pts;
00045 for (int i=1; i<s.num()-1; i++) {
00046
00047 s[i] = (_pts[i-1] + _pts[i]*6.0 + _pts[i+1])/8.0;
00048 }
00049 _pts = s;
00050 }
00051
00052 void
00053 GESTURE::smooth_points(int n)
00054 {
00055 for (int i=0; i<n; i++)
00056 smooth_points();
00057 }
00058
00059 void
00060 GESTURE::reflect_points(const PIXELline& l)
00061 {
00062 PIXEL_list s = _pts;
00063 for (int i=0; i<s.num(); i++) {
00064 s[i] = l.reflection(_pts[i]);
00065 }
00066 _pts = s;
00067 }
00068
00069 void
00070 GESTURE::complete(CPIXEL& p, CEvent& up)
00071 {
00072 if (_pts.num() < 2)
00073 add(p,0,_pressures.last());
00074 _up = up;
00075
00076
00077 trim();
00078
00079 static int n = Config::get_var_int("GEST_SMOOTH_PASSES",1);
00080 smooth_points(n);
00081
00082 _complete = true;
00083 }
00084
00085
00086
00087 template <class A>
00088 inline void
00089 clip_tip(A& array, int num_to_clip)
00090 {
00091
00092 A tmp = array;
00093 tmp.reverse();
00094 tmp.truncate(array.num() - num_to_clip);
00095 tmp.reverse();
00096 array = tmp;
00097 }
00098
00099 void
00100 GESTURE::trim()
00101 {
00102
00103
00104
00105 if (spread() < 15)
00106 return;
00107
00108
00109 static double trim_dist = Config::get_var_dbl("TRIM_DIST", 10,true);
00110
00111
00112 static double trim_angle = Config::get_var_dbl("TRIM_ANGLE", deg2rad(30),true);
00113
00114
00115 if (length() <= trim_dist*2.5)
00116 return;
00117
00118 int n = _pts.num(), i;
00119 int trim_i = 0;
00120
00121 static bool debug = Config::get_var_bool("GEST_DEBUG_TRIM",false);
00122
00123
00124 for (i=1; (i < n-1) && (_pts[i].dist(_pts[0]) < trim_dist); i++) {
00125 if (angle(i) > trim_angle)
00126 trim_i = i;
00127 }
00128 if (trim_i > 0) {
00129 err_adv(debug, "GESTURE::trim: clipping at %d", trim_i);
00130 clip_tip(_pts, trim_i);
00131 _pts.update_length();
00132 clip_tip(_times, trim_i);
00133 clip_tip(_pressures, trim_i);
00134 }
00135
00136
00137 n = _pts.num();
00138 trim_i = n-1;
00139 for (i=n-2; (i > 0) && (_pts[i].dist(_pts.last()) < trim_dist); i--) {
00140 if (angle(i) > trim_angle)
00141 trim_i = i;
00142 }
00143 if (trim_i < n-1) {
00144
00145
00146
00147
00148
00149 _pts. truncate(trim_i + 1);
00150 _pts.update_length();
00151 _times. truncate(trim_i + 1);
00152 _pressures.truncate(trim_i + 1);
00153 err_adv(debug, "GESTURE::trim: trimming at %d", trim_i);
00154 }
00155 }
00156
00157 GESTUREptr
00158 GESTURE::prev(int k) const
00159 {
00160
00161
00162
00163 return _gest_int->gesture(_index-k);
00164 }
00165
00166 double
00167 GESTURE::speed() const
00168 {
00169
00170
00171 double et = elapsed_time();
00172 return (et > 1e-12) ? length()/et : 0;
00173 }
00174
00175 double
00176 GESTURE::startup_time(double dist_thresh) const
00177 {
00178
00179
00180
00181 assert(dist_thresh >= 0);
00182
00183 if (!is_stroke()) {
00184 err_msg("GESTURE::startup_time: Error: gesture is not complete");
00185 return 0;
00186 }
00187
00188 if (dist_thresh > length())
00189 return elapsed_time();
00190
00191
00192 int i = -1;
00193 _pts.interpolate(dist_thresh/length(), 0, &i);
00194 if (!_times.valid_index(i)) {
00195 err_msg("GESTURE::startup_time: Error: PIXEL_list::interpolate failed");
00196 return 0;
00197 } else if (_times.valid_index(i+1)) {
00198
00199 i++;
00200 }
00201
00202 return elapsed_time(i);
00203 }
00204
00205 double
00206 GESTURE::radius() const
00207 {
00208
00209
00210 if (_pts.empty())
00211 return 0;
00212
00213 PIXEL c = center();
00214
00215 double ret=0;
00216 for (int k=0; k<_pts.num(); k++)
00217 ret += _pts[k].dist(c);
00218
00219 return (ret / _pts.num());
00220 }
00221
00222 inline double
00223 angle(CPIXEL& a, CPIXEL& b, CPIXEL& c)
00224 {
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243 return (b - a).signed_angle(c - b);
00244 }
00245
00246 double
00247 GESTURE::winding(bool do_trim, bool do_abs) const
00248 {
00249 static bool debug = Config::get_var_bool("GEST_DEBUG_WINDING",false);
00250
00251 if (_pts.length() < 1.0)
00252 return 0;
00253
00254
00255
00256 int k1=0, k2=_pts.num()-1;
00257
00258 err_adv(debug, "GESTURE::winding");
00259
00260 if (do_trim) {
00261
00262
00263
00264
00265 double trim = min(.1 * _pts.length(), 12.0);
00266
00267
00268
00269
00270
00271
00272
00273
00274 double t = clamp(trim/_pts.length(), 0.0, 1.0);
00275
00276 err_adv(debug, " t: %f, t*len: %f", t, t*_pts.length());
00277
00278 double v=0;
00279 _pts.interpolate_length(t, k1, v);
00280 _pts.interpolate_length(1-t, k2, v);
00281
00282 }
00283
00284 err_adv(debug, " %d points, k1: %d, k2: %d", _pts.num(), k1, k2);
00285
00286
00287 double ret = 0;
00288 for (int k = max(k1,1); k < k2; k++) {
00289 double a = ::angle(_pts[k-1], _pts[k], _pts[k+1]);
00290 ret += (do_abs ? fabs(a) : a);
00291 }
00292 return ret/(M_PI*2);
00293 }
00294
00295 bool
00296 GESTURE::is_corner(int i) const
00297 {
00298
00299
00300
00301
00302 if (i == 0 || i ==_pts.num()-1)
00303 return true;
00304
00305
00306 if (!_pts.valid_index(i))
00307 return false;
00308
00309
00310 static const double MIN_ANGLE = Config::get_var_int("JOT_CORNER_ANGLE",45);
00311 return rad2deg(fabs(angle(i))) > MIN_ANGLE;
00312 }
00313
00314 ARRAY<int>
00315 GESTURE::corners() const
00316 {
00317 ARRAY<int> ret;
00318 for (int i=0;i < _pts.num(); i++)
00319 if (is_corner(i))
00320 ret += i;
00321 return ret;
00322 }
00323
00324 bool
00325 GESTURE::is_stroke() const
00326 {
00327 return (_down._d && _up._d && _pts.num() > 1);
00328 }
00329
00330 bool
00331 GESTURE::is_closed() const
00332 {
00333
00334
00335 double thresh = max(10.0, length()*0.07);
00336 return (
00337 is_stroke() &&
00338 (length() > 50) &&
00339 (endpoint_dist() < thresh)
00340 );
00341 }
00342
00343 bool
00344 GESTURE::is_tap() const
00345 {
00346 return (
00347 is_stroke() &&
00348 (elapsed_time() < .20) &&
00349 (length() < 10)
00350 );
00351 }
00352
00353 bool
00354 GESTURE::is_press_hold() const
00355 {
00356 return (
00357 is_stroke() &&
00358 (elapsed_time() > 0.5) &&
00359 (length() < 10)
00360 );
00361 }
00362
00363 bool
00364 GESTURE::is_double_tap() const
00365 {
00366 GESTUREptr p = prev();
00367
00368 return (is_tap() && p && p->is_tap() &&
00369 elapsed_time(p) < 1 && dist(p) < 10);
00370 }
00371
00372 bool
00373 GESTURE::is_dslash() const
00374 {
00375
00376
00377 return (
00378 is_stroke() &&
00379 (length() > 10) &&
00380 (straightness() > .9) &&
00381 (length() < 100) &&
00382 (startup_time() > 0.25) &&
00383 (remaining_time() < 0.1)
00384 );
00385 }
00386
00387 bool
00388 GESTURE::is_tslash() const
00389 {
00390
00391
00392 GESTUREptr p = prev();
00393
00394 return (is_slash() && p && p->is_tap() &&
00395 elapsed_time(p) < 1 && dist(p) < 10);
00396 }
00397
00398 bool
00399 GESTURE::is_slash_tap() const
00400 {
00401
00402
00403 GESTUREptr p = prev();
00404
00405 return (is_tap() && p && p->is_slash() && elapsed_time(p) < 1);
00406 }
00407
00408 bool
00409 GESTURE::is_zip_zap() const
00410 {
00411
00412
00413 if (!is_stroke() ||
00414 (length() < 20) ||
00415 (length() > 200) ||
00416 (straightness() > 0.5))
00417 return false;
00418
00419
00420 ARRAY<int> corn = corners();
00421 if (corn.num() != 3)
00422 return false;
00423
00424
00425 PIXEL c = _pts[corn[1]];
00426 return (start().dist(c) + end().dist(c) > 0.95 * length());
00427 }
00428
00429 bool
00430 GESTURE::is_arc() const
00431 {
00432
00433
00434 double aw = fabs(winding());
00435 double wa = winding_abs();
00436 return (
00437 is_stroke() &&
00438 (length() > 12) &&
00439 (straightness() > 0.8) &&
00440 (aw > 0.08) &&
00441 (aw < 0.35) &&
00442 (fabs(aw - wa) < 0.06)
00443 );
00444 }
00445
00446 bool
00447 GESTURE::is_small_arc() const
00448 {
00449
00450
00451 return (is_arc() && length() < 60);
00452 }
00453
00454 bool
00455 GESTURE::is_dot() const
00456 {
00457 if (!is_stroke() || length() < 40 || straightness() > .2)
00458 return 0;
00459
00460 PIXEL c = center();
00461
00462 for (int k=0; k<_pts.num(); k++)
00463 if (_pts[k].dist(c) > 25)
00464 return 0;
00465
00466
00467
00468 return ( fabs(winding()) > 1.75);
00469 }
00470
00471 bool
00472 GESTURE::is_scribble() const
00473 {
00474 return (
00475 is_stroke() &&
00476 (length() > 85) &&
00477 (endpoint_dist() > 17) &&
00478 (speed() > 250) &&
00479 (straightness() < .45) &&
00480 (winding(1,1) > 2.0)
00481 );
00482 }
00483
00484 bool
00485 GESTURE::is_loop() const
00486 {
00487 return (
00488 is_stroke() &&
00489 (length() > 50) &&
00490 (straightness() < .15) &&
00491 (fabs(1 - fabs(winding())) < .3) &&
00492 (winding(1,1) < 1.5)
00493 );
00494 }
00495
00496 bool
00497 GESTURE::is_lasso() const
00498 {
00499 return (
00500 is_stroke() &&
00501 (speed() > 300) &&
00502 (length() > 50) &&
00503 (straightness() < .2) &&
00504 (fabs(1 - fabs(winding())) < .5) &&
00505 (winding(1,1) < 1.5)
00506 );
00507 }
00508
00509 bool
00510 GESTURE::is_circle(
00511 double max_ratio
00512 ) const
00513 {
00514 static bool debug = Config::get_var_bool("DEBUG_CIRCLE",false);
00515
00516 PIXEL center;
00517 VEXEL axis;
00518 double r1=0, r2=0;
00519 if (!is_ellipse(center, axis, r1, r2)) {
00520 err_adv(debug, "GESTURE::is_circle: GESTURE is not an ellipse");
00521 return false;
00522 }
00523
00524
00525
00526 const double MIN_R2 = 1e-8;
00527 bool success = (r2 >= MIN_R2 && r1/r2 < max_ratio);
00528 err_adv(debug && !success,
00529 "GESTURE::is_circle: too distorted (r1/r2 = %f > %f)",
00530 (r2 < MIN_R2) ? 0.0 : r1/r2, max_ratio);
00531 return success;
00532 }
00533
00534 bool
00535 GESTURE::is_small_circle(
00536 double max_ratio
00537 ) const
00538 {
00539 static bool debug = Config::get_var_bool("DEBUG_SMALL_CIRCLE",false);
00540
00541 err_adv(debug, "GESTURE::is_small_circle:");
00542
00543 const double SMALL_CIRCLE_SPREAD=10;
00544
00545 double w = winding();
00546 double aw = fabs(w);
00547 double diffw1 = fabs(1 - aw);
00548 double wa = winding(1,1);
00549
00550 err_adv(debug, " spread: %f", spread());
00551 err_adv(debug, " length: %f", length());
00552 err_adv(debug, " straightness: %f", straightness());
00553 err_adv(debug, " |1 - |w||: %f", diffw1);
00554 err_adv(debug, " w abs: %f", wa);
00555
00556 bool ret = (
00557 is_stroke() &&
00558 spread() < SMALL_CIRCLE_SPREAD &&
00559 (length() > 15) &&
00560 (length() < 45) &&
00561 (straightness() < .20) &&
00562 (diffw1 < .4) &&
00563 (wa < 1.75)
00564 );
00565 if (ret) err_adv(debug, " passed");
00566 else err_adv(debug, " failed");
00567 return ret;
00568 }
00569
00570 bool
00571 GESTURE::is_click_hold() const
00572 {
00573 return (
00574 _down._d &&
00575 !_up._d &&
00576 (length() < 10) &&
00577 (elapsed_time() > 0.8)
00578 );
00579 }
00580
00581 bool
00582 GESTURE::is_line(double min_straightness, double min_len) const
00583 {
00584 return (
00585 is_stroke() &&
00586 (straightness() > min_straightness) &&
00587 (length() > min_len)
00588 );
00589 }
00590
00591 bool
00592 GESTURE::is_line() const
00593 {
00594 static const double min_straightness =
00595 Config::get_var_dbl("JOT_LINE_MIN_STRAIGHTNESS",0.95);
00596 static const double min_len =
00597 Config::get_var_dbl("JOT_LINE_MIN_LEN",20);
00598 return is_line(min_straightness, min_len);
00599 }
00600
00601 bool
00602 GESTURE::is_slash() const
00603 {
00604 return (
00605 is_stroke() &&
00606 (length() > 8) &&
00607 (straightness() > .8) &&
00608 (length() < 80) &&
00609 (speed() > 120)
00610 );
00611 }
00612
00613 bool
00614 GESTURE::is_n_line() const
00615 {
00616 if (!is_line())
00617 return 0;
00618
00619
00620
00621
00622 VEXEL u = endpt_vec().normalized();
00623 return (u[1] > 0.92);
00624 }
00625
00626 bool
00627 GESTURE::is_e_line() const
00628 {
00629 if (!is_line())
00630 return 0;
00631
00632
00633
00634
00635 VEXEL u = endpt_vec().normalized();
00636 return (u[0] > 0.92);
00637 }
00638
00639 bool
00640 GESTURE::is_s_line() const
00641 {
00642 if (!is_line())
00643 return 0;
00644
00645
00646
00647
00648 VEXEL u = endpt_vec().normalized();
00649 return (u[1] < -0.92);
00650 }
00651
00652 bool
00653 GESTURE::is_w_line() const
00654 {
00655 if (!is_line())
00656 return 0;
00657
00658
00659
00660
00661 VEXEL u = endpt_vec().normalized();
00662 return (u[1] < -0.92);
00663 }
00664
00665 bool
00666 GESTURE::is_ne_line() const
00667 {
00668 if (!is_line())
00669 return 0;
00670
00671
00672
00673
00674 VEXEL u = endpt_vec().normalized();
00675 return ((u * VEXEL(1,1))/M_SQRT2 > 0.92);
00676 }
00677
00678 bool
00679 GESTURE::is_se_line() const
00680 {
00681 if (!is_line())
00682 return 0;
00683
00684
00685
00686
00687 VEXEL u = endpt_vec().normalized();
00688 return ((u * VEXEL(1,-1))/M_SQRT2 > 0.92);
00689 }
00690
00691 bool
00692 GESTURE::is_sw_line() const
00693 {
00694 if (!is_line())
00695 return 0;
00696
00697
00698
00699
00700 VEXEL u = endpt_vec().normalized();
00701 return ((u * VEXEL(-1,-1))/M_SQRT2 > 0.92);
00702 }
00703
00704 bool
00705 GESTURE::is_nw_line() const
00706 {
00707 if (!is_line())
00708 return 0;
00709
00710
00711
00712
00713 VEXEL u = endpt_vec().normalized();
00714 return ((u * VEXEL(-1,1))/M_SQRT2 > 0.92);
00715 }
00716
00717 bool
00718 GESTURE::is_x() const
00719 {
00720 GESTUREptr p = prev();
00721
00722 const double STRAIGHT_THRESH = 0.92;
00723 const double MAX_LENGTH = 100.0;
00724 const double MIN_LENGTH = 10.0;
00725
00726
00727 if (!is_line(STRAIGHT_THRESH,MIN_LENGTH) ||
00728 length() > MAX_LENGTH ||
00729 length() < MIN_LENGTH ||
00730 !p ||
00731 !p->is_line(STRAIGHT_THRESH,MIN_LENGTH) ||
00732 p->length() > MAX_LENGTH ||
00733 p->length() < MIN_LENGTH ||
00734 (end_time() - p->start_time() > 2.0) ||
00735 center().dist(p->center())/max(length(),p->length()) > 0.5)
00736 return 0;
00737
00738
00739
00740 double dot = fabs(endpt_vec().normalized() *
00741 p->endpt_vec().normalized());
00742 return (dot < cos(M_PI/3));
00743 }
00744
00745 inline double
00746 linear_interp(double x1, double y1, double x2, double y2, double x)
00747 {
00748 return ((x - x1)*((y2 - y1)/(x2 - x1))) + y1;
00749 }
00750
00751 inline double
00752 ellipse_max_err(const GESTURE* gest)
00753 {
00754
00755
00756
00757
00758
00759 double err_factor = Config::get_var_dbl("ELLIPSE_ERR_FACTOR", 0.01,true);
00760 double max_err = err_factor * gest->length();
00761
00762
00763
00764
00765
00766 double LO_SPEED = 200,
00767 LO_FACTOR = Config::get_var_dbl("ELLIPSE_LO_FACTOR", 0.9,true);
00768 double HI_SPEED = 500,
00769 HI_FACTOR = Config::get_var_dbl("ELLIPSE_HI_FACTOR", 1.4,true);
00770 double s = gest->speed();
00771 double r = linear_interp(LO_SPEED, LO_FACTOR, HI_SPEED, HI_FACTOR, s);
00772 max_err *= clamp(r, LO_FACTOR, HI_FACTOR);
00773
00774 return max_err;
00775 }
00776
00777
00778
00779
00780 class ELLIPSE {
00781 protected:
00782 PIXEL _center;
00783 VEXEL _x;
00784 VEXEL _y;
00785 double _r1;
00786 double _r2;
00787 PIXEL_list _pts;
00788
00789 public:
00790
00791
00792
00793 ELLIPSE(CPIXEL& c, CVEXEL& x, double r1, double r2, int res=32) {
00794 set(c,x,r1,r2,res);
00795 }
00796
00797 virtual ~ELLIPSE() {}
00798
00799
00800
00801 virtual void set(CPIXEL& c, CVEXEL& x, double r1, double r2, int res) {
00802 _center = c;
00803 _x = x.normalized();
00804 _y = _x.perpend();
00805 _r1 = fabs(r1);
00806 _r2 = fabs(r2);
00807 rebuild(res);
00808 }
00809
00810
00811 void rebuild(int res);
00812
00813
00814 double avg_dist(CPIXEL_list& pts) {
00815 RunningAvg<double> ret(0);
00816 for (int i=0; i<pts.num(); i++)
00817 ret.add(_pts.dist(pts[i]));
00818 return ret.val();
00819 }
00820 };
00821
00822 void
00823 ELLIPSE::rebuild(int res)
00824 {
00825
00826
00827
00828 assert(_r1 > gEpsAbsMath && _r2 > gEpsAbsMath &&
00829 !_x.is_null() && !_y.is_null() && res > 4);
00830
00831
00832 _pts.clear();
00833 _pts.realloc(res);
00834
00835
00836 for (int i=0; i<res; i++) {
00837 double t = (2*M_PI)*i/res;
00838 _pts += _center + _x*(_r1*cos(t)) + _y*(_r2*sin(t));
00839 }
00840 }
00841
00842
00843
00844
00845 class DEBUG_ELLIPSE : public GEL, public ELLIPSE {
00846 protected:
00847 egg_timer _timer;
00848
00849 public:
00850
00851
00852
00853 DEBUG_ELLIPSE(CPIXEL& c, CVEXEL& x, double r1, double r2, int res=32) :
00854 ELLIPSE(c,x,r1,r2,res),
00855 _timer(10) {}
00856
00857
00858
00859 virtual void set(CPIXEL& c, CVEXEL& x, double r1, double r2, int res) {
00860 ELLIPSE::set(c,x,r1,r2,res);
00861 _timer.reset(10);
00862 }
00863
00864
00865
00866 DEFINE_RTTI_METHODS3("DEBUG_ELLIPSE", DEBUG_ELLIPSE*, GEL, CDATA_ITEM *);
00867
00868
00869
00870 double alpha() const {
00871 const double FADE_TIME = 0.5;
00872 return min(_timer.remaining() / FADE_TIME, 1.0);
00873 }
00874
00875
00876
00877 virtual bool needs_blend() const { return alpha() < 1; }
00878 virtual int draw(CVIEWptr &);
00879
00880
00881
00882 virtual DATA_ITEM* dup() const { return 0; }
00883 };
00884
00885 int
00886 DEBUG_ELLIPSE::draw(CVIEWptr& v)
00887 {
00888
00889 glMatrixMode(GL_MODELVIEW);
00890 glPushMatrix();
00891 glLoadIdentity();
00892
00893
00894 glMatrixMode(GL_PROJECTION);
00895 glPushMatrix();
00896 glLoadMatrixd(v->pix_proj().transpose().matrix());
00897
00898
00899 glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT | GL_CURRENT_BIT);
00900 glDisable(GL_LIGHTING);
00901
00902
00903
00904
00905 GL_VIEW::init_line_smooth(GLfloat(v->line_scale()*2));
00906 GL_COL(COLOR::blue, alpha());
00907 glBegin(GL_LINE_STRIP);
00908 for (int k=0; k<_pts.num(); k++)
00909 glVertex2dv(_pts[k].data());
00910 glEnd();
00911 GL_VIEW::end_line_smooth();
00912
00913
00914 GL_VIEW::init_line_smooth(GLfloat(v->line_scale()*1));
00915 glColor4d(.8,.8,.8,alpha());
00916 glBegin(GL_LINES);
00917 glVertex2dv((_center - _x*_r1).data());
00918 glVertex2dv((_center + _x*_r1).data());
00919 glVertex2dv((_center - _y*_r2).data());
00920 glVertex2dv((_center + _y*_r2).data());
00921 glEnd();
00922 GL_VIEW::end_line_smooth();
00923
00924
00925 GL_VIEW::init_point_smooth(float(v->line_scale()*3));
00926 GL_COL(Color::orange, alpha());
00927 glBegin(GL_POINTS);
00928 glVertex2dv(_center.data());
00929 glEnd();
00930 GL_VIEW::end_point_smooth();
00931
00932 glPopAttrib();
00933 glPopMatrix();
00934 glMatrixMode(GL_MODELVIEW);
00935 glPopMatrix();
00936
00937 if (alpha() == 0)
00938 WORLD::undisplay(this, false);
00939
00940 return 0;
00941 }
00942
00943 inline PIXEL_list
00944 get_section(PIXEL_list& pts, double s0, double s1)
00945 {
00946
00947
00948
00949
00950
00951 pts.update_length();
00952
00953 PIXEL_list ret;
00954 if (!(in_interval(s0, 0.0, 1.0) && in_interval(s1, 0.0, 1.0) && s0 < s1)) {
00955 err_msg("get_section: bad parameters: %f, %f", s0, s1);
00956 return ret;
00957 }
00958 if (pts.empty()) {
00959 err_msg("get_section: empty list");
00960 return ret;
00961 }
00962 VEXEL v0, v1; int k0, k1; double t0, t1;
00963 PIXEL p0 = pts.interpolate(s0, &v0, &k0, &t0);
00964 PIXEL p1 = pts.interpolate(s1, &v1, &k1, &t1);
00965 ret += p0;
00966 for (int k=k0+1; k<=k1; k++)
00967 ret += pts[k];
00968 if (t1 > 0)
00969 ret += p1;
00970 return ret;
00971 }
00972
00973 inline PIXEL_list
00974 trim_endpt_overlap(CPIXEL_list& pts)
00975 {
00976
00977
00978
00979
00980
00981
00982
00983
00984
00985
00986 PIXEL_list ret = pts;
00987
00988
00989 double END_ZONE = 0.15;
00990
00991
00992 PIXEL_list start = get_section(ret, 0, END_ZONE);
00993
00994
00995 int last_k;
00996 ret.interpolate(1 - END_ZONE, 0, &last_k, 0);
00997
00998
00999
01000 PIXEL foo; int fum;
01001 for (int k=pts.num()-1; k>last_k; k--) {
01002 if (start.closest(ret[k], foo, fum) < 10.0)
01003 ret.pop();
01004 else
01005 break;
01006 }
01007 return ret;
01008 }
01009
01010 bool
01011 GESTURE::is_ellipse(
01012 PIXEL& center,
01013 VEXEL& axis,
01014 double& r1,
01015 double& r2,
01016 double err_mult
01017 ) const
01018 {
01019
01020
01021
01022
01023
01024
01025
01026
01027
01028
01029
01030
01031
01032
01033
01034
01035
01036
01037
01038
01039
01040
01041
01042
01043
01044
01045
01046
01047
01048
01049
01050
01051
01052
01053
01054
01055
01056
01057
01058
01059
01060
01061
01062
01063
01064
01065
01066
01067
01068
01069
01070
01071
01072
01073
01074
01075 static const GESTURE* last_gesture = 0;
01076 static bool do_debug = Config::get_var_bool("DEBUG_ELLIPSE",false);
01077 bool debug = do_debug && last_gesture != this;
01078 last_gesture = this;
01079
01080
01081
01082
01083 int n = _pts.num(), k;
01084 if (n < 8) {
01085 err_adv(debug, "GESTURE::is_ellipse: too few points", n);
01086 return false;
01087 }
01088
01089 if (!is_loop()) {
01090 err_adv(debug, "GESTURE::is_ellipse: it's not a loop");
01091 return false;
01092 }
01093
01094
01095
01096
01097
01098
01099
01100
01101
01102
01103
01104
01105
01106 center = trim_endpt_overlap(_pts).average();
01107
01108
01109
01110
01111 ARRAY<VEXEL> P(n);
01112 for (k=0; k<n; k++)
01113 P[k] += (_pts[k] - center);
01114
01115
01116
01117
01118
01119
01120
01121 double* Xt[3];
01122 Xt[0] = new double [ n ];
01123 Xt[1] = new double [ n ];
01124 Xt[2] = new double [ n ];
01125
01126 for (k=0; k<n; k++) {
01127 Xt[0][k] = P[k][0] * P[k][0];
01128 Xt[1][k] = P[k][0] * P[k][1];
01129 Xt[2][k] = P[k][1] * P[k][1];
01130 }
01131
01132 Wvec XtJ;
01133 Wtransf XtX;
01134 XtX(0,0) = XtX(1,1) = XtX(2,2) = 0;
01135 for (k=0; k<n; k++) {
01136
01137 for (int i=0; i<3; i++) {
01138 XtJ[i] += Xt[i][k];
01139 for (int j=0; j<3; j++) {
01140 XtX(i,j) += Xt[i][k] * Xt[j][k];
01141 }
01142 }
01143 }
01144
01145
01146 delete [] Xt[0];
01147 delete [] Xt[1];
01148 delete [] Xt[2];
01149
01150
01151 Wvec A = XtX.inverse() * XtJ;
01152
01153
01154
01155
01156
01157
01158
01159 double a = A[0], b = A[1]/2, c = A[2];
01160
01161
01162
01163
01164 double det = sqrt(sqr(a-c) + 4*sqr(b));
01165 double lambda_1 = (a + c + det)/2;
01166 double lambda_2 = (a + c - det)/2;
01167
01168 if (lambda_1 <= 0 || lambda_2 <= 0) {
01169 err_adv(debug, "GESTURE::is_ellipse: non-positive eigenvalues in matrix");
01170 return false;
01171 }
01172
01173
01174 if (lambda_2 > lambda_1) {
01175
01176 err_adv(debug, "GESTURE::is_ellipse: warning: a + c < 0");
01177 swap(lambda_1, lambda_2);
01178 }
01179
01180
01181 if (lambda_2 < 1e-6) {
01182 err_adv(debug, "GESTURE::is_ellipse: vanishing eigenvalue (%f)", lambda_2);
01183 return false;
01184 }
01185
01186
01187
01188
01189
01190
01191 VEXEL row1(a - lambda_1, b );
01192 VEXEL row2( b, c - lambda_1);
01193
01194
01195 axis = (row1.length() > row2.length()) ? row1 : row2;
01196
01197
01198 axis = axis.normalized();
01199 if (axis.is_null()) {
01200 err_adv(debug, "GESTURE::is_ellipse: can't find principle axis");
01201 return false;
01202 }
01203
01204
01205 r1 = 1 / sqrt(lambda_2);
01206 r2 = 1 / sqrt(lambda_1);
01207
01208
01209
01210
01211
01212
01213
01214 double max_err = err_mult * ellipse_max_err(this);
01215 double err = ELLIPSE(center, axis, r1, r2).avg_dist(_pts);
01216 err_adv(debug, "GESTURE::is_ellipse: err %f, max: %f", err, max_err);
01217 bool success = (err <= max_err);
01218 if (debug)
01219 WORLD::create(new DEBUG_ELLIPSE(center, axis, r1, r2), false);
01220
01221 return success;
01222 }
01223
01224 void
01225 GESTURE::print_stats() const
01226 {
01227
01228
01229 cerr << endl;
01230
01231
01232 err_msg("speed: %1.1f", speed());
01233 err_msg("winding: %1.4f", winding());
01234 err_msg("winding_abs: %1.4f", winding_abs());
01235
01236 err_msg("length: %1.1f", length());
01237 err_msg("straightness: %1.3f", straightness());
01238
01239 cerr << endl;
01240 }
01241
01242 void
01243 GESTURE::print_types() const
01244 {
01245
01246 is_circle();
01247
01248 cerr << "gesture is: ";
01249 if (is_stroke()) cerr << "stroke, ";
01250 if (is_tap()) cerr << "tap, ";
01251 if (is_double_tap()) cerr << "double tap, ";
01252 if (is_line()) cerr << "line, ";
01253 if (is_slash()) cerr << "slash, ";
01254 if (is_dslash()) cerr << "dslash, ";
01255 if (is_tslash()) cerr << "tslash, ";
01256 if (is_zip_zap()) cerr << "zip-zap, ";
01257 if (is_small_arc()) cerr << "small arc, ";
01258 if (is_dot()) cerr << "dot, ";
01259 if (is_scribble()) cerr << "scribble, ";
01260 if (is_closed()) cerr << "closed, ";
01261 if (is_loop()) cerr << "loop, ";
01262 if (is_lasso()) cerr << "lasso, ";
01263 if (is_circle()) cerr << "circle, ";
01264 if (is_small_circle())cerr << "small circle, ";
01265 if (is_ellipse()) cerr << "ellipse, ";
01266 if (is_n_line()) cerr << "n_line, ";
01267 if (is_e_line()) cerr << "e_line, ";
01268 if (is_s_line()) cerr << "s_line, ";
01269 if (is_w_line()) cerr << "w_line, ";
01270 if (is_ne_line()) cerr << "ne_line, ";
01271 if (is_se_line()) cerr << "se_line, ";
01272 if (is_sw_line()) cerr << "sw_line, ";
01273 if (is_nw_line()) cerr << "nw_line, ";
01274 if (is_x()) cerr << "x, ";
01275 if (is_click_hold()) cerr << "click_hold, ";
01276 if (is_press_hold()) cerr << "press_hold, ";
01277 cerr << endl;
01278 }
01279
01280 int
01281 GESTURE::draw(CVIEWptr &v)
01282 {
01283 return 0;
01284 }
01285
01286 int
01287 GESTURE::draw_final(CVIEWptr &v)
01288 {
01289 return _drawer ? _drawer->draw(this, v) : 0;
01290 }
01291
01292 inline double
01293 pressure_to_grey(double p)
01294 {
01295
01296
01297
01298
01299
01300 const double MIN_PRESSURE = 0.3;
01301
01302
01303 const double GREY0 = 0.6;
01304
01305
01306
01307
01308
01309 return GREY0*(1 - sqrt(max(p, MIN_PRESSURE) - MIN_PRESSURE));
01310 }
01311
01312 int
01313 GestureDrawer::draw(const GESTURE* gest, CVIEWptr& v)
01314 {
01315
01316
01317
01318
01319 glMatrixMode(GL_MODELVIEW);
01320 glPushMatrix();
01321 glLoadIdentity();
01322
01323
01324 glMatrixMode(GL_PROJECTION);
01325 glPushMatrix();
01326 glLoadMatrixd(VIEW::peek()->pix_proj().transpose().matrix());
01327
01328
01329 glPushAttrib(GL_ENABLE_BIT | GL_LINE_BIT | GL_CURRENT_BIT);
01330 glDisable(GL_LIGHTING);
01331
01332
01333 GL_VIEW::init_line_smooth(GLfloat(v->line_scale()*2));
01334
01335
01336 const PIXEL_list& pts = gest->pts();
01337 const ARRAY<double>& press = gest->pressures();
01338 glBegin(GL_LINE_STRIP);
01339 for (int k=0; k< pts.num(); k++) {
01340 double grey = pressure_to_grey(press[k]);
01341 glColor3d(grey, grey, grey);
01342 glVertex2dv(pts[k].data());
01343 }
01344 glEnd();
01345
01346
01347
01348
01349 GL_VIEW::end_line_smooth();
01350
01351 glPopAttrib();
01352 glPopMatrix();
01353 glMatrixMode(GL_MODELVIEW);
01354 glPopMatrix();
01355
01356 return 0;
01357 }
01358
01359