[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>
This commit is contained in:
parent
c4aea1ea20
commit
1e441d298d
@ -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
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static inline bool myFuzzyCompareRadians(float p1, float p2)
|
||||
void tst_QQuaternion::fromAxes()
|
||||
{
|
||||
static const float fPI = float(M_PI);
|
||||
if (p1 < -fPI)
|
||||
p1 += 2.0f * fPI;
|
||||
else if (p1 > fPI)
|
||||
p1 -= 2.0f * fPI;
|
||||
QFETCH(float, x1);
|
||||
QFETCH(float, y1);
|
||||
QFETCH(float, z1);
|
||||
QFETCH(float, angle);
|
||||
QFETCH(QVector3D, xAxis);
|
||||
QFETCH(QVector3D, yAxis);
|
||||
QFETCH(QVector3D, zAxis);
|
||||
|
||||
if (p2 < -fPI)
|
||||
p2 += 2.0f * fPI;
|
||||
else if (p2 > fPI)
|
||||
p2 -= 2.0f * fPI;
|
||||
QQuaternion result = QQuaternion::fromAxisAndAngle(QVector3D(x1, y1, z1), angle);
|
||||
|
||||
return qAbs(qAbs(p1) - qAbs(p2)) <= qDegreesToRadians(0.05f);
|
||||
}
|
||||
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));
|
||||
|
||||
static inline bool myFuzzyCompareDegrees(float p1, float p2)
|
||||
{
|
||||
p1 = qDegreesToRadians(p1);
|
||||
p2 = qDegreesToRadians(p2);
|
||||
return myFuzzyCompareRadians(p1, p2);
|
||||
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.
|
||||
|
Loading…
Reference in New Issue
Block a user