3D Rotation without Trigonometry? - math

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

Related

The X angle between two 3D vectors?

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;

Finding the coordinates of points from distance matrix

I have a set of points (with unknow coordinates) and the distance matrix. I need to find the coordinates of these points in order to plot them and show the solution of my algorithm.
I can set one of these points in the coordinate (0,0) to simpify, and find the others. Can anyone tell me if it's possible to find the coordinates of the other points, and if yes, how?
Thanks in advance!
EDIT
Forgot to say that I need the coordinates on x-y only
The answers based on angles are cumbersome to implement and can't be easily generalized to data in higher dimensions. A better approach is that mentioned in my and WimC's answers here: given the distance matrix D(i, j), define
M(i, j) = 0.5*(D(1, j)^2 + D(i, 1)^2 - D(i, j)^2)
which should be a positive semi-definite matrix with rank equal to the minimal Euclidean dimension k in which the points can be embedded. The coordinates of the points can then be obtained from the k eigenvectors v(i) of M corresponding to non-zero eigenvalues q(i): place the vectors sqrt(q(i))*v(i) as columns in an n x k matrix X; then each row of X is a point. In other words, sqrt(q(i))*v(i) gives the ith component of all of the points.
The eigenvalues and eigenvectors of a matrix can be obtained easily in most programming languages (e.g., using GSL in C/C++, using the built-in function eig in Matlab, using Numpy in Python, etc.)
Note that this particular method always places the first point at the origin, but any rotation, reflection, or translation of the points will also satisfy the original distance matrix.
Step 1, arbitrarily assign one point P1 as (0,0).
Step 2, arbitrarily assign one point P2 along the positive x axis. (0, Dp1p2)
Step 3, find a point P3 such that
Dp1p2 ~= Dp1p3+Dp2p3
Dp1p3 ~= Dp1p2+Dp2p3
Dp2p3 ~= Dp1p3+Dp1p2
and set that point in the "positive" y domain (if it meets any of these criteria, the point should be placed on the P1P2 axis).
Use the cosine law to determine the distance:
cos (A) = (Dp1p2^2 + Dp1p3^2 - Dp2p3^2)/(2*Dp1p2* Dp1p3)
P3 = (Dp1p3 * cos (A), Dp1p3 * sin(A))
You have now successfully built an orthonormal space and placed three points in that space.
Step 4: To determine all the other points, repeat step 3, to give you a tentative y coordinate.
(Xn, Yn).
Compare the distance {(Xn, Yn), (X3, Y3)} to Dp3pn in your matrix. If it is identical, you have successfully identified the coordinate for point n. Otherwise, the point n is at (Xn, -Yn).
Note there is an alternative to step 4, but it is too much math for a Saturday afternoon
If for points p, q, and r you have pq, qr, and rp in your matrix, you have a triangle.
Wherever you have a triangle in your matrix you can compute one of two solutions for that triangle (independent of a euclidean transform of the triangle on the plane). That is, for each triangle you compute, it's mirror image is also a triangle that satisfies the distance constraints on p, q, and r. The fact that there are two solutions even for a triangle leads to the chirality problem: You have to choose the chirality (orientation) of each triangle, and not all choices may lead to a feasible solution to the problem.
Nevertheless, I have some suggestions. If the number entries is small, consider using simulated annealing. You could incorporate chirality into the annealing step. This will be slow for large systems, and it may not converge to a perfect solution, but for some problems it's the best you and do.
The second suggestion will not give you a perfect solution, but it will distribute the error: the method of least squares. In your case the objective function will be the error between the distances in your matrix, and actual distances between your points.
This is a math problem. To derive coordinate matrix X only given by its distance matrix.
However there is an efficient solution to this -- Multidimensional Scaling, that do some linear algebra. Simply put, it requires a pairwise Euclidean distance matrix D, and the output is the estimated coordinate Y (perhaps rotated), which is a proximation to X. For programming reason, just use SciKit.manifold.MDS in Python.
The "eigenvector" method given by the favourite replies above is very general and automatically outputs a set of coordinates as the OP requested, however I noticed that that algorithm does not even ask for a desired orientation (rotation angle) for the frame of the output points, the algorithm chooses that orientation all by itself!
People who use it might want to know at what angle the frame will be tipped before hand so I found an equation which gives the answer for the case of up to three input points, however I have not had time to generalize it to n-points and hope someone will do that and add it to this discussion. Here are the three angles the output sides will form with the x-axis as a function of the input side lengths:
angle side a = arcsin(sqrt(((c+b+a)*(c+b-a)*(c-b+a)*(-c+b+a)*(c^2-b^2)^2)/(a^4*((c^2+b^2-a^2)^2+(c^2-b^2)^2))))*180/Pi/2
angle side b = arcsin(sqrt(((c+b+a)*(c+b-a)*(c-b+a)*(-c+b+a)*(c^2+b^2-a^2)^2)/(4*b^4*((c^2+b^2-a^2)^2+(c^2-b^2)^2))))*180/Pi/2
angle side c = arcsin(sqrt(((c+b+a)*(c+b-a)*(c-b+a)*(-c+b+a)*(c^2+b^2-a^2)^2)/(4*c^4*((c^2+b^2-a^2)^2+(c^2-b^2)^2))))*180/Pi/2
Those equations also lead directly to a solution to the OP's problem of finding the coordinates for each point because: the side lengths are already given from the OP as the input, and my equations give the slope of each side versus the x-axis of the solution, thus revealing the vector for each side of the polygon answer, and summing those sides through vector addition up to a desired vertex will produce the coordinate of that vertex. So if anyone can extend my angle equations to handling beyond three input lengths (but I note: that might be impossible?), it might be a very fast way to the general solution of the OP's question, since slow parts of the algorithms that people gave above like "least square fitting" or "matrix equation solving" might be avoidable.

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.

Combine Rotation Axis Vectors

I'm experimenting with using axis-angle vectors for rotations in my hobby game engine. This is a 3-component vector along the axis of rotation with a length of the rotation in radians. I like them because:
Unlike quats or rotation matrices, I can actually see the numbers and visualize the rotation in my mind
They're a little less memory than quaternions or matrices.
I can represent values outside the range of -Pi to Pi (This is important if I store an angular velocity)
However, I have a tight loop that updates the rotation of all of my objects (tens of thousands) based on their angular velocity. Currently, the only way I know to combine two rotation axis vectors is to convert them to quaternions, multiply them, and then convert the result back to an axis/angle. Through profiling, I've identified this as a bottleneck. Does anyone know a more straightforward approach?
You representation is equivalent to quaternion rotation, provided your rotation vectors are unit length. If you don't want to use some canned quaternion data structure you should simply ensure your rotation vectors are of unit length, and then work out the equivalent quaternion multiplications / reciprocal computation to determine the aggregate rotation. You might be able to reduce the number of multiplications or additions.
If your angle is the only thing that is changing (i.e. the axis of rotation is constant), then you can simply use a linear scaling of the angle, and, if you'd like, mod it to be in the range [0, 2π). So, if you have a rotation rate of α raidans per second, starting from an initial angle of θ0 at time t0, then the final rotation angle at time t is given by:
θ(t) = θ0+α(t-t0) mod 2π
You then just apply that rotation to your collection of vectors.
If none of this improves your performance, you should consider using a canned quaternion library as such things are already optimized for the kinds of application you're disucssing.
You can keep them as angle axis values.
Build a cross-product (anti-symmetric) matrix using the angle axis values (x,y,z) and weight the elements of this matrix by multiplying them by the angle value. Now sum up all of these cross-product matrices (one for each angle axis value) and find the final rotation matrix by using the matrix exponential.
If matrix A represents this cross-product matrix (built from Angle Axis value) then,
exp(A) is equivalent to the rotation matrix R (i.e., equivalent to your quaternion in matrix form).
Therefore,
exp (A1 + A2) = R1 * R2
probably a more expensive calucation in the end...
You should use unit quaternions rather than scaled vectors to represent your rotations. It can be shown (not by me) that any representation of rotations using three parameters will run into problems (i.e. is singular) at some point. In your case it occurs where your vector has a length of 0 (i.e. the identity) and at lengths of 2pi, 4pi, etc. In these cases the representation becomes singular. Unit quaternions and rotation matrices do not have this problem.
From your description, it sounds like you are updating your rotation state as a result of numerical integration. In this case you can update your rotation state by converting your rotational rate (\omega) to a quaternion rate (q_dot). If we represent your quaternion as q = [q0 q1 q2 q3] where q0 is the scalar part then:
q_dot = E*\omega
where
[ -q1 -q2 -q3 ]
E = [ q0 -q3 q2 ]
[ q3 q0 -q1 ]
[ -q2 q1 q0 ]
Then your update becomes
q(k+1) = q(k) + q_dot*dt
for simple integration. You could choose a different integrator if you choose.
Old question, but another example of stack overflow answering questions the OP wasn't asking. OP already listed out his reasoning for not using quaternions to represent velocity. I was in the same boat.
That said, the way you combine two angular velocities, with each represented by a vector, which represents the axis of rotation with its magnitude representing the amount of rotation.
Just add them together. Component-by-component. Hope that helps some other soul out there.

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