Introduce QQuaternion::fromDirection()
...which constructs a quaternion using specified forward and upward directions, so that the resulting Z axis "faces" a given forward direction. Change-Id: Ib77b8ab5c359a4880b0d946face87026acdc6f0b Reviewed-by: Paul Lemire <paul.lemire@kdab.com> Reviewed-by: Michael Krasnyk <michael.krasnyk@gmail.com> Reviewed-by: Lars Knoll <lars.knoll@digia.com>
This commit is contained in:
parent
3fb6014ce7
commit
95d034a14f
@ -717,11 +717,41 @@ QQuaternion QQuaternion::fromAxes(const QVector3D &xAxis, const QVector3D &yAxis
|
||||
return QQuaternion::fromRotationMatrix(rot3x3);
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 5.5
|
||||
|
||||
Constructs the quaternion using specified forward direction \a direction
|
||||
and upward direction \a up.
|
||||
If the upward direction was not specified or the forward and upward
|
||||
vectors are collinear, a new orthonormal upward direction will be generated.
|
||||
|
||||
\sa fromAxes(), rotationTo()
|
||||
*/
|
||||
QQuaternion QQuaternion::fromDirection(const QVector3D &direction, const QVector3D &up)
|
||||
{
|
||||
if (qFuzzyIsNull(direction.x()) && qFuzzyIsNull(direction.y()) && qFuzzyIsNull(direction.z()))
|
||||
return QQuaternion();
|
||||
|
||||
const QVector3D zAxis(direction.normalized());
|
||||
QVector3D xAxis(QVector3D::crossProduct(up, zAxis));
|
||||
if (qFuzzyIsNull(xAxis.lengthSquared())) {
|
||||
// collinear or invalid up vector; derive shortest arc to new direction
|
||||
return QQuaternion::rotationTo(QVector3D(0.0f, 0.0f, 1.0f), zAxis);
|
||||
}
|
||||
|
||||
xAxis.normalize();
|
||||
const QVector3D yAxis(QVector3D::crossProduct(zAxis, xAxis));
|
||||
|
||||
return QQuaternion::fromAxes(xAxis, yAxis, zAxis);
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 5.5
|
||||
|
||||
Returns the shortest arc quaternion to rotate from the direction described by the vector \a from
|
||||
to the direction described by the vector \a to.
|
||||
|
||||
\sa fromDirection()
|
||||
*/
|
||||
QQuaternion QQuaternion::rotationTo(const QVector3D &from, const QVector3D &to)
|
||||
{
|
||||
|
@ -138,6 +138,8 @@ public:
|
||||
void getAxes(QVector3D *xAxis, QVector3D *yAxis, QVector3D *zAxis) const;
|
||||
static QQuaternion fromAxes(const QVector3D &xAxis, const QVector3D &yAxis, const QVector3D &zAxis);
|
||||
|
||||
static QQuaternion fromDirection(const QVector3D &direction, const QVector3D &up);
|
||||
|
||||
static QQuaternion rotationTo(const QVector3D &from, const QVector3D &to);
|
||||
#endif
|
||||
|
||||
|
@ -141,6 +141,9 @@ private slots:
|
||||
void rotationTo_data();
|
||||
void rotationTo();
|
||||
|
||||
void fromDirection_data();
|
||||
void fromDirection();
|
||||
|
||||
void fromEulerAngles_data();
|
||||
void fromEulerAngles();
|
||||
|
||||
@ -989,6 +992,97 @@ void tst_QQuaternion::rotationTo()
|
||||
QVERIFY(myFuzzyCompare(vec2, from));
|
||||
}
|
||||
|
||||
static QByteArray testnameForAxis(const QVector3D &axis)
|
||||
{
|
||||
QByteArray testname;
|
||||
if (axis == QVector3D()) {
|
||||
testname = "null";
|
||||
} else {
|
||||
if (axis.x()) {
|
||||
testname += axis.x() < 0 ? "-" : "+";
|
||||
testname += "X";
|
||||
}
|
||||
if (axis.y()) {
|
||||
testname += axis.y() < 0 ? "-" : "+";
|
||||
testname += "Y";
|
||||
}
|
||||
if (axis.z()) {
|
||||
testname += axis.z() < 0 ? "-" : "+";
|
||||
testname += "Z";
|
||||
}
|
||||
}
|
||||
return testname;
|
||||
}
|
||||
|
||||
// Test quaternion convertion to and from orthonormal axes.
|
||||
void tst_QQuaternion::fromDirection_data()
|
||||
{
|
||||
QTest::addColumn<QVector3D>("direction");
|
||||
QTest::addColumn<QVector3D>("up");
|
||||
|
||||
QList<QQuaternion> orientations;
|
||||
orientations << QQuaternion();
|
||||
for (int angle = 45; angle <= 360; angle += 45) {
|
||||
orientations << QQuaternion::fromAxisAndAngle(QVector3D(1, 0, 0), angle)
|
||||
<< QQuaternion::fromAxisAndAngle(QVector3D(0, 1, 0), angle)
|
||||
<< QQuaternion::fromAxisAndAngle(QVector3D(0, 0, 1), angle)
|
||||
<< QQuaternion::fromAxisAndAngle(QVector3D(1, 0, 0), angle)
|
||||
* QQuaternion::fromAxisAndAngle(QVector3D(0, 1, 0), angle)
|
||||
* QQuaternion::fromAxisAndAngle(QVector3D(0, 0, 1), angle);
|
||||
}
|
||||
|
||||
// othonormal up and dir
|
||||
foreach (const QQuaternion &q, orientations) {
|
||||
QVector3D xAxis, yAxis, zAxis;
|
||||
q.getAxes(&xAxis, &yAxis, &zAxis);
|
||||
|
||||
QTest::newRow("dir: " + testnameForAxis(zAxis) + ", up: " + testnameForAxis(yAxis))
|
||||
<< zAxis * 10.0f << yAxis * 10.0f;
|
||||
}
|
||||
|
||||
// collinear up and dir
|
||||
QTest::newRow("dir: +X, up: +X") << QVector3D(10.0f, 0.0f, 0.0f) << QVector3D(10.0f, 0.0f, 0.0f);
|
||||
QTest::newRow("dir: +X, up: -X") << QVector3D(10.0f, 0.0f, 0.0f) << QVector3D(-10.0f, 0.0f, 0.0f);
|
||||
QTest::newRow("dir: +Y, up: +Y") << QVector3D(0.0f, 10.0f, 0.0f) << QVector3D(0.0f, 10.0f, 0.0f);
|
||||
QTest::newRow("dir: +Y, up: -Y") << QVector3D(0.0f, 10.0f, 0.0f) << QVector3D(0.0f, -10.0f, 0.0f);
|
||||
QTest::newRow("dir: +Z, up: +Z") << QVector3D(0.0f, 0.0f, 10.0f) << QVector3D(0.0f, 0.0f, 10.0f);
|
||||
QTest::newRow("dir: +Z, up: -Z") << QVector3D(0.0f, 0.0f, 10.0f) << QVector3D(0.0f, 0.0f, -10.0f);
|
||||
QTest::newRow("dir: +X+Y+Z, up: +X+Y+Z") << QVector3D(10.0f, 10.0f, 10.0f) << QVector3D(10.0f, 10.0f, 10.0f);
|
||||
QTest::newRow("dir: +X+Y+Z, up: -X-Y-Z") << QVector3D(10.0f, 10.0f, 10.0f) << QVector3D(-10.0f, -10.0f, -10.0f);
|
||||
|
||||
// invalid up
|
||||
foreach (const QQuaternion &q, orientations) {
|
||||
QVector3D xAxis, yAxis, zAxis;
|
||||
q.getAxes(&xAxis, &yAxis, &zAxis);
|
||||
|
||||
QTest::newRow("dir: " + testnameForAxis(zAxis) + ", up: null")
|
||||
<< zAxis * 10.0f << QVector3D();
|
||||
}
|
||||
}
|
||||
void tst_QQuaternion::fromDirection()
|
||||
{
|
||||
QFETCH(QVector3D, direction);
|
||||
QFETCH(QVector3D, up);
|
||||
|
||||
QVector3D expextedZ(direction != QVector3D() ? direction.normalized() : QVector3D(0, 0, 1));
|
||||
QVector3D expextedY(up.normalized());
|
||||
|
||||
QQuaternion result = QQuaternion::fromDirection(direction, up);
|
||||
QVERIFY(myFuzzyCompare(result, result.normalized()));
|
||||
|
||||
QVector3D xAxis, yAxis, zAxis;
|
||||
result.getAxes(&xAxis, &yAxis, &zAxis);
|
||||
|
||||
QVERIFY(myFuzzyCompare(zAxis, expextedZ));
|
||||
|
||||
if (!qFuzzyIsNull(QVector3D::crossProduct(expextedZ, expextedY).lengthSquared())) {
|
||||
QVector3D expextedX(QVector3D::crossProduct(expextedY, expextedZ));
|
||||
|
||||
QVERIFY(myFuzzyCompare(yAxis, expextedY));
|
||||
QVERIFY(myFuzzyCompare(xAxis, expextedX));
|
||||
}
|
||||
}
|
||||
|
||||
// Test quaternion creation from an axis and an angle.
|
||||
void tst_QQuaternion::fromEulerAngles_data()
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user