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