Moved $1 Gestures into the SDL Library
authorJim Grandpre <jim.tla@gmail.com>
Fri, 09 Jul 2010 00:50:40 -0700
changeset 4658454385d76845
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
     1.1 --- a/include/SDL_events.h	Wed Jul 07 04:13:08 2010 -0700
     1.2 +++ b/include/SDL_events.h	Fri Jul 09 00:50:40 2010 -0700
     1.3 @@ -354,6 +354,22 @@
     1.4  
     1.5  } SDL_MultiGestureEvent;
     1.6  
     1.7 +typedef struct SDL_DollarGestureEvent
     1.8 +{
     1.9 +    Uint32 type;        /**< ::SDL_DOLLARGESTURE */
    1.10 +    Uint32 windowID;    /**< The window with mouse focus, if any */
    1.11 +    Uint8 touchId;        /**< The touch device index */
    1.12 +    Uint8 gestureId;
    1.13 +    Uint8 padding2;
    1.14 +    Uint8 padding3;
    1.15 +    float error;
    1.16 +  /*
    1.17 +    //TODO: Enable to give location?
    1.18 +    float x;  //currently 0...1. Change to screen coords?
    1.19 +    float y;  
    1.20 +  */
    1.21 +} SDL_DollarGestureEvent;
    1.22 +
    1.23  
    1.24  
    1.25  
    1.26 @@ -443,6 +459,7 @@
    1.27      SDL_TouchFingerEvent tfinger;   /**< Touch finger event data */
    1.28      SDL_TouchButtonEvent tbutton;   /**< Touch button event data */
    1.29      SDL_MultiGestureEvent mgesture; /**< Multi Finger Gesture data*/
    1.30 +    SDL_DollarGestureEvent dgesture; /**< Multi Finger Gesture data*/
    1.31  
    1.32      /** Temporarily here for backwards compatibility */
    1.33      /*@{*/
     2.1 --- a/src/events/SDL_gesture.c	Wed Jul 07 04:13:08 2010 -0700
     2.2 +++ b/src/events/SDL_gesture.c	Fri Jul 09 00:50:40 2010 -0700
     2.3 @@ -30,6 +30,14 @@
     2.4  //TODO: Replace with malloc
     2.5  #define MAXFINGERS 3
     2.6  #define MAXTOUCHES 2
     2.7 +#define MAXTEMPLATES 4
     2.8 +#define MAXPATHSIZE 1024
     2.9 +
    2.10 +#define DOLLARNPOINTS 64
    2.11 +#define DOLLARSIZE 256
    2.12 +
    2.13 +//PHI = ((sqrt(5)-1)/2)
    2.14 +#define PHI 0.618033989 
    2.15  
    2.16  typedef struct {
    2.17    float x,y;
    2.18 @@ -42,10 +50,20 @@
    2.19    int id;
    2.20  } Finger;
    2.21  
    2.22 +
    2.23 +typedef struct {
    2.24 +  float length;
    2.25 +  
    2.26 +  int numPoints;
    2.27 +  Point p[MAXPATHSIZE];
    2.28 +} DollarPath;
    2.29 +
    2.30 +
    2.31  typedef struct {
    2.32    Finger f;
    2.33    Point cv;
    2.34    float dtheta,dDist;
    2.35 +  DollarPath dollarPath;
    2.36  } TouchPoint;
    2.37  
    2.38  
    2.39 @@ -55,10 +73,160 @@
    2.40    Point centroid;
    2.41    TouchPoint gestureLast[MAXFINGERS];
    2.42    int numDownFingers;
    2.43 +
    2.44 +  int numDollarTemplates;
    2.45 +  Point dollarTemplate[MAXTEMPLATES][DOLLARNPOINTS];
    2.46  } GestureTouch;
    2.47  
    2.48  GestureTouch gestureTouch[MAXTOUCHES];
    2.49  int numGestureTouches = 0;
    2.50 +
    2.51 +float dollarDifference(Point* points,Point* templ,float ang) {
    2.52 +  //  Point p[DOLLARNPOINTS];
    2.53 +  float dist = 0;
    2.54 +  Point p;
    2.55 +  int i;
    2.56 +  for(i = 0; i < DOLLARNPOINTS; i++) {
    2.57 +    p.x = points[i].x * cos(ang) - points[i].y * sin(ang);
    2.58 +    p.y = points[i].x * sin(ang) + points[i].y * cos(ang);
    2.59 +    dist += sqrt((p.x-templ[i].x)*(p.x-templ[i].x)+
    2.60 +		 (p.y-templ[i].y)*(p.y-templ[i].y));
    2.61 +  }
    2.62 +  return dist/DOLLARNPOINTS;
    2.63 +  
    2.64 +}
    2.65 +
    2.66 +float bestDollarDifference(Point* points,Point* templ) {
    2.67 +  //------------BEGIN DOLLAR BLACKBOX----------------//
    2.68 +  //-TRANSLATED DIRECTLY FROM PSUDEO-CODE AVAILABLE AT-//
    2.69 +  //-"http://depts.washington.edu/aimgroup/proj/dollar/"-//
    2.70 +  float ta = -M_PI/4;
    2.71 +  float tb = M_PI/4;
    2.72 +  float dt = M_PI/90;
    2.73 +  float x1 = PHI*ta + (1-PHI)*tb;
    2.74 +  float f1 = dollarDifference(points,templ,x1);
    2.75 +  float x2 = (1-PHI)*ta + PHI*tb;
    2.76 +  float f2 = dollarDifference(points,templ,x2);
    2.77 +  while(abs(ta-tb) > dt) {
    2.78 +    if(f1 < f2) {
    2.79 +      tb = x2;
    2.80 +      x2 = x1;
    2.81 +      f2 = f1;
    2.82 +      x1 = PHI*ta + (1-PHI)*tb;
    2.83 +      f1 = dollarDifference(points,templ,x1);
    2.84 +    }
    2.85 +    else {
    2.86 +      ta = x1;
    2.87 +      x1 = x2;
    2.88 +      f1 = f2;
    2.89 +      x2 = (1-PHI)*ta + PHI*tb;
    2.90 +      f2 = dollarDifference(points,templ,x2);
    2.91 +    }
    2.92 +  }
    2.93 +  /*
    2.94 +  if(f1 <= f2)
    2.95 +    printf("Min angle (x1): %f\n",x1);
    2.96 +  else if(f1 >  f2)
    2.97 +    printf("Min angle (x2): %f\n",x2);
    2.98 +  */
    2.99 +  return SDL_min(f1,f2);  
   2.100 +}
   2.101 +
   2.102 +float dollarRecognize(DollarPath path,int *bestTempl,GestureTouch* touch) {
   2.103 +
   2.104 +  Point points[DOLLARNPOINTS];
   2.105 +  int numPoints = dollarNormalize(path,points);
   2.106 +  int i;
   2.107 + 
   2.108 +  int bestDiff = 10000;
   2.109 +  *bestTempl = -1;
   2.110 +  for(i = 0;i < touch->numDollarTemplates;i++) {
   2.111 +    int diff = bestDollarDifference(points,touch->dollarTemplate[i]);
   2.112 +    if(diff < bestDiff) {bestDiff = diff; *bestTempl = i;}
   2.113 +  }
   2.114 +  return bestDiff;
   2.115 +}
   2.116 +
   2.117 +//DollarPath contains raw points, plus (possibly) the calculated length
   2.118 +int dollarNormalize(DollarPath path,Point *points) {
   2.119 +  int i;
   2.120 +  //Calculate length if it hasn't already been done
   2.121 +  if(path.length <= 0) {
   2.122 +    for(i=1;i<path.numPoints;i++) {
   2.123 +      float dx = path.p[i  ].x - 
   2.124 +	         path.p[i-1].x;
   2.125 +      float dy = path.p[i  ].y - 
   2.126 +	         path.p[i-1].y;
   2.127 +      path.length += sqrt(dx*dx+dy*dy);
   2.128 +    }
   2.129 +  }
   2.130 +
   2.131 +
   2.132 +  //Resample
   2.133 +  float interval = path.length/(DOLLARNPOINTS - 1);
   2.134 +  float dist = 0;
   2.135 +
   2.136 +  int numPoints = 0;
   2.137 +  Point centroid; centroid.x = 0;centroid.y = 0;
   2.138 +  //printf("(%f,%f)\n",path.p[path.numPoints-1].x,path.p[path.numPoints-1].y);
   2.139 +  for(i = 1;i < path.numPoints;i++) {
   2.140 +    float d = sqrt((path.p[i-1].x-path.p[i].x)*(path.p[i-1].x-path.p[i].x)+
   2.141 +		   (path.p[i-1].y-path.p[i].y)*(path.p[i-1].y-path.p[i].y));
   2.142 +    //printf("d = %f dist = %f/%f\n",d,dist,interval);
   2.143 +    while(dist + d > interval) {
   2.144 +      points[numPoints].x = path.p[i-1].x + 
   2.145 +	((interval-dist)/d)*(path.p[i].x-path.p[i-1].x);
   2.146 +      points[numPoints].y = path.p[i-1].y + 
   2.147 +	((interval-dist)/d)*(path.p[i].y-path.p[i-1].y);
   2.148 +      centroid.x += points[numPoints].x;
   2.149 +      centroid.y += points[numPoints].y;
   2.150 +      numPoints++;
   2.151 +
   2.152 +      dist -= interval;
   2.153 +    }
   2.154 +    dist += d;
   2.155 +  }
   2.156 +  if(numPoints < 1) return 0;
   2.157 +  centroid.x /= numPoints;
   2.158 +  centroid.y /= numPoints;
   2.159 + 
   2.160 +  //printf("Centroid (%f,%f)",centroid.x,centroid.y);
   2.161 +  //Rotate Points so point 0 is left of centroid and solve for the bounding box
   2.162 +  float xmin,xmax,ymin,ymax;
   2.163 +  xmin = centroid.x;
   2.164 +  xmax = centroid.x;
   2.165 +  ymin = centroid.y;
   2.166 +  ymax = centroid.y;
   2.167 +  
   2.168 +  float ang = atan2(centroid.y - points[0].y,
   2.169 +		    centroid.x - points[0].x);
   2.170 +
   2.171 +  for(i = 0;i<numPoints;i++) {					       
   2.172 +    float px = points[i].x;
   2.173 +    float py = points[i].y;
   2.174 +    points[i].x = (px - centroid.x)*cos(ang) - 
   2.175 +                  (py - centroid.y)*sin(ang) + centroid.x;
   2.176 +    points[i].y = (px - centroid.x)*sin(ang) + 
   2.177 +                  (py - centroid.y)*cos(ang) + centroid.y;
   2.178 +
   2.179 +
   2.180 +    if(points[i].x < xmin) xmin = points[i].x;
   2.181 +    if(points[i].x > xmax) xmax = points[i].x; 
   2.182 +    if(points[i].y < ymin) ymin = points[i].y;
   2.183 +    if(points[i].y > ymax) ymax = points[i].y;
   2.184 +  }
   2.185 +
   2.186 +  //Scale points to DOLLARSIZE, and translate to the origin
   2.187 +  float w = xmax-xmin;
   2.188 +  float h = ymax-ymin;
   2.189 +
   2.190 +  for(i=0;i<numPoints;i++) {
   2.191 +    points[i].x = (points[i].x - centroid.x)*DOLLARSIZE/w;
   2.192 +    points[i].y = (points[i].y - centroid.y)*DOLLARSIZE/h;
   2.193 +  }  
   2.194 +  return numPoints;
   2.195 +}
   2.196 +
   2.197  int SDL_GestureAddTouch(SDL_Touch* touch) { 
   2.198    if(numGestureTouches >= MAXTOUCHES) return -1;
   2.199    
   2.200 @@ -69,6 +237,8 @@
   2.201    gestureTouch[numGestureTouches].res.x = touch->xres;
   2.202    gestureTouch[numGestureTouches].id = touch->id;
   2.203  
   2.204 +  gestureTouch[numGestureTouches].numDollarTemplates = 0;
   2.205 +
   2.206    numGestureTouches++;
   2.207    return 0;
   2.208  }
   2.209 @@ -93,6 +263,20 @@
   2.210    return SDL_PushEvent(&event) > 0;
   2.211  }
   2.212  
   2.213 +int SDL_SendGestureDollar(GestureTouch* touch,int gestureId,float error) {
   2.214 +  SDL_Event event;
   2.215 +  event.dgesture.type = SDL_DOLLARGESTURE;
   2.216 +  event.dgesture.touchId = touch->id;
   2.217 +  /*
   2.218 +    //TODO: Add this to give location of gesture?
   2.219 +  event.mgesture.x = touch->centroid.x;
   2.220 +  event.mgesture.y = touch->centroid.y;
   2.221 +  */
   2.222 +  event.dgesture.gestureId = gestureId;
   2.223 +  event.dgesture.error = error;  
   2.224 +  return SDL_PushEvent(&event) > 0;
   2.225 +}
   2.226 +
   2.227  void SDL_GestureProcessEvent(SDL_Event* event)
   2.228  {
   2.229    if(event->type == SDL_FINGERMOTION || 
   2.230 @@ -100,7 +284,6 @@
   2.231       event->type == SDL_FINGERUP) {
   2.232      GestureTouch* inTouch = SDL_GetGestureTouch(event->tfinger.touchId);
   2.233  
   2.234 -
   2.235      //Shouldn't be possible
   2.236      if(inTouch == NULL) return;
   2.237      
   2.238 @@ -114,12 +297,35 @@
   2.239  
   2.240        if(event->type == SDL_FINGERUP) {
   2.241  	inTouch->numDownFingers--;
   2.242 +
   2.243 +	int bestTempl;
   2.244 +	float error;
   2.245 +	error = dollarRecognize(inTouch->gestureLast[j].dollarPath,
   2.246 +				&bestTempl,inTouch);
   2.247 +	if(bestTempl >= 0){
   2.248 +	  //Send Event
   2.249 +	  int gestureId = 0; //?
   2.250 +	  SDL_SendGestureDollar(inTouch->id,gestureId,error);
   2.251 +
   2.252 +
   2.253 +	  printf("Dollar error: %f\n",error);
   2.254 +	}
   2.255 +
   2.256  	inTouch->gestureLast[j] = inTouch->gestureLast[inTouch->numDownFingers];
   2.257  	break;
   2.258        }
   2.259        else {
   2.260  	float dx = x - inTouch->gestureLast[j].f.p.x;
   2.261  	float dy = y - inTouch->gestureLast[j].f.p.y;
   2.262 +	DollarPath* path = &inTouch->gestureLast[j].dollarPath;
   2.263 +	if(path->numPoints < MAXPATHSIZE) {
   2.264 +	  path->p[path->numPoints].x = x;
   2.265 +	  path->p[path->numPoints].y = y;
   2.266 +	  path->length += sqrt(dx*dx + dy*dy);
   2.267 +	  path->numPoints++;
   2.268 +	}
   2.269 +
   2.270 +
   2.271  	inTouch->centroid.x += dx/inTouch->numDownFingers;
   2.272  	inTouch->centroid.y += dy/inTouch->numDownFingers;    
   2.273  	if(inTouch->numDownFingers > 1) {
   2.274 @@ -181,6 +387,11 @@
   2.275        inTouch->gestureLast[j].f.p.y  = y;	
   2.276        inTouch->gestureLast[j].cv.x = 0;
   2.277        inTouch->gestureLast[j].cv.y = 0;
   2.278 +
   2.279 +      inTouch->gestureLast[j].dollarPath.length = 0;
   2.280 +      inTouch->gestureLast[j].dollarPath.p[0].x = x;
   2.281 +      inTouch->gestureLast[j].dollarPath.p[0].y = y;
   2.282 +      inTouch->gestureLast[j].dollarPath.numPoints = 1;
   2.283      }
   2.284    }
   2.285  }  
     3.1 Binary file touchTest/touchPong has changed
     4.1 Binary file touchTest/touchSimp has changed