Я работаю над приложением для iPhone с множеством различных жестов, которые вы можете сделать. В настоящее время есть выбор / перетаскивание одним пальцем, прокрутка двумя пальцами и увеличение / уменьшение масштаба двумя пальцами. Я хочу добавить вращение двумя пальцами (ваши пальцы вращают точку между ними), но я не могу понять, как заставить его работать правильно. Все остальные жесты были линейными, так что они сводились только к использованию точечного или перекрестного произведения.
Я думаю, мне нужно сохранить наклон между двумя предыдущими точками каждого пальца, и если угол между векторами близок к 90, то есть вероятность вращения. Если угол перемещения следующего пальца также близок к 90, а направление вектора на одном пальце изменилось положительно и изменилось отрицательно, то вы получили вращение. Проблема в том, что мне нужно очень четкое различие между этим жестом и остальными, а вышесказанное недостаточно далеко.
Есть предложения?
РЕДАКТИРОВАТЬ: Вот как я это сделал в векторном анализе (в отличие от предложения ниже о соответствующих пикселях, обратите внимание, что я использую мою векторную структуру здесь, вы сможете угадать, что делает каждая функция):
//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");
}
Вы должны заметить, что если вы просто делаете манипуляции с изображениями или выполняете прямое вращение / масштабирование, тогда вышеприведенный подход вполне подойдет. Однако, если вы похожи на меня и используете жест, чтобы вызвать что-то, что требует времени для загрузки, вероятно, вы захотите избегать выполнения действия, пока этот жест не будет активирован несколько раз подряд. Разница между каждым из них в моем коде все еще не является совершенно отдельной, поэтому иногда в ряде масштабов вы получаете вращение или наоборот.