Thanks Steve. That helps me clarify a bit. Here's what I've been able to reverse engineer (C# code):
struct Vector
{
public double X;
public double Y;
public double Z;
}
struct Quaternion
{
public double X;
public double Y;
public double Z;
public double W;
}
// http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/
static Quaternion Multiply(Quaternion a, Quaternion b)
{
return new Quaternion
{
X = a.X * b.W + a.Y * b.Z - a.Z * b.Y + a.W * b.X,
Y = -a.X * b.Z + a.Y * b.W + a.Z * b.X + a.W * b.Y,
Z = a.X * b.Y - a.Y * b.X + a.Z * b.W + a.W * b.Z,
W = -a.X * b.X - a.Y * b.Y - a.Z * b.Z + a.W * b.W,
};
}
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
static Quaternion ToQuaternionFromAxisAngle(Vector axis, double angleDegrees)
{
double sin = System.Math.Sin(angleDegrees * System.Math.PI / 180 / 2);
double cos = System.Math.Cos(angleDegrees * System.Math.PI / 180 / 2);
return new Quaternion
{
X = axis.X * sin,
Y = axis.Y * sin,
Z = axis.Z * sin,
W = cos,
};
}
static Quaternion ToQuaternionFromFigureAngles(double angleDegreesX, double angleDegreesY, double angleDegreesZ)
{
// Anim8or rotates clockwise instead of counter-clockwise (confirmed by Steve)
Quaternion quatX = ToQuaternionFromAxisAngle(new Vector { X = 1, Y = 0, Z = 0 }, -angleDegreesX);
Quaternion quatY = ToQuaternionFromAxisAngle(new Vector { X = 0, Y = 1, Z = 0 }, -angleDegreesY);
Quaternion quatZ = ToQuaternionFromAxisAngle(new Vector { X = 0, Y = 0, Z = 1 }, -angleDegreesZ);
// Anim8or seems to apply -Y then -X then -Z in figure mode
return Multiply(Multiply(quatY, quatX), quatZ);
}
static Quaternion ToQuaternionFromSequenceAngles(double angleDegreesX, double angleDegreesY, double angleDegreesZ)
{
// Anim8or rotates counter-clockwise
Quaternion quatX = ToQuaternionFromAxisAngle(new Vector { X = 1, Y = 0, Z = 0 }, angleDegreesX);
Quaternion quatY = ToQuaternionFromAxisAngle(new Vector { X = 0, Y = 1, Z = 0 }, angleDegreesY);
Quaternion quatZ = ToQuaternionFromAxisAngle(new Vector { X = 0, Y = 0, Z = 1 }, angleDegreesZ);
// Anim8or seems to apply X then Z then Y in sequence mode
return Multiply(Multiply(quatX, quatZ), quatY);
}
The above code is not optimized for clarity and simplicity. It looks like in Figure mode Anim8or actually uses -Y-X-Z Euler angles, that is a negative Y rotation is applied first then a negative X rotation then a negative Z rotation. It also looks like in Sequence mode Anim8or uses XZY Euler angles, that is a positive X rotation is applied first then a positive Z rotation then a positive Y rotation. This time I made sure all the signs are correct, and I tested X, Y, and Z angles. In my previous posts I was wrong in some of my statements since I ignored flipped signs and didn't test X rotations.
I will post corrections if I find that I made a mistake. Let me know if you think there is a problem with my sample code.