Introduce QQuaternion::rotationTo(vecFrom, vecTo)
which returns the shortest arc quaternion to rotate vector from to the destination vector to. Change-Id: Ibd7a746789ecdfe6f7fe17e4ac9049f7ac46560d Reviewed-by: Lars Knoll <lars.knoll@digia.com> Reviewed-by: Paul Lemire <paul.lemire@kdab.com>
This commit is contained in:
parent
1e441d298d
commit
da681a41cb
@ -717,6 +717,38 @@ QQuaternion QQuaternion::fromAxes(const QVector3D &xAxis, const QVector3D &yAxis
|
||||
return QQuaternion::fromRotationMatrix(rot3x3);
|
||||
}
|
||||
|
||||
/*!
|
||||
\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.
|
||||
*/
|
||||
QQuaternion QQuaternion::rotationTo(const QVector3D &from, const QVector3D &to)
|
||||
{
|
||||
// Based on Stan Melax's article in Game Programming Gems
|
||||
|
||||
const QVector3D v0(from.normalized());
|
||||
const QVector3D v1(to.normalized());
|
||||
|
||||
float d = QVector3D::dotProduct(v0, v1) + 1.0f;
|
||||
|
||||
// if dest vector is close to the inverse of source vector, ANY axis of rotation is valid
|
||||
if (qFuzzyIsNull(d)) {
|
||||
QVector3D axis = QVector3D::crossProduct(QVector3D(1.0f, 0.0f, 0.0f), v0);
|
||||
if (qFuzzyIsNull(axis.lengthSquared()))
|
||||
axis = QVector3D::crossProduct(QVector3D(0.0f, 1.0f, 0.0f), v0);
|
||||
axis.normalize();
|
||||
|
||||
// same as QQuaternion::fromAxisAndAngle(axis, 180.0f)
|
||||
return QQuaternion(0.0f, axis.x(), axis.y(), axis.z());
|
||||
}
|
||||
|
||||
d = std::sqrt(2.0f * d);
|
||||
const QVector3D axis(QVector3D::crossProduct(v0, v1) / d);
|
||||
|
||||
return QQuaternion(d * 0.5f, axis).normalized();
|
||||
}
|
||||
|
||||
#endif // QT_NO_VECTOR3D
|
||||
|
||||
/*!
|
||||
|
@ -137,6 +137,8 @@ public:
|
||||
#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);
|
||||
|
||||
static QQuaternion rotationTo(const QVector3D &from, const QVector3D &to);
|
||||
#endif
|
||||
|
||||
static QQuaternion slerp
|
||||
|
@ -138,6 +138,9 @@ private slots:
|
||||
void fromAxes_data();
|
||||
void fromAxes();
|
||||
|
||||
void rotationTo_data();
|
||||
void rotationTo();
|
||||
|
||||
void fromEulerAngles_data();
|
||||
void fromEulerAngles();
|
||||
|
||||
@ -929,6 +932,63 @@ void tst_QQuaternion::fromAxes()
|
||||
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()
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user