Calculate rotations to look at a 3D point? - math

I need to calculate the 2 angles (yaw and pitch) for a 3D object to face an arbitrary 3D point. These rotations are known as "Euler" rotations simply because after the first rotation, (lets say Z, based on the picture below) the Y axis also rotates with the object.
This is the code I'm using but its not working fully. When on the ground plane (Y = 0) the object correctly rotates to face the point, but as soon as I move the point upwards in Y, the rotations don't look correct.
// x, y, z represent a fractional value between -[1] and [1]
// a "unit vector" of the point I need to rotate towards
yaw = Math.atan2( y, x )
pitch = Math.atan2( z, Math.sqrt( x * x + y * y ) )
Do you know how to calculate the 2 Euler angles given a point?
The picture below shows the way I rotate. These are the angles I need to calculate.
(The only difference is I'm rotating the object in the order X,Y,Z and not Z,Y,X)
This is my system.
coordinate system is x = to the right, y = downwards, z = further back
an object is by default at (0,0,1) which is facing backward
rotations are in the order X, Y, Z where rotation upon X is pitch, Y is yaw and Z is roll

Here are my working assumptions:
The coordinate system (x,y,z) is such that positive x is to the right, positive y is down, and z is the remaining direction. In particular, y=0 is the ground plane.
An object at (0,0,0) currently facing towards (0,0,1) is being turned to face towards (x,y,z).
In order to accomplish this, there will be a rotation about the x-axis followed by one around the y-axis. Finally, there is a rotation about the z-axis in order to have things upright.
(The terminology yaw, pitch, and roll can be confusing, so I'd like to avoid using it, but roughly speaking the correspondence is x=pitch, y=yaw, z=roll.)
Here is my attempt to solve your problem given this setup:
rotx = Math.atan2( y, z )
roty = Math.atan2( x * Math.cos(rotx), z )
rotz = Math.atan2( Math.cos(rotx), Math.sin(rotx) * Math.sin(roty) )
Hopefully this is correct up to signs. I think the easiest way to fix the signs is by trial and error. Indeed, you appear to have gotten the signs on rotx and roty correct -- including a subtle issue with regards to z -- so you only need to fix the sign on rotz.
I expect this to be nontrivial (possibly depending on which octant you're in), but please try a few possibilities before saying it's wrong. Good luck!
Here is the code that finally worked for me.
I noticed a "flip" effect that occurred when the object moved from any front quadrant (positive Z) to any back quadrant. In the front quadrants the front of the object would always face the point. In the back quadrants the back of the object always faces the point.
This code corrects the flip effect so the front of the object always faces the point. I encountered it through trial-and-error so I don't really know what's happening!
rotx = Math.atan2( y, z );
if (z >= 0) {
roty = -Math.atan2( x * Math.cos(rotx), z );
}else{
roty = Math.atan2( x * Math.cos(rotx), -z );
}

Rich Seller's answer shows you how to rotate a point from one 3-D coordinate system to another system, given a set of Euler angles describing the rotation between the two coordinate systems.
But it sounds like you're asking for something different:
You have: 3-D coordinates of a single point
You want: a set of Euler angles
If that's what you're asking for, you don't have enough information. To find the Euler angles,
you'd need coordinates of at least two points, in both coordinate systems, to determine the rotation from one coordinate system into the other.
You should also be aware that Euler angles can be ambiguous: Rich's answer assumes the
rotations are applied to Z, then X', then Z', but that's not standardized. If you have to interoperate with some other code using Euler angles, you need to make sure you're using the same convention.
You might want to consider using rotation matrices or quaternions instead of Euler angles.

This series of rotations will give you what you're asking for:
About X: 0
About Y: atan2(z, x)
About Z: atan2(y, sqrt(x*x + z*z))
I cannot tell you what these are in terms of "roll", "pitch" and "yaw" unless you first define how you are using these terms. You are not using them in the standard way.
EDIT:
All right, then try this:
About X: -atan2(y, z)
About Y: atan2(x, sqrt(y*y + z*z))
About Z: 0

Talking about the rotation of axes, I think step 3 should have been the rotation of X'-, Y''-, and Z'-axes about the Y''-axis.

Related

Maths in programming. Get the angle at point on a parabola

I'm learning Unity3d + some basic maths I've forgotten by messing around.
Heres what I'm doing now..
As you can probably tell the sides of this shape form a parabola.
The distance they are out from the centre is the base radius + the height squared * by a constant (0.05 in this image)
The code generating this is very simple..
for (int changer = 1; changer > -2; changer-=2) {
Vector3 newPos = new Vector3(
transform.position.x
,transform.position.y + currentheight*changer
,transform.position.z - RadiusAtZero -(Mathf.Pow(currentheight,2)*CurveMultiplier)
);
var newFleck = Instantiate(Fleck, newPos, Quaternion.identity)as GameObject;
newFleck.transform.RotateAround(transform.position,Vector3.up,angle*changer);
FleckList.Add(newFleck );
}
Btw the for loop and 'changer' mirror everything so 'currentheight' is really just the distance from the centreline of the parabola.
Anyway I'd like to make the cubes (or flecks as I've called them) be angled so that they are tangentional to the parabola I have made.
I need to determine the angle of a tangent to the parabola at particular point.
I found this
to find the line tangent to y=x^2 -3 at (1, -2) we can simultaneously solve
y=x^2 -3 and y+2=m(x-1) and set the discriminant equal to zero
But I dont know how to implement this. Also I reckon my 'CurveMultiplier' constant makes my parabola equation different from that one.
Can someone write some code that determines the angle? (and also maybe explain it)
Update.
Here is fixed version using the derivative of the equation. (Also I have changed from boxes to tetrahedrons and few other superficial things)
The easiest solution is to use a derivative for the parabolic equation.
In your picture then I'll assume Y is vertical, X horizontal, and Z in/out of the screen. Then the parabola being rotated, based upon your description, is:
f(h) = 0.05*h^2 + R
(h is height, R is base radius). If you imagine a plane containing the Y axis, you can rotate the plane around the Y axis at any angle and the dual parabola looks the same.
The derivative of a parabolic equation of the form f(x) = C*h^2 + R is f'(x) = 2*C*h, which is the slope of the tangent at h. In this specific case, that would be:
f'(h) = 0.1*h
Since the cross-sectional plane has an angle relative to X and Z axes, then that tangent will also have the same angular component (you have a rotated parabola).
Depending upon the units given for the constants in f(h), particularly the 0.05 value, you may have to adjust this for the correct results.

x,y,z polar coordinates in a sphere

I am currently using the following math to get the x,y,z coordinates with an assumed Hypotenuse of 150 and my known yaw pitch and roll.
float zPos = (float)Math.tan(Math.toRadians(rmPitch-90))*150;
float xPos = (float)Math.cos(Math.toRadians(90-rmYaw))*150;
float yPos = (float)Math.cos(Math.toRadians(rmYaw))*150;
Assuming a viewer is standing at 0,0,0 and looking up at P. Q is 150 units away and i know the yaw and pitch of the view);
my math seems to work fine until i my pitch gets closer to straight up and down, at which point i realize that x and y need to take into account z in some manner.. please help
The easiest way to think about this is to think about a unit circle embedded in the plane containing P and the z axis. There, the vector Q is one side of the triangle, and PQ is the other. So, armed with that picture in your head:
First, z should be cos(pitch), not tan(pitch). Then, to correct x, y for the pitch, multiply them by sin(pitch).
Note that this is assuming you did intend for phi/pitch to be the angle between the z axis and the vector (and not the more standard angle between the x-y plane and the vector).
There is a problem with Euler angles (i.e. the Azimuth, Pitch, Roll system of coordindates) when pitch is plus/minus 90 degrees. That seems to be what you're referring to, and its known as Grimbal Lock. I recently asked and then answered a question about this on math.stackexchange.com, so perhaps this link will help.
Since phi is the angle between P and the z-axis, the z-coordinate should be rho*cos(phi). Similarly x is rho*sin(phi)cos(theta), y is rho*sin(phi)sin(theta)
dsharlet, using tan(pitch-90) seemed to be working how i expected... if i switch to cos i have to make it (pitch+180) but following your advise the following code seems to work.. i must be flipping and axis or something weird.. can you speak a bit to the difference between tan(pitch-90) and cos(pitch+180)
float zPos = (float)Math.cos(Math.toRadians(rmPitch+180))*radius;
float xPos = (float)Math.cos(Math.toRadians(90-rmYaw))*(float)Math.sin(Math.toRadians(rmPitch))*radius;
float yPos = (float)Math.cos(Math.toRadians(rmYaw))*(float)Math.sin(Math.toRadians(rmPitch))*radius;

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;

Given start point, angles in each rotational axis and a direction, calculate end point

I have a start point in 3D coordinates, e.g. (0,0,0).
I have the direction I am pointing, represented by three angles - one for each angle of rotation (rotation in X, rotation in Y, rotation in Z) (for the sake of the example let's assume I'm one of those old logo turtles with a pen) and the distance I will travel in the direction I am pointing.
How would I go about calculating the end point coordinates?
I know for a 2D system it would be simple:
new_x = old_x + cos(angle) * distance
new_y = old_y + sin(angle) * distance
but I can't work out how to apply this to 3 dimensions
I suppose another way of thinking about this would be trying to find a point on the surface of a sphere, knowing the direction you're pointing and the sphere's radius.
First of all, for positioning a point in 3D you only need two angles (just like you only needed one in 2D)
Secondly, for various reasons (slow cos&sin, gimbal lock, ...) you might want to store the direction as a vector in the first place and avoid angles alltogether.
Anyway, Assuming direction is initially z aligned, then rotated around x axis followed by rotation around y axis.
x=x0 + distance * cos (angleZ) * sin (angleY)
Y=y0 + distance * sin (Anglez)
Z=z0 + distance * cos (angleZ) * cos (angleY)
Based in the three angles you have to construct the 3x3 rotation matrix. Then each column of the matrix represents the local x, y and z directions. If you have a local direction you want to move by, then multiply the 3x3 rotation with the direction vector to get the result in global coordinates.
I made a little intro to 3D coordinate transformations that I think will answer your question.
3D Coordinates
First, it is strange to have three angles to represent the direction -- two would be enough. Second, the result depends on the order in which you turn about the respective axes. Rotations about different axes do not commute.
Possibly you are simply looking for the conversion between spherical and Cartesian coordinates.

Triangle mathematics for game development

I'm trying to make a triangle (isosceles triangle) to move around the screen and at the same time slightly rotate it when a user presses a directional key (like right or left).
I would like the nose (top point) of the triangle to lead the triangle at all times. (Like that old asteroids game).
My problem is with the maths behind this. At every X time interval, I want the triangle to move in "some direction", I need help finding this direction (x and y increments/decrements).
I can find the center point (Centroid) of the triangle, and I have the top most x an y points, so I have a line vector to work with, but not a clue as to "how" to work with it.
I think it has something to do with the old Sin and Cos methods and the amount (angle) that the triangle has been rotated, but I'm a bit rusty on that stuff.
Any help is greatly appreciated.
The arctangent (inverse tangent) of vy/vx, where vx and vy are the components of your (centroid->tip) vector, gives you the angle the vector is facing.
The classical arctangent gives you an angle normalized to -90° < r < +90° degrees, however, so you have to add or subtract 90 degrees from the result depending on the sign of the result and the sign of vx.
Luckily, your standard library should proive an atan2() function that takes vx and vy seperately as parameters, and returns you an angle between 0° and 360°, or -180° and +180° degrees. It will also deal with the special case where vx=0, which would result in a division by zero if you were not careful.
See http://www.arctangent.net/atan.html or just search for "arctangent".
Edit: I've used degrees in my post for clarity, but Java and many other languages/libraries work in radians where 180° = π.
You can also just add vx and vy to the triangle's points to make it move in the "forward" direction, but make sure that the vector is normalized (vx² + vy² = 1), else the speed will depend on your triangle's size.
#Mark:
I've tried writing a primer on vectors, coordinates, points and angles in this answer box twice, but changed my mind on both occasions because it would take too long and I'm sure there are many tutorials out there explaining stuff better than I ever can.
Your centroid and "tip" coordinates are not vectors; that is to say, there is nothing to be gained from thinking of them as vectors.
The vector you want, vForward = pTip - pCentroid, can be calculated by subtracting the coordinates of the "tip" corner from the centroid point. The atan2() of this vector, i.e. atan2(tipY-centY, tipX-centX), gives you the angle your triangle is "facing".
As for what it's relative to, it doesn't matter. Your library will probably use the convention that the increasing X axis (---> the right/east direction on presumably all the 2D graphs you've seen) is 0° or 0π. The increasing Y (top, north) direction will correspond to 90° or (1/2)π.
It seems to me that you need to store the rotation angle of the triangle and possibly it's current speed.
x' = x + speed * cos(angle)
y' = y + speed * sin(angle)
Note that angle is in radians, not degrees!
Radians = Degrees * RadiansInACircle / DegreesInACircle
RadiansInACircle = 2 * Pi
DegressInACircle = 360
For the locations of the vertices, each is located at a certain distance and angle from the center. Add the current rotation angle before doing this calculation. It's the same math as for figuring the movement.
Here's some more:
Vectors represent displacement. Displacement, translation, movement or whatever you want to call it, is meaningless without a starting point, that's why I referred to the "forward" vector above as "from the centroid," and that's why the "centroid vector," the vector with the x/y components of the centroid point doesn't make sense. Those components give you the displacement of the centroid point from the origin. In other words, pOrigin + vCentroid = pCentroid. If you start from the 0 point, then add a vector representing the centroid point's displacement, you get the centroid point.
Note that:
vector + vector = vector
(addition of two displacements gives you a third, different displacement)
point + vector = point
(moving/displacing a point gives you another point)
point + point = ???
(adding two points doesn't make sense; however:)
point - point = vector
(the difference of two points is the displacement between them)
Now, these displacements can be thought of in (at least) two different ways. The one you're already familiar with is the rectangular (x, y) system, where the two components of a vector represent the displacement in the x and y directions, respectively. However, you can also use polar coordinates, (r, Θ). Here, Θ represents the direction of the displacement (in angles relative to an arbitary zero angle) and r, the distance.
Take the (1, 1) vector, for example. It represents a movement one unit to the right and one unit upwards in the coordinate system we're all used to seeing. The polar equivalent of this vector would be (1.414, 45°); the same movement, but represented as a "displacement of 1.414 units in the 45°-angle direction. (Again, using a convenient polar coordinate system where the East direction is 0° and angles increase counter-clockwise.)
The relationship between polar and rectangular coordinates are:
Θ = atan2(y, x)
r = sqrt(x²+y²) (now do you see where the right triangle comes in?)
and conversely,
x = r * cos(Θ)
y = r * sin(Θ)
Now, since a line segment drawn from your triangle's centroid to the "tip" corner would represent the direction your triangle is "facing," if we were to obtain a vector parallel to that line (e.g. vForward = pTip - pCentroid), that vector's Θ-coordinate would correspond to the angle that your triangle is facing.
Take the (1, 1) vector again. If this was vForward, then that would have meant that your "tip" point's x and y coordinates were both 1 more than those of your centroid. Let's say the centroid is on (10, 10). That puts the "tip" corner over at (11, 11). (Remember, pTip = pCentroid + vForward by adding "+ pCentroid" to both sides of the previous equation.) Now in which direction is this triangle facing? 45°, right? That's the Θ-coordinate of our (1, 1) vector!
keep the centroid at the origin. use the vector from the centroid to the nose as the direction vector. http://en.wikipedia.org/wiki/Coordinate_rotation#Two_dimensions will rotate this vector. construct the other two points from this vector. translate the three points to where they are on the screen and draw.
double v; // velocity
double theta; // direction of travel (angle)
double dt; // time elapsed
// To compute increments
double dx = v*dt*cos(theta);
double dy = v*dt*sin(theta);
// To compute position of the top of the triangle
double size; // distance between centroid and top
double top_x = x + size*cos(theta);
double top_y = y + size*sin(theta);
I can see that I need to apply the common 2d rotation formulas to my triangle to get my result, Im just having a little bit of trouble with the relationships between the different components here.
aib, stated that:
The arctangent (inverse tangent) of
vy/vx, where vx and vy are the
components of your (centroid->tip)
vector, gives you the angle the vector
is facing.
Is vx and vy the x and y coords of the centriod or the tip? I think Im getting confused as to the terminology of a "vector" here. I was under the impression that a Vector was just a point in 2d (in this case) space that represented direction.
So in this case, how is the vector of the centroid->tip calculated? Is it just the centriod?
meyahoocomlorenpechtel stated:
It seems to me that you need to store
the rotation angle of the triangle and
possibly it's current speed.
What is the rotation angle relative to? The origin of the triangle, or the game window itself? Also, for future rotations, is the angle the angle from the last rotation or the original position of the triangle?
Thanks all for the help so far, I really appreciate it!
you will want the topmost vertex to be the centroid in order to achieve the desired effect.
First, I would start with the centroid rather than calculate it. You know the position of the centroid and the angle of rotation of the triangle, I would use this to calculate the locations of the verticies. (I apologize in advance for any syntax errors, I have just started to dabble in Java.)
//starting point
double tip_x = 10;
double tip_y = 10;
should be
double center_x = 10;
double center_y = 10;
//triangle details
int width = 6; //base
int height = 9;
should be an array of 3 angle, distance pairs.
angle = rotation_angle + vertex[1].angle;
dist = vertex[1].distance;
p1_x = center_x + math.cos(angle) * dist;
p1_y = center_y - math.sin(angle) * dist;
// and the same for the other two points
Note that I am subtracting the Y distance. You're being tripped up by the fact that screen space is inverted. In our minds Y increases as you go up--but screen coordinates don't work that way.
The math is a lot simpler if you track things as position and rotation angle rather than deriving the rotation angle.
Also, in your final piece of code you're modifying the location by the rotation angle. The result will be that your ship turns by the rotation angle every update cycle. I think the objective is something like Asteroids, not a cat chasing it's tail!

Resources