我正在开发一个 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(¤tDifference );
//Find the distance between the two previous points and the two current points.
float previousDistance = getMagnitudeOfVector(&previousDifference);
float currentDistance = getMagnitudeOfVector(¤tDifference );
//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");
}
您应该注意,如果您只是进行图像处理或直接旋转/缩放,那么上述方法应该没问题。但是,如果您像我一样使用手势来导致需要时间加载的内容,那么您可能希望避免执行该操作,直到该手势连续激活几次。每个与我的代码之间的区别仍然不是完全分开的,所以偶尔在一堆缩放中你会得到一个旋转,反之亦然。