Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to "snap" a directional (2D) vector to a compass (N, NE, E, SE, S, SW, W, NW)?

I have a bunch of vectors normal to window surfaces in a 3D modelling software. Projected to the XY-Plane, I would like to know in which direction they are facing, translated to the 8 compass coordinates (North, North-East, East, South-East, South, South-West, West and North-West).

The vectors work like this:

  • the X axis represents East-West (with East being positive)
  • the y axis represents North-South (with North being positive)
  • thus
    • (0, 1) == North
    • (1, 0) == East
    • (0,-1) == South
    • (-1,0) == West

Given a vector (x, y) I am looking for the closest of the 8 compass coordinates. Any ideas on how to do this elegantly?

like image 333
Daren Thomas Avatar asked Sep 17 '09 09:09

Daren Thomas


3 Answers

This works in Java, computing a value 0...7 for the eight directions:

import static java.lang.Math.*;    

int compass = (((int) round(atan2(y, x) / (2 * PI / 8))) + 8) % 8;

The result maps to the compass as follows:

0 => E
1 => NE
2 => N
3 => NW
4 => W
5 => SW
6 => S
7 => SE
like image 76
starblue Avatar answered Oct 26 '22 14:10

starblue


I would probably just do a call to atan2() to figure out the heading angle ("yaw"), and then use either a sequence of if:s or some maths to "snap" it to a multiple of 90 degrees.

like image 39
unwind Avatar answered Oct 26 '22 15:10

unwind


no need to do an atan function.

if you do: y/x you'll get the slope of the line. Judging by the number you get you can determine the angle/octant.

for positive x's (x>0)

  • (y/x) > 2.4 -=> 90 degrees (north)
  • 2.4 > (y/x) > 0.4 -=> 45 degrees (northwest)
  • 0.4 > (y/x) > -0.4 -=> 0 degrees (west)
  • -0.4 > (y/x) > -2.4 -=> -45 degrees (southwest)
  • -2.4 > (y/x) -=> 90 degrees (south)

and a similar list for the negative x's

and finally the exception cases:

  • (x==0 && y>0) -=> -90 degrees (south)
  • (x==0 && y<0) -=> 90 degrees (south)

addendum: I only report this method for when calculating an atan is a no go (for instance on an embedded system))

I had to dig a bit. Here's a highly optimized routine I use (used in mobile games).

input: x1,y1 = startpoint of vector x2,y2 = endpoint of vector output (0-7) = 0=north, 1=northwest, 2=west,...etc

 int CalcDir( int x1, int y1, int x2, int y2 )
 {
      int dx = x2 - x1, dy = y2 - y1;
      int adx = (dx<0)?-dx:dx, ady = (dy<0)?-dy:dy, r;
      r=(dy>0?4:0)+(dx>0?2:0)+(adx>ady?1:0);
      r=(int []){2,3,1,0,5,4,6,7}[r];
      return r;
 }

 void CalcDirTest(){
      int t = CalcDir(0, 0, 10, 1);
      printf("t = %d",t);
      t = CalcDir(0, 0, 9, 10);
      printf("t = %d",t);
      t = CalcDir(0, 0, -1, 10);
      printf("t = %d",t);
      t = CalcDir(0, 0, -10, 9);
      printf("t = %d",t);
      t = CalcDir(0, 0, -10, -1);
      printf("t = %d",t);
      t = CalcDir(0, 0, -9, -10);
      printf("t = %d",t);
      t = CalcDir(0, 0, 1, -10);
      printf("t = %d",t);
      t = CalcDir(0, 0, 10, -9);
      printf("t = %d",t);
 }

This will result in the following output:

 t = 7
 t = 6
 t = 5
 t = 4
 t = 3
 t = 2
 t = 1
 t = 0

(The vectors for the test might look oddly chosen, but I tweaked them all a bit to be clearly in one octant and not on the exact border)

like image 24
Toad Avatar answered Oct 26 '22 14:10

Toad