get angle between two 3d points - math

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

Related

calculate angle from vector to coord

I am breaking my head trying to find an appropriate formula to calculate a what sounds to be an easy task but in practice is a big mathematical headache.
I want to find out the offset it needs to turn my vector's angle (X, Y, Angle) to face a coord ( X, Y )
My vector won't always be facing 360 degrees, so i need that as a variable as well..
Hoping an answer before i'm breaking my pc screen.
Thank you.
input
p1 = (x1,y1) point1 (vector origin)
p2 = (x2,y2) point2
a1 = 360 deg direction of vector
assuming your coodinate system is: X+ is right Y+ is up ang+ is CCW
your image suggest that you have X,Y mixed up (angle usually start from X axis not Y)
da=? change of a1 to match direction of p2-p1
solution 1:
da=a1-a2=a1-atanxy(x2-x1,y1-y1)
atanxy(dx,dy) is also called atan2 on some libs just make sure the order of operands is the right one
you can also use mine atanxy in C++
it is 4 quadrant arctangens
solution 2:
v1=(cos(a1),sin(a1))
v2=(x2-x1,y2-y1)
da=acos(dot(v1,v2)/(|v1|*|v2|))
or the same slightly different
v1=(cos(a1),sin(a1))
v2=(x2-x1,y2-y1)
v2/=|v2| // makes v2 unit vector, v1 is already unit
da=acos(dot(v1,v2))
so:
da=acos((cos(a1)*(x2-x1)+sin(a1)*(y2-y1)/sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)));
[notes]
just change it to match your coordinate system (which you did not specify)
use radians or degrees according to your sin,cos,atan dependencies ...
The difference between the vectors is also a vector.
Then calculate the tangens (y part / x part) and invert it to an angle.
Of course use the sign of y if x = 0.
if the coord to face is (x2 ,y2)
deltaY = y2 - y1
deltaX = x2 - x1
You have the angle in degrees between the two points using this formula...
angleInDegrees = arctan(deltaY / deltaX) * 180 / PI
subtract the original angle of your vector and you will get the correct offset!

Angle between two 3D vectors in 3D rotated by a quaternion

I have a 3D unit quaternion = {w,x,y,z} coming from a sensor device. I want to get the amount of angles(ax,ay,az) about X, Y and Z axes ( But the angles should be independent, one should not change when the other changes). I have come across, Quaternion to Euler angle conversion, they have gimbal lock problem & they are dependents.
So I'm thinking to take different approach.
Take 3D vectors x = [1,0,0], y = [0,1,0] and z = [0,0,1]. If I rotate these vectors x, y and z by quaternion, we get 3 vectors xx, yy and zz. Then calculate the angle between x, xx vectors. Similarly angle between y, yy and z, zz. This also seems to be not working.
The following is the C# code I've written. The angle range should be -180 to 180 or 0 to 360deg. acos is not preferred as it has precision problems.
How to get this done ? Are there standard approaches ? How to decompose a 3D quaternion into 3 quaternions for individual X, Y and Z axes ?
Vector3D rotVecX = QuatVecRotation(Quaternion, new Vector3D(1,0,0));
Vector3D rotVecY = QuatVecRotation(Quaternion, new Vector3D(0,1,0));
Vector3D rotVecZ = QuatVecRotation(Quaternion, new Vector3D(0,0,1));
float aX = (float)GetXangle(new Vector3D(1, 0, 0), rotVec1);
float aY = (float)GetYangle(new Vector3D(0, 1, 0), rotVec2);
float aZ = (float)GetZangle(new Vector3D(0, 0, 1), rotVec3);
Vector3D QuatVecRotation(Quaternion quat, Vector3D vec)
{
Quaternion Qvec = new Quaternion(0,vec.X,vec.Y,vec.Z);
Quaternion QvecR = Quaternion.Multiply(quat, Qvec);
Quaternion Qinv = new Quaternion( quat.W, -quat.X, -quat.Y, -quat.Z); // conjugate or Inverse
Quaternion Qr = Quaternion.Multiply(QvecR, Qinv);
Vector3D resultVec = new Vector3D(Qr.X, Qr.Y, Qr.Z);
resultVec.Normalize();
return resultVec;
}
public double GetXangle(Vector3D vec1, Vector3D vec2)
{
Vector3D axis = Vector3D.CrossProduct(vec1, vec2);
double angle = Rad2Deg((float)Math.Atan2(axis.Length, Vector3D.DotProduct(vec1, vec2)));
double dir = Vector3D.DotProduct(Vector3D.CrossProduct(axis, vec1), new Vector3D(0, 1, 1));
if(dir<0)
angle = angle * Math.Sign(dir);
return angle;
}
use dot product:
x·xx = |x||xx|cos(angle)
so:
angle = acos( x·xx / ( |x||xx| ) )
If the Euler angles obtained from a quaternion becomes bigger, then we will experience gimbal-lock problem, if they are smaller ( between two successive quaternions), then there should be no problem. We may just add the present angles to the previous ones & is possible to get them in desired ranges.
I would suggest calculating the Rodriguez rotation first. This is quite simple
Considering you have two vectors a and b.
The rotation axis will be defined by their cross product N=axb.
The rotation angle will be defined by their normalized dot product
cos(theta) = a * b/(||a|| ||b||)
The formula to convert between the Rodriguez rotation and it's Quaternion is here

Getting angle back from a sin/cos conversion

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;
}

Calculate Angle from Two Points and a Direction Vector

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)));

Limit camera pitch

How can I efficiently limit camera pitch when I have only camera quaternion? Do I have to convert to euler angles and then back to quaternion or is there any other way?
If the camera never has any roll (as is common in lots of games, such as first person shooters), then the solution is simple. If there is roll, then there's an additional step involved. I'll start with what to do if there is no roll, and generalize the solution to what to do if there is.
Let qc be the camera rotation. Let qy a rotation with the same yaw as qc, but with zero pitch. If there is no roll, the camera rotation is a yaw rotation followed by a pitch rotation:
qc = qp * qy
We can recover the pitch rotation qp as the rotation from qy to qc:
qp = qc * qy^-1
The trick, then, is to construct qy, so we can plug it into the above equation to solve for qp. Let vc be the unit vector pointing out of the lens of the camera, or the "forward vector". Let vy be the same vector, but projected to the horizontal plane and normalized. Finally, let v0 be the forward vector when the camera rotation qc is the identity rotation. The rotation that rotates v0 into vy is the yaw rotation. The angle can be given as:
yaw = asin(Norm(cross(v0, vy)))
The corresponding yaw rotation is:
qy = { cos(yaw/2), up * sin(yaw/2) }
Where "up" is the unit vector in the up direction, aka the axis for yaw rotations. Plug this into qp = qy^-1 * qc above to get pitch quaternion qp. Finally, get the pitch angle from qp as:
pitch = 2*asin(Dot(right, [qp[1], qp[2], qp[3]]))
Where "right" is the unit vector in the right direction, aka the axis for pitch rotations.
Like I said, things get more complicated if the camera also has roll, but the general strategy is the same. You formulate the camera rotation as a product of rotation components, then isolate the component you want (in this case, pitch). For example, if the euler sequence you use to define "pitch" is the common yaw-pitch-roll sequence, you define qc as:
qc = qr * qp * qy
We can define a variable qx to be the combined pitch and roll rotations:
qx = qr * qp
We can now write qc as:
qc = qx * qy
We already know how to solve for qx in this form, by retracing the steps we used above to solve for qp. Rearranging the definition for qx, we get:
qp = qr^-1 * qx
We just solved for qx, so to solve for the pitch rotation qp, we only need the roll qr. We can construct it using vectors as we did previously. Let vc be the forward vector again. The roll will be a rotation around this vector. Let vu be the camera's up vector (in world coordinates), and let vu0 be the camera's up vector with zero roll. We can construct vu0 by projecting the global up vector to the plane perpendicular to vc, then normalizing. The roll rotation qr is then the rotation from vu0 to vu. The axis of this rotation is the forward vector vc. The roll angle is
roll = asin(Dot(vc, cross(vu0, vu)))
The corresponding quaternion is:
qr = { cos(roll/2), forward * sin(roll/2) }
Where "forward" is the axis of roll rotations.
The pitch is just one component of the full rotation, so if you want to think about your rotation like that, you'd better store the pitch separately, possibly using Euler angles.
Just converting to Euler angles and back when you need to limit the movement might not work too well, since you'l need to remember the last frame (if you have any) to see if you passed the limit, and in what direction.
As I see it, the main point of quaternions is that you don't need to bother with limits like that. Everything just works without any singularities. Why exactly do you want to limit the pitch?
Camera rotation quaternions can be defined as:
vector A = [x, y, z]
Q.x = A.x * sin(theta/2)
Q.y = A.y * sin(theta/2)
Q.z = A.z * sin(theta/2)
Q.w = cos(theta/2)
Where A is the position and theta is the angle you want to rotate the camera (adjust the pitch).
So if you have the quaternion available you can limit the angle of pitch by verifying each time if the rotation angle plus/minus the actual angle is ok.
I think you can avoid conversion if you set your limits as
+cos(supLim/2) < (Q.w + P.w) < -cos(infLim/2)
As cosine is an continuous function this is supposed to work.
If you could post the code you're actually using maybe we can help a little more.
I may be a little late to the party, but this is how I solved it:
// "Up" = local vector -> rotation * Vector3.UnitY
// "Forward" = local vector -> rotation * Vector3.UnitZ
// "Right" = local vector -> rotation * Vector3.UnitX
public void Rotate(Vector3 axis, float angle)
{
if (LerpRotation)
{
RotationTarget *= Quaternion.FromAxisAngle(axis, angle);
}
else
{
Rotation *= Quaternion.FromAxisAngle(axis, angle);
}
//Locking the Pitch in 180°
float a = Vector3.CalculateAngle(Vector3.UnitY, Up);
float sign = Math.Sign(Forward.Y);
float delta = (float)Math.PI / 2 - a;
if(delta < 0)
Rotation *= Quaternion.FromAxisAngle(Right, delta * sign);
}

Resources