Determining direction of 2D vector with 8 discrete directions (horizontal, vertical and diagonals)

StackOverflow https://stackoverflow.com/questions/8202866

Question

I have the input of a gamepad thumbstick that's a vector. The range of values of the vector defines a circle within a rectangle that spans -1, -1 to +1, +1.

I want to know which of the 8 possible directions the vector is closes to. Don't worry about the case where the vector is (0, 0), I'll deal with this separately. How can I do this?

Note: This is for a game, so this function will be ran very frequently. Anything to speed up the function would help, for example I know I could use the dot product and trig to work out the angle between my vector and each direction vector, however I wonder if something better could be done given I don't need the exact angle.

Was it helpful?

Solution

Imagine a square inside the -1..1 square, where the stick position (X) is on one of the sides:

+-----------------+  +-----------------+
|                 |  |                 |
|                 |  |                 |
|          X      |  |    +-----X-+    |
|                 |  |    |       |    |
|        O        |  |    |   O   |    |
|                 |  |    |       |    |
|                 |  |    +-------+    |
|                 |  |                 |
|                 |  |                 |
+-----------------+  +-----------------+

You just have to find out which side the coordinates are on, and check whether it's closer to the center of the side, or a corner. If the inner square is too small, you can consider the stick to be centered.

Something like:

Public enum Direction {
  None,
  LeftUp, Up, RightUp, Right, RightDown, Down, LeftDown, Left
}

public Direction GetDirection(double x, double y) {
  double absX = Math.Abs(x);
  double absY = Math.Abs(y);
  if (absX < 0.1 && absY < 0.1) {
    // close to center
    return Direction.None;
  }
  if (absX > absY) {
    // vertical side
    double half = absX * 0.4142;
    if (x > 0) {
      // left side
      if (y > half) return Direction.LeftDown;
      if (y < -half) return Diretion.LeftUp;
      return Direction.Left;
    } else {
      // right side
      if (y > half) return Direction.RightDown;
      if (y < -half) return Direction.RightUp;
      return Direction.Right;
    }
  } else {
    // horisontal side
    double half = absY * 0.4142;
    if (y > 0) {
      // bottom
      if (x > half) return Direction.RightDown;
      if (x < -half) return Direction.LeftDown;
      return Direction.Down;
    } else {
      // top
      if (x > half) return Direction.RightUp;
      if (x < -half) return Direction.LeftUp;
      return Direction.Up;
    }
  }
}

No trigonometry, just simple comparisons, so it should be pretty fast. :)

(Although I used trigonometry to calculate the point 0.4142, which is the tan(22.5), or the position on the side where the angle is 45/2.)

OTHER TIPS

sin a / cos a = tan a,

and you have

Y = sin a

X = cos a

So, apply the reverse tangent function to Y/X and you'll get the angle.

EDIT: In a full circle, there are two angles with the same tangent value (a and a + pi). Use the sign of X and Y to determine which one is the valid one.

You could use a pre-built approximation table of tan^-1. Should be very fast to calculate an angle based on the size of your input vector, and you probably won't need a very large resolution in the table (in fact, if you only want 8 discrete directions, 8 entries in your table will suffice).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top