i have object that are receiving 4 transformations step by step at different location in the software. We use a CAD engine library so i have custom objects to do transformation.
The Situation
i need to recuperate from the matrix of one of the object translation and rotation so that i can create a new matrix later one from those rotation.
The Code (sample)
// create a transformation object, very simple 1 translation and 3 rotations
var t = new Translation(10, 15, 20)
* new Rotation(10d.ToRadian(), Vector3D.AxisX)
* new Rotation(10d.ToRadian(), Vector3D.AxisY)
* new Rotation(10d.ToRadian(), Vector3D.AxisZ);
// convert to the windows Matrix3D
var m1 = ToWindowsMatrix(t);
// decompose the matrix to get one random possibility of combinaison of rotation x, y and Z to obtain the same result
var rx = Math.Atan2(m1.M32, m1.M33).ToDegree();
var ry = Math.Atan2(-m1.M31, Math.Sqrt(Math.Pow(m1.M32, 2) + Math.Pow(m1.M33, 2))).ToDegree();
var rz = Math.Atan2(m1.M21, m1.M11).ToDegree();
Source for formula to get rotation x,y and z:
// create a transformation object, in the same order, hardcoded translate but use the computed equivalent rotation x,y and z
var t2 = new Translation(10, 15, 20)
* new Rotation(rx.ToRadian(), Vector3D.AxisX)
* new Rotation(ry.ToRadian(), Vector3D.AxisY)
* new Rotation(rz.ToRadian(), Vector3D.AxisZ);
// convert to the windows Matrix3D
var m2 = ToWindowsMatrix(t2);
The Problem
if i take a 3d object and apply the transformation of t2 the rotation of the object is completely off compared to what t1 gives.
I absolutely need to get decomposed value as the whole system display the objects using 6 parameters (origin x,y,z and rotation x,y,z) which then create an identity matrix and apply a translate and 3 rotate.
I have been working of this for around 7 hours and i figured out that if i ONLY apply a single rotation on t1 ie "rotation X 90" and on t2 i still apply ALL rotations with the computed value the rotation DO match. So i have the feeling that the order of transformation need to be in a specific order but i cannot change it. So it mean i need to get the rotation x,y and z so that by applying Translate * RotX * RotY * RotZ it matches.
Anything i might have missed ? i have found some mathlab samples too but formula are all the same so far.
Figured out after a while how to get the XYZ rotation of a matrix. The problem is that the Euler angles return in a ZYX rotation. More operation need to be performed in order to extract the XYZ from it.
public static double[] GetXYZRotation(this Matrix3D matrix)
{
// get the euler angles
var eulerX = Math.Atan2(matrix.M32, matrix.M33).ToDegree();
var eulerY = Math.Atan2(-matrix.M31, Math.Sqrt(Math.Pow(matrix.M32, 2) + Math.Pow(matrix.M33, 2))).ToDegree();
var eulerZ = Math.Atan2(matrix.M21, matrix.M11).ToDegree();
// create transformation of the euler angle to get a matrix. euler rotation is ZYX
var eulerMatrix = (new Translation(matrix.OffsetX,matrix.OffsetY,matrix.OffsetZ)
* new Rotation(eulerZ.ToRadian(), Vector3D.AxisZ)
* new Rotation(eulerY.ToRadian(), Vector3D.AxisY)
* new Rotation(eulerX.ToRadian(), Vector3D.AxisX)).ToWindowsMatrix();
// get the Alpha Beta and Gamma of the euler matrix
var beta = Math.Atan2(eulerMatrix.M13, Math.Sqrt(Math.Pow(eulerMatrix.M11, 2d) + Math.Pow(-eulerMatrix.M12, 2d)));
var alpha = Math.Atan2(-(eulerMatrix.M23 / Math.Cos(beta)), eulerMatrix.M33 / Math.Cos(beta));
var gamma = Math.Atan2(-(eulerMatrix.M12 / Math.Cos(beta)), eulerMatrix.M11 / Math.Cos(beta));
// get the XYZ rotation per euler computed alpha beta gamma
var rotationX = alpha.ToDegree();
var rotationY = beta.ToDegree();
var rotationZ = gamma.ToDegree();
return new[] { rotationX, rotationY, rotationZ };
}
So i needed to compute the matrix applying the euler angle in the reverse order : Z then Y then X and from there calculate the Alpha, Beta and Gamma which retreive a XYZ rotation values
Related
I've already figured out how to make a 3D trajectory using a start point and an angle.
However, I am trying to make a trajectory from a start point, an end point, and a height.
I tried taking the approach of a parabola on a 2D plane in a 3D space. I calculated the Prabola's A, B, and C values as well as the plane it's on given 3 points on the Parabola.
However, I've had a few complications with this sort of calculation, I assume it has to do with the inability to properly calculate a Z-axis without a plane but I cannot tell.
Other than a 2D parabola on a plane google did not provide another possible answer and a 3D trajectory yields a formula using a start point, an angle, and a power multiplier.
Is there any way to calculate a 3D trajectory given the start point, end point, and height?
Appreciating your help
Edit:
My code to calculate a parabola using 3 points (in case someone would like to know how I've done that and perhaps fix what I've done wrong)
public Parabola(Vector3 pa, Vector3 pb, Vector3 pc)
{
this.pa = pa;
this.pc = pc;
float a1 = -pa.x * pa.x + pb.x * pb.x, b1 = -pa.x + pb.x, c1 = -pa.y + pb.y;
float a2 = -pb.x * pb.x + pc.x * pc.x, b2 = -pb.x + pc.x, c2 = -pb.y + pc.y;
float bm = -(b2 / b1), a3 = bm * a1 + a2, c3 = bm * c1 + c2;
float a = c3 / a3, b = (c1 - a1 * a) / b1, c = pa.y - a * pa.x * pa.x - b * pa.x;
this.a = a; this.b = b; this.c = c;
plane = Vector3.Cross(pb - pa, pc - pa);
}
public Vector3 GetPoint(float x)
{
float angle = Mathf.Atan2(pc.z - pa.z, pc.x - pa.x) * Mathf.Rad2Deg;
float xs = Mathf.Cos(angle * Mathf.Deg2Rad) * x, zs = Mathf.Sin(angle * Mathf.Deg2Rad) * x;
return new Vector3(xs, a * x * x + b * x + c, zs);
}
public Vector3 ProjectOn(float x) => Vector3.ProjectOnPlane(GetPoint(x), plane);
The result looks ok when it's only on 2 Axis, but not 3.
here are 2 images for demonstration:
Looking at the second image, the parabola seems to be correct aside from being scaled incorrectly. Let's take a look at your code.
public Vector3 GetPoint(float x)
{
float angle = Mathf.Atan2(pc.z - pa.z, pc.x - pa.x) * Mathf.Rad2Deg;
float xs = Mathf.Cos(angle * Mathf.Deg2Rad) * x, zs = Mathf.Sin(angle * Mathf.Deg2Rad) * x;
return new Vector3(xs, a * x * x + b * x + c, zs);
}
I'm making a lot of assumptions here, but it seems like x is meant as a value that goes from 0.0 to 1.0, representing the start and end of the parabola, respectively. If so, you are determining the X and Z coordinates of this point based exclusively on the sine/cosine of the angle and x. This means that the values xs and zs should only ever be able to be between -1 and 1, limiting yourself to the confines of the unit circle.
The values xs and zs look like they need to be scaled by a factor s calculated by measuring the 2D distance of the start and end points when projected onto the XZ plane. This should stretch the parabola just enough to reach the end point.
I found an answer, but it's kinda a workaround.
Before messing around with Parabolas in 3D, I messed around with linear equations in 3D.
Unlike parabolas, lines have a defined equation even in 3D(Pn = P0 + t x V)(Pn vector containing XYZ, P0 initial point containing XYZ, t float, V Vector3)
In addition, there's only ONE line that goes through 2 points, even in 3D.
I used that to make a trajectory that's made out of 2 points and a height.
I make a new point in the center of those two points and add the height value to the highest Y value of the points, thus creating an Apex.
then I use the same calculations as before to calculate the A, B, and C values that
a parabola with those 3 points would have had.
I made a method that takes in an X value and returns a Vector3 containing the point this X is on a linear equation, but instead, changing the vector's Y value based on the parabola's equation.
Practically creating an elevated line, I made something that looks and behaves perfectly like a 3D parabola.
If you're in C#, here is the code(images):
FIX!!
in the Linear's GetX(float x) method.
it should be:
public Vector3 GetX(float x) => => r0 + (x - r0.x)/v.x * v;
I made a slight mistake in the calculations which I noticed immediately and changed.
I have this formula to rotate around a sphere
double naX = o.Node.Angle.X;
double naY = o.Node.Angle.Y;
double x = o.DrawingPosition.X - 4.0 * Math.Cos(naX) * Math.Sin(naY);
double y = o.DrawingPosition.Y - 4.0 * Math.Sin(naX) * Math.Sin(naY);
double z = o.DrawingPosition.Z - 4.0 * Math.Cos(naY);
Where 4.0 is the radius to follow and o.DrawingPosition is the center
I want it to rotate along the transform x axis (I have a quaternion and a unit vector normalized for calculated for the Z -1 normal) but if I add offsets the angles won't match
naX += _rotationTicks;
naY += _rotationTicks;
For example, it will follow a infinite shaped trajectory, how can I calculate the correct rotation so it behaves like a perfect circle?
Edit:
I found this answer on Rotating body from spherical coordinates
But the main core difference is that both the XY rotation angles and the origins are arbitrary values, there is a forward vector calculated with a quaternion to determine facing like this:
var quat = System.Numerics.Quaternion.CreateFromYawPitchRoll((float)o.Node.Angle.X, (float)o.Node.Angle.Y, 0);
var dirVec = new Vector3(0, -1, 0).ToNumerics();
var downwards = quat.Multiply(dirVec); // it can be backwards, left, right, etc, changing the unit vector from dirVec
Thanks in advance.
I am developing a controller in WPF 3D (using C#) to be able to easily move and rotate a ProjectionCamera using Move()- and Pitch()-functions. My controller will become a Behavior<ProjectionCamera> that can be attached to a ProjectionCamera. In order to initialize the controller, I want to calculate the current rotation of the camera by looking at its current Up and Forward-vectors and compare them to the default camera orientation (Up = [0 1 0], Forward = [0 0 -1]). In other words, I want to calculate a rotation that will transform the camera default's orientation to its current one.
Ultimately, I want to express the rotation as a single Quaternion, but as an intermediate step I first calculate the Proper Euler Angle rotations of the form z-N-Z expressed as AxisAngleRotation3D-values, following the default definition of Wikipedia:
var alphaRotation = CalculateRotation(z, x, N);
var betaRotation = CalculateRotation(N, z, Z);
var gammaRotation = CalculateRotation(Z, N, X);
with
CalculateRotation(Vector3D axisOfRotation, Vector3D from, Vector3D to) : AxisAngleRotation3D
The Euler Angle Rotations seem to be calculated correctly, based on some unit tests. However, when I convert these rotations to a single Quaternion, the resulting Quaternion represents a rotation that differs from the Euler Angle Rotations, and I don't know why.
This is how I convert the Euler Angles to a single Quaternion:
var rotation =
new Quaternion(alphaRotation.Axis, alphaRotation.Angle) *
new Quaternion(betaRotation.Axis, betaRotation.Angle) *
new Quaternion(gammaRotation.Axis, gammaRotation.Angle);
For example, when I initialize a ProjectionCamera with an UpDirection of [1 0 0], meaning it's been rotated 90 degrees around its LookDirection axis ([0 0 -1]), the calculated Euler Angle Rotations are as follows:
alphaRotation --> 90 deg. around [0 1 0]
betaRotation --> 90 deg. around [0 0 -1]
gammaRotation --> -90 deg. around [1 0 0]
My test verifies that, when applied in order, these rotations will transform the default Up-vector ([0 1 0]) into the current Up-vector ([1 0 0]), effectively rotating it 90 deg. around the [0 0 -1] axis. (It's also reasonable straightforward to verify this by hand.)
However, when I apply the calculated QuaternionRotation to the default Up-vector, it is transformed to the vector [-1 0 0], which is obviously wrong. I have hard-coded these results within a Unit Test and got the same results:
[TestMethod]
public void ConversionTest()
{
var vector = new Vector3D(0, 1, 0);
var alphaRotation = new AxisAngleRotation3D(new Vector3D(0, 1, 0), 90);
var betaRotation = new AxisAngleRotation3D(new Vector3D(0, 0, -1), 90);
var gammaRotation = new AxisAngleRotation3D(new Vector3D(1, 0, 0), -90);
var a = new Quaternion(alphaRotation.Axis, alphaRotation.Angle);
var b = new Quaternion(betaRotation.Axis, betaRotation.Angle);
var c = new Quaternion(gammaRotation.Axis, gammaRotation.Angle);
var combinedRotation = a * b * c;
var x = Apply(vector, alphaRotation, betaRotation, gammaRotation);
var y = Apply(vector, combinedRotation);
}
When you run the test above, you will see that x gives you the expected vector ([1 0 0]) but y will be different, where it should be exactly the same rotation.
What am I missing?
I solved the issue. Apparantly, the order of the multiplication of the individual Quaternions should be reversed. So, to convert Euler Angles (or any set of rotations) into a single Quaternion in .NET you should do the following:
var rotation = gammaRotation * betaRotation * alphaRotation;
where rotation represents geometrically applying alphaRotation first, then betaRotation and finally gammaRotation. I just wished they had documented this, since the meaning of the order depends on the specific library you are working with....
I rotate a vector using the following code:
var newVectorX = Math.Cos(step) * normalizedVector.X
- Math.Sin(step) * normalizedVector.Y;
var newVectorY = - Math.Sin(step) * (normalizedVector.X )
+ Math.Cos(step) * normalizedVector.Y;
I tried to create a 2x2 matrix so I just can multiply my normalized vector with the matrix. The result would be the new rotated vector instead the coordinates.
Unfortunately System.Windows.Media.Matrix doesn't support 2x2 matrices. I couldn't find any implementation of this rotation matrix so far. How would you implement this?
Actually, System.Windows.Media.Matrix is exactly what you need. While it may seem that you want a 2x2 matrix, using a 3x3 matrix allows for translations too. Just use a System.Windows.Media.Matrix and ignore the part you don't need.
Matrix rotate = Matrix.Identity;
rotate.Rotate(step * 180 / Math.PI); // Rotate() takes degrees
Vector newVector = rotate.Transform(normalizedVector);
i do have 2 points on a 2d plane. one has already an vector that does determine in which direction it will move.
now i want to add a vector to this existing vector. so he accelerates in the direction of the other point.
to be a bit more clear, it is about 2 asteroids flying in space (only 2d) and gravitation should move them a bit closer to each other.
what i did build till now is this:
c = body.position - body2.position;
dist = c.Length();
acc = (body.masse * body2.masse) / (dist * dist);
xDist = body2.position.X - body.position.X;
yDist = body2.position.Y - body.position.Y;
direction = MathHelper.ToDegrees((float)(Math.Atan2((double)yDist, (double)xDist)));
body.velocity.Y = body.velocity.Y + (float)(Math.Sin(direction) * acc);
body.velocity.X = body.velocity.X + (float)(Math.Cos(direction) * acc);
in the moment the direction calculated is completly off. surely i am making just a stupid mistake, but i have no idea.
You need to pass your direction angle in in radians to Math.sin and Math.Cos (rather then in degree as you do in your smaple code).
see also:
http://msdn.microsoft.com/en-us/library/system.math.sin.aspx
The angle, a, must be in radians. Multiply by Math.PI/180 to convert degrees to radians.
My mechanics and linear algebra are a bit rusty but I think you should be able to do it without resorting to trigonometry. These formulae probably need tweaking, I'm not sure if I got u and -u mixed up.
Here it is in pseudo code
T is whatever time period you're iterating over
G is the gravitational constant
body1 starts with a velocity of v1
body2 starts with a velocity of v2
c = body.position - body2.position
c1 is a vector
use the vector c to get a vector of length 1 in the direction of the force
u = c1 / c.Length()
body1 should have an acceleration vector of a1 = G * body2mass/c.Length()^2 * (-u)
body2 should have an acceleration vector of a2 = G * body1mass/c.Length()^2 * (u)
body1 has a new velocity vector of v1 + a1/T
body2 has a new velocity vector of v1 + a2/T
rinse and repeat
Not completely sure what you try to do. Why can't you just use Vector2.Add(v1, v2)?