summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRhys Weatherley <rhys.weatherley@nokia.com>2009-04-15 22:33:32 (GMT)
committerRhys Weatherley <rhys.weatherley@nokia.com>2009-04-15 22:33:32 (GMT)
commit1cb11b428ef1bee070af72676b7eb1fa325bb980 (patch)
treef098bff499691223ba9255a8e0500314cae20e6e
parent10688c0168fc010671abc59f367c19357bb6776c (diff)
downloadQt-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.cpp39
-rw-r--r--src/gui/math3d/qquaternion.h2
-rw-r--r--tests/auto/math3d/qquaternion/tst_qquaternion.cpp57
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"