Finding Signed Angle Between Vectors

Finding Signed Angle Between Vectors

If you have an atan2() function in your math library of choice:

signed_angle = atan2(b.y,b.x) - atan2(a.y,a.x)

How to calculate the signed angle between 2 vectors with a given axis normal in 3D?

I don't think the problem has a solution unless both A and B are the same length and A and B both make the same angle (in the usual sense of shortest angle between vectors) with the axis. I will assume that these are given.

In that case, one solution would be to compute the orthogonal projection of both A and B into a plane that is orthogonal to the axis. This could be done by subtracting the component that is in the direction of the axis. So if I have a unit vector in the direction of the axis and call it X, the computation would be something like

Aproj = A - dot(A, X)X
Bproj = B - dot(B, X)X

Then the angle between Aproj and Bproj (in the usual sense of shortest angle) is the angle of rotation around the axis that you are asking about.

I'm not sure if this is the simplest way to compute it, but it should work pretty generally.

Signed angle between two vectors without a reference plane

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.

Getting a signed angle value

Dot product will only give you the cosine of the angle formed by the two vectors. It is positive of they are facing the same direction, negative for opposite directions and 0 if they are perpendicular.

Simply check the "RightDirection" to get the sign that corresponds to the original dot product.

By doing this, you will exclude the zeros given by the cardinal directions, so substitute the opposite axis when zero.

Here is the Blueprint pseudocode:

   Vector TargetDirection = Normalize(Player.GetActorLocation - Object.GetActorLocation);
float LR = dot(TargetDirection, Player.GetActorRotation.GetRightVector);
float SignLR = Sign(LR); // returns -1,0,1
float Angle = dot(TargetDirection,Player.GetActorRotation.GetForwardVector);

// catch the dot product zeros at(0,90,180,270...)
// if is a float comparison object with the bool output passed to a branch

if(Angle == 0) Angle = LR; // pick a direction (90 degrees left or right)
if(SignLR == 0) SignLR = Angle; //pick a direction

float Signed Angle = SignLR * Angle;

Note: The variables are not needed, when implementing this code, the output pin is wired directly to the multiple inputs.

signed angle between 2D vectors

subtract-headings 30 330

will give you what you asked for by saying "clockwise is positive (against the example you gave).

Using atan2 to find angle between two vectors

 atan2(vector1.y - vector2.y, vector1.x - vector2.x)

is the angle between the difference vector (connecting vector2 and vector1) and the x-axis,
which is problably not what you meant.

The (directed) angle from vector1 to vector2 can be computed as

angle = atan2(vector2.y, vector2.x) - atan2(vector1.y, vector1.x);

and you may want to normalize it to the range [0, 2 π):

if (angle < 0) { angle += 2 * M_PI; }

or to the range (-π, π]:

if (angle > M_PI)        { angle -= 2 * M_PI; }
else if (angle <= -M_PI) { angle += 2 * M_PI; }

How to calculate signed angles between 3 points

The signed angle between two 2D-vectors u and v can be obtained with the help of the atan2 function:

angle_unsigned <- function(u, v){
acos( sum(u*v) / ( sqrt(sum(u*u)) * sqrt(sum(v*v)) ) )
}

angle_signed <- function(u, v){
atan2(v[2], v[1]) - atan2(u[2], u[1])
}


EDIT

In 3D, you need to have a direction, represented by a vector n. The angle3D_signed function below returns the angle in [0,2pi[ by which the vector u must rotate counterclockwise, as seen from the direction defined by n, to reach the vector v.

crossProduct <- function(v, w){ 
c(
v[2] * w[3] - v[3] * w[2],
v[3] * w[1] - v[1] * w[3],
v[1] * w[2] - v[2] * w[1]
)
}

angle3D_signed <- function(n, u, v){
n <- n / sqrt(sum(n*n)) # normalize n
unorm <- sqrt(sum(u*u))
vnorm <- sqrt(sum(v*v))
s <- sum(crossProduct(n, u) * v) # "unnormalized sine"
cosAngle <- sum(u*v) / unorm / vnorm
ifelse(s >= 0, acos(cosAngle), 2*pi - acos(cosAngle))
}

Direct way of computing clockwise angle between 2 vectors

2D case

Just like the dot product is proportional to the cosine of the angle, the determinant is proprortional to its sine. So you can compute the angle like this:

dot = x1*x2 + y1*y2      # dot product between [x1, y1] and [x2, y2]
det = x1*y2 - y1*x2 # determinant
angle = atan2(det, dot) # atan2(y, x) or atan2(sin, cos)

The orientation of this angle matches that of the coordinate system. In a left-handed coordinate system, i.e. x pointing right and y down as is common for computer graphics, this will mean you get a positive sign for clockwise angles. If the orientation of the coordinate system is mathematical with y up, you get counter-clockwise angles as is the convention in mathematics. Changing the order of the inputs will change the sign, so if you are unhappy with the signs just swap the inputs.

3D case

In 3D, two arbitrarily placed vectors define their own axis of rotation, perpendicular to both. That axis of rotation does not come with a fixed orientation, which means that you cannot uniquely fix the direction of the angle of rotation either. One common convention is to let angles be always positive, and to orient the axis in such a way that it fits a positive angle. In this case, the dot product of the normalized vectors is enough to compute angles.

dot = x1*x2 + y1*y2 + z1*z2    #between [x1, y1, z1] and [x2, y2, z2]
lenSq1 = x1*x1 + y1*y1 + z1*z1
lenSq2 = x2*x2 + y2*y2 + z2*z2
angle = acos(dot/sqrt(lenSq1 * lenSq2))

Edit: Note that some comments and alternate answers advise against the use of acos for numeric reasons, in particular if the angles to be measured are small.

Plane embedded in 3D

One special case is the case where your vectors are not placed arbitrarily, but lie within a plane with a known normal vector n. Then the axis of rotation will be in direction n as well, and the orientation of n will fix an orientation for that axis. In this case, you can adapt the 2D computation above, including n into the determinant to make its size 3×3.

dot = x1*x2 + y1*y2 + z1*z2
det = x1*y2*zn + x2*yn*z1 + xn*y1*z2 - z1*y2*xn - z2*yn*x1 - zn*y1*x2
angle = atan2(det, dot)

One condition for this to work is that the normal vector n has unit length. If not, you'll have to normalize it.

As triple product

This determinant could also be expressed as the triple product, as @Excrubulent pointed out in a suggested edit.

det = n · (v1 × v2)

This might be easier to implement in some APIs, and gives a different perspective on what's going on here: The cross product is proportional to the sine of the angle, and will lie perpendicular to the plane, hence be a multiple of n. The dot product will therefore basically measure the length of that vector, but with the correct sign attached to it.

How do you find the singed angles between two vectors in three.js?

I'll suggest you to use a reference plane, personally i didn't need the sign of the angle
returned from .angleTo() method so i suggest you to read this two posts:

Signed angle between two 3D vectors with same origin within the same plane

How to calculate the angle between 2 vectors in a plane



Related Topics



Leave a reply



Submit