diff options
author | Inho Lee <[email protected]> | 2021-05-10 15:32:26 +0200 |
---|---|---|
committer | Inho Lee <[email protected]> | 2021-06-01 09:01:23 +0200 |
commit | 7ea2fbddcf674d49ad7d219cdb8a4b760258360c (patch) | |
tree | f04ddf0e3f284726e13e7f448b765887b5fef014 | |
parent | 4f853744c9a88d986b46fc80d62200c82eb048b2 (diff) |
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
Pick-to: 6.1 6.0 5.15
Change-Id: Ice321a80ad90dba9cf3ee3a14ec7d3d047c21bd3
Reviewed-by: Laszlo Agocs <[email protected]>
Reviewed-by: Andy Nichols <[email protected]>
-rw-r--r-- | src/gui/math3d/qquaternion.cpp | 6 | ||||
-rw-r--r-- | tests/auto/gui/math3d/qquaternion/tst_qquaternion.cpp | 36 |
2 files changed, 33 insertions, 9 deletions
diff --git a/src/gui/math3d/qquaternion.cpp b/src/gui/math3d/qquaternion.cpp index 48e12f12fe8..ba82aaa7378 100644 --- a/src/gui/math3d/qquaternion.cpp +++ b/src/gui/math3d/qquaternion.cpp @@ -511,7 +511,11 @@ void QQuaternion::getEulerAngles(float *pitch, float *yaw, float *roll) const const float zz = zps * zps; const float zw = zps * wps; - *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)); diff --git a/tests/auto/gui/math3d/qquaternion/tst_qquaternion.cpp b/tests/auto/gui/math3d/qquaternion/tst_qquaternion.cpp index aaf0fb7e0de..4229fa1017d 100644 --- a/tests/auto/gui/math3d/qquaternion/tst_qquaternion.cpp +++ b/tests/auto/gui/math3d/qquaternion/tst_qquaternion.cpp @@ -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)); } } |