Page 1 of 1

Quaternion.toEuler() bug?

Posted: Sun May 01, 2011 7:01 am
by softbike
my english is poor....

i use quaternion to rotation...

the following is an example.

vector3df A(0.0, 0.0, 0.0);
Quaternion cQuat(A*core::DEGTORAD);
core::vector3df B;
cQuat.toEuler(B);
B*=core::RADTODEG;

if(B.x < 0.0f)
B.x += 360.0f;
if(B.y < 0.0f)
B.y += 360.0f;
if(B.z < 0.0f)
B.z += 360.0f;

this is Result...
A: 0.00, -0.00, 0.00
B: 0.00, -0.00, 0.00
-------------------------------------------------------- good
A: 90.00, -0.00, 0.00
B: 90.00, -0.00, 0.00
-------------------------------------------------------- good
A: 179.30, 359.18, 0.58
B: 179.30, 359.18, 0.58
-------------------------------------------------------- good
A: 355.82, 345.96, 273.26
B: 355.82, 345.96, 273.26
-------------------------------------------------------- good
A: 4.85, 359.30, 94.33
B: 4.85, 359.30, 94.33
-------------------------------------------------------- good
A: 0.00, 90.00, 270.00
B: 0.00, 90.00, 0.00
-------------------------------------------------------- bad
A: 3.23, 359.65, 10.21
B: 3.23, 359.65, 10.21
-------------------------------------------------------- good
A: 3.23, 359.65, 10.17
B: 3.23, 359.65, 10.17
-------------------------------------------------------- good
A: 0.00, 0.00, 24.04
B: 0.00, 0.00, 24.04
-------------------------------------------------------- good
A: 0.58, 4.36, 334.36
B: 0.58, 4.36, 334.36
-------------------------------------------------------- good
A: 0.00, 0.00, 103.99
B: 0.00, 0.00, 103.99
-------------------------------------------------------- good
A: 0.00, 90.00, 159.51
B: 176.21, 89.98, 335.72
-------------------------------------------------------- bad
A: 0.00, 90.00, 249.48
B: 235.26, 89.98, 124.74
-------------------------------------------------------- bad
A: 0.00, 75.55, 155.89
B: 0.00, 75.55, 155.89
-------------------------------------------------------- good
A: 11.64, 311.52, 345.35
B: 11.64, 311.52, 345.35
-------------------------------------------------------- good
A: 0.00, 0.00, 276.00
B: 0.00, 0.00, 276.00
-------------------------------------------------------- good
A: 90.95, 360.00, 360.00
B: 90.95, 0.00, 0.00
-------------------------------------------------------- good
A: 76.68, 1.11, 18.65
B: 76.68, 1.11, 18.65
-------------------------------------------------------- good
A: 360.00, 0.00, 295.00
B: 0.00, 0.00, 295.00
-------------------------------------------------------- good
A: 360.00, 0.00, 0.00
B: 0.00, 0.00, 0.00
-------------------------------------------------------- good
A: 351.10, 353.37, 9.27
B: 351.10, 353.37, 9.27
-------------------------------------------------------- good
A: 360.00, 360.00, 80.00
B: 0.00, 0.00, 80.00
-------------------------------------------------------- good
A: 360.00, 0.00, 80.00
B: 0.00, 0.00, 80.00
-------------------------------------------------------- good
A: 0.04, 359.99, 9.50
B: 0.04, 359.99, 9.50
-------------------------------------------------------- good
A: 360.00, 0.00, 80.00
B: 0.00, 0.00, 80.00
-------------------------------------------------------- good
A: 360.00, 360.00, 80.00
B: 0.00, 0.00, 80.00
-------------------------------------------------------- good
A: 12.10, 4.72, 11.24
B: 12.10, 4.72, 11.24
-------------------------------------------------------- good
A: 360.00, 360.00, 80.00
B: 0.00, 0.00, 80.00
-------------------------------------------------------- good
A: 0.00, 360.00, 80.00
B: 0.00, 0.00, 80.00
-------------------------------------------------------- good
A: 0.34, 89.58, 360.00
B: 0.34, 89.58, 0.00
-------------------------------------------------------- good
A: 0.00, 284.45, 155.89
B: 0.00, 284.45, 155.89
-------------------------------------------------------- good
A: 14.63, 48.72, 31.79
B: 14.63, 48.72, 31.79
-------------------------------------------------------- good
A: 0.00, 360.00, 286.29
B: 0.00, 0.00, 286.29
-------------------------------------------------------- good
A: 269.05, 0.00, 0.00
B: 269.05, 0.00, 0.00
-------------------------------------------------------- good
A: 283.32, 358.89, 18.65
B: 283.32, 358.89, 18.65
--------------------------------------------------------
A: 360.00, 360.00, 295.00
B: 0.00, 0.00, 295.00
--------------------------------------------------------
A: 0.00, 360.00, 0.00
B: 0.00, 0.00, 0.00
--------------------------------------------------------
A: 8.90, 6.63, 9.27
B: 8.90, 6.63, 9.27
--------------------------------------------------------
A: 360.00, 0.00, 80.00
B: 0.00, 0.00, 80.00
--------------------------------------------------------
A: 360.00, 360.00, 80.00
B: 0.00, 0.00, 80.00
--------------------------------------------------------
A: 359.96, 0.01, 9.50
B: 359.96, 0.01, 9.50
--------------------------------------------------------
A: 360.00, 0.00, 80.00
B: 0.00, 0.00, 80.00
--------------------------------------------------------
A: 360.00, 0.00, 80.00
B: 0.00, 0.00, 80.00
--------------------------------------------------------
A: 347.90, 355.28, 11.24
B: 347.90, 355.28, 11.24
--------------------------------------------------------
A: 0.00, 360.00, 80.00
B: 0.00, 0.00, 80.00
--------------------------------------------------------
A: 360.00, 0.00, 80.00
B: 0.00, 0.00, 80.00
--------------------------------------------------------
A: 0.01, 0.42, 90.38
B: 0.01, 0.42, 90.38
--------------------------------------------------------
A: 0.01, 0.42, 90.38
B: 0.01, 0.42, 90.38
--------------------------------------------------------
A: 115.52, 89.04, 205.51
B: 115.52, 89.04, 205.51
--------------------------------------------------------
A: 180.41, 359.94, 179.69
B: 180.41, 359.94, 179.69
--------------------------------------------------------
A: 0.00, 90.00, 269.91
B: 161.61, 90.00, 71.52
-------------------------------------------------------- bad
A: 0.00, 90.00, 269.91
B: 161.61, 90.00, 71.52
-------------------------------------------------------- bad
A: 180.92, 10.79, 144.53
B: 180.92, 10.79, 144.53
--------------------------------------------------------
A: 0.00, 0.00, 261.73
B: 0.00, 0.00, 261.73
--------------------------------------------------------
A: 358.24, 358.07, 342.82
B: 358.24, 358.07, 342.82
--------------------------------------------------------
A: 0.00, 0.00, 71.19
B: 0.00, 0.00, 71.19
--------------------------------------------------------
A: 90.01, 270.49, 360.00
B: 90.01, 270.49, 360.00
--------------------------------------------------------
A: 180.09, 270.06, 0.00
B: 180.09, 270.06, 0.00
--------------------------------------------------------
A: 173.58, 348.13, 132.25
B: 173.58, 348.13, 132.25
--------------------------------------------------------
A: 360.00, 0.00, 318.30
B: 0.00, 0.00, 318.30
--------------------------------------------------------
A: 359.78, 357.69, 7.52
B: 359.78, 357.69, 7.52
--------------------------------------------------------
A: 0.00, 360.00, 71.00
B: 0.00, 0.00, 71.00
--------------------------------------------------------
A: 269.99, 270.49, 360.00
B: 269.99, 270.49, 0.00
--------------------------------------------------------
A: 181.95, 270.03, 0.00
B: 181.95, 270.03, 360.00
--------------------------------------------------------

Why A and B do not always match...?
please answer ...

Posted: Sun May 01, 2011 7:19 am
by mongoose7
Interesting. Is it only Y=90 that causes the problem? What about X=90 or Z=90?

Also, try Y=89, 89.9, 89.99, 89.999.

There seems to be a singularity at 90 degrees. But I don't think Euler angles have any validity at this point. They are a second-rate method for general rotations.

Posted: Sun May 01, 2011 7:49 am
by softbike
i try 89.9999 ... result is same..

thank you...

Posted: Mon May 02, 2011 2:16 am
by mongoose7
OK, I don't want to write code myself, so could you please do this. For the first "bad" test, could you please print out the four values of the quaternion. Thanks.

Posted: Mon May 02, 2011 6:34 am
by REDDemon
i think this has ben fixed in the latest trunk. A guy had the same problem. Are you using irrlicht 1.7.1 , 1.7.2 or 1.8.x?

Posted: Mon May 02, 2011 8:47 am
by mongoose7
I just diffed quaternion.h 1.7.2 with SVN 3556 and the only change is the copyright notice. :o

Posted: Mon May 02, 2011 9:01 am
by REDDemon
strange. maybe it needs to be moved to the bug report forum

Posted: Mon May 02, 2011 1:42 pm
by mongoose7
OK, I wrote some code to check
A: 0.00, 90.00, 270.00
B: 0.00, 90.00, 0.00
I get B: 180, 90, 90

Edit: I just repeated the test and there is a problem. In fact the quaternion is (-0,5, -0.5, 0.5, -0.5) and B is (0, 90, 0), as softbike said.

But B should be (180, 90, 90).

Posted: Mon May 02, 2011 2:02 pm
by hybrid
Yes, there is a gimbal lock problem in the toEuler method. Will be fixed in the 1.8 branch soon.
Oh, and Irrlicht's euler angles go from right to left IIRC.

Re: Quaternion.toEuler() bug?

Posted: Thu Dec 05, 2019 5:37 pm
by CuteAlien
Bit of thread necromancy, but I still had it on my todo to test this and just 8 years later I found the time (not so long, right?). Result of my testing is - this works correct. You don't get the same angles back, but it's still the same rotation.

Code: Select all

 
#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif
 
using namespace irr;
using namespace core;
 
float toRange360(f32 x)
{
    if ( x < 0 )
        return x + 360.f;
    if ( x >= 360.f)
        return x - 360.f;
    return x;
}
 
vector3df toRange360(const vector3df& v)
{
    return vector3df(toRange360(v.X), toRange360(v.Y), toRange360(v.Z));
}
 
bool cmrRot(f32 a, f32 b)
{
    const f32 eps = 0.1;
    return core::equals( a, b, eps)
        ||  core::equals( a+360.f, b, eps)
        ||  core::equals( a, b+360.f, eps);
}
 
int main() 
{
    core::array<vector3df> orig;
 
    orig.push_back(vector3df(0.00, -0.00, 0.00));
    orig.push_back(vector3df(90.00, -0.00, 0.00));
    orig.push_back(vector3df(179.30, 359.18, 0.58));
    orig.push_back(vector3df(355.82, 345.96, 273.26));
    orig.push_back(vector3df(4.85, 359.30, 94.33));
    orig.push_back(vector3df(0.00, 90.00, 270.00));
    orig.push_back(vector3df(3.23, 359.65, 10.21));
    orig.push_back(vector3df(3.23, 359.65, 10.17));
    orig.push_back(vector3df(0.00, 0.00, 24.04));
    orig.push_back(vector3df(0.58, 4.36, 334.36));
    orig.push_back(vector3df(0.00, 0.00, 103.99));
    orig.push_back(vector3df(0.00, 90.00, 159.51));
    orig.push_back(vector3df(0.00, 90.00, 249.48));
    orig.push_back(vector3df(0.00, 75.55, 155.89));
    orig.push_back(vector3df(11.64, 311.52, 345.35));
    orig.push_back(vector3df(0.00, 0.00, 276.00));
    orig.push_back(vector3df(90.95, 360.00, 360.00));
    orig.push_back(vector3df(76.68, 1.11, 18.65));
    orig.push_back(vector3df(360.00, 0.00, 295.00));
    orig.push_back(vector3df(360.00, 0.00, 0.00));
    orig.push_back(vector3df(351.10, 353.37, 9.27));
    orig.push_back(vector3df(360.00, 360.00, 80.00));
    orig.push_back(vector3df(360.00, 0.00, 80.00));
    orig.push_back(vector3df(0.04, 359.99, 9.50));
    orig.push_back(vector3df(360.00, 0.00, 80.00));
    orig.push_back(vector3df(360.00, 360.00, 80.00));
    orig.push_back(vector3df(12.10, 4.72, 11.24));
    orig.push_back(vector3df(360.00, 360.00, 80.00));
    orig.push_back(vector3df(0.00, 360.00, 80.00));
    orig.push_back(vector3df(0.34, 89.58, 360.00));
    orig.push_back(vector3df(0.00, 284.45, 155.89));
    orig.push_back(vector3df(14.63, 48.72, 31.79));
    orig.push_back(vector3df(0.00, 360.00, 286.29));
    orig.push_back(vector3df(269.05, 0.00, 0.00));
    orig.push_back(vector3df(283.32, 358.89, 18.65));
    orig.push_back(vector3df(360.00, 360.00, 295.00));
    orig.push_back(vector3df(0.00, 360.00, 0.00));
    orig.push_back(vector3df(8.90, 6.63, 9.27));
    orig.push_back(vector3df(360.00, 0.00, 80.00));
    orig.push_back(vector3df(360.00, 360.00, 80.00));
    orig.push_back(vector3df(359.96, 0.01, 9.50));
    orig.push_back(vector3df(360.00, 0.00, 80.00));
    orig.push_back(vector3df(360.00, 0.00, 80.00));
    orig.push_back(vector3df(347.90, 355.28, 11.24));
    orig.push_back(vector3df(0.00, 360.00, 80.00));
    orig.push_back(vector3df(360.00, 0.00, 80.00));
    orig.push_back(vector3df(0.01, 0.42, 90.38));
    orig.push_back(vector3df(0.01, 0.42, 90.38));
    orig.push_back(vector3df(115.52, 89.04, 205.51));
    orig.push_back(vector3df(180.41, 359.94, 179.69));
    orig.push_back(vector3df(0.00, 90.00, 269.91));
    orig.push_back(vector3df(0.00, 90.00, 269.91));
    orig.push_back(vector3df(180.92, 10.79, 144.53));
    orig.push_back(vector3df(0.00, 0.00, 261.73));
    orig.push_back(vector3df(358.24, 358.07, 342.82));
    orig.push_back(vector3df(0.00, 0.00, 71.19));
    orig.push_back(vector3df(90.01, 270.49, 360.00));
    orig.push_back(vector3df(180.09, 270.06, 0.00));    // different angles, but same rotation
    orig.push_back(vector3df(173.58, 348.13, 132.25));
    orig.push_back(vector3df(360.00, 0.00, 318.30));
    orig.push_back(vector3df(359.78, 357.69, 7.52));
    orig.push_back(vector3df(0.00, 360.00, 71.00));
    orig.push_back(vector3df(269.99, 270.49, 360.00));
    orig.push_back(vector3df(181.95, 270.03, 0.00));    // different angles, but same rotation
    orig.push_back(vector3df(300.00, 240.00, 0.00));    // different angles, but same rotation
 
    const f32 step = 15.f;
    for ( f32 x = -360.f; x <= 360.f; x += step )
        for ( f32 y = -360.f; y <= 360.f; y += step )
            for ( f32 z = -360.f; y <= 360.f; y += step )
            {
                orig.push_back(vector3df(x, y, z));
                orig.push_back(vector3df(x-0.05, y-0.05f, z-0.05));
                orig.push_back(vector3df(x+0.05, y+0.05f, z+0.05));
            }
 
    for ( u32 i=0; i < orig.size(); ++i )
    {
        vector3df A(orig[i]);
 
        quaternion cQuat(A*core::DEGTORAD);
        core::vector3df B;
        cQuat.toEuler(B);
        B *= core::RADTODEG;
 
        B = toRange360(B);
        vector3df Ar = toRange360(A);
 
        // Test if we get same angles back
        if (    !cmrRot(Ar.X, B.X)
            ||  !cmrRot(Ar.Y, B.Y)
            ||  !cmrRot(Ar.Z, B.Z) )
        {
            // still a chance it's same rotation, but with different angles
            irr::core::vector3df v1(123, 345, 678);
            irr::core::vector3df v2(v1);
            irr::core::matrix4 m1, m2;
            m1.setRotationDegrees(A);
            m2.setRotationDegrees(B);
            m1.rotateVect(v1);
            m2.rotateVect(v2);
 
            if (  !v1.equals(v2, 1.f) ) // yeah, there is some difference around gimbal lock, but that's as good as it gets
            {
                std::cout << "A: " << A.X << " " << A.Y << " " << A.Z << "\n";
                std::cout << "B: " << B.X << " " << B.Y << " " << B.Z << "\n";
            }
        }
    }
 
    std::cout << "done\n";
    int ch = std::cin.get();
}