Starting with a 3D rotation matrix, how do I compute the Heading, Pitch, and Roll values that the various 3D symbols desire?
More Detail:
So, given a rotation matrix that looks like this:
[ M00, M01, M02 ]
[ M10, M11, M12 ]
[ M20, M21, M22 ]
And I'm trying to create a ConeMarkerSymbol.
First I tried this computation (source😞
Heading = Math.Atan2(M10, M00) * (180 / Math.PI)
Pitch = Math.Atan2(-M20, Math.Sqrt(M21 * M21 + M22 * M22)) * (180 / Math.PI)
Roll = Math.Atan2(M21, M22) * (180 / Math.PI)
But that didn't yield the correct results. Then I tried this computation (source😞
Heading = Math.Atan2(M20, M21) * (180 / Math.PI)
Pitch = Math.Acos(M22) * (180 / Math.PI)
Roll = -Math.Atan2(M02, M12) * (180 / Math.PI)
But that didn't get it either. What's the correct calculation?
Even More Details:
I'm doing all of my business logic using a state planar projection and then converting it to WGS84 to display it in a SceneView. The GraphicsLayer is using a SurfacePlacement of Absolute and I'm able to place all of the objects correctly, but getting them oriented properly is a challenge. I am able to render the objects in an DirectX window with the correct orientation, so I think the rotation matrix is correct,
I'm using C# and the .NET runtime SDK. Thanks in advance!
Solved! Go to Solution.
Calculation:
public class TaitBryanAngles
{
public static TaitBryanAngles FromMatrix(Matrix3D matrix)
{
double psi, theta, phi;
if (!(matrix.M02 == 1 || matrix.M02 == -1))
{
theta = -Math.Asin(matrix.M02);
psi = Math.Atan2(matrix.M12, matrix.M22);
phi = Math.Atan2(matrix.M01, matrix.M00);
}
else
{
phi = 0.0;
if (matrix.M02 == -1)
{
theta = Math.PI / 2;
psi = phi + Math.Atan2(matrix.M10, matrix.M20);
}
else
{
theta = -Math.PI / 2;
psi = -phi + Math.Atan2(-matrix.M10, -matrix.M20);
}
}
return new TaitBryanAngles(phi * 180 / Math.PI, psi * 180 / Math.PI, theta * 180 / Math.PI);
}
public TaitBryanAngles(double heading, double roll, double pitch)
{
Heading = heading;
Roll = roll;
Pitch = pitch;
}
public double Heading { get; }
public double Roll { get; }
public double Pitch { get; }
}
Explanation:
The values that are expected are the Tait-Bryan XYZ angles. In order to derive these angles, I followed this paper here:
http://staff.city.ac.uk/~sbbh653/publications/euler.pdf
In that paper, they derive the ZYX angles. However, the API expects the XYZ angles. So, I followed the same procedure with a different order of the rotation matrix multiplication and arrived at the answer given above. Hopefully this is helpful for someone else!
Calculation:
public class TaitBryanAngles
{
public static TaitBryanAngles FromMatrix(Matrix3D matrix)
{
double psi, theta, phi;
if (!(matrix.M02 == 1 || matrix.M02 == -1))
{
theta = -Math.Asin(matrix.M02);
psi = Math.Atan2(matrix.M12, matrix.M22);
phi = Math.Atan2(matrix.M01, matrix.M00);
}
else
{
phi = 0.0;
if (matrix.M02 == -1)
{
theta = Math.PI / 2;
psi = phi + Math.Atan2(matrix.M10, matrix.M20);
}
else
{
theta = -Math.PI / 2;
psi = -phi + Math.Atan2(-matrix.M10, -matrix.M20);
}
}
return new TaitBryanAngles(phi * 180 / Math.PI, psi * 180 / Math.PI, theta * 180 / Math.PI);
}
public TaitBryanAngles(double heading, double roll, double pitch)
{
Heading = heading;
Roll = roll;
Pitch = pitch;
}
public double Heading { get; }
public double Roll { get; }
public double Pitch { get; }
}
Explanation:
The values that are expected are the Tait-Bryan XYZ angles. In order to derive these angles, I followed this paper here:
http://staff.city.ac.uk/~sbbh653/publications/euler.pdf
In that paper, they derive the ZYX angles. However, the API expects the XYZ angles. So, I followed the same procedure with a different order of the rotation matrix multiplication and arrived at the answer given above. Hopefully this is helpful for someone else!