QtGui/math3d : Fix QQuaternion::getEulerAngles

When rotating M_PI_2 based on x-axis, quaternion to euler conversion
makes NaN for the x-rotation value.  This patch fixes this corner case.

Fixes: QTBUG-93600
Change-Id: Ice321a80ad90dba9cf3ee3a14ec7d3d047c21bd3
Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
(cherry picked from commit 7ea2fbddcf674d49ad7d219cdb8a4b760258360c)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Inho Lee 2021-05-10 15:32:26 +02:00 committed by Qt Cherry-pick Bot
parent de86dac33c
commit 4a8aa7f62c
2 changed files with 33 additions and 9 deletions

View File

@ -527,7 +527,11 @@ void QQuaternion::getEulerAngles(float *pitch, float *yaw, float *roll) const
zw /= lengthSquared;
}
*pitch = std::asin(-2.0f * (yz - xw));
const float sinp = -2.0f * (yz - xw);
if (std::abs(sinp) >= 1.0f)
*pitch = std::copysign(M_PI_2, sinp);
else
*pitch = std::asin(sinp);
if (*pitch < M_PI_2) {
if (*pitch > -M_PI_2) {
*yaw = std::atan2(2.0f * (xz + yw), 1.0f - 2.0f * (xx + yy));

View File

@ -1085,35 +1085,38 @@ void tst_QQuaternion::fromEulerAngles_data()
QTest::addColumn<float>("yaw");
QTest::addColumn<float>("roll");
QTest::addColumn<QQuaternion>("quaternion");
QTest::newRow("null")
<< 0.0f << 0.0f << 0.0f;
<< 0.0f << 0.0f << 0.0f << QQuaternion(1.0f, 0.0f, 0.0f, 0.0f);
QTest::newRow("xonly")
<< 90.0f << 0.0f << 0.0f;
<< 90.0f << 0.0f << 0.0f << QQuaternion(0.707107f, 0.707107f, 0.0f, 0.0f);
QTest::newRow("yonly")
<< 0.0f << 180.0f << 0.0f;
<< 0.0f << 180.0f << 0.0f << QQuaternion(0.0f, 0.0f, 1.0f, 0.0f);
QTest::newRow("zonly")
<< 0.0f << 0.0f << 270.0f;
<< 0.0f << 0.0f << 270.0f << QQuaternion(-0.707107f, 0.0f, 0.0f, 0.707107f);
QTest::newRow("x+z")
<< 30.0f << 0.0f << 45.0f;
<< 30.0f << 0.0f << 45.0f << QQuaternion(0.892399f, 0.239118f, -0.099046f, 0.369644f);
QTest::newRow("x+y")
<< 30.0f << 90.0f << 0.0f;
<< 30.0f << 90.0f << 0.0f << QQuaternion(0.683013f, 0.183013f, 0.683013f, -0.183013f);
QTest::newRow("y+z")
<< 0.0f << 45.0f << 30.0f;
<< 0.0f << 45.0f << 30.0f << QQuaternion(0.892399f, 0.099046f, 0.369644f, 0.239118f);
QTest::newRow("complex")
<< 30.0f << 240.0f << -45.0f;
<< 30.0f << 240.0f << -45.0f << QQuaternion(-0.531976f, -0.43968f, 0.723317f, -0.02226f);
}
void tst_QQuaternion::fromEulerAngles()
{
QFETCH(float, pitch);
QFETCH(float, yaw);
QFETCH(float, roll);
QFETCH(QQuaternion, quaternion);
// Use a straight-forward implementation of the algorithm at:
// http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q60
@ -1129,11 +1132,22 @@ void tst_QQuaternion::fromEulerAngles()
QVERIFY(myFuzzyCompare(answer.z(), result.z()));
QVERIFY(myFuzzyCompare(answer.scalar(), result.scalar()));
// quaternion should be the same as the result
QVERIFY(myFuzzyCompare(answer.x(), quaternion.x()));
QVERIFY(myFuzzyCompare(answer.y(), quaternion.y()));
QVERIFY(myFuzzyCompare(answer.z(), quaternion.z()));
QVERIFY(myFuzzyCompare(answer.scalar(), quaternion.scalar()));
{
QVector3D answerEulerAngles = answer.toEulerAngles();
QVERIFY(myFuzzyCompareDegrees(answerEulerAngles.x(), pitch));
QVERIFY(myFuzzyCompareDegrees(answerEulerAngles.y(), yaw));
QVERIFY(myFuzzyCompareDegrees(answerEulerAngles.z(), roll));
QVector3D quaternionEulerAngles = quaternion.toEulerAngles();
QVERIFY(myFuzzyCompareDegrees(quaternionEulerAngles.x(), pitch));
QVERIFY(myFuzzyCompareDegrees(quaternionEulerAngles.y(), yaw));
QVERIFY(myFuzzyCompareDegrees(quaternionEulerAngles.z(), roll));
}
answer = QQuaternion::fromEulerAngles(pitch, yaw, roll);
@ -1148,6 +1162,12 @@ void tst_QQuaternion::fromEulerAngles()
QVERIFY(myFuzzyCompareDegrees(answerPitch, pitch));
QVERIFY(myFuzzyCompareDegrees(answerYaw, yaw));
QVERIFY(myFuzzyCompareDegrees(answerRoll, roll));
float quaternionPitch, quaternionYaw, quaternionRoll;
quaternion.getEulerAngles(&quaternionPitch, &quaternionYaw, &quaternionRoll);
QVERIFY(myFuzzyCompareDegrees(quaternionPitch, pitch));
QVERIFY(myFuzzyCompareDegrees(quaternionYaw, yaw));
QVERIFY(myFuzzyCompareDegrees(quaternionRoll, roll));
}
}