Moved $1 Gestures into the SDL Library
authorJim Grandpre <jim.tla@gmail.com>
Fri, 09 Jul 2010 00:50:40 -0700
changeset 4658 454385d76845
parent 4657 eed063a0bf5b
child 4659 063b9455bd1a
Moved $1 Gestures into the SDL Library
include/SDL_events.h
src/events/SDL_gesture.c
touchTest/touchPong
touchTest/touchSimp
--- a/include/SDL_events.h	Wed Jul 07 04:13:08 2010 -0700
+++ b/include/SDL_events.h	Fri Jul 09 00:50:40 2010 -0700
@@ -354,6 +354,22 @@
 
 } SDL_MultiGestureEvent;
 
+typedef struct SDL_DollarGestureEvent
+{
+    Uint32 type;        /**< ::SDL_DOLLARGESTURE */
+    Uint32 windowID;    /**< The window with mouse focus, if any */
+    Uint8 touchId;        /**< The touch device index */
+    Uint8 gestureId;
+    Uint8 padding2;
+    Uint8 padding3;
+    float error;
+  /*
+    //TODO: Enable to give location?
+    float x;  //currently 0...1. Change to screen coords?
+    float y;  
+  */
+} SDL_DollarGestureEvent;
+
 
 
 
@@ -443,6 +459,7 @@
     SDL_TouchFingerEvent tfinger;   /**< Touch finger event data */
     SDL_TouchButtonEvent tbutton;   /**< Touch button event data */
     SDL_MultiGestureEvent mgesture; /**< Multi Finger Gesture data*/
+    SDL_DollarGestureEvent dgesture; /**< Multi Finger Gesture data*/
 
     /** Temporarily here for backwards compatibility */
     /*@{*/
--- a/src/events/SDL_gesture.c	Wed Jul 07 04:13:08 2010 -0700
+++ b/src/events/SDL_gesture.c	Fri Jul 09 00:50:40 2010 -0700
@@ -30,6 +30,14 @@
 //TODO: Replace with malloc
 #define MAXFINGERS 3
 #define MAXTOUCHES 2
+#define MAXTEMPLATES 4
+#define MAXPATHSIZE 1024
+
+#define DOLLARNPOINTS 64
+#define DOLLARSIZE 256
+
+//PHI = ((sqrt(5)-1)/2)
+#define PHI 0.618033989 
 
 typedef struct {
   float x,y;
@@ -42,10 +50,20 @@
   int id;
 } Finger;
 
+
+typedef struct {
+  float length;
+  
+  int numPoints;
+  Point p[MAXPATHSIZE];
+} DollarPath;
+
+
 typedef struct {
   Finger f;
   Point cv;
   float dtheta,dDist;
+  DollarPath dollarPath;
 } TouchPoint;
 
 
@@ -55,10 +73,160 @@
   Point centroid;
   TouchPoint gestureLast[MAXFINGERS];
   int numDownFingers;
+
+  int numDollarTemplates;
+  Point dollarTemplate[MAXTEMPLATES][DOLLARNPOINTS];
 } GestureTouch;
 
 GestureTouch gestureTouch[MAXTOUCHES];
 int numGestureTouches = 0;
+
+float dollarDifference(Point* points,Point* templ,float ang) {
+  //  Point p[DOLLARNPOINTS];
+  float dist = 0;
+  Point p;
+  int i;
+  for(i = 0; i < DOLLARNPOINTS; i++) {
+    p.x = points[i].x * cos(ang) - points[i].y * sin(ang);
+    p.y = points[i].x * sin(ang) + points[i].y * cos(ang);
+    dist += sqrt((p.x-templ[i].x)*(p.x-templ[i].x)+
+		 (p.y-templ[i].y)*(p.y-templ[i].y));
+  }
+  return dist/DOLLARNPOINTS;
+  
+}
+
+float bestDollarDifference(Point* points,Point* templ) {
+  //------------BEGIN DOLLAR BLACKBOX----------------//
+  //-TRANSLATED DIRECTLY FROM PSUDEO-CODE AVAILABLE AT-//
+  //-"http://depts.washington.edu/aimgroup/proj/dollar/"-//
+  float ta = -M_PI/4;
+  float tb = M_PI/4;
+  float dt = M_PI/90;
+  float x1 = PHI*ta + (1-PHI)*tb;
+  float f1 = dollarDifference(points,templ,x1);
+  float x2 = (1-PHI)*ta + PHI*tb;
+  float f2 = dollarDifference(points,templ,x2);
+  while(abs(ta-tb) > dt) {
+    if(f1 < f2) {
+      tb = x2;
+      x2 = x1;
+      f2 = f1;
+      x1 = PHI*ta + (1-PHI)*tb;
+      f1 = dollarDifference(points,templ,x1);
+    }
+    else {
+      ta = x1;
+      x1 = x2;
+      f1 = f2;
+      x2 = (1-PHI)*ta + PHI*tb;
+      f2 = dollarDifference(points,templ,x2);
+    }
+  }
+  /*
+  if(f1 <= f2)
+    printf("Min angle (x1): %f\n",x1);
+  else if(f1 >  f2)
+    printf("Min angle (x2): %f\n",x2);
+  */
+  return SDL_min(f1,f2);  
+}
+
+float dollarRecognize(DollarPath path,int *bestTempl,GestureTouch* touch) {
+
+  Point points[DOLLARNPOINTS];
+  int numPoints = dollarNormalize(path,points);
+  int i;
+ 
+  int bestDiff = 10000;
+  *bestTempl = -1;
+  for(i = 0;i < touch->numDollarTemplates;i++) {
+    int diff = bestDollarDifference(points,touch->dollarTemplate[i]);
+    if(diff < bestDiff) {bestDiff = diff; *bestTempl = i;}
+  }
+  return bestDiff;
+}
+
+//DollarPath contains raw points, plus (possibly) the calculated length
+int dollarNormalize(DollarPath path,Point *points) {
+  int i;
+  //Calculate length if it hasn't already been done
+  if(path.length <= 0) {
+    for(i=1;i<path.numPoints;i++) {
+      float dx = path.p[i  ].x - 
+	         path.p[i-1].x;
+      float dy = path.p[i  ].y - 
+	         path.p[i-1].y;
+      path.length += sqrt(dx*dx+dy*dy);
+    }
+  }
+
+
+  //Resample
+  float interval = path.length/(DOLLARNPOINTS - 1);
+  float dist = 0;
+
+  int numPoints = 0;
+  Point centroid; centroid.x = 0;centroid.y = 0;
+  //printf("(%f,%f)\n",path.p[path.numPoints-1].x,path.p[path.numPoints-1].y);
+  for(i = 1;i < path.numPoints;i++) {
+    float d = sqrt((path.p[i-1].x-path.p[i].x)*(path.p[i-1].x-path.p[i].x)+
+		   (path.p[i-1].y-path.p[i].y)*(path.p[i-1].y-path.p[i].y));
+    //printf("d = %f dist = %f/%f\n",d,dist,interval);
+    while(dist + d > interval) {
+      points[numPoints].x = path.p[i-1].x + 
+	((interval-dist)/d)*(path.p[i].x-path.p[i-1].x);
+      points[numPoints].y = path.p[i-1].y + 
+	((interval-dist)/d)*(path.p[i].y-path.p[i-1].y);
+      centroid.x += points[numPoints].x;
+      centroid.y += points[numPoints].y;
+      numPoints++;
+
+      dist -= interval;
+    }
+    dist += d;
+  }
+  if(numPoints < 1) return 0;
+  centroid.x /= numPoints;
+  centroid.y /= numPoints;
+ 
+  //printf("Centroid (%f,%f)",centroid.x,centroid.y);
+  //Rotate Points so point 0 is left of centroid and solve for the bounding box
+  float xmin,xmax,ymin,ymax;
+  xmin = centroid.x;
+  xmax = centroid.x;
+  ymin = centroid.y;
+  ymax = centroid.y;
+  
+  float ang = atan2(centroid.y - points[0].y,
+		    centroid.x - points[0].x);
+
+  for(i = 0;i<numPoints;i++) {					       
+    float px = points[i].x;
+    float py = points[i].y;
+    points[i].x = (px - centroid.x)*cos(ang) - 
+                  (py - centroid.y)*sin(ang) + centroid.x;
+    points[i].y = (px - centroid.x)*sin(ang) + 
+                  (py - centroid.y)*cos(ang) + centroid.y;
+
+
+    if(points[i].x < xmin) xmin = points[i].x;
+    if(points[i].x > xmax) xmax = points[i].x; 
+    if(points[i].y < ymin) ymin = points[i].y;
+    if(points[i].y > ymax) ymax = points[i].y;
+  }
+
+  //Scale points to DOLLARSIZE, and translate to the origin
+  float w = xmax-xmin;
+  float h = ymax-ymin;
+
+  for(i=0;i<numPoints;i++) {
+    points[i].x = (points[i].x - centroid.x)*DOLLARSIZE/w;
+    points[i].y = (points[i].y - centroid.y)*DOLLARSIZE/h;
+  }  
+  return numPoints;
+}
+
 int SDL_GestureAddTouch(SDL_Touch* touch) { 
   if(numGestureTouches >= MAXTOUCHES) return -1;
   
@@ -69,6 +237,8 @@
   gestureTouch[numGestureTouches].res.x = touch->xres;
   gestureTouch[numGestureTouches].id = touch->id;
 
+  gestureTouch[numGestureTouches].numDollarTemplates = 0;
+
   numGestureTouches++;
   return 0;
 }
@@ -93,6 +263,20 @@
   return SDL_PushEvent(&event) > 0;
 }
 
+int SDL_SendGestureDollar(GestureTouch* touch,int gestureId,float error) {
+  SDL_Event event;
+  event.dgesture.type = SDL_DOLLARGESTURE;
+  event.dgesture.touchId = touch->id;
+  /*
+    //TODO: Add this to give location of gesture?
+  event.mgesture.x = touch->centroid.x;
+  event.mgesture.y = touch->centroid.y;
+  */
+  event.dgesture.gestureId = gestureId;
+  event.dgesture.error = error;  
+  return SDL_PushEvent(&event) > 0;
+}
+
 void SDL_GestureProcessEvent(SDL_Event* event)
 {
   if(event->type == SDL_FINGERMOTION || 
@@ -100,7 +284,6 @@
      event->type == SDL_FINGERUP) {
     GestureTouch* inTouch = SDL_GetGestureTouch(event->tfinger.touchId);
 
-
     //Shouldn't be possible
     if(inTouch == NULL) return;
     
@@ -114,12 +297,35 @@
 
       if(event->type == SDL_FINGERUP) {
 	inTouch->numDownFingers--;
+
+	int bestTempl;
+	float error;
+	error = dollarRecognize(inTouch->gestureLast[j].dollarPath,
+				&bestTempl,inTouch);
+	if(bestTempl >= 0){
+	  //Send Event
+	  int gestureId = 0; //?
+	  SDL_SendGestureDollar(inTouch->id,gestureId,error);
+
+
+	  printf("Dollar error: %f\n",error);
+	}
+
 	inTouch->gestureLast[j] = inTouch->gestureLast[inTouch->numDownFingers];
 	break;
       }
       else {
 	float dx = x - inTouch->gestureLast[j].f.p.x;
 	float dy = y - inTouch->gestureLast[j].f.p.y;
+	DollarPath* path = &inTouch->gestureLast[j].dollarPath;
+	if(path->numPoints < MAXPATHSIZE) {
+	  path->p[path->numPoints].x = x;
+	  path->p[path->numPoints].y = y;
+	  path->length += sqrt(dx*dx + dy*dy);
+	  path->numPoints++;
+	}
+
+
 	inTouch->centroid.x += dx/inTouch->numDownFingers;
 	inTouch->centroid.y += dy/inTouch->numDownFingers;    
 	if(inTouch->numDownFingers > 1) {
@@ -181,6 +387,11 @@
       inTouch->gestureLast[j].f.p.y  = y;	
       inTouch->gestureLast[j].cv.x = 0;
       inTouch->gestureLast[j].cv.y = 0;
+
+      inTouch->gestureLast[j].dollarPath.length = 0;
+      inTouch->gestureLast[j].dollarPath.p[0].x = x;
+      inTouch->gestureLast[j].dollarPath.p[0].y = y;
+      inTouch->gestureLast[j].dollarPath.numPoints = 1;
     }
   }
 }  
Binary file touchTest/touchPong has changed
Binary file touchTest/touchSimp has changed