2009-08-19 418 views
1

我正在研究一个iPhone应用程序,其中包含许多可以做的不同手势输入。目前有单指选择/拖动,双指滚动和两指捏放大/缩小。我想添加两个手指旋转(你的手指在它们之间旋转一个点),但我无法弄清楚如何让它正常工作。所有其他手势都是线性的,因此它们只是使用点或交叉产品的问题,非常多。iPhone上的双指旋转手势?

我在想我必须在每个手指的前两个点之间存储斜率,并且如果矢量之间的角度接近90°,那么存在旋转的可能性。如果下一个手指移动角度也接近90°,并且一个手指上的矢量方向改变为正向并且负向改变,那么您将进行旋转。问题是,我需要在这个手势和其他手势之间有一个非常干净的区别 - 上面的内容还不够深入。

有什么建议吗?编辑:这是我做矢量分析的方式(与下面关于匹配像素的建议相反,请注意我在这里使用我的Vector结构,你应该能够猜到每个函数的作用):

//First, find the vector formed by the first touch's previous and current positions. 
struct Vector2f firstChange = getSubtractedVector([theseTouches get:0], [lastTouches get:0]); 
//We're going to store whether or not we should scroll. 
BOOL scroll = NO; 

//If there was only one touch, then we'll scroll no matter what. 
if ([theseTouches count] <= 1) 
{ 
    scroll = YES; 
} 
//Otherwise, we might scroll, scale, or rotate. 
else 
{ 
    //In the case of multiple touches, we need to test the slope between the two touches. 
    //If they're going in roughly the same direction, we should scroll. If not, zoom. 
    struct Vector2f secondChange = getSubtractedVector([theseTouches get:1], [lastTouches get:1]); 

    //Get the dot product of the two change vectors. 
    float dotChanges = getDotProduct(&firstChange, &secondChange); 

    //Get the 2D cross product of the two normalized change vectors. 
    struct Vector2f normalFirst = getNormalizedVector(&firstChange); 
    struct Vector2f normalSecond = getNormalizedVector(&secondChange); 
    float crossChanges = getCrossProduct(&normalFirst, &normalSecond); 

    //If the two vectors have a cross product that is less than cosf(30), then we know the angle between them is 30 degrees or less. 
    if (fabsf(crossChanges) <= SCROLL_MAX_CROSS && dotChanges > 0) 
    { 
     scroll = YES; 
    } 
    //Otherwise, they're in different directions so we should zoom or rotate. 
    else 
    { 
     //Store the vectors represented by the two sets of touches. 
     struct Vector2f previousDifference = getSubtractedVector([lastTouches get:1], [lastTouches get:0]); 
     struct Vector2f currentDifference = getSubtractedVector([theseTouches get:1], [theseTouches get:0]); 

     //Also find the normals of the two vectors. 
     struct Vector2f previousNormal = getNormalizedVector(&previousDifference); 
     struct Vector2f currentNormal = getNormalizedVector(&currentDifference); 

     //Find the distance between the two previous points and the two current points. 
     float previousDistance = getMagnitudeOfVector(&previousDifference); 
     float currentDistance = getMagnitudeOfVector(&currentDifference); 

     //Find the angles between the two previous points and the two current points. 
     float angleBetween = atan2(previousNormal.y,previousNormal.x) - atan2(currentNormal.y,currentNormal.x); 

     //If we had a short change in distance and the angle between touches is a big one, rotate. 
     if (fabsf(previousDistance - currentDistance) <= ROTATE_MIN_DISTANCE && fabsf(angleBetween) >= ROTATE_MAX_ANGLE) 
     { 
      if (angleBetween > 0) 
      { 
       printf("Rotate right.\n"); 
      } 
      else 
      { 
       printf("Rotate left.\n"); 
      } 
     } 
     else 
     { 
      //Get the dot product of the differences of the two points and the two vectors. 
      struct Vector2f differenceChange = getSubtracted(&secondChange, &firstChange); 
      float dotDifference = getDot(&previousDifference, &differenceChange); 
      if (dotDifference > 0) 
      { 
       printf("Zoom in.\n"); 
      } 
      else 
      { 
       printf("Zoom out.\n"); 
      } 
     } 
    } 
} 

if (scroll) 
{ 
    prinf("Scroll.\n"); 
} 

你应该注意到,如果你只是在做图像处理或直接旋转/缩放,那么上述方法应该没问题。但是,如果您像我一样,并且您正在使用手势来导致需要花费时间加载的内容,那么您可能会希望避免执行此操作,直到连续激活了该手势几次。每个代码与我的代码之间的区别仍然不是完全独立的,所以偶尔会在一堆缩放中获得旋转,反之亦然。

回答

2

我之前通过查找两个手指之间的前一个和当前距离以及之前和当前线之间的角度来完成此操作。 然后,我选择了一些经验门槛的距离三角洲和角度θ,这对我来说工作得很好。

如果距离大于我的阈值,并且角度小于我的阈值,我缩放图像。否则我旋转它。 2手指滚动似乎很容易区分。

顺便说一句,如果你实际存储的值,触摸已经存储以前的点值。

CGPoint previousPoint1 = [self scalePoint:[touch1 previousLocationInView:nil]]; 
CGPoint previousPoint2 = [self scalePoint:[touch2 previousLocationInView:nil]]; 
CGPoint currentPoint1 = [self scalePoint:[touch1 locationInView:nil]]; 
CGPoint currentPoint2 = [self scalePoint:[touch2 locationInView:nil]]; 
+0

啊,距离是缺失的环节。我不敢相信我没有想到这一点。我只专注于矢量的角度和方向,但距离真的是我认为最大的区别。我已经知道以前的触摸(用于缩放),但感谢您指出。 – Eli 2009-08-20 15:07:23

2

两个手指,两个移动,相反(ish)方向。什么姿态与此相冲突?我认为捏合/缩放比较接近,但捏合/缩放将从中心点开始移动(如果从每条线向后追踪,线会平行并关闭),旋转最初将具有平行线(向后追踪),它们将彼此远离,并且这些线将不断地改变斜率(同时保持距离)。

编辑:你知道 - 这些都可以用相同的算法解决。

不是计算直线,而是计算每个手指下的像素。如果手指移动,平移图像,使两个初始像素仍然在两根手指下。

这解决了包括滚动在内的所有双指动作。

双指滚动或缩放有时会看起来有点晃动,因为它也会执行其他操作,但这是地图应用似乎工作的方式(不包括它没有的旋转)。

+0

我喜欢你的像素想法,这将很好的图像处理。不过,我正在制作一款游戏,而旋转手势会将关卡旋转到不同的视角,这需要一些资源加载等,因此不能直接区分。尽管如此,图像案例中的好主意+1。 – Eli 2009-08-27 00:27:27