src/events/SDL_gesture.c
changeset 4658 454385d76845
parent 4657 eed063a0bf5b
child 4659 063b9455bd1a
     1.1 --- a/src/events/SDL_gesture.c	Wed Jul 07 04:13:08 2010 -0700
     1.2 +++ b/src/events/SDL_gesture.c	Fri Jul 09 00:50:40 2010 -0700
     1.3 @@ -30,6 +30,14 @@
     1.4  //TODO: Replace with malloc
     1.5  #define MAXFINGERS 3
     1.6  #define MAXTOUCHES 2
     1.7 +#define MAXTEMPLATES 4
     1.8 +#define MAXPATHSIZE 1024
     1.9 +
    1.10 +#define DOLLARNPOINTS 64
    1.11 +#define DOLLARSIZE 256
    1.12 +
    1.13 +//PHI = ((sqrt(5)-1)/2)
    1.14 +#define PHI 0.618033989 
    1.15  
    1.16  typedef struct {
    1.17    float x,y;
    1.18 @@ -42,10 +50,20 @@
    1.19    int id;
    1.20  } Finger;
    1.21  
    1.22 +
    1.23 +typedef struct {
    1.24 +  float length;
    1.25 +  
    1.26 +  int numPoints;
    1.27 +  Point p[MAXPATHSIZE];
    1.28 +} DollarPath;
    1.29 +
    1.30 +
    1.31  typedef struct {
    1.32    Finger f;
    1.33    Point cv;
    1.34    float dtheta,dDist;
    1.35 +  DollarPath dollarPath;
    1.36  } TouchPoint;
    1.37  
    1.38  
    1.39 @@ -55,10 +73,160 @@
    1.40    Point centroid;
    1.41    TouchPoint gestureLast[MAXFINGERS];
    1.42    int numDownFingers;
    1.43 +
    1.44 +  int numDollarTemplates;
    1.45 +  Point dollarTemplate[MAXTEMPLATES][DOLLARNPOINTS];
    1.46  } GestureTouch;
    1.47  
    1.48  GestureTouch gestureTouch[MAXTOUCHES];
    1.49  int numGestureTouches = 0;
    1.50 +
    1.51 +float dollarDifference(Point* points,Point* templ,float ang) {
    1.52 +  //  Point p[DOLLARNPOINTS];
    1.53 +  float dist = 0;
    1.54 +  Point p;
    1.55 +  int i;
    1.56 +  for(i = 0; i < DOLLARNPOINTS; i++) {
    1.57 +    p.x = points[i].x * cos(ang) - points[i].y * sin(ang);
    1.58 +    p.y = points[i].x * sin(ang) + points[i].y * cos(ang);
    1.59 +    dist += sqrt((p.x-templ[i].x)*(p.x-templ[i].x)+
    1.60 +		 (p.y-templ[i].y)*(p.y-templ[i].y));
    1.61 +  }
    1.62 +  return dist/DOLLARNPOINTS;
    1.63 +  
    1.64 +}
    1.65 +
    1.66 +float bestDollarDifference(Point* points,Point* templ) {
    1.67 +  //------------BEGIN DOLLAR BLACKBOX----------------//
    1.68 +  //-TRANSLATED DIRECTLY FROM PSUDEO-CODE AVAILABLE AT-//
    1.69 +  //-"http://depts.washington.edu/aimgroup/proj/dollar/"-//
    1.70 +  float ta = -M_PI/4;
    1.71 +  float tb = M_PI/4;
    1.72 +  float dt = M_PI/90;
    1.73 +  float x1 = PHI*ta + (1-PHI)*tb;
    1.74 +  float f1 = dollarDifference(points,templ,x1);
    1.75 +  float x2 = (1-PHI)*ta + PHI*tb;
    1.76 +  float f2 = dollarDifference(points,templ,x2);
    1.77 +  while(abs(ta-tb) > dt) {
    1.78 +    if(f1 < f2) {
    1.79 +      tb = x2;
    1.80 +      x2 = x1;
    1.81 +      f2 = f1;
    1.82 +      x1 = PHI*ta + (1-PHI)*tb;
    1.83 +      f1 = dollarDifference(points,templ,x1);
    1.84 +    }
    1.85 +    else {
    1.86 +      ta = x1;
    1.87 +      x1 = x2;
    1.88 +      f1 = f2;
    1.89 +      x2 = (1-PHI)*ta + PHI*tb;
    1.90 +      f2 = dollarDifference(points,templ,x2);
    1.91 +    }
    1.92 +  }
    1.93 +  /*
    1.94 +  if(f1 <= f2)
    1.95 +    printf("Min angle (x1): %f\n",x1);
    1.96 +  else if(f1 >  f2)
    1.97 +    printf("Min angle (x2): %f\n",x2);
    1.98 +  */
    1.99 +  return SDL_min(f1,f2);  
   1.100 +}
   1.101 +
   1.102 +float dollarRecognize(DollarPath path,int *bestTempl,GestureTouch* touch) {
   1.103 +
   1.104 +  Point points[DOLLARNPOINTS];
   1.105 +  int numPoints = dollarNormalize(path,points);
   1.106 +  int i;
   1.107 + 
   1.108 +  int bestDiff = 10000;
   1.109 +  *bestTempl = -1;
   1.110 +  for(i = 0;i < touch->numDollarTemplates;i++) {
   1.111 +    int diff = bestDollarDifference(points,touch->dollarTemplate[i]);
   1.112 +    if(diff < bestDiff) {bestDiff = diff; *bestTempl = i;}
   1.113 +  }
   1.114 +  return bestDiff;
   1.115 +}
   1.116 +
   1.117 +//DollarPath contains raw points, plus (possibly) the calculated length
   1.118 +int dollarNormalize(DollarPath path,Point *points) {
   1.119 +  int i;
   1.120 +  //Calculate length if it hasn't already been done
   1.121 +  if(path.length <= 0) {
   1.122 +    for(i=1;i<path.numPoints;i++) {
   1.123 +      float dx = path.p[i  ].x - 
   1.124 +	         path.p[i-1].x;
   1.125 +      float dy = path.p[i  ].y - 
   1.126 +	         path.p[i-1].y;
   1.127 +      path.length += sqrt(dx*dx+dy*dy);
   1.128 +    }
   1.129 +  }
   1.130 +
   1.131 +
   1.132 +  //Resample
   1.133 +  float interval = path.length/(DOLLARNPOINTS - 1);
   1.134 +  float dist = 0;
   1.135 +
   1.136 +  int numPoints = 0;
   1.137 +  Point centroid; centroid.x = 0;centroid.y = 0;
   1.138 +  //printf("(%f,%f)\n",path.p[path.numPoints-1].x,path.p[path.numPoints-1].y);
   1.139 +  for(i = 1;i < path.numPoints;i++) {
   1.140 +    float d = sqrt((path.p[i-1].x-path.p[i].x)*(path.p[i-1].x-path.p[i].x)+
   1.141 +		   (path.p[i-1].y-path.p[i].y)*(path.p[i-1].y-path.p[i].y));
   1.142 +    //printf("d = %f dist = %f/%f\n",d,dist,interval);
   1.143 +    while(dist + d > interval) {
   1.144 +      points[numPoints].x = path.p[i-1].x + 
   1.145 +	((interval-dist)/d)*(path.p[i].x-path.p[i-1].x);
   1.146 +      points[numPoints].y = path.p[i-1].y + 
   1.147 +	((interval-dist)/d)*(path.p[i].y-path.p[i-1].y);
   1.148 +      centroid.x += points[numPoints].x;
   1.149 +      centroid.y += points[numPoints].y;
   1.150 +      numPoints++;
   1.151 +
   1.152 +      dist -= interval;
   1.153 +    }
   1.154 +    dist += d;
   1.155 +  }
   1.156 +  if(numPoints < 1) return 0;
   1.157 +  centroid.x /= numPoints;
   1.158 +  centroid.y /= numPoints;
   1.159 + 
   1.160 +  //printf("Centroid (%f,%f)",centroid.x,centroid.y);
   1.161 +  //Rotate Points so point 0 is left of centroid and solve for the bounding box
   1.162 +  float xmin,xmax,ymin,ymax;
   1.163 +  xmin = centroid.x;
   1.164 +  xmax = centroid.x;
   1.165 +  ymin = centroid.y;
   1.166 +  ymax = centroid.y;
   1.167 +  
   1.168 +  float ang = atan2(centroid.y - points[0].y,
   1.169 +		    centroid.x - points[0].x);
   1.170 +
   1.171 +  for(i = 0;i<numPoints;i++) {					       
   1.172 +    float px = points[i].x;
   1.173 +    float py = points[i].y;
   1.174 +    points[i].x = (px - centroid.x)*cos(ang) - 
   1.175 +                  (py - centroid.y)*sin(ang) + centroid.x;
   1.176 +    points[i].y = (px - centroid.x)*sin(ang) + 
   1.177 +                  (py - centroid.y)*cos(ang) + centroid.y;
   1.178 +
   1.179 +
   1.180 +    if(points[i].x < xmin) xmin = points[i].x;
   1.181 +    if(points[i].x > xmax) xmax = points[i].x; 
   1.182 +    if(points[i].y < ymin) ymin = points[i].y;
   1.183 +    if(points[i].y > ymax) ymax = points[i].y;
   1.184 +  }
   1.185 +
   1.186 +  //Scale points to DOLLARSIZE, and translate to the origin
   1.187 +  float w = xmax-xmin;
   1.188 +  float h = ymax-ymin;
   1.189 +
   1.190 +  for(i=0;i<numPoints;i++) {
   1.191 +    points[i].x = (points[i].x - centroid.x)*DOLLARSIZE/w;
   1.192 +    points[i].y = (points[i].y - centroid.y)*DOLLARSIZE/h;
   1.193 +  }  
   1.194 +  return numPoints;
   1.195 +}
   1.196 +
   1.197  int SDL_GestureAddTouch(SDL_Touch* touch) { 
   1.198    if(numGestureTouches >= MAXTOUCHES) return -1;
   1.199    
   1.200 @@ -69,6 +237,8 @@
   1.201    gestureTouch[numGestureTouches].res.x = touch->xres;
   1.202    gestureTouch[numGestureTouches].id = touch->id;
   1.203  
   1.204 +  gestureTouch[numGestureTouches].numDollarTemplates = 0;
   1.205 +
   1.206    numGestureTouches++;
   1.207    return 0;
   1.208  }
   1.209 @@ -93,6 +263,20 @@
   1.210    return SDL_PushEvent(&event) > 0;
   1.211  }
   1.212  
   1.213 +int SDL_SendGestureDollar(GestureTouch* touch,int gestureId,float error) {
   1.214 +  SDL_Event event;
   1.215 +  event.dgesture.type = SDL_DOLLARGESTURE;
   1.216 +  event.dgesture.touchId = touch->id;
   1.217 +  /*
   1.218 +    //TODO: Add this to give location of gesture?
   1.219 +  event.mgesture.x = touch->centroid.x;
   1.220 +  event.mgesture.y = touch->centroid.y;
   1.221 +  */
   1.222 +  event.dgesture.gestureId = gestureId;
   1.223 +  event.dgesture.error = error;  
   1.224 +  return SDL_PushEvent(&event) > 0;
   1.225 +}
   1.226 +
   1.227  void SDL_GestureProcessEvent(SDL_Event* event)
   1.228  {
   1.229    if(event->type == SDL_FINGERMOTION || 
   1.230 @@ -100,7 +284,6 @@
   1.231       event->type == SDL_FINGERUP) {
   1.232      GestureTouch* inTouch = SDL_GetGestureTouch(event->tfinger.touchId);
   1.233  
   1.234 -
   1.235      //Shouldn't be possible
   1.236      if(inTouch == NULL) return;
   1.237      
   1.238 @@ -114,12 +297,35 @@
   1.239  
   1.240        if(event->type == SDL_FINGERUP) {
   1.241  	inTouch->numDownFingers--;
   1.242 +
   1.243 +	int bestTempl;
   1.244 +	float error;
   1.245 +	error = dollarRecognize(inTouch->gestureLast[j].dollarPath,
   1.246 +				&bestTempl,inTouch);
   1.247 +	if(bestTempl >= 0){
   1.248 +	  //Send Event
   1.249 +	  int gestureId = 0; //?
   1.250 +	  SDL_SendGestureDollar(inTouch->id,gestureId,error);
   1.251 +
   1.252 +
   1.253 +	  printf("Dollar error: %f\n",error);
   1.254 +	}
   1.255 +
   1.256  	inTouch->gestureLast[j] = inTouch->gestureLast[inTouch->numDownFingers];
   1.257  	break;
   1.258        }
   1.259        else {
   1.260  	float dx = x - inTouch->gestureLast[j].f.p.x;
   1.261  	float dy = y - inTouch->gestureLast[j].f.p.y;
   1.262 +	DollarPath* path = &inTouch->gestureLast[j].dollarPath;
   1.263 +	if(path->numPoints < MAXPATHSIZE) {
   1.264 +	  path->p[path->numPoints].x = x;
   1.265 +	  path->p[path->numPoints].y = y;
   1.266 +	  path->length += sqrt(dx*dx + dy*dy);
   1.267 +	  path->numPoints++;
   1.268 +	}
   1.269 +
   1.270 +
   1.271  	inTouch->centroid.x += dx/inTouch->numDownFingers;
   1.272  	inTouch->centroid.y += dy/inTouch->numDownFingers;    
   1.273  	if(inTouch->numDownFingers > 1) {
   1.274 @@ -181,6 +387,11 @@
   1.275        inTouch->gestureLast[j].f.p.y  = y;	
   1.276        inTouch->gestureLast[j].cv.x = 0;
   1.277        inTouch->gestureLast[j].cv.y = 0;
   1.278 +
   1.279 +      inTouch->gestureLast[j].dollarPath.length = 0;
   1.280 +      inTouch->gestureLast[j].dollarPath.p[0].x = x;
   1.281 +      inTouch->gestureLast[j].dollarPath.p[0].y = y;
   1.282 +      inTouch->gestureLast[j].dollarPath.numPoints = 1;
   1.283      }
   1.284    }
   1.285  }