views:

258

answers:

9

Hey,

Im trying to find out the angle (in degrees) between two 2D vectors. I know I need to use trig but I'm not too good with it. This is what I'm trying to work out (the Y axis increases downward): alt text

I'm trying to use this code at the moment, but it's not working at all (calculates random angles for some reason):

private float calcAngle(float x, float y, float x1, float y1)
{
    float _angle = (float)Math.toDegrees(Math.atan2(Math.abs(x1-x), Math.abs(y1-y)));
    Log.d("Angle","Angle: "+_angle+" x: "+x+" y: "+y+" x1: "+x1+" y1: "+y1);
    return _angle;
}

These are my results (There constant when providing a constant position, but when I change the position, the angle changes and I can't find any link between the two angles):

Position 1: x:100 y:100 x1:50 y1:50 Angle: 45

Position 2: x:92 y:85 x1:24 y1:16 Angle: 44.58

Position 3: x:44 y: 16 x1:106 y1:132 Angle: 28.12

Edit: Thanks everyone who answered and helped me figure out that was wrong! Sorry the title and the question was confusing.

A: 

It should be :

atan( abs(x1 - x)/abs(y1 - y) ) 

abs stands for absolute (to avoid negative values)

Squ36
Don't forget the case y1=y.
Peter G.
If you do it that way you only get an angle between -pi/2 and pi/2. The OP is asking about `atan2`.
Troubadour
A: 

Are you using integers? Cast the arguments as doubles, and I would use fabs on the result, not the arguments. The result will be in radians; to get degrees, use:

res *= (360.0/(2.0*Math.PI));

Jess
Whoever downvoted this should realise that it was posted _before_ the OP posted code that confirms the use of floating point precision.
Troubadour
Thanks Troubadour :-)
Jess
A: 

My first guess would be to calculate the angle of each vector with the axes using atan(y/x) and then subtract those angels and take the absolute value, that is:

abs(atan(y/x) - atan(y1/x1))

smichak
+6  A: 

You first have to understand how to compute angle between two vectors and there are several of them. I will give you what I think is the simplest.

  1. Given v1 and v2, their dot product is: v1x * v2x + v1y * v2y
  2. The norm of a vector v is given by: sqtr(vx^2+vy^2)

With this information, please take this definition:

dot(v1, v2) = norm(v1) * norm(v2) * cos(angle(v1, v2))

Now, you solve for angle(v1, v2):

angle(v1, v2) = acos( dot(v1, v2) / (norm(v1) * norm(v2)) )

Finally, taking the definitions given at the beginning, then you end up with:

angle(v1, v2) = acos( (v1x * v2x + v1y * v2y) / (sqrt(v1x^2+v1y^2) * sqrt(v2x^2+v2y^2)) )

Again, there are many ways to do this, but I like this one because it is helpful for dot product given angle and norm, or angle, given vectors.

The answer will be in radians, but you know that pi radians (that is 3.14 radians) are 180 degrees, so you simply multiply by the conversion factor 180/pi.

Arrieta
Thanks for explaining it! I'm trying out your algorithm, but when the angle is obtuse I have to mirror it and when it's not, I have to add 45 degrees. Is there any reason for this? I mean, I'm fine doing a simple if else statement, but I'd like to know why im doing it :)
Niall
Don't worry, just figured out i had to use atan2 and mirror the angle. Thanks for your answer and for explaining it though! :)
Niall
Yeah. If you think about the dot product of two vectors, you will realize why the arctangent resolves the quadrant issue. In other words, it picks the correct sign for the angle. Good luck.
Arrieta
A: 

The angle of the second vector relative to the first = atan2(y2,x2) - atan2(y1,x1).

http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm

Mark Cidade
+1  A: 

I believe the equation for the angle between two vectors should look more like:

toDegrees(acos((x*x1+y*y1)/(sqrt(x*x+y*y)*sqrt(x1*x1+y1*y1))))

Your above equation will calculate the angle made between the vector p1-p2 and the line made by extending an orthogonal from the point p2 to the vector p1.

The dot product of two vectors V1 and V2 is equal to |V1|*|V2|cos(theta). Therefore, theta is equal to acos((V1 dot V2)/(|V1||V2|)). V1 dot V2 is V1.x*V2.x+V1.y*V2.y. The magnitude of V (i.e., |V|) is the pathogorean theorem... sqrt(V.x^2 + V.y^2)

aepryus
+4  A: 

Do not take the absolute value of the arguments to atan2. The whole point of atan2 is that it uses the signs of its arguments to work out which qaudrant the angle is in. By taking the absolute values you are forcing atan2 to only return values between 0 and pi/2 instead of -pi to pi.

Troubadour
Thanks for explaining why I shouldn't take the absolute values when using atan2 :) Turns out in the end I just had to mirror the angle (E.g. 180+(-toDegrees(atan2(x1-x,y1-y))) )
Niall
@Niall: Glad to help. :) See my comment to your answer. Also, since you are actually trying to work out the angle between the negative y-axis and a single vector perhaps you should edit the title of the question as it's very misleading at the moment. Something like "How to calculate the angle of a vector from the vertical?" and point out in the question that y increases downwards in your system.
Troubadour
Sure, I'll just edit it now. :)
Niall
A: 

Aha! Turns out I just needed to flip my angle and use atan2. This is my final code:

private float calcAngle(float x, float y, float x1, float y1)
{
    float _angle = (float)Math.toDegrees(Math.atan2(x1-x, y-y1));
    return _angle;
}

Thanks everyone for helping me figure this out and also for helping me to understand what I'm actually doing! :)

Niall
So your y co-ord increases down the way then and you want North to be straight up. Instead of subtracting from 180 you can just flip the signs of y i.e. your second argument to `atan2` is therefore `y-y1`.
Troubadour
oohhh, thanks!!
Niall
warning: a division by zero will result when (y1 - y) == 0.
Leftium
@wonsungi: No it won't. `atan2` takes care of that and returns either +pi/2 or -pi/2 depending on the signs of the two arguments (including the argument that is zero since that can still have a sign). At least it does in the C version. I assume other languages would do something similar.
Troubadour
@Troubadour: You're right! Thanks~ That guard must be from before I knew about `atan2`; I was using asin and doing the division manually...
Leftium
+1  A: 

It looks like Niall figured it out, but I'll finish my explanation, anyways. In addition to explaining why the solution works, my solution has two advantages:

  • Potential division by zero within atan2() is avoided
  • Return value is always positive in the range 0 to 360 degrees

atan2() returns the counter-clockwise angle relative to the positive X axis. Niall was looking for the clockwise angle relative to the positive Y axis (between the vector formed by the two points and the positve Y axis).

The following function is adapted from my asteroids game where I wanted to calculate the direction a ship/velocity vector was "pointing:"

// Calculate angle between vector from (x1,y1) to (x2,y2) & +Y axis in degrees.
// Essentially gives a compass reading, where N is 0 degrees and E is 90 degrees.

double bearing(double x1, double y1, double x2, double y2)
{
    // x and y args to atan2() swapped to rotate resulting angle 90 degrees
    // (Thus angle in respect to +Y axis instead of +X axis)
    double angle = Math.toDegrees(atan2(x1 - x2, y2 - y1));

    // Ensure result is in interval [0, 360)
    // Subtract because positive degree angles go clockwise
    return (360 - angle) %  360;
}
Leftium
Niall
I changed the code to be more Java-like: `Math.toDegrees()` is effectively the same as multiplying by the previous DEG_PER_RAD constant. (Otherwise you can create a Java constant with `static final double`). Float has less precision than double, and will likely incur a performance penalty since conversions between float and double will be required. Unless saving memory is a big concern, I would always use double. Also Troubadour pointed out atan2 does not need to be guarded against division by zero.
Leftium