Signed angle between two vectors without a reference plane

15,950

Solution 1

Thanks all. After reviewing the comments here and looking back at what I was trying to do, I realized that I can accomplish what I need to do with the given, standard formula for a signed angle. I just got hung up in the unit test for my signed angle function.

For reference, I'm feeding the resulting angle back into a rotate function. I had failed to account for the fact that this will naturally use the same axis as in signed_angle (the cross product of input vectors), and the correct direction of rotation will follow from which ever direction that axis is facing.

More simply put, both of these should just "do the right thing" and rotate in different directions:

rotate(cross(Va, Vb), signed_angle(Va, Vb), point)
rotate(cross(Vb, Va), signed_angle(Vb, Va), point)

Where the first argument is the axis of rotation and second is the amount to rotate.

Solution 2

The relevant mathematical formulas:

  dot_product(a,b) == length(a) * length(b) * cos(angle)
  length(cross_product(a,b)) == length(a) * length(b) * sin(angle)

For a robust angle between 3-D vectors, your actual computation should be:

  s = length(cross_product(a,b))
  c = dot_product(a,b)
  angle = atan2(s, c)

If you use acos(c) alone, you will get severe precision problems for cases when the angle is small. Computing s and using atan2() gives you a robust result for all possible cases.

Since s is always nonnegative, the resulting angle will range from 0 to pi. There will always be an equivalent negative angle (angle - 2*pi), but there is no geometric reason to prefer it.

Solution 3

Signed angle between two vectors without a reference plane

angle = acos(dotproduct(normalized(a), normalized(b)));

signed_angle(a, b) == -signed_angle(b, a)

I think that's impossible without some kind of reference vector.

Share:
15,950
metatheorem
Author by

metatheorem

Updated on June 16, 2022

Comments

  • metatheorem
    metatheorem almost 2 years

    (In three dimensions) I'm looking for a way to compute the signed angle between two vectors, given no information other than those vectors. As answered in this question, it is simple enough to compute the signed angle given the normal of a plane to which the vectors are perpendicular. But I can find no way to do this without that value. It's obvious that the cross product of two vectors produces such a normal, but I've run into the following contradiction using the answer above:

    signed_angle(x_dir, y_dir) == 90
    signed_angle(y_dir, x_dir) == 90
    

    where I would expect the second result to be negative. This is due to the fact that the cross product cross(x_dir, y_dir) is in the opposite direction of cross(y_dir, x_dir), given the following psuedocode with normalized input:

    signed_angle(Va, Vb)
        magnitude = acos(dot(Va, Vb))
        axis = cross(Va, Vb)
        dir = dot(Vb, cross(axis, Va))
        if dir < 0 then
            magnitude = -magnitude
        endif
        return magnitude
    

    I don't believe dir will ever be negative above.

    I've seen the same problem with the suggested atan2 solution.

    I'm looking for a way to make:

    signed_angle(a, b) == -signed_angle(b, a)
    
  • Jeff
    Jeff about 12 years
    I was thinking about it some more, and this is right. The sign of the angle depends on which axis of rotation you use as your reference. And of course there are two different axes, one in the direction of the cross product and one in the opposite direction.
  • SigTerm
    SigTerm about 12 years
    @JeffE: Yes, and while you can find "rotational" axis by using cross product, you can't determine in which direction it was initially facing - in a cross b or in b cross a - swap vectors, and you'll get axis facing in opposite direction. As a result it is impossible to determine if rotational angle is within 0..pi range or if it is within pi..pi*2 range.
  • metatheorem
    metatheorem about 12 years
    Thanks, I'll keep that about acos. I guess it's obvious when you visualize the function.
  • Ahmed Fasih
    Ahmed Fasih about 8 years
    Warning: this function is commutative, and it shouldn't be: angle from the +x direction (1 0 0) to the +y direction (0 1 0) should be +90°. Vice versa, from +y to +x, should be -90°. But with this function, f(x, y) == f(y, x). It can't tell the difference since s is non-negative, and that cross-product is the only thing that could have told you about the direction between the two.
  • comingstorm
    comingstorm about 8 years
    If it were in 2 dimensions, it would be anti-commutative. However, in 3 dimensions, it is (and should be) commutative -- because, in 3 dimensions, you need a third vector to determine chirality. Without a third vector to discriminate between positive and negative angles for the first two, you have no geometric reason to prefer one direction over the other.
  • Ahmed Fasih
    Ahmed Fasih about 8 years
    Thanks for the quick reply. I think I do have a reason to prefer the direction from one vector to the other: in bistatic radar imaging, specifically calculating the bistatic angle, it matters whether the transmitter or receiver are 15 degrees ahead of or behind the other, since the material responds differently. Also, one could in principle rewrite the two 3D vectors as 2D vectors in the plane containing them both, then apply the signed 2D angle right?
  • comingstorm
    comingstorm about 8 years
    The title of the question says "... without a reference plane". A reference plane is as good as an extra vector, in the sense that it can give you a geometric basis for preferring one direction over the other.
  • comingstorm
    comingstorm about 8 years
    ... at least, that is true assuming your plane is signed independently of the two vectors, so that you can use the plane normal to discriminate directions. The two vectors do indeed determine a plane through the origin -- but it is not a reference plane: swapping the vectors reverses the orientation of that plane; it still can't give you a geometric basis for preferring one direction over the other.
  • Aaron Franke
    Aaron Franke over 4 years
    Can you share your implementation of signed_angle?