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