Added $1 gesture recognition.
Functional.
1.1 --- a/src/events/SDL_touch.c Tue Jun 15 02:12:16 2010 -0400
1.2 +++ b/src/events/SDL_touch.c Thu Jun 17 03:41:27 2010 -0400
1.3 @@ -322,7 +322,7 @@
1.4 nf.last_y = y;
1.5 nf.last_pressure = pressure;
1.6 SDL_AddFinger(touch,&nf);
1.7 -
1.8 + //if(x < 0 || y < 0) return 0; //should defer if only a partial input
1.9 posted = 0;
1.10 if (SDL_GetEventState(SDL_FINGERDOWN) == SDL_ENABLE) {
1.11 SDL_Event event;
1.12 @@ -364,15 +364,14 @@
1.13 int xrel;
1.14 int yrel;
1.15 int x_max = 0, y_max = 0;
1.16 -
1.17 +
1.18 if (!touch || touch->flush_motion) {
1.19 - return 0;
1.20 + return 0;
1.21 }
1.22 -
1.23 - if(finger == NULL) {
1.24 - SDL_SendFingerDown(id,fingerid,SDL_TRUE,x,y,pressure);
1.25 - return 0;
1.26 - } else {
1.27 +
1.28 + if(finger == NULL) {
1.29 + return SDL_SendFingerDown(id,fingerid,SDL_TRUE,x,y,pressure);
1.30 + } else {
1.31 /* the relative motion is calculated regarding the last position */
1.32 if (relative) {
1.33 xrel = x;
1.34 @@ -389,9 +388,9 @@
1.35
1.36 /* Drop events that don't change state */
1.37 if (!xrel && !yrel) {
1.38 - #if 0
1.39 +#if 0
1.40 printf("Touch event didn't change state - dropped!\n");
1.41 - #endif
1.42 +#endif
1.43 return 0;
1.44 }
1.45
2.1 --- a/touchTest/gestureTest.c Tue Jun 15 02:12:16 2010 -0400
2.2 +++ b/touchTest/gestureTest.c Thu Jun 17 03:41:27 2010 -0400
2.3 @@ -4,6 +4,7 @@
2.4 #include <SDL_touch.h>
2.5
2.6 #define PI 3.1415926535897
2.7 +#define PHI ((sqrt(5)-1)/2)
2.8 #define WIDTH 640
2.9 #define HEIGHT 480
2.10 #define BPP 4
2.11 @@ -11,8 +12,8 @@
2.12
2.13 #define MAXFINGERS 3
2.14
2.15 -
2.16 -
2.17 +#define DOLLARNPOINTS 64
2.18 +#define DOLLARSIZE 256
2.19
2.20 //MUST BE A POWER OF 2!
2.21 #define EVENT_BUF_SIZE 256
2.22 @@ -45,10 +46,26 @@
2.23 } Line;
2.24
2.25
2.26 +typedef struct {
2.27 + float length;
2.28 +
2.29 + int numPoints;
2.30 + Point p[EVENT_BUF_SIZE]; //To be safe
2.31 +} DollarPath;
2.32 +
2.33 +
2.34 Finger finger[MAXFINGERS];
2.35
2.36 Finger gestureLast[MAXFINGERS];
2.37 +DollarPath dollarPath[MAXFINGERS];
2.38 +
2.39 +#define MAXTEMPLATES 4
2.40 +
2.41 +Point dollarTemplate[MAXTEMPLATES][DOLLARNPOINTS];
2.42 +int numDollarTemplates = 0;
2.43 +#ifdef DRAW_VECTOR_EST
2.44 Line gestureLine[MAXFINGERS];
2.45 +#endif
2.46
2.47 void handler (int sig)
2.48 {
2.49 @@ -110,8 +127,7 @@
2.50 setpix(screen,tx,(int)(y+r*sin(a)),c);
2.51 setpix(screen,tx,(int)(y-r*sin(a)),c);
2.52 }
2.53 - }
2.54 - else {
2.55 + } else {
2.56 //Draw Outline
2.57 setpix(screen,(int)(x+r*cos(a)),(int)(y+r*sin(a)),c);
2.58 setpix(screen,(int)(x-r*cos(a)),(int)(y+r*sin(a)),c);
2.59 @@ -135,6 +151,165 @@
2.60 }
2.61 }
2.62
2.63 +void drawDollarPath(SDL_Surface* screen,Point* points,int numPoints,
2.64 + int rad,unsigned int col){
2.65 + int i;
2.66 + for(i=0;i<numPoints;i++) {
2.67 + drawCircle(screen,points[i].x+screen->w/2,
2.68 + points[i].y+screen->h/2,
2.69 + rad,col);
2.70 + }
2.71 +}
2.72 +
2.73 +float dollarDifference(Point* points,Point* templ,float ang) {
2.74 + // Point p[DOLLARNPOINTS];
2.75 + float dist = 0;
2.76 + Point p;
2.77 + int i;
2.78 + for(i = 0; i < DOLLARNPOINTS; i++) {
2.79 + p.x = points[i].x * cos(ang) - points[i].y * sin(ang);
2.80 + p.y = points[i].x * sin(ang) + points[i].y * cos(ang);
2.81 + dist += sqrt((p.x-templ[i].x)*(p.x-templ[i].x)+
2.82 + (p.y-templ[i].y)*(p.y-templ[i].y));
2.83 + }
2.84 + return dist/DOLLARNPOINTS;
2.85 +
2.86 +}
2.87 +
2.88 +float bestDollarDifference(Point* points,Point* templ) {
2.89 + //------------BEGIN DOLLAR BLACKBOX----------------//
2.90 + //-TRANSLATED DIRECTLY FROM PSUDEO-CODE AVAILABLE AT-//
2.91 + //-"http://depts.washington.edu/aimgroup/proj/dollar/"-//
2.92 + float ta = -PI/4;
2.93 + float tb = PI/4;
2.94 + float dt = PI/90;
2.95 + float x1 = PHI*ta + (1-PHI)*tb;
2.96 + float f1 = dollarDifference(points,templ,x1);
2.97 + float x2 = (1-PHI)*ta + PHI*tb;
2.98 + float f2 = dollarDifference(points,templ,x2);
2.99 + while(abs(ta-tb) > dt) {
2.100 + if(f1 < f2) {
2.101 + tb = x2;
2.102 + x2 = x1;
2.103 + f2 = f1;
2.104 + x1 = PHI*ta + (1-PHI)*tb;
2.105 + f1 = dollarDifference(points,templ,x1);
2.106 + }
2.107 + else {
2.108 + ta = x1;
2.109 + x1 = x2;
2.110 + f1 = f2;
2.111 + x2 = (1-PHI)*ta + PHI*tb;
2.112 + f2 = dollarDifference(points,templ,x2);
2.113 + }
2.114 + }
2.115 + return SDL_min(f1,f2);
2.116 +
2.117 +}
2.118 +
2.119 +float dollarRecognize(SDL_Surface* screen, DollarPath path,int *bestTempl) {
2.120 +
2.121 + Point points[DOLLARNPOINTS];
2.122 + int numPoints = dollarNormalize(path,points);
2.123 + int i;
2.124 +
2.125 + int k;
2.126 + for(k = 0;k<DOLLARNPOINTS;k++) {
2.127 + printf("(%f,%f)\n",points[k].x,
2.128 + points[k].y);
2.129 + }
2.130 +
2.131 + drawDollarPath(screen,points,numPoints,-15,0xFF6600);
2.132 +
2.133 + int bestDiff = 10000;
2.134 + *bestTempl = -1;
2.135 + for(i = 0;i < numDollarTemplates;i++) {
2.136 + int diff = bestDollarDifference(points,dollarTemplate[i]);
2.137 + if(diff < bestDiff) {bestDiff = diff; *bestTempl = i;}
2.138 + }
2.139 + return bestDiff;
2.140 +}
2.141 +
2.142 +//DollarPath contains raw points, plus (possibly) the calculated length
2.143 +int dollarNormalize(DollarPath path,Point *points) {
2.144 + int i;
2.145 + //Calculate length if it hasn't already been done
2.146 + if(path.length <= 0) {
2.147 + for(i=1;i<path.numPoints;i++) {
2.148 + float dx = path.p[i ].x -
2.149 + path.p[i-1].x;
2.150 + float dy = path.p[i ].y -
2.151 + path.p[i-1].y;
2.152 + path.length += sqrt(dx*dx+dy*dy);
2.153 + }
2.154 + }
2.155 +
2.156 +
2.157 + //Resample
2.158 + float interval = path.length/(DOLLARNPOINTS - 1);
2.159 + float dist = 0;
2.160 +
2.161 + int numPoints = 0;
2.162 + Point centroid; centroid.x = 0;centroid.y = 0;
2.163 + //printf("(%f,%f)\n",path.p[path.numPoints-1].x,path.p[path.numPoints-1].y);
2.164 + for(i = 1;i < path.numPoints;i++) {
2.165 + float d = sqrt((path.p[i-1].x-path.p[i].x)*(path.p[i-1].x-path.p[i].x)+
2.166 + (path.p[i-1].y-path.p[i].y)*(path.p[i-1].y-path.p[i].y));
2.167 + //printf("d = %f dist = %f/%f\n",d,dist,interval);
2.168 + while(dist + d > interval) {
2.169 + points[numPoints].x = path.p[i-1].x +
2.170 + ((interval-dist)/d)*(path.p[i].x-path.p[i-1].x);
2.171 + points[numPoints].y = path.p[i-1].y +
2.172 + ((interval-dist)/d)*(path.p[i].y-path.p[i-1].y);
2.173 + centroid.x += points[numPoints].x;
2.174 + centroid.y += points[numPoints].y;
2.175 + numPoints++;
2.176 +
2.177 + dist -= interval;
2.178 + }
2.179 + dist += d;
2.180 + }
2.181 + if(numPoints < 1) return 0;
2.182 + centroid.x /= numPoints;
2.183 + centroid.y /= numPoints;
2.184 +
2.185 + //printf("Centroid (%f,%f)",centroid.x,centroid.y);
2.186 + //Rotate Points so point 0 is left of centroid and solve for the bounding box
2.187 + float xmin,xmax,ymin,ymax;
2.188 + xmin = centroid.x;
2.189 + xmax = centroid.x;
2.190 + ymin = centroid.y;
2.191 + ymax = centroid.y;
2.192 +
2.193 + float ang = atan2(centroid.y - points[0].y,
2.194 + centroid.x - points[0].x);
2.195 +
2.196 + for(i = 0;i<numPoints;i++) {
2.197 + float px = points[i].x;
2.198 + float py = points[i].y;
2.199 + points[i].x = (px - centroid.x)*cos(ang) -
2.200 + (py - centroid.y)*sin(ang) + centroid.x;
2.201 + points[i].y = (px - centroid.x)*sin(ang) +
2.202 + (py - centroid.y)*cos(ang) + centroid.y;
2.203 +
2.204 +
2.205 + if(points[i].x < xmin) xmin = points[i].x;
2.206 + if(points[i].x > xmax) xmax = points[i].x;
2.207 + if(points[i].y < ymin) ymin = points[i].y;
2.208 + if(points[i].y > ymax) ymax = points[i].y;
2.209 + }
2.210 +
2.211 + //Scale points to DOLLARSIZE, and translate to the origin
2.212 + float w = xmax-xmin;
2.213 + float h = ymax-ymin;
2.214 +
2.215 + for(i=0;i<numPoints;i++) {
2.216 + points[i].x = (points[i].x - centroid.x)*DOLLARSIZE/w;
2.217 + points[i].y = (points[i].y - centroid.y)*DOLLARSIZE/h;
2.218 + }
2.219 + return numPoints;
2.220 +}
2.221 +
2.222 void DrawScreen(SDL_Surface* screen, int h)
2.223 {
2.224 int x, y, xm,ym,c;
2.225 @@ -161,7 +336,9 @@
2.226 //draw Touch History
2.227 for(i = 0;i < MAXFINGERS;i++) {
2.228 gestureLast[i].id = -1;
2.229 +#ifdef DRAW_VECTOR_EST
2.230 gestureLine[i].points = 0;
2.231 +#endif
2.232 }
2.233 for(i = SDL_max(0,eventWrite - EVENT_BUF_SIZE);i != eventWrite;i++) {
2.234 SDL_Event event = events[i&(EVENT_BUF_SIZE-1)];
2.235 @@ -175,11 +352,13 @@
2.236 float x = ((float)event.tfinger.x)/inTouch->xres;
2.237 float y = ((float)event.tfinger.y)/inTouch->yres;
2.238 int j,empty = -1;
2.239 +
2.240 for(j = 0;j<MAXFINGERS;j++) {
2.241 if(gestureLast[j].id == event.tfinger.fingerId) {
2.242 if(event.type == SDL_FINGERUP) {
2.243 +#ifdef DRAW_VECTOR_EST
2.244 if(gestureLine[j].points > 0)
2.245 - drawLine(screen,
2.246 + drawLine(screen,
2.247 gestureLine[j].s.x*screen->w,
2.248 gestureLine[j].s.y*screen->h,
2.249 (gestureLine[j].s.x +50*gestureLine[j].d.x)*screen->w,
2.250 @@ -187,10 +366,47 @@
2.251 0xFF00);
2.252
2.253 gestureLine[j].points = 0;
2.254 +#endif
2.255 + //ignore last point - probably invalid
2.256 + dollarPath[j].numPoints--;
2.257 +
2.258 +
2.259 + float dx = dollarPath[j].p[dollarPath[j].numPoints].x -
2.260 + dollarPath[j].p[dollarPath[j].numPoints - 1].x;
2.261 + float dy = dollarPath[j].p[dollarPath[j].numPoints].y -
2.262 + dollarPath[j].p[dollarPath[j].numPoints - 1].y;
2.263 + dollarPath[j].length -= sqrt(dx*dx+dy*dy);
2.264 +
2.265 + if(!keystat[32]){ //spacebar
2.266 + int bestTempl;
2.267 + float error = dollarRecognize(screen,dollarPath[j],&bestTempl);
2.268 + printf("%i\n",bestTempl);
2.269 + if(bestTempl >= 0){
2.270 + drawDollarPath(screen,dollarTemplate[bestTempl]
2.271 + ,DOLLARNPOINTS,-15,0x0066FF);\
2.272 +
2.273 + printf("ERROR: %f\n",error);
2.274 + }
2.275 +
2.276 + }
2.277 + else if(numDollarTemplates < MAXTEMPLATES) {
2.278 +
2.279 + dollarNormalize(dollarPath[j],
2.280 + dollarTemplate[numDollarTemplates]);
2.281 + int k;
2.282 + /*
2.283 + for(k = 0;k<DOLLARNPOINTS;k++) {
2.284 + printf("(%f,%f)\n",dollarTemplate[numDollarTemplates][i].x,
2.285 + dollarTemplate[numDollarTemplates][i].y);
2.286 + }*/
2.287 + numDollarTemplates++;
2.288 + }
2.289 +
2.290 gestureLast[j].id = -1;
2.291 break;
2.292 }
2.293 else {
2.294 +#ifdef DRAW_VECTOR_EST
2.295 if(gestureLine[j].points == 1) {
2.296 gestureLine[j].d.x = x - gestureLine[j].s.x;
2.297 gestureLine[j].d.y = y - gestureLine[j].s.y;
2.298 @@ -211,9 +427,18 @@
2.299 gestureLine[j].s.y /= gestureLine[j].points;
2.300
2.301 gestureLine[j].d.x /= gestureLine[j].points;
2.302 - gestureLine[j].d.y /= gestureLine[j].points;
2.303 + gestureLine[j].d.y /= gestureLine[j].points;
2.304 +#endif
2.305
2.306 -
2.307 + dollarPath[j].p[dollarPath[j].numPoints].x = x;
2.308 + dollarPath[j].p[dollarPath[j].numPoints].y = y;
2.309 + float dx = (dollarPath[j].p[dollarPath[j].numPoints-1].x-
2.310 + dollarPath[j].p[dollarPath[j].numPoints ].x);
2.311 + float dy = (dollarPath[j].p[dollarPath[j].numPoints-1].y-
2.312 + dollarPath[j].p[dollarPath[j].numPoints ].y);
2.313 + dollarPath[j].length += sqrt(dx*dx + dy*dy);
2.314 +
2.315 + dollarPath[j].numPoints++;
2.316
2.317
2.318 gestureLast[j].p.x = x;
2.319 @@ -226,17 +451,23 @@
2.320 empty = j;
2.321 }
2.322 }
2.323 -
2.324 +
2.325 if(j >= MAXFINGERS && empty >= 0) {
2.326 - printf("Finger Down!!!\n");
2.327 + // printf("Finger Down!!!\n");
2.328 j = empty; //important that j is the index of the added finger
2.329 gestureLast[j].id = event.tfinger.fingerId;
2.330 gestureLast[j].p.x = x;
2.331 gestureLast[j].p.y = y;
2.332 -
2.333 +#ifdef DRAW_VECTOR_EST
2.334 gestureLine[j].s.x = x;
2.335 gestureLine[j].s.y = y;
2.336 gestureLine[j].points = 1;
2.337 +#endif
2.338 +
2.339 + dollarPath[j].length = 0;
2.340 + dollarPath[j].p[0].x = x;
2.341 + dollarPath[j].p[0].y = y;
2.342 + dollarPath[j].numPoints = 1;
2.343 }
2.344
2.345 //draw the touch && each centroid:
2.346 @@ -273,7 +504,7 @@
2.347 ,20,0xFF);
2.348
2.349
2.350 -
2.351 + keystat[32] = 0;
2.352
2.353 if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen);
2.354
2.355 @@ -400,11 +631,11 @@
2.356 }
2.357 //And draw
2.358 DrawScreen(screen,h);
2.359 - /*
2.360 - for(i=0;i<512;i++)
2.361 - if(keystat[i]) printf("%i\n",i);
2.362 - printf("Buttons:%i\n",bstatus);
2.363 - */
2.364 +
2.365 + //for(i=0;i<512;i++)
2.366 + // if(keystat[i]) printf("%i\n",i);
2.367 +
2.368 +
2.369 }
2.370 SDL_Quit();
2.371