- Home /
how do I compare Quaternions?
I'm trying to compare two rotations. I'm checking to see if they are equal like this
if(rot==prevrot){
dosomething;
}
else{
dosomethingelse
}
Here's an example of the Debug.Log of two that were not equal according to the if statement. But look at the output...
prevrot=(0.0, 0.0, 0.0, 1.0) rot=(0.0, 0.0, 0.0, 1.0)
Why doesn't the Debug.Log output match the if statement?
Answer by ScroodgeM · Jul 22, 2012 at 06:32 AM
internally quaternion stored as 4 floats, so two quaternions can be different but both logs as (0,0,0,1) due to precision of float.
http://docs.unity3d.com/Documentation/ScriptReference/Quaternion.Angle.html
use this to compare two rotations by angle between them - for example if angle is lower then some threshold then these quaternions are same
Answer by silentreaver · Oct 09, 2019 at 10:45 AM
Just to formalise @reddtoric's answer into something you can use:
public static bool Approximately(this Quaternion quatA, Quaternion value, float acceptableRange)
{
return 1 - Mathf.Abs(Quaternion.Dot(quatA, value)) < acceptableRange;
}
That'll extend the Quaternion class so you can use it in future to compare two quaternions
Answer by SoxwareInteractive · Jan 02, 2017 at 09:23 AM
You should note that Quaternion.Angle() will sometimes not return 0 even though the Quaternions are an exact copy of each other. The same applies for the "==" equality operator of quaternions.
Quaterion q2 = q1;
if (q1 == q2)
{
Debug.Log("That's what you would expect!");
}
else
{
// Please note that the result depends on the value q1 has.
Debug.Log("That's what happens!");
}
This is due to floating point errors introduced by the Mathf() functions used in the implementations of Quaternion.Angle() and the equality operator. The equality operator for example uses the dot product of the two quaternions to compare them.
The Equal() method on the other hand compares each element of the quaterion (x, y, z, w) binary wise. So when you have exact copies it is guaranteed to return true. But it will have troubles if the 2 quaternions are really close to each other but due to some floating point rounding errors they may be binary wise not exactly the same. That's when the equality operator does a better job.
So something like this would combine the best of both:
public bool QuaternionsEqual(Quaternion q1, Quaternion q2)
{
return (q1.Equals(q2) || (q1 == q2));
}
Answer by winxalex · Sep 30, 2017 at 06:14 PM
public static bool Approximately(Quaternion val, Quaternion about, float range) {
return Quaternion.Dot(val,about) > 1f-range;
}
This worked for me, although I did have to use the absolute value of the dot product as it sometimes returns the opposite sign
Answer by reddtoric · Jul 12, 2019 at 04:46 AM
Improvement on winxalex's answer
More understandable variable names and some explanation.
If anyone has any suggestions to a better solution for the precision variable, please chime in. For example, a float representing the angle of acceptance across all 3 axes rather than what it is now. (I usually use 0.01f or 0.001f to check approx. but in this case for 1 degree on 1 axis results in 0.0000004f difference which is very small and I don't go that small but the results isn't what I want.
If okay with 1 degree difference on 1 axis:
bool isQ1Q2Exact = isApproximate(q1, q2, 0.0000004f);
public static bool isApproximate(Quaternion q1, Quaternion q2, float precision)
{
return Mathf.Abs(Quaternion.Dot(q1, q2)) >= 1 - precision;
}
Quaternion.Dot method returns a value between 1 and -1. If it returns 1 or -1, it means the 2 quaternions are "exact". It's like 0 degrees and 360 degrees. If it returns 0, it is far from exact. Closer to Mathf.Abs(1) means more exact. Closer to 0 means further from exact.
Quaternion zero = Quaternion.identity;
// returns 0.9999996 => precision = 0.0000004
Quaternion tenthDegree1Axis = Quaternion.Euler(0.1f, 0, 0);
float veryClose = Quaternion.Dot(zero, tenthDegree1Axis);
// returns 0.9999619 => precision = 0.0000381
Quaternion oneDegree1Axis = Quaternion.Euler(1f, 0, 0);
float alsoVeryClose = Quaternion.Dot(zero, oneDegree1Axis);
// returns -4.371139E-08 ≈ 0
Quaternion _180Degree1Axis = Quaternion.Euler(180f, 0, 0);
float farFromClose = Quaternion.Dot(zero, _180Degree1Axis);
// returns => 0.5
Quaternion _90Degree2Axis = Quaternion.Euler(90f, 90f, 0);
float alsoFarFromClose = Quaternion.Dot(zero, _90Degree2Axis);
Your answer
Follow this Question
Related Questions
preventing errors by calling Debug.Log on a property 1 Answer
How to compare rotation position with an Int value (2D game) 1 Answer
Manual Quaternions 3 Answers
Child versus Parent rotations 3 Answers
3D nested Turret Prefab Rotation 1 Answer