I want to reverse a sin/cos operation to get back an angle, but I can't figure out what I should be doing.
I have used sin and cos on an angle in radians to get the x/y vector as such:
double angle = 90.0 * M_PI / 180.0; // 90 deg. to rad.
double s_x = cos( angle );
double s_y = sin( angle );
Given s_x and s_y, is it possible to get back the angle? I thought atan2 was the function to use, but I'm not getting the expected results.
atan2(s_y, s_x) should give you the correct angle. Maybe you have reversed the order of s_x and s_y. Also, you can use the acos and asin functions directly on s_x and s_y respectively.
I use the acos function to get back the angle from the given s_x cosinus. But because several angles may result to the same cosinus (for example cos(+60°) = cos(-60°) = 0.5), it's not possible to get back the angle directly from s_x. So I also use the sign of s_y to get back the sign of the angle.
// Java code
double angleRadian = (s_y > 0) ? Math.acos(s_x) : -Math.acos(s_x);
double angleDegrees = angleRadian * 180 / Math.PI;
for the specific case of (s_y == 0), it does not matter to take +acos or -acos because it means the angle is 0° (+0° or -0° are the same angles) or 180° (+180° or -180° are the same angles).
In math is reverse operation for sin and cos. This is arcsin and arccos.
I don't know what programming language you use. But usually if it have cos and sin function then it can have reverse function.
asin(s_x), acos(s_y), perhaps, if you are using c.
double angle_from_sin_cos( double sinx, double cosx ) //result in -pi to +pi range
{
double ang_from_cos = acos(cosx);
double ang_from_sin = asin(sinx);
double sin2 = sinx*sinx;
if(sinx<0)
{
ang_from_cos = -ang_from_cos;
if(cosx<0) //both negative
ang_from_sin = -PI -ang_from_sin;
}
else if(cosx<0)
ang_from_sin = PI - ang_from_sin;
//now favor the computation coming from the
//smaller of sinx and cosx, as the smaller
//the input value, the smaller the error
return (1.0-sin2)*ang_from_sin + sin2*ang_from_cos;
}
Related
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|
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.
I made a get_angle function inside my Vector3 class but I have a problem with it.
The Y angle is perfectly fine.
The pitch angle (X) it returns is slightly over my destination position when the base vector is over it (and when under the opposite is happening).
The amount of it being wrong is dependant on the height difference.
Angle get_angle(const Vector3f& v) const {
return Angle(
math::rad_to_deg(atan2(get_distance(v), v.z - z)) - 90.0f,
math::rad_to_deg(atan2(v.y - y, v.x - x)),
0.0f);
}
It's probably my maths that are bad here.
I found a solution to my problem:
Angle get_angle(const Vector3f& v) const {
return Angle(
math::rad_to_deg(
atan2(
sqrt(pow(X - v.X, 2) + pow(Y - v.Y, 2)), // the problem was here,
// the distance between the vectors should not include the Z position distance in it
v.Z - Z)) - 90.0f,
math::rad_to_deg(atan2(v.Y - Y, v.X - X)), // this worked correctly
0.0f // roll angle should always be 0.0f
);
}
What exactly are you trying to calculate? What does your "Angle" class represent?
I guess you either want to:
Calculate the angle between the two vectors, i.e. a single scalar value. The formula can be found here as cos(theta) == dot(*this, v) / (norm() * v.norm()).
https://math.stackexchange.com/questions/974178/how-to-calculate-the-angle-between-2-vectors-in-3d-space-given-a-preset-function
Or, convert both vectors to spherical coordinates (phi, theta), and calculate a delta for each phi and theta, i.e. you calculate two angles. The conversion formula from cartesian to spherical coordinates can be found here: https://en.wikipedia.org/wiki/Spherical_coordinate_system#Cartesian_coordinates
What is the inverse of the function
math.atan2
I use this in Lua where I can get the inverse of math.atan by math.tan.
But I am lost here.
EDIT
OK, let me give you more details.
I needed to calculate angle between 2 points (x1,y1) and (x2,y2) and I did,
local dy = y1-y2
local dx = x1-x2
local angle = atan2(dy,dx)* 180 / pi
Now if I have the angle, is it possible to get back dy and dx?
Given only the angle you can only derive a unit vector pointing to (dx, dy). To get the original (dx, dy) you also need to know the length of the vector (dx, dy), which I'll call len. You also have to convert the angle you derived from degrees back to radians and then use the trig equations mentioned elsewhere in this post. That is you have:
local dy = y1-y2
local dx = x1-x2
local angle = atan2(dy,dx) * 180 / pi
local len = sqrt(dx*dx + dy*dy)
Given angle (in degrees) and the vector length, len, you can derive dx and dy by:
local theta = angle * pi / 180
local dx = len * cos(theta)
local dy = len * sin(theta)
Apparently, something like this will help:
x = cos(theta)
y = sin(theta)
Simple Google search threw this up, and the guy who asked the question said it solved it.
You'll probably get the wrong numbers if you use:
local dy = y1-y2
local dx = x1-x2
local angle = atan2(dy,dx) * 180 / pi
If you are using the coordinate system where y gets bigger going down the screen and x gets bigger going to the right then you should use:
local dy = y1 - y2
local dx = x2 - x1
local angle = math.deg(math.atan2(dy, dx))
if (angle < 0) then
angle = 360 + angle
end
The reason you want to use this is because atan2 in lua will give you a number between -180 and 180. It will be correct until you hit 180 then as it should go beyond 180 (i.e. 187) it will invert it to a negative number going down from -180 to 0 as you get closer to 360. To correct for this we check to see if the angle is less than 0 and if it is we add 360 to give us the correct angle.
According this reference:
Returns the arc tangent of y/x (in radians), but uses the signs of
both parameters to find the quadrant of the result. (It also handles
correctly the case of x being zero.)
So I guess you can use math.tan to invert it also.
As atan2 works as tan-1, so the inverse could be tan, taking into consideration conversion between radian and degree
I have two vectors in a game. One vector is the player, one vector is an object. I also have a vector that specifies the direction the player if facing. The direction vector has no z part. It is a point that has a magnitude of 1 placed somewhere around the origin.
I want to calculate the angle between the direction the soldier is currently facing and the object, so I can correctly pan some audio (stereo only).
The diagram below describes my problem. I want to calculate the angle between the two dashed lines. One dashed line connects the player and the object, and the other is a line representing the direction the player is facing from the point the player is at.
At the moment, I am doing this (assume player, object and direction are all vectors with 3 points, x, y and z):
Vector3d v1 = direction;
Vector3d v2 = object - player;
v1.normalise();
v2.normalise();
float angle = acos(dotProduct(v1, v2));
But it seems to give me incorrect results. Any advice?
Test of code:
Vector3d soldier = Vector3d(1.f, 1.f, 0.f);
Vector3d object = Vector3d(1.f, -1.f, 0.f);
Vector3d dir = Vector3d(1.f, 0.f, 0.f);
Vector3d v1 = dir;
Vector3d v2 = object - soldier;
long steps = 360;
for (long step = 0; step < steps; step++) {
float rad = (float)step * (M_PI / 180.f);
v1.x = cosf(rad);
v1.y = sinf(rad);
v1.normalise();
float dx = dotProduct(v2, v1);
float dy = dotProduct(v2, soldier);
float vangle = atan2(dx, dy);
}
You shoud always use atan2 when computing angular deltas, and then normalize.
The reason is that for example acos is a function with domain -1...1; even normalizing if the input absolute value (because of approximations) gets bigger than 1 the function will fail even if it's clear that in such a case you would have liked an angle of 0 or PI instead. Also acos cannot measure the full range -PI..PI and you'd need to use explicitly sign tests to find the correct quadrant.
Instead atan2 only singularity is at (0, 0) (where of course it doesn't make sense to compute an angle) and its codomain is the full circle -PI...PI.
Here is an example in C++
// Absolute angle 1
double a1 = atan2(object.y - player.y, object.x - player.x);
// Absolute angle 2
double a2 = atan2(direction.y, direction.x);
// Relative angle
double rel_angle = a1 - a2;
// Normalize to -PI .. +PI
rel_angle -= floor((rel_angle + PI)/(2*PI)) * (2*PI) - PI;
In the case of a general 3d orientation you need two orthogonal directions, e.g. the vector of where the nose is pointing to and the vector to where your right ear is.
In that case the formulas are just slightly more complex, but simpler if you have the dot product handy:
// I'm assuming that '*' is defined as the dot product
// between two vectors: x1*x2 + y1*y2 + z1*z2
double dx = (object - player) * nose_direction;
double dy = (object - player) * right_ear_direction;
double angle = atan2(dx, dy); // Already in -PI ... PI range
In 3D space, you also need to compute the axis:
Vector3d axis = normalise(crossProduct(normalise(v1), normalise(v2)));