summaryrefslogtreecommitdiffstats
path: root/src/gui/math3d/qquaternion.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/math3d/qquaternion.cpp')
-rw-r--r--src/gui/math3d/qquaternion.cpp163
1 files changed, 64 insertions, 99 deletions
diff --git a/src/gui/math3d/qquaternion.cpp b/src/gui/math3d/qquaternion.cpp
index ba82aaa737..e7c5945208 100644
--- a/src/gui/math3d/qquaternion.cpp
+++ b/src/gui/math3d/qquaternion.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtGui module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** 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 The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qquaternion.h"
#include <QtCore/qdatastream.h>
@@ -62,14 +26,14 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn QQuaternion::QQuaternion()
+ \fn QQuaternion::QQuaternion() noexcept
Constructs an identity quaternion (1, 0, 0, 0), i.e. with the vector (0, 0, 0)
and scalar 1.
*/
/*!
- \fn QQuaternion::QQuaternion(Qt::Initialization)
+ \fn QQuaternion::QQuaternion(Qt::Initialization) noexcept
\since 5.5
\internal
@@ -77,7 +41,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn QQuaternion::QQuaternion(float scalar, float xpos, float ypos, float zpos)
+ \fn QQuaternion::QQuaternion(float scalar, float xpos, float ypos, float zpos) noexcept
Constructs a quaternion with the vector (\a xpos, \a ypos, \a zpos)
and \a scalar.
@@ -86,7 +50,7 @@ QT_BEGIN_NAMESPACE
#ifndef QT_NO_VECTOR3D
/*!
- \fn QQuaternion::QQuaternion(float scalar, const QVector3D& vector)
+ \fn QQuaternion::QQuaternion(float scalar, const QVector3D &vector) noexcept
Constructs a quaternion vector from the specified \a vector and
\a scalar.
@@ -95,7 +59,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn QVector3D QQuaternion::vector() const
+ \fn QVector3D QQuaternion::vector() const noexcept
Returns the vector component of this quaternion.
@@ -103,7 +67,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn void QQuaternion::setVector(const QVector3D& vector)
+ \fn void QQuaternion::setVector(const QVector3D &vector) noexcept
Sets the vector component of this quaternion to \a vector.
@@ -113,7 +77,7 @@ QT_BEGIN_NAMESPACE
#endif
/*!
- \fn void QQuaternion::setVector(float x, float y, float z)
+ \fn void QQuaternion::setVector(float x, float y, float z) noexcept
Sets the vector component of this quaternion to (\a x, \a y, \a z).
@@ -123,13 +87,13 @@ QT_BEGIN_NAMESPACE
#ifndef QT_NO_VECTOR4D
/*!
- \fn QQuaternion::QQuaternion(const QVector4D& vector)
+ \fn QQuaternion::QQuaternion(const QVector4D &vector) noexcept
Constructs a quaternion from the components of \a vector.
*/
/*!
- \fn QVector4D QQuaternion::toVector4D() const
+ \fn QVector4D QQuaternion::toVector4D() const noexcept
Returns this quaternion as a 4D vector.
*/
@@ -137,14 +101,14 @@ QT_BEGIN_NAMESPACE
#endif
/*!
- \fn bool QQuaternion::isNull() const
+ \fn bool QQuaternion::isNull() const noexcept
Returns \c true if the x, y, z, and scalar components of this
quaternion are set to 0.0; otherwise returns \c false.
*/
/*!
- \fn bool QQuaternion::isIdentity() const
+ \fn bool QQuaternion::isIdentity() const noexcept
Returns \c true if the x, y, and z components of this
quaternion are set to 0.0, and the scalar component is set
@@ -152,7 +116,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn float QQuaternion::x() const
+ \fn float QQuaternion::x() const noexcept
Returns the x coordinate of this quaternion's vector.
@@ -160,7 +124,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn float QQuaternion::y() const
+ \fn float QQuaternion::y() const noexcept
Returns the y coordinate of this quaternion's vector.
@@ -168,7 +132,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn float QQuaternion::z() const
+ \fn float QQuaternion::z() const noexcept
Returns the z coordinate of this quaternion's vector.
@@ -176,7 +140,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn float QQuaternion::scalar() const
+ \fn float QQuaternion::scalar() const noexcept
Returns the scalar component of this quaternion.
@@ -184,7 +148,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn void QQuaternion::setX(float x)
+ \fn void QQuaternion::setX(float x) noexcept
Sets the x coordinate of this quaternion's vector to the given
\a x coordinate.
@@ -193,7 +157,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn void QQuaternion::setY(float y)
+ \fn void QQuaternion::setY(float y) noexcept
Sets the y coordinate of this quaternion's vector to the given
\a y coordinate.
@@ -202,7 +166,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn void QQuaternion::setZ(float z)
+ \fn void QQuaternion::setZ(float z) noexcept
Sets the z coordinate of this quaternion's vector to the given
\a z coordinate.
@@ -211,7 +175,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn void QQuaternion::setScalar(float scalar)
+ \fn void QQuaternion::setScalar(float scalar) noexcept
Sets the scalar component of this quaternion to \a scalar.
@@ -219,7 +183,7 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn float QQuaternion::dotProduct(const QQuaternion &q1, const QQuaternion &q2)
+ \fn float QQuaternion::dotProduct(const QQuaternion &q1, const QQuaternion &q2) noexcept
\since 5.5
Returns the dot product of \a q1 and \a q2.
@@ -263,8 +227,6 @@ float QQuaternion::lengthSquared() const
QQuaternion QQuaternion::normalized() const
{
const float scale = length();
- if (qFuzzyCompare(scale, 1.0f))
- return *this;
if (qFuzzyIsNull(scale))
return QQuaternion(0.0f, 0.0f, 0.0f, 0.0f);
return *this / scale;
@@ -279,7 +241,7 @@ QQuaternion QQuaternion::normalized() const
void QQuaternion::normalize()
{
const float len = length();
- if (qFuzzyCompare(len, 1.0f) || qFuzzyIsNull(len))
+ if (qFuzzyIsNull(len))
return;
xp /= len;
@@ -289,7 +251,7 @@ void QQuaternion::normalize()
}
/*!
- \fn QQuaternion QQuaternion::inverted() const
+ \fn QQuaternion QQuaternion::inverted() const noexcept
\since 5.5
Returns the inverse of this quaternion.
@@ -299,7 +261,7 @@ void QQuaternion::normalize()
*/
/*!
- \fn QQuaternion QQuaternion::conjugated() const
+ \fn QQuaternion QQuaternion::conjugated() const noexcept
\since 5.5
Returns the conjugate of this quaternion, which is
@@ -316,13 +278,13 @@ void QQuaternion::normalize()
\snippet code/src_gui_math3d_qquaternion.cpp 1
*/
-QVector3D QQuaternion::rotatedVector(const QVector3D& vector) const
+QVector3D QQuaternion::rotatedVector(const QVector3D &vector) const
{
return (*this * QQuaternion(0, vector) * conjugated()).vector();
}
/*!
- \fn QQuaternion &QQuaternion::operator+=(const QQuaternion &quaternion)
+ \fn QQuaternion &QQuaternion::operator+=(const QQuaternion &quaternion) noexcept
Adds the given \a quaternion to this quaternion and returns a reference to
this quaternion.
@@ -331,7 +293,7 @@ QVector3D QQuaternion::rotatedVector(const QVector3D& vector) const
*/
/*!
- \fn QQuaternion &QQuaternion::operator-=(const QQuaternion &quaternion)
+ \fn QQuaternion &QQuaternion::operator-=(const QQuaternion &quaternion) noexcept
Subtracts the given \a quaternion from this quaternion and returns a
reference to this quaternion.
@@ -340,7 +302,7 @@ QVector3D QQuaternion::rotatedVector(const QVector3D& vector) const
*/
/*!
- \fn QQuaternion &QQuaternion::operator*=(float factor)
+ \fn QQuaternion &QQuaternion::operator*=(float factor) noexcept
Multiplies this quaternion's components by the given \a factor, and
returns a reference to this quaternion.
@@ -349,7 +311,7 @@ QVector3D QQuaternion::rotatedVector(const QVector3D& vector) const
*/
/*!
- \fn QQuaternion &QQuaternion::operator*=(const QQuaternion &quaternion)
+ \fn QQuaternion &QQuaternion::operator*=(const QQuaternion &quaternion) noexcept
Multiplies this quaternion by \a quaternion and returns a reference
to this quaternion.
@@ -367,7 +329,7 @@ QVector3D QQuaternion::rotatedVector(const QVector3D& vector) const
#ifndef QT_NO_VECTOR3D
/*!
- \fn void QQuaternion::getAxisAndAngle(QVector3D *axis, float *angle) const
+ \fn void QQuaternion::getAxisAndAngle(QVector3D *axis, float *angle) const noexcept
\since 5.5
\overload
@@ -383,7 +345,7 @@ QVector3D QQuaternion::rotatedVector(const QVector3D& vector) const
\sa getAxisAndAngle()
*/
-QQuaternion QQuaternion::fromAxisAndAngle(const QVector3D& axis, float angle)
+QQuaternion QQuaternion::fromAxisAndAngle(const QVector3D &axis, float angle)
{
// Algorithm from:
// http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q56
@@ -424,7 +386,7 @@ void QQuaternion::getAxisAndAngle(float *x, float *y, float *z, float *angle) co
*y = yp / length;
*z = zp / length;
}
- *angle = qRadiansToDegrees(2.0f * std::acos(wp));
+ *angle = qRadiansToDegrees(2.0f * std::atan2(length, wp));
} else {
// angle is 0 (mod 2*pi), so any axis will fit
*x = *y = *z = *angle = 0.0f;
@@ -492,10 +454,14 @@ void QQuaternion::getEulerAngles(float *pitch, float *yaw, float *roll) const
Q_ASSERT(pitch && yaw && roll);
// Algorithm adapted from:
- // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q37
+ // https://ingmec.ual.es/~jlblanco/papers/jlblanco2010geometry3D_techrep.pdf
+ // "A tutorial on SE(3) transformation parameterizations and on-manifold optimization".
+ // We can only detect Gimbal lock when we normalize, which we can't do when
+ // length is nearly zero. Do so before multiplying coordinates, to avoid
+ // underflow.
const float len = length();
- const bool rescale = !qFuzzyCompare(len, 1.0f) && !qFuzzyIsNull(len);
+ const bool rescale = !qFuzzyIsNull(len);
const float xps = rescale ? xp / len : xp;
const float yps = rescale ? yp / len : yp;
const float zps = rescale ? zp / len : zp;
@@ -511,24 +477,23 @@ void QQuaternion::getEulerAngles(float *pitch, float *yaw, float *roll) const
const float zz = zps * zps;
const float zw = zps * wps;
+ // For the common case, we have a hidden division by cos(pitch) to calculate
+ // yaw and roll: atan2(a / cos(pitch), b / cos(pitch)) = atan2(a, b). This equation
+ // wouldn't work if cos(pitch) is close to zero (i.e. abs(sin(pitch)) =~ 1.0).
+ // This threshold is copied from qFuzzyIsNull() to avoid the hidden division by zero.
+ constexpr float epsilon = 0.00001f;
+
const float sinp = -2.0f * (yz - xw);
- if (std::abs(sinp) >= 1.0f)
- *pitch = std::copysign(M_PI_2, sinp);
- else
+ if (std::abs(sinp) < 1.0f - epsilon) {
*pitch = std::asin(sinp);
- if (*pitch < M_PI_2) {
- if (*pitch > -M_PI_2) {
- *yaw = std::atan2(2.0f * (xz + yw), 1.0f - 2.0f * (xx + yy));
- *roll = std::atan2(2.0f * (xy + zw), 1.0f - 2.0f * (xx + zz));
- } else {
- // not a unique solution
- *roll = 0.0f;
- *yaw = -std::atan2(-2.0f * (xy - zw), 1.0f - 2.0f * (yy + zz));
- }
+ *yaw = std::atan2(2.0f * (xz + yw), 1.0f - 2.0f * (xx + yy));
+ *roll = std::atan2(2.0f * (xy + zw), 1.0f - 2.0f * (xx + zz));
} else {
- // not a unique solution
+ // Gimbal lock case, which doesn't have a unique solution. We just use
+ // XY rotation.
+ *pitch = std::copysign(static_cast<float>(M_PI_2), sinp);
+ *yaw = 2.0f * std::atan2(yps, wps);
*roll = 0.0f;
- *yaw = std::atan2(-2.0f * (xy - zw), 1.0f - 2.0f * (yy + zz));
}
*pitch = qRadiansToDegrees(*pitch);
@@ -773,21 +738,21 @@ QQuaternion QQuaternion::rotationTo(const QVector3D &from, const QVector3D &to)
#endif // QT_NO_VECTOR3D
/*!
- \fn bool QQuaternion::operator==(const QQuaternion &q1, const QQuaternion &q2)
+ \fn bool QQuaternion::operator==(const QQuaternion &q1, const QQuaternion &q2) noexcept
Returns \c true if \a q1 is equal to \a q2; otherwise returns \c false.
This operator uses an exact floating-point comparison.
*/
/*!
- \fn bool QQuaternion::operator!=(const QQuaternion &q1, const QQuaternion &q2)
+ \fn bool QQuaternion::operator!=(const QQuaternion &q1, const QQuaternion &q2) noexcept
Returns \c true if \a q1 is not equal to \a q2; otherwise returns \c false.
This operator uses an exact floating-point comparison.
*/
/*!
- \fn const QQuaternion operator+(const QQuaternion &q1, const QQuaternion &q2)
+ \fn const QQuaternion operator+(const QQuaternion &q1, const QQuaternion &q2) noexcept
\relates QQuaternion
Returns a QQuaternion object that is the sum of the given quaternions,
@@ -797,7 +762,7 @@ QQuaternion QQuaternion::rotationTo(const QVector3D &from, const QVector3D &to)
*/
/*!
- \fn const QQuaternion operator-(const QQuaternion &q1, const QQuaternion &q2)
+ \fn const QQuaternion operator-(const QQuaternion &q1, const QQuaternion &q2) noexcept
\relates QQuaternion
Returns a QQuaternion object that is formed by subtracting
@@ -807,7 +772,7 @@ QQuaternion QQuaternion::rotationTo(const QVector3D &from, const QVector3D &to)
*/
/*!
- \fn const QQuaternion operator*(float factor, const QQuaternion &quaternion)
+ \fn const QQuaternion operator*(float factor, const QQuaternion &quaternion) noexcept
\relates QQuaternion
Returns a copy of the given \a quaternion, multiplied by the
@@ -817,7 +782,7 @@ QQuaternion QQuaternion::rotationTo(const QVector3D &from, const QVector3D &to)
*/
/*!
- \fn const QQuaternion operator*(const QQuaternion &quaternion, float factor)
+ \fn const QQuaternion operator*(const QQuaternion &quaternion, float factor) noexcept
\relates QQuaternion
Returns a copy of the given \a quaternion, multiplied by the
@@ -827,7 +792,7 @@ QQuaternion QQuaternion::rotationTo(const QVector3D &from, const QVector3D &to)
*/
/*!
- \fn const QQuaternion operator*(const QQuaternion &q1, const QQuaternion& q2)
+ \fn const QQuaternion operator*(const QQuaternion &q1, const QQuaternion &q2) noexcept
\relates QQuaternion
Multiplies \a q1 and \a q2 using quaternion multiplication.
@@ -838,7 +803,7 @@ QQuaternion QQuaternion::rotationTo(const QVector3D &from, const QVector3D &to)
*/
/*!
- \fn const QQuaternion operator-(const QQuaternion &quaternion)
+ \fn const QQuaternion operator-(const QQuaternion &quaternion) noexcept
\relates QQuaternion
\overload
@@ -861,7 +826,7 @@ QQuaternion QQuaternion::rotationTo(const QVector3D &from, const QVector3D &to)
#ifndef QT_NO_VECTOR3D
/*!
- \fn QVector3D operator*(const QQuaternion &quaternion, const QVector3D &vec)
+ \fn QVector3D operator*(const QQuaternion &quaternion, const QVector3D &vec) noexcept
\since 5.5
\relates QQuaternion
@@ -871,7 +836,7 @@ QQuaternion QQuaternion::rotationTo(const QVector3D &from, const QVector3D &to)
#endif
/*!
- \fn bool qFuzzyCompare(const QQuaternion& q1, const QQuaternion& q2)
+ \fn bool qFuzzyCompare(const QQuaternion &q1, const QQuaternion &q2) noexcept
\relates QQuaternion
Returns \c true if \a q1 and \a q2 are equal, allowing for a small
@@ -890,7 +855,7 @@ QQuaternion QQuaternion::rotationTo(const QVector3D &from, const QVector3D &to)
\sa nlerp()
*/
QQuaternion QQuaternion::slerp
- (const QQuaternion& q1, const QQuaternion& q2, float t)
+ (const QQuaternion &q1, const QQuaternion &q2, float t)
{
// Handle the easy cases first.
if (t <= 0.0f)
@@ -939,7 +904,7 @@ QQuaternion QQuaternion::slerp
\sa slerp()
*/
QQuaternion QQuaternion::nlerp
- (const QQuaternion& q1, const QQuaternion& q2, float t)
+ (const QQuaternion &q1, const QQuaternion &q2, float t)
{
// Handle the easy cases first.
if (t <= 0.0f)