diff options
Diffstat (limited to 'src/gui/math3d')
-rw-r--r-- | src/gui/math3d/qgenericmatrix.cpp | 22 | ||||
-rw-r--r-- | src/gui/math3d/qgenericmatrix.h | 18 | ||||
-rw-r--r-- | src/gui/math3d/qmatrix4x4.cpp | 114 | ||||
-rw-r--r-- | src/gui/math3d/qmatrix4x4.h | 22 | ||||
-rw-r--r-- | src/gui/math3d/qquaternion.cpp | 450 | ||||
-rw-r--r-- | src/gui/math3d/qquaternion.h | 80 | ||||
-rw-r--r-- | src/gui/math3d/qvector2d.cpp | 52 | ||||
-rw-r--r-- | src/gui/math3d/qvector2d.h | 29 | ||||
-rw-r--r-- | src/gui/math3d/qvector3d.cpp | 117 | ||||
-rw-r--r-- | src/gui/math3d/qvector3d.h | 34 | ||||
-rw-r--r-- | src/gui/math3d/qvector4d.cpp | 52 | ||||
-rw-r--r-- | src/gui/math3d/qvector4d.h | 31 |
12 files changed, 861 insertions, 160 deletions
diff --git a/src/gui/math3d/qgenericmatrix.cpp b/src/gui/math3d/qgenericmatrix.cpp index 995cf1716c..7b46bb452c 100644 --- a/src/gui/math3d/qgenericmatrix.cpp +++ b/src/gui/math3d/qgenericmatrix.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. ** @@ -10,9 +10,9 @@ ** 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser @@ -23,8 +23,8 @@ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ @@ -61,6 +61,14 @@ QT_BEGIN_NAMESPACE */ /*! + \fn QGenericMatrix::QGenericMatrix(Qt::Initialization) + \since 5.5 + \internal + + Constructs a NxM matrix without initializing the contents. +*/ + +/*! \fn QGenericMatrix::QGenericMatrix(const QGenericMatrix<N, M, T>& other) Constructs a copy of \a other. diff --git a/src/gui/math3d/qgenericmatrix.h b/src/gui/math3d/qgenericmatrix.h index 5b45ab7fa1..89bc09f544 100644 --- a/src/gui/math3d/qgenericmatrix.h +++ b/src/gui/math3d/qgenericmatrix.h @@ -1,7 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. ** @@ -10,9 +10,9 @@ ** 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser @@ -23,8 +23,8 @@ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ @@ -46,6 +46,7 @@ class QGenericMatrix { public: QGenericMatrix(); + explicit QGenericMatrix(Qt::Initialization) {} QGenericMatrix(const QGenericMatrix<N, M, T>& other); explicit QGenericMatrix(const T *values); @@ -340,6 +341,7 @@ typedef QGenericMatrix<4, 3, float> QMatrix4x3; template <int N, int M, typename T> QDebug operator<<(QDebug dbg, const QGenericMatrix<N, M, T> &m) { + QDebugStateSaver saver(dbg); dbg.nospace() << "QGenericMatrix<" << N << ", " << M << ", " << QTypeInfo<T>::name() << ">(" << endl << qSetFieldWidth(10); @@ -349,7 +351,7 @@ QDebug operator<<(QDebug dbg, const QGenericMatrix<N, M, T> &m) dbg << endl; } dbg << qSetFieldWidth(0) << ')'; - return dbg.space(); + return dbg; } #endif diff --git a/src/gui/math3d/qmatrix4x4.cpp b/src/gui/math3d/qmatrix4x4.cpp index c8a036918c..eb7c7f4b7a 100644 --- a/src/gui/math3d/qmatrix4x4.cpp +++ b/src/gui/math3d/qmatrix4x4.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. ** @@ -10,9 +10,9 @@ ** 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser @@ -23,8 +23,8 @@ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ @@ -76,6 +76,14 @@ static const float inv_dist_to_plane = 1.0f / 1024.0f; */ /*! + \fn QMatrix4x4::QMatrix4x4(Qt::Initialization) + \since 5.5 + \internal + + Constructs a matrix without initializing the contents. +*/ + +/*! Constructs a matrix from the given 16 floating-point \a values. The contents of the array \a values is assumed to be in row-major order. @@ -290,6 +298,18 @@ QMatrix4x4::QMatrix4x4(const QTransform& transform) */ /*! + \fn bool QMatrix4x4::isAffine() const + \since 5.5 + + Returns \c true if this matrix is affine matrix; false otherwise. + + An affine matrix is a 4x4 matrix with row 3 equal to (0, 0, 0, 1), + e.g. no projective coefficients. + + \sa isIdentity() +*/ + +/*! \fn bool QMatrix4x4::isIdentity() const Returns \c true if this matrix is the identity; false otherwise. @@ -1105,8 +1125,8 @@ void QMatrix4x4::rotate(float angle, float x, float y, float z) c = -1.0f; } else { float a = angle * M_PI / 180.0f; - c = cosf(a); - s = sinf(a); + c = std::cos(a); + s = std::sin(a); } if (x == 0.0f) { if (y == 0.0f) { @@ -1166,7 +1186,7 @@ void QMatrix4x4::rotate(float angle, float x, float y, float z) double(y) * double(y) + double(z) * double(z); if (!qFuzzyCompare(len, 1.0) && !qFuzzyIsNull(len)) { - len = sqrt(len); + len = std::sqrt(len); x = float(double(x) / len); y = float(double(y) / len); z = float(double(z) / len); @@ -1214,8 +1234,8 @@ void QMatrix4x4::projectedRotate(float angle, float x, float y, float z) c = -1.0f; } else { float a = angle * M_PI / 180.0f; - c = cosf(a); - s = sinf(a); + c = std::cos(a); + s = std::sin(a); } if (x == 0.0f) { if (y == 0.0f) { @@ -1262,7 +1282,7 @@ void QMatrix4x4::projectedRotate(float angle, float x, float y, float z) double(y) * double(y) + double(z) * double(z); if (!qFuzzyCompare(len, 1.0) && !qFuzzyIsNull(len)) { - len = sqrt(len); + len = std::sqrt(len); x = float(double(x) / len); y = float(double(y) / len); z = float(double(z) / len); @@ -1302,27 +1322,33 @@ void QMatrix4x4::rotate(const QQuaternion& quaternion) { // Algorithm from: // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q54 - QMatrix4x4 m(1); - float xx = quaternion.x() * quaternion.x(); - float xy = quaternion.x() * quaternion.y(); - float xz = quaternion.x() * quaternion.z(); - float xw = quaternion.x() * quaternion.scalar(); - float yy = quaternion.y() * quaternion.y(); - float yz = quaternion.y() * quaternion.z(); - float yw = quaternion.y() * quaternion.scalar(); - float zz = quaternion.z() * quaternion.z(); - float zw = quaternion.z() * quaternion.scalar(); - m.m[0][0] = 1.0f - 2 * (yy + zz); - m.m[1][0] = 2 * (xy - zw); - m.m[2][0] = 2 * (xz + yw); + + QMatrix4x4 m(Qt::Uninitialized); + + const float f2x = quaternion.x() + quaternion.x(); + const float f2y = quaternion.y() + quaternion.y(); + const float f2z = quaternion.z() + quaternion.z(); + const float f2xw = f2x * quaternion.scalar(); + const float f2yw = f2y * quaternion.scalar(); + const float f2zw = f2z * quaternion.scalar(); + const float f2xx = f2x * quaternion.x(); + const float f2xy = f2x * quaternion.y(); + const float f2xz = f2x * quaternion.z(); + const float f2yy = f2y * quaternion.y(); + const float f2yz = f2y * quaternion.z(); + const float f2zz = f2z * quaternion.z(); + + m.m[0][0] = 1.0f - (f2yy + f2zz); + m.m[1][0] = f2xy - f2zw; + m.m[2][0] = f2xz + f2yw; m.m[3][0] = 0.0f; - m.m[0][1] = 2 * (xy + zw); - m.m[1][1] = 1.0f - 2 * (xx + zz); - m.m[2][1] = 2 * (yz - xw); + m.m[0][1] = f2xy + f2zw; + m.m[1][1] = 1.0f - (f2xx + f2zz); + m.m[2][1] = f2yz - f2xw; m.m[3][1] = 0.0f; - m.m[0][2] = 2 * (xz - yw); - m.m[1][2] = 2 * (yz + xw); - m.m[2][2] = 1.0f - 2 * (xx + yy); + m.m[0][2] = f2xz - f2yw; + m.m[1][2] = f2yz + f2xw; + m.m[2][2] = 1.0f - (f2xx + f2yy); m.m[3][2] = 0.0f; m.m[0][3] = 0.0f; m.m[1][3] = 0.0f; @@ -1467,10 +1493,10 @@ void QMatrix4x4::perspective(float verticalAngle, float aspectRatio, float nearP // Construct the projection. QMatrix4x4 m(1); float radians = (verticalAngle / 2.0f) * M_PI / 180.0f; - float sine = sinf(radians); + float sine = std::sin(radians); if (sine == 0.0f) return; - float cotan = cosf(radians) / sine; + float cotan = std::cos(radians) / sine; float clip = farPlane - nearPlane; m.m[0][0] = cotan / aspectRatio; m.m[1][0] = 0.0f; @@ -1497,14 +1523,21 @@ void QMatrix4x4::perspective(float verticalAngle, float aspectRatio, float nearP #ifndef QT_NO_VECTOR3D /*! - Multiplies this matrix by another that applies an \a eye position - transformation. The \a center value indicates the center of the - view that the \a eye is looking at. The \a up value indicates - which direction should be considered up with respect to the \a eye. + Multiplies this matrix by a viewing matrix derived from an eye + point. The \a center value indicates the center of the view that + the \a eye is looking at. The \a up value indicates which direction + should be considered up with respect to the \a eye. + + \note The \a up vector must not be parallel to the line of sight + from \a eye to \a center. */ void QMatrix4x4::lookAt(const QVector3D& eye, const QVector3D& center, const QVector3D& up) { - QVector3D forward = (center - eye).normalized(); + QVector3D forward = center - eye; + if (qFuzzyIsNull(forward.x()) && qFuzzyIsNull(forward.y()) && qFuzzyIsNull(forward.z())) + return; + + forward.normalize(); QVector3D side = QVector3D::crossProduct(forward, up).normalized(); QVector3D upVector = QVector3D::crossProduct(side, forward); @@ -1977,6 +2010,7 @@ QMatrix4x4::operator QVariant() const QDebug operator<<(QDebug dbg, const QMatrix4x4 &m) { + QDebugStateSaver saver(dbg); // Create a string that represents the matrix type. QByteArray bits; if (m.flagBits == QMatrix4x4::Identity) { @@ -2006,7 +2040,7 @@ QDebug operator<<(QDebug dbg, const QMatrix4x4 &m) << m(2, 0) << m(2, 1) << m(2, 2) << m(2, 3) << endl << m(3, 0) << m(3, 1) << m(3, 2) << m(3, 3) << endl << qSetFieldWidth(0) << ')'; - return dbg.space(); + return dbg; } #endif diff --git a/src/gui/math3d/qmatrix4x4.h b/src/gui/math3d/qmatrix4x4.h index 31a4dbf1ca..6f6ff235cf 100644 --- a/src/gui/math3d/qmatrix4x4.h +++ b/src/gui/math3d/qmatrix4x4.h @@ -1,7 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. ** @@ -10,9 +10,9 @@ ** 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser @@ -23,8 +23,8 @@ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ @@ -53,6 +53,7 @@ class Q_GUI_EXPORT QMatrix4x4 { public: inline QMatrix4x4() { setToIdentity(); } + explicit QMatrix4x4(Qt::Initialization) : flagBits(General) {} explicit QMatrix4x4(const float *values); inline QMatrix4x4(float m11, float m12, float m13, float m14, float m21, float m22, float m23, float m24, @@ -77,6 +78,8 @@ public: inline void setRow(int index, const QVector4D& value); #endif + inline bool isAffine() const; + inline bool isIdentity() const; inline void setToIdentity(); @@ -302,6 +305,11 @@ inline void QMatrix4x4::setRow(int index, const QVector4D& value) Q_GUI_EXPORT QMatrix4x4 operator/(const QMatrix4x4& matrix, float divisor); +inline bool QMatrix4x4::isAffine() const +{ + return m[0][3] == 0.0f && m[1][3] == 0.0f && m[2][3] == 0.0f && m[3][3] == 1.0f; +} + inline bool QMatrix4x4::isIdentity() const { if (flagBits == Identity) diff --git a/src/gui/math3d/qquaternion.cpp b/src/gui/math3d/qquaternion.cpp index f1af8922ca..6914a0b45e 100644 --- a/src/gui/math3d/qquaternion.cpp +++ b/src/gui/math3d/qquaternion.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. ** @@ -10,9 +10,9 @@ ** 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser @@ -23,8 +23,8 @@ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ @@ -63,6 +63,14 @@ QT_BEGIN_NAMESPACE */ /*! + \fn QQuaternion::QQuaternion(Qt::Initialization) + \since 5.5 + \internal + + Constructs a quaternion without initializing the contents. +*/ + +/*! \fn QQuaternion::QQuaternion(float scalar, float xpos, float ypos, float zpos) Constructs a quaternion with the vector (\a xpos, \a ypos, \a zpos) @@ -205,19 +213,28 @@ QT_BEGIN_NAMESPACE */ /*! + \fn float QQuaternion::dotProduct(const QQuaternion &q1, const QQuaternion &q2) + \since 5.5 + + Returns the dot product of \a q1 and \a q2. + + \sa length() +*/ + +/*! Returns the length of the quaternion. This is also called the "norm". - \sa lengthSquared(), normalized() + \sa lengthSquared(), normalized(), dotProduct() */ float QQuaternion::length() const { - return qSqrt(xp * xp + yp * yp + zp * zp + wp * wp); + return std::sqrt(xp * xp + yp * yp + zp * zp + wp * wp); } /*! Returns the squared length of the quaternion. - \sa length() + \sa length(), dotProduct() */ float QQuaternion::lengthSquared() const { @@ -232,7 +249,7 @@ float QQuaternion::lengthSquared() const will be returned as-is. Otherwise the normalized form of the quaternion of length 1 will be returned. - \sa length(), normalize() + \sa normalize(), length(), dotProduct() */ QQuaternion QQuaternion::normalized() const { @@ -244,7 +261,7 @@ QQuaternion QQuaternion::normalized() const if (qFuzzyIsNull(len - 1.0f)) return *this; else if (!qFuzzyIsNull(len)) - return *this / qSqrt(len); + return *this / std::sqrt(len); else return QQuaternion(0.0f, 0.0f, 0.0f, 0.0f); } @@ -265,7 +282,7 @@ void QQuaternion::normalize() if (qFuzzyIsNull(len - 1.0f) || qFuzzyIsNull(len)) return; - len = qSqrt(len); + len = std::sqrt(len); xp /= len; yp /= len; @@ -274,6 +291,16 @@ void QQuaternion::normalize() } /*! + \fn QQuaternion QQuaternion::inverted() const + \since 5.5 + + Returns the inverse of this quaternion. + If this quaternion is null, then a null quaternion is returned. + + \sa isNull(), length() +*/ + +/*! \fn QQuaternion QQuaternion::conjugate() const Returns the conjugate of this quaternion, which is @@ -345,8 +372,21 @@ QVector3D QQuaternion::rotatedVector(const QVector3D& vector) const #ifndef QT_NO_VECTOR3D /*! + \fn void QQuaternion::getAxisAndAngle(QVector3D *axis, float *angle) const + \since 5.5 + \overload + + Extracts a 3D axis \a axis and a rotating angle \a angle (in degrees) + that corresponds to this quaternion. + + \sa fromAxisAndAngle() +*/ + +/*! Creates a normalized quaternion that corresponds to rotating through \a angle degrees about the specified 3D \a axis. + + \sa getAxisAndAngle() */ QQuaternion QQuaternion::fromAxisAndAngle(const QVector3D& axis, float angle) { @@ -355,8 +395,8 @@ QQuaternion QQuaternion::fromAxisAndAngle(const QVector3D& axis, float angle) // We normalize the result just in case the values are close // to zero, as suggested in the above FAQ. float a = (angle / 2.0f) * M_PI / 180.0f; - float s = sinf(a); - float c = cosf(a); + float s = std::sin(a); + float c = std::cos(a); QVector3D ax = axis.normalized(); return QQuaternion(c, ax.x() * s, ax.y() * s, ax.z() * s).normalized(); } @@ -364,24 +404,353 @@ QQuaternion QQuaternion::fromAxisAndAngle(const QVector3D& axis, float angle) #endif /*! + \since 5.5 + + Extracts a 3D axis (\a x, \a y, \a z) and a rotating angle \a angle (in degrees) + that corresponds to this quaternion. + + \sa fromAxisAndAngle() +*/ +void QQuaternion::getAxisAndAngle(float *x, float *y, float *z, float *angle) const +{ + Q_ASSERT(x && y && z && angle); + + // The quaternion representing the rotation is + // q = cos(A/2)+sin(A/2)*(x*i+y*j+z*k) + + float length = xp * xp + yp * yp + zp * zp; + if (!qFuzzyIsNull(length)) { + *x = xp; + *y = yp; + *z = zp; + if (!qFuzzyIsNull(length - 1.0f)) { + length = std::sqrt(length); + *x /= length; + *y /= length; + *z /= length; + } + *angle = 2.0f * std::acos(wp); + } else { + // angle is 0 (mod 2*pi), so any axis will fit + *x = *y = *z = *angle = 0.0f; + } + + *angle = qRadiansToDegrees(*angle); +} + +/*! Creates a normalized quaternion that corresponds to rotating through \a angle degrees about the 3D axis (\a x, \a y, \a z). + + \sa getAxisAndAngle() */ QQuaternion QQuaternion::fromAxisAndAngle (float x, float y, float z, float angle) { - float length = qSqrt(x * x + y * y + z * z); + float length = std::sqrt(x * x + y * y + z * z); if (!qFuzzyIsNull(length - 1.0f) && !qFuzzyIsNull(length)) { x /= length; y /= length; z /= length; } float a = (angle / 2.0f) * M_PI / 180.0f; - float s = sinf(a); - float c = cosf(a); + float s = std::sin(a); + float c = std::cos(a); return QQuaternion(c, x * s, y * s, z * s).normalized(); } +#ifndef QT_NO_VECTOR3D + +/*! + \fn QVector3D QQuaternion::toEulerAngles() const + \since 5.5 + \overload + + Calculates roll, pitch, and yaw Euler angles (in degrees) + that corresponds to this quaternion. + + \sa fromEulerAngles() +*/ + +/*! + \fn QQuaternion QQuaternion::fromEulerAngles(const QVector3D &eulerAngles) + \since 5.5 + \overload + + Creates a quaternion that corresponds to a rotation of \a eulerAngles: + eulerAngles.z() degrees around the z axis, eulerAngles.x() degrees around the x axis, + and eulerAngles.y() degrees around the y axis (in that order). + + \sa toEulerAngles() +*/ + +#endif // QT_NO_VECTOR3D + +/*! + \since 5.5 + + Calculates \a roll, \a pitch, and \a yaw Euler angles (in degrees) + that corresponds to this quaternion. + + \sa fromEulerAngles() +*/ +void QQuaternion::getEulerAngles(float *pitch, float *yaw, float *roll) const +{ + Q_ASSERT(pitch && yaw && roll); + + // Algorithm from: + // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q37 + + float xx = xp * xp; + float xy = xp * yp; + float xz = xp * zp; + float xw = xp * wp; + float yy = yp * yp; + float yz = yp * zp; + float yw = yp * wp; + float zz = zp * zp; + float zw = zp * wp; + + const float lengthSquared = xx + yy + zz + wp * wp; + if (!qFuzzyIsNull(lengthSquared - 1.0f) && !qFuzzyIsNull(lengthSquared)) { + xx /= lengthSquared; + xy /= lengthSquared; // same as (xp / length) * (yp / length) + xz /= lengthSquared; + xw /= lengthSquared; + yy /= lengthSquared; + yz /= lengthSquared; + yw /= lengthSquared; + zz /= lengthSquared; + zw /= lengthSquared; + } + + *pitch = std::asin(-2.0f * (yz - xw)); + 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)); + } + } else { + // not a unique solution + *roll = 0.0f; + *yaw = std::atan2(-2.0f * (xy - zw), 1.0f - 2.0f * (yy + zz)); + } + + *pitch = qRadiansToDegrees(*pitch); + *yaw = qRadiansToDegrees(*yaw); + *roll = qRadiansToDegrees(*roll); +} + +/*! + \since 5.5 + + Creates a quaternion that corresponds to a rotation of + \a roll degrees around the z axis, \a pitch degrees around the x axis, + and \a yaw degrees around the y axis (in that order). + + \sa getEulerAngles() +*/ +QQuaternion QQuaternion::fromEulerAngles(float pitch, float yaw, float roll) +{ + // Algorithm from: + // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q60 + + pitch = qDegreesToRadians(pitch); + yaw = qDegreesToRadians(yaw); + roll = qDegreesToRadians(roll); + + pitch *= 0.5f; + yaw *= 0.5f; + roll *= 0.5f; + + const float c1 = std::cos(yaw); + const float s1 = std::sin(yaw); + const float c2 = std::cos(roll); + const float s2 = std::sin(roll); + const float c3 = std::cos(pitch); + const float s3 = std::sin(pitch); + const float c1c2 = c1 * c2; + const float s1s2 = s1 * s2; + + const float w = c1c2 * c3 + s1s2 * s3; + const float x = c1c2 * s3 + s1s2 * c3; + const float y = s1 * c2 * c3 - c1 * s2 * s3; + const float z = c1 * s2 * c3 - s1 * c2 * s3; + + return QQuaternion(w, x, y, z); +} + +/*! + \since 5.5 + + Creates a rotation matrix that corresponds to this quaternion. + + \note If this quaternion is not normalized, + the resulting rotation matrix will contain scaling information. + + \sa fromRotationMatrix(), getAxes() +*/ +QMatrix3x3 QQuaternion::toRotationMatrix() const +{ + // Algorithm from: + // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q54 + + QMatrix3x3 rot3x3(Qt::Uninitialized); + + const float f2x = xp + xp; + const float f2y = yp + yp; + const float f2z = zp + zp; + const float f2xw = f2x * wp; + const float f2yw = f2y * wp; + const float f2zw = f2z * wp; + const float f2xx = f2x * xp; + const float f2xy = f2x * yp; + const float f2xz = f2x * zp; + const float f2yy = f2y * yp; + const float f2yz = f2y * zp; + const float f2zz = f2z * zp; + + rot3x3(0, 0) = 1.0f - (f2yy + f2zz); + rot3x3(0, 1) = f2xy - f2zw; + rot3x3(0, 2) = f2xz + f2yw; + rot3x3(1, 0) = f2xy + f2zw; + rot3x3(1, 1) = 1.0f - (f2xx + f2zz); + rot3x3(1, 2) = f2yz - f2xw; + rot3x3(2, 0) = f2xz - f2yw; + rot3x3(2, 1) = f2yz + f2xw; + rot3x3(2, 2) = 1.0f - (f2xx + f2yy); + + return rot3x3; +} + +/*! + \since 5.5 + + Creates a quaternion that corresponds to a rotation matrix \a rot3x3. + + \note If a given rotation matrix is not normalized, + the resulting quaternion will contain scaling information. + + \sa toRotationMatrix(), fromAxes() +*/ +QQuaternion QQuaternion::fromRotationMatrix(const QMatrix3x3 &rot3x3) +{ + // Algorithm from: + // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q55 + + float scalar; + float axis[3]; + + const float trace = rot3x3(0, 0) + rot3x3(1, 1) + rot3x3(2, 2); + if (trace > 0.00000001f) { + const float s = 2.0f * std::sqrt(trace + 1.0f); + scalar = 0.25f * s; + axis[0] = (rot3x3(2, 1) - rot3x3(1, 2)) / s; + axis[1] = (rot3x3(0, 2) - rot3x3(2, 0)) / s; + axis[2] = (rot3x3(1, 0) - rot3x3(0, 1)) / s; + } else { + static int s_next[3] = { 1, 2, 0 }; + int i = 0; + if (rot3x3(1, 1) > rot3x3(0, 0)) + i = 1; + if (rot3x3(2, 2) > rot3x3(i, i)) + i = 2; + int j = s_next[i]; + int k = s_next[j]; + + const float s = 2.0f * std::sqrt(rot3x3(i, i) - rot3x3(j, j) - rot3x3(k, k) + 1.0f); + axis[i] = 0.25f * s; + scalar = (rot3x3(k, j) - rot3x3(j, k)) / s; + axis[j] = (rot3x3(j, i) + rot3x3(i, j)) / s; + axis[k] = (rot3x3(k, i) + rot3x3(i, k)) / s; + } + + 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); +} + +/*! + \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 + /*! \fn bool operator==(const QQuaternion &q1, const QQuaternion &q2) \relates QQuaternion @@ -470,6 +839,18 @@ QQuaternion QQuaternion::fromAxisAndAngle \sa QQuaternion::operator/=() */ +#ifndef QT_NO_VECTOR3D + +/*! + \fn QVector3D operator*(const QQuaternion &quaternion, const QVector3D &vec) + \since 5.5 + \relates QQuaternion + + Rotates a vector \a vec with a quaternion \a quaternion to produce a new vector in 3D space. +*/ + +#endif + /*! \fn bool qFuzzyCompare(const QQuaternion& q1, const QQuaternion& q2) \relates QQuaternion @@ -499,13 +880,10 @@ QQuaternion QQuaternion::slerp return q2; // Determine the angle between the two quaternions. - QQuaternion q2b; - float dot; - dot = q1.xp * q2.xp + q1.yp * q2.yp + q1.zp * q2.zp + q1.wp * q2.wp; - if (dot >= 0.0f) { - q2b = q2; - } else { - q2b = -q2; + QQuaternion q2b(q2); + float dot = QQuaternion::dotProduct(q1, q2); + if (dot < 0.0f) { + q2b = -q2b; dot = -dot; } @@ -514,11 +892,11 @@ QQuaternion QQuaternion::slerp float factor1 = 1.0f - t; float factor2 = t; if ((1.0f - dot) > 0.0000001) { - float angle = acosf(dot); - float sinOfAngle = sinf(angle); + float angle = std::acos(dot); + float sinOfAngle = std::sin(angle); if (sinOfAngle > 0.0000001) { - factor1 = sinf((1.0f - t) * angle) / sinOfAngle; - factor2 = sinf(t * angle) / sinOfAngle; + factor1 = std::sin((1.0f - t) * angle) / sinOfAngle; + factor2 = std::sin(t * angle) / sinOfAngle; } } @@ -551,13 +929,10 @@ QQuaternion QQuaternion::nlerp return q2; // Determine the angle between the two quaternions. - QQuaternion q2b; - float dot; - dot = q1.xp * q2.xp + q1.yp * q2.yp + q1.zp * q2.zp + q1.wp * q2.wp; - if (dot >= 0.0f) - q2b = q2; - else - q2b = -q2; + QQuaternion q2b(q2); + float dot = QQuaternion::dotProduct(q1, q2); + if (dot < 0.0f) + q2b = -q2b; // Perform the linear interpolation. return (q1 * (1.0f - t) + q2b * t).normalized(); @@ -575,10 +950,11 @@ QQuaternion::operator QVariant() const QDebug operator<<(QDebug dbg, const QQuaternion &q) { + QDebugStateSaver saver(dbg); dbg.nospace() << "QQuaternion(scalar:" << q.scalar() << ", vector:(" << q.x() << ", " << q.y() << ", " << q.z() << "))"; - return dbg.space(); + return dbg; } #endif diff --git a/src/gui/math3d/qquaternion.h b/src/gui/math3d/qquaternion.h index e0e52cefdd..c3918645d4 100644 --- a/src/gui/math3d/qquaternion.h +++ b/src/gui/math3d/qquaternion.h @@ -1,7 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. ** @@ -10,9 +10,9 @@ ** 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser @@ -23,8 +23,8 @@ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ @@ -34,6 +34,7 @@ #ifndef QQUATERNION_H #define QQUATERNION_H +#include <QtGui/qgenericmatrix.h> #include <QtGui/qvector3d.h> #include <QtGui/qvector4d.h> @@ -49,6 +50,7 @@ class Q_GUI_EXPORT QQuaternion { public: QQuaternion(); + explicit QQuaternion(Qt::Initialization) {} QQuaternion(float scalar, float xpos, float ypos, float zpos); #ifndef QT_NO_VECTOR3D QQuaternion(float scalar, const QVector3D& vector); @@ -76,12 +78,16 @@ public: void setZ(float z); void setScalar(float scalar); + Q_DECL_CONSTEXPR static inline float dotProduct(const QQuaternion &q1, const QQuaternion &q2); + float length() const; float lengthSquared() const; QQuaternion normalized() const; void normalize(); + inline QQuaternion inverted() const; + QQuaternion conjugate() const; QVector3D rotatedVector(const QVector3D& vector) const; @@ -111,11 +117,30 @@ public: operator QVariant() const; #ifndef QT_NO_VECTOR3D + inline void getAxisAndAngle(QVector3D *axis, float *angle) const; static QQuaternion fromAxisAndAngle(const QVector3D& axis, float angle); #endif + void getAxisAndAngle(float *x, float *y, float *z, float *angle) const; static QQuaternion fromAxisAndAngle (float x, float y, float z, float angle); +#ifndef QT_NO_VECTOR3D + inline QVector3D toEulerAngles() const; + static inline QQuaternion fromEulerAngles(const QVector3D &eulerAngles); +#endif + void getEulerAngles(float *pitch, float *yaw, float *roll) const; + static QQuaternion fromEulerAngles(float pitch, float yaw, float roll); + + 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); + + static QQuaternion rotationTo(const QVector3D &from, const QVector3D &to); +#endif + static QQuaternion slerp (const QQuaternion& q1, const QQuaternion& q2, float t); static QQuaternion nlerp @@ -152,6 +177,23 @@ inline void QQuaternion::setY(float aY) { yp = aY; } inline void QQuaternion::setZ(float aZ) { zp = aZ; } inline void QQuaternion::setScalar(float aScalar) { wp = aScalar; } +Q_DECL_CONSTEXPR inline float QQuaternion::dotProduct(const QQuaternion &q1, const QQuaternion &q2) +{ + return q1.xp * q2.xp + q1.yp * q2.yp + q1.zp * q2.zp + q1.wp * q2.wp; +} + +inline QQuaternion QQuaternion::inverted() const +{ + // Need some extra precision if the length is very small. + double len = double(xp) * double(xp) + + double(yp) * double(yp) + + double(zp) * double(zp) + + double(wp) * double(wp); + if (!qFuzzyIsNull(len)) + return QQuaternion(wp / len, -xp / len, -yp / len, -zp / len); + return QQuaternion(0.0f, 0.0f, 0.0f, 0.0f); +} + inline QQuaternion QQuaternion::conjugate() const { return QQuaternion(wp, -xp, -yp, -zp); @@ -280,6 +322,30 @@ inline QVector3D QQuaternion::vector() const return QVector3D(xp, yp, zp); } +inline QVector3D operator*(const QQuaternion &quaternion, const QVector3D &vec) +{ + return quaternion.rotatedVector(vec); +} + +inline void QQuaternion::getAxisAndAngle(QVector3D *axis, float *angle) const +{ + float aX, aY, aZ; + getAxisAndAngle(&aX, &aY, &aZ, angle); + *axis = QVector3D(aX, aY, aZ); +} + +inline QVector3D QQuaternion::toEulerAngles() const +{ + float pitch, yaw, roll; + getEulerAngles(&pitch, &yaw, &roll); + return QVector3D(pitch, yaw, roll); +} + +inline QQuaternion QQuaternion::fromEulerAngles(const QVector3D &eulerAngles) +{ + return QQuaternion::fromEulerAngles(eulerAngles.x(), eulerAngles.y(), eulerAngles.z()); +} + #endif inline void QQuaternion::setVector(float aX, float aY, float aZ) diff --git a/src/gui/math3d/qvector2d.cpp b/src/gui/math3d/qvector2d.cpp index c46dd35766..2d8a1d3a0f 100644 --- a/src/gui/math3d/qvector2d.cpp +++ b/src/gui/math3d/qvector2d.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. ** @@ -10,9 +10,9 @@ ** 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser @@ -23,8 +23,8 @@ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ @@ -64,6 +64,14 @@ QT_BEGIN_NAMESPACE */ /*! + \fn QVector2D::QVector2D(Qt::Initialization) + \since 5.5 + \internal + + Constructs a vector without initializing the contents. +*/ + +/*! \fn QVector2D::QVector2D(float xpos, float ypos) Constructs a vector with coordinates (\a xpos, \a ypos). @@ -181,7 +189,7 @@ float QVector2D::length() const // Need some extra precision if the length is very small. double len = double(xp) * double(xp) + double(yp) * double(yp); - return float(sqrt(len)); + return float(std::sqrt(len)); } /*! @@ -212,7 +220,7 @@ QVector2D QVector2D::normalized() const if (qFuzzyIsNull(len - 1.0f)) { return *this; } else if (!qFuzzyIsNull(len)) { - double sqrtLen = sqrt(len); + double sqrtLen = std::sqrt(len); return QVector2D(float(double(xp) / sqrtLen), float(double(yp) / sqrtLen)); } else { return QVector2D(); @@ -233,7 +241,7 @@ void QVector2D::normalize() if (qFuzzyIsNull(len - 1.0f) || qFuzzyIsNull(len)) return; - len = sqrt(len); + len = std::sqrt(len); xp = float(double(xp) / len); yp = float(double(yp) / len); @@ -316,6 +324,16 @@ float QVector2D::distanceToLine */ /*! + \fn QVector2D &QVector2D::operator/=(const QVector2D &vector) + \since 5.5 + + Divides the components of this vector by the corresponding + components in \a vector. + + \sa operator*=() +*/ + +/*! Returns the dot product of \a v1 and \a v2. */ float QVector2D::dotProduct(const QVector2D& v1, const QVector2D& v2) @@ -407,6 +425,17 @@ float QVector2D::dotProduct(const QVector2D& v1, const QVector2D& v2) */ /*! + \fn const QVector2D operator/(const QVector2D &vector, const QVector2D &divisor) + \relates QVector2D + \since 5.5 + + Returns the QVector2D object formed by dividing components of the given + \a vector by a respective components of the given \a divisor. + + \sa QVector2D::operator/=() +*/ + +/*! \fn bool qFuzzyCompare(const QVector2D& v1, const QVector2D& v2) \relates QVector2D @@ -470,8 +499,9 @@ QVector2D::operator QVariant() const QDebug operator<<(QDebug dbg, const QVector2D &vector) { + QDebugStateSaver saver(dbg); dbg.nospace() << "QVector2D(" << vector.x() << ", " << vector.y() << ')'; - return dbg.space(); + return dbg; } #endif diff --git a/src/gui/math3d/qvector2d.h b/src/gui/math3d/qvector2d.h index 2cacd8ade0..20264fa84f 100644 --- a/src/gui/math3d/qvector2d.h +++ b/src/gui/math3d/qvector2d.h @@ -1,7 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. ** @@ -10,9 +10,9 @@ ** 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser @@ -23,8 +23,8 @@ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ @@ -50,6 +50,7 @@ class Q_GUI_EXPORT QVector2D { public: Q_DECL_CONSTEXPR QVector2D(); + explicit QVector2D(Qt::Initialization) {} Q_DECL_CONSTEXPR QVector2D(float xpos, float ypos); Q_DECL_CONSTEXPR explicit QVector2D(const QPoint& point); Q_DECL_CONSTEXPR explicit QVector2D(const QPointF& point); @@ -85,6 +86,7 @@ public: QVector2D &operator*=(float factor); QVector2D &operator*=(const QVector2D &vector); QVector2D &operator/=(float divisor); + inline QVector2D &operator/=(const QVector2D &vector); static float dotProduct(const QVector2D& v1, const QVector2D& v2); //In Qt 6 convert to inline and constexpr @@ -97,6 +99,7 @@ public: Q_DECL_CONSTEXPR friend inline const QVector2D operator*(const QVector2D &v1, const QVector2D &v2); Q_DECL_CONSTEXPR friend inline const QVector2D operator-(const QVector2D &vector); Q_DECL_CONSTEXPR friend inline const QVector2D operator/(const QVector2D &vector, float divisor); + Q_DECL_CONSTEXPR friend inline const QVector2D operator/(const QVector2D &vector, const QVector2D &divisor); Q_DECL_CONSTEXPR friend inline bool qFuzzyCompare(const QVector2D& v1, const QVector2D& v2); @@ -187,6 +190,13 @@ inline QVector2D &QVector2D::operator/=(float divisor) return *this; } +inline QVector2D &QVector2D::operator/=(const QVector2D &vector) +{ + xp /= vector.xp; + yp /= vector.yp; + return *this; +} + Q_DECL_CONSTEXPR inline bool operator==(const QVector2D &v1, const QVector2D &v2) { return v1.xp == v2.xp && v1.yp == v2.yp; @@ -232,6 +242,11 @@ Q_DECL_CONSTEXPR inline const QVector2D operator/(const QVector2D &vector, float return QVector2D(vector.xp / divisor, vector.yp / divisor); } +Q_DECL_CONSTEXPR inline const QVector2D operator/(const QVector2D &vector, const QVector2D &divisor) +{ + return QVector2D(vector.xp / divisor.xp, vector.yp / divisor.yp); +} + Q_DECL_CONSTEXPR inline bool qFuzzyCompare(const QVector2D& v1, const QVector2D& v2) { return qFuzzyCompare(v1.xp, v2.xp) && qFuzzyCompare(v1.yp, v2.yp); diff --git a/src/gui/math3d/qvector3d.cpp b/src/gui/math3d/qvector3d.cpp index 75d61e5f85..19aa4facdb 100644 --- a/src/gui/math3d/qvector3d.cpp +++ b/src/gui/math3d/qvector3d.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. ** @@ -10,9 +10,9 @@ ** 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser @@ -23,8 +23,8 @@ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ @@ -34,10 +34,12 @@ #include "qvector3d.h" #include "qvector2d.h" #include "qvector4d.h" +#include "qmatrix4x4.h" #include <QtCore/qdatastream.h> #include <QtCore/qmath.h> #include <QtCore/qvariant.h> #include <QtCore/qdebug.h> +#include <QtCore/qrect.h> QT_BEGIN_NAMESPACE @@ -67,6 +69,14 @@ QT_BEGIN_NAMESPACE */ /*! + \fn QVector3D::QVector3D(Qt::Initialization) + \since 5.5 + \internal + + Constructs a vector without initializing the contents. +*/ + +/*! \fn QVector3D::QVector3D(float xpos, float ypos, float zpos) Constructs a vector with coordinates (\a xpos, \a ypos, \a zpos). @@ -225,7 +235,7 @@ QVector3D QVector3D::normalized() const if (qFuzzyIsNull(len - 1.0f)) { return *this; } else if (!qFuzzyIsNull(len)) { - double sqrtLen = sqrt(len); + double sqrtLen = std::sqrt(len); return QVector3D(float(double(xp) / sqrtLen), float(double(yp) / sqrtLen), float(double(zp) / sqrtLen)); @@ -249,7 +259,7 @@ void QVector3D::normalize() if (qFuzzyIsNull(len - 1.0f) || qFuzzyIsNull(len)) return; - len = sqrt(len); + len = std::sqrt(len); xp = float(double(xp) / len); yp = float(double(yp) / len); @@ -306,6 +316,16 @@ void QVector3D::normalize() */ /*! + \fn QVector3D &QVector3D::operator/=(const QVector3D &vector) + \since 5.5 + + Divides the components of this vector by the corresponding + components in \a vector. + + \sa operator*=() +*/ + +/*! Returns the dot product of \a v1 and \a v2. */ float QVector3D::dotProduct(const QVector3D& v1, const QVector3D& v2) @@ -359,6 +379,69 @@ QVector3D QVector3D::normal } /*! + \since 5.5 + + Returns the window coordinates of this vector initially in object/model + coordinates using the model view matrix \a modelView, the projection matrix + \a projection and the viewport dimensions \a viewport. + + When transforming from clip to normalized space, a division by the w + component on the vector components takes place. To prevent dividing by 0 if + w equals to 0, it is set to 1. + + \note the returned y coordinates are in OpenGL orientation. OpenGL expects + the bottom to be 0 whereas for Qt top is 0. + + \sa unproject() + */ +QVector3D QVector3D::project(const QMatrix4x4 &modelView, const QMatrix4x4 &projection, const QRect &viewport) const +{ + QVector4D tmp(*this, 1.0f); + tmp = projection * modelView * tmp; + if (qFuzzyIsNull(tmp.w())) + tmp.setW(1.0f); + tmp /= tmp.w(); + + tmp = tmp * 0.5f + QVector4D(0.5f, 0.5f, 0.5f, 0.5f); + tmp.setX(tmp.x() * viewport.width() + viewport.x()); + tmp.setY(tmp.y() * viewport.height() + viewport.y()); + + return tmp.toVector3D(); +} + +/*! + \since 5.5 + + Returns the object/model coordinates of this vector initially in window + coordinates using the model view matrix \a modelView, the projection matrix + \a projection and the viewport dimensions \a viewport. + + When transforming from clip to normalized space, a division by the w + component of the vector components takes place. To prevent dividing by 0 if + w equals to 0, it is set to 1. + + \note y coordinates in \a viewport should use OpenGL orientation. OpenGL + expects the bottom to be 0 whereas for Qt top is 0. + + \sa project() + */ +QVector3D QVector3D::unproject(const QMatrix4x4 &modelView, const QMatrix4x4 &projection, const QRect &viewport) const +{ + QMatrix4x4 inverse = QMatrix4x4( projection * modelView ).inverted(); + + QVector4D tmp(*this, 1.0f); + tmp.setX((tmp.x() - float(viewport.x())) / float(viewport.width())); + tmp.setY((tmp.y() - float(viewport.y())) / float(viewport.height())); + tmp = tmp * 2.0f - QVector4D(1.0f, 1.0f, 1.0f, 1.0f); + + QVector4D obj = inverse * tmp; + if (qFuzzyIsNull(obj.w())) + obj.setW(1.0f); + obj /= obj.w(); + return obj.toVector3D(); +} + +/*! \since 5.1 Returns the distance from this vertex to a point defined by @@ -513,6 +596,17 @@ float QVector3D::distanceToLine */ /*! + \fn const QVector3D operator/(const QVector3D &vector, const QVector3D &divisor) + \relates QVector3D + \since 5.5 + + Returns the QVector3D object formed by dividing components of the given + \a vector by a respective components of the given \a divisor. + + \sa QVector3D::operator/=() +*/ + +/*! \fn bool qFuzzyCompare(const QVector3D& v1, const QVector3D& v2) \relates QVector3D @@ -585,7 +679,7 @@ float QVector3D::length() const double len = double(xp) * double(xp) + double(yp) * double(yp) + double(zp) * double(zp); - return float(sqrt(len)); + return float(std::sqrt(len)); } /*! @@ -603,9 +697,10 @@ float QVector3D::lengthSquared() const QDebug operator<<(QDebug dbg, const QVector3D &vector) { + QDebugStateSaver saver(dbg); dbg.nospace() << "QVector3D(" << vector.x() << ", " << vector.y() << ", " << vector.z() << ')'; - return dbg.space(); + return dbg; } #endif diff --git a/src/gui/math3d/qvector3d.h b/src/gui/math3d/qvector3d.h index 40e9035621..5be637e0c5 100644 --- a/src/gui/math3d/qvector3d.h +++ b/src/gui/math3d/qvector3d.h @@ -1,7 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. ** @@ -10,9 +10,9 @@ ** 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser @@ -23,8 +23,8 @@ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ @@ -43,6 +43,7 @@ QT_BEGIN_NAMESPACE class QMatrix4x4; class QVector2D; class QVector4D; +class QRect; #ifndef QT_NO_VECTOR3D @@ -50,6 +51,7 @@ class Q_GUI_EXPORT QVector3D { public: Q_DECL_CONSTEXPR QVector3D(); + explicit QVector3D(Qt::Initialization) {} Q_DECL_CONSTEXPR QVector3D(float xpos, float ypos, float zpos) : xp(xpos), yp(ypos), zp(zpos) {} Q_DECL_CONSTEXPR explicit QVector3D(const QPoint& point); @@ -86,6 +88,7 @@ public: QVector3D &operator*=(float factor); QVector3D &operator*=(const QVector3D& vector); QVector3D &operator/=(float divisor); + inline QVector3D &operator/=(const QVector3D &vector); static float dotProduct(const QVector3D& v1, const QVector3D& v2); //In Qt 6 convert to inline and constexpr static QVector3D crossProduct(const QVector3D& v1, const QVector3D& v2); //in Qt 6 convert to inline and constexpr @@ -94,6 +97,9 @@ public: static QVector3D normal (const QVector3D& v1, const QVector3D& v2, const QVector3D& v3); + QVector3D project(const QMatrix4x4 &modelView, const QMatrix4x4 &projection, const QRect &viewport) const; + QVector3D unproject(const QMatrix4x4 &modelView, const QMatrix4x4 &projection, const QRect &viewport) const; + float distanceToPoint(const QVector3D& point) const; float distanceToPlane(const QVector3D& plane, const QVector3D& normal) const; float distanceToPlane(const QVector3D& plane1, const QVector3D& plane2, const QVector3D& plane3) const; @@ -108,6 +114,7 @@ public: Q_DECL_CONSTEXPR friend const QVector3D operator*(const QVector3D &v1, const QVector3D& v2); Q_DECL_CONSTEXPR friend inline const QVector3D operator-(const QVector3D &vector); Q_DECL_CONSTEXPR friend inline const QVector3D operator/(const QVector3D &vector, float divisor); + Q_DECL_CONSTEXPR friend inline const QVector3D operator/(const QVector3D &vector, const QVector3D &divisor); Q_DECL_CONSTEXPR friend inline bool qFuzzyCompare(const QVector3D& v1, const QVector3D& v2); @@ -207,6 +214,14 @@ inline QVector3D &QVector3D::operator/=(float divisor) return *this; } +inline QVector3D &QVector3D::operator/=(const QVector3D &vector) +{ + xp /= vector.xp; + yp /= vector.yp; + zp /= vector.zp; + return *this; +} + Q_DECL_CONSTEXPR inline bool operator==(const QVector3D &v1, const QVector3D &v2) { return v1.xp == v2.xp && v1.yp == v2.yp && v1.zp == v2.zp; @@ -252,6 +267,11 @@ Q_DECL_CONSTEXPR inline const QVector3D operator/(const QVector3D &vector, float return QVector3D(vector.xp / divisor, vector.yp / divisor, vector.zp / divisor); } +Q_DECL_CONSTEXPR inline const QVector3D operator/(const QVector3D &vector, const QVector3D &divisor) +{ + return QVector3D(vector.xp / divisor.xp, vector.yp / divisor.yp, vector.zp / divisor.zp); +} + Q_DECL_CONSTEXPR inline bool qFuzzyCompare(const QVector3D& v1, const QVector3D& v2) { return qFuzzyCompare(v1.xp, v2.xp) && diff --git a/src/gui/math3d/qvector4d.cpp b/src/gui/math3d/qvector4d.cpp index 3603f655cb..494e6f97f0 100644 --- a/src/gui/math3d/qvector4d.cpp +++ b/src/gui/math3d/qvector4d.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. ** @@ -10,9 +10,9 @@ ** 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser @@ -23,8 +23,8 @@ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ @@ -63,6 +63,14 @@ QT_BEGIN_NAMESPACE */ /*! + \fn QVector4D::QVector4D(Qt::Initialization) + \since 5.5 + \internal + + Constructs a vector without initializing the contents. +*/ + +/*! \fn QVector4D::QVector4D(float xpos, float ypos, float zpos, float wpos) Constructs a vector with coordinates (\a xpos, \a ypos, \a zpos, \a wpos). @@ -248,7 +256,7 @@ float QVector4D::length() const double(yp) * double(yp) + double(zp) * double(zp) + double(wp) * double(wp); - return float(sqrt(len)); + return float(std::sqrt(len)); } /*! @@ -281,7 +289,7 @@ QVector4D QVector4D::normalized() const if (qFuzzyIsNull(len - 1.0f)) { return *this; } else if (!qFuzzyIsNull(len)) { - double sqrtLen = sqrt(len); + double sqrtLen = std::sqrt(len); return QVector4D(float(double(xp) / sqrtLen), float(double(yp) / sqrtLen), float(double(zp) / sqrtLen), @@ -307,7 +315,7 @@ void QVector4D::normalize() if (qFuzzyIsNull(len - 1.0f) || qFuzzyIsNull(len)) return; - len = sqrt(len); + len = std::sqrt(len); xp = float(double(xp) / len); yp = float(double(yp) / len); @@ -359,6 +367,16 @@ void QVector4D::normalize() */ /*! + \fn QVector4D &QVector4D::operator/=(const QVector4D &vector) + \since 5.5 + + Divides the components of this vector by the corresponding + components in \a vector. + + \sa operator*=() +*/ + +/*! Returns the dot product of \a v1 and \a v2. */ float QVector4D::dotProduct(const QVector4D& v1, const QVector4D& v2) @@ -452,6 +470,17 @@ float QVector4D::dotProduct(const QVector4D& v1, const QVector4D& v2) */ /*! + \fn const QVector4D operator/(const QVector4D &vector, const QVector4D &divisor) + \relates QVector4D + \since 5.5 + + Returns the QVector4D object formed by dividing components of the given + \a vector by a respective components of the given \a divisor. + + \sa QVector4D::operator/=() +*/ + +/*! \fn bool qFuzzyCompare(const QVector4D& v1, const QVector4D& v2) \relates QVector4D @@ -544,10 +573,11 @@ QVector4D::operator QVariant() const QDebug operator<<(QDebug dbg, const QVector4D &vector) { + QDebugStateSaver saver(dbg); dbg.nospace() << "QVector4D(" << vector.x() << ", " << vector.y() << ", " << vector.z() << ", " << vector.w() << ')'; - return dbg.space(); + return dbg; } #endif diff --git a/src/gui/math3d/qvector4d.h b/src/gui/math3d/qvector4d.h index 291af5c072..aa69104f55 100644 --- a/src/gui/math3d/qvector4d.h +++ b/src/gui/math3d/qvector4d.h @@ -1,7 +1,7 @@ /**************************************************************************** ** -** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. ** @@ -10,9 +10,9 @@ ** 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser @@ -23,8 +23,8 @@ ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** $QT_END_LICENSE$ @@ -50,6 +50,7 @@ class Q_GUI_EXPORT QVector4D { public: Q_DECL_CONSTEXPR QVector4D(); + explicit QVector4D(Qt::Initialization) {} Q_DECL_CONSTEXPR QVector4D(float xpos, float ypos, float zpos, float wpos); Q_DECL_CONSTEXPR explicit QVector4D(const QPoint& point); Q_DECL_CONSTEXPR explicit QVector4D(const QPointF& point); @@ -88,6 +89,7 @@ public: QVector4D &operator*=(float factor); QVector4D &operator*=(const QVector4D &vector); QVector4D &operator/=(float divisor); + inline QVector4D &operator/=(const QVector4D &vector); static float dotProduct(const QVector4D& v1, const QVector4D& v2); //In Qt 6 convert to inline and constexpr @@ -100,6 +102,7 @@ public: Q_DECL_CONSTEXPR friend inline const QVector4D operator*(const QVector4D &v1, const QVector4D& v2); Q_DECL_CONSTEXPR friend inline const QVector4D operator-(const QVector4D &vector); Q_DECL_CONSTEXPR friend inline const QVector4D operator/(const QVector4D &vector, float divisor); + Q_DECL_CONSTEXPR friend inline const QVector4D operator/(const QVector4D &vector, const QVector4D &divisor); Q_DECL_CONSTEXPR friend inline bool qFuzzyCompare(const QVector4D& v1, const QVector4D& v2); @@ -210,6 +213,15 @@ inline QVector4D &QVector4D::operator/=(float divisor) return *this; } +inline QVector4D &QVector4D::operator/=(const QVector4D &vector) +{ + xp /= vector.xp; + yp /= vector.yp; + zp /= vector.zp; + wp /= vector.wp; + return *this; +} + Q_DECL_CONSTEXPR inline bool operator==(const QVector4D &v1, const QVector4D &v2) { return v1.xp == v2.xp && v1.yp == v2.yp && v1.zp == v2.zp && v1.wp == v2.wp; @@ -255,6 +267,11 @@ Q_DECL_CONSTEXPR inline const QVector4D operator/(const QVector4D &vector, float return QVector4D(vector.xp / divisor, vector.yp / divisor, vector.zp / divisor, vector.wp / divisor); } +Q_DECL_CONSTEXPR inline const QVector4D operator/(const QVector4D &vector, const QVector4D &divisor) +{ + return QVector4D(vector.xp / divisor.xp, vector.yp / divisor.yp, vector.zp / divisor.zp, vector.wp / divisor.wp); +} + Q_DECL_CONSTEXPR inline bool qFuzzyCompare(const QVector4D& v1, const QVector4D& v2) { return qFuzzyCompare(v1.xp, v2.xp) && |