Sony's Changes to GNUstep GUI Library: Adding Touch
Posted by postfuturist on 2010-11-27 01:21:27

When Sony announced and then promptly put the SNAP (Sony's Networked Application Platform) on hold, they left the SDK's available for download. I'm a big fan of open development platforms and I was keen to give this one a whirl, especially seeing that it is built on open technologies like the Linux kernel and GNUstep platform. I managed to download the latest version of SNAP (1.2.2) before they removed the download links. I'll be disappointed if the SNAP Developer Program gets killed for unknown internal reasons at Sony, but it won't be a complete loss, as Sony did distribute (albeit for a short time) modifications and additions to the GNUstep GUI library which is under LGPL.

Sony built on top of version 0.17.1 of the GNUstep GUI library. I found the original sources of the library and created a github repo with the original 0.17.1 version of the library as the first commit, with Sony's changes applied as the second commit, so you can easily see what they changed. That is available here: https://github.com/deliciousrobots/gnustep-gui-sony .

What did Sony add? Mostly they added touch interface related code. The files they added are prefixed with "SN" so they are easy to find:


$ find -name "SN*"
./Headers/AppKit/SNCheckmarkGestureRecognizer.h
./Headers/AppKit/SNRotationGestureRecognizer.h
./Headers/AppKit/SNLongPressGestureRecognizer.h
./Headers/AppKit/SNGestureRecognizer.h
./Headers/AppKit/SNSwipeGestureRecognizer.h
./Headers/AppKit/SNTouch.h
./Headers/AppKit/SNTapGestureRecognizer.h
./Headers/AppKit/SNPanGestureRecognizer.h
./Headers/AppKit/SNPinchGestureRecognizer.h
./Headers/AppKit/SNLassoGestureRecognizer.h
./Source/SNLassoGestureRecognizer.m
./Source/SNPinchGestureRecognizer.m
./Source/SNCheckmarkGestureRecognizer.m
./Source/SNPanGestureRecognizer.m
./Source/SNLongPressGestureRecognizer.m
./Source/SNGestureRecognizer.m
./Source/SNTapGestureRecognizer.m
./Source/SNRotationGestureRecognizer.m
./Source/SNTouch.m
./Source/SNSwipeGestureRecognizer.m

As you can see, mostly they added touch and multi-touch gesture recognition. I looked at the code, it is a bit rough in places, definitely a work-in-progress, but interesting and now an LGPL'ed library of gesture recognition code in Objective-C. I was impressed with how nice some of the comments on the code were. Take a look at this bit from the SNLassoGestureRecognizer class:

/* 
 * Returns whether or not a given point is inside the lasso.
 * This is a "Point in Polygon" problem, and we solve it by the
 * "winding number algorithm".
 *
 * We first translate all vertices of lasso by -p to make it the
 * new origin. Then, we traverse all verticies to see how many times
 * the polygon winds around the point, using the "Axis Crossing 
 * Method". If the winding number is 0, then the point is not inside
 * the polygon. Otherwise, the point is.
 *
 * Note: This algorithm works for convex/concave/complex polygons.
 * Detailed algorithm can be found here:
 * http://www.engr.colostate.edu/~dga/dga/papers/point_in_polygon.pdf
 *
 *            _____        __
 *      |    /   __\      |  \
 *      |   /   /   point |  | 
 *      |  |    \  *     /   | 
 *      |  |     \______/  __|
 *      |   \             /
 *      |    \__         /
 *      |       \______/
 *   ----------------------- 
 *      |
 *
 *
 *   ---> translate polygon by -p
 * 
 *
 *                 |
 *        II       |       I
 *            _____|       __
 *           /   __|      |  \
 *          /   /  |point |  | 
 *    -----|----\--*-----/---|--- 
 *         |     \_|____/  __|
 *          \      |      /
 *      III  \__   |     /  IV
 *              \__|___/
 *                 |
 *                 |
 * 
 *
 */
- (BOOL) pointInLasso: (NSPoint) point inView: (NSView *) view;
{
   NSPoint pointInWindow;

   // convert to window-based coordinate first, if necessary
   if (view)
      pointInWindow = [view convertPoint: point toView: nil];
   else
      pointInWindow = point;

   if (pointInWindow.x > maxX || pointInWindow.x < minX ||
       pointInWindow.y > maxY || pointInWindow.y < minY)
   {
      // point is not even in enclosing rectangle
      return NO;
   }

   /***
   NSEnumerator *enumerator = [vertices objectEnumerator];
   NSValue *value;
   NSPoint vertex;
   NSPoint translatedVertex;
   BOOL quadrant_I = NO, quadrant_II = NO, quadrant_III = NO, quadrant_IV = NO;
   
   while ((value = [enumerator nextObject]))
   {
      vertex = [value pointValue];
      printf("vertex = (%f, %f)\n", vertex.x, vertex.y);
      translatedVertex = NSMakePoint(vertex.x - pointInWindow.x, vertex.y - pointInWindow.y);

      if (translatedVertex.x > 0 && translatedVertex.y > 0)
         quadrant_I = YES;
      else if (translatedVertex.x < 0 && translatedVertex.y > 0)
         quadrant_II = YES;
      else if (translatedVertex.x < 0 && translatedVertex.y < 0)
         quadrant_III = YES;
      else if (translatedVertex.x > 0 && translatedVertex.y < 0)
         quadrant_IV = YES;

      if (quadrant_I && quadrant_II && quadrant_III && quadrant_IV)
         return YES;
   }
   ***/

   NSEnumerator *enumerator = [vertices objectEnumerator];
   NSValue *value1;
   NSValue *value2;
   NSPoint vertex1, vertex2;
   NSPoint initialVertex, translatedVertex1, translatedVertex2;

   value1 = [enumerator nextObject];
   value2 = [enumerator nextObject];
   vertex1 = [value1 pointValue];
   translatedVertex1 = NSMakePoint(vertex1.x - pointInWindow.x, vertex1.y - pointInWindow.y);
   initialVertex = translatedVertex1;

   winding = 0;
   do
   {
      vertex2 = [value2 pointValue];
      translatedVertex2 = NSMakePoint(vertex2.x - pointInWindow.x, vertex2.y - pointInWindow.y);

      winding += [self calculateWindingNumberFrom: translatedVertex1
                                               to: translatedVertex2];

      translatedVertex1 = translatedVertex2;
   }
   while ((value2 = [enumerator nextObject]));

   // last vertex to first vertex
   winding += [self calculateWindingNumberFrom: translatedVertex2
                                            to: initialVertex];

   if (winding == 0) 
      return NO;
   else 
      return YES;
}

- (float) calculateWindingNumberFrom: (NSPoint) v1 to: (NSPoint) v2
{
   float intersect;
   float x1, y1, x2, y2;
   x1 = v1.x;
   y1 = v1.y;
   x2 = v2.x;
   y2 = v2.y;

   if (y1 == 0 && y2 == 0)
   {
      // both v1 v2 are on x-axis, winding number unchanged
      return 0;
   }
   else if (y1 * y2 < 0) // line v1 -> v2 crosses x-axis
   {
      intersect = x1 + (y1*(x2-x1))/(y1-y2); // x-coordinate of intersection of line v1 -> v2 and x-axis
      if (intersect > 0) // line v1 -> v2 crosses positive x-axis
      {
         if (y1 < 0)  //counter-clockwise
            return 1;
         else         //clockwise
            return -1;
      }
   }
   else if (y1 == 0 && x1 > 0) // v1 on positive x-axis
   {
      if (y2 > 0) 
         return 0.5;
      else 
         return -0.5;
   }
   else if (y2 == 0 && x2 > 0) // v2 on positive x-axis
   {
      if (y1 < 0) 
         return 0.5;
      else 
         return -0.5;
   }

   return 0;
}

You've got to love the ASCII-art diagrams. None of this code is revolutionary and most of it is pretty boring except for a couple interesting algorithms like the one above. Sony added a good deal of code and changed some things around in the existing GUI module files, too, to incorporate all the multi-touch code into the rest of the system. I hope this code is of some use to the GNUstep team. Anyone implementing a multi-touch interface could also look at this code for a little inspiration.

For me, it's mostly just interesting. I was intrigued by the idea of another open platform to build interesting apps for. Here's hoping Sony moves forward with it.


blog comments powered by Disqus