views:

401

answers:

4

I want to calculate the angle between two vectors a and b. Lets assume these are at the origin. This can be done with

theta = arccos(a . b / |a| * |b|)

However arccos gives you the angle in [0, pi], i.e. it will never give you an angle greater than 180 degrees, which is what I want. So how do you find out when the vectors have gone past the 180 degree mark? In 2D I would simply let the sign of the y-component on one of the vectors determine what quadrant the vector is in. But what is the easiest way to do it in 3D?

EDIT: I wanted to keep the question general but here we go. I'm programming this in c and the code I use to get the angle is theta = acos(dot(a, b)/mag(a)*mag(b)) so how would you programmatically determine the orientation?

+1  A: 

This works in 2D because you have a plane defined in which you define the rotation.

If you want to do this in 3D, there is no such implicit 2D plane. You could transform your 3D coordinates to a 2D plane going through all three points, and do your calculation inside this plane.

But, there are of course two possible orientations for the plane, and that will affect which angles will be > 180 or smaller.

jdv
I've actually gone that route but haven't got it fully working yet. I was hoping for an easier solution. What I do is I multiply my vectors a and b with the inverse of C, where C=[a; a x (a x b); a x b] to get u and v. I expect u and v to have a zero z-components, but they have not. So maybe my C matrix is defined incorrect?
Reimund
A: 

Strictly speaking, two 3D vectors always have two angles between them - one below or equal to 180, the other over or equal to 180. Arccos gives you one of them, you can get the other by subtracting from 360. Think of it that way: imagine two lines intersect. You have 4 angles there - 2 of one value, 2 of another. What's the angle between the lines? No single answer. Same here. Without some kind of extra criteria, you can not, in theory, tell which of the two angle values should be taken into account.

EDIT: So what you really need is an arbitrary example of fixing an orientation. Here's one: we look from the positive Z direction. If the plane between the two vectors contains the Z axis, we look from the positive Y direction. If the plane is YZ, we look from the positive X direction. I'll think how to express this in coordinate form, then edit again.

Seva Alekseyev
The "extra criteria" is what my question is about!
Reimund
You tell me :) In 3D space there are two ways to define orientation. The wording of your question implies that you know it when you see it; but you never cared to formalize. Think of it: two vectors A and B in 3D define a plane. If you look at the plane from one side, the angle from A to B is X. If you look at the plane from the other side, the angle is (360-x). Which way of looking at the plane is right?
Seva Alekseyev
Yes, that's the tricky part I'm struggeling with. I need a signed value so that I can differentiate from the two.
Reimund
A: 

One solution that you could use:
What you effectively need to do is create a plane that one of the vectors is coplanar to.

Getting the cross product of both vectors will create a plane, then is you get the normal of this plane, you can get the angle between this and the vector you need to get the signed angle for, and you can use the angle to determine the sign.
If the angle is greater than 90 degrees, then it is below the created plane; less than 90 degrees, and it is above.
Depending on cost of calculations, the dot product can be used at this stage instead of the angle.

Just make sure that you always calculate the normals by the same order of vectors.

This is useable more easily if you're using the XYZ axes, and that's what you're comparing against, since you already have the vectors needed for the plane.

There are possbly more efficient solutions, but this is one I came up with.

Edit: clarification of created vectors a X b = p. This is perpendicular to both a and b. Then, do either: a X p or b X p to create another vector that is the normal to the plane created by the 2 vectors. Choice of vector depends on which you're trying to find the angle for.

Farrell
Are you sure this works? I tried that before and angle I got was always 90 degrees (which is by the definition of the cross product).Can you elaborate on how to solve it with the use the dot product?
Reimund
hopeful clarification made
Farrell
It's still a bit unclear. I want the signed angle between a and b, and if I understand you correctly I should check if the angle between a and (a X p) is less than or greater than 90?
Reimund
no, you'd do `a` and `(b X p)` in that case, since `a` is by definition perpendicular to `a X p`
Farrell
It seems the angle between a and (b X p) is always less than 90 :/
Reimund
oh crap, you're right - when `a` is on the opposite side, it'll cause the direction of the generated `p` to flip, and therefore the resulting normal. I guess more info is needed about the nature of `a` and `b` to see if a constant plane could generated...
Farrell
A: 

I came up with the following solution that takes advantage of the direction change of the cross product of the two vectors:

  1. Make a vector n = a X b and normalize it. This vector is normal to the plane spanned by a and b.

  2. Whenever a new angle is calculated compare it with the old normal. In the comparison, treat the old and the current normals as points and compute the distance between them. If this distance is 2 the normal (i.e. the cross product a X b has flipped).

You might want to have a threshold for the distance as the distance after a flip might be shorter than 2, depending on how the vectors a and b are oriented and how often you update the angle.

Reimund