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