2

我正在开发一个 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");
}

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

4

2 回答 2

3

我之前通过查找两个手指之间的先前和当前距离以及先前和当前线之间的角度来做到这一点。然后我为距离增量和角度θ选择了一些经验阈值,这对我来说效果很好。

如果距离大于我的阈值,并且角度小于我的阈值,我会缩放图像。否则我旋转它。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]];
于 2009-08-20T03:31:08.930 回答
2

两个手指,都在移动,方向相反。什么手势与此冲突?

我猜捏/缩放接近了,但是捏/缩放将开始远离中心点(如果您从每条线向后追踪,您的线将平行且闭合),旋转最初将具有平行线(向后追踪)这将彼此远离,并且这些线将不断改变坡度(同时保持距离)。

编辑:你知道——这两个都可以用相同的算法来解决。

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

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

两指滚动或缩放有时可能看起来有点不稳定,因为它也会执行其他操作,但这就是地图应用程序的工作方式(不包括它没有的旋转)。

于 2009-08-19T23:44:17.480 回答