summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKonstantin Ritt <ritt.ks@gmail.com>2015-02-26 19:16:15 +0400
committerKonstantin Ritt <ritt.ks@gmail.com>2015-03-07 01:14:04 +0000
commit1e441d298db5e7ad1635067106e4b7ed251fd4bd (patch)
tree5fc797995ffbea1c1b904039399a1453a249d750
parentc4aea1ea20962bd12695147c8546558b02ad859c (diff)
[QQuaternion] Add a way to convert to/from orthonormal axes
It is just a convenience wrapper around convertion to/from the rotation matrix. Change-Id: I27511b43866827172960b0152f1c7b65da857f6f Reviewed-by: Lars Knoll <lars.knoll@digia.com>
-rw-r--r--src/gui/math3d/qquaternion.cpp51
-rw-r--r--src/gui/math3d/qquaternion.h5
-rw-r--r--tests/auto/gui/math3d/qquaternion/tst_qquaternion.cpp123
3 files changed, 149 insertions, 30 deletions
diff --git a/src/gui/math3d/qquaternion.cpp b/src/gui/math3d/qquaternion.cpp
index 141651eda1..d367f74e25 100644
--- a/src/gui/math3d/qquaternion.cpp
+++ b/src/gui/math3d/qquaternion.cpp
@@ -592,7 +592,7 @@ QQuaternion QQuaternion::fromEulerAngles(float pitch, float yaw, float roll)
\note If this quaternion is not normalized,
the resulting rotation matrix will contain scaling information.
- \sa fromRotationMatrix()
+ \sa fromRotationMatrix(), getAxes()
*/
QMatrix3x3 QQuaternion::toRotationMatrix() const
{
@@ -635,7 +635,7 @@ QMatrix3x3 QQuaternion::toRotationMatrix() const
\note If a given rotation matrix is not normalized,
the resulting quaternion will contain scaling information.
- \sa toRotationMatrix()
+ \sa toRotationMatrix(), fromAxes()
*/
QQuaternion QQuaternion::fromRotationMatrix(const QMatrix3x3 &rot3x3)
{
@@ -672,6 +672,53 @@ QQuaternion QQuaternion::fromRotationMatrix(const QMatrix3x3 &rot3x3)
return QQuaternion(scalar, axis[0], axis[1], axis[2]);
}
+#ifndef QT_NO_VECTOR3D
+
+/*!
+ \since 5.5
+
+ Returns the 3 orthonormal axes (\a xAxis, \a yAxis, \a zAxis) defining the quaternion.
+
+ \sa fromAxes(), toRotationMatrix()
+*/
+void QQuaternion::getAxes(QVector3D *xAxis, QVector3D *yAxis, QVector3D *zAxis) const
+{
+ Q_ASSERT(xAxis && yAxis && zAxis);
+
+ const QMatrix3x3 rot3x3(toRotationMatrix());
+
+ *xAxis = QVector3D(rot3x3(0, 0), rot3x3(1, 0), rot3x3(2, 0));
+ *yAxis = QVector3D(rot3x3(0, 1), rot3x3(1, 1), rot3x3(2, 1));
+ *zAxis = QVector3D(rot3x3(0, 2), rot3x3(1, 2), rot3x3(2, 2));
+}
+
+/*!
+ \since 5.5
+
+ Constructs the quaternion using 3 axes (\a xAxis, \a yAxis, \a zAxis).
+
+ \note The axes are assumed to be orthonormal.
+
+ \sa getAxes(), fromRotationMatrix()
+*/
+QQuaternion QQuaternion::fromAxes(const QVector3D &xAxis, const QVector3D &yAxis, const QVector3D &zAxis)
+{
+ QMatrix3x3 rot3x3(Qt::Uninitialized);
+ rot3x3(0, 0) = xAxis.x();
+ rot3x3(1, 0) = xAxis.y();
+ rot3x3(2, 0) = xAxis.z();
+ rot3x3(0, 1) = yAxis.x();
+ rot3x3(1, 1) = yAxis.y();
+ rot3x3(2, 1) = yAxis.z();
+ rot3x3(0, 2) = zAxis.x();
+ rot3x3(1, 2) = zAxis.y();
+ rot3x3(2, 2) = zAxis.z();
+
+ return QQuaternion::fromRotationMatrix(rot3x3);
+}
+
+#endif // QT_NO_VECTOR3D
+
/*!
\fn bool operator==(const QQuaternion &q1, const QQuaternion &q2)
\relates QQuaternion
diff --git a/src/gui/math3d/qquaternion.h b/src/gui/math3d/qquaternion.h
index b4022e8579..6ce3979bbe 100644
--- a/src/gui/math3d/qquaternion.h
+++ b/src/gui/math3d/qquaternion.h
@@ -134,6 +134,11 @@ public:
QMatrix3x3 toRotationMatrix() const;
static QQuaternion fromRotationMatrix(const QMatrix3x3 &rot3x3);
+#ifndef QT_NO_VECTOR3D
+ void getAxes(QVector3D *xAxis, QVector3D *yAxis, QVector3D *zAxis) const;
+ static QQuaternion fromAxes(const QVector3D &xAxis, const QVector3D &yAxis, const QVector3D &zAxis);
+#endif
+
static QQuaternion slerp
(const QQuaternion& q1, const QQuaternion& q2, float t);
static QQuaternion nlerp
diff --git a/tests/auto/gui/math3d/qquaternion/tst_qquaternion.cpp b/tests/auto/gui/math3d/qquaternion/tst_qquaternion.cpp
index e358937a62..6b8eeeaca5 100644
--- a/tests/auto/gui/math3d/qquaternion/tst_qquaternion.cpp
+++ b/tests/auto/gui/math3d/qquaternion/tst_qquaternion.cpp
@@ -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
@@ -89,6 +135,9 @@ private slots:
void fromRotationMatrix_data();
void fromRotationMatrix();
+ void fromAxes_data();
+ void fromAxes();
+
void fromEulerAngles_data();
void fromEulerAngles();
@@ -826,40 +875,58 @@ void tst_QQuaternion::fromRotationMatrix()
QVERIFY(qFuzzyCompare(answer, result) || qFuzzyCompare(-answer, result));
}
-// 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)
+// Test quaternion convertion to and from orthonormal axes.
+void tst_QQuaternion::fromAxes_data()
{
- if (qFuzzyIsNull(p1))
- return qFuzzyIsNull(p2);
- if (qFuzzyIsNull(p2))
- return false;
- // a very slightly looser version of qFuzzyCompare
- // for use with values that are not very close to zero
- return qAbs(p1 - p2) <= 0.00003f * qMin(qAbs(p1), qAbs(p2));
-}
+ 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");
-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;
+ QTest::newRow("null")
+ << 0.0f << 0.0f << 0.0f << 0.0f
+ << QVector3D(1, 0, 0) << QVector3D(0, 1, 0) << QVector3D(0, 0, 1);
- if (p2 < -fPI)
- p2 += 2.0f * fPI;
- else if (p2 > fPI)
- p2 -= 2.0f * fPI;
+ QTest::newRow("xonly")
+ << 1.0f << 0.0f << 0.0f << 90.0f
+ << QVector3D(1, 0, 0) << QVector3D(0, 0, 1) << QVector3D(0, -1, 0);
- return qAbs(qAbs(p1) - qAbs(p2)) <= qDegreesToRadians(0.05f);
-}
+ QTest::newRow("yonly")
+ << 0.0f << 1.0f << 0.0f << 180.0f
+ << QVector3D(-1, 0, 0) << QVector3D(0, 1, 0) << QVector3D(0, 0, -1);
-static inline bool myFuzzyCompareDegrees(float p1, float p2)
+ 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()
{
- p1 = qDegreesToRadians(p1);
- p2 = qDegreesToRadians(p2);
- return myFuzzyCompareRadians(p1, p2);
+ 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 quaternion creation from an axis and an angle.