summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorYuya Nishihara <yuya@tcha.org>2021-08-01 14:02:27 +0900
committerInho Lee <inho.lee@qt.io>2022-01-16 01:12:15 +0100
commit6ffc8d8eb6c44fbd51e37770e7013c4610ead96d (patch)
treecb915e45be2b701ed8bda4a92b8f6f86e3139101 /tests
parent6852c20502ffc78ad76924630d9021552287e35d (diff)
QtGui/math3d: Fix QQuaternion::getEulerAngles for GimbalLock cases
This is heavily inspired by the patch written by Inho Lee <inho.lee@qt.io>, which says "There is a precision problem in the previous algorithm when checking pitch value. (In the case that the rotation on the X-axis makes Gimbal lock.)" In order to work around the precision problem, this patch does: 1. switch to the algorithm described in the inline comment to make the story simple. 2. forcibly normalize the {x, y, z, w} components to eliminate fractional errors. 3. set threshold to avoid hidden division by cos(pitch) =~ 0. From my testing which compares dot product of the original quaternion and the one recreated from Euler angles, calculation within float range seems okay. (abs(normalize(q_orig) * normalize(q_roundtrip)) >= 0.99999) Many thanks to Inho Lee for the original patch and discussion about rounding errors. Fixes: QTBUG-72103 Pick-to: 6.3 6.2 5.15 Change-Id: I8995e4affe603111ff2303a0dfcbdb0b1ae03f10 Reviewed-by: Yuya Nishihara <yuya@tcha.org> Reviewed-by: Inho Lee <inho.lee@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Diffstat (limited to 'tests')
-rw-r--r--tests/auto/gui/math3d/qquaternion/tst_qquaternion.cpp29
1 files changed, 29 insertions, 0 deletions
diff --git a/tests/auto/gui/math3d/qquaternion/tst_qquaternion.cpp b/tests/auto/gui/math3d/qquaternion/tst_qquaternion.cpp
index 4229fa1017..e37480f633 100644
--- a/tests/auto/gui/math3d/qquaternion/tst_qquaternion.cpp
+++ b/tests/auto/gui/math3d/qquaternion/tst_qquaternion.cpp
@@ -1110,6 +1110,35 @@ void tst_QQuaternion::fromEulerAngles_data()
QTest::newRow("complex")
<< 30.0f << 240.0f << -45.0f << QQuaternion(-0.531976f, -0.43968f, 0.723317f, -0.02226f);
+
+ // Three gimbal_lock cases are not unique for the conversions from quaternion
+ // to euler, Qt will use only XY rotations for these cases.
+ // For example, QQuaternion(0.5f, 0.5f, -0.5f, 0.5f) can be EulerXYZ(90.0f, 0.0f, 90.0f), too.
+ // But Qt will always convert it to EulerXYZ(90.0f, -90.0f, 0.0f) without Z-rotation.
+ QTest::newRow("gimbal_lock_1")
+ << 90.0f << -90.0f << 0.0f << QQuaternion(0.5f, 0.5f, -0.5f, 0.5f);
+
+ QTest::newRow("gimbal_lock_2")
+ << 90.0f << 40.0f << 0.0f << QQuaternion(0.664463f, 0.664463f, 0.241845f, -0.241845f);
+
+ QTest::newRow("gimbal_lock_3") << 90.0f << 170.0f << 0.0f
+ << QQuaternion(0.0616285f, 0.0616285f, 0.704416f, -0.704416f);
+
+ // These four examples have a fraction of errors that would bypass normalize() threshold
+ // and could make Gimbal lock detection fail.
+ QTest::newRow("gimbal_lock_fraction_1")
+ << -90.0f << 90.001152f << 0.0f << QQuaternion(0.499989986f, -0.5f, 0.5f, 0.5f);
+
+ QTest::newRow("gimbal_lock_fraction_2")
+ << -90.0f << -179.999985f << 0.0f
+ << QQuaternion(1.00000001e-07f, 1.00000001e-10f, -0.707106769f, -0.707105756f);
+
+ QTest::newRow("gimbal_lock_fraction_3")
+ << -90.0f << 90.0011597f << 0.0f << QQuaternion(0.499989986f, -0.49999994f, 0.5f, 0.5f);
+
+ QTest::newRow("gimbal_lock_fraction_4")
+ << -90.0f << -180.0f << 0.0f
+ << QQuaternion(9.99999996e-12f, 9.99999996e-12f, -0.707106769f, -0.707096756f);
}
void tst_QQuaternion::fromEulerAngles()
{