diff options
author | Rhys Weatherley <rhys.weatherley@nokia.com> | 2009-04-15 22:33:32 (GMT) |
---|---|---|
committer | Rhys Weatherley <rhys.weatherley@nokia.com> | 2009-04-15 22:33:32 (GMT) |
commit | 1cb11b428ef1bee070af72676b7eb1fa325bb980 (patch) | |
tree | f098bff499691223ba9255a8e0500314cae20e6e | |
parent | 10688c0168fc010671abc59f367c19357bb6776c (diff) | |
download | Qt-1cb11b428ef1bee070af72676b7eb1fa325bb980.zip Qt-1cb11b428ef1bee070af72676b7eb1fa325bb980.tar.gz Qt-1cb11b428ef1bee070af72676b7eb1fa325bb980.tar.bz2 |
Add the QQuaternion::nlerp() function as a counterpart to slerp()
nlerp() implements "normalized linear interpolation", which is faster
than slerp() and gives approximate results that are good enough for
some applications.
Reviewed-by: trustme
-rw-r--r-- | src/gui/math3d/qquaternion.cpp | 39 | ||||
-rw-r--r-- | src/gui/math3d/qquaternion.h | 2 | ||||
-rw-r--r-- | tests/auto/math3d/qquaternion/tst_qquaternion.cpp | 57 |
3 files changed, 98 insertions, 0 deletions
diff --git a/src/gui/math3d/qquaternion.cpp b/src/gui/math3d/qquaternion.cpp index 1348fed..a91b0b9 100644 --- a/src/gui/math3d/qquaternion.cpp +++ b/src/gui/math3d/qquaternion.cpp @@ -484,6 +484,8 @@ QQuaternion QQuaternion::fromAxisAndAngle If \a t is less than or equal to 0, then \a q1 will be returned. If \a t is greater than or equal to 1, then \a q2 will be returned. + + \sa nlerp() */ QQuaternion QQuaternion::slerp (const QQuaternion& q1, const QQuaternion& q2, qreal t) @@ -522,6 +524,43 @@ QQuaternion QQuaternion::slerp return q1 * factor1 + q2b * factor2; } +/*! + Interpolates along the shortest linear path between the rotational + positions \a q1 and \a q2. The value \a t should be between 0 and 1, + indicating the distance to travel between \a q1 and \a q2. + The result will be normalized(). + + If \a t is less than or equal to 0, then \a q1 will be returned. + If \a t is greater than or equal to 1, then \a q2 will be returned. + + The nlerp() function is typically faster than slerp() and will + give approximate results to spherical interpolation that are + good enough for some applications. + + \sa slerp() +*/ +QQuaternion QQuaternion::nlerp + (const QQuaternion& q1, const QQuaternion& q2, qreal t) +{ + // Handle the easy cases first. + if (t <= 0.0f) + return q1; + else if (t >= 1.0f) + return q2; + + // Determine the angle between the two quaternions. + QQuaternion q2b; + qreal dot; + dot = q1.xp * q2.xp + q1.yp * q2.yp + q1.zp * q2.zp + q1.wp * q2.wp; + if (dot >= 0.0f) + q2b = q2; + else + q2b = -q2; + + // Perform the linear interpolation. + return (q1 * (1.0f - t) + q2b * t).normalized(); +} + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug dbg, const QQuaternion &q) diff --git a/src/gui/math3d/qquaternion.h b/src/gui/math3d/qquaternion.h index ad6fddf..c05c641 100644 --- a/src/gui/math3d/qquaternion.h +++ b/src/gui/math3d/qquaternion.h @@ -126,6 +126,8 @@ public: static QQuaternion slerp (const QQuaternion& q1, const QQuaternion& q2, qreal t); + static QQuaternion nlerp + (const QQuaternion& q1, const QQuaternion& q2, qreal t); private: float wp, xp, yp, zp; diff --git a/tests/auto/math3d/qquaternion/tst_qquaternion.cpp b/tests/auto/math3d/qquaternion/tst_qquaternion.cpp index 6a69755..f25f858 100644 --- a/tests/auto/math3d/qquaternion/tst_qquaternion.cpp +++ b/tests/auto/math3d/qquaternion/tst_qquaternion.cpp @@ -90,6 +90,9 @@ private slots: void slerp_data(); void slerp(); + + void nlerp_data(); + void nlerp(); }; // qFuzzyCompare isn't quite "fuzzy" enough to handle conversion @@ -768,6 +771,60 @@ void tst_QQuaternion::slerp() QVERIFY(fuzzyCompare(result.scalar(), q3.scalar())); } +// Test normalized linear interpolation of quaternions. +void tst_QQuaternion::nlerp_data() +{ + slerp_data(); +} +void tst_QQuaternion::nlerp() +{ + QFETCH(qreal, x1); + QFETCH(qreal, y1); + QFETCH(qreal, z1); + QFETCH(qreal, angle1); + QFETCH(qreal, x2); + QFETCH(qreal, y2); + QFETCH(qreal, z2); + QFETCH(qreal, angle2); + QFETCH(qreal, t); + + QQuaternion q1 = QQuaternion::fromAxisAndAngle(x1, y1, z1, angle1); + QQuaternion q2 = QQuaternion::fromAxisAndAngle(x2, y2, z2, angle2); + + QQuaternion result = QQuaternion::nlerp(q1, q2, t); + + qreal resultx, resulty, resultz, resultscalar; + if (t <= 0.0f) { + resultx = q1.x(); + resulty = q1.y(); + resultz = q1.z(); + resultscalar = q1.scalar(); + } else if (t >= 1.0f) { + resultx = q2.x(); + resulty = q2.y(); + resultz = q2.z(); + resultscalar = q2.scalar(); + } else if (qAbs(angle1 - angle2) <= 180.f) { + resultx = q1.x() * (1 - t) + q2.x() * t; + resulty = q1.y() * (1 - t) + q2.y() * t; + resultz = q1.z() * (1 - t) + q2.z() * t; + resultscalar = q1.scalar() * (1 - t) + q2.scalar() * t; + } else { + // Angle greater than 180 degrees: negate q2. + resultx = q1.x() * (1 - t) - q2.x() * t; + resulty = q1.y() * (1 - t) - q2.y() * t; + resultz = q1.z() * (1 - t) - q2.z() * t; + resultscalar = q1.scalar() * (1 - t) - q2.scalar() * t; + } + + QQuaternion q3 = QQuaternion(resultscalar, resultx, resulty, resultz).normalized(); + + QVERIFY(fuzzyCompare(result.x(), q3.x())); + QVERIFY(fuzzyCompare(result.y(), q3.y())); + QVERIFY(fuzzyCompare(result.z(), q3.z())); + QVERIFY(fuzzyCompare(result.scalar(), q3.scalar())); +} + QTEST_APPLESS_MAIN(tst_QQuaternion) #include "tst_qquaternion.moc" |