src/events/SDL_gesture.c
author Jim Grandpre <jim.tla@gmail.com>
Sat, 07 Aug 2010 11:32:11 -0400
changeset 4685 e0c3b09368a6
parent 4684 f47c2640c667
child 4686 463cd74304b9
permissions -rw-r--r--
Fixed Dollar Recognition.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2010 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software    Founation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    17 
    18     Sam Lantinga
    19     slouken@libsdl.org
    20 */
    21 #include "SDL_config.h"
    22 
    23 /* General mouse handling code for SDL */
    24 
    25 #include "SDL_events.h"
    26 #include "SDL_events_c.h"
    27 #include "SDL_gesture_c.h"
    28 
    29 //TODO: Replace with malloc
    30 #define MAXFINGERS 5
    31 #define MAXTOUCHES 2
    32 #define MAXTEMPLATES 4
    33 #define MAXPATHSIZE 1024
    34 
    35 #define DOLLARNPOINTS 64
    36 #define DOLLARSIZE 256
    37 
    38 #define ENABLE_DOLLAR
    39 
    40 //PHI = ((sqrt(5)-1)/2)
    41 #define PHI 0.618033989 
    42 
    43 typedef struct {
    44   float x,y;
    45 } Point;
    46 
    47 
    48 typedef struct {
    49   Point p;
    50   float pressure;
    51   SDL_FingerID id;
    52 } Finger;
    53 
    54 
    55 typedef struct {
    56   float length;
    57   
    58   int numPoints;
    59   Point p[MAXPATHSIZE];
    60 } DollarPath;
    61 
    62 /*
    63 typedef struct {
    64   Finger f;
    65   Point cv;
    66   float dtheta,dDist;
    67   DollarPath dollarPath;
    68 } TouchPoint;
    69 */
    70 typedef struct {
    71   Point path[DOLLARNPOINTS];
    72   unsigned long hash;
    73 } DollarTemplate;
    74 
    75 typedef struct {
    76   SDL_GestureID id;
    77   Point res;
    78   Point centroid;
    79   //TouchPoint gestureLast[MAXFINGERS];
    80   DollarPath dollarPath;
    81   Uint16 numDownFingers;
    82 
    83   int numDollarTemplates;
    84   DollarTemplate dollarTemplate[MAXTEMPLATES];
    85 
    86   SDL_bool recording;
    87 } GestureTouch;
    88 
    89 GestureTouch gestureTouch[MAXTOUCHES];
    90 int numGestureTouches = 0;
    91 SDL_bool recordAll;
    92 
    93 void SDL_PrintPath(Point *path) {
    94   int i;
    95   printf("Path:");
    96   for(i=0;i<DOLLARNPOINTS;i++) {
    97     printf(" (%f,%f)",path[i].x,path[i].y);
    98   }
    99   printf("\n");
   100 }
   101 
   102 int SDL_RecordGesture(SDL_TouchID touchId) {
   103   int i;
   104   if(touchId < 0) recordAll = SDL_TRUE;
   105   for(i = 0;i < numGestureTouches; i++) {
   106     if((touchId < 0) || (gestureTouch[i].id == touchId)) {
   107       gestureTouch[i].recording = SDL_TRUE;
   108       if(touchId >= 0)
   109 	return 1;
   110     }      
   111   }
   112   return (touchId < 0);
   113 }
   114 
   115 unsigned long SDL_HashDollar(Point* points) {
   116   unsigned long hash = 5381;
   117   int i;
   118   for(i = 0;i < DOLLARNPOINTS; i++) { 
   119     hash = ((hash<<5) + hash) + points[i].x;
   120     hash = ((hash<<5) + hash) + points[i].y;
   121   }
   122   return hash;
   123 }
   124 
   125 
   126 static int SaveTemplate(DollarTemplate *templ, SDL_RWops * src) {
   127   if(src == NULL) return 0;
   128 
   129   int i;
   130   
   131   //No Longer storing the Hash, rehash on load
   132   //if(SDL_RWops.write(src,&(templ->hash),sizeof(templ->hash),1) != 1) return 0;
   133 
   134   if(SDL_RWwrite(src,templ->path,
   135 		 sizeof(templ->path[0]),DOLLARNPOINTS) != DOLLARNPOINTS) 
   136     return 0;
   137 
   138   return 1;
   139 }
   140 
   141 
   142 int SDL_SaveAllDollarTemplates(SDL_RWops *src) {  
   143   int i,j,rtrn = 0;
   144   for(i = 0; i < numGestureTouches; i++) {
   145     GestureTouch* touch = &gestureTouch[i];
   146     for(j = 0;j < touch->numDollarTemplates; j++) {
   147 	rtrn += SaveTemplate(&touch->dollarTemplate[i],src);
   148     }
   149   }
   150   return rtrn;  
   151 }
   152 
   153 int SDL_SaveDollarTemplate(SDL_GestureID gestureId, SDL_RWops *src) {
   154   int i,j;
   155   for(i = 0; i < numGestureTouches; i++) {
   156     GestureTouch* touch = &gestureTouch[i];
   157     for(j = 0;j < touch->numDollarTemplates; j++) {
   158       if(touch->dollarTemplate[i].hash == gestureId) {
   159 	return SaveTemplate(&touch->dollarTemplate[i],src);
   160       }
   161     }
   162   }
   163   SDL_SetError("Unknown gestureId");
   164   return -1;
   165 }
   166 
   167 //path is an already sampled set of points
   168 //Returns the index of the gesture on success, or -1
   169 static int SDL_AddDollarGesture(GestureTouch* inTouch,Point* path) {
   170   if(inTouch == NULL) {
   171     if(numGestureTouches == 0) return -1;
   172     int i = 0;
   173     for(i = 0;i < numGestureTouches; i++) {
   174       inTouch = &gestureTouch[i];
   175       if(inTouch->numDollarTemplates < MAXTEMPLATES) {
   176 	DollarTemplate *templ = 
   177 	  &inTouch->dollarTemplate[inTouch->numDollarTemplates];
   178 	memcpy(templ->path,path,DOLLARNPOINTS*sizeof(Point));
   179 	templ->hash = SDL_HashDollar(templ->path);
   180 	inTouch->numDollarTemplates++;
   181       }
   182     }
   183     return inTouch->numDollarTemplates - 1;
   184   }else if(inTouch->numDollarTemplates < MAXTEMPLATES) {
   185     DollarTemplate *templ = 
   186       &inTouch->dollarTemplate[inTouch->numDollarTemplates];
   187     memcpy(templ->path,path,DOLLARNPOINTS*sizeof(Point));
   188     templ->hash = SDL_HashDollar(templ->path);
   189     inTouch->numDollarTemplates++;
   190     return inTouch->numDollarTemplates - 1;
   191   }
   192   return -1;
   193 }
   194 
   195 int SDL_LoadDollarTemplates(SDL_TouchID touchId, SDL_RWops *src) {
   196   if(src == NULL) return 0;
   197   int i,loaded = 0;
   198   GestureTouch *touch = NULL;
   199   if(touchId >= 0) {
   200     for(i = 0;i < numGestureTouches; i++)
   201       if(gestureTouch[i].id == touchId)
   202 	touch = &gestureTouch[i];
   203     if(touch == NULL) return -1;
   204   }
   205 
   206   while(1) {
   207     DollarTemplate templ;
   208     //fscanf(fp,"%lu ",&templ.hash);
   209     /*
   210     for(i = 0;i < DOLLARNPOINTS; i++) {		
   211       int x,y;
   212       if(fscanf(fp,"%i %i ",&x,&y) != 2) break;
   213       templ.path[i].x = x;
   214       templ.path[i].y = y;
   215     }
   216     fscanf(fp,"\n");
   217     */
   218     if(SDL_RWread(src,templ.path,sizeof(templ.path[0]),DOLLARNPOINTS) < DOLLARNPOINTS) break;
   219 
   220     if(touchId >= 0) {
   221       printf("Adding loaded gesture to 1 touch\n");
   222       if(SDL_AddDollarGesture(touch,templ.path)) loaded++;
   223     }
   224     else {
   225       printf("Adding to: %i touches\n",numGestureTouches);
   226       for(i = 0;i < numGestureTouches; i++) {
   227 	touch = &gestureTouch[i];
   228 	printf("Adding loaded gesture to + touches\n");
   229 	//TODO: What if this fails?
   230 	SDL_AddDollarGesture(touch,templ.path);	
   231       }
   232       loaded++;
   233     }
   234   }
   235 
   236   return loaded; 
   237 }
   238 
   239 
   240 float dollarDifference(Point* points,Point* templ,float ang) {
   241   //  Point p[DOLLARNPOINTS];
   242   float dist = 0;
   243   Point p;
   244   int i;
   245   for(i = 0; i < DOLLARNPOINTS; i++) {
   246     p.x = points[i].x * cos(ang) - points[i].y * sin(ang);
   247     p.y = points[i].x * sin(ang) + points[i].y * cos(ang);
   248     dist += sqrt((p.x-templ[i].x)*(p.x-templ[i].x)+
   249 		 (p.y-templ[i].y)*(p.y-templ[i].y));
   250   }
   251   return dist/DOLLARNPOINTS;
   252   
   253 }
   254 
   255 float bestDollarDifference(Point* points,Point* templ) {
   256   //------------BEGIN DOLLAR BLACKBOX----------------//
   257   //-TRANSLATED DIRECTLY FROM PSUDEO-CODE AVAILABLE AT-//
   258   //-"http://depts.washington.edu/aimgroup/proj/dollar/"-//
   259   float ta = -M_PI/4;
   260   float tb = M_PI/4;
   261   float dt = M_PI/90;
   262   float x1 = PHI*ta + (1-PHI)*tb;
   263   float f1 = dollarDifference(points,templ,x1);
   264   float x2 = (1-PHI)*ta + PHI*tb;
   265   float f2 = dollarDifference(points,templ,x2);
   266   while(abs(ta-tb) > dt) {
   267     if(f1 < f2) {
   268       tb = x2;
   269       x2 = x1;
   270       f2 = f1;
   271       x1 = PHI*ta + (1-PHI)*tb;
   272       f1 = dollarDifference(points,templ,x1);
   273     }
   274     else {
   275       ta = x1;
   276       x1 = x2;
   277       f1 = f2;
   278       x2 = (1-PHI)*ta + PHI*tb;
   279       f2 = dollarDifference(points,templ,x2);
   280     }
   281   }
   282   /*
   283   if(f1 <= f2)
   284     printf("Min angle (x1): %f\n",x1);
   285   else if(f1 >  f2)
   286     printf("Min angle (x2): %f\n",x2);
   287   */
   288   return SDL_min(f1,f2);  
   289 }
   290 
   291 //DollarPath contains raw points, plus (possibly) the calculated length
   292 int dollarNormalize(DollarPath path,Point *points) {
   293   int i;
   294   //Calculate length if it hasn't already been done
   295   printf("length: %f\n",path.length);
   296   if(path.length <= 0) {
   297     for(i=1;i<path.numPoints;i++) {
   298       float dx = path.p[i  ].x - 
   299 	         path.p[i-1].x;
   300       float dy = path.p[i  ].y - 
   301 	         path.p[i-1].y;
   302       path.length += sqrt(dx*dx+dy*dy);
   303     }
   304   }
   305   printf("New length: %f\n",path.length);
   306 
   307   //Resample
   308   float interval = path.length/(DOLLARNPOINTS - 1);
   309   float dist = interval;
   310 
   311   int numPoints = 0;
   312   Point centroid; 
   313   centroid.x = 0;centroid.y = 0;
   314   
   315   //printf("(%f,%f)\n",path.p[path.numPoints-1].x,path.p[path.numPoints-1].y);
   316   for(i = 1;i < path.numPoints;i++) {
   317     float d = sqrt((path.p[i-1].x-path.p[i].x)*(path.p[i-1].x-path.p[i].x)+
   318 		   (path.p[i-1].y-path.p[i].y)*(path.p[i-1].y-path.p[i].y));
   319     //printf("d = %f dist = %f/%f\n",d,dist,interval);
   320     while(dist + d > interval) {
   321       points[numPoints].x = path.p[i-1].x + 
   322 	((interval-dist)/d)*(path.p[i].x-path.p[i-1].x);
   323       points[numPoints].y = path.p[i-1].y + 
   324 	((interval-dist)/d)*(path.p[i].y-path.p[i-1].y);
   325       centroid.x += points[numPoints].x;
   326       centroid.y += points[numPoints].y;
   327       numPoints++;
   328 
   329       dist -= interval;
   330     }
   331     dist += d;
   332   }
   333   if(numPoints < DOLLARNPOINTS-1) {
   334     printf("ERROR: NumPoints = %i\n",numPoints); 
   335     return 0;
   336   }
   337   //copy the last point
   338   points[DOLLARNPOINTS-1] = path.p[path.numPoints-1];
   339   numPoints = DOLLARNPOINTS;
   340 
   341   centroid.x /= numPoints;
   342   centroid.y /= numPoints;
   343  
   344   //printf("Centroid (%f,%f)",centroid.x,centroid.y);
   345   //Rotate Points so point 0 is left of centroid and solve for the bounding box
   346   float xmin,xmax,ymin,ymax;
   347   xmin = centroid.x;
   348   xmax = centroid.x;
   349   ymin = centroid.y;
   350   ymax = centroid.y;
   351   
   352   float ang = atan2(centroid.y - points[0].y,
   353 		    centroid.x - points[0].x);
   354 
   355   for(i = 0;i<numPoints;i++) {					       
   356     float px = points[i].x;
   357     float py = points[i].y;
   358     points[i].x = (px - centroid.x)*cos(ang) - 
   359                   (py - centroid.y)*sin(ang) + centroid.x;
   360     points[i].y = (px - centroid.x)*sin(ang) + 
   361                   (py - centroid.y)*cos(ang) + centroid.y;
   362 
   363 
   364     if(points[i].x < xmin) xmin = points[i].x;
   365     if(points[i].x > xmax) xmax = points[i].x; 
   366     if(points[i].y < ymin) ymin = points[i].y;
   367     if(points[i].y > ymax) ymax = points[i].y;
   368   }
   369 
   370   //Scale points to DOLLARSIZE, and translate to the origin
   371   float w = xmax-xmin;
   372   float h = ymax-ymin;
   373 
   374   for(i=0;i<numPoints;i++) {
   375     points[i].x = (points[i].x - centroid.x)*DOLLARSIZE/w;
   376     points[i].y = (points[i].y - centroid.y)*DOLLARSIZE/h;
   377   }  
   378   return numPoints;
   379 }
   380 
   381 float dollarRecognize(DollarPath path,int *bestTempl,GestureTouch* touch) {
   382 	
   383 	Point points[DOLLARNPOINTS];
   384 	int numPoints = dollarNormalize(path,points);
   385 	SDL_PrintPath(points);
   386 	int i;
   387 	
   388 	int bestDiff = 10000;
   389 	*bestTempl = -1;
   390 	for(i = 0;i < touch->numDollarTemplates;i++) {
   391 		int diff = bestDollarDifference(points,touch->dollarTemplate[i].path);
   392 		if(diff < bestDiff) {bestDiff = diff; *bestTempl = i;}
   393 	}
   394 	return bestDiff;
   395 }
   396 
   397 int SDL_GestureAddTouch(SDL_Touch* touch) { 
   398   if(numGestureTouches >= MAXTOUCHES) return -1;
   399   
   400   gestureTouch[numGestureTouches].res.x = touch->xres;
   401   gestureTouch[numGestureTouches].res.y = touch->yres;
   402   gestureTouch[numGestureTouches].numDownFingers = 0;
   403 
   404   gestureTouch[numGestureTouches].res.x = touch->xres;
   405   gestureTouch[numGestureTouches].id = touch->id;
   406 
   407   gestureTouch[numGestureTouches].numDollarTemplates = 0;
   408 
   409   gestureTouch[numGestureTouches].recording = SDL_FALSE;
   410 
   411   numGestureTouches++;
   412   return 0;
   413 }
   414 
   415 GestureTouch * SDL_GetGestureTouch(SDL_TouchID id) {
   416   int i;
   417   for(i = 0;i < numGestureTouches; i++) {
   418     //printf("%i ?= %i\n",gestureTouch[i].id,id);
   419     if(gestureTouch[i].id == id) return &gestureTouch[i];
   420   }
   421   return NULL;
   422 }
   423 
   424 int SDL_SendGestureMulti(GestureTouch* touch,float dTheta,float dDist) {
   425   SDL_Event event;
   426   event.mgesture.type = SDL_MULTIGESTURE;
   427   event.mgesture.touchId = touch->id;
   428   event.mgesture.x = touch->centroid.x;
   429   event.mgesture.y = touch->centroid.y;
   430   event.mgesture.dTheta = dTheta;
   431   event.mgesture.dDist = dDist;  
   432   event.mgesture.numFingers = touch->numDownFingers;
   433   return SDL_PushEvent(&event) > 0;
   434 }
   435 
   436 int SDL_SendGestureDollar(GestureTouch* touch,
   437 			  SDL_GestureID gestureId,float error) {
   438   SDL_Event event;
   439   event.dgesture.type = SDL_DOLLARGESTURE;
   440   event.dgesture.touchId = touch->id;
   441   /*
   442     //TODO: Add this to give location of gesture?
   443   event.mgesture.x = touch->centroid.x;
   444   event.mgesture.y = touch->centroid.y;
   445   */
   446   event.dgesture.gestureId = gestureId;
   447   event.dgesture.error = error;  
   448   return SDL_PushEvent(&event) > 0;
   449 }
   450 
   451 
   452 int SDL_SendDollarRecord(GestureTouch* touch,SDL_GestureID gestureId) {
   453   SDL_Event event;
   454   event.dgesture.type = SDL_DOLLARRECORD;
   455   event.dgesture.touchId = touch->id;
   456   event.dgesture.gestureId = gestureId;
   457 
   458   return SDL_PushEvent(&event) > 0;
   459 }
   460 
   461 
   462 void SDL_GestureProcessEvent(SDL_Event* event)
   463 {
   464   if(event->type == SDL_FINGERMOTION || 
   465      event->type == SDL_FINGERDOWN ||
   466      event->type == SDL_FINGERUP) {
   467     GestureTouch* inTouch = SDL_GetGestureTouch(event->tfinger.touchId);
   468     
   469     //Shouldn't be possible
   470     if(inTouch == NULL) return;
   471     
   472     //printf("@ (%i,%i) with res: (%i,%i)\n",(int)event->tfinger.x,
   473     //	   (int)event->tfinger.y,
   474     //   (int)inTouch->res.x,(int)inTouch->res.y);
   475 
   476     
   477     float x = ((float)event->tfinger.x)/(float)inTouch->res.x;
   478     float y = ((float)event->tfinger.y)/(float)inTouch->res.y;   
   479 
   480 
   481     //Finger Up
   482     if(event->type == SDL_FINGERUP) {
   483       inTouch->numDownFingers--;
   484       
   485 #ifdef ENABLE_DOLLAR
   486       if(inTouch->recording) {
   487 	inTouch->recording = SDL_FALSE;
   488 	Point path[DOLLARNPOINTS];
   489 	dollarNormalize(inTouch->dollarPath,path);
   490 	SDL_PrintPath(path);
   491 	int index;
   492 	if(recordAll) {
   493 	  index = SDL_AddDollarGesture(NULL,path);
   494 	  int i;
   495 	  for(i = 0;i < numGestureTouches; i++)
   496 	    gestureTouch[i].recording = SDL_FALSE;
   497 	}
   498 	else {
   499 	  index = SDL_AddDollarGesture(inTouch,path);
   500 	}
   501 	
   502 	if(index >= 0) {
   503 	  SDL_SendDollarRecord(inTouch,inTouch->dollarTemplate[index].hash);
   504 	}
   505 	else {
   506 	  SDL_SendDollarRecord(inTouch,-1);
   507 	}
   508       }
   509       else {	
   510 	int bestTempl;
   511 	float error;
   512 	error = dollarRecognize(inTouch->dollarPath,
   513 				&bestTempl,inTouch);
   514 	if(bestTempl >= 0){
   515 	  //Send Event
   516 	  unsigned long gestureId = inTouch->dollarTemplate[bestTempl].hash;
   517 	  SDL_SendGestureDollar(inTouch,gestureId,error);
   518 	  //printf ("%s\n",);("Dollar error: %f\n",error);
   519 	}
   520       }
   521 #endif 
   522       //inTouch->gestureLast[j] = inTouch->gestureLast[inTouch->numDownFingers];
   523       if(inTouch->numDownFingers > 0) {
   524 	inTouch->centroid.x = (inTouch->centroid.x*(inTouch->numDownFingers+1)-
   525 			       x)/inTouch->numDownFingers;
   526 	inTouch->centroid.y = (inTouch->centroid.y*(inTouch->numDownFingers+1)-
   527 			       y)/inTouch->numDownFingers;
   528       }
   529     }
   530     else if(event->type == SDL_FINGERMOTION) {
   531       float dx = ((float)event->tfinger.dx)/(float)inTouch->res.x;
   532       float dy = ((float)event->tfinger.dy)/(float)inTouch->res.y;
   533       //printf("dx,dy: (%f,%f)\n",dx,dy); 
   534 #ifdef ENABLE_DOLLAR
   535       DollarPath* path = &inTouch->dollarPath;
   536       if(path->numPoints < MAXPATHSIZE) {
   537 	path->p[path->numPoints].x = inTouch->centroid.x;
   538 	path->p[path->numPoints].y = inTouch->centroid.y;
   539 	float pathDx = 
   540 	  (path->p[path->numPoints].x-path->p[path->numPoints-1].x);
   541 	float pathDy = 
   542 	  (path->p[path->numPoints].y-path->p[path->numPoints-1].y);
   543 	path->length += sqrt(pathDx*pathDx + pathDy*pathDy);
   544 	path->numPoints++;
   545       }
   546 #endif
   547       Point lastP;
   548       lastP.x = x - dx;
   549       lastP.y = y - dy;
   550       Point lastCentroid;
   551       lastCentroid = inTouch->centroid;
   552       
   553       inTouch->centroid.x += dx/inTouch->numDownFingers;
   554       inTouch->centroid.y += dy/inTouch->numDownFingers;
   555       //printf("Centrid : (%f,%f)\n",inTouch->centroid.x,inTouch->centroid.y);
   556       if(inTouch->numDownFingers > 1) {
   557 	Point lv; //Vector from centroid to last x,y position
   558 	Point v; //Vector from centroid to current x,y position
   559 	//lv = inTouch->gestureLast[j].cv;
   560 	lv.x = lastP.x - lastCentroid.x;
   561 	lv.y = lastP.y - lastCentroid.y;
   562 	float lDist = sqrt(lv.x*lv.x + lv.y*lv.y);
   563 	//printf("lDist = %f\n",lDist);
   564 	v.x = x - inTouch->centroid.x;
   565 	v.y = y - inTouch->centroid.y;
   566 	//inTouch->gestureLast[j].cv = v;
   567 	float Dist = sqrt(v.x*v.x+v.y*v.y);
   568 	// cos(dTheta) = (v . lv)/(|v| * |lv|)
   569 	
   570 	//Normalize Vectors to simplify angle calculation
   571 	lv.x/=lDist;
   572 	lv.y/=lDist;
   573 	v.x/=Dist;
   574 	v.y/=Dist;
   575 	float dtheta = atan2(lv.x*v.y - lv.y*v.x,lv.x*v.x + lv.y*v.y);
   576 	
   577 	float dDist = (Dist - lDist);
   578 	if(lDist == 0) {dDist = 0;dtheta = 0;} //To avoid impossible values
   579 	
   580 	//inTouch->gestureLast[j].dDist = dDist;
   581 	//inTouch->gestureLast[j].dtheta = dtheta;
   582 	
   583 	//printf("dDist = %f, dTheta = %f\n",dDist,dtheta);
   584 	//gdtheta = gdtheta*.9 + dtheta*.1;
   585 	//gdDist  =  gdDist*.9 +  dDist*.1
   586 	//knob.r += dDist/numDownFingers;
   587 	//knob.ang += dtheta;
   588 	//printf("thetaSum = %f, distSum = %f\n",gdtheta,gdDist);
   589 	//printf("id: %i dTheta = %f, dDist = %f\n",j,dtheta,dDist);
   590 	SDL_SendGestureMulti(inTouch,dtheta,dDist);
   591       }
   592       else {
   593 	//inTouch->gestureLast[j].dDist = 0;
   594 	//inTouch->gestureLast[j].dtheta = 0;
   595 	//inTouch->gestureLast[j].cv.x = 0;
   596 	//inTouch->gestureLast[j].cv.y = 0;
   597       }
   598       //inTouch->gestureLast[j].f.p.x = x;
   599       //inTouch->gestureLast[j].f.p.y = y;
   600       //break;
   601       //pressure?
   602     }
   603     
   604     if(event->type == SDL_FINGERDOWN) {
   605 
   606       inTouch->numDownFingers++;
   607       inTouch->centroid.x = (inTouch->centroid.x*(inTouch->numDownFingers - 1)+ 
   608 			     x)/inTouch->numDownFingers;
   609       inTouch->centroid.y = (inTouch->centroid.y*(inTouch->numDownFingers - 1)+
   610 			     y)/inTouch->numDownFingers;
   611       printf("Finger Down: (%f,%f). Centroid: (%f,%f\n",x,y,
   612 	     inTouch->centroid.x,inTouch->centroid.y);
   613       /*
   614 	inTouch->gestureLast[j].f.id = event->tfinger.fingerId;
   615 	inTouch->gestureLast[j].f.p.x  = x;
   616 	inTouch->gestureLast[j].f.p.y  = y;	
   617 	inTouch->gestureLast[j].cv.x = 0;
   618 	inTouch->gestureLast[j].cv.y = 0;
   619       */
   620 #ifdef ENABLE_DOLLAR
   621       inTouch->dollarPath.length = 0;
   622       inTouch->dollarPath.p[0].x = x;
   623       inTouch->dollarPath.p[0].y = y;
   624       inTouch->dollarPath.numPoints = 1;
   625 #endif
   626     }
   627   }
   628 }
   629 
   630   /* vi: set ts=4 sw=4 expandtab: */
   631