Suggestion for very accurately measuring the angle between two rotations?

I need to accurately measure the angle between two rotations, and I was wondering if anyone has any recommendations for me? I would like to get better than 0.001 precision.

Vector3.Angle() cannot seem to measure an angle less than 0.01978234 degrees.
Quaternion.Angle() cannot measure below 0.1631294 degrees.

I can get better results comparing eulers, but that can get a bit complicated with rotations on multiple axes (and it feels a bit dirty).

Any suggestions?

That’s…ominous. What happens when you try? And how did you determine those thresholds to 7 significant figures?

In any case, I’m sure there are standard mathematical equations for this. Based on some brief Googling, this looks like a promising answer (referring to the “most popular” answer in that thread). Wow, it even mentions that the simplest method for calculating the angle works poorly when the angle is small! Guess that’s a possible answer to why Unity’s provided functions are having trouble. The link also gives a more complex method that (it claims) works better for small angles.

(For reference, in Unity’s quaternions, the “scalar part” is called “w”, and x-y-z are the vector part.)

1 Like

Thanks for the response @Antistone . I will digest that link tomorrow. As for your question of how I came up with those numbers, I wrote a script that slowly rotated two objects relative to each other and printed the results of those two functions. Those were the first non-zero results to come up after a while of waiting, and they didn’t change for a while as the rotation slowly progressed.

Edit: to add, I was able to observe rotation changes both visually and in the transform inspector while those functions were returning 0’s.

@Antistone I looked through that link, and while I wish I could make sense of it and understand how to translate it into C# code, I guess it is a bit over my head. In particular, asin() operates on a float, and the link says:

theta = 2*asin(norm of the vector part of the quaternion product q1*q2^(-1))

I assumed “norm” means normalized or normal using the vector part of the quaternions multiplied together, but that is a vector in either case, not a float.

After some more looking, it appears that “norm” in math lingo means vector length. I tested this out and I do appear to be getting VERY small angles in radians with the following code:

        var product = Quaternion.Inverse(transform.rotation) * child.transform.rotation;
        Angle = 2*Mathf.Asin((new Vector3(product.x, product.y, product.z)).magnitude);

Thanks for your help!

EDIT: for what it is worth, this method was able to measure a rotation about an axis as small as (float.epsilon * 86) as 2.802597E-45 radians. That’s a pretty small angle. :slight_smile:

Ha! Just verified this approximation myself, and the quaternions do crap out a lot earlier in the precision chain.

EDIT: to be clear the quaternions clearly contain enough precision, but the Quaternion.Angle() method appears to have extreme limitations of less than 3 decimal places precision.

using UnityEngine;

public class TinyAngles : MonoBehaviour
{
    void Start ()
    {
        Vector3 v1 = Vector3.right;

        // vanishingly-smaller angles
        for (int i = 0; i <= 10; i++)
        {
            float angle = Mathf.Pow( 0.1f, i);

            var q = Quaternion.Euler( 0, 0, angle);

            Vector3 v2 = q * v1;

            // approximating very slender angles with arcsin
            float sin = Vector3.Distance( v1, v2) / v1.magnitude;

            float angle2 = Mathf.Asin( sin) * Mathf.Rad2Deg;

            float byQuat = Quaternion.Angle( Quaternion.identity, q);

            Debug.Log( "angle: " + angle + ", sin: " + sin + ", angle2: " + angle2 + ", byQuat: " + byQuat);
        }
    }
}

7409111--905948--Screen Shot 2021-08-11 at 8.24.46 AM.png

1 Like

I had the impression you were supposed to invert one of the quaternions before taking the product? That’s what I expected q2^(-1) meant.

Indeed, I discovered my error in testing when I rotated the parent. Updated the code. Thanks Again!