Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I am trying to calculate the direction of a swipe on a touch device screen. Lets say the screen size is 1024 wide and 768 high. The bottom left x and y coordinate is 0,0 and the top right coordinate is 1024,768.

For any given swipe, I will receive a delta vector which indicates the movement on the x and y axis. For instance, if I start a touch at 100,100 and the end the touch at 500,150 I will receive a delta vector of 400,50 and I know from this that the swipe was to the right (and a tiny bit up).

From any given delta vector, is there a formula I can use to determine which direction the vector most resembles, allowing for diagonal swiping too (i.e. left, right, up, down, up-left, up-right, down-left, down-right). I do have some code which calculates the closest with an allowed margin for error - but it occurred to me there must be a mathematical way to solve this without having to check each direction and the margin.

Hope that makes sense, Thank you.

P.s. Already tried to post this in the math overflow site, but got shot down as apparently it's not a math problem. Which actually it is. Hope this is a better choice!

EDIT:

Bornander: The solution you gave (which is very clever) provides the detection as shown in this image...

Solution by bornander

Where what I'm realistically after is a slightly turned version like this...

enter image description here

share|improve this question
    
So what you want is basically a formula that takes a vector and returns a direction (like an enum up/down/left/right) ? –  Raxvan Apr 8 at 10:41
    
Basically yes. Sorry if I made it sound over-complicated - just started doing this stuff in the context of games! –  Simon Apr 8 at 12:48

2 Answers 2

up vote 2 down vote accepted

You can take the unsigned angle of the vector;

angle = angle < 0 ? (2 * PI + angle) : angle; 

and if you then scale that so that it ranges from zero to one;

fraction = angle / (2 * PI)

then if you multiply that fraction with the number of available "directions" and round it to an int you'll get the direction (provided you enumerate the directions in a uniform counter clockwise direction).

In C# it might look something like this;

using System;

namespace Angles {

    enum LimitedDirection {
        Right, Up, Left, Down
    }

    enum Direction {
        Right, RightUp, Up, LeftUp, Left, LeftDown, Down, DownRight
    }

    class Vector
    {
        public double X { get; private set; }
        public double Y { get; private set; }

        public Vector(double x, double y)
        {
            X = x;
            Y = y;
        }

        public override string ToString()
        {
            return String.Format("({0:00.###}, {1:00.###})", X, Y);
        }

        public double Angle
        {
            get {
                var angle = Math.Atan2(Y, X);
                return angle < 0 ? 2 * Math.PI + angle : angle;
            }
        }

        public static explicit operator LimitedDirection(Vector v)
        {
            var fraction = v.Angle/(2*Math.PI);
            return (LimitedDirection)(fraction*4); // Four directions
        }

        public static explicit operator Direction(Vector v) {
            var fraction = v.Angle / (2 * Math.PI);
            return (Direction)(fraction * 8); // Eight directions
        }
    }

    class Program {
        static void Main(string[] args)
        {
            var vectors = new[] {
                    new Vector(1, 0), new Vector(1, 1), new Vector(0, 1), new Vector(-1, 1),
                    new Vector(-1, 0), new Vector(-1, -1), new Vector(0, -1), new Vector(1, -1)
                };

            foreach (var vector in vectors)
            {
                Console.WriteLine("{0}@{1:0.####} {2} {3}", vector, vector.Angle, (LimitedDirection)vector, (Direction)vector);
            }
        }
    }
}

Hope that helps.

share|improve this answer
1  
Really like this solution but the hit detection is odd. I've updated my post to demonstrate how it works according to the code you gave just now. How can I modify this to get the desired result? –  Simon Apr 8 at 13:50
1  
Just subtract 22.5° from the input angle (more generally, 180°/N where N is the number of sectors) –  Dan Hulme Apr 8 at 14:07

Here is an approach to get the direction regardless of the how many directions you have and how many space dimensions:

unsigned int getDirection(vector v, vector[] direction_vectors, float angle_threshhold )
{
    for(unsigned int i = 0;i < direction_vectors.size();i++)
    {
        if(acos(dot(v,direction_vectors[i])) < angle_threshhold )
            return i;
    }
    return direction_vectors.size();
}
//usage:
//directions: up/right/down/left
vector my_directions[]= {vector(0,1),vector(1,0),vector(0,-1),vector(-1,0)};
unsigned int dir = getDirection(my_vector,my_directions,45.0);
if(dir < my_directions.size())
{
    //have a direction equivalent to the definition of my_directions
}
else
{
    //undefined direction
}

Requirement: v and my_directions must be normalized, angle_threshhold and acos() must be in the same space(radians or degrees)

Also for 8 directions you must specify all 8 directions vector and the angle_threshhold must be 22.5

share|improve this answer
    
Going to give this one a go too, but will this match my (updated) diagram for direction input? –  Simon Apr 8 at 14:03
    
@Simon yes it totally works, the good thing about this is that you can define whatever vector in my_directions. The code basically just calculates the angle difference between v and directions so if direction vector is (0,1) and your vector is normalize(0,0.95) it will still fall into the same direction since the angle is below the angle threshold –  Raxvan Apr 8 at 16:08

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.