From d2f4e830ea18e82fae9e8cd3a2c3aacd1641871b Mon Sep 17 00:00:00 2001 From: Jim Grandpre Date: Fri, 9 Jul 2010 00:50:40 -0700 Subject: [PATCH] Moved $1 Gestures into the SDL Library --- include/SDL_events.h | 17 ++++ src/events/SDL_gesture.c | 213 ++++++++++++++++++++++++++++++++++++++- touchTest/touchPong | Bin 29499 -> 29683 bytes touchTest/touchSimp | Bin 29308 -> 29492 bytes 4 files changed, 229 insertions(+), 1 deletion(-) diff --git a/include/SDL_events.h b/include/SDL_events.h index fe6109271..2cd0cc69a 100644 --- a/include/SDL_events.h +++ b/include/SDL_events.h @@ -354,6 +354,22 @@ typedef struct SDL_MultiGestureEvent } SDL_MultiGestureEvent; +typedef struct SDL_DollarGestureEvent +{ + Uint32 type; /**< ::SDL_DOLLARGESTURE */ + Uint32 windowID; /**< The window with mouse focus, if any */ + Uint8 touchId; /**< The touch device index */ + Uint8 gestureId; + Uint8 padding2; + Uint8 padding3; + float error; + /* + //TODO: Enable to give location? + float x; //currently 0...1. Change to screen coords? + float y; + */ +} SDL_DollarGestureEvent; + @@ -443,6 +459,7 @@ typedef union SDL_Event SDL_TouchFingerEvent tfinger; /**< Touch finger event data */ SDL_TouchButtonEvent tbutton; /**< Touch button event data */ SDL_MultiGestureEvent mgesture; /**< Multi Finger Gesture data*/ + SDL_DollarGestureEvent dgesture; /**< Multi Finger Gesture data*/ /** Temporarily here for backwards compatibility */ /*@{*/ diff --git a/src/events/SDL_gesture.c b/src/events/SDL_gesture.c index 4ef644faa..107f64611 100644 --- a/src/events/SDL_gesture.c +++ b/src/events/SDL_gesture.c @@ -30,6 +30,14 @@ //TODO: Replace with malloc #define MAXFINGERS 3 #define MAXTOUCHES 2 +#define MAXTEMPLATES 4 +#define MAXPATHSIZE 1024 + +#define DOLLARNPOINTS 64 +#define DOLLARSIZE 256 + +//PHI = ((sqrt(5)-1)/2) +#define PHI 0.618033989 typedef struct { float x,y; @@ -42,10 +50,20 @@ typedef struct { int id; } Finger; + +typedef struct { + float length; + + int numPoints; + Point p[MAXPATHSIZE]; +} DollarPath; + + typedef struct { Finger f; Point cv; float dtheta,dDist; + DollarPath dollarPath; } TouchPoint; @@ -55,10 +73,160 @@ typedef struct { Point centroid; TouchPoint gestureLast[MAXFINGERS]; int numDownFingers; + + int numDollarTemplates; + Point dollarTemplate[MAXTEMPLATES][DOLLARNPOINTS]; } GestureTouch; GestureTouch gestureTouch[MAXTOUCHES]; int numGestureTouches = 0; + +float dollarDifference(Point* points,Point* templ,float ang) { + // Point p[DOLLARNPOINTS]; + float dist = 0; + Point p; + int i; + for(i = 0; i < DOLLARNPOINTS; i++) { + p.x = points[i].x * cos(ang) - points[i].y * sin(ang); + p.y = points[i].x * sin(ang) + points[i].y * cos(ang); + dist += sqrt((p.x-templ[i].x)*(p.x-templ[i].x)+ + (p.y-templ[i].y)*(p.y-templ[i].y)); + } + return dist/DOLLARNPOINTS; + +} + +float bestDollarDifference(Point* points,Point* templ) { + //------------BEGIN DOLLAR BLACKBOX----------------// + //-TRANSLATED DIRECTLY FROM PSUDEO-CODE AVAILABLE AT-// + //-"http://depts.washington.edu/aimgroup/proj/dollar/"-// + float ta = -M_PI/4; + float tb = M_PI/4; + float dt = M_PI/90; + float x1 = PHI*ta + (1-PHI)*tb; + float f1 = dollarDifference(points,templ,x1); + float x2 = (1-PHI)*ta + PHI*tb; + float f2 = dollarDifference(points,templ,x2); + while(abs(ta-tb) > dt) { + if(f1 < f2) { + tb = x2; + x2 = x1; + f2 = f1; + x1 = PHI*ta + (1-PHI)*tb; + f1 = dollarDifference(points,templ,x1); + } + else { + ta = x1; + x1 = x2; + f1 = f2; + x2 = (1-PHI)*ta + PHI*tb; + f2 = dollarDifference(points,templ,x2); + } + } + /* + if(f1 <= f2) + printf("Min angle (x1): %f\n",x1); + else if(f1 > f2) + printf("Min angle (x2): %f\n",x2); + */ + return SDL_min(f1,f2); +} + +float dollarRecognize(DollarPath path,int *bestTempl,GestureTouch* touch) { + + Point points[DOLLARNPOINTS]; + int numPoints = dollarNormalize(path,points); + int i; + + int bestDiff = 10000; + *bestTempl = -1; + for(i = 0;i < touch->numDollarTemplates;i++) { + int diff = bestDollarDifference(points,touch->dollarTemplate[i]); + if(diff < bestDiff) {bestDiff = diff; *bestTempl = i;} + } + return bestDiff; +} + +//DollarPath contains raw points, plus (possibly) the calculated length +int dollarNormalize(DollarPath path,Point *points) { + int i; + //Calculate length if it hasn't already been done + if(path.length <= 0) { + for(i=1;i interval) { + points[numPoints].x = path.p[i-1].x + + ((interval-dist)/d)*(path.p[i].x-path.p[i-1].x); + points[numPoints].y = path.p[i-1].y + + ((interval-dist)/d)*(path.p[i].y-path.p[i-1].y); + centroid.x += points[numPoints].x; + centroid.y += points[numPoints].y; + numPoints++; + + dist -= interval; + } + dist += d; + } + if(numPoints < 1) return 0; + centroid.x /= numPoints; + centroid.y /= numPoints; + + //printf("Centroid (%f,%f)",centroid.x,centroid.y); + //Rotate Points so point 0 is left of centroid and solve for the bounding box + float xmin,xmax,ymin,ymax; + xmin = centroid.x; + xmax = centroid.x; + ymin = centroid.y; + ymax = centroid.y; + + float ang = atan2(centroid.y - points[0].y, + centroid.x - points[0].x); + + for(i = 0;i xmax) xmax = points[i].x; + if(points[i].y < ymin) ymin = points[i].y; + if(points[i].y > ymax) ymax = points[i].y; + } + + //Scale points to DOLLARSIZE, and translate to the origin + float w = xmax-xmin; + float h = ymax-ymin; + + for(i=0;i= MAXTOUCHES) return -1; @@ -69,6 +237,8 @@ int SDL_GestureAddTouch(SDL_Touch* touch) { gestureTouch[numGestureTouches].res.x = touch->xres; gestureTouch[numGestureTouches].id = touch->id; + gestureTouch[numGestureTouches].numDollarTemplates = 0; + numGestureTouches++; return 0; } @@ -93,6 +263,20 @@ int SDL_SendGestureMulti(GestureTouch* touch,float dTheta,float dDist) { return SDL_PushEvent(&event) > 0; } +int SDL_SendGestureDollar(GestureTouch* touch,int gestureId,float error) { + SDL_Event event; + event.dgesture.type = SDL_DOLLARGESTURE; + event.dgesture.touchId = touch->id; + /* + //TODO: Add this to give location of gesture? + event.mgesture.x = touch->centroid.x; + event.mgesture.y = touch->centroid.y; + */ + event.dgesture.gestureId = gestureId; + event.dgesture.error = error; + return SDL_PushEvent(&event) > 0; +} + void SDL_GestureProcessEvent(SDL_Event* event) { if(event->type == SDL_FINGERMOTION || @@ -100,7 +284,6 @@ void SDL_GestureProcessEvent(SDL_Event* event) event->type == SDL_FINGERUP) { GestureTouch* inTouch = SDL_GetGestureTouch(event->tfinger.touchId); - //Shouldn't be possible if(inTouch == NULL) return; @@ -114,12 +297,35 @@ void SDL_GestureProcessEvent(SDL_Event* event) if(event->type == SDL_FINGERUP) { inTouch->numDownFingers--; + + int bestTempl; + float error; + error = dollarRecognize(inTouch->gestureLast[j].dollarPath, + &bestTempl,inTouch); + if(bestTempl >= 0){ + //Send Event + int gestureId = 0; //? + SDL_SendGestureDollar(inTouch->id,gestureId,error); + + + printf("Dollar error: %f\n",error); + } + inTouch->gestureLast[j] = inTouch->gestureLast[inTouch->numDownFingers]; break; } else { float dx = x - inTouch->gestureLast[j].f.p.x; float dy = y - inTouch->gestureLast[j].f.p.y; + DollarPath* path = &inTouch->gestureLast[j].dollarPath; + if(path->numPoints < MAXPATHSIZE) { + path->p[path->numPoints].x = x; + path->p[path->numPoints].y = y; + path->length += sqrt(dx*dx + dy*dy); + path->numPoints++; + } + + inTouch->centroid.x += dx/inTouch->numDownFingers; inTouch->centroid.y += dy/inTouch->numDownFingers; if(inTouch->numDownFingers > 1) { @@ -181,6 +387,11 @@ void SDL_GestureProcessEvent(SDL_Event* event) inTouch->gestureLast[j].f.p.y = y; inTouch->gestureLast[j].cv.x = 0; inTouch->gestureLast[j].cv.y = 0; + + inTouch->gestureLast[j].dollarPath.length = 0; + inTouch->gestureLast[j].dollarPath.p[0].x = x; + inTouch->gestureLast[j].dollarPath.p[0].y = y; + inTouch->gestureLast[j].dollarPath.numPoints = 1; } } } diff --git a/touchTest/touchPong b/touchTest/touchPong index a2a496ee7cb9176364e0062ab19b8699072cd0f7..b398f5a1a281e9056775f9523110a62da29f6965 100755 GIT binary patch delta 2754 zcmX|D2~d>h8Ghg2U07gW*aa3@7ex*MIRutN2pEGxT91jQ#8_q2a4algMF&<<+XRnz zYoqyMOggEl);2m#sFkTPo{<`j)>^I9yEdkd(M*%6V`{8PY@csg*qMLd`+mRg{}0~g z`=9fs;r!|Elcsa*@jHbp9>!mrxVY+m$NcS{M-P5A+0(s&lM*}Y5{NqDiM+L~4L(mB zy#su{r>$*X+Z@lLW&*cFF?Y*xcHLM8;@# ztl9!Qk)SxOob&^ z@Rw>*@{5Hnh-#Lap+q6lg|b3rOB9hs5~GR2q$iRnTq;pi1X)d?Roj?I;?>>c68?!w zPI2>bH7%utx2vC}Tr*;wswK7Ewh7vyp&SBLBPHB6OMR6(F?)KzHKzxd@wOPm3ylnt z31pAeHc@OHgwXY>VMMvLei+cFLrnPhtM^8XQ5{aQ-BN+~Gox~0Bcalng8zj#I{DZ^`@F~d>fSV+jVfPuyb#*^u zCU>c4M&?B|VzS?mbRyA*B%9U7kuJWcc8{D_dJK|HEMD|rGs{&_ud#dypjZgiz0R^4 zvMnr5P;ang18ij(tDent@me)EGmjssZJBxXhfwVwSatyHV|f8de8BPnir&vM7-fFQ z(u)KSu!Num2U#|NI>fRK;4sS`fFmqD07qHkk?BV)cR(Fu$w59nEMKeqtjSSk5NE-| zdj5swsM?S<(sBu5F0y2*!&y)A1od5(D|H$2yv;Hpoal3w#c1#{LsrEQEjQ#tfE9*p zRb|<(xXTc&G30qO(ON^Cpw<~ORr#`AT&>n*KUs7itT$mBOZ1i@R?O(Qqj$ z#RX3*A|+4V&uKRzoXR`ubqyYm8p3mwGxwk-F6OS%V0PYXoU88TxwuA2{uF+!X63uI zU`c+X2A}0m<|kFIyGRp_?hGiu>P`b|pWAI4fr-fDzGGz$gTh1iXiijl1)25@s8Aw; zN6u`6tfe5!UZV}7cnS`Nkzyr#qI$RBIqmuTf~w(Zkh+&tj7k)ivywSFj#3B2;HJdUyzKjRbtO% zE}sK8&;=b<+WDWIU_>9rnwsRNyka1Sm-=%cyB5mJ+z46pBZDYbUg0|fiYxmS`;uKX zknB;tCaZ&Ox7d5cxlF>~9SNVWzf8ksRjJz#WZxP)E2JuOP>HeZ?Q;JsMxGH}@kx zg4dKIuyx0HoDKLgt<%FNG@hzg|Brma4t%}d+v7X|=iaef;~#Su@J`3~+R+J)!DZqo z2S-i@_Q27l7wD6~s2vxz(^Fi65**<;=yIBWtetisL-`Z$(Y6&j(KDP1GsjG=^Jl)K z@gM2(&j$9}aZ@kFg}@*jm3RS3Z*icTWY>+6OMDg)%pYSHA_o^>Rm^WAxk#rzyOuGC zWs7o5LhfPt>Sjqy=D!iqNU?PiiNLV{-@@!TD%v8*C=r%rg@Ue#4L)TX@+@_%A+jYk`J<1KZqY>OiV3VUG} z(E|TtDSQJ!k6Kw?5UanUhHLKsm=Y^6RgS)`&Xf-=_rXMeNd-(GTcq8&T;Q;I(9ZoP zOQOC-d6?D%XXzer&ZC!_(>-ELu8FDpxr(^>xa!Kub1LWge6?*q@U;6Ew0Wu)dRqP6 z%`+yM!?4+jAlW^C&W%CcQ|tXEqb@<6XiDZI>TXlI(K1#=d(-%qD)pwD m&y6EmsXX2kV_L0R=S?%#)~UUao>N!6>BbB5yN%}3@c#q#;V4l6 delta 2562 zcmX|D4N%kP8GoL?2_%q*BtS?YC_<0`0r_r0z)$d6wRJspwt8q0Vu|u$A&7eC(AuhN zaZ}$~>)Kl9Z8x@7Yl~XHXw}`k)pK6SXw9 zH@5~m$>AYd5$x>j=v*9J6$(=&h;y1cgTZ#% z3Nh3k3P;7W5Zf9Z|M-1(PGt?2jltj(;N3 z*o3Hb zrnth5@d)Z4P?6*S-y{|IHOoV4Z_XTEufEAy%coV?KP4lCEX(C`EK!9V5uyiV4>S+T zLJLu)98?$mb9j@=%`N7DdLq}``%N0r1(NYDqK`;cV22?RKdO6?q|8S22a>ypVVL9* z#D9ro4~q6jk|9*!Pb7Ze0Mn`)t(fTasP`dYfd8+LGtx%j%bT9aVc^*~-#_8f;@Z3TZpbIS|Ei z4dg|ZO|b1?xee(hmUyJRlVyy0EZ@tWsx7~mzfimLi<^FqNjSvv0?1*OhY`fPEPIgj z5f(SnJjybN0FSZ6pajQRx*?qaJjfu+E|B+F-T--@#e+ysvRr_4iX{bGpJq9$N&>U& zUtxm}Sqd?p=UMtyUm(wN659;3B&or`L@rg|1-#kw5$AQp5l3``WeEzr-jIz+M9&y< z6l8-T>r{1tH{}#en+^HCndpax7?7Sfq)N3Gc)3PxE|^$x1*(_fn@qIJ5F2{*6A;YT zIukiBqK|Pea6Y`vAw&@$NXPV8>ePP=>XPfAuvNT<`q@lUIfSK>ujUj!!A)vkp_j*~ z4+^s^HxcND1W8r53cHMdc~r~z7d5#%K8+tzsYS=NFkG}zlSReb*{p6Bd$~?Y$!xx% z8cMv{u%=|GCZCtgV!tXXt=z;|9yUfGMlY*!iqY;*r z*K>yIFE7v@!{zNo8G4f$(Ipl~#A9Yuv^pYqv!Y_02I#7&cSd{`-1mX@gNgtyqpvGw z_6}l1Ow!7P|y znzF@_1rJ%p&yG~Uk*mekQL)!SPK_*U*ap3G9)?=h@>=kA&2!>HeH+Ph{1#Lr0dBmv zZ&F*=QzV-rCc|f2S@5peSSkDY0bKvC3;KF}iw|gixt^K>{2u-y zT#=5y%~KuV*XuR!@EOgE9=icP;{+epGjot1#+h?%*Zd*guleg<>LoeMNjS)^8ffI* z$Q-!#qv3KS(rVYR20hA$PzBegI-O(uxCZS(gmRn@Xy1Gt=n2k-o2wW{S_b)&=0DSe za5_?aR~;^V`E{g#a$|01-`@XIss^F8iKZ` z?2Zf&S#|B~?nW34pT(+&&F&eI;;hqfQQ1>lBQ_hkE$qJPR{N^bEfWC2sVCg(LY12x z>dPuWS1RWOzwCt}Z8so!{9cc$pHOP_d(`R)6`~&zBZe9nj!&2``W-Q%nGQ`x^`gL3 zB+Eh87-d^bmIi#UAWHqhNc%V1!@48x{Vo#XAip;4SmivYH*P9;D=hU;J|5PolU!kTR8ATb8U}EMv9y ze@q!u&_zz1uNKyhuDJ>q{c4H0z_v=mdHbMY^QfO4F zTVxve^OO|NfOEkNvkERA90)vlb<{vi^GcI3JjQ(-yyRWuiLUl7s8yW{clE mc`DIDH9F)oUU^bY4doc;8`ZK<4tvzLkl*O<7&sQHiu*s{lM9ys diff --git a/touchTest/touchSimp b/touchTest/touchSimp index 99cb10e2566cba093172ac2ed0ccb923636cf653..bbbd1353406ad66234d780e59607598bbabb99c0 100755 GIT binary patch delta 2696 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