Quaternion.Angle() unexpected output. Compare to Vector3.Angle() which produces correct values.
I'm struggling to understand output from Quaternion.Angle(). The docs say it "Returns the angle in degrees between two rotations a and b." But it produces a value that I do not expect. To illustrate this, below is a script that applies the same rotations to Vector3s (that represent a direction, not euler angles.) and uses Vector3.Angle() in a way that produces the correct result, and that I think should also be returned from Quaternion.Angle().
Why are qAngle and vAngle_Q different? or qAngle_Down and vAngle_Q_Down?
Am I misunderstanding what Quaternion.Angle() does, or is it broken?
using System;
using System.Collections.Generic;
using UnityEngine;
namespace WFR.VRPack
{
class QTest : MonoBehaviour
{
[SerializeField] private Vector3 eulerAnglesInput = new Vector3(45, 30, 0);
[ContextMenu("Q Test")]
private void DoQTest()
{
string stringLog = "";
stringLog +=
"identity:" + "\n" +
"A:" + Quaternion.identity.eulerAngles + "\n" +
"Q:" + Quaternion.identity + "\n" +
"V:" + Quaternion.identity * Vector3.forward + "\n" +
"";
Quaternion quatFromEuler = Quaternion.Euler(eulerAnglesInput);
Vector3 vecFromQuat = quatFromEuler * Vector3.forward;
Vector3 vecFromEuler = Vector3.forward;
// Unity uses euler order Y - X - Z.
// Manually perform Y rotation.
vecFromEuler = Quaternion.AngleAxis(eulerAnglesInput.y, Vector3.up) * vecFromEuler;
// Manually perform X rotation. Unity is a left handed coordinate system, so Vector3.up comes first.
Vector3 newVector3Right = Vector3.Cross(Vector3.up, vecFromEuler);
vecFromEuler = Quaternion.AngleAxis(eulerAnglesInput.x, newVector3Right) * vecFromEuler;
// Manually perform z rotation.
// Note: In this simple test without a gameobject where the vector represents forward, it makes no difference to rotate around forward.
Vector3 newVector3Forward = vecFromEuler;
vecFromEuler = Quaternion.AngleAxis(eulerAnglesInput.z, newVector3Forward) * vecFromEuler;
stringLog += "\n";
stringLog +=
"input:" + "\n" +
"eulerAnglesInput: " + eulerAnglesInput + "\n" +
"quatFromEuler: " + quatFromEuler + "\n" +
"vecFromQuat: " + vecFromQuat + "\n" +
"vecFromEuler: " + vecFromEuler + "\n" +
"";
float qAngle = Quaternion.Angle(Quaternion.identity, quatFromEuler);
float vAngle_Q = Vector3.Angle(Quaternion.identity * Vector3.forward, vecFromQuat);
float vAngle_E = Vector3.Angle(Quaternion.identity * Vector3.forward, vecFromEuler);
stringLog += "\n";
stringLog +=
"output:" + "\n" +
"qAngle: " + qAngle + "\n" +
"vAngle_Q: " + vAngle_Q + "\n" +
"vAngle_E: " + vAngle_E + "\n" +
"";
Quaternion quatDown = Quaternion.FromToRotation(Vector3.forward, Vector3.down);
float qAngle_Down = Quaternion.Angle(quatDown, quatFromEuler);
float vAngle_Q_Down = Vector3.Angle(quatDown * Vector3.forward, vecFromQuat);
float vAngle_E_Down = Vector3.Angle(quatDown * Vector3.forward, vecFromEuler);
stringLog += "\n";
stringLog +=
"output:" + "\n" +
"qAngle_Down: " + qAngle_Down + "\n" +
"vAngle_Q_Down: " + vAngle_Q_Down + "\n" +
"vAngle_E_Down: " + vAngle_E_Down + "\n" +
"";
Debug.Log(stringLog);
}
}
}
sample output:
identity:
A:(0.0, 0.0, 0.0)
Q:(0.0, 0.0, 0.0, 1.0)
V:(0.0, 0.0, 1.0)
input:
eulerAnglesInput: (45.0, 30.0, 0.0)
quatFromEuler: (0.4, 0.2, -0.1, 0.9)
vecFromQuat: (0.4, -0.7, 0.6)
vecFromEuler: (0.4, -0.7, 0.6)
output:
qAngle: 53.64744
vAngle_Q: 52.23875
vAngle_E: 52.23876
output:
qAngle_Down: 53.64743
vAngle_Q_Down: 45
vAngle_E_Down: 45
Your answer
Follow this Question
Related Questions
Quaternions to EulerAngles weird x-axis interaction 1 Answer
Avoiding Gimbal Lock with Vector3 EulerAngles and Applying Torque 0 Answers
How to add to a rotation angle? 0 Answers
Any simple ways of keeping track of simple rotation? 2 Answers
Order of the rotations around the axes with Euler angles 1 Answer