The X angle between two 3D vectors? - vector

I have two 3D vectors called A and B that both only have a 3D position. I know how to find the angle along the unit circle ranging from 0-360 degrees with the atan2 function by doing:
EDIT: (my atan2 function made no sense, now it should find the "y-angle" between 2 vectors):
toDegrees(atan2(A.x-B.x,A.z-B.z))+180
But that gives me the Y angle between the 2 vectors.
I need to find the X angle between them. It has to do with using the x, y and z position values. Not the x and z only, because that gives the Y angle between the two vectors.
I need the X angle, I know it sounds vague but I don't know how to explain. Maybe for example you have a camera in 3D space, if you look up or down than you rotate the x-axis. But now I need to get the "up/down" angle between the 2 vectors. If I rotate that 3D camera along the y-axis, the x-axis doens't change. So with the 2 vectors, no matter what the "y-angle" is between them, the x-angle between the 2 vectors wil stay the same if y-angle changes because it's the "up/down" angle, like in the camara.
Please help? I just need a line of math/pseudocode, or explanation. :)

atan2(crossproduct.length,scalarproduct)
The reason for using atan2 instead of arccos or arcsin is accuracy. arccos behaves very badly close to 0 degrees. Small computation errors in argument will lead to disproportionally big errors in result. arcsin has same problem close to 90 degrees.

Computing the altitude angle
OK, it might be I finally understood your comment below about the result being independent of the y angle, and about how it relates to the two vectors. It seems you are not really interested in two vectors and the angle between these two, but instead you're interested in the difference vector and the angle that one forms against the horizontal plane. In a horizontal coordinate system (often used in astronomy), that angle would be called “altitude” or “elevation”, as opposed to the “azimuth” you compute with the formula in your (edited) question. “altitude” closely relates to the “tilt” of your camera, whereas “azimuth” relates to “panning”.
We still have a 2D problem. One coordinate of the 2D vector is the y coordinate of the difference vector. The other coordinate is the length of the vector after projecting it on the horizontal plane, i.e. sqrt(x*x + z*z). The final solution would be
x = A.x - B.x
y = A.y - B.y
z = A.z - B.z
alt = toDegrees(atan2(y, sqrt(x*x + z*z)))
az = toDegrees(atan2(-x, -z))
The order (A - B as opposed to B - A) was chosen such that “A above B” yields a positive y and therefore a positive altitude, in accordance with your comment below. The minus signs in the azimuth computation above should replace the + 180 in the code from your question, except that the range now is [-180, 180] instead of your [0, 360]. Just to give you an alternative, choose whichever you prefer. In effect you compute the azimuth of B - A either way. The fact that you use a different order for these two angles might be somewhat confusing, so think about whether this really is what you want, or whether you want to reverse the sign of the altitude or change the azimuth by 180°.
Orthogonal projection
For reference, I'll include my original answer below, for those who are actually looking for the angle of rotation around some fixed x axis, the way the original question suggested.
If this x angle you mention in your question is indeed the angle of rotation around the x axis, as the camera example suggests, then you might want to think about it this way: set the x coordinate to zero, and you will end up with 2D vectors in the y-z plane. You can think of this as an orthogonal projection onto said plain. Now you are back to a 2D problem and can tackle it there.
Personally I'd simply call atan2 twice, once for each vector, and subtract the resulting angles:
toDegrees(atan2(A.z, A.y) - atan2(B.z, B.y))
The x=0 is implicit in the above formula simply because I only operate on y and z.
I haven't fully understood the logic behind your single atan2 call yet, but the fact that I have to think about it this long indicates that I wouldn't want to maintain it, at least not without a good explanatory comment.
I hope I understood your question correctly, and this is the thing you're looking for.

Just like 2D Vectors , you calculate their angle by solving cos of their Dot Product
You don't need atan, you always go for the dot product since its a fundamental operation of vectors and then use acos to get the angle.
double angleInDegrees = acos ( cos(theta) ) * 180.0 / PI;

Related

compute angle of rotation between two orthographic projections

I Have the orthographic projection of a unit cube with one of its vertex at origin as shown above. I have the x,y (no z) co ordinates of the projections. I would like to compute the angle of rotation of the plane to get the second orthographic projection from the first one (maybe euler angles??)
Is there any other easy way to compute this?
UPDATE:
Could I use this rotation matrix to get a system of equations in cos, sin angles and the x,y and x',y' and solve them easily? Or is there any easier way to get the angles back? (Am I on the right direction to solve this? )
First method
Use this idea to generate equations:
a1, a2 and a3 are coordinates in the original system, x y are the coordinates you get from the end-result and z is a coordinate you don’t know. This generates 2 equations for every point of the cube. E.g for point 0 with coordinates (-1, -1, 1) these are:
Do this for the 4 front points of the cube and you get 8 equations. Now add the fact that this is a rotation matrix -> the determinant is 1 and you have 9 equations. Solve these with any of the usual algorithms for solving equation systems and you have the transformation matrix. Getting the axis and angle from that is easy via google: http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/
Second method
Naming your points 0, 1, 2, 3 a, b, c, d respectively, you can get the z coordinates of the vectors between them (e.g. b-a) with this idea:
you will still have to sort out if b3-a3 is positive, though. One way to do that is to use the centermost point as b (calculate distance from the center for all points, use the one with the minimal distance). Then you know for sure that b3-a3 is positive (if z is positive towards you).
Now assume that a is (0,0,0) in your transformed space and you can calculate all the point positions by adding the appropriate vectors to that.
To get the rotation you use the fact that you know where b-a did point in your origin space (e.g. (1,0,0)). You get the rotation angle via dot product of b-a and (1,0,0) and the rotation axis via cross product between those vectors.

Signed angle between two vectors without a reference plane

(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)
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.
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.
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.
If all you want is a consistent result, then any arbitrary way of choosing between a × b and b × a for your normal will do. Perhaps pick the one that's lexicographically smaller?
(But you might want to explain what problem you are actually trying to solve: maybe there's a solution that doesn't involve computing a consistent signed angle between arbitrary 3-vectors.)

3D rotations of a plane

I'm doing something where I have a plane in a coord sys A with a set of points already on it. I also have a normal vector in space N. How can I rotate the points on coord sys A so that the underlying plane will have the same normal direction as N?
Wondering if any one has a good idea on how to do this. Thanks
If you have, or can easily compute, the normal vector to the plane that your points are currently in, I think the easiest way to do this will be to rotate around the axis common to the two planes. Here's how I'd go about it:
Let M be the vector normal to your current plane, and N be the vector normal to the plane you want to rotate into. If M == N you can stop now and leave the original points unchanged.
Calculate the rotation angle as
costheta = dot(M,N)/(norm(M)*norm(N))
Calculate the rotation axis as
axis = unitcross(M, N)
where unitcross is a function that performs the cross product and normalizes it to a unit vector, i.e. unitcross(a, b) = cross(a, b) / norm(cross(a, b)). As user1318499 pointed out in a comment, this step can cause an error if M == N, unless your implementation of unitcross returns (0,0,0) when a == b.
Compute the rotation matrix from the axis and angle as
c = costheta
s = sqrt(1-c*c)
C = 1-c
rmat = matrix([ x*x*C+c x*y*C-z*s x*z*C+y*s ],
[ y*x*C+z*s y*y*C+c y*z*C-x*s ]
[ z*x*C-y*s z*y*C+x*s z*z*C+c ])
where x, y, and z are the components of axis. This formula is described on Wikipedia.
For each point, compute its corresponding point on the new plane as
newpoint = dot(rmat, point)
where the function dot performs matrix multiplication.
This is not unique, of course; as mentioned in peterk's answer, there are an infinite number of possible rotations you could make that would transform the plane normal to M into the plane normal to N. This corresponds to the fact that, after you take the steps described above, you can then rotate the plane around N, and your points will be in different places while staying in the same plane. (In other words, each rotation you can make that satisfies your conditions corresponds to doing the procedure described above followed by another rotation around N.) But if you don't care where in the plane your points wind up, I think this rotation around the common axis is the simplest way to just get the points into the plane you want them in.
If you don't have M, but you do have the coordinates of the points in your starting plane relative to an origin in that plane, you can compute the starting normal vector from two points' positions x1 and x2 as
M = cross(x1, x2)
(you can also use unitcross here but it doesn't make any difference). If you have the points' coordinates relative to an origin that is not in the plane, you can still do it, but you'll need three points' positions:
M = cross(x3-x1, x3-x2)
A single vector (your normal - N) will not be enough. You will need another two vectors for the other two dimensions. (Imagine that your 3D space could still rotate/spin around the normal vector, and you need another 2 vectors to nail it down). Once you have the normal and another one on the plane, the 3rd one should be easy to find (left- or right-handed depending on your system).
Make sure all three are normalized (length of 1) and put them in a matrix; use that matrix to transform any point in your 3D space (use matrix multiplication). This should give you the new coordinates.
I'm thinking make a unit vector [0,0,1] and use the dot-product along two planes to find the angle of difference, and shift all your points by those angles. This is assuming you want the z-axis to align with the normal vector, else just use [1,0,0] or [0,1,0] for x and y respectively.

3D Rotation without Trigonometry?

I am developing a rotate-around-axis algorithm in 3 dimensions. My inputs are
the axis I am revolving around, as a vector from my center point
the center point (obviously)
the angle I wish to rotate around
my current position
I am wondering if there is a way to do this without trigonometry, just with vector operations. Does anyone have a potential solution?
EDIT: Is there a way that I could rotate by pi/4 radians (45 degrees) each time, rather than an inputted angle theta? This might simplify things a bit, I don't know.
Rotations are inherently well-described by and .
It's a handy trick that unit quaternions nicely represent 3-D rotations just as well as (and in some senses, better than) rotation matrices. Converting a rotation by angle about a normal axis where , does require a little bit of trigonometry: .
But from there on it's simple arithmetic.
A quaternion can be directly applied to rotate a vector with , or converted to a rotation matrix .
This is a rotation around the origin, of course. To rotate around an arbitrary point in space, simply translate by to the origin, rotate, then translate by to return.
use matrices: http://en.wikipedia.org/wiki/Rotation_matrix#Rotations_in_three_dimensions
If this is some sort of dumb homework problem, you can use Taylor Series approximation of the sine/consine functions. Whether or not this "counts" as trigonometry is I guess up for debate. You could then use these values in a rotation matrix or quarternion, if you want to use vector operations.
But again, there's no practical reason to do this.
Are there other techniques that don't use trig functions? Possibly, but there are no know efficient, general (i.e. for arbitrary angles) ways to perform rotations without use of trig functions.
However, based on your edit, you can precompute the sin and cos for a collection of angles you're interested in and store them in a lookup table. You need not be constrained in such a circumstance to π/4 increments, but you can do π/256 or π/1024 increments if you want. Also, you don't need two tables, since cos(θ) = sin(θ+π/2).
From there, you can use any of a number of interpolation methods to include simple rounding, linear interpolation or some sort of polynomial interpolation based on your needs.
You would then use either the matrix or quaternion based transformation to compute the rotated vector.
This will be faster than computing the sin and cos for general angles, though will require some additional space, and there will be an accuracy penalty as well. But if it satisfies your needs...
Theres a cheaper way than matrices, I think ive got it to sum count of adders.
The perimetre box of the vector is as good as an angle, if you step in partitions of the box size. (thats only a binary shift if its a power of 2.)
Then that would be a "box rotate" then just use the side report to give you how far along the diagonal you would be then you can split it up into so many gradients, the circle shape.
Id like to see someone proove that u can rotate without matrices or any trig like that too.
Is it possible to rotate without trigonometry? Yes.
Is it useful to rotate without using trigonometry? Probably not.
The first option is a problem-level solution: Change your coordinate system to spherical or cylindrical coordinates.
Since you rotate around an axis cylindrical coordinates of the form (alpha, radius, x3) will work.
Naming your center point O (for origin) and the point to rotate P, you can get the vector between them v=P-O. You also know the normal vector n of your plane of rotation (the vector you rotate around). With this, you can get the components of v that are parallel and orthogonal to n using a vector projection.
You have the freedom to choose how your new coordinate frame is rotated (relative to your original frame), so you can measure angles from the projection of v onto the plane of rotation. You also have the freedom to choose between degree and radians.
From there, you can now rotate to your heart's content using addition and subtraction.
Using dot(.,.) to denote the scalar product it would look something like this in code
v_parallel = dot(v, n) / dot(n, n) * n
radius = norm(v - v_parallel)
x3 = norm(v_parallel)
new_axis = (v - v_parallel) / norm(v - v_parallel)
P_polar = (0, radius, x3)
# P rotated by 90 degrees
P_polar = (pi/2, radius, x3)
# P rotated by -10 degrees
P_polar = (-pi/36, radius, x3)
However, if you want to change back to a standard basis you will have to use trigonometry again. Hence why I said this approach exists, but may not be too useful in practice.
Another approach comes from the cool observation that you can describe any planar rotation using two reflections along two given axis (represented by two vectors). The plane of rotation is the plane that is spun up by the two vectors and the angle of rotation is twice the angle between the two vectors.
You can reflect a vector using the vector projection from above; hence, you can do the entire process without trigonometry if you know the two vectors (let's call them x1 and x2).
tmp = v - 2 * dot(v, x1) / dot(x1, x1) * x1
v_rotated = tmp - 2 * dot(tmp, x2) / dot(x2, x2) * x2
The problem then turns into finding two vectors that are orthogonal to n and have an enclosing angle of alpha/2. How to do this is specific to your problem. For arbitrary alpha this is again the point where you can't dodge the trigonometry bullet; hence, it is again possible, but maybe not so viable in practice.
With help from Mathematica, it looks like we can rotate a point around a vector without Sin/Cos if you are willing to specify the amount of rotation as a number between -1 and 1, rather than an angle in radians.
The below starts with Mathematica's RotationTransform of a point {x,y,z} around a vector {u,v,w} by c radians (which contains many instances of Cos[c] and Sin[c]). It then substitutes all the Cos[c] with "c" and Sin[c] with Sqrt[1-c^2] (a trig identity for Sin in terms of Cos). Everything is simplified with the assumption that the rotation vector is normalized. The resulting equation produces the rotated point without any trig operations.
Note: as c ranges from -1 to 1 the point will only rotate through half a circle, the other half of the rotation can be achieved by flipping the signs on {u,v,w}.

How do you calculate the reflex angle given two vectors in 3D space?

I want to calculate the angle between two vectors a and b. Lets assume these are at the origin. This can be done with
theta = arccos(a . b / |a| * |b|)
However arccos gives you the angle in [0, pi], i.e. it will never give you an angle greater than 180 degrees, which is what I want. So how do you find out when the vectors have gone past the 180 degree mark? In 2D I would simply let the sign of the y-component on one of the vectors determine what quadrant the vector is in. But what is the easiest way to do it in 3D?
EDIT: I wanted to keep the question general but here we go. I'm programming this in c and the code I use to get the angle is theta = acos(dot(a, b)/mag(a)*mag(b)) so how would you programmatically determine the orientation?
This works in 2D because you have a plane defined in which you define the rotation.
If you want to do this in 3D, there is no such implicit 2D plane. You could transform your 3D coordinates to a 2D plane going through all three points, and do your calculation inside this plane.
But, there are of course two possible orientations for the plane, and that will affect which angles will be > 180 or smaller.
I came up with the following solution that takes advantage of the direction change of the cross product of the two vectors:
Make a vector n = a X b and normalize it. This vector is normal to the plane spanned by a and b.
Whenever a new angle is calculated compare it with the old normal. In the comparison, treat the old and the current normals as points and compute the distance between them. If this distance is 2 the normal (i.e. the cross product a X b has flipped).
You might want to have a threshold for the distance as the distance after a flip might be shorter than 2, depending on how the vectors a and b are oriented and how often you update the angle.
One solution that you could use:
What you effectively need to do is create a plane that one of the vectors is coplanar to.
Getting the cross product of both vectors will create a plane, then is you get the normal of this plane, you can get the angle between this and the vector you need to get the signed angle for, and you can use the angle to determine the sign.
If the angle is greater than 90 degrees, then it is below the created plane; less than 90 degrees, and it is above.
Depending on cost of calculations, the dot product can be used at this stage instead of the angle.
Just make sure that you always calculate the normals by the same order of vectors.
This is useable more easily if you're using the XYZ axes, and that's what you're comparing against, since you already have the vectors needed for the plane.
There are possbly more efficient solutions, but this is one I came up with.
Edit: clarification of created vectors
a X b = p. This is perpendicular to both a and b.
Then, do either:
a X p or b X p to create another vector that is the normal to the plane created by the 2 vectors. Choice of vector depends on which you're trying to find the angle for.
Strictly speaking, two 3D vectors always have two angles between them - one below or equal to 180, the other over or equal to 180. Arccos gives you one of them, you can get the other by subtracting from 360. Think of it that way: imagine two lines intersect. You have 4 angles there - 2 of one value, 2 of another. What's the angle between the lines? No single answer. Same here. Without some kind of extra criteria, you can not, in theory, tell which of the two angle values should be taken into account.
EDIT: So what you really need is an arbitrary example of fixing an orientation. Here's one: we look from the positive Z direction. If the plane between the two vectors contains the Z axis, we look from the positive Y direction. If the plane is YZ, we look from the positive X direction. I'll think how to express this in coordinate form, then edit again.

Resources