summaryrefslogtreecommitdiffstats
path: root/tests/auto/gui/math3d/qquaternion/tst_qquaternion.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/gui/math3d/qquaternion/tst_qquaternion.cpp')
-rw-r--r--tests/auto/gui/math3d/qquaternion/tst_qquaternion.cpp386
1 files changed, 376 insertions, 10 deletions
diff --git a/tests/auto/gui/math3d/qquaternion/tst_qquaternion.cpp b/tests/auto/gui/math3d/qquaternion/tst_qquaternion.cpp
index cdbc242640..2c38e4c111 100644
--- a/tests/auto/gui/math3d/qquaternion/tst_qquaternion.cpp
+++ b/tests/auto/gui/math3d/qquaternion/tst_qquaternion.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
-** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
@@ -10,9 +10,9 @@
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/contact-us.
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
@@ -23,8 +23,8 @@
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
@@ -35,6 +35,52 @@
#include <QtCore/qmath.h>
#include <QtGui/qquaternion.h>
+// This is a more tolerant version of qFuzzyCompare that also handles the case
+// where one or more of the values being compare are close to zero
+static inline bool myFuzzyCompare(float p1, float p2)
+{
+ if (qFuzzyIsNull(p1) && qFuzzyIsNull(p2))
+ return true;
+ return qAbs(qAbs(p1) - qAbs(p2)) <= 0.00003f;
+}
+
+static inline bool myFuzzyCompare(const QVector3D &v1, const QVector3D &v2)
+{
+ return myFuzzyCompare(v1.x(), v2.x())
+ && myFuzzyCompare(v1.y(), v2.y())
+ && myFuzzyCompare(v1.z(), v2.z());
+}
+
+static inline bool myFuzzyCompare(const QQuaternion &q1, const QQuaternion &q2)
+{
+ const float d = QQuaternion::dotProduct(q1, q2);
+ return myFuzzyCompare(d * d, 1.0f);
+}
+
+static inline bool myFuzzyCompareRadians(float p1, float p2)
+{
+ static const float fPI = float(M_PI);
+ if (p1 < -fPI)
+ p1 += 2.0f * fPI;
+ else if (p1 > fPI)
+ p1 -= 2.0f * fPI;
+
+ if (p2 < -fPI)
+ p2 += 2.0f * fPI;
+ else if (p2 > fPI)
+ p2 -= 2.0f * fPI;
+
+ return qAbs(qAbs(p1) - qAbs(p2)) <= qDegreesToRadians(0.05f);
+}
+
+static inline bool myFuzzyCompareDegrees(float p1, float p2)
+{
+ p1 = qDegreesToRadians(p1);
+ p2 = qDegreesToRadians(p2);
+ return myFuzzyCompareRadians(p1, p2);
+}
+
+
class tst_QQuaternion : public QObject
{
Q_OBJECT
@@ -45,6 +91,9 @@ public:
private slots:
void create();
+ void dotProduct_data();
+ void dotProduct();
+
void length_data();
void length();
@@ -54,6 +103,9 @@ private slots:
void normalize_data();
void normalize();
+ void inverted_data();
+ void inverted();
+
void compare();
void add_data();
@@ -80,6 +132,18 @@ private slots:
void fromAxisAndAngle_data();
void fromAxisAndAngle();
+ void fromRotationMatrix_data();
+ void fromRotationMatrix();
+
+ void fromAxes_data();
+ void fromAxes();
+
+ void rotationTo_data();
+ void rotationTo();
+
+ void fromEulerAngles_data();
+ void fromEulerAngles();
+
void slerp_data();
void slerp();
@@ -210,6 +274,58 @@ void tst_QQuaternion::create()
QCOMPARE(v10.w(), 34.0f);
}
+// Test the computation of dot product.
+void tst_QQuaternion::dotProduct_data()
+{
+ QTest::addColumn<float>("x1");
+ QTest::addColumn<float>("y1");
+ QTest::addColumn<float>("z1");
+ QTest::addColumn<float>("scalar1");
+ QTest::addColumn<float>("x2");
+ QTest::addColumn<float>("y2");
+ QTest::addColumn<float>("z2");
+ QTest::addColumn<float>("scalar2");
+ QTest::addColumn<float>("dot");
+
+ QTest::newRow("null")
+ << 0.0f << 0.0f << 0.0f << 0.0f
+ << 0.0f << 0.0f << 0.0f << 0.0f
+ << 0.0f;
+
+ QTest::newRow("identity")
+ << 0.0f << 0.0f << 0.0f << 1.0f
+ << 0.0f << 0.0f << 0.0f << 1.0f
+ << 1.0f;
+
+ QTest::newRow("unitvec")
+ << 1.0f << 0.0f << 0.0f << 0.0f
+ << 0.0f << 1.0f << 0.0f << 0.0f
+ << 0.0f;
+
+ QTest::newRow("complex")
+ << 1.0f << 2.0f << 3.0f << 4.0f
+ << 4.0f << 5.0f << 6.0f << 7.0f
+ << 60.0f;
+}
+void tst_QQuaternion::dotProduct()
+{
+ QFETCH(float, x1);
+ QFETCH(float, y1);
+ QFETCH(float, z1);
+ QFETCH(float, scalar1);
+ QFETCH(float, x2);
+ QFETCH(float, y2);
+ QFETCH(float, z2);
+ QFETCH(float, scalar2);
+ QFETCH(float, dot);
+
+ QQuaternion q1(scalar1, x1, y1, z1);
+ QQuaternion q2(scalar2, x2, y2, z2);
+
+ QCOMPARE(QQuaternion::dotProduct(q1, q2), dot);
+ QCOMPARE(QQuaternion::dotProduct(q2, q1), dot);
+}
+
// Test length computation for quaternions.
void tst_QQuaternion::length_data()
{
@@ -228,7 +344,7 @@ void tst_QQuaternion::length_data()
QTest::newRow("-1y") << 0.0f << -1.0f << 0.0f << 0.0f << 1.0f;
QTest::newRow("-1z") << 0.0f << 0.0f << -1.0f << 0.0f << 1.0f;
QTest::newRow("-1w") << 0.0f << 0.0f << 0.0f << -1.0f << 1.0f;
- QTest::newRow("two") << 2.0f << -2.0f << 2.0f << 2.0f << sqrtf(16.0f);
+ QTest::newRow("two") << 2.0f << -2.0f << 2.0f << 2.0f << std::sqrt(16.0f);
}
void tst_QQuaternion::length()
{
@@ -291,6 +407,32 @@ void tst_QQuaternion::normalize()
QCOMPARE(v.length(), 1.0f);
}
+void tst_QQuaternion::inverted_data()
+{
+ // Use the same test data as the length test.
+ length_data();
+}
+void tst_QQuaternion::inverted()
+{
+ QFETCH(float, x);
+ QFETCH(float, y);
+ QFETCH(float, z);
+ QFETCH(float, w);
+ QFETCH(float, len);
+
+ QQuaternion v(w, x, y, z);
+ QQuaternion u = v.inverted();
+ if (v.isNull()) {
+ QVERIFY(u.isNull());
+ } else {
+ len *= len;
+ QCOMPARE(-u.x() * len, v.x());
+ QCOMPARE(-u.y() * len, v.y());
+ QCOMPARE(-u.z() * len, v.z());
+ QCOMPARE(u.scalar() * len, v.scalar());
+ }
+}
+
// Test the comparison operators for quaternions.
void tst_QQuaternion::compare()
{
@@ -675,8 +817,9 @@ void tst_QQuaternion::fromAxisAndAngle()
// http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q56
// to calculate the answer we expect to get.
QVector3D vector = QVector3D(x1, y1, z1).normalized();
- float sin_a = sinf((angle * M_PI / 180.0) / 2.0);
- float cos_a = cosf((angle * M_PI / 180.0) / 2.0);
+ const float a = (angle * M_PI / 180.0) / 2.0;
+ const float sin_a = std::sin(a);
+ const float cos_a = std::cos(a);
QQuaternion result(cos_a,
(vector.x() * sin_a),
(vector.y() * sin_a),
@@ -689,11 +832,234 @@ void tst_QQuaternion::fromAxisAndAngle()
QVERIFY(qFuzzyCompare(answer.z(), result.z()));
QVERIFY(qFuzzyCompare(answer.scalar(), result.scalar()));
+ {
+ QVector3D answerAxis;
+ float answerAngle;
+ answer.getAxisAndAngle(&answerAxis, &answerAngle);
+ QVERIFY(qFuzzyCompare(answerAxis.x(), vector.x()));
+ QVERIFY(qFuzzyCompare(answerAxis.y(), vector.y()));
+ QVERIFY(qFuzzyCompare(answerAxis.z(), vector.z()));
+ QVERIFY(qFuzzyCompare(answerAngle, angle));
+ }
+
answer = QQuaternion::fromAxisAndAngle(x1, y1, z1, angle);
QVERIFY(qFuzzyCompare(answer.x(), result.x()));
QVERIFY(qFuzzyCompare(answer.y(), result.y()));
QVERIFY(qFuzzyCompare(answer.z(), result.z()));
QVERIFY(qFuzzyCompare(answer.scalar(), result.scalar()));
+
+ {
+ float answerAxisX, answerAxisY, answerAxisZ;
+ float answerAngle;
+ answer.getAxisAndAngle(&answerAxisX, &answerAxisY, &answerAxisZ, &answerAngle);
+ QVERIFY(qFuzzyCompare(answerAxisX, vector.x()));
+ QVERIFY(qFuzzyCompare(answerAxisY, vector.y()));
+ QVERIFY(qFuzzyCompare(answerAxisZ, vector.z()));
+ QVERIFY(qFuzzyCompare(answerAngle, angle));
+ }
+}
+
+// Test quaternion convertion to and from rotation matrix.
+void tst_QQuaternion::fromRotationMatrix_data()
+{
+ fromAxisAndAngle_data();
+}
+void tst_QQuaternion::fromRotationMatrix()
+{
+ QFETCH(float, x1);
+ QFETCH(float, y1);
+ QFETCH(float, z1);
+ QFETCH(float, angle);
+
+ QQuaternion result = QQuaternion::fromAxisAndAngle(QVector3D(x1, y1, z1), angle);
+ QMatrix3x3 rot3x3 = result.toRotationMatrix();
+ QQuaternion answer = QQuaternion::fromRotationMatrix(rot3x3);
+
+ QVERIFY(qFuzzyCompare(answer, result) || qFuzzyCompare(-answer, result));
+}
+
+// Test quaternion convertion to and from orthonormal axes.
+void tst_QQuaternion::fromAxes_data()
+{
+ QTest::addColumn<float>("x1");
+ QTest::addColumn<float>("y1");
+ QTest::addColumn<float>("z1");
+ QTest::addColumn<float>("angle");
+ QTest::addColumn<QVector3D>("xAxis");
+ QTest::addColumn<QVector3D>("yAxis");
+ QTest::addColumn<QVector3D>("zAxis");
+
+ QTest::newRow("null")
+ << 0.0f << 0.0f << 0.0f << 0.0f
+ << QVector3D(1, 0, 0) << QVector3D(0, 1, 0) << QVector3D(0, 0, 1);
+
+ QTest::newRow("xonly")
+ << 1.0f << 0.0f << 0.0f << 90.0f
+ << QVector3D(1, 0, 0) << QVector3D(0, 0, 1) << QVector3D(0, -1, 0);
+
+ QTest::newRow("yonly")
+ << 0.0f << 1.0f << 0.0f << 180.0f
+ << QVector3D(-1, 0, 0) << QVector3D(0, 1, 0) << QVector3D(0, 0, -1);
+
+ QTest::newRow("zonly")
+ << 0.0f << 0.0f << 1.0f << 270.0f
+ << QVector3D(0, -1, 0) << QVector3D(1, 0, 0) << QVector3D(0, 0, 1);
+
+ QTest::newRow("complex")
+ << 1.0f << 2.0f << -3.0f << 45.0f
+ << QVector3D(0.728028, -0.525105, -0.440727) << QVector3D(0.608789, 0.790791, 0.0634566) << QVector3D(0.315202, -0.314508, 0.895395);
+}
+void tst_QQuaternion::fromAxes()
+{
+ QFETCH(float, x1);
+ QFETCH(float, y1);
+ QFETCH(float, z1);
+ QFETCH(float, angle);
+ QFETCH(QVector3D, xAxis);
+ QFETCH(QVector3D, yAxis);
+ QFETCH(QVector3D, zAxis);
+
+ QQuaternion result = QQuaternion::fromAxisAndAngle(QVector3D(x1, y1, z1), angle);
+
+ QVector3D axes[3];
+ result.getAxes(&axes[0], &axes[1], &axes[2]);
+ QVERIFY(myFuzzyCompare(axes[0], xAxis));
+ QVERIFY(myFuzzyCompare(axes[1], yAxis));
+ QVERIFY(myFuzzyCompare(axes[2], zAxis));
+
+ QQuaternion answer = QQuaternion::fromAxes(axes[0], axes[1], axes[2]);
+
+ QVERIFY(qFuzzyCompare(answer, result) || qFuzzyCompare(-answer, result));
+}
+
+// Test shortest arc quaternion.
+void tst_QQuaternion::rotationTo_data()
+{
+ QTest::addColumn<QVector3D>("from");
+ QTest::addColumn<QVector3D>("to");
+
+ // same
+ QTest::newRow("+X -> +X") << QVector3D(10.0f, 0.0f, 0.0f) << QVector3D(10.0f, 0.0f, 0.0f);
+ QTest::newRow("-X -> -X") << QVector3D(-10.0f, 0.0f, 0.0f) << QVector3D(-10.0f, 0.0f, 0.0f);
+ QTest::newRow("+Y -> +Y") << QVector3D(0.0f, 10.0f, 0.0f) << QVector3D(0.0f, 10.0f, 0.0f);
+ QTest::newRow("-Y -> -Y") << QVector3D(0.0f, -10.0f, 0.0f) << QVector3D(0.0f, -10.0f, 0.0f);
+ QTest::newRow("+Z -> +Z") << QVector3D(0.0f, 0.0f, 10.0f) << QVector3D(0.0f, 0.0f, 10.0f);
+ QTest::newRow("-Z -> -Z") << QVector3D(0.0f, 0.0f, -10.0f) << QVector3D(0.0f, 0.0f, -10.0f);
+ QTest::newRow("+X+Y+Z -> +X+Y+Z") << QVector3D(10.0f, 10.0f, 10.0f) << QVector3D(10.0f, 10.0f, 10.0f);
+ QTest::newRow("-X-Y-Z -> -X-Y-Z") << QVector3D(-10.0f, -10.0f, -10.0f) << QVector3D(-10.0f, -10.0f, -10.0f);
+
+ // arbitrary
+ QTest::newRow("+Z -> +X") << QVector3D(0.0f, 0.0f, 10.0f) << QVector3D(10.0f, 0.0f, 0.0f);
+ QTest::newRow("+Z -> -X") << QVector3D(0.0f, 0.0f, 10.0f) << QVector3D(-10.0f, 0.0f, 0.0f);
+ QTest::newRow("+Z -> +Y") << QVector3D(0.0f, 0.0f, 10.0f) << QVector3D(0.0f, 10.0f, 0.0f);
+ QTest::newRow("+Z -> -Y") << QVector3D(0.0f, 0.0f, 10.0f) << QVector3D(0.0f, -10.0f, 0.0f);
+ QTest::newRow("-Z -> +X") << QVector3D(0.0f, 0.0f, -10.0f) << QVector3D(10.0f, 0.0f, 0.0f);
+ QTest::newRow("-Z -> -X") << QVector3D(0.0f, 0.0f, -10.0f) << QVector3D(-10.0f, 0.0f, 0.0f);
+ QTest::newRow("-Z -> +Y") << QVector3D(0.0f, 0.0f, -10.0f) << QVector3D(0.0f, 10.0f, 0.0f);
+ QTest::newRow("-Z -> -Y") << QVector3D(0.0f, 0.0f, -10.0f) << QVector3D(0.0f, -10.0f, 0.0f);
+ QTest::newRow("+X -> +Y") << QVector3D(10.0f, 0.0f, 0.0f) << QVector3D(0.0f, 10.0f, 0.0f);
+ QTest::newRow("+X -> -Y") << QVector3D(10.0f, 0.0f, 0.0f) << QVector3D(0.0f, -10.0f, 0.0f);
+ QTest::newRow("-X -> +Y") << QVector3D(-10.0f, 0.0f, 0.0f) << QVector3D(0.0f, 10.0f, 0.0f);
+ QTest::newRow("-X -> -Y") << QVector3D(-10.0f, 0.0f, 0.0f) << QVector3D(0.0f, -10.0f, 0.0f);
+ QTest::newRow("+X+Y+Z -> +X-Y-Z") << QVector3D(10.0f, 10.0f, 10.0f) << QVector3D(10.0f, -10.0f, -10.0f);
+ QTest::newRow("-X-Y+Z -> -X+Y-Z") << QVector3D(-10.0f, -10.0f, 10.0f) << QVector3D(-10.0f, 10.0f, -10.0f);
+ QTest::newRow("+X+Y+Z -> +Z") << QVector3D(10.0f, 10.0f, 10.0f) << QVector3D(0.0f, 0.0f, 10.0f);
+
+ // collinear
+ QTest::newRow("+X -> -X") << QVector3D(10.0f, 0.0f, 0.0f) << QVector3D(-10.0f, 0.0f, 0.0f);
+ QTest::newRow("+Y -> -Y") << QVector3D(0.0f, 10.0f, 0.0f) << QVector3D(0.0f, -10.0f, 0.0f);
+ QTest::newRow("+Z -> -Z") << QVector3D(0.0f, 0.0f, 10.0f) << QVector3D(0.0f, 0.0f, -10.0f);
+ QTest::newRow("+X+Y+Z -> -X-Y-Z") << QVector3D(10.0f, 10.0f, 10.0f) << QVector3D(-10.0f, -10.0f, -10.0f);
+}
+void tst_QQuaternion::rotationTo()
+{
+ QFETCH(QVector3D, from);
+ QFETCH(QVector3D, to);
+
+ QQuaternion q1 = QQuaternion::rotationTo(from, to);
+ QVERIFY(myFuzzyCompare(q1, q1.normalized()));
+ QVector3D vec1(q1 * from);
+ vec1 *= (to.length() / from.length()); // discard rotated length
+ QVERIFY(myFuzzyCompare(vec1, to));
+
+ QQuaternion q2 = QQuaternion::rotationTo(to, from);
+ QVERIFY(myFuzzyCompare(q2, q2.normalized()));
+ QVector3D vec2(q2 * to);
+ vec2 *= (from.length() / to.length()); // discard rotated length
+ QVERIFY(myFuzzyCompare(vec2, from));
+}
+
+// Test quaternion creation from an axis and an angle.
+void tst_QQuaternion::fromEulerAngles_data()
+{
+ QTest::addColumn<float>("pitch");
+ QTest::addColumn<float>("yaw");
+ QTest::addColumn<float>("roll");
+
+ QTest::newRow("null")
+ << 0.0f << 0.0f << 0.0f;
+
+ QTest::newRow("xonly")
+ << 90.0f << 0.0f << 0.0f;
+
+ QTest::newRow("yonly")
+ << 0.0f << 180.0f << 0.0f;
+
+ QTest::newRow("zonly")
+ << 0.0f << 0.0f << 270.0f;
+
+ QTest::newRow("x+z")
+ << 30.0f << 0.0f << 45.0f;
+
+ QTest::newRow("x+y")
+ << 30.0f << 90.0f << 0.0f;
+
+ QTest::newRow("y+z")
+ << 0.0f << 45.0f << 30.0f;
+
+ QTest::newRow("complex")
+ << 30.0f << 240.0f << -45.0f;
+}
+void tst_QQuaternion::fromEulerAngles()
+{
+ QFETCH(float, pitch);
+ QFETCH(float, yaw);
+ QFETCH(float, roll);
+
+ // Use a straight-forward implementation of the algorithm at:
+ // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q60
+ // to calculate the answer we expect to get.
+ QQuaternion qx = QQuaternion::fromAxisAndAngle(QVector3D(1, 0, 0), pitch);
+ QQuaternion qy = QQuaternion::fromAxisAndAngle(QVector3D(0, 1, 0), yaw);
+ QQuaternion qz = QQuaternion::fromAxisAndAngle(QVector3D(0, 0, 1), roll);
+ QQuaternion result = qy * (qx * qz);
+ QQuaternion answer = QQuaternion::fromEulerAngles(QVector3D(pitch, yaw, roll));
+
+ QVERIFY(myFuzzyCompare(answer.x(), result.x()));
+ QVERIFY(myFuzzyCompare(answer.y(), result.y()));
+ QVERIFY(myFuzzyCompare(answer.z(), result.z()));
+ QVERIFY(myFuzzyCompare(answer.scalar(), result.scalar()));
+
+ {
+ QVector3D answerEulerAngles = answer.toEulerAngles();
+ QVERIFY(myFuzzyCompareDegrees(answerEulerAngles.x(), pitch));
+ QVERIFY(myFuzzyCompareDegrees(answerEulerAngles.y(), yaw));
+ QVERIFY(myFuzzyCompareDegrees(answerEulerAngles.z(), roll));
+ }
+
+ answer = QQuaternion::fromEulerAngles(pitch, yaw, roll);
+ QVERIFY(myFuzzyCompare(answer.x(), result.x()));
+ QVERIFY(myFuzzyCompare(answer.y(), result.y()));
+ QVERIFY(myFuzzyCompare(answer.z(), result.z()));
+ QVERIFY(myFuzzyCompare(answer.scalar(), result.scalar()));
+
+ {
+ float answerPitch, answerYaw, answerRoll;
+ answer.getEulerAngles(&answerPitch, &answerYaw, &answerRoll);
+ QVERIFY(myFuzzyCompareDegrees(answerPitch, pitch));
+ QVERIFY(myFuzzyCompareDegrees(answerYaw, yaw));
+ QVERIFY(myFuzzyCompareDegrees(answerRoll, roll));
+ }
}
// Test spherical interpolation of quaternions.