How to get the "anti-clockwise" angle between two 2D vectors? - math

I have two vectors and I want to get the angle between those vectors, I am currently doing it with this formula :
acos(dot(v1.unitVector, v2.unitVector))
Here is what I get with it :
I would want the green angle rather than the red angle, but I don't know what formula I should use...
Thank you.
EDIT : So, hen the vectors are still in a certain position (like the first two pairs of vectors, it's ok, but whenever it is in a configuration like in the third pair, it doesn't give me the right angle anymore)

With the dot product you get always an angle that is independent of the order of the vectors and the smaller of the two possibilities.
For what you want, you need the argument function of complex numbers that is realized by the atan2 function. The angle from a=ax+i*ay to b=bx+i*by is the argument of the conjugate of a times b (rotating b backwards by the angle of a, scale not considered), which in coordinates is
(ax-i*ay) * (bx+i*by) = ax*bx+ay*by + i*(ax*by-ay*bx)
so the angle is
atan2( ax*by-ay*bx, ax*bx+ay*by ).

Adding to the accepted answer, the problem with atan2 is that, if you imagine vector a being static and vector b rotating in anti-clockwise direction, you will see the return value ranging from 0 to π, but then it suddenly turns negative and proceeds from -π to 0, which is not exactly good if you're interested in an angle increasing from 0 to 2π.
To tackle that problem, the function below conveniently maps the result from atan2 and returns a value between 0 and 2π as one would expect:
const TAU = Math.PI * 2;
/**
* Gives the angle in radians from vector a to vector b in anticlockwise direction,
* ranging from 0 to 2π.
*
* #param {Vector} a
* #param {Vector} b
* #returns {Number}
*/
static angle(a, b) {
let angle = Math.atan2(a.x * b.y - a.y * b.x, a.x * b.x + a.y * b.y);
if (angle < 0) {
angle += TAU;
}
return angle;
}
It is written in JavaScript, but it's easily portable to other languages.

Lutz already answered this correctly but let me add that I highly recommend basing modern vector math code on Geometric Algebra which raises the abstraction level dramatically.
Using GA, you can simply multiply the two vectors U and V to get a rotor. The rotor internally looks like A + Bxy where A = U dot V = |U|V|cos(angle) and Bxy = U wedge V = |U||V|sin(angle)xy (this is isomorphic to the complex numbers). Then you can return the rotor's signed CCW angle which is atan2( B, A ).
So with operator overloading, you can just type (u * v).Angle. The final calculations end up the same, but the abstraction level you think and work in is much higher.

Maybe this one is more fit:
atan2( ax*by-ay*bx, ax*bx+ay*by ) % (PI*2)
the calculation which could get the full anti-clockwise radian.

Related

Turn rate on a player's rotation doing a 360 once it hits pi

Making a game using Golang since it seems to work quite well for games. I made the player face the mouse always, but wanted a turn rate to make certain characters turn slower than others. Here is how it calculates the turn circle:
func (p *player) handleTurn(win pixelgl.Window, dt float64) {
mouseRad := math.Atan2(p.pos.Y-win.MousePosition().Y, win.MousePosition().X-p.pos.X) // the angle the player needs to turn to face the mouse
if mouseRad > p.rotateRad-(p.turnSpeed*dt) {
p.rotateRad += p.turnSpeed * dt
} else if mouseRad < p.rotateRad+(p.turnSpeed*dt) {
p.rotateRad -= p.turnSpeed * dt
}
}
The mouseRad being the radians for the turn to face the mouse, and I'm just adding the turn rate [in this case, 2].
What's happening is when the mouse reaches the left side and crosses the center y axis, the radian angle goes from -pi to pi or vice-versa. This causes the player to do a full 360.
What is a proper way to fix this? I've tried making the angle an absolute value and it only made it occur at pi and 0 [left and right side of the square at the center y axis].
I have attached a gif of the problem to give better visualization.
Basic summarization:
Player slowly rotates to follow mouse, but when the angle reaches pi, it changes polarity which causes the player to do a 360 [counts all the back to the opposite polarity angle].
Edit:
dt is delta time, just for proper frame-decoupled changes in movement obviously
p.rotateRad starts at 0 and is a float64.
Github repo temporarily: here
You need this library to build it! [go get it]
Note beforehand: I downloaded your example repo and applied my change on it, and it worked flawlessly. Here's a recording of it:
(for reference, GIF recorded with byzanz)
An easy and simple solution would be to not compare the angles (mouseRad and the changed p.rotateRad), but rather calculate and "normalize" the difference so it's in the range of -Pi..Pi. And then you can decide which way to turn based on the sign of the difference (negative or positive).
"Normalizing" an angle can be achieved by adding / subtracting 2*Pi until it falls in the -Pi..Pi range. Adding / subtracting 2*Pi won't change the angle, as 2*Pi is exactly a full circle.
This is a simple normalizer function:
func normalize(x float64) float64 {
for ; x < -math.Pi; x += 2 * math.Pi {
}
for ; x > math.Pi; x -= 2 * math.Pi {
}
return x
}
And use it in your handleTurn() like this:
func (p *player) handleTurn(win pixelglWindow, dt float64) {
// the angle the player needs to turn to face the mouse:
mouseRad := math.Atan2(p.pos.Y-win.MousePosition().Y,
win.MousePosition().X-p.pos.X)
if normalize(mouseRad-p.rotateRad-(p.turnSpeed*dt)) > 0 {
p.rotateRad += p.turnSpeed * dt
} else if normalize(mouseRad-p.rotateRad+(p.turnSpeed*dt)) < 0 {
p.rotateRad -= p.turnSpeed * dt
}
}
You can play with it in this working Go Playground demo.
Note that if you store your angles normalized (being in the range -Pi..Pi), the loops in the normalize() function will have at most 1 iteration, so that's gonna be really fast. Obviously you don't want to store angles like 100*Pi + 0.1 as that is identical to 0.1. normalize() would produce correct result with both of these input angles, while the loops in case of the former would have 50 iterations, in the case of the latter would have 0 iterations.
Also note that normalize() could be optimized for "big" angles by using floating operations analogue to integer division and remainder, but if you stick to normalized or "small" angles, this version is actually faster.
Preface: this answer assumes some knowledge of linear algebra, trigonometry, and rotations/transformations.
Your problem stems from the usage of rotation angles. Due to the discontinuous nature of the inverse trigonometric functions, it is quite difficult (if not outright impossible) to eliminate "jumps" in the value of the functions for relatively close inputs. Specifically, when x < 0, atan2(+0, x) = +pi (where +0 is a positive number very close to zero), but atan2(-0, x) = -pi. This is exactly why you experience the difference of 2 * pi which causes your problem.
Because of this, it is often better to work directly with vectors, rotation matrices and/or quaternions. They use angles as arguments to trigonometric functions, which are continuous and eliminate any discontinuities. In our case, spherical linear interpolation (slerp) should do the trick.
Since your code measures the angle formed by the relative position of the mouse to the absolute rotation of the object, our goal boils down to rotating the object such that the local axis (1, 0) (= (cos rotateRad, sin rotateRad) in world space) points towards the mouse. In effect, we have to rotate the object such that (cos p.rotateRad, sin p.rotateRad) equals (win.MousePosition().Y - p.pos.Y, win.MousePosition().X - p.pos.X).normalized.
How does slerp come into play here? Considering the above statement, we simply have to slerp geometrically from (cos p.rotateRad, sin p.rotateRad) (represented by current) to (win.MousePosition().Y - p.pos.Y, win.MousePosition().X - p.pos.X).normalized (represented by target) by an appropriate parameter which will be determined by the rotation speed.
Now that we have laid out the groundwork, we can move on to actually calculating the new rotation. According to the slerp formula,
slerp(p0, p1; t) = p0 * sin(A * (1-t)) / sin A + p1 * sin (A * t) / sin A
Where A is the angle between unit vectors p0 and p1, or cos A = dot(p0, p1).
In our case, p0 == current and p1 == target. The only thing that remains is the calculation of the parameter t, which can also be considered as the fraction of the angle to slerp through. Since we know that we are going to rotate by an angle p.turnSpeed * dt at every time step, t = p.turnSpeed * dt / A. After substituting the value of t, our slerp formula becomes
p0 * sin(A - p.turnSpeed * dt) / sin A + p1 * sin (p.turnSpeed * dt) / sin A
To avoid having to calculate A using acos, we can use the compound angle formula for sin to simplify this further. Note that the result of the slerp operation is stored in result.
result = p0 * (cos(p.turnSpeed * dt) - sin(p.turnSpeed * dt) * cos A / sin A) + p1 * sin(p.turnSpeed * dt) / sin A
We now have everything we need to calculate result. As noted before, cos A = dot(p0, p1). Similarly, sin A = abs(cross(p0, p1)), where cross(a, b) = a.X * b.Y - a.Y * b.X.
Now comes the problem of actually finding the rotation from result. Note that result = (cos newRotation, sin newRotation). There are two possibilities:
Directly calculate rotateRad by p.rotateRad = atan2(result.Y, result.X), or
If you have access to the 2D rotation matrix, simply replace the rotation matrix with the matrix
|result.X -result.Y|
|result.Y result.X|

Cant find angle between 2 vectors

Could someone help me solve this problem? How can I find the angle shown in picture? I think I need to find angle between 2 vectors but im really bad at geometry.
http://i.stack.imgur.com/W0RKh.png
If you are developing your program in C++, then to calculating an angle between two vectors you can use the atan2 function, it is present in many programming languages.
You need to call atan2 giving it the two components of a single vector and then you make calculations this way:
Calculating for the first vector: atan2(v1_y, v1_x)
Calculating for the second vector: atan2(v2_y, v2_x)
Caution:
If the value returned by atan2 is negative (as atan2 returns value from range (-pi;+pi]), then you need to add 2 * pi to the result for each of the vector.
Finally you subtract the values of the vectors and what you get is the angle. The angle will be either positive or negative, depending which atan2 value you subtract from which one.
You need to normalize both vectors and then perform a dot product.
Step 1: Vector normalization:
A normalized vector has a length of 1. To achieve this, you divide its coordinates by its length:
float d = 1 / sqrt(X * X + Y * Y + Z * Z);
normalizedX = X * d;
normalizedY = Y * d;
normalizedZ = Z * d;
Note: The length is inversed and then multiplied instead of divided in order to increase performance.
Step 2: Dot product
After your normalized both vectors like in Step 1, you need to perform a dot product:
float angle = acos(x1 * x2 + y1 * y2 + z1 * z2);
The result is the cosine of the angle between the two vectors. After an acos you have your angle.

How to determine if a vector is between two other vectors?

I am looking for a fast and effective way to determine if vector B is Between the small angle of vector A and vector C. Normally I would use the perpendicular dot product to determine which sides of each line B lies on but in this case is not so simple because of the following:
None of the vectors can be assumed to be normalized and normalizing them is an extra step I would prefer to avoid.
I have no clear notion as to which side is the smallest angle so it is hard to say which side of the line is good or not.
It is possible for A and B to be co-linear or exactly 180 degrees apart in which case I want to return false.
While I am working in a 3D enviroment it is easy for me to simplify this to 2D if that makes things easier and more importantly faster. This test will be used in an algorithm that needs to run as fast as possible.
If there is some easy and efficient method to determine which direction my perpendicular vectors should both point I could use the two dot products for my test.
Another approach I have been considering without much success so far is using a matrix. In theory from what I understand of matrix transforms I should be able to use A and C as basis vectors. Then multiplying B by the matrix I should be able to test what quadrant B then lies in by whether X and Y are both positive. If I could get this approach to work it would likely be the best since one matrix multiplication should be faster than two dot products and I should not have to worry about which side has the smallest angle on it.
The problem is from my tests I cannot simply use A and C as bases and multiply it normally and get correct behavior. I am really not sure what i am doing wrong here. I have run across the term "Vector spaces" a few times which as near as I can figure seems to be a very similar concept to matrix transforms without any requirements for orthogonal bases or orthonormal bases. Is it the same thing as matrix? If not, might there be a better approach and how would I use that?
Just to give a more visual explanation of what I am talking about:
#Aki Suihkonen
I can't seem to get it working. Coded up a mock case I could run through and see if I can't figure somthing out
For this case using
Ax 2.9579773 Ay 3.315979
Cx 2.5879822 Cy 5.1630249
For B I rotated around the four quadrants the vectors divide the space up into.
The signs I got:
- For Q1 --
- For Q2 +-
- For Q3 +-
- For Q4 --
Assuming I rotated around in the enviroment the same direction as the image I am fairly sure I did.
I think Aki's solution is close, but there are cases where it doesn't work:
From his solution:
return (ay * bx - ax * by) * (ay * cx - ax * cy) < 0;
This is equivalent to checking whether the cross product between B and A has the same sign as the cross product between C and A.
The sign of the cross product (U x V) tells you whether V lies on one side of U or the other (out of the board, into the board). In most coordinate systems, if U needs to rotate counter-clockwise (out of the board), then the sign will be positive.
So Aki's solution checks to see if B needs to rotate in one direction to get to A, while C needs to rotate in the other direction. If this is the case, B is not within A and C. This solution doesn't work when you don't know the 'order' of A and C, as follows:
To know for certain whether B is within A and C you need to check both ways. That is, the rotation direction from A to B should be the same as from A to C, and the rotation direction from C to B should be the same as from C to A.
This reduces to:
if (AxB * AxC >= 0 && CxB * CxA >= 0)
// then B is definitely inside A and C
One method to think about this is to regard all these vectors A, B, C as complex numbers.
Multiplying A, C all with B*, which is the complex conjugate of B, both the resulting vectors will be rotated in complex plane so that the reference axis (B*Conj(B)) is now the real axis (or y = 0) -- and that axis doesn't need to be calculated. In this case one only has to check if the sign of 'y' or imaginary component differ. Also in this case both resulting vectors have been scaled by the same length |B|.
`return sign(Imag(A * Conj(B))) != sign(Imag(C * Conj(B)));`
A = ax + i * ay; B = bx + i * by; C = cx + i * cy;
Conj(B) = bx - i * by;
A * B = (ax * bx - ay * by) + i * (ax * by + ay * bx);
I think this equation leads to even better performance, as only the Imaginary component of the multiplication is needed.
As a full solution, this converts to:
return (ay * bx - ax * by) * (ay * cx - ax * cy) < 0;
The middle multiplication is a short cut for:
return Sign(ay * bx - ax * by) != Sign(ay * cx - ax * cy);
Without complex numbers, the problem can be also seen as vector B being
{ Rcos beta, Rsin beta }, which can be represented as a rotation matrix.
R*[ cb -sb ] [ bx -by ], cb = cos(beta), sb = sin(beta)
[ sb cb ] = [ by bx ] cos(-beta) = cos(beta), sin(-beta) = -sin(beta)
Multiplying [ax,ay], [cx,cy] with the transpose of the scaled matrix [bx by, -by bx] affects the lengths of [ax, ay] * rotMatrix(-beta), [cx, cy] * rotMatrix(-beta) in exactly the same way.
In polar coordinates, you would just be asking if θA < θB < θC. So transform to polar first:
a_theta = ax ? atan(ay / ax) : sign(ay) * pi

Vector deltas and moving in unknown areas

I was in need of a little math help that I can't seem to find the answer to, any links to documentation would be greatly appreciated.
Heres my situation, I have no idea where I am in this maze, but I need to move around and find my way back to the start. I was thinking of implementing a waypoint list of places i've been offset from my start at 0,0. This is a 2D cartesian plane.
I've been given 2 properties, my translation speed from 0-1 and my rotation speed from -1 to 1. -1 is very left and +1 is very right. These are speed and not angles so thats where my problem lies. If I'm given 0 as a translation speed and 0.2 I will continually turn to my right at a slow speed.
How do I figure out the offsets given these 2 variables? I can store it every time I take a 'step'.
I just need to figure out the offsets in x and y terms given the translations and rotation speeds. And the rotation to get to those points.
Any help is appreciated.
Your question is unclear on a couple of points, so I have to make some assumptions:
During each time interval, translation speed and rotational velocity are constant.
You know the values of these variables in every time interval (and you know rotational velocity in usable units, like radians per second, not just "very left").
You know initial heading.
You can maintain enough precision that roundoff error is not a problem.
Given that, there is an exact solution. First the easy part:
delta_angle = omega * delta_t
Where omega is the angular velocity. The distance traveled (maybe along a curve) is
dist = speed * delta_t
and the radius of the curve is
radius = dist / delta_angle
(This gets huge when angular velocity is near zero-- we'll deal with that in a moment.) If angle (at the beginning of the interval) is zero, defined as pointing in the +x direction, then the translation in the interval is easy, and we'll call it deta_x_0 and delta_y_0:
delta_x_0 = radius * sin(delta_angle)
delta_y_0 = radius * (1 - cos(delta_angle))
Since we want to be able to deal with very small delta_angle and very large radius, we'll expand sin and cos, and use this only when angular velocity is close to zero:
dx0 = r * sin(da) = (dist/da) * [ da - da^3/3! + da^5/5! - ...]
= dist * [ 1 - da^2/3! + da^4/5! - ...]
dy0 = r * (1-cos(da)) = (dist/da) * [ da^2/2! - da^4/4! + da^6/6! - ...]
= dist * [ da/2! - da^3/4! + da^5/6! - ...]
But angle generally isn't equal to zero, so we have to rotate these displacements:
dx = cos(angle) * dx0 - sin(angle) * dy0
dy = sin(angle) * dx0 - cos(angle) * dy0
You could do it in two stages. First work out the change of direction to get a new direction vector and then secondly work out the new position using this new direction. Something like
angle = angle + omega * delta_t;
const double d_x = cos( angle );
const double d_y = sin( angle );
x = x + d_x * delta_t * v;
y = y + d_y * delta_t * v;
where you store your current angle out at each step. ( d_x, d_y ) is the current direction vector and omega is the rotation speed that you have. delta_t is obviously your timestep and v is your speed.
This may be too naive to split it up into two distinct stages. I'm not sure I haven't really thought it through too much and haven't tested it but if it works let me know!

Perturb vector by some angle

I have a unit vector in 3D space whose direction I wish to perturb by some angle within the range 0 to theta, with the position of the vector remaining the same. What is a way I can accomplish this?
Thanks.
EDIT: After thinking about the way I posed the question, it seems to be a bit too general. I'll attempt to make it more specific: Assume that the vector originates from the surface of an object (i.e. sphere, circle, box, line, cylinder, cone). If there are different methods to finding the new direction for each of those objects, then providing help for the sphere one is fine.
EDIT 2: I was going to type this in a comment but it was too much.
So I have orig_vector, which I wish to perturb in some direction between 0 and theta. The theta can be thought of as forming a cone around my vector (with theta being the angle between the center and one side of the cone) and I wish to generate a new vector within that cone. I can generate a point lying on the plane that is tangent to my vector and thus creating a unit vector in the direction of the point, call it rand_vector. At this time, I orig_vector and trand_vector are two unit vectors perpendicular to each other.
I generate my first angle, angle1 between 0 and 2pi and I rotate rand_vector around orig_vector by angle1, forming rand_vector2. I looked up a resource online and it said that the second angle, angle2 should be between 0 and sin(theta) (where theta is the original "cone" angle). Then I rotate rand_vector2 by acos(angle2) around the vector defined by the cross product between rand_vector2 and orig_vector.
When I do this, I don't obtain the desired results. That is, when theta=0, I still get perturbed vectors, and I expect to get orig_vector. If anyone can explain the reason for the angles and why they are the way they are, I would greatly appreciate it.
EDIT 3: This is the final edit, I promise =). So I fixed my bug and everything that I described above works (it was an implementation bug, not a theory bug). However, my question about the angles (i.e. why is angle2 = sin(theta)*rand() and why is perturbed_vector = rand_vector2.Rotate(rand_vector2.Cross(orig_vector), acos(angle2)). Thanks so much!
Here's the algorithm that I've used for this kind of problem before. It was described in Ray Tracing News.
1) Make a third vector perpendicular to the other two to build an orthogonal basis:
cross_vector = unit( cross( orig_vector, rand_vector ) )
2) Pick two uniform random numbers in [0,1]:
s = rand( 0, 1 )
r = rand( 0, 1 )
3) Let h be the cosine of the cone's angle:
h = cos( theta )
4) Modify uniform sampling on a sphere to pick a random vector in the cone around +Z:
phi = 2 * pi * s
z = h + ( 1 - h ) * r
sinT = sqrt( 1 - z * z )
x = cos( phi ) * sinT
y = sin( phi ) * sinT
5) Change of basis to reorient it around the original angle:
perturbed = rand_vector * x + cross_vector * y + orig_vector * z
If you have another vector to represent an axis of rotation, there are libraries that will take the axis and the angle and give you a rotation matrix, which can then be multiplied by your starting vector to get the result you want.
However, the axis of rotation should be at right angles to your starting vector, to get the amount of rotation you expect. If the axis of rotation does not lie in the plane perpendicular to your vector, the result will be somewhat different than theta.
That being said, if you already have a vector at right angles to the one you want to perturb, and you're not picky about the direction of the perturbation, you can just as easily take a linear combination of your starting vector with the perpendicular one, adjust for magnitude as needed.
I.e., if P and Q are vectors having identical magnitude, and are perpendicular, and you want to rotate P in the direction of Q, then the vector R given by R = [Pcos(theta)+Qsin(theta)] will satisfy the constraints you've given. If P and Q have differing magnitude, then there will be some scaling involved.
You may be interested in 3D-coordinate transformations to change your vector angle.
I don't know how many directions you want to change your angle in, but transforming your Cartesian coordinates to spherical coordinates should allow you to make your angle change as you like.
Actually, it is very easy to do that. All you have to do is multiply your vector by the correct rotation matrix. The resulting vector will be your rotated vector. Now, how do you obtain such rotation matrix? That depends on the 3d framework/engine you are using. Any 3d framework must provide functions for obtaining rotation matrices, normally as static methods of the Matrix class.
Good luck.
Like said in other comments you can rotate your vector using a rotation matrix.
The rotation matrix has two angles you rotate your vector around. You can pick them with a random number generator, but just picking two from a flat generator is not correct. To ensure that your rotation vector is generated flat, you have to pick one random angle φ from a flat generator and the other one from a generator flat in cosθ ;this ensures that your solid angle element dcos(θ)dφ is defined correctly (φ and θ defined as usual for spherical coordinates).
Example: picking a random direction with no restriction on range, random() generates flat in [0,1]
angle1 = acos(random())
angle2 = 2*pi*random()
My code in unity - tested and working:
/*
* this is used to perturb given vector 'direction' by changing it by angle not more than 'angle' vector from
* base direction. Used to provide errors for player playing algorithms
*
*/
Vector3 perturbDirection( Vector3 direction, float angle ) {
// division by zero protection
if( Mathf.Approximately( direction.z, 0f )) {
direction.z = 0.0001f;
}
// 1 get some orthogonal vector to direction ( solve direction and orthogonal dot product = 0, assume x = 1, y = 1, then z = as below ))
Vector3 orthogonal = new Vector3( 1f, 1f, - ( direction.x + direction.y ) / direction.z );
// 2 get random vector from circle on flat orthogonal to direction vector. get full range to assume all cone space randomization (-180, 180 )
float orthoAngle = UnityEngine.Random.Range( -180f, 180f );
Quaternion rotateTowardsDirection = Quaternion.AngleAxis( orthoAngle, direction );
Vector3 randomOrtho = rotateTowardsDirection * orthogonal;
// 3 rotate direction towards random orthogonal vector by vector from our available range
float perturbAngle = UnityEngine.Random.Range( 0f, angle ); // range from (0, angle), full cone cover guarantees previous (-180,180) range
Quaternion rotateDirection = Quaternion.AngleAxis( perturbAngle, randomOrtho );
Vector3 perturbedDirection = rotateDirection * direction;
return perturbedDirection;
}

Resources