diff options
Diffstat (limited to 'src/gui/math3d')
-rw-r--r-- | src/gui/math3d/math3d.pri | 15 | ||||
-rw-r--r-- | src/gui/math3d/qgenericmatrix.cpp | 251 | ||||
-rw-r--r-- | src/gui/math3d/qgenericmatrix.h | 371 | ||||
-rw-r--r-- | src/gui/math3d/qmatrix4x4.cpp | 1808 | ||||
-rw-r--r-- | src/gui/math3d/qmatrix4x4.h | 998 | ||||
-rw-r--r-- | src/gui/math3d/qquaternion.cpp | 584 | ||||
-rw-r--r-- | src/gui/math3d/qquaternion.h | 337 | ||||
-rw-r--r-- | src/gui/math3d/qvector2d.cpp | 417 | ||||
-rw-r--r-- | src/gui/math3d/qvector2d.h | 256 | ||||
-rw-r--r-- | src/gui/math3d/qvector3d.cpp | 565 | ||||
-rw-r--r-- | src/gui/math3d/qvector3d.h | 284 | ||||
-rw-r--r-- | src/gui/math3d/qvector4d.cpp | 518 | ||||
-rw-r--r-- | src/gui/math3d/qvector4d.h | 289 |
13 files changed, 6693 insertions, 0 deletions
diff --git a/src/gui/math3d/math3d.pri b/src/gui/math3d/math3d.pri new file mode 100644 index 0000000..e4dd53a --- /dev/null +++ b/src/gui/math3d/math3d.pri @@ -0,0 +1,15 @@ +HEADERS += \ + math3d/qgenericmatrix.h \ + math3d/qmatrix4x4.h \ + math3d/qquaternion.h \ + math3d/qvector2d.h \ + math3d/qvector3d.h \ + math3d/qvector4d.h + +SOURCES += \ + math3d/qgenericmatrix.cpp \ + math3d/qmatrix4x4.cpp \ + math3d/qquaternion.cpp \ + math3d/qvector2d.cpp \ + math3d/qvector3d.cpp \ + math3d/qvector4d.cpp diff --git a/src/gui/math3d/qgenericmatrix.cpp b/src/gui/math3d/qgenericmatrix.cpp new file mode 100644 index 0000000..82be256 --- /dev/null +++ b/src/gui/math3d/qgenericmatrix.cpp @@ -0,0 +1,251 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgenericmatrix.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGenericMatrix + \brief The QGenericMatrix class is a template class that represents a NxM transformation matrix with N columns and M rows. + \since 4.6 + + The QGenericMatrix template has four parameters: + + \table + \row \i N \i Number of columns. + \row \i M \i Number of rows. + \row \i T \i Element type that is visible to users of the class. + \row \i InnerT \i Element type that is used inside the class. + \endtable + + Normally T and InnerT are the same type; e.g. float or double. + But they can be different if the user wants to store elements + internally in a fixed-point format for the underlying hardware. + + \sa QMatrix4x4 +*/ + +/*! + \fn QGenericMatrix::QGenericMatrix() + + Constructs a NxM identity matrix. +*/ + +/*! + \fn QGenericMatrix::QGenericMatrix(const QGenericMatrix<N, M, T, InnerT>& other) + + Constructs a copy of \a other. +*/ + +/*! + \fn QGenericMatrix::QGenericMatrix(const T *values) + + Constructs a matrix from the given N * M floating-point \a values. + The contents of the array \a values is assumed to be in + row-major order. + + \sa toValueArray() +*/ + +/*! + \fn T QGenericMatrix::operator()(int row, int column) const + + Returns the element at position (\a row, \a column) in this matrix. +*/ + +/*! + \fn InnerT& QGenericMatrix::operator()(int row, int column) + + Returns a reference to the element at position (\a row, \a column) + in this matrix so that the element can be assigned to. +*/ + +/*! + \fn bool QGenericMatrix::isIdentity() const + + Returns true if this matrix is the identity; false otherwise. + + \sa setIdentity() +*/ + +/*! + \fn void QGenericMatrix::setIdentity() + + Sets this matrix to the identity. + + \sa isIdentity() +*/ + +/*! + \fn void QGenericMatrix::fill(T value) + + Fills all elements of this matrix with \a value. +*/ + +/*! + \fn QGenericMatrix<M, N> QGenericMatrix::transposed() const + + Returns this matrix, transposed about its diagonal. +*/ + +/*! + \fn QGenericMatrix<N, M, T, InnerT>& QGenericMatrix::operator+=(const QGenericMatrix<N, M, T, InnerT>& other) + + Adds the contents of \a other to this matrix. +*/ + +/*! + \fn QGenericMatrix<N, M, T, InnerT>& QGenericMatrix::operator-=(const QGenericMatrix<N, M, T, InnerT>& other) + + Subtracts the contents of \a other from this matrix. +*/ + +/*! + \fn QGenericMatrix<N, M, T, InnerT>& QGenericMatrix::operator*=(T factor) + + Multiplies all elements of this matrix by \a factor. +*/ + +/*! + \fn QGenericMatrix<N, M, T, InnerT>& QGenericMatrix::operator/=(T divisor) + + Divides all elements of this matrix by \a divisor. +*/ + +/*! + \fn bool QGenericMatrix::operator==(const QGenericMatrix<N, M, T, InnerT>& other) const + + Returns true if this matrix is identical to \a other; false otherwise. +*/ + +/*! + \fn bool QGenericMatrix::operator!=(const QGenericMatrix<N, M, T, InnerT>& other) const + + Returns true if this matrix is not identical to \a other; false otherwise. +*/ + +/*! + \fn QGenericMatrix<N, M, T, InnerT> operator+(const QGenericMatrix<N, M, T, InnerT>& m1, const QGenericMatrix<N, M, T, InnerT>& m2) + \relates QGenericMatrix + + Returns the sum of \a m1 and \a m2. +*/ + +/*! + \fn QGenericMatrix<N, M, T, InnerT> operator-(const QGenericMatrix<N, M, T, InnerT>& m1, const QGenericMatrix<N, M, T, InnerT>& m2) + \relates QGenericMatrix + + Returns the difference of \a m1 and \a m2. +*/ + +/*! + \fn QGenericMatrix<M1, M2, T, InnerT> operator*(const QGenericMatrix<N, M2, T, InnerT>& m1, const QGenericMatrix<M1, N, T, InnerT>& m2) + \relates QGenericMatrix + + Returns the product of the NxM2 matrix \a m1 and the M1xN matrix \a m2 + to produce a M1xM2 matrix result. +*/ + +/*! + \fn QGenericMatrix<N, M, T, InnerT> operator-(const QGenericMatrix<N, M, T, InnerT>& matrix) + \overload + \relates QGenericMatrix + + Returns the negation of \a matrix. +*/ + +/*! + \fn QGenericMatrix<N, M, T, InnerT> operator*(T factor, const QGenericMatrix<N, M, T, InnerT>& matrix) + \relates QGenericMatrix + + Returns the result of multiplying all elements of \a matrix by \a factor. +*/ + +/*! + \fn QGenericMatrix<N, M, T, InnerT> operator*(const QGenericMatrix<N, M, T, InnerT>& matrix, T factor) + \relates QGenericMatrix + + Returns the result of multiplying all elements of \a matrix by \a factor. +*/ + +/*! + \fn QGenericMatrix<N, M, T, InnerT> operator/(const QGenericMatrix<N, M, T, InnerT>& matrix, T divisor) + \relates QGenericMatrix + + Returns the result of dividing all elements of \a matrix by \a divisor. +*/ + +/*! + \fn void QGenericMatrix::toValueArray(T *values) + + Retrieves the N * M items in this matrix and writes them to \a values + in row-major order. +*/ + +/*! + \fn InnerT *QGenericMatrix::data() + + Returns a pointer to the raw data of this matrix. This is intended + for use with raw GL functions. + + \sa constData() +*/ + +/*! + \fn const InnerT *QGenericMatrix::data() const + + Returns a constant pointer to the raw data of this matrix. + This is intended for use with raw GL functions. + + \sa constData() +*/ + +/*! + \fn const InnerT *QGenericMatrix::constData() const + + Returns a constant pointer to the raw data of this matrix. + This is intended for use with raw GL functions. + + \sa data() +*/ + +QT_END_NAMESPACE diff --git a/src/gui/math3d/qgenericmatrix.h b/src/gui/math3d/qgenericmatrix.h new file mode 100644 index 0000000..330ded2 --- /dev/null +++ b/src/gui/math3d/qgenericmatrix.h @@ -0,0 +1,371 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGENERICMATRIX_H +#define QGENERICMATRIX_H + +#include <QtCore/qmetatype.h> +#include <QtCore/qdebug.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +template <int N, int M, typename T, typename InnerT = T> +class QGenericMatrix +{ +public: + QGenericMatrix(); + QGenericMatrix(const QGenericMatrix<N, M, T, InnerT>& other); + explicit QGenericMatrix(const T *values); + + T operator()(int row, int column) const; + InnerT& operator()(int row, int column); + + bool isIdentity() const; + void setIdentity(); + + void fill(T value); + + QGenericMatrix<M, N, T, InnerT> transposed() const; + + QGenericMatrix<N, M, T, InnerT>& operator+=(const QGenericMatrix<N, M, T, InnerT>& other); + QGenericMatrix<N, M, T, InnerT>& operator-=(const QGenericMatrix<N, M, T, InnerT>& other); + QGenericMatrix<N, M, T, InnerT>& operator*=(T factor); + QGenericMatrix<N, M, T, InnerT>& operator/=(T divisor); + bool operator==(const QGenericMatrix<N, M, T, InnerT>& other) const; + bool operator!=(const QGenericMatrix<N, M, T, InnerT>& other) const; + + void toValueArray(T *values); + + InnerT *data() { return m[0]; } + const InnerT *data() const { return m[0]; } + const InnerT *constData() const { return m[0]; } + +#if !defined(Q_NO_TEMPLATE_FRIENDS) + template<int NN, int MM, typename TT, typename ITT> + friend QGenericMatrix<NN, MM, TT, ITT> operator+(const QGenericMatrix<NN, MM, TT, ITT>& m1, const QGenericMatrix<NN, MM, TT, ITT>& m2); + template<int NN, int MM, typename TT, typename ITT> + friend QGenericMatrix<NN, MM, TT, ITT> operator-(const QGenericMatrix<NN, MM, TT, ITT>& m1, const QGenericMatrix<NN, MM, TT, ITT>& m2); + template<int NN, int M1, int M2, typename TT, typename ITT> + friend QGenericMatrix<M1, M2, TT, ITT> operator*(const QGenericMatrix<NN, M2, TT, ITT>& m1, const QGenericMatrix<M1, NN, TT, ITT>& m2); + template<int NN, int MM, typename TT, typename ITT> + friend QGenericMatrix<NN, MM, TT, ITT> operator-(const QGenericMatrix<NN, MM, TT, ITT>& matrix); + template<int NN, int MM, typename TT, typename ITT> + friend QGenericMatrix<NN, MM, TT, ITT> operator*(TT factor, const QGenericMatrix<NN, MM, TT, ITT>& matrix); + template<int NN, int MM, typename TT, typename ITT> + friend QGenericMatrix<NN, MM, TT, ITT> operator*(const QGenericMatrix<NN, MM, TT, ITT>& matrix, TT factor); + template<int NN, int MM, typename TT, typename ITT> + friend QGenericMatrix<NN, MM, TT, ITT> operator/(const QGenericMatrix<NN, MM, TT, ITT>& matrix, TT divisor); + +private: +#endif + InnerT m[N][M]; // Column-major order to match OpenGL. + + QGenericMatrix(int) {} // Construct without initializing identity matrix. + +#if !defined(Q_NO_TEMPLATE_FRIENDS) + template <int NN, int MM, typename TT, typename ITT> + friend class QGenericMatrix; +#endif +}; + +template <int N, int M, typename T, typename InnerT> +Q_INLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT>::QGenericMatrix() +{ + setIdentity(); +} + +template <int N, int M, typename T, typename InnerT> +Q_INLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT>::QGenericMatrix(const QGenericMatrix<N, M, T, InnerT>& other) +{ + qMemCopy(m, other.m, sizeof(m)); +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT>::QGenericMatrix(const T *values) +{ + for (int col = 0; col < N; ++col) + for (int row = 0; row < M; ++row) + m[col][row] = values[row * N + col]; +} + +template <int N, int M, typename T, typename InnerT> +Q_INLINE_TEMPLATE T QGenericMatrix<N, M, T, InnerT>::operator()(int row, int column) const +{ + Q_ASSERT(row >= 0 && row < M && column >= 0 && column < N); + return T(m[column][row]); +} + +template <int N, int M, typename T, typename InnerT> +Q_INLINE_TEMPLATE InnerT& QGenericMatrix<N, M, T, InnerT>::operator()(int row, int column) +{ + Q_ASSERT(row >= 0 && row < M && column >= 0 && column < N); + return m[column][row]; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE bool QGenericMatrix<N, M, T, InnerT>::isIdentity() const +{ + for (int col = 0; col < N; ++col) { + for (int row = 0; row < M; ++row) { + if (row == col) { + if (m[col][row] != 1.0f) + return false; + } else { + if (m[col][row] != 0.0f) + return false; + } + } + } + return true; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE void QGenericMatrix<N, M, T, InnerT>::setIdentity() +{ + for (int col = 0; col < N; ++col) { + for (int row = 0; row < M; ++row) { + if (row == col) + m[col][row] = 1.0f; + else + m[col][row] = 0.0f; + } + } +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE void QGenericMatrix<N, M, T, InnerT>::fill(T value) +{ + for (int col = 0; col < N; ++col) + for (int row = 0; row < M; ++row) + m[col][row] = value; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE QGenericMatrix<M, N, T, InnerT> QGenericMatrix<N, M, T, InnerT>::transposed() const +{ + QGenericMatrix<M, N, T, InnerT> result(1); + for (int row = 0; row < M; ++row) + for (int col = 0; col < N; ++col) + result.m[row][col] = m[col][row]; + return result; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT>& QGenericMatrix<N, M, T, InnerT>::operator+=(const QGenericMatrix<N, M, T, InnerT>& other) +{ + for (int index = 0; index < N * M; ++index) + m[0][index] += other.m[0][index]; + return *this; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT>& QGenericMatrix<N, M, T, InnerT>::operator-=(const QGenericMatrix<N, M, T, InnerT>& other) +{ + for (int index = 0; index < N * M; ++index) + m[0][index] -= other.m[0][index]; + return *this; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT>& QGenericMatrix<N, M, T, InnerT>::operator*=(T factor) +{ + InnerT f(factor); + for (int index = 0; index < N * M; ++index) + m[0][index] *= f; + return *this; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE bool QGenericMatrix<N, M, T, InnerT>::operator==(const QGenericMatrix<N, M, T, InnerT>& other) const +{ + for (int index = 0; index < N * M; ++index) { + if (m[0][index] != other.m[0][index]) + return false; + } + return true; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE bool QGenericMatrix<N, M, T, InnerT>::operator!=(const QGenericMatrix<N, M, T, InnerT>& other) const +{ + for (int index = 0; index < N * M; ++index) { + if (m[0][index] != other.m[0][index]) + return true; + } + return false; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT>& QGenericMatrix<N, M, T, InnerT>::operator/=(T divisor) +{ + InnerT d(divisor); + for (int index = 0; index < N * M; ++index) + m[0][index] /= d; + return *this; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT> operator+(const QGenericMatrix<N, M, T, InnerT>& m1, const QGenericMatrix<N, M, T, InnerT>& m2) +{ + QGenericMatrix<N, M, T, InnerT> result(1); + for (int index = 0; index < N * M; ++index) + result.m[0][index] = m1.m[0][index] + m2.m[0][index]; + return result; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT> operator-(const QGenericMatrix<N, M, T, InnerT>& m1, const QGenericMatrix<N, M, T, InnerT>& m2) +{ + QGenericMatrix<N, M, T, InnerT> result(1); + for (int index = 0; index < N * M; ++index) + result.m[0][index] = m1.m[0][index] - m2.m[0][index]; + return result; +} + +template <int N, int M1, int M2, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE QGenericMatrix<M1, M2, T, InnerT> operator*(const QGenericMatrix<N, M2, T, InnerT>& m1, const QGenericMatrix<M1, N, T, InnerT>& m2) +{ + QGenericMatrix<M1, M2, T, InnerT> result(1); + for (int row = 0; row < M2; ++row) { + for (int col = 0; col < M1; ++col) { + InnerT sum(0.0f); + for (int j = 0; j < N; ++j) + sum += m1.m[j][row] * m2.m[col][j]; + result.m[col][row] = sum; + } + } + return result; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT> operator-(const QGenericMatrix<N, M, T, InnerT>& matrix) +{ + QGenericMatrix<N, M, T, InnerT> result(1); + for (int index = 0; index < N * M; ++index) + result.m[0][index] = -matrix.m[0][index]; + return result; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT> operator*(T factor, const QGenericMatrix<N, M, T, InnerT>& matrix) +{ + InnerT f(factor); + QGenericMatrix<N, M, T, InnerT> result(1); + for (int index = 0; index < N * M; ++index) + result.m[0][index] = matrix.m[0][index] * f; + return result; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT> operator*(const QGenericMatrix<N, M, T, InnerT>& matrix, T factor) +{ + InnerT f(factor); + QGenericMatrix<N, M, T, InnerT> result(1); + for (int index = 0; index < N * M; ++index) + result.m[0][index] = matrix.m[0][index] * f; + return result; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE QGenericMatrix<N, M, T, InnerT> operator/(const QGenericMatrix<N, M, T, InnerT>& matrix, T divisor) +{ + InnerT d(divisor); + QGenericMatrix<N, M, T, InnerT> result(1); + for (int index = 0; index < N * M; ++index) + result.m[0][index] = matrix.m[0][index] / d; + return result; +} + +template <int N, int M, typename T, typename InnerT> +Q_OUTOFLINE_TEMPLATE void QGenericMatrix<N, M, T, InnerT>::toValueArray(T *values) +{ + for (int col = 0; col < N; ++col) + for (int row = 0; row < M; ++row) + values[row * N + col] = T(m[col][row]); +} + +// Define aliases for the useful variants of QGenericMatrix. +typedef QGenericMatrix<2, 2, qreal, float> QMatrix2x2; +typedef QGenericMatrix<2, 3, qreal, float> QMatrix2x3; +typedef QGenericMatrix<2, 4, qreal, float> QMatrix2x4; +typedef QGenericMatrix<3, 2, qreal, float> QMatrix3x2; +typedef QGenericMatrix<3, 3, qreal, float> QMatrix3x3; +typedef QGenericMatrix<3, 4, qreal, float> QMatrix3x4; +typedef QGenericMatrix<4, 2, qreal, float> QMatrix4x2; +typedef QGenericMatrix<4, 3, qreal, float> QMatrix4x3; + +#ifndef QT_NO_DEBUG_STREAM + +template <int N, int M, typename T, typename InnerT> +QDebug operator<<(QDebug dbg, const QGenericMatrix<N, M, T, InnerT> &m) +{ + dbg.nospace() << "QGenericMatrix<" << N << ", " << M + << ", " << QTypeInfo<T>::name() << ", " << QTypeInfo<InnerT>::name() + << ">(" << endl << qSetFieldWidth(10); + for (int row = 0; row < M; ++row) { + for (int col = 0; col < N; ++col) + dbg << m(row, col); + dbg << endl; + } + dbg << qSetFieldWidth(0) << ')'; + return dbg.space(); +} + +#endif + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QMatrix2x2) +Q_DECLARE_METATYPE(QMatrix2x3) +Q_DECLARE_METATYPE(QMatrix2x4) +Q_DECLARE_METATYPE(QMatrix3x2) +Q_DECLARE_METATYPE(QMatrix3x3) +Q_DECLARE_METATYPE(QMatrix3x4) +Q_DECLARE_METATYPE(QMatrix4x2) +Q_DECLARE_METATYPE(QMatrix4x3) + +QT_END_HEADER + +#endif diff --git a/src/gui/math3d/qmatrix4x4.cpp b/src/gui/math3d/qmatrix4x4.cpp new file mode 100644 index 0000000..a69b9df --- /dev/null +++ b/src/gui/math3d/qmatrix4x4.cpp @@ -0,0 +1,1808 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmatrix4x4.h" +#include <QtCore/qmath.h> +#include <QtGui/qmatrix.h> +#include <QtGui/qtransform.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_MATRIX4X4 + +/*! + \class QMatrix4x4 + \brief The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space. + \since 4.6 + + The matrix elements are stored internally using the most efficient + numeric representation for the underlying hardware: floating-point + or fixed-point. + + \sa QVector3D, QGenericMatrix +*/ + +/*! + \fn QMatrix4x4::QMatrix4x4() + + Constructs an identity matrix. +*/ + +/*! + 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. + + If the matrix has a special type (identity, translate, scale, etc), + the programmer should follow this constructor with a call to + inferSpecialType() if they wish QMatrix4x4 to optimize further + calls to translate(), scale(), etc. + + \sa toValueArray(), inferSpecialType() +*/ +QMatrix4x4::QMatrix4x4(const qreal *values) +{ + for (int row = 0; row < 4; ++row) + for (int col = 0; col < 4; ++col) + m[col][row] = values[row * 4 + col]; + flagBits = General; +} + +/*! + \fn QMatrix4x4::QMatrix4x4(qreal m11, qreal m12, qreal m13, qreal m14, qreal m21, qreal m22, qreal m23, qreal m24, qreal m31, qreal m32, qreal m33, qreal m34, qreal m41, qreal m42, qreal m43, qreal m44) + + Constructs a matrix from the 16 elements \a m11, \a m12, \a m13, \a m14, + \a m21, \a m22, \a m23, \a m24, \a m31, \a m32, \a m33, \a m34, + \a m41, \a m42, \a m43, and \a m44. The elements are specified in + row-major order. + + If the matrix has a special type (identity, translate, scale, etc), + the programmer should follow this constructor with a call to + inferSpecialType() if they wish QMatrix4x4 to optimize further + calls to translate(), scale(), etc. + + \sa inferSpecialType() +*/ + +#if !defined(QT_NO_MEMBER_TEMPLATES) || defined(Q_QDOC) + +/*! + \fn QMatrix4x4::QMatrix4x4(const QGenericMatrix<N, M, qreal, float>& matrix) + + Constructs a 4x4 matrix from the left-most 4 columns and top-most + 4 rows of \a matrix. If \a matrix has less than 4 columns or rows, + the remaining elements are filled with elements from the identity + matrix. + + \sa toGenericMatrix(), qGenericMatrixToMatrix4x4() +*/ + +/*! + \fn QGenericMatrix<N, M, qreal, float> QMatrix4x4::toGenericMatrix() const + + Constructs a NxM generic matrix from the left-most N columns and + top-most M rows of this 4x4 matrix. If N or M is greater than 4, + then the remaining elements are filled with elements from the + identity matrix. + + \sa qGenericMatrixFromMatrix4x4() +*/ + +#endif + +/*! + \fn QMatrix4x4 qGenericMatrixToMatrix4x4(const QGenericMatrix<N, M, qreal, float>& matrix) + \relates QMatrix4x4 + + Returns a 4x4 matrix constructed from the left-most 4 columns and + top-most 4 rows of \a matrix. If \a matrix has less than 4 columns + or rows, the remaining elements are filled with elements from the + identity matrix. + + \sa qGenericMatrixFromMatrix4x4() +*/ + +/*! + \fn QGenericMatrix<N, M, qreal, float> qGenericMatrixFromMatrix4x4(const QMatrix4x4& matrix) + \relates QMatrix4x4 + + Returns a NxM generic matrix constructed from the left-most N columns + and top-most M rows of \a matrix. If N or M is greater than 4, + then the remaining elements are filled with elements from the + identity matrix. + + \sa qGenericMatrixToMatrix4x4(), QMatrix4x4::toGenericMatrix() +*/ + +/*! + \internal +*/ +QMatrix4x4::QMatrix4x4(const float *values, int cols, int rows) +{ + for (int col = 0; col < 4; ++col) { + for (int row = 0; row < 4; ++row) { + if (col < cols && row < rows) + m[col][row] = values[col * rows + row]; + else if (col == row) + m[col][row] = 1.0f; + else + m[col][row] = 0.0f; + } + } + flagBits = General; +} + +/*! + Constructs a 4x4 matrix from a conventional Qt 2D affine + transformation \a matrix. + + If \a matrix has a special type (identity, translate, scale, etc), + the programmer should follow this constructor with a call to + inferSpecialType() if they wish QMatrix4x4 to optimize further + calls to translate(), scale(), etc. + + \sa toAffine(), inferSpecialType() +*/ +QMatrix4x4::QMatrix4x4(const QMatrix& matrix) +{ + m[0][0] = matrix.m11(); + m[0][1] = matrix.m12(); + m[0][2] = 0.0f; + m[0][3] = 0.0f; + m[1][0] = matrix.m21(); + m[1][1] = matrix.m22(); + m[1][2] = 0.0f; + m[1][3] = 0.0f; + m[2][0] = 0.0f; + m[2][1] = 0.0f; + m[2][2] = 1.0f; + m[2][3] = 0.0f; + m[3][0] = matrix.dx(); + m[3][1] = matrix.dy(); + m[3][2] = 0.0f; + m[3][3] = 1.0f; + flagBits = General; +} + +/*! + Constructs a 4x4 matrix from the conventional Qt 2D + transformation matrix \a transform. + + If \a transform has a special type (identity, translate, scale, etc), + the programmer should follow this constructor with a call to + inferSpecialType() if they wish QMatrix4x4 to optimize further + calls to translate(), scale(), etc. + + \sa toTransform(), inferSpecialType() +*/ +QMatrix4x4::QMatrix4x4(const QTransform& transform) +{ + m[0][0] = transform.m11(); + m[0][1] = transform.m12(); + m[0][2] = 0.0f; + m[0][3] = transform.m13(); + m[1][0] = transform.m21(); + m[1][1] = transform.m22(); + m[1][2] = 0.0f; + m[1][3] = transform.m23(); + m[2][0] = 0.0f; + m[2][1] = 0.0f; + m[2][2] = 1.0f; + m[2][3] = 0.0f; + m[3][0] = transform.dx(); + m[3][1] = transform.dy(); + m[3][2] = 0.0f; + m[3][3] = transform.m33(); + flagBits = General; +} + +/*! + \fn qreal QMatrix4x4::operator()(int row, int column) const + + Returns the element at position (\a row, \a column) in this matrix. + + \sa column(), row() +*/ + +/*! + \fn float& QMatrix4x4::operator()(int row, int column) + + Returns a reference to the element at position (\a row, \a column) + in this matrix so that the element can be assigned to. + + \sa inferSpecialType(), setColumn(), setRow() +*/ + +/*! + \fn QVector4D QMatrix4x4::column(int index) const + + Returns the elements of column \a index as a 4D vector. + + \sa setColumn(), row() +*/ + +/*! + \fn void QMatrix4x4::setColumn(int index, const QVector4D& value) + + Sets the elements of column \a index to the components of \a value. + + \sa column(), setRow() +*/ + +/*! + \fn QVector4D QMatrix4x4::row(int index) const + + Returns the elements of row \a index as a 4D vector. + + \sa setRow(), column() +*/ + +/*! + \fn void QMatrix4x4::setRow(int index, const QVector4D& value) + + Sets the elements of row \a index to the components of \a value. + + \sa row(), setColumn() +*/ + +/*! + \fn bool QMatrix4x4::isIdentity() const + + Returns true if this matrix is the identity; false otherwise. + + \sa setIdentity() +*/ + +/*! + \fn void QMatrix4x4::setIdentity() + + Sets this matrix to the identity. + + \sa isIdentity() +*/ + +/*! + \fn void QMatrix4x4::fill(qreal value) + + Fills all elements of this matrx with \a value. +*/ + +// The 4x4 matrix inverse algorithm is based on that described at: +// http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q24 +// Some optimization has been done to avoid making copies of 3x3 +// sub-matrices, to do calculations in fixed-point where required, +// and to unroll the loops. + +// Calculate the determinant of a 3x3 sub-matrix. +// | A B C | +// M = | D E F | det(M) = A * (EI - HF) - B * (DI - GF) + C * (DH - GE) +// | G H I | +static inline float matrixDet3 + (const float m[4][4], int col0, int col1, int col2, + int row0, int row1, int row2) +{ + return m[col0][row0] * + (m[col1][row1] * m[col2][row2] - + m[col1][row2] * m[col2][row1]) - + m[col1][row0] * + (m[col0][row1] * m[col2][row2] - + m[col0][row2] * m[col2][row1]) + + m[col2][row0] * + (m[col0][row1] * m[col1][row2] - + m[col0][row2] * m[col1][row1]); +} + +// Calculate the determinant of a 4x4 matrix. +static inline float matrixDet4(const float m[4][4]) +{ + float det; + det = m[0][0] * matrixDet3(m, 1, 2, 3, 1, 2, 3); + det -= m[1][0] * matrixDet3(m, 0, 2, 3, 1, 2, 3); + det += m[2][0] * matrixDet3(m, 0, 1, 3, 1, 2, 3); + det -= m[3][0] * matrixDet3(m, 0, 1, 2, 1, 2, 3); + return det; +} + +/*! + Returns the determinant of this matrix. +*/ +qreal QMatrix4x4::determinant() const +{ + return qreal(matrixDet4(m)); +} + +/*! + Returns the inverse of this matrix. Returns the identity if + this matrix cannot be inverted; i.e. determinant() is zero. + If \a invertible is not null, then true will be written to + that location if the matrix can be inverted; false otherwise. + + If the matrix is recognized as the identity or an orthonormal + matrix, then this function will quickly invert the matrix + using optimized routines. + + \sa determinant(), normalMatrix() +*/ +QMatrix4x4 QMatrix4x4::inverted(bool *invertible) const +{ + // Handle some of the easy cases first. + if (flagBits == Identity) { + if (invertible) + *invertible = true; + return QMatrix4x4(); + } else if (flagBits == Translation) { + QMatrix4x4 inv; + inv.m[3][0] = -m[3][0]; + inv.m[3][1] = -m[3][1]; + inv.m[3][2] = -m[3][2]; + inv.flagBits = Translation; + if (invertible) + *invertible = true; + return inv; + } else if (flagBits == Rotation || flagBits == (Rotation | Translation)) { + if (invertible) + *invertible = true; + return orthonormalInverse(); + } + + QMatrix4x4 inv(1); // The "1" says to not load the identity. + + float det = matrixDet4(m); + if (det == 0.0f) { + if (invertible) + *invertible = false; + return QMatrix4x4(); + } + det = 1.0f / det; + + inv.m[0][0] = matrixDet3(m, 1, 2, 3, 1, 2, 3) * det; + inv.m[0][1] = -matrixDet3(m, 0, 2, 3, 1, 2, 3) * det; + inv.m[0][2] = matrixDet3(m, 0, 1, 3, 1, 2, 3) * det; + inv.m[0][3] = -matrixDet3(m, 0, 1, 2, 1, 2, 3) * det; + inv.m[1][0] = -matrixDet3(m, 1, 2, 3, 0, 2, 3) * det; + inv.m[1][1] = matrixDet3(m, 0, 2, 3, 0, 2, 3) * det; + inv.m[1][2] = -matrixDet3(m, 0, 1, 3, 0, 2, 3) * det; + inv.m[1][3] = matrixDet3(m, 0, 1, 2, 0, 2, 3) * det; + inv.m[2][0] = matrixDet3(m, 1, 2, 3, 0, 1, 3) * det; + inv.m[2][1] = -matrixDet3(m, 0, 2, 3, 0, 1, 3) * det; + inv.m[2][2] = matrixDet3(m, 0, 1, 3, 0, 1, 3) * det; + inv.m[2][3] = -matrixDet3(m, 0, 1, 2, 0, 1, 3) * det; + inv.m[3][0] = -matrixDet3(m, 1, 2, 3, 0, 1, 2) * det; + inv.m[3][1] = matrixDet3(m, 0, 2, 3, 0, 1, 2) * det; + inv.m[3][2] = -matrixDet3(m, 0, 1, 3, 0, 1, 2) * det; + inv.m[3][3] = matrixDet3(m, 0, 1, 2, 0, 1, 2) * det; + + if (invertible) + *invertible = true; + return inv; +} + +/*! + Returns the normal matrix corresponding to this 4x4 transformation. + The normal matrix is the transpose of the inverse of the top-left + 3x3 part of this 4x4 matrix. If the 3x3 sub-matrix is not invertible, + this function returns the identity. + + \sa inverted() +*/ +QMatrix3x3 QMatrix4x4::normalMatrix() const +{ + QMatrix3x3 inv; + + // Handle the simple cases first. + if (flagBits == Identity || flagBits == Translation) { + return inv; + } else if (flagBits == Scale || flagBits == (Translation | Scale)) { + if (m[0][0] == 0.0f || m[1][1] == 0.0f || m[2][2] == 0.0f) + return inv; + inv.data()[0] = 1.0f / m[0][0]; + inv.data()[4] = 1.0f / m[1][1]; + inv.data()[8] = 1.0f / m[2][2]; + return inv; + } + + float det = matrixDet3(m, 0, 1, 2, 0, 1, 2); + if (det == 0.0f) + return inv; + det = 1.0f / det; + + float *invm = inv.data(); + + // Invert and transpose in a single step. + invm[0 + 0 * 3] = (m[1][1] * m[2][2] - m[2][1] * m[1][2]) * det; + invm[1 + 0 * 3] = -(m[1][0] * m[2][2] - m[1][2] * m[2][0]) * det; + invm[2 + 0 * 3] = (m[1][0] * m[2][1] - m[1][1] * m[2][0]) * det; + invm[0 + 1 * 3] = -(m[0][1] * m[2][2] - m[2][1] * m[0][2]) * det; + invm[1 + 1 * 3] = (m[0][0] * m[2][2] - m[0][2] * m[2][0]) * det; + invm[2 + 1 * 3] = -(m[0][0] * m[2][1] - m[0][1] * m[2][0]) * det; + invm[0 + 2 * 3] = (m[0][1] * m[1][2] - m[0][2] * m[1][1]) * det; + invm[1 + 2 * 3] = -(m[0][0] * m[1][2] - m[0][2] * m[1][0]) * det; + invm[2 + 2 * 3] = (m[0][0] * m[1][1] - m[1][0] * m[0][1]) * det; + + return inv; +} + +/*! + Returns this matrix, transposed about its diagonal. +*/ +QMatrix4x4 QMatrix4x4::transposed() const +{ + QMatrix4x4 result(1); // The "1" says to not load the identity. + for (int row = 0; row < 4; ++row) { + for (int col = 0; col < 4; ++col) { + result.m[col][row] = m[row][col]; + } + } + return result; +} + +/*! + \fn QMatrix4x4& QMatrix4x4::operator+=(const QMatrix4x4& other) + + Adds the contents of \a other to this matrix. +*/ + +/*! + \fn QMatrix4x4& QMatrix4x4::operator-=(const QMatrix4x4& other) + + Subtracts the contents of \a other from this matrix. +*/ + +/*! + \fn QMatrix4x4& QMatrix4x4::operator*=(const QMatrix4x4& other) + + Multiplies the contents of \a other by this matrix. +*/ + +/*! + \fn QMatrix4x4& QMatrix4x4::operator*=(qreal factor) + \overload + + Multiplies all elements of this matrix by \a factor. +*/ + +/*! + \overload + + Divides all elements of this matrix by \a divisor. +*/ +QMatrix4x4& QMatrix4x4::operator/=(qreal divisor) +{ + m[0][0] /= divisor; + m[0][1] /= divisor; + m[0][2] /= divisor; + m[0][3] /= divisor; + m[1][0] /= divisor; + m[1][1] /= divisor; + m[1][2] /= divisor; + m[1][3] /= divisor; + m[2][0] /= divisor; + m[2][1] /= divisor; + m[2][2] /= divisor; + m[2][3] /= divisor; + m[3][0] /= divisor; + m[3][1] /= divisor; + m[3][2] /= divisor; + m[3][3] /= divisor; + flagBits = General; + return *this; +} + +/*! + \fn bool QMatrix4x4::operator==(const QMatrix4x4& other) const + + Returns true if this matrix is identical to \a other; false otherwise. + This operator uses an exact floating-point comparison. +*/ + +/*! + \fn bool QMatrix4x4::operator!=(const QMatrix4x4& other) const + + Returns true if this matrix is not identical to \a other; false otherwise. + This operator uses an exact floating-point comparison. +*/ + +/*! + \fn QMatrix4x4 operator+(const QMatrix4x4& m1, const QMatrix4x4& m2) + \relates QMatrix4x4 + + Returns the sum of \a m1 and \a m2. +*/ + +/*! + \fn QMatrix4x4 operator-(const QMatrix4x4& m1, const QMatrix4x4& m2) + \relates QMatrix4x4 + + Returns the difference of \a m1 and \a m2. +*/ + +/*! + \fn QMatrix4x4 operator*(const QMatrix4x4& m1, const QMatrix4x4& m2) + \relates QMatrix4x4 + + Returns the product of \a m1 and \a m2. +*/ + +#ifndef QT_NO_VECTOR3D + +/*! + \fn QVector3D operator*(const QVector3D& vector, const QMatrix4x4& matrix) + \relates QMatrix4x4 + + Returns the result of transforming \a vector according to \a matrix, + with the matrix applied post-vector. +*/ + +/*! + \fn QVector3D operator*(const QMatrix4x4& matrix, const QVector3D& vector) + \relates QMatrix4x4 + + Returns the result of transforming \a vector according to \a matrix, + with the matrix applied pre-vector. +*/ + +#endif + +#ifndef QT_NO_VECTOR4D + +/*! + \fn QVector4D operator*(const QVector4D& vector, const QMatrix4x4& matrix) + \relates QMatrix4x4 + + Returns the result of transforming \a vector according to \a matrix, + with the matrix applied post-vector. +*/ + +/*! + \fn QVector4D operator*(const QMatrix4x4& matrix, const QVector4D& vector) + \relates QMatrix4x4 + + Returns the result of transforming \a vector according to \a matrix, + with the matrix applied pre-vector. +*/ + +#endif + +/*! + \fn QPoint operator*(const QPoint& point, const QMatrix4x4& matrix) + \relates QMatrix4x4 + + Returns the result of transforming \a point according to \a matrix, + with the matrix applied post-point. +*/ + +/*! + \fn QPointF operator*(const QPointF& point, const QMatrix4x4& matrix) + \relates QMatrix4x4 + + Returns the result of transforming \a point according to \a matrix, + with the matrix applied post-point. +*/ + +/*! + \fn QPoint operator*(const QMatrix4x4& matrix, const QPoint& point) + \relates QMatrix4x4 + + Returns the result of transforming \a point according to \a matrix, + with the matrix applied pre-point. +*/ + +/*! + \fn QPointF operator*(const QMatrix4x4& matrix, const QPointF& point) + \relates QMatrix4x4 + + Returns the result of transforming \a point according to \a matrix, + with the matrix applied pre-point. +*/ + +/*! + \fn QMatrix4x4 operator-(const QMatrix4x4& matrix) + \overload + \relates QMatrix4x4 + + Returns the negation of \a matrix. +*/ + +/*! + \fn QMatrix4x4 operator*(qreal factor, const QMatrix4x4& matrix) + \relates QMatrix4x4 + + Returns the result of multiplying all elements of \a matrix by \a factor. +*/ + +/*! + \fn QMatrix4x4 operator*(const QMatrix4x4& matrix, qreal factor) + \relates QMatrix4x4 + + Returns the result of multiplying all elements of \a matrix by \a factor. +*/ + +/*! + \relates QMatrix4x4 + + Returns the result of dividing all elements of \a matrix by \a divisor. +*/ +QMatrix4x4 operator/(const QMatrix4x4& matrix, qreal divisor) +{ + QMatrix4x4 m(1); // The "1" says to not load the identity. + m.m[0][0] = matrix.m[0][0] / divisor; + m.m[0][1] = matrix.m[0][1] / divisor; + m.m[0][2] = matrix.m[0][2] / divisor; + m.m[0][3] = matrix.m[0][3] / divisor; + m.m[1][0] = matrix.m[1][0] / divisor; + m.m[1][1] = matrix.m[1][1] / divisor; + m.m[1][2] = matrix.m[1][2] / divisor; + m.m[1][3] = matrix.m[1][3] / divisor; + m.m[2][0] = matrix.m[2][0] / divisor; + m.m[2][1] = matrix.m[2][1] / divisor; + m.m[2][2] = matrix.m[2][2] / divisor; + m.m[2][3] = matrix.m[2][3] / divisor; + m.m[3][0] = matrix.m[3][0] / divisor; + m.m[3][1] = matrix.m[3][1] / divisor; + m.m[3][2] = matrix.m[3][2] / divisor; + m.m[3][3] = matrix.m[3][3] / divisor; + return m; +} + +/*! + \fn bool qFuzzyCompare(const QMatrix4x4& m1, const QMatrix4x4& m2) + \relates QMatrix4x4 + + Returns true if \a m1 and \a m2 are equal, allowing for a small + fuzziness factor for floating-point comparisons; false otherwise. +*/ + +#ifndef QT_NO_VECTOR3D + +/*! + Multiplies this matrix by another that scales coordinates by + the components of \a vector. Returns this matrix. + + \sa translate(), rotate() +*/ +QMatrix4x4& QMatrix4x4::scale(const QVector3D& vector) +{ + float vx = vector.xp; + float vy = vector.yp; + float vz = vector.zp; + if (flagBits == Identity) { + m[0][0] = vx; + m[1][1] = vy; + m[2][2] = vz; + flagBits = Scale; + } else if (flagBits == Scale || flagBits == (Scale | Translation)) { + m[0][0] *= vx; + m[1][1] *= vy; + m[2][2] *= vz; + } else if (flagBits == Translation) { + m[0][0] = vx; + m[1][1] = vy; + m[2][2] = vz; + flagBits |= Scale; + } else { + m[0][0] *= vx; + m[0][1] *= vx; + m[0][2] *= vx; + m[0][3] *= vx; + m[1][0] *= vy; + m[1][1] *= vy; + m[1][2] *= vy; + m[1][3] *= vy; + m[2][0] *= vz; + m[2][1] *= vz; + m[2][2] *= vz; + m[2][3] *= vz; + flagBits = General; + } + return *this; +} +#endif + +/*! + \overload + + Multiplies this matrix by another that scales coordinates by the + components \a x, and \a y. Returns this matrix. + + \sa translate(), rotate() +*/ +QMatrix4x4& QMatrix4x4::scale(qreal x, qreal y) +{ + float vx(x); + float vy(y); + if (flagBits == Identity) { + m[0][0] = vx; + m[1][1] = vy; + flagBits = Scale; + } else if (flagBits == Scale || flagBits == (Scale | Translation)) { + m[0][0] *= vx; + m[1][1] *= vy; + } else if (flagBits == Translation) { + m[0][0] = vx; + m[1][1] = vy; + flagBits |= Scale; + } else { + m[0][0] *= vx; + m[0][1] *= vx; + m[0][2] *= vx; + m[0][3] *= vx; + m[1][0] *= vy; + m[1][1] *= vy; + m[1][2] *= vy; + m[1][3] *= vy; + flagBits = General; + } + return *this; +} + +/*! + \overload + + Multiplies this matrix by another that scales coordinates by the + components \a x, \a y, and \a z. Returns this matrix. + + \sa translate(), rotate() +*/ +QMatrix4x4& QMatrix4x4::scale(qreal x, qreal y, qreal z) +{ + float vx(x); + float vy(y); + float vz(z); + if (flagBits == Identity) { + m[0][0] = vx; + m[1][1] = vy; + m[2][2] = vz; + flagBits = Scale; + } else if (flagBits == Scale || flagBits == (Scale | Translation)) { + m[0][0] *= vx; + m[1][1] *= vy; + m[2][2] *= vz; + } else if (flagBits == Translation) { + m[0][0] = vx; + m[1][1] = vy; + m[2][2] = vz; + flagBits |= Scale; + } else { + m[0][0] *= vx; + m[0][1] *= vx; + m[0][2] *= vx; + m[0][3] *= vx; + m[1][0] *= vy; + m[1][1] *= vy; + m[1][2] *= vy; + m[1][3] *= vy; + m[2][0] *= vz; + m[2][1] *= vz; + m[2][2] *= vz; + m[2][3] *= vz; + flagBits = General; + } + return *this; +} + +/*! + \overload + + Multiplies this matrix by another that scales coordinates by the + given \a factor. Returns this matrix. + + \sa translate(), rotate() +*/ +QMatrix4x4& QMatrix4x4::scale(qreal factor) +{ + if (flagBits == Identity) { + m[0][0] = factor; + m[1][1] = factor; + m[2][2] = factor; + flagBits = Scale; + } else if (flagBits == Scale || flagBits == (Scale | Translation)) { + m[0][0] *= factor; + m[1][1] *= factor; + m[2][2] *= factor; + } else if (flagBits == Translation) { + m[0][0] = factor; + m[1][1] = factor; + m[2][2] = factor; + flagBits |= Scale; + } else { + m[0][0] *= factor; + m[0][1] *= factor; + m[0][2] *= factor; + m[0][3] *= factor; + m[1][0] *= factor; + m[1][1] *= factor; + m[1][2] *= factor; + m[1][3] *= factor; + m[2][0] *= factor; + m[2][1] *= factor; + m[2][2] *= factor; + m[2][3] *= factor; + flagBits = General; + } + return *this; +} + +#ifndef QT_NO_VECTOR3D +/*! + Multiplies this matrix by another that translates coordinates by + the components of \a vector. Returns this matrix. + + \sa scale(), rotate() +*/ +QMatrix4x4& QMatrix4x4::translate(const QVector3D& vector) +{ + float vx = vector.xp; + float vy = vector.yp; + float vz = vector.zp; + if (flagBits == Identity) { + m[3][0] = vx; + m[3][1] = vy; + m[3][2] = vz; + flagBits = Translation; + } else if (flagBits == Translation) { + m[3][0] += vx; + m[3][1] += vy; + m[3][2] += vz; + } else if (flagBits == Scale) { + m[3][0] = m[0][0] * vx; + m[3][1] = m[1][1] * vy; + m[3][2] = m[2][2] * vz; + flagBits |= Translation; + } else if (flagBits == (Scale | Translation)) { + m[3][0] += m[0][0] * vx; + m[3][1] += m[1][1] * vy; + m[3][2] += m[2][2] * vz; + } else { + m[3][0] += m[0][0] * vx + m[1][0] * vy + m[2][0] * vz; + m[3][1] += m[0][1] * vx + m[1][1] * vy + m[2][1] * vz; + m[3][2] += m[0][2] * vx + m[1][2] * vy + m[2][2] * vz; + m[3][3] += m[0][3] * vx + m[1][3] * vy + m[2][3] * vz; + if (flagBits == Rotation) + flagBits |= Translation; + else if (flagBits != (Rotation | Translation)) + flagBits = General; + } + return *this; +} + +#endif + +/*! + \overload + + Multiplies this matrix by another that translates coordinates + by the components \a x, and \a y. Returns this matrix. + + \sa scale(), rotate() +*/ +QMatrix4x4& QMatrix4x4::translate(qreal x, qreal y) +{ + float vx(x); + float vy(y); + if (flagBits == Identity) { + m[3][0] = vx; + m[3][1] = vy; + flagBits = Translation; + } else if (flagBits == Translation) { + m[3][0] += vx; + m[3][1] += vy; + } else if (flagBits == Scale) { + m[3][0] = m[0][0] * vx; + m[3][1] = m[1][1] * vy; + m[3][2] = 0.; + flagBits |= Translation; + } else if (flagBits == (Scale | Translation)) { + m[3][0] += m[0][0] * vx; + m[3][1] += m[1][1] * vy; + } else { + m[3][0] += m[0][0] * vx + m[1][0] * vy; + m[3][1] += m[0][1] * vx + m[1][1] * vy; + m[3][2] += m[0][2] * vx + m[1][2] * vy; + m[3][3] += m[0][3] * vx + m[1][3] * vy; + if (flagBits == Rotation) + flagBits |= Translation; + else if (flagBits != (Rotation | Translation)) + flagBits = General; + } + return *this; +} + +/*! + \overload + + Multiplies this matrix by another that translates coordinates + by the components \a x, \a y, and \a z. Returns this matrix. + + \sa scale(), rotate() +*/ +QMatrix4x4& QMatrix4x4::translate(qreal x, qreal y, qreal z) +{ + float vx(x); + float vy(y); + float vz(z); + if (flagBits == Identity) { + m[3][0] = vx; + m[3][1] = vy; + m[3][2] = vz; + flagBits = Translation; + } else if (flagBits == Translation) { + m[3][0] += vx; + m[3][1] += vy; + m[3][2] += vz; + } else if (flagBits == Scale) { + m[3][0] = m[0][0] * vx; + m[3][1] = m[1][1] * vy; + m[3][2] = m[2][2] * vz; + flagBits |= Translation; + } else if (flagBits == (Scale | Translation)) { + m[3][0] += m[0][0] * vx; + m[3][1] += m[1][1] * vy; + m[3][2] += m[2][2] * vz; + } else { + m[3][0] += m[0][0] * vx + m[1][0] * vy + m[2][0] * vz; + m[3][1] += m[0][1] * vx + m[1][1] * vy + m[2][1] * vz; + m[3][2] += m[0][2] * vx + m[1][2] * vy + m[2][2] * vz; + m[3][3] += m[0][3] * vx + m[1][3] * vy + m[2][3] * vz; + if (flagBits == Rotation) + flagBits |= Translation; + else if (flagBits != (Rotation | Translation)) + flagBits = General; + } + return *this; +} + +#ifndef QT_NO_VECTOR3D + +/*! + Multiples this matrix by another that rotates coordinates through + \a angle degrees about \a vector. Returns this matrix. + + \sa scale(), translate() +*/ +QMatrix4x4& QMatrix4x4::rotate(qreal angle, const QVector3D& vector) +{ + return rotate(angle, vector.x(), vector.y(), vector.z()); +} + +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +/*! + \overload + + Multiplies this matrix by another that rotates coordinates through + \a angle degrees about the vector (\a x, \a y, \a z). Returns this matrix. + + \sa scale(), translate() +*/ +QMatrix4x4& QMatrix4x4::rotate(qreal angle, qreal x, qreal y, qreal z) +{ + QMatrix4x4 m(1); // The "1" says to not load the identity. + qreal a = angle * M_PI / 180.0f; + qreal c = qCos(a); + qreal s = qSin(a); + qreal ic; + bool quick = false; + if (x == 0.0f) { + if (y == 0.0f) { + if (z != 0.0f) { + // Rotate around the Z axis. + m.setIdentity(); + m.m[0][0] = c; + m.m[1][1] = c; + if (z < 0.0f) { + m.m[1][0] = s; + m.m[0][1] = -s; + } else { + m.m[1][0] = -s; + m.m[0][1] = s; + } + m.flagBits = General; + quick = true; + } + } else if (z == 0.0f) { + // Rotate around the Y axis. + m.setIdentity(); + m.m[0][0] = c; + m.m[2][2] = c; + if (y < 0.0f) { + m.m[2][0] = -s; + m.m[0][2] = s; + } else { + m.m[2][0] = s; + m.m[0][2] = -s; + } + m.flagBits = General; + quick = true; + } + } else if (y == 0.0f && z == 0.0f) { + // Rotate around the X axis. + m.setIdentity(); + m.m[1][1] = c; + m.m[2][2] = c; + if (x < 0.0f) { + m.m[2][1] = s; + m.m[1][2] = -s; + } else { + m.m[2][1] = -s; + m.m[1][2] = s; + } + m.flagBits = General; + quick = true; + } + if (!quick) { + qreal len = x * x + y * y + z * z; + if (!qFuzzyIsNull(len - 1.0f) && !qFuzzyIsNull(len)) { + len = qSqrt(len); + x /= len; + y /= len; + z /= len; + } + ic = 1.0f - c; + m.m[0][0] = x * x * ic + c; + m.m[1][0] = x * y * ic - z * s; + m.m[2][0] = x * z * ic + y * s; + m.m[3][0] = 0.0f; + m.m[0][1] = y * x * ic + z * s; + m.m[1][1] = y * y * ic + c; + m.m[2][1] = y * z * ic - x * s; + m.m[3][1] = 0.0f; + m.m[0][2] = x * z * ic - y * s; + m.m[1][2] = y * z * ic + x * s; + m.m[2][2] = z * z * ic + c; + m.m[3][2] = 0.0f; + m.m[0][3] = 0.0f; + m.m[1][3] = 0.0f; + m.m[2][3] = 0.0f; + m.m[3][3] = 1.0f; + } + int flags = flagBits; + *this *= m; + if (flags != Identity) + flagBits = flags | Rotation; + else + flagBits = Rotation; + return *this; +} + +#ifndef QT_NO_VECTOR4D + +/*! + Multiples this matrix by another that rotates coordinates according + to a specified \a quaternion. The \a quaternion is assumed to have + been normalized. Returns this matrix. + + \sa scale(), translate(), QQuaternion +*/ +QMatrix4x4& QMatrix4x4::rotate(const QQuaternion& quaternion) +{ + // Algorithm from: + // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q54 + QMatrix4x4 m(1); + float xx = quaternion.xp * quaternion.xp; + float xy = quaternion.xp * quaternion.yp; + float xz = quaternion.xp * quaternion.zp; + float xw = quaternion.xp * quaternion.wp; + float yy = quaternion.yp * quaternion.yp; + float yz = quaternion.yp * quaternion.zp; + float yw = quaternion.yp * quaternion.wp; + float zz = quaternion.zp * quaternion.zp; + float zw = quaternion.zp * quaternion.wp; + m.m[0][0] = 1.0f - 2 * (yy + zz); + m.m[1][0] = 2 * (xy - zw); + m.m[2][0] = 2 * (xz + yw); + 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[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[3][2] = 0.0f; + m.m[0][3] = 0.0f; + m.m[1][3] = 0.0f; + m.m[2][3] = 0.0f; + m.m[3][3] = 1.0f; + int flags = flagBits; + *this *= m; + if (flags != Identity) + flagBits = flags | Rotation; + else + flagBits = Rotation; + return *this; +} + +#endif + +/*! + \overload + + Multiplies this matrix by another that applies an orthographic + projection for a window with boundaries specified by \a rect. + The near and far clipping planes will be -1 and 1 respectively. + Returns this matrix. + + \sa frustum(), perspective() +*/ +QMatrix4x4& QMatrix4x4::ortho(const QRect& rect) +{ + // Note: rect.right() and rect.bottom() subtract 1 in QRect, + // which gives the location of a pixel within the rectangle, + // instead of the extent of the rectangle. We want the extent. + // QRectF expresses the extent properly. + return ortho(rect.x(), rect.x() + rect.width(), rect.y() + rect.height(), rect.y(), -1.0f, 1.0f); +} + +/*! + \overload + + Multiplies this matrix by another that applies an orthographic + projection for a window with boundaries specified by \a rect. + The near and far clipping planes will be -1 and 1 respectively. + Returns this matrix. + + \sa frustum(), perspective() +*/ +QMatrix4x4& QMatrix4x4::ortho(const QRectF& rect) +{ + return ortho(rect.left(), rect.right(), rect.bottom(), rect.top(), -1.0f, 1.0f); +} + +/*! + Multiplies this matrix by another that applies an orthographic + projection for a window with lower-left corner (\a left, \a bottom), + upper-right corner (\a right, \a top), and the specified \a nearPlane + and \a farPlane clipping planes. Returns this matrix. + + \sa frustum(), perspective() +*/ +QMatrix4x4& QMatrix4x4::ortho(qreal left, qreal right, qreal bottom, qreal top, qreal nearPlane, qreal farPlane) +{ + // Bail out if the projection volume is zero-sized. + if (left == right || bottom == top || nearPlane == farPlane) + return *this; + + // Construct the projection. + qreal width = right - left; + qreal invheight = top - bottom; + qreal clip = farPlane - nearPlane; +#ifndef QT_NO_VECTOR3D + if (clip == 2.0f && (nearPlane + farPlane) == 0.0f) { + // We can express this projection as a translate and scale + // which will be more efficient to modify with further + // transformations than producing a "General" matrix. + translate(QVector3D + (-(left + right) / width, + -(top + bottom) / invheight, + 0.0f, 1)); + scale(QVector3D + (2.0f / width, + 2.0f / invheight, + -1.0f, 1)); + return *this; + } +#endif + QMatrix4x4 m(1); + m.m[0][0] = 2.0f / width; + m.m[1][0] = 0.0f; + m.m[2][0] = 0.0f; + m.m[3][0] = -(left + right) / width; + m.m[0][1] = 0.0f; + m.m[1][1] = 2.0f / invheight; + m.m[2][1] = 0.0f; + m.m[3][1] = -(top + bottom) / invheight; + m.m[0][2] = 0.0f; + m.m[1][2] = 0.0f; + m.m[2][2] = -2.0f / clip; + m.m[3][2] = -(nearPlane + farPlane) / clip; + m.m[0][3] = 0.0f; + m.m[1][3] = 0.0f; + m.m[2][3] = 0.0f; + m.m[3][3] = 1.0f; + + // Apply the projection. + *this *= m; + return *this; +} + +/*! + Multiplies this matrix by another that applies a perspective + frustum projection for a window with lower-left corner (\a left, \a bottom), + upper-right corner (\a right, \a top), and the specified \a nearPlane + and \a farPlane clipping planes. Returns this matrix. + + \sa ortho(), perspective() +*/ +QMatrix4x4& QMatrix4x4::frustum(qreal left, qreal right, qreal bottom, qreal top, qreal nearPlane, qreal farPlane) +{ + // Bail out if the projection volume is zero-sized. + if (left == right || bottom == top || nearPlane == farPlane) + return *this; + + // Construct the projection. + QMatrix4x4 m(1); + qreal width = right - left; + qreal invheight = top - bottom; + qreal clip = farPlane - nearPlane; + m.m[0][0] = 2.0f * nearPlane / width; + m.m[1][0] = 0.0f; + m.m[2][0] = (left + right) / width; + m.m[3][0] = 0.0f; + m.m[0][1] = 0.0f; + m.m[1][1] = 2.0f * nearPlane / invheight; + m.m[2][1] = (top + bottom) / invheight; + m.m[3][1] = 0.0f; + m.m[0][2] = 0.0f; + m.m[1][2] = 0.0f; + m.m[2][2] = -(nearPlane + farPlane) / clip; + m.m[3][2] = -2.0f * nearPlane * farPlane / clip; + m.m[0][3] = 0.0f; + m.m[1][3] = 0.0f; + m.m[2][3] = -1.0f; + m.m[3][3] = 0.0f; + + // Apply the projection. + *this *= m; + return *this; +} + +/*! + Multiplies this matrix by another that applies a perspective + projection. The field of view will be \a angle degrees within + a window with a given \a aspect ratio. The projection will + have the specified \a nearPlane and \a farPlane clipping planes. + Returns this matrix. + + \sa ortho(), frustum() +*/ +QMatrix4x4& QMatrix4x4::perspective(qreal angle, qreal aspect, qreal nearPlane, qreal farPlane) +{ + // Bail out if the projection volume is zero-sized. + if (nearPlane == farPlane || aspect == 0.0f) + return *this; + + // Construct the projection. + QMatrix4x4 m(1); + qreal radians = (angle / 2.0f) * M_PI / 180.0f; + qreal sine = qSin(radians); + if (sine == 0.0f) + return *this; + qreal cotan = qCos(radians) / sine; + qreal clip = farPlane - nearPlane; + m.m[0][0] = cotan / aspect; + m.m[1][0] = 0.0f; + m.m[2][0] = 0.0f; + m.m[3][0] = 0.0f; + m.m[0][1] = 0.0f; + m.m[1][1] = cotan; + m.m[2][1] = 0.0f; + m.m[3][1] = 0.0f; + m.m[0][2] = 0.0f; + m.m[1][2] = 0.0f; + m.m[2][2] = -(nearPlane + farPlane) / clip; + m.m[3][2] = -(2.0f * nearPlane * farPlane) / clip; + m.m[0][3] = 0.0f; + m.m[1][3] = 0.0f; + m.m[2][3] = -1.0f; + m.m[3][3] = 0.0f; + + // Apply the projection. + *this *= m; + return *this; +} + +#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. + Returns this matrix. +*/ +QMatrix4x4& QMatrix4x4::lookAt(const QVector3D& eye, const QVector3D& center, const QVector3D& up) +{ + QVector3D forward = (center - eye).normalized(); + QVector3D side = QVector3D::crossProduct(forward, up).normalized(); + QVector3D upVector = QVector3D::crossProduct(side, forward); + + QMatrix4x4 m(1); + + m.m[0][0] = side.xp; + m.m[1][0] = side.yp; + m.m[2][0] = side.zp; + m.m[3][0] = 0.0f; + m.m[0][1] = upVector.xp; + m.m[1][1] = upVector.yp; + m.m[2][1] = upVector.zp; + m.m[3][1] = 0.0f; + m.m[0][2] = -forward.xp; + m.m[1][2] = -forward.yp; + m.m[2][2] = -forward.zp; + m.m[3][2] = 0.0f; + m.m[0][3] = 0.0f; + m.m[1][3] = 0.0f; + m.m[2][3] = 0.0f; + m.m[3][3] = 1.0f; + + *this *= m; + return translate(-eye); +} + +#endif + +/*! + Flips between right-handed and left-handed coordinate systems + by multiplying the y and z co-ordinates by -1. This is normally + used to create a left-handed orthographic view without scaling + the viewport as ortho() does. Returns this matrix. + + \sa ortho() +*/ +QMatrix4x4& QMatrix4x4::flipCoordinates() +{ + if (flagBits == Scale || flagBits == (Scale | Translation)) { + m[1][1] = -m[1][1]; + m[2][2] = -m[2][2]; + } else if (flagBits == Translation) { + m[1][1] = -m[1][1]; + m[2][2] = -m[2][2]; + flagBits |= Scale; + } else if (flagBits == Identity) { + m[1][1] = -1.0f; + m[2][2] = -1.0f; + flagBits = Scale; + } else { + m[1][0] = -m[1][0]; + m[1][1] = -m[1][1]; + m[1][2] = -m[1][2]; + m[1][3] = -m[1][3]; + m[2][0] = -m[2][0]; + m[2][1] = -m[2][1]; + m[2][2] = -m[2][2]; + m[2][3] = -m[2][3]; + flagBits = General; + } + return *this; +} + +/*! + Retrieves the 16 items in this matrix and writes them to \a values + in row-major order. +*/ +void QMatrix4x4::toValueArray(qreal *values) const +{ + for (int row = 0; row < 4; ++row) + for (int col = 0; col < 4; ++col) + values[row * 4 + col] = qreal(m[col][row]); +} + +/*! + Returns the conventional Qt 2D affine transformation matrix that + corresponds to this matrix. It is assumed that this matrix + only contains 2D affine transformation elements. + + \sa toTransform() +*/ +QMatrix QMatrix4x4::toAffine() const +{ + return QMatrix(qreal(m[0][0]), qreal(m[0][1]), + qreal(m[1][0]), qreal(m[1][1]), + qreal(m[3][0]), qreal(m[3][1])); +} + +/*! + Returns the conventional Qt 2D transformation matrix that + corresponds to this matrix. It is assumed that this matrix + only contains 2D transformation elements. + + \sa toAffine() +*/ +QTransform QMatrix4x4::toTransform() const +{ + return QTransform(qreal(m[0][0]), qreal(m[0][1]), qreal(m[0][3]), + qreal(m[1][0]), qreal(m[1][1]), qreal(m[1][3]), + qreal(m[3][0]), qreal(m[3][1]), qreal(m[3][3])); +} + +/*! + \fn QPoint QMatrix4x4::map(const QPoint& point) const + + Maps \a point by multiplying this matrix by \a point. + + \sa mapRect() +*/ + +/*! + \fn QPointF QMatrix4x4::map(const QPointF& point) const + + Maps \a point by multiplying this matrix by \a point. + + \sa mapRect() +*/ + +#ifndef QT_NO_VECTOR3D + +/*! + \fn QVector3D QMatrix4x4::map(const QVector3D& point) const + + Maps \a point by multiplying this matrix by \a point. + + \sa mapRect() +*/ + +#endif + +#ifndef QT_NO_VECTOR4D + +/*! + \fn QVector4D QMatrix4x4::map(const QVector4D& point) const; + + Maps \a point by multiplying this matrix by \a point. + + \sa mapRect() +*/ + +#endif + +/*! + Maps \a rect by multiplying this matrix by the corners + of \a rect and then forming a new rectangle from the results. + The returned rectangle will be an ordinary 2D rectangle + with sides parallel to the horizontal and vertical axes. + + \sa map() +*/ +QRect QMatrix4x4::mapRect(const QRect& rect) const +{ + if (flagBits == (Translation | Scale) || flagBits == Scale) { + qreal x = rect.x() * m[0][0] + m[3][0]; + qreal y = rect.y() * m[1][1] + m[3][1]; + qreal w = rect.width() * m[0][0]; + qreal h = rect.height() * m[1][1]; + if (w < 0) { + w = -w; + x -= w; + } + if (h < 0) { + h = -h; + y -= h; + } + return QRect(qRound(x), qRound(y), qRound(w), qRound(h)); + } else if (flagBits == Translation) { + return QRect(qRound(rect.x() + m[3][0]), + qRound(rect.y() + m[3][1]), + rect.width(), rect.height()); + } + + QPoint tl = map(rect.topLeft()); + QPoint tr = map(QPoint(rect.x() + rect.width(), rect.y())); + QPoint bl = map(QPoint(rect.x(), rect.y() + rect.height())); + QPoint br = map(QPoint(rect.x() + rect.width(), + rect.y() + rect.height())); + + int xmin = qMin(qMin(tl.x(), tr.x()), qMin(bl.x(), br.x())); + int xmax = qMax(qMax(tl.x(), tr.x()), qMax(bl.x(), br.x())); + int ymin = qMin(qMin(tl.y(), tr.y()), qMin(bl.y(), br.y())); + int ymax = qMax(qMax(tl.y(), tr.y()), qMax(bl.y(), br.y())); + + return QRect(xmin, ymin, xmax - xmin, ymax - ymin); +} + +/*! + Maps \a rect by multiplying this matrix by the corners + of \a rect and then forming a new rectangle from the results. + The returned rectangle will be an ordinary 2D rectangle + with sides parallel to the horizontal and vertical axes. + + \sa map() +*/ +QRectF QMatrix4x4::mapRect(const QRectF& rect) const +{ + if (flagBits == (Translation | Scale) || flagBits == Scale) { + qreal x = rect.x() * m[0][0] + m[3][0]; + qreal y = rect.y() * m[1][1] + m[3][1]; + qreal w = rect.width() * m[0][0]; + qreal h = rect.height() * m[1][1]; + if (w < 0) { + w = -w; + x -= w; + } + if (h < 0) { + h = -h; + y -= h; + } + return QRectF(x, y, w, h); + } else if (flagBits == Translation) { + return rect.translated(m[3][0], m[3][1]); + } + + QPointF tl = map(rect.topLeft()); QPointF tr = map(rect.topRight()); + QPointF bl = map(rect.bottomLeft()); QPointF br = map(rect.bottomRight()); + + qreal xmin = qMin(qMin(tl.x(), tr.x()), qMin(bl.x(), br.x())); + qreal xmax = qMax(qMax(tl.x(), tr.x()), qMax(bl.x(), br.x())); + qreal ymin = qMin(qMin(tl.y(), tr.y()), qMin(bl.y(), br.y())); + qreal ymax = qMax(qMax(tl.y(), tr.y()), qMax(bl.y(), br.y())); + + return QRectF(QPointF(xmin, ymin), QPointF(xmax, ymax)); +} + +/*! + \fn float *QMatrix4x4::data() + + Returns a pointer to the raw data of this matrix. This is intended + for use with raw GL functions. + + \sa constData(), inferSpecialType() +*/ + +/*! + \fn const float *QMatrix4x4::data() const + + Returns a constant pointer to the raw data of this matrix. + This is intended for use with raw GL functions. + + \sa constData() +*/ + +/*! + \fn const float *QMatrix4x4::constData() const + + Returns a constant pointer to the raw data of this matrix. + This is intended for use with raw GL functions. + + \sa data() +*/ + +// Helper routine for inverting orthonormal matrices that consist +// of just rotations and translations. +QMatrix4x4 QMatrix4x4::orthonormalInverse() const +{ + QMatrix4x4 result(1); // The '1' says not to load identity + + result.m[0][0] = m[0][0]; + result.m[1][0] = m[0][1]; + result.m[2][0] = m[0][2]; + + result.m[0][1] = m[1][0]; + result.m[1][1] = m[1][1]; + result.m[2][1] = m[1][2]; + + result.m[0][2] = m[2][0]; + result.m[1][2] = m[2][1]; + result.m[2][2] = m[2][2]; + + result.m[0][3] = 0.0f; + result.m[1][3] = 0.0f; + result.m[2][3] = 0.0f; + + result.m[3][0] = -(result.m[0][0] * m[3][0] + result.m[1][0] * m[3][1] + result.m[2][0] * m[3][2]); + result.m[3][1] = -(result.m[0][1] * m[3][0] + result.m[1][1] * m[3][1] + result.m[2][1] * m[3][2]); + result.m[3][2] = -(result.m[0][2] * m[3][0] + result.m[1][2] * m[3][1] + result.m[2][2] * m[3][2]); + result.m[3][3] = 1.0f; + + return result; +} + +#ifndef QT_NO_VECTOR3D +/*! + Decomposes the current rotation matrix into an \a axis of rotation plus + an \a angle. The result can be used to construct an equivalent rotation + matrix using glRotate(). It is assumed that the homogenous coordinate + is 1.0. The returned vector is guaranteed to be normalized. + + \code + qreal angle; + QVector3D axis; + + matrix.extractAxisAngle(angle, axis); + glRotate(angle, axis[0], axis[1], axis[2]); + \endcode + + \sa rotate() +*/ +void QMatrix4x4::extractAxisRotation(qreal &angle, QVector3D &axis) const +{ + // Orientation is dependent on the upper 3x3 matrix; subtract the + // homogeneous scaling element from the trace of the 4x4 matrix + float tr = m[0][0] + m[1][1] + m[2][2]; + qreal cosa = qreal(0.5f * (tr - 1.0f)); + angle = acos(cosa) * 180.0f / M_PI; + + // Any axis will work if r is zero (means no rotation) + if (qFuzzyIsNull(angle)) { + axis.setX(1.0f); + axis.setY(0.0f); + axis.setZ(0.0f); + return; + } + + if (angle < 180.0f) { + axis.xp = m[1][2] - m[2][1]; + axis.yp = m[2][0] - m[0][2]; + axis.zp = m[0][1] - m[1][0]; + axis.normalize(); + return; + } + + // rads == PI + float tmp; + + // r00 is maximum + if ((m[0][0] >= m[2][2]) && (m[0][0] >= m[1][1])) { + axis.xp = 0.5f * qSqrt(m[0][0] - m[1][1] - m[2][2] + 1.0f); + tmp = 0.5f / axis.x(); + axis.yp = m[1][0] * tmp; + axis.zp = m[2][0] * tmp; + } + + // r11 is maximum + if ((m[1][1] >= m[2][2]) && (m[1][1] >= m[0][0])) { + axis.yp = 0.5f * qSqrt(m[1][1] - m[0][0] - m[2][2] + 1.0f); + tmp = 0.5f / axis.y(); + axis.xp = tmp * m[1][0]; + axis.zp = tmp * m[2][1]; + } + + // r22 is maximum + if ((m[2][2] >= m[1][1]) && (m[2][2] >= m[0][0])) { + axis.zp = 0.5f * qSqrt(m[2][2] - m[0][0] - m[1][1] + 1.0f); + tmp = 0.5f / axis.z(); + axis.xp = m[2][0]*tmp; + axis.yp = m[2][1]*tmp; + } +} + +/*! + If this is an orthonormal transformation matrix (e.g. only rotations and + translations have been applied to the matrix, no scaling, or shearing) + then the world translational component can be obtained by calling this function. + + This is most useful for camera matrices, where the negation of this vector + is effectively the camera world coordinates. +*/ +QVector3D QMatrix4x4::extractTranslation() const +{ + return QVector3D + (m[0][0] * m[3][0] + m[0][1] * m[3][1] + m[0][2] * m[3][2], + m[1][0] * m[3][0] + m[1][1] * m[3][1] + m[1][2] * m[3][2], + m[2][0] * m[3][0] + m[2][1] * m[3][1] + m[2][2] * m[3][2], 1); +} +#endif + +/*! + Infers the special type of this matrix from its current elements. + + Some operations such as translate(), scale(), and rotate() can be + performed more efficiently if the matrix being modified is already + known to be the identity, a previous translate(), a previous + scale(), etc. + + Normally the QMatrix4x4 class keeps track of this special type internally + as operations are performed. However, if the matrix is modified + directly with operator()() or data(), then QMatrix4x4 will lose track of + the special type and will revert to the safest but least efficient + operations thereafter. + + By calling inferSpecialType() after directly modifying the matrix, + the programmer can force QMatrix4x4 to recover the special type if + the elements appear to conform to one of the known optimized types. + + \sa operator()(), data(), translate() +*/ +void QMatrix4x4::inferSpecialType() +{ + // If the last element is not 1, then it can never be special. + if (m[3][3] != 1.0f) { + flagBits = General; + return; + } + + // If the upper three elements m12, m13, and m21 are not all zero, + // or the lower elements below the diagonal are not all zero, then + // the matrix can never be special. + if (m[1][0] != 0.0f || m[2][0] != 0.0f || m[2][1] != 0.0f) { + flagBits = General; + return; + } + if (m[0][1] != 0.0f || m[0][2] != 0.0f || m[0][3] != 0.0f || + m[1][2] != 0.0f || m[1][3] != 0.0f || m[2][3] != 0.0f) { + flagBits = General; + return; + } + + // Determine what we have in the remaining regions of the matrix. + bool identityAlongDiagonal + = (m[0][0] == 1.0f && m[1][1] == 1.0f && m[2][2] == 1.0f); + bool translationPresent + = (m[3][0] != 0.0f || m[3][1] != 0.0f || m[3][2] != 0.0f); + + // Now determine the special matrix type. + if (translationPresent && identityAlongDiagonal) + flagBits = Translation; + else if (translationPresent) + flagBits = (Translation | Scale); + else if (identityAlongDiagonal) + flagBits = Identity; + else + flagBits = Scale; +} + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<(QDebug dbg, const QMatrix4x4 &m) +{ + // Create a string that represents the matrix type. + QByteArray bits; + if ((m.flagBits & QMatrix4x4::Identity) != 0) + bits += "Identity,"; + if ((m.flagBits & QMatrix4x4::General) != 0) + bits += "General,"; + if ((m.flagBits & QMatrix4x4::Translation) != 0) + bits += "Translation,"; + if ((m.flagBits & QMatrix4x4::Scale) != 0) + bits += "Scale,"; + if ((m.flagBits & QMatrix4x4::Rotation) != 0) + bits += "Rotation,"; + if (bits.size() > 0) + bits = bits.left(bits.size() - 1); + + // Output in row-major order because it is more human-readable. + dbg.nospace() << "QMatrix4x4(type:" << bits.constData() << endl + << qSetFieldWidth(10) + << m(0, 0) << m(0, 1) << m(0, 2) << m(0, 3) << endl + << m(1, 0) << m(1, 1) << m(1, 2) << m(1, 3) << endl + << 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(); +} + +#endif + +#endif + +QT_END_NAMESPACE diff --git a/src/gui/math3d/qmatrix4x4.h b/src/gui/math3d/qmatrix4x4.h new file mode 100644 index 0000000..da1bcba --- /dev/null +++ b/src/gui/math3d/qmatrix4x4.h @@ -0,0 +1,998 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMATRIX4X4_H +#define QMATRIX4X4_H + +#include <QtGui/qvector3d.h> +#include <QtGui/qvector4d.h> +#include <QtGui/qquaternion.h> +#include <QtGui/qgenericmatrix.h> +#include <QtCore/qrect.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_MATRIX4X4 + +class QMatrix; +class QTransform; + +class Q_GUI_EXPORT QMatrix4x4 +{ +public: + inline QMatrix4x4() { setIdentity(); } + explicit QMatrix4x4(const qreal *values); + inline QMatrix4x4(qreal m11, qreal m12, qreal m13, qreal m14, + qreal m21, qreal m22, qreal m23, qreal m24, + qreal m31, qreal m32, qreal m33, qreal m34, + qreal m41, qreal m42, qreal m43, qreal m44); +#if !defined(QT_NO_MEMBER_TEMPLATES) || defined(Q_QDOC) + template <int N, int M> + explicit QMatrix4x4(const QGenericMatrix<N, M, qreal, float>& matrix); +#endif + QMatrix4x4(const float *values, int cols, int rows); + QMatrix4x4(const QTransform& transform); + QMatrix4x4(const QMatrix& matrix); + + inline qreal operator()(int row, int column) const; + inline float& operator()(int row, int column); + + inline QVector4D column(int index) const; + inline void setColumn(int index, const QVector4D& value); + + inline QVector4D row(int index) const; + inline void setRow(int index, const QVector4D& value); + + inline bool isIdentity() const; + inline void setIdentity(); + + inline void fill(qreal value); + + qreal determinant() const; + QMatrix4x4 inverted(bool *invertible = 0) const; + QMatrix4x4 transposed() const; + QMatrix3x3 normalMatrix() const; + + inline QMatrix4x4& operator+=(const QMatrix4x4& other); + inline QMatrix4x4& operator-=(const QMatrix4x4& other); + inline QMatrix4x4& operator*=(const QMatrix4x4& other); + inline QMatrix4x4& operator*=(qreal factor); + QMatrix4x4& operator/=(qreal divisor); + inline bool operator==(const QMatrix4x4& other) const; + inline bool operator!=(const QMatrix4x4& other) const; + + friend QMatrix4x4 operator+(const QMatrix4x4& m1, const QMatrix4x4& m2); + friend QMatrix4x4 operator-(const QMatrix4x4& m1, const QMatrix4x4& m2); + friend QMatrix4x4 operator*(const QMatrix4x4& m1, const QMatrix4x4& m2); +#ifndef QT_NO_VECTOR3D + friend QVector3D operator*(const QMatrix4x4& matrix, const QVector3D& vector); + friend QVector3D operator*(const QVector3D& vector, const QMatrix4x4& matrix); +#endif +#ifndef QT_NO_VECTOR4D + friend QVector4D operator*(const QVector4D& vector, const QMatrix4x4& matrix); + friend QVector4D operator*(const QMatrix4x4& matrix, const QVector4D& vector); +#endif + friend QPoint operator*(const QPoint& point, const QMatrix4x4& matrix); + friend QPointF operator*(const QPointF& point, const QMatrix4x4& matrix); + friend QMatrix4x4 operator-(const QMatrix4x4& matrix); + friend QPoint operator*(const QMatrix4x4& matrix, const QPoint& point); + friend QPointF operator*(const QMatrix4x4& matrix, const QPointF& point); + friend QMatrix4x4 operator*(qreal factor, const QMatrix4x4& matrix); + friend QMatrix4x4 operator*(const QMatrix4x4& matrix, qreal factor); + friend Q_GUI_EXPORT QMatrix4x4 operator/(const QMatrix4x4& matrix, qreal divisor); + + friend inline bool qFuzzyCompare(const QMatrix4x4& m1, const QMatrix4x4& m2); + +#ifndef QT_NO_VECTOR3D + QMatrix4x4& scale(const QVector3D& vector); + QMatrix4x4& translate(const QVector3D& vector); + QMatrix4x4& rotate(qreal angle, const QVector3D& vector); +#endif + QMatrix4x4& scale(qreal x, qreal y); + QMatrix4x4& scale(qreal x, qreal y, qreal z); + QMatrix4x4& scale(qreal factor); + QMatrix4x4& translate(qreal x, qreal y); + QMatrix4x4& translate(qreal x, qreal y, qreal z); + QMatrix4x4& rotate(qreal angle, qreal x, qreal y, qreal z = 0.0f); +#ifndef QT_NO_QUATERNION + QMatrix4x4& rotate(const QQuaternion& quaternion); +#endif + +#ifndef QT_NO_VECTOR3D + void extractAxisRotation(qreal &angle, QVector3D &axis) const; + QVector3D extractTranslation() const; +#endif + + QMatrix4x4& ortho(const QRect& rect); + QMatrix4x4& ortho(const QRectF& rect); + QMatrix4x4& ortho(qreal left, qreal right, qreal bottom, qreal top, qreal nearPlane, qreal farPlane); + QMatrix4x4& frustum(qreal left, qreal right, qreal bottom, qreal top, qreal nearPlane, qreal farPlane); + QMatrix4x4& perspective(qreal angle, qreal aspect, qreal nearPlane, qreal farPlane); +#ifndef QT_NO_VECTOR3D + QMatrix4x4& lookAt(const QVector3D& eye, const QVector3D& center, const QVector3D& up); +#endif + QMatrix4x4& flipCoordinates(); + + void toValueArray(qreal *values) const; + + QMatrix toAffine() const; + QTransform toTransform() const; + + QPoint map(const QPoint& point) const; + QPointF map(const QPointF& point) const; +#ifndef QT_NO_VECTOR3D + QVector3D map(const QVector3D& point) const; +#endif +#ifndef QT_NO_VECTOR4D + QVector4D map(const QVector4D& point) const; +#endif + QRect mapRect(const QRect& rect) const; + QRectF mapRect(const QRectF& rect) const; + +#if !defined(QT_NO_MEMBER_TEMPLATES) || defined(Q_QDOC) + template <int N, int M> + QGenericMatrix<N, M, qreal, float> toGenericMatrix() const; +#endif + + inline float *data(); + inline const float *data() const { return m[0]; } + inline const float *constData() const { return m[0]; } + + void inferSpecialType(); + +#ifndef QT_NO_DEBUG_STREAM + friend Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QMatrix4x4 &m); +#endif + +private: + float m[4][4]; // Column-major order to match OpenGL. + int flagBits; // Flag bits from the enum below. + + enum { + Identity = 0x0001, // Identity matrix + General = 0x0002, // General matrix, unknown contents + Translation = 0x0004, // Contains a simple translation + Scale = 0x0008, // Contains a simple scale + Rotation = 0x0010 // Contains a simple rotation + }; + + // Construct without initializing identity matrix. + QMatrix4x4(int) { flagBits = General; } + + QMatrix4x4 orthonormalInverse() const; +}; + +inline QMatrix4x4::QMatrix4x4 + (qreal m11, qreal m12, qreal m13, qreal m14, + qreal m21, qreal m22, qreal m23, qreal m24, + qreal m31, qreal m32, qreal m33, qreal m34, + qreal m41, qreal m42, qreal m43, qreal m44) +{ + m[0][0] = m11; m[0][1] = m21; m[0][2] = m31; m[0][3] = m41; + m[1][0] = m12; m[1][1] = m22; m[1][2] = m32; m[1][3] = m42; + m[2][0] = m13; m[2][1] = m23; m[2][2] = m33; m[2][3] = m43; + m[3][0] = m14; m[3][1] = m24; m[3][2] = m34; m[3][3] = m44; + flagBits = General; +} + +#if !defined(QT_NO_MEMBER_TEMPLATES) + +template <int N, int M> +Q_INLINE_TEMPLATE QMatrix4x4::QMatrix4x4 + (const QGenericMatrix<N, M, qreal, float>& matrix) +{ + const float *values = matrix.constData(); + for (int col = 0; col < 4; ++col) { + for (int row = 0; row < 4; ++row) { + if (col < N && row < M) + m[col][row] = values[col * M + row]; + else if (col == row) + m[col][row] = 1.0f; + else + m[col][row] = 0.0f; + } + } + flagBits = General; +} + +template <int N, int M> +QGenericMatrix<N, M, qreal, float> QMatrix4x4::toGenericMatrix() const +{ + QGenericMatrix<N, M, qreal, float> result; + float *values = result.data(); + for (int col = 0; col < N; ++col) { + for (int row = 0; row < M; ++row) { + if (col < 4 && row < 4) + values[col * M + row] = m[col][row]; + else if (col == row) + values[col * M + row] = 1.0f; + else + values[col * M + row] = 0.0f; + } + } + return result; +} + +#endif + +inline qreal QMatrix4x4::operator()(int row, int column) const +{ + Q_ASSERT(row >= 0 && row < 4 && column >= 0 && column < 4); + return qreal(m[column][row]); +} + +inline float& QMatrix4x4::operator()(int row, int column) +{ + Q_ASSERT(row >= 0 && row < 4 && column >= 0 && column < 4); + flagBits = General; + return m[column][row]; +} + +inline QVector4D QMatrix4x4::column(int index) const +{ + Q_ASSERT(index >= 0 && index < 4); + return QVector4D(m[index][0], m[index][1], m[index][2], m[index][3], 1); +} + +inline void QMatrix4x4::setColumn(int index, const QVector4D& value) +{ + Q_ASSERT(index >= 0 && index < 4); + m[index][0] = value.xp; + m[index][1] = value.yp; + m[index][2] = value.zp; + m[index][3] = value.wp; + flagBits = General; +} + +inline QVector4D QMatrix4x4::row(int index) const +{ + Q_ASSERT(index >= 0 && index < 4); + return QVector4D(m[0][index], m[1][index], m[2][index], m[3][index], 1); +} + +inline void QMatrix4x4::setRow(int index, const QVector4D& value) +{ + Q_ASSERT(index >= 0 && index < 4); + m[0][index] = value.xp; + m[1][index] = value.yp; + m[2][index] = value.zp; + m[3][index] = value.wp; + flagBits = General; +} + +Q_GUI_EXPORT QMatrix4x4 operator/(const QMatrix4x4& matrix, qreal divisor); + +inline bool QMatrix4x4::isIdentity() const +{ + if (flagBits == Identity) + return true; + if (m[0][0] != 1.0f || m[0][1] != 0.0f || m[0][2] != 0.0f) + return false; + if (m[0][3] != 0.0f || m[1][0] != 0.0f || m[1][1] != 1.0f) + return false; + if (m[1][2] != 0.0f || m[1][3] != 0.0f || m[2][0] != 0.0f) + return false; + if (m[2][1] != 0.0f || m[2][2] != 1.0f || m[2][3] != 0.0f) + return false; + if (m[3][0] != 0.0f || m[3][1] != 0.0f || m[3][2] != 0.0f) + return false; + return (m[3][3] == 1.0f); +} + +inline void QMatrix4x4::setIdentity() +{ + m[0][0] = 1.0f; + m[0][1] = 0.0f; + m[0][2] = 0.0f; + m[0][3] = 0.0f; + m[1][0] = 0.0f; + m[1][1] = 1.0f; + m[1][2] = 0.0f; + m[1][3] = 0.0f; + m[2][0] = 0.0f; + m[2][1] = 0.0f; + m[2][2] = 1.0f; + m[2][3] = 0.0f; + m[3][0] = 0.0f; + m[3][1] = 0.0f; + m[3][2] = 0.0f; + m[3][3] = 1.0f; + flagBits = Identity; +} + +inline void QMatrix4x4::fill(qreal value) +{ + m[0][0] = value; + m[0][1] = value; + m[0][2] = value; + m[0][3] = value; + m[1][0] = value; + m[1][1] = value; + m[1][2] = value; + m[1][3] = value; + m[2][0] = value; + m[2][1] = value; + m[2][2] = value; + m[2][3] = value; + m[3][0] = value; + m[3][1] = value; + m[3][2] = value; + m[3][3] = value; + flagBits = General; +} + +inline QMatrix4x4& QMatrix4x4::operator+=(const QMatrix4x4& other) +{ + m[0][0] += other.m[0][0]; + m[0][1] += other.m[0][1]; + m[0][2] += other.m[0][2]; + m[0][3] += other.m[0][3]; + m[1][0] += other.m[1][0]; + m[1][1] += other.m[1][1]; + m[1][2] += other.m[1][2]; + m[1][3] += other.m[1][3]; + m[2][0] += other.m[2][0]; + m[2][1] += other.m[2][1]; + m[2][2] += other.m[2][2]; + m[2][3] += other.m[2][3]; + m[3][0] += other.m[3][0]; + m[3][1] += other.m[3][1]; + m[3][2] += other.m[3][2]; + m[3][3] += other.m[3][3]; + flagBits = General; + return *this; +} + +inline QMatrix4x4& QMatrix4x4::operator-=(const QMatrix4x4& other) +{ + m[0][0] -= other.m[0][0]; + m[0][1] -= other.m[0][1]; + m[0][2] -= other.m[0][2]; + m[0][3] -= other.m[0][3]; + m[1][0] -= other.m[1][0]; + m[1][1] -= other.m[1][1]; + m[1][2] -= other.m[1][2]; + m[1][3] -= other.m[1][3]; + m[2][0] -= other.m[2][0]; + m[2][1] -= other.m[2][1]; + m[2][2] -= other.m[2][2]; + m[2][3] -= other.m[2][3]; + m[3][0] -= other.m[3][0]; + m[3][1] -= other.m[3][1]; + m[3][2] -= other.m[3][2]; + m[3][3] -= other.m[3][3]; + flagBits = General; + return *this; +} + +inline QMatrix4x4& QMatrix4x4::operator*=(const QMatrix4x4& other) +{ + if (flagBits == Identity) { + *this = other; + return *this; + } else if (other.flagBits == Identity) { + return *this; + } else { + *this = *this * other; + return *this; + } +} + +inline QMatrix4x4& QMatrix4x4::operator*=(qreal factor) +{ + m[0][0] *= factor; + m[0][1] *= factor; + m[0][2] *= factor; + m[0][3] *= factor; + m[1][0] *= factor; + m[1][1] *= factor; + m[1][2] *= factor; + m[1][3] *= factor; + m[2][0] *= factor; + m[2][1] *= factor; + m[2][2] *= factor; + m[2][3] *= factor; + m[3][0] *= factor; + m[3][1] *= factor; + m[3][2] *= factor; + m[3][3] *= factor; + flagBits = General; + return *this; +} + +inline bool QMatrix4x4::operator==(const QMatrix4x4& other) const +{ + return m[0][0] == other.m[0][0] && + m[0][1] == other.m[0][1] && + m[0][2] == other.m[0][2] && + m[0][3] == other.m[0][3] && + m[1][0] == other.m[1][0] && + m[1][1] == other.m[1][1] && + m[1][2] == other.m[1][2] && + m[1][3] == other.m[1][3] && + m[2][0] == other.m[2][0] && + m[2][1] == other.m[2][1] && + m[2][2] == other.m[2][2] && + m[2][3] == other.m[2][3] && + m[3][0] == other.m[3][0] && + m[3][1] == other.m[3][1] && + m[3][2] == other.m[3][2] && + m[3][3] == other.m[3][3]; +} + +inline bool QMatrix4x4::operator!=(const QMatrix4x4& other) const +{ + return m[0][0] != other.m[0][0] || + m[0][1] != other.m[0][1] || + m[0][2] != other.m[0][2] || + m[0][3] != other.m[0][3] || + m[1][0] != other.m[1][0] || + m[1][1] != other.m[1][1] || + m[1][2] != other.m[1][2] || + m[1][3] != other.m[1][3] || + m[2][0] != other.m[2][0] || + m[2][1] != other.m[2][1] || + m[2][2] != other.m[2][2] || + m[2][3] != other.m[2][3] || + m[3][0] != other.m[3][0] || + m[3][1] != other.m[3][1] || + m[3][2] != other.m[3][2] || + m[3][3] != other.m[3][3]; +} + +inline QMatrix4x4 operator+(const QMatrix4x4& m1, const QMatrix4x4& m2) +{ + QMatrix4x4 m(1); + m.m[0][0] = m1.m[0][0] + m2.m[0][0]; + m.m[0][1] = m1.m[0][1] + m2.m[0][1]; + m.m[0][2] = m1.m[0][2] + m2.m[0][2]; + m.m[0][3] = m1.m[0][3] + m2.m[0][3]; + m.m[1][0] = m1.m[1][0] + m2.m[1][0]; + m.m[1][1] = m1.m[1][1] + m2.m[1][1]; + m.m[1][2] = m1.m[1][2] + m2.m[1][2]; + m.m[1][3] = m1.m[1][3] + m2.m[1][3]; + m.m[2][0] = m1.m[2][0] + m2.m[2][0]; + m.m[2][1] = m1.m[2][1] + m2.m[2][1]; + m.m[2][2] = m1.m[2][2] + m2.m[2][2]; + m.m[2][3] = m1.m[2][3] + m2.m[2][3]; + m.m[3][0] = m1.m[3][0] + m2.m[3][0]; + m.m[3][1] = m1.m[3][1] + m2.m[3][1]; + m.m[3][2] = m1.m[3][2] + m2.m[3][2]; + m.m[3][3] = m1.m[3][3] + m2.m[3][3]; + return m; +} + +inline QMatrix4x4 operator-(const QMatrix4x4& m1, const QMatrix4x4& m2) +{ + QMatrix4x4 m(1); + m.m[0][0] = m1.m[0][0] - m2.m[0][0]; + m.m[0][1] = m1.m[0][1] - m2.m[0][1]; + m.m[0][2] = m1.m[0][2] - m2.m[0][2]; + m.m[0][3] = m1.m[0][3] - m2.m[0][3]; + m.m[1][0] = m1.m[1][0] - m2.m[1][0]; + m.m[1][1] = m1.m[1][1] - m2.m[1][1]; + m.m[1][2] = m1.m[1][2] - m2.m[1][2]; + m.m[1][3] = m1.m[1][3] - m2.m[1][3]; + m.m[2][0] = m1.m[2][0] - m2.m[2][0]; + m.m[2][1] = m1.m[2][1] - m2.m[2][1]; + m.m[2][2] = m1.m[2][2] - m2.m[2][2]; + m.m[2][3] = m1.m[2][3] - m2.m[2][3]; + m.m[3][0] = m1.m[3][0] - m2.m[3][0]; + m.m[3][1] = m1.m[3][1] - m2.m[3][1]; + m.m[3][2] = m1.m[3][2] - m2.m[3][2]; + m.m[3][3] = m1.m[3][3] - m2.m[3][3]; + return m; +} + +inline QMatrix4x4 operator*(const QMatrix4x4& m1, const QMatrix4x4& m2) +{ + if (m1.flagBits == QMatrix4x4::Identity) + return m2; + else if (m2.flagBits == QMatrix4x4::Identity) + return m1; + + QMatrix4x4 m(1); + m.m[0][0] = m1.m[0][0] * m2.m[0][0] + + m1.m[1][0] * m2.m[0][1] + + m1.m[2][0] * m2.m[0][2] + + m1.m[3][0] * m2.m[0][3]; + m.m[0][1] = m1.m[0][1] * m2.m[0][0] + + m1.m[1][1] * m2.m[0][1] + + m1.m[2][1] * m2.m[0][2] + + m1.m[3][1] * m2.m[0][3]; + m.m[0][2] = m1.m[0][2] * m2.m[0][0] + + m1.m[1][2] * m2.m[0][1] + + m1.m[2][2] * m2.m[0][2] + + m1.m[3][2] * m2.m[0][3]; + m.m[0][3] = m1.m[0][3] * m2.m[0][0] + + m1.m[1][3] * m2.m[0][1] + + m1.m[2][3] * m2.m[0][2] + + m1.m[3][3] * m2.m[0][3]; + m.m[1][0] = m1.m[0][0] * m2.m[1][0] + + m1.m[1][0] * m2.m[1][1] + + m1.m[2][0] * m2.m[1][2] + + m1.m[3][0] * m2.m[1][3]; + m.m[1][1] = m1.m[0][1] * m2.m[1][0] + + m1.m[1][1] * m2.m[1][1] + + m1.m[2][1] * m2.m[1][2] + + m1.m[3][1] * m2.m[1][3]; + m.m[1][2] = m1.m[0][2] * m2.m[1][0] + + m1.m[1][2] * m2.m[1][1] + + m1.m[2][2] * m2.m[1][2] + + m1.m[3][2] * m2.m[1][3]; + m.m[1][3] = m1.m[0][3] * m2.m[1][0] + + m1.m[1][3] * m2.m[1][1] + + m1.m[2][3] * m2.m[1][2] + + m1.m[3][3] * m2.m[1][3]; + m.m[2][0] = m1.m[0][0] * m2.m[2][0] + + m1.m[1][0] * m2.m[2][1] + + m1.m[2][0] * m2.m[2][2] + + m1.m[3][0] * m2.m[2][3]; + m.m[2][1] = m1.m[0][1] * m2.m[2][0] + + m1.m[1][1] * m2.m[2][1] + + m1.m[2][1] * m2.m[2][2] + + m1.m[3][1] * m2.m[2][3]; + m.m[2][2] = m1.m[0][2] * m2.m[2][0] + + m1.m[1][2] * m2.m[2][1] + + m1.m[2][2] * m2.m[2][2] + + m1.m[3][2] * m2.m[2][3]; + m.m[2][3] = m1.m[0][3] * m2.m[2][0] + + m1.m[1][3] * m2.m[2][1] + + m1.m[2][3] * m2.m[2][2] + + m1.m[3][3] * m2.m[2][3]; + m.m[3][0] = m1.m[0][0] * m2.m[3][0] + + m1.m[1][0] * m2.m[3][1] + + m1.m[2][0] * m2.m[3][2] + + m1.m[3][0] * m2.m[3][3]; + m.m[3][1] = m1.m[0][1] * m2.m[3][0] + + m1.m[1][1] * m2.m[3][1] + + m1.m[2][1] * m2.m[3][2] + + m1.m[3][1] * m2.m[3][3]; + m.m[3][2] = m1.m[0][2] * m2.m[3][0] + + m1.m[1][2] * m2.m[3][1] + + m1.m[2][2] * m2.m[3][2] + + m1.m[3][2] * m2.m[3][3]; + m.m[3][3] = m1.m[0][3] * m2.m[3][0] + + m1.m[1][3] * m2.m[3][1] + + m1.m[2][3] * m2.m[3][2] + + m1.m[3][3] * m2.m[3][3]; + return m; +} + +#ifndef QT_NO_VECTOR3D + +inline QVector3D operator*(const QVector3D& vector, const QMatrix4x4& matrix) +{ + float x, y, z, w; + x = vector.xp * matrix.m[0][0] + + vector.yp * matrix.m[0][1] + + vector.zp * matrix.m[0][2] + + matrix.m[0][3]; + y = vector.xp * matrix.m[1][0] + + vector.yp * matrix.m[1][1] + + vector.zp * matrix.m[1][2] + + matrix.m[1][3]; + z = vector.xp * matrix.m[2][0] + + vector.yp * matrix.m[2][1] + + vector.zp * matrix.m[2][2] + + matrix.m[2][3]; + w = vector.xp * matrix.m[3][0] + + vector.yp * matrix.m[3][1] + + vector.zp * matrix.m[3][2] + + matrix.m[3][3]; + if (w == 1.0f) + return QVector3D(x, y, z, 1); + else + return QVector3D(x / w, y / w, z / w, 1); +} + +inline QVector3D operator*(const QMatrix4x4& matrix, const QVector3D& vector) +{ + float x, y, z, w; + if (matrix.flagBits == QMatrix4x4::Identity) { + return vector; + } else if (matrix.flagBits == QMatrix4x4::Translation) { + return QVector3D(vector.xp + matrix.m[3][0], + vector.yp + matrix.m[3][1], + vector.zp + matrix.m[3][2], 1); + } else if (matrix.flagBits == + (QMatrix4x4::Translation | QMatrix4x4::Scale)) { + return QVector3D(vector.xp * matrix.m[0][0] + matrix.m[3][0], + vector.yp * matrix.m[1][1] + matrix.m[3][1], + vector.zp * matrix.m[2][2] + matrix.m[3][2], 1); + } else if (matrix.flagBits == QMatrix4x4::Scale) { + return QVector3D(vector.xp * matrix.m[0][0], + vector.yp * matrix.m[1][1], + vector.zp * matrix.m[2][2], 1); + } else { + x = vector.xp * matrix.m[0][0] + + vector.yp * matrix.m[1][0] + + vector.zp * matrix.m[2][0] + + matrix.m[3][0]; + y = vector.xp * matrix.m[0][1] + + vector.yp * matrix.m[1][1] + + vector.zp * matrix.m[2][1] + + matrix.m[3][1]; + z = vector.xp * matrix.m[0][2] + + vector.yp * matrix.m[1][2] + + vector.zp * matrix.m[2][2] + + matrix.m[3][2]; + w = vector.xp * matrix.m[0][3] + + vector.yp * matrix.m[1][3] + + vector.zp * matrix.m[2][3] + + matrix.m[3][3]; + if (w == 1.0f) + return QVector3D(x, y, z, 1); + else + return QVector3D(x / w, y / w, z / w, 1); + } +} + +#endif + +#ifndef QT_NO_VECTOR4D + +inline QVector4D operator*(const QVector4D& vector, const QMatrix4x4& matrix) +{ + float x, y, z, w; + x = vector.xp * matrix.m[0][0] + + vector.yp * matrix.m[0][1] + + vector.zp * matrix.m[0][2] + + vector.wp * matrix.m[0][3]; + y = vector.xp * matrix.m[1][0] + + vector.yp * matrix.m[1][1] + + vector.zp * matrix.m[1][2] + + vector.wp * matrix.m[1][3]; + z = vector.xp * matrix.m[2][0] + + vector.yp * matrix.m[2][1] + + vector.zp * matrix.m[2][2] + + vector.wp * matrix.m[2][3]; + w = vector.xp * matrix.m[3][0] + + vector.yp * matrix.m[3][1] + + vector.zp * matrix.m[3][2] + + vector.wp * matrix.m[3][3]; + return QVector4D(x, y, z, w, 1); +} + +inline QVector4D operator*(const QMatrix4x4& matrix, const QVector4D& vector) +{ + float x, y, z, w; + x = vector.xp * matrix.m[0][0] + + vector.yp * matrix.m[1][0] + + vector.zp * matrix.m[2][0] + + vector.wp * matrix.m[3][0]; + y = vector.xp * matrix.m[0][1] + + vector.yp * matrix.m[1][1] + + vector.zp * matrix.m[2][1] + + vector.wp * matrix.m[3][1]; + z = vector.xp * matrix.m[0][2] + + vector.yp * matrix.m[1][2] + + vector.zp * matrix.m[2][2] + + vector.wp * matrix.m[3][2]; + w = vector.xp * matrix.m[0][3] + + vector.yp * matrix.m[1][3] + + vector.zp * matrix.m[2][3] + + vector.wp * matrix.m[3][3]; + return QVector4D(x, y, z, w, 1); +} + +#endif + +inline QPoint operator*(const QPoint& point, const QMatrix4x4& matrix) +{ + float xin, yin; + float x, y, w; + xin = point.x(); + yin = point.y(); + x = xin * matrix.m[0][0] + + yin * matrix.m[0][1] + + matrix.m[0][3]; + y = xin * matrix.m[1][0] + + yin * matrix.m[1][1] + + matrix.m[1][3]; + w = xin * matrix.m[3][0] + + yin * matrix.m[3][1] + + matrix.m[3][3]; + if (w == 1.0f) + return QPoint(qRound(x), qRound(y)); + else + return QPoint(qRound(x / w), qRound(y / w)); +} + +inline QPointF operator*(const QPointF& point, const QMatrix4x4& matrix) +{ + float xin, yin; + float x, y, w; + xin = point.x(); + yin = point.y(); + x = xin * matrix.m[0][0] + + yin * matrix.m[0][1] + + matrix.m[0][3]; + y = xin * matrix.m[1][0] + + yin * matrix.m[1][1] + + matrix.m[1][3]; + w = xin * matrix.m[3][0] + + yin * matrix.m[3][1] + + matrix.m[3][3]; + if (w == 1.0f) { + return QPointF(qreal(x), qreal(y)); + } else { + return QPointF(qreal(x / w), qreal(y / w)); + } +} + +inline QPoint operator*(const QMatrix4x4& matrix, const QPoint& point) +{ + float xin, yin; + float x, y, w; + xin = point.x(); + yin = point.y(); + if (matrix.flagBits == QMatrix4x4::Identity) { + return point; + } else if (matrix.flagBits == QMatrix4x4::Translation) { + return QPoint(qRound(xin + matrix.m[3][0]), + qRound(yin + matrix.m[3][1])); + } else if (matrix.flagBits == + (QMatrix4x4::Translation | QMatrix4x4::Scale)) { + return QPoint(qRound(xin * matrix.m[0][0] + matrix.m[3][0]), + qRound(yin * matrix.m[1][1] + matrix.m[3][1])); + } else if (matrix.flagBits == QMatrix4x4::Scale) { + return QPoint(qRound(xin * matrix.m[0][0]), + qRound(yin * matrix.m[1][1])); + } else { + x = xin * matrix.m[0][0] + + yin * matrix.m[1][0] + + matrix.m[3][0]; + y = xin * matrix.m[0][1] + + yin * matrix.m[1][1] + + matrix.m[3][1]; + w = xin * matrix.m[0][3] + + yin * matrix.m[1][3] + + matrix.m[3][3]; + if (w == 1.0f) + return QPoint(qRound(x), qRound(y)); + else + return QPoint(qRound(x / w), qRound(y / w)); + } +} + +inline QPointF operator*(const QMatrix4x4& matrix, const QPointF& point) +{ + float xin, yin; + float x, y, w; + xin = point.x(); + yin = point.y(); + if (matrix.flagBits == QMatrix4x4::Identity) { + return point; + } else if (matrix.flagBits == QMatrix4x4::Translation) { + return QPointF(xin + matrix.m[3][0], + yin + matrix.m[3][1]); + } else if (matrix.flagBits == + (QMatrix4x4::Translation | QMatrix4x4::Scale)) { + return QPointF(xin * matrix.m[0][0] + matrix.m[3][0], + yin * matrix.m[1][1] + matrix.m[3][1]); + } else if (matrix.flagBits == QMatrix4x4::Scale) { + return QPointF(xin * matrix.m[0][0], + yin * matrix.m[1][1]); + } else { + x = xin * matrix.m[0][0] + + yin * matrix.m[1][0] + + matrix.m[3][0]; + y = xin * matrix.m[0][1] + + yin * matrix.m[1][1] + + matrix.m[3][1]; + w = xin * matrix.m[0][3] + + yin * matrix.m[1][3] + + matrix.m[3][3]; + if (w == 1.0f) { + return QPointF(qreal(x), qreal(y)); + } else { + return QPointF(qreal(x / w), qreal(y / w)); + } + } +} + +inline QMatrix4x4 operator-(const QMatrix4x4& matrix) +{ + QMatrix4x4 m(1); + m.m[0][0] = -matrix.m[0][0]; + m.m[0][1] = -matrix.m[0][1]; + m.m[0][2] = -matrix.m[0][2]; + m.m[0][3] = -matrix.m[0][3]; + m.m[1][0] = -matrix.m[1][0]; + m.m[1][1] = -matrix.m[1][1]; + m.m[1][2] = -matrix.m[1][2]; + m.m[1][3] = -matrix.m[1][3]; + m.m[2][0] = -matrix.m[2][0]; + m.m[2][1] = -matrix.m[2][1]; + m.m[2][2] = -matrix.m[2][2]; + m.m[2][3] = -matrix.m[2][3]; + m.m[3][0] = -matrix.m[3][0]; + m.m[3][1] = -matrix.m[3][1]; + m.m[3][2] = -matrix.m[3][2]; + m.m[3][3] = -matrix.m[3][3]; + return m; +} + +inline QMatrix4x4 operator*(qreal factor, const QMatrix4x4& matrix) +{ + QMatrix4x4 m(1); + m.m[0][0] = matrix.m[0][0] * factor; + m.m[0][1] = matrix.m[0][1] * factor; + m.m[0][2] = matrix.m[0][2] * factor; + m.m[0][3] = matrix.m[0][3] * factor; + m.m[1][0] = matrix.m[1][0] * factor; + m.m[1][1] = matrix.m[1][1] * factor; + m.m[1][2] = matrix.m[1][2] * factor; + m.m[1][3] = matrix.m[1][3] * factor; + m.m[2][0] = matrix.m[2][0] * factor; + m.m[2][1] = matrix.m[2][1] * factor; + m.m[2][2] = matrix.m[2][2] * factor; + m.m[2][3] = matrix.m[2][3] * factor; + m.m[3][0] = matrix.m[3][0] * factor; + m.m[3][1] = matrix.m[3][1] * factor; + m.m[3][2] = matrix.m[3][2] * factor; + m.m[3][3] = matrix.m[3][3] * factor; + return m; +} + +inline QMatrix4x4 operator*(const QMatrix4x4& matrix, qreal factor) +{ + QMatrix4x4 m(1); + m.m[0][0] = matrix.m[0][0] * factor; + m.m[0][1] = matrix.m[0][1] * factor; + m.m[0][2] = matrix.m[0][2] * factor; + m.m[0][3] = matrix.m[0][3] * factor; + m.m[1][0] = matrix.m[1][0] * factor; + m.m[1][1] = matrix.m[1][1] * factor; + m.m[1][2] = matrix.m[1][2] * factor; + m.m[1][3] = matrix.m[1][3] * factor; + m.m[2][0] = matrix.m[2][0] * factor; + m.m[2][1] = matrix.m[2][1] * factor; + m.m[2][2] = matrix.m[2][2] * factor; + m.m[2][3] = matrix.m[2][3] * factor; + m.m[3][0] = matrix.m[3][0] * factor; + m.m[3][1] = matrix.m[3][1] * factor; + m.m[3][2] = matrix.m[3][2] * factor; + m.m[3][3] = matrix.m[3][3] * factor; + return m; +} + +inline bool qFuzzyCompare(const QMatrix4x4& m1, const QMatrix4x4& m2) +{ + return qFuzzyCompare(m1.m[0][0], m2.m[0][0]) && + qFuzzyCompare(m1.m[0][1], m2.m[0][1]) && + qFuzzyCompare(m1.m[0][2], m2.m[0][2]) && + qFuzzyCompare(m1.m[0][3], m2.m[0][3]) && + qFuzzyCompare(m1.m[1][0], m2.m[1][0]) && + qFuzzyCompare(m1.m[1][1], m2.m[1][1]) && + qFuzzyCompare(m1.m[1][2], m2.m[1][2]) && + qFuzzyCompare(m1.m[1][3], m2.m[1][3]) && + qFuzzyCompare(m1.m[2][0], m2.m[2][0]) && + qFuzzyCompare(m1.m[2][1], m2.m[2][1]) && + qFuzzyCompare(m1.m[2][2], m2.m[2][2]) && + qFuzzyCompare(m1.m[2][3], m2.m[2][3]) && + qFuzzyCompare(m1.m[3][0], m2.m[3][0]) && + qFuzzyCompare(m1.m[3][1], m2.m[3][1]) && + qFuzzyCompare(m1.m[3][2], m2.m[3][2]) && + qFuzzyCompare(m1.m[3][3], m2.m[3][3]); +} + +inline QPoint QMatrix4x4::map(const QPoint& point) const +{ + return *this * point; +} + +inline QPointF QMatrix4x4::map(const QPointF& point) const +{ + return *this * point; +} + +#ifndef QT_NO_VECTOR3D + +inline QVector3D QMatrix4x4::map(const QVector3D& point) const +{ + return *this * point; +} + +#endif + +#ifndef QT_NO_VECTOR4D + +inline QVector4D QMatrix4x4::map(const QVector4D& point) const +{ + return *this * point; +} + +#endif + +inline float *QMatrix4x4::data() +{ + // We have to assume that the caller will modify the matrix elements, + // so we flip it over to "General" mode. + flagBits = General; + return m[0]; +} + +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QMatrix4x4 &m); +#endif + +template <int N, int M> +QMatrix4x4 qGenericMatrixToMatrix4x4(const QGenericMatrix<N, M, qreal, float>& matrix) +{ + return QMatrix4x4(matrix.constData(), N, M); +} + +template <int N, int M> +QGenericMatrix<N, M, qreal, float> qGenericMatrixFromMatrix4x4(const QMatrix4x4& matrix) +{ + QGenericMatrix<N, M, qreal, float> result; + const float *m = matrix.constData(); + float *values = result.data(); + for (int col = 0; col < N; ++col) { + for (int row = 0; row < M; ++row) { + if (col < 4 && row < 4) + values[col * M + row] = m[col * 4 + row]; + else if (col == row) + values[col * M + row] = 1.0f; + else + values[col * M + row] = 0.0f; + } + } + return result; +} + +#endif + +QT_END_NAMESPACE + +#ifndef QT_NO_MATRIX4X4 +Q_DECLARE_METATYPE(QMatrix4x4) +#endif + +QT_END_HEADER + +#endif diff --git a/src/gui/math3d/qquaternion.cpp b/src/gui/math3d/qquaternion.cpp new file mode 100644 index 0000000..03f626a --- /dev/null +++ b/src/gui/math3d/qquaternion.cpp @@ -0,0 +1,584 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquaternion.h" +#include <QtCore/qmath.h> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_QUATERNION + +/*! + \class QQuaternion + \brief The QQuaternion class represents a quaternion consisting of a vector and scalar. + \since 4.6 + + Quaternions are used to represent rotations in 3D space, and + consist of a 3D rotation axis specified by the x, y, and z + coordinates, and a scalar representing the rotation angle. + + The components of a quaternion are stored internally using the most + efficient representation for the GL rendering engine, which will be + either floating-point or fixed-point. +*/ + +/*! + \fn QQuaternion::QQuaternion() + + Constructs an identity quaternion, i.e. with coordinates (1, 0, 0, 0). +*/ + +/*! + \fn QQuaternion::QQuaternion(qreal scalar, qreal xpos, qreal ypos, qreal zpos) + + Constructs a quaternion with the vector (\a xpos, \a ypos, \a zpos) + and \a scalar. +*/ + +#ifndef QT_NO_VECTOR3D + +/*! + \fn QQuaternion::QQuaternion(qreal scalar, const QVector3D& vector) + + Constructs a quaternion vector from the specified \a vector and + \a scalar. + + \sa vector(), scalar() +*/ + +/*! + \fn QVector3D QQuaternion::vector() const + + Returns the vector component of this quaternion. + + \sa setVector(), scalar() +*/ + +/*! + \fn void QQuaternion::setVector(const QVector3D& vector) + + Sets the vector component of this quaternion to \a vector. + + \sa vector(), setScalar() +*/ + +#endif + +/*! + \fn void QQuaternion::setVector(qreal x, qreal y, qreal z) + + Sets the vector component of this quaternion to (\a x, \a y, \a z). + + \sa vector(), setScalar() +*/ + +#ifndef QT_NO_VECTOR4D + +/*! + \fn QQuaternion::QQuaternion(const QVector4D& vector) + + Constructs a quaternion from the components of \a vector. +*/ + +/*! + \fn QVector4D QQuaternion::toVector4D() const + + Returns this quaternion as a 4D vector. +*/ + +#endif + +/*! + \fn bool QQuaternion::isNull() const + + Returns true if the x, y, z, and scalar components of this + quaternion are set to 0.0; otherwise returns false. +*/ + +/*! + \fn bool QQuaternion::isIdentity() const + + Returns true if the x, y, and z components of this + quaternion are set to 0.0, and the scalar component is set + to 1.0; otherwise returns false. +*/ + +/*! + \fn qreal QQuaternion::x() const + + Returns the x coordinate of this quaternion's vector. + + \sa setX(), y(), z(), scalar() +*/ + +/*! + \fn qreal QQuaternion::y() const + + Returns the y coordinate of this quaternion's vector. + + \sa setY(), x(), z(), scalar() +*/ + +/*! + \fn qreal QQuaternion::z() const + + Returns the z coordinate of this quaternion's vector. + + \sa setZ(), x(), y(), scalar() +*/ + +/*! + \fn qreal QQuaternion::scalar() const + + Returns the scalar component of this quaternion. + + \sa setScalar(), x(), y(), z() +*/ + +/*! + \fn void QQuaternion::setX(qreal x) + + Sets the x coordinate of this quaternion's vector to the given + \a x coordinate. + + \sa x(), setY(), setZ(), setScalar() +*/ + +/*! + \fn void QQuaternion::setY(qreal y) + + Sets the y coordinate of this quaternion's vector to the given + \a y coordinate. + + \sa y(), setX(), setZ(), setScalar() +*/ + +/*! + \fn void QQuaternion::setZ(qreal z) + + Sets the z coordinate of this quaternion's vector to the given + \a z coordinate. + + \sa z(), setX(), setY(), setScalar() +*/ + +/*! + \fn void QQuaternion::setScalar(qreal scalar) + + Sets the scalar component of this quaternion to \a scalar. + + \sa scalar(), setX(), setY(), setZ() +*/ + +/*! + Returns the length of the quaternion. This is also called the "norm". + + \sa lengthSquared(), normalized() +*/ +qreal QQuaternion::length() const +{ + return qSqrt(xp * xp + yp * yp + zp * zp + wp * wp); +} + +/*! + Returns the squared length of the quaternion. + + \sa length() +*/ +qreal QQuaternion::lengthSquared() const +{ + return xp * xp + yp * yp + zp * zp + wp * wp; +} + +/*! + Returns the normalized unit form of this quaternion. + + If this quaternion is null, then a null quaternion is returned. + If the length of the quaternion is very close to 1, then the quaternion + will be returned as-is. Otherwise the normalized form of the + quaternion of length 1 will be returned. + + \sa length(), normalize() +*/ +QQuaternion QQuaternion::normalized() const +{ + qreal len = lengthSquared(); + if (qFuzzyIsNull(len - 1.0f)) + return *this; + else if (!qFuzzyIsNull(len)) + return *this / qSqrt(len); + else + return QQuaternion(0.0f, 0.0f, 0.0f, 0.0f); +} + +/*! + Normalizes the currect quaternion in place. Nothing happens if this + is a null quaternion or the length of the quaternion is very close to 1. + + \sa length(), normalized() +*/ +void QQuaternion::normalize() +{ + qreal len = lengthSquared(); + if (qFuzzyIsNull(len - 1.0f) || qFuzzyIsNull(len)) + return; + + len = qSqrt(len); + + xp /= len; + yp /= len; + zp /= len; + wp /= len; +} + +/*! + \fn QQuaternion QQuaternion::conjugate() const + + Returns the conjugate of this quaternion, which is + (-x, -y, -z, scalar). +*/ + +/*! + Rotates \a vector with this quaternion to produce a new vector + in 3D space. The following code: + + \code + QVector3D result = q.rotateVector(vector); + \endcode + + is equivalent to the following: + + \code + QVector3D result = (q * QQuaternion(0, vector) * q.conjugate()).vector(); + \endcode +*/ +QVector3D QQuaternion::rotateVector(const QVector3D& vector) const +{ + return (*this * QQuaternion(0, vector) * conjugate()).vector(); +} + +/*! + \fn QQuaternion &QQuaternion::operator+=(const QQuaternion &quaternion) + + Adds the given \a quaternion to this quaternion and returns a reference to + this quaternion. + + \sa operator-=() +*/ + +/*! + \fn QQuaternion &QQuaternion::operator-=(const QQuaternion &quaternion) + + Subtracts the given \a quaternion from this quaternion and returns a + reference to this quaternion. + + \sa operator+=() +*/ + +/*! + \fn QQuaternion &QQuaternion::operator*=(qreal factor) + + Multiplies this quaternion's components by the given \a factor, and + returns a reference to this quaternion. + + \sa operator/=() +*/ + +/*! + \fn QQuaternion &QQuaternion::operator*=(const QQuaternion &quaternion) + + Multiplies this quaternion by \a quaternion and returns a reference + to this quaternion. +*/ + +/*! + \fn QQuaternion &QQuaternion::operator/=(qreal divisor) + + Divides this quaternion's components by the given \a divisor, and + returns a reference to this quaternion. + + \sa operator*=() +*/ + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#ifndef QT_NO_VECTOR3D + +/*! + Creates a normalized quaternion that corresponds to rotating through + \a angle degrees about the specified 3D \a axis. +*/ +QQuaternion QQuaternion::fromAxisAndAngle(const QVector3D& axis, qreal angle) +{ + // Algorithm from: + // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q56 + // We normalize the result just in case the values are close + // to zero, as suggested in the above FAQ. + qreal a = (angle / 2.0f) * M_PI / 180.0f; + qreal s = qSin(a); + qreal c = qCos(a); + QVector3D ax = axis.normalized(); + return QQuaternion(c, ax.xp * s, ax.yp * s, ax.zp * s, 1).normalized(); +} + +#endif + +/*! + Creates a normalized quaternion that corresponds to rotating through + \a angle degrees about the 3D axis (\a x, \a y, \a z). +*/ +QQuaternion QQuaternion::fromAxisAndAngle + (qreal x, qreal y, qreal z, qreal angle) +{ + float xp = x; + float yp = y; + float zp = z; + qreal length = qSqrt(xp * xp + yp * yp + zp * zp); + if (!qIsNull(length)) { + xp /= length; + yp /= length; + zp /= length; + } + qreal a = (angle / 2.0f) * M_PI / 180.0f; + qreal s = qSin(a); + qreal c = qCos(a); + return QQuaternion(c, xp * s, yp * s, zp * s, 1).normalized(); +} + +/*! + \fn bool operator==(const QQuaternion &q1, const QQuaternion &q2) + \relates QQuaternion + + Returns true if \a q1 is equal to \a q2; otherwise returns false. + This operator uses an exact floating-point comparison. +*/ + +/*! + \fn bool operator!=(const QQuaternion &q1, const QQuaternion &q2) + \relates QQuaternion + + Returns true if \a q1 is not equal to \a q2; otherwise returns false. + This operator uses an exact floating-point comparison. +*/ + +/*! + \fn const QQuaternion operator+(const QQuaternion &q1, const QQuaternion &q2) + \relates QQuaternion + + Returns a QQuaternion object that is the sum of the given quaternions, + \a q1 and \a q2; each component is added separately. + + \sa QQuaternion::operator+=() +*/ + +/*! + \fn const QQuaternion operator-(const QQuaternion &q1, const QQuaternion &q2) + \relates QQuaternion + + Returns a QQuaternion object that is formed by subtracting + \a q2 from \a q1; each component is subtracted separately. + + \sa QQuaternion::operator-=() +*/ + +/*! + \fn const QQuaternion operator*(qreal factor, const QQuaternion &quaternion) + \relates QQuaternion + + Returns a copy of the given \a quaternion, multiplied by the + given \a factor. + + \sa QQuaternion::operator*=() +*/ + +/*! + \fn const QQuaternion operator*(const QQuaternion &quaternion, qreal factor) + \relates QQuaternion + + Returns a copy of the given \a quaternion, multiplied by the + given \a factor. + + \sa QQuaternion::operator*=() +*/ + +/*! + \fn const QQuaternion operator*(const QQuaternion &q1, const QQuaternion& q2) + \relates QQuaternion + + Multiplies \a q1 and \a q2 using quaternion multiplication. + The result corresponds to applying both of the rotations specified + by \a q1 and \a q2. + + \sa QQuaternion::operator*=() +*/ + +/*! + \fn const QQuaternion operator-(const QQuaternion &quaternion) + \relates QQuaternion + \overload + + Returns a QQuaternion object that is formed by changing the sign of + all three components of the given \a quaternion. + + Equivalent to \c {QQuaternion(0,0,0,0) - quaternion}. +*/ + +/*! + \fn const QQuaternion operator/(const QQuaternion &quaternion, qreal divisor) + \relates QQuaternion + + Returns the QQuaternion object formed by dividing all components of + the given \a quaternion by the given \a divisor. + + \sa QQuaternion::operator/=() +*/ + +/*! + \fn bool qFuzzyCompare(const QQuaternion& q1, const QQuaternion& q2) + \relates QQuaternion + + Returns true if \a q1 and \a q2 are equal, allowing for a small + fuzziness factor for floating-point comparisons; false otherwise. +*/ + +/*! + Interpolates along the shortest spherical path between the + rotational positions \a q1 and \a q2. The value \a t should + be between 0 and 1, indicating the spherical distance to travel + between \a q1 and \a q2. + + If \a t is less than or equal to 0, then \a q1 will be returned. + If \a t is greater than or equal to 1, then \a q2 will be returned. + + \sa nlerp() +*/ +QQuaternion QQuaternion::slerp + (const QQuaternion& q1, const QQuaternion& q2, qreal t) +{ + // Handle the easy cases first. + if (t <= 0.0f) + return q1; + else if (t >= 1.0f) + return q2; + + // Determine the angle between the two quaternions. + QQuaternion q2b; + qreal 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; + dot = -dot; + } + + // Get the scale factors. If they are too small, + // then revert to simple linear interpolation. + qreal factor1 = 1.0f - t; + qreal factor2 = t; + if ((1.0f - dot) > 0.0000001) { + qreal angle = qreal(qAcos(dot)); + qreal sinOfAngle = qreal(qSin(angle)); + if (sinOfAngle > 0.0000001) { + factor1 = qreal(qSin((1.0f - t) * angle)) / sinOfAngle; + factor2 = qreal(qSin(t * angle)) / sinOfAngle; + } + } + + // Construct the result quaternion. + return q1 * factor1 + q2b * factor2; +} + +/*! + Interpolates along the shortest linear path between the rotational + positions \a q1 and \a q2. The value \a t should be between 0 and 1, + indicating the distance to travel between \a q1 and \a q2. + The result will be normalized(). + + If \a t is less than or equal to 0, then \a q1 will be returned. + If \a t is greater than or equal to 1, then \a q2 will be returned. + + The nlerp() function is typically faster than slerp() and will + give approximate results to spherical interpolation that are + good enough for some applications. + + \sa slerp() +*/ +QQuaternion QQuaternion::nlerp + (const QQuaternion& q1, const QQuaternion& q2, qreal t) +{ + // Handle the easy cases first. + if (t <= 0.0f) + return q1; + else if (t >= 1.0f) + return q2; + + // Determine the angle between the two quaternions. + QQuaternion q2b; + qreal 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; + + // Perform the linear interpolation. + return (q1 * (1.0f - t) + q2b * t).normalized(); +} + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<(QDebug dbg, const QQuaternion &q) +{ + dbg.nospace() << "QQuaternion(scalar:" << q.scalar() + << ", vector:(" << q.x() << ", " + << q.y() << ", " << q.z() << "))"; + return dbg.space(); +} + +#endif + +#endif + +QT_END_NAMESPACE diff --git a/src/gui/math3d/qquaternion.h b/src/gui/math3d/qquaternion.h new file mode 100644 index 0000000..b17cf73 --- /dev/null +++ b/src/gui/math3d/qquaternion.h @@ -0,0 +1,337 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUATERNION_H +#define QQUATERNION_H + +#include <QtGui/qvector3d.h> +#include <QtGui/qvector4d.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#ifndef QT_NO_QUATERNION + +class QMatrix4x4; + +class Q_GUI_EXPORT QQuaternion +{ +public: + QQuaternion(); + QQuaternion(qreal scalar, qreal xpos, qreal ypos, qreal zpos); +#ifndef QT_NO_VECTOR3D + QQuaternion(qreal scalar, const QVector3D& vector); +#endif +#ifndef QT_NO_VECTOR4D + explicit QQuaternion(const QVector4D& vector); +#endif + + bool isNull() const; + bool isIdentity() const; + +#ifndef QT_NO_VECTOR3D + QVector3D vector() const; + void setVector(const QVector3D& vector); +#endif + void setVector(qreal x, qreal y, qreal z); + + qreal x() const; + qreal y() const; + qreal z() const; + qreal scalar() const; + + void setX(qreal x); + void setY(qreal y); + void setZ(qreal z); + void setScalar(qreal scalar); + + qreal length() const; + qreal lengthSquared() const; + + QQuaternion normalized() const; + void normalize(); + + QQuaternion conjugate() const; + + QVector3D rotateVector(const QVector3D& vector) const; + + QQuaternion &operator+=(const QQuaternion &quaternion); + QQuaternion &operator-=(const QQuaternion &quaternion); + QQuaternion &operator*=(qreal factor); + QQuaternion &operator*=(const QQuaternion &quaternion); + QQuaternion &operator/=(qreal divisor); + + friend inline bool operator==(const QQuaternion &q1, const QQuaternion &q2); + friend inline bool operator!=(const QQuaternion &q1, const QQuaternion &q2); + friend inline const QQuaternion operator+(const QQuaternion &q1, const QQuaternion &q2); + friend inline const QQuaternion operator-(const QQuaternion &q1, const QQuaternion &q2); + friend inline const QQuaternion operator*(qreal factor, const QQuaternion &quaternion); + friend inline const QQuaternion operator*(const QQuaternion &quaternion, qreal factor); + friend inline const QQuaternion operator*(const QQuaternion &q1, const QQuaternion& q2); + friend inline const QQuaternion operator-(const QQuaternion &quaternion); + friend inline const QQuaternion operator/(const QQuaternion &quaternion, qreal divisor); + + friend inline bool qFuzzyCompare(const QQuaternion& q1, const QQuaternion& q2); + +#ifndef QT_NO_VECTOR4D + QVector4D toVector4D() const; +#endif + +#ifndef QT_NO_VECTOR3D + static QQuaternion fromAxisAndAngle(const QVector3D& axis, qreal angle); +#endif + static QQuaternion fromAxisAndAngle + (qreal x, qreal y, qreal z, qreal angle); + + static QQuaternion slerp + (const QQuaternion& q1, const QQuaternion& q2, qreal t); + static QQuaternion nlerp + (const QQuaternion& q1, const QQuaternion& q2, qreal t); + +private: + float wp, xp, yp, zp; + + friend class QMatrix4x4; + + QQuaternion(float scalar, float xpos, float ypos, float zpos, int dummy); +}; + +inline QQuaternion::QQuaternion() : wp(1.0f), xp(0.0f), yp(0.0f), zp(0.0f) {} + +inline QQuaternion::QQuaternion(qreal scalar, qreal xpos, qreal ypos, qreal zpos) : wp(scalar), xp(xpos), yp(ypos), zp(zpos) {} + + +inline QQuaternion::QQuaternion(float scalar, float xpos, float ypos, float zpos, int) : wp(scalar), xp(xpos), yp(ypos), zp(zpos) {} + +inline bool QQuaternion::isNull() const +{ + return qIsNull(xp) && qIsNull(yp) && qIsNull(zp) && qIsNull(wp); +} + +inline bool QQuaternion::isIdentity() const +{ + return qIsNull(xp) && qIsNull(yp) && qIsNull(zp) && wp == 1.0f; +} + +inline qreal QQuaternion::x() const { return qreal(xp); } +inline qreal QQuaternion::y() const { return qreal(yp); } +inline qreal QQuaternion::z() const { return qreal(zp); } +inline qreal QQuaternion::scalar() const { return qreal(wp); } + +inline void QQuaternion::setX(qreal x) { xp = x; } +inline void QQuaternion::setY(qreal y) { yp = y; } +inline void QQuaternion::setZ(qreal z) { zp = z; } +inline void QQuaternion::setScalar(qreal scalar) { wp = scalar; } + +inline QQuaternion QQuaternion::conjugate() const +{ + return QQuaternion(wp, -xp, -yp, -zp, 1); +} + +inline QQuaternion &QQuaternion::operator+=(const QQuaternion &quaternion) +{ + xp += quaternion.xp; + yp += quaternion.yp; + zp += quaternion.zp; + wp += quaternion.wp; + return *this; +} + +inline QQuaternion &QQuaternion::operator-=(const QQuaternion &quaternion) +{ + xp -= quaternion.xp; + yp -= quaternion.yp; + zp -= quaternion.zp; + wp -= quaternion.wp; + return *this; +} + +inline QQuaternion &QQuaternion::operator*=(qreal factor) +{ + xp *= factor; + yp *= factor; + zp *= factor; + wp *= factor; + return *this; +} + +inline const QQuaternion operator*(const QQuaternion &q1, const QQuaternion& q2) +{ + // Algorithm from: + // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q53 + float x = q1.wp * q2.xp + + q1.xp * q2.wp + + q1.yp * q2.zp - + q1.zp * q2.yp; + float y = q1.wp * q2.yp + + q1.yp * q2.wp + + q1.zp * q2.xp - + q1.xp * q2.zp; + float z = q1.wp * q2.zp + + q1.zp * q2.wp + + q1.xp * q2.yp - + q1.yp * q2.xp; + float w = q1.wp * q2.wp - + q1.xp * q2.xp - + q1.yp * q2.yp - + q1.zp * q2.zp; + return QQuaternion(w, x, y, z, 1); +} + +inline QQuaternion &QQuaternion::operator*=(const QQuaternion &quaternion) +{ + *this = *this * quaternion; + return *this; +} + +inline QQuaternion &QQuaternion::operator/=(qreal divisor) +{ + xp /= divisor; + yp /= divisor; + zp /= divisor; + wp /= divisor; + return *this; +} + +inline bool operator==(const QQuaternion &q1, const QQuaternion &q2) +{ + return q1.xp == q2.xp && q1.yp == q2.yp && q1.zp == q2.zp && q1.wp == q2.wp; +} + +inline bool operator!=(const QQuaternion &q1, const QQuaternion &q2) +{ + return q1.xp != q2.xp || q1.yp != q2.yp || q1.zp != q2.zp || q1.wp != q2.wp; +} + +inline const QQuaternion operator+(const QQuaternion &q1, const QQuaternion &q2) +{ + return QQuaternion(q1.wp + q2.wp, q1.xp + q2.xp, q1.yp + q2.yp, q1.zp + q2.zp, 1); +} + +inline const QQuaternion operator-(const QQuaternion &q1, const QQuaternion &q2) +{ + return QQuaternion(q1.wp - q2.wp, q1.xp - q2.xp, q1.yp - q2.yp, q1.zp - q2.zp, 1); +} + +inline const QQuaternion operator*(qreal factor, const QQuaternion &quaternion) +{ + return QQuaternion(quaternion.wp * factor, quaternion.xp * factor, quaternion.yp * factor, quaternion.zp * factor, 1); +} + +inline const QQuaternion operator*(const QQuaternion &quaternion, qreal factor) +{ + return QQuaternion(quaternion.wp * factor, quaternion.xp * factor, quaternion.yp * factor, quaternion.zp * factor, 1); +} + +inline const QQuaternion operator-(const QQuaternion &quaternion) +{ + return QQuaternion(-quaternion.wp, -quaternion.xp, -quaternion.yp, -quaternion.zp, 1); +} + +inline const QQuaternion operator/(const QQuaternion &quaternion, qreal divisor) +{ + return QQuaternion(quaternion.wp / divisor, quaternion.xp / divisor, quaternion.yp / divisor, quaternion.zp / divisor, 1); +} + +inline bool qFuzzyCompare(const QQuaternion& q1, const QQuaternion& q2) +{ + return qFuzzyCompare(q1.xp, q2.xp) && + qFuzzyCompare(q1.yp, q2.yp) && + qFuzzyCompare(q1.zp, q2.zp) && + qFuzzyCompare(q1.wp, q2.wp); +} + +#ifndef QT_NO_VECTOR3D + +inline QQuaternion::QQuaternion(qreal scalar, const QVector3D& vector) + : wp(scalar), xp(vector.xp), yp(vector.yp), zp(vector.zp) {} + +inline void QQuaternion::setVector(const QVector3D& vector) +{ + xp = vector.xp; + yp = vector.yp; + zp = vector.zp; +} + +inline QVector3D QQuaternion::vector() const +{ + return QVector3D(xp, yp, zp, 1); +} + +#endif + +inline void QQuaternion::setVector(qreal x, qreal y, qreal z) +{ + xp = x; + yp = y; + zp = z; +} + +#ifndef QT_NO_VECTOR4D + +inline QQuaternion::QQuaternion(const QVector4D& vector) + : wp(vector.wp), xp(vector.xp), yp(vector.yp), zp(vector.zp) {} + +inline QVector4D QQuaternion::toVector4D() const +{ + return QVector4D(xp, yp, zp, wp, 1); +} + +#endif + +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QQuaternion &q); +#endif + +#endif + +QT_END_NAMESPACE + +#ifndef QT_NO_QUATERNION +Q_DECLARE_METATYPE(QQuaternion) +#endif + +QT_END_HEADER + +#endif diff --git a/src/gui/math3d/qvector2d.cpp b/src/gui/math3d/qvector2d.cpp new file mode 100644 index 0000000..df2519d --- /dev/null +++ b/src/gui/math3d/qvector2d.cpp @@ -0,0 +1,417 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qvector2d.h" +#include "qvector3d.h" +#include "qvector4d.h" +#include <QtCore/qdebug.h> +#include <QtCore/qmath.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_VECTOR2D + +/*! + \class QVector2D + \brief The QVector2D class represents a vector or vertex in 2D space. + \since 4.6 + + The QVector2D class can also be used to represent vertices in 2D space. + We therefore do not need to provide a separate vertex class. + + The coordinates are stored internally using the most efficient + representation for the GL rendering engine, which will be either + floating-point or fixed-point. +*/ + +/*! + \fn QVector2D::QVector2D() + + Constructs a null vector, i.e. with coordinates (0, 0, 0). +*/ + +/*! + \fn QVector2D::QVector2D(qreal xpos, qreal ypos) + + Constructs a vector with coordinates (\a xpos, \a ypos). +*/ + +/*! + \fn QVector2D::QVector2D(const QPoint& point) + + Constructs a vector with x and y coordinates from a 2D \a point. +*/ + +/*! + \fn QVector2D::QVector2D(const QPointF& point) + + Constructs a vector with x and y coordinates from a 2D \a point. +*/ + +#ifndef QT_NO_VECTOR3D + +/*! + Constructs a vector with x and y coordinates from a 3D \a vector. + The z coordinate of \a vector is dropped. + + \sa toVector3D() +*/ +QVector2D::QVector2D(const QVector3D& vector) +{ + xp = vector.xp; + yp = vector.yp; +} + +#endif + +#ifndef QT_NO_VECTOR4D + +/*! + Constructs a vector with x and y coordinates from a 3D \a vector. + The z and w coordinates of \a vector are dropped. + + \sa toVector4D() +*/ +QVector2D::QVector2D(const QVector4D& vector) +{ + xp = vector.xp; + yp = vector.yp; +} + +#endif + +/*! + \fn bool QVector2D::isNull() const + + Returns true if the x and y coordinates are set to 0.0, + otherwise returns false. +*/ + +/*! + \fn qreal QVector2D::x() const + + Returns the x coordinate of this point. + + \sa setX(), y() +*/ + +/*! + \fn qreal QVector2D::y() const + + Returns the y coordinate of this point. + + \sa setY(), x() +*/ + +/*! + \fn void QVector2D::setX(qreal x) + + Sets the x coordinate of this point to the given \a x coordinate. + + \sa x(), setY() +*/ + +/*! + \fn void QVector2D::setY(qreal y) + + Sets the y coordinate of this point to the given \a y coordinate. + + \sa y(), setX() +*/ + +/*! + Returns the length of the vector from the origin. + + \sa lengthSquared(), normalized() +*/ +qreal QVector2D::length() const +{ + return qSqrt(xp * xp + yp * yp); +} + +/*! + Returns the squared length of the vector from the origin. + This is equivalent to the dot product of the vector with itself. + + \sa length(), dotProduct() +*/ +qreal QVector2D::lengthSquared() const +{ + return xp * xp + yp * yp; +} + +/*! + Returns the normalized unit vector form of this vector. + + If this vector is null, then a null vector is returned. If the length + of the vector is very close to 1, then the vector will be returned as-is. + Otherwise the normalized form of the vector of length 1 will be returned. + + \sa length(), normalize() +*/ +QVector2D QVector2D::normalized() const +{ + qreal len = lengthSquared(); + if (qFuzzyIsNull(len - 1.0f)) + return *this; + else if (!qFuzzyIsNull(len)) + return *this / qSqrt(len); + else + return QVector2D(); +} + +/*! + Normalizes the currect vector in place. Nothing happens if this + vector is a null vector or the length of the vector is very close to 1. + + \sa length(), normalized() +*/ +void QVector2D::normalize() +{ + qreal len = lengthSquared(); + if (qFuzzyIsNull(len - 1.0f) || qFuzzyIsNull(len)) + return; + + len = qSqrt(len); + + xp /= len; + yp /= len; +} + +/*! + \fn QVector2D &QVector2D::operator+=(const QVector2D &vector) + + Adds the given \a vector to this vector and returns a reference to + this vector. + + \sa operator-=() +*/ + +/*! + \fn QVector2D &QVector2D::operator-=(const QVector2D &vector) + + Subtracts the given \a vector from this vector and returns a reference to + this vector. + + \sa operator+=() +*/ + +/*! + \fn QVector2D &QVector2D::operator*=(qreal factor) + + Multiplies this vector's coordinates by the given \a factor, and + returns a reference to this vector. + + \sa operator/=() +*/ + +/*! + \fn QVector2D &QVector2D::operator*=(const QVector2D &vector) + + Multiplies the components of this vector by the corresponding + components in \a vector. +*/ + +/*! + \fn QVector2D &QVector2D::operator/=(qreal divisor) + + Divides this vector's coordinates by the given \a divisor, and + returns a reference to this vector. + + \sa operator*=() +*/ + +/*! + Returns the dot product of \a v1 and \a v2. +*/ +qreal QVector2D::dotProduct(const QVector2D& v1, const QVector2D& v2) +{ + return v1.xp * v2.xp + v1.yp * v2.yp; +} + +/*! + \fn bool operator==(const QVector2D &v1, const QVector2D &v2) + \relates QVector2D + + Returns true if \a v1 is equal to \a v2; otherwise returns false. + This operator uses an exact floating-point comparison. +*/ + +/*! + \fn bool operator!=(const QVector2D &v1, const QVector2D &v2) + \relates QVector2D + + Returns true if \a v1 is not equal to \a v2; otherwise returns false. + This operator uses an exact floating-point comparison. +*/ + +/*! + \fn const QVector2D operator+(const QVector2D &v1, const QVector2D &v2) + \relates QVector2D + + Returns a QVector2D object that is the sum of the given vectors, \a v1 + and \a v2; each component is added separately. + + \sa QVector2D::operator+=() +*/ + +/*! + \fn const QVector2D operator-(const QVector2D &v1, const QVector2D &v2) + \relates QVector2D + + Returns a QVector2D object that is formed by subtracting \a v2 from \a v1; + each component is subtracted separately. + + \sa QVector2D::operator-=() +*/ + +/*! + \fn const QVector2D operator*(qreal factor, const QVector2D &vector) + \relates QVector2D + + Returns a copy of the given \a vector, multiplied by the given \a factor. + + \sa QVector2D::operator*=() +*/ + +/*! + \fn const QVector2D operator*(const QVector2D &vector, qreal factor) + \relates QVector2D + + Returns a copy of the given \a vector, multiplied by the given \a factor. + + \sa QVector2D::operator*=() +*/ + +/*! + \fn const QVector2D operator*(const QVector2D &v1, const QVector2D &v2) + \relates QVector2D + + Multiplies the components of \a v1 by the corresponding + components in \a v2. +*/ + +/*! + \fn const QVector2D operator-(const QVector2D &vector) + \relates QVector2D + \overload + + Returns a QVector2D object that is formed by changing the sign of + the components of the given \a vector. + + Equivalent to \c {QVector2D(0,0) - vector}. +*/ + +/*! + \fn const QVector2D operator/(const QVector2D &vector, qreal divisor) + \relates QVector2D + + Returns the QVector2D object formed by dividing all three components of + the given \a vector by the given \a divisor. + + \sa QVector2D::operator/=() +*/ + +/*! + \fn bool qFuzzyCompare(const QVector2D& v1, const QVector2D& v2) + \relates QVector2D + + Returns true if \a v1 and \a v2 are equal, allowing for a small + fuzziness factor for floating-point comparisons; false otherwise. +*/ + +#ifndef QT_NO_VECTOR3D + +/*! + Returns the 3D form of this 2D vector, with the z coordinate set to zero. + + \sa toVector4D(), toPoint() +*/ +QVector3D QVector2D::toVector3D() const +{ + return QVector3D(xp, yp, 0.0f, 1); +} + +#endif + +#ifndef QT_NO_VECTOR4D + +/*! + Returns the 4D form of this 2D vector, with the z and w coordinates set to zero. + + \sa toVector3D(), toPoint() +*/ +QVector4D QVector2D::toVector4D() const +{ + return QVector4D(xp, yp, 0.0f, 0.0f, 1); +} + +#endif + +/*! + \fn QPoint QVector2D::toPoint() const + + Returns the QPoint form of this 2D vector. + + \sa toPointF(), toVector3D() +*/ + +/*! + \fn QPointF QVector2D::toPointF() const + + Returns the QPointF form of this 2D vector. + + \sa toPoint(), toVector3D() +*/ + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<(QDebug dbg, const QVector2D &vector) +{ + dbg.nospace() << "QVector2D(" << vector.x() << ", " << vector.y() << ')'; + return dbg.space(); +} + +#endif + +#endif + +QT_END_NAMESPACE diff --git a/src/gui/math3d/qvector2d.h b/src/gui/math3d/qvector2d.h new file mode 100644 index 0000000..2be0a11 --- /dev/null +++ b/src/gui/math3d/qvector2d.h @@ -0,0 +1,256 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QVECTOR2D_H +#define QVECTOR2D_H + +#include <QtCore/qpoint.h> +#include <QtCore/qmetatype.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QVector3D; +class QVector4D; + +#ifndef QT_NO_VECTOR2D + +class Q_GUI_EXPORT QVector2D +{ +public: + QVector2D(); + QVector2D(qreal xpos, qreal ypos); + explicit QVector2D(const QPoint& point); + explicit QVector2D(const QPointF& point); +#ifndef QT_NO_VECTOR3D + explicit QVector2D(const QVector3D& vector); +#endif +#ifndef QT_NO_VECTOR4D + explicit QVector2D(const QVector4D& vector); +#endif + + bool isNull() const; + + qreal x() const; + qreal y() const; + + void setX(qreal x); + void setY(qreal y); + + qreal length() const; + qreal lengthSquared() const; + + QVector2D normalized() const; + void normalize(); + + QVector2D &operator+=(const QVector2D &vector); + QVector2D &operator-=(const QVector2D &vector); + QVector2D &operator*=(qreal factor); + QVector2D &operator*=(const QVector2D &vector); + QVector2D &operator/=(qreal divisor); + + static qreal dotProduct(const QVector2D& v1, const QVector2D& v2); + + friend inline bool operator==(const QVector2D &v1, const QVector2D &v2); + friend inline bool operator!=(const QVector2D &v1, const QVector2D &v2); + friend inline const QVector2D operator+(const QVector2D &v1, const QVector2D &v2); + friend inline const QVector2D operator-(const QVector2D &v1, const QVector2D &v2); + friend inline const QVector2D operator*(qreal factor, const QVector2D &vector); + friend inline const QVector2D operator*(const QVector2D &vector, qreal factor); + friend inline const QVector2D operator*(const QVector2D &v1, const QVector2D &v2); + friend inline const QVector2D operator-(const QVector2D &vector); + friend inline const QVector2D operator/(const QVector2D &vector, qreal divisor); + + friend inline bool qFuzzyCompare(const QVector2D& v1, const QVector2D& v2); + +#ifndef QT_NO_VECTOR3D + QVector3D toVector3D() const; +#endif +#ifndef QT_NO_VECTOR4D + QVector4D toVector4D() const; +#endif + + QPoint toPoint() const; + QPointF toPointF() const; + +private: + float xp, yp; + + QVector2D(float xpos, float ypos, int dummy); + + friend class QVector3D; + friend class QVector4D; +}; + +inline QVector2D::QVector2D() : xp(0.0f), yp(0.0f) {} + +inline QVector2D::QVector2D(float xpos, float ypos, int) : xp(xpos), yp(ypos) {} + +inline QVector2D::QVector2D(qreal xpos, qreal ypos) : xp(xpos), yp(ypos) {} + +inline QVector2D::QVector2D(const QPoint& point) : xp(point.x()), yp(point.y()) {} + +inline QVector2D::QVector2D(const QPointF& point) : xp(point.x()), yp(point.y()) {} + +inline bool QVector2D::isNull() const +{ + return qIsNull(xp) && qIsNull(yp); +} + +inline qreal QVector2D::x() const { return qreal(xp); } +inline qreal QVector2D::y() const { return qreal(yp); } + +inline void QVector2D::setX(qreal x) { xp = x; } +inline void QVector2D::setY(qreal y) { yp = y; } + +inline QVector2D &QVector2D::operator+=(const QVector2D &vector) +{ + xp += vector.xp; + yp += vector.yp; + return *this; +} + +inline QVector2D &QVector2D::operator-=(const QVector2D &vector) +{ + xp -= vector.xp; + yp -= vector.yp; + return *this; +} + +inline QVector2D &QVector2D::operator*=(qreal factor) +{ + xp *= factor; + yp *= factor; + return *this; +} + +inline QVector2D &QVector2D::operator*=(const QVector2D &vector) +{ + xp *= vector.xp; + yp *= vector.yp; + return *this; +} + +inline QVector2D &QVector2D::operator/=(qreal divisor) +{ + xp /= divisor; + yp /= divisor; + return *this; +} + +inline bool operator==(const QVector2D &v1, const QVector2D &v2) +{ + return v1.xp == v2.xp && v1.yp == v2.yp; +} + +inline bool operator!=(const QVector2D &v1, const QVector2D &v2) +{ + return v1.xp != v2.xp || v1.yp != v2.yp; +} + +inline const QVector2D operator+(const QVector2D &v1, const QVector2D &v2) +{ + return QVector2D(v1.xp + v2.xp, v1.yp + v2.yp, 1); +} + +inline const QVector2D operator-(const QVector2D &v1, const QVector2D &v2) +{ + return QVector2D(v1.xp - v2.xp, v1.yp - v2.yp, 1); +} + +inline const QVector2D operator*(qreal factor, const QVector2D &vector) +{ + return QVector2D(vector.xp * factor, vector.yp * factor, 1); +} + +inline const QVector2D operator*(const QVector2D &vector, qreal factor) +{ + return QVector2D(vector.xp * factor, vector.yp * factor, 1); +} + +inline const QVector2D operator*(const QVector2D &v1, const QVector2D &v2) +{ + return QVector2D(v1.xp * v2.xp, v1.yp * v2.yp, 1); +} + +inline const QVector2D operator-(const QVector2D &vector) +{ + return QVector2D(-vector.xp, -vector.yp, 1); +} + +inline const QVector2D operator/(const QVector2D &vector, qreal divisor) +{ + return QVector2D(vector.xp / divisor, vector.yp / divisor, 1); +} + +inline bool qFuzzyCompare(const QVector2D& v1, const QVector2D& v2) +{ + return qFuzzyCompare(v1.xp, v2.xp) && qFuzzyCompare(v1.yp, v2.yp); +} + +inline QPoint QVector2D::toPoint() const +{ + return QPoint(qRound(xp), qRound(yp)); +} + +inline QPointF QVector2D::toPointF() const +{ + return QPointF(qreal(xp), qreal(yp)); +} + +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QVector2D &vector); +#endif + +#endif + +QT_END_NAMESPACE + +#ifndef QT_NO_VECTOR2D +Q_DECLARE_METATYPE(QVector2D) +#endif + +QT_END_HEADER + +#endif diff --git a/src/gui/math3d/qvector3d.cpp b/src/gui/math3d/qvector3d.cpp new file mode 100644 index 0000000..a5f5c5b --- /dev/null +++ b/src/gui/math3d/qvector3d.cpp @@ -0,0 +1,565 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qvector3d.h" +#include "qvector2d.h" +#include "qvector4d.h" +#include <QtCore/qmath.h> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_VECTOR3D + +/*! + \class QVector3D + \brief The QVector3D class represents a vector or vertex in 3D space. + \since 4.6 + + Vectors are one of the main building blocks of 3D representation and + drawing. They consist of three coordinates, traditionally called + x, y, and z. + + The QVector3D class can also be used to represent vertices in 3D space. + We therefore do not need to provide a separate vertex class. + + The coordinates are stored internally using the most efficient + representation for the GL rendering engine, which will be either + floating-point or fixed-point. +*/ + +/*! + \fn QVector3D::QVector3D() + + Constructs a null vector, i.e. with coordinates (0, 0, 0). +*/ + +/*! + \fn QVector3D::QVector3D(qreal xpos, qreal ypos, qreal zpos) + + Constructs a vector with coordinates (\a xpos, \a ypos, \a zpos). +*/ + +/*! + \fn QVector3D::QVector3D(const QPoint& point) + + Constructs a vector with x and y coordinates from a 2D \a point, and a + z coordinate of 0. +*/ + +/*! + \fn QVector3D::QVector3D(const QPointF& point) + + Constructs a vector with x and y coordinates from a 2D \a point, and a + z coordinate of 0. +*/ + +#ifndef QT_NO_VECTOR2D + +/*! + Constructs a 3D vector from the specified 2D \a vector. The z + coordinate is set to zero. + + \sa toVector2D() +*/ +QVector3D::QVector3D(const QVector2D& vector) +{ + xp = vector.xp; + yp = vector.yp; + zp = 0.0f; +} + +/*! + Constructs a 3D vector from the specified 2D \a vector. The z + coordinate is set to \a zpos. + + \sa toVector2D() +*/ +QVector3D::QVector3D(const QVector2D& vector, qreal zpos) +{ + xp = vector.xp; + yp = vector.yp; + zp = zpos; +} + +#endif + +#ifndef QT_NO_VECTOR4D + +/*! + Constructs a 3D vector from the specified 4D \a vector. The w + coordinate is dropped. + + \sa toVector4D() +*/ +QVector3D::QVector3D(const QVector4D& vector) +{ + xp = vector.xp; + yp = vector.yp; + zp = vector.zp; +} + +#endif + +/*! + \fn bool QVector3D::isNull() const + + Returns true if the x, y, and z coordinates are set to 0.0, + otherwise returns false. +*/ + +/*! + \fn qreal QVector3D::x() const + + Returns the x coordinate of this point. + + \sa setX(), y(), z() +*/ + +/*! + \fn qreal QVector3D::y() const + + Returns the y coordinate of this point. + + \sa setY(), x(), z() +*/ + +/*! + \fn qreal QVector3D::z() const + + Returns the z coordinate of this point. + + \sa setZ(), x(), y() +*/ + +/*! + \fn void QVector3D::setX(qreal x) + + Sets the x coordinate of this point to the given \a x coordinate. + + \sa x(), setY(), setZ() +*/ + +/*! + \fn void QVector3D::setY(qreal y) + + Sets the y coordinate of this point to the given \a y coordinate. + + \sa y(), setX(), setZ() +*/ + +/*! + \fn void QVector3D::setZ(qreal z) + + Sets the z coordinate of this point to the given \a z coordinate. + + \sa z(), setX(), setY() +*/ + +/*! + Returns the normalized unit vector form of this vector. + + If this vector is null, then a null vector is returned. If the length + of the vector is very close to 1, then the vector will be returned as-is. + Otherwise the normalized form of the vector of length 1 will be returned. + + \sa length(), normalize() +*/ +QVector3D QVector3D::normalized() const +{ + qreal len = lengthSquared(); + if (qFuzzyIsNull(len - 1.0f)) + return *this; + else if (!qFuzzyIsNull(len)) + return *this / qSqrt(len); + else + return QVector3D(); +} + +/*! + Normalizes the currect vector in place. Nothing happens if this + vector is a null vector or the length of the vector is very close to 1. + + \sa length(), normalized() +*/ +void QVector3D::normalize() +{ + qreal len = lengthSquared(); + if (qFuzzyIsNull(len - 1.0f) || qFuzzyIsNull(len)) + return; + + len = qSqrt(len); + + xp /= len; + yp /= len; + zp /= len; +} + +/*! + \fn QVector3D &QVector3D::operator+=(const QVector3D &vector) + + Adds the given \a vector to this vector and returns a reference to + this vector. + + \sa operator-=() +*/ + +/*! + \fn QVector3D &QVector3D::operator-=(const QVector3D &vector) + + Subtracts the given \a vector from this vector and returns a reference to + this vector. + + \sa operator+=() +*/ + +/*! + \fn QVector3D &QVector3D::operator*=(qreal factor) + + Multiplies this vector's coordinates by the given \a factor, and + returns a reference to this vector. + + \sa operator/=() +*/ + +/*! + \fn QVector3D &QVector3D::operator*=(const QVector3D& vector) + \overload + + Multiplies the components of this vector by the corresponding + components in \a vector. + + Note: this is not the same as the crossProduct() of this + vector and \a vector. + + \sa crossProduct() +*/ + +/*! + \fn QVector3D &QVector3D::operator/=(qreal divisor) + + Divides this vector's coordinates by the given \a divisor, and + returns a reference to this vector. + + \sa operator*=() +*/ + +/*! + Returns the dot product of \a v1 and \a v2. +*/ +qreal QVector3D::dotProduct(const QVector3D& v1, const QVector3D& v2) +{ + return v1.xp * v2.xp + v1.yp * v2.yp + v1.zp * v2.zp; +} + +/*! + Returns the cross-product of vectors \a v1 and \a v2, which corresponds + to the normal vector of a plane defined by \a v1 and \a v2. + + \sa normal() +*/ +QVector3D QVector3D::crossProduct(const QVector3D& v1, const QVector3D& v2) +{ + return QVector3D(v1.yp * v2.zp - v1.zp * v2.yp, + v1.zp * v2.xp - v1.xp * v2.zp, + v1.xp * v2.yp - v1.yp * v2.xp, 1); +} + +/*! + Returns the normal vector of a plane defined by vectors \a v1 and \a v2, + normalized to be a unit vector. + + Use crossProduct() to compute the cross-product of \a v1 and \a v2 if you + do not need the result to be normalized to a unit vector. + + \sa crossProduct(), distanceToPlane() +*/ +QVector3D QVector3D::normal(const QVector3D& v1, const QVector3D& v2) +{ + return crossProduct(v1, v2).normalized(); +} + +/*! + \overload + + Returns the normal vector of a plane defined by vectors + \a v2 - \a v1 and \a v3 - \a v1, normalized to be a unit vector. + + Use crossProduct() to compute the cross-product of \a v2 - \a v1 and + \a v3 - \a v1 if you do not need the result to be normalized to a + unit vector. + + \sa crossProduct(), distanceToPlane() +*/ +QVector3D QVector3D::normal + (const QVector3D& v1, const QVector3D& v2, const QVector3D& v3) +{ + return crossProduct((v2 - v1), (v3 - v1)).normalized(); +} + +/*! + Returns the distance from this vertex to a plane defined by + the vertex \a plane and a \a normal unit vector. The \a normal + parameter is assumed to have been normalized to a unit vector. + + The return value will be negative if the vertex is below the plane, + or zero if it is on the plane. + + \sa normal(), distanceToLine() +*/ +qreal QVector3D::distanceToPlane + (const QVector3D& plane, const QVector3D& normal) const +{ + return dotProduct(*this - plane, normal); +} + +/*! + \overload + + Returns the distance from this vertex a plane defined by + the vertices \a plane1, \a plane2 and \a plane3. + + The return value will be negative if the vertex is below the plane, + or zero if it is on the plane. + + The two vectors that define the plane are \a plane2 - \a plane1 + and \a plane3 - \a plane1. + + \sa normal(), distanceToLine() +*/ +qreal QVector3D::distanceToPlane + (const QVector3D& plane1, const QVector3D& plane2, const QVector3D& plane3) const +{ + QVector3D n = normal(plane2 - plane1, plane3 - plane1); + return dotProduct(*this - plane1, n); +} + +/*! + Returns the distance that this vertex is from a line defined + by \a point and the unit vector \a direction. + + If \a direction is a null vector, then it does not define a line. + In that case, the distance from \a point to this vertex is returned. + + \sa distanceToPlane() +*/ +qreal QVector3D::distanceToLine + (const QVector3D& point, const QVector3D& direction) const +{ + if (direction.isNull()) + return (*this - point).length(); + QVector3D p = point + dotProduct(*this - point, direction) * direction; + return (*this - p).length(); +} + +/*! + \fn bool operator==(const QVector3D &v1, const QVector3D &v2) + \relates QVector3D + + Returns true if \a v1 is equal to \a v2; otherwise returns false. + This operator uses an exact floating-point comparison. +*/ + +/*! + \fn bool operator!=(const QVector3D &v1, const QVector3D &v2) + \relates QVector3D + + Returns true if \a v1 is not equal to \a v2; otherwise returns false. + This operator uses an exact floating-point comparison. +*/ + +/*! + \fn const QVector3D operator+(const QVector3D &v1, const QVector3D &v2) + \relates QVector3D + + Returns a QVector3D object that is the sum of the given vectors, \a v1 + and \a v2; each component is added separately. + + \sa QVector3D::operator+=() +*/ + +/*! + \fn const QVector3D operator-(const QVector3D &v1, const QVector3D &v2) + \relates QVector3D + + Returns a QVector3D object that is formed by subtracting \a v2 from \a v1; + each component is subtracted separately. + + \sa QVector3D::operator-=() +*/ + +/*! + \fn const QVector3D operator*(qreal factor, const QVector3D &vector) + \relates QVector3D + + Returns a copy of the given \a vector, multiplied by the given \a factor. + + \sa QVector3D::operator*=() +*/ + +/*! + \fn const QVector3D operator*(const QVector3D &vector, qreal factor) + \relates QVector3D + + Returns a copy of the given \a vector, multiplied by the given \a factor. + + \sa QVector3D::operator*=() +*/ + +/*! + \fn const QVector3D operator*(const QVector3D &v1, const QVector3D& v2) + \relates QVector3D + + Multiplies the components of \a v1 by the corresponding components in \a v2. + + Note: this is not the same as the crossProduct() of \a v1 and \a v2. + + \sa QVector3D::crossProduct() +*/ + +/*! + \fn const QVector3D operator-(const QVector3D &vector) + \relates QVector3D + \overload + + Returns a QVector3D object that is formed by changing the sign of + all three components of the given \a vector. + + Equivalent to \c {QVector3D(0,0,0) - vector}. +*/ + +/*! + \fn const QVector3D operator/(const QVector3D &vector, qreal divisor) + \relates QVector3D + + Returns the QVector3D object formed by dividing all three components of + the given \a vector by the given \a divisor. + + \sa QVector3D::operator/=() +*/ + +/*! + \fn bool qFuzzyCompare(const QVector3D& v1, const QVector3D& v2) + \relates QVector3D + + Returns true if \a v1 and \a v2 are equal, allowing for a small + fuzziness factor for floating-point comparisons; false otherwise. +*/ + +#ifndef QT_NO_VECTOR2D + +/*! + Returns the 2D vector form of this 3D vector, dropping the z coordinate. + + \sa toVector4D(), toPoint() +*/ +QVector2D QVector3D::toVector2D() const +{ + return QVector2D(xp, yp, 1); +} + +#endif + +#ifndef QT_NO_VECTOR4D + +/*! + Returns the 4D form of this 3D vector, with the w coordinate set to zero. + + \sa toVector2D(), toPoint() +*/ +QVector4D QVector3D::toVector4D() const +{ + return QVector4D(xp, yp, zp, 0.0f, 1); +} + +#endif + +/*! + \fn QPoint QVector3D::toPoint() const + + Returns the QPoint form of this 3D vector. + + \sa toPointF(), toVector2D() +*/ + +/*! + \fn QPointF QVector3D::toPointF() const + + Returns the QPointF form of this 3D vector. + + \sa toPoint(), toVector2D() +*/ + +/*! + Returns the length of the vector from the origin. + + \sa lengthSquared(), normalized() +*/ +qreal QVector3D::length() const +{ + return qSqrt(xp * xp + yp * yp + zp * zp); +} + +/*! + Returns the squared length of the vector from the origin. + This is equivalent to the dot product of the vector with itself. + + \sa length(), dotProduct() +*/ +qreal QVector3D::lengthSquared() const +{ + return xp * xp + yp * yp + zp * zp; +} + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<(QDebug dbg, const QVector3D &vector) +{ + dbg.nospace() << "QVector3D(" + << vector.x() << ", " << vector.y() << ", " << vector.z() << ')'; + return dbg.space(); +} + +#endif + +#endif + +QT_END_NAMESPACE diff --git a/src/gui/math3d/qvector3d.h b/src/gui/math3d/qvector3d.h new file mode 100644 index 0000000..480667a --- /dev/null +++ b/src/gui/math3d/qvector3d.h @@ -0,0 +1,284 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QVECTOR3D_H +#define QVECTOR3D_H + +#include <QtCore/qpoint.h> +#include <QtCore/qmetatype.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QMatrix4x4; +class QVector2D; +class QVector4D; +class QQuaternion; + +#ifndef QT_NO_VECTOR3D + +class Q_GUI_EXPORT QVector3D +{ +public: + QVector3D(); + QVector3D(qreal xpos, qreal ypos, qreal zpos); + explicit QVector3D(const QPoint& point); + explicit QVector3D(const QPointF& point); +#ifndef QT_NO_VECTOR2D + QVector3D(const QVector2D& vector); + QVector3D(const QVector2D& vector, qreal zpos); +#endif +#ifndef QT_NO_VECTOR4D + explicit QVector3D(const QVector4D& vector); +#endif + + bool isNull() const; + + qreal x() const; + qreal y() const; + qreal z() const; + + void setX(qreal x); + void setY(qreal y); + void setZ(qreal z); + + qreal length() const; + qreal lengthSquared() const; + + QVector3D normalized() const; + void normalize(); + + QVector3D &operator+=(const QVector3D &vector); + QVector3D &operator-=(const QVector3D &vector); + QVector3D &operator*=(qreal factor); + QVector3D &operator*=(const QVector3D& vector); + QVector3D &operator/=(qreal divisor); + + static qreal dotProduct(const QVector3D& v1, const QVector3D& v2); + static QVector3D crossProduct(const QVector3D& v1, const QVector3D& v2); + static QVector3D normal(const QVector3D& v1, const QVector3D& v2); + static QVector3D normal + (const QVector3D& v1, const QVector3D& v2, const QVector3D& v3); + + qreal distanceToPlane(const QVector3D& plane, const QVector3D& normal) const; + qreal distanceToPlane(const QVector3D& plane1, const QVector3D& plane2, const QVector3D& plane3) const; + qreal distanceToLine(const QVector3D& point, const QVector3D& direction) const; + + friend inline bool operator==(const QVector3D &v1, const QVector3D &v2); + friend inline bool operator!=(const QVector3D &v1, const QVector3D &v2); + friend inline const QVector3D operator+(const QVector3D &v1, const QVector3D &v2); + friend inline const QVector3D operator-(const QVector3D &v1, const QVector3D &v2); + friend inline const QVector3D operator*(qreal factor, const QVector3D &vector); + friend inline const QVector3D operator*(const QVector3D &vector, qreal factor); + friend const QVector3D operator*(const QVector3D &v1, const QVector3D& v2); + friend inline const QVector3D operator-(const QVector3D &vector); + friend inline const QVector3D operator/(const QVector3D &vector, qreal divisor); + + friend inline bool qFuzzyCompare(const QVector3D& v1, const QVector3D& v2); + +#ifndef QT_NO_VECTOR2D + QVector2D toVector2D() const; +#endif +#ifndef QT_NO_VECTOR4D + QVector4D toVector4D() const; +#endif + + QPoint toPoint() const; + QPointF toPointF() const; + +private: + float xp, yp, zp; + + QVector3D(float xpos, float ypos, float zpos, int dummy); + + friend class QVector2D; + friend class QVector4D; + friend class QQuaternion; + friend class QMatrix4x4; +#ifndef QT_NO_MATRIX4X4 + friend QVector3D operator*(const QVector3D& vector, const QMatrix4x4& matrix); + friend QVector3D operator*(const QMatrix4x4& matrix, const QVector3D& vector); +#endif +}; + +inline QVector3D::QVector3D() : xp(0.0f), yp(0.0f), zp(0.0f) {} + +inline QVector3D::QVector3D(qreal xpos, qreal ypos, qreal zpos) : xp(xpos), yp(ypos), zp(zpos) {} + +inline QVector3D::QVector3D(float xpos, float ypos, float zpos, int) : xp(xpos), yp(ypos), zp(zpos) {} + +inline QVector3D::QVector3D(const QPoint& point) : xp(point.x()), yp(point.y()), zp(0.0f) {} + +inline QVector3D::QVector3D(const QPointF& point) : xp(point.x()), yp(point.y()), zp(0.0f) {} + +inline bool QVector3D::isNull() const +{ + return qIsNull(xp) && qIsNull(yp) && qIsNull(zp); +} + +inline qreal QVector3D::x() const { return qreal(xp); } +inline qreal QVector3D::y() const { return qreal(yp); } +inline qreal QVector3D::z() const { return qreal(zp); } + +inline void QVector3D::setX(qreal x) { xp = x; } +inline void QVector3D::setY(qreal y) { yp = y; } +inline void QVector3D::setZ(qreal z) { zp = z; } + +inline QVector3D &QVector3D::operator+=(const QVector3D &vector) +{ + xp += vector.xp; + yp += vector.yp; + zp += vector.zp; + return *this; +} + +inline QVector3D &QVector3D::operator-=(const QVector3D &vector) +{ + xp -= vector.xp; + yp -= vector.yp; + zp -= vector.zp; + return *this; +} + +inline QVector3D &QVector3D::operator*=(qreal factor) +{ + xp *= factor; + yp *= factor; + zp *= factor; + return *this; +} + +inline QVector3D &QVector3D::operator*=(const QVector3D& vector) +{ + xp *= vector.xp; + yp *= vector.yp; + zp *= vector.zp; + return *this; +} + +inline QVector3D &QVector3D::operator/=(qreal divisor) +{ + xp /= divisor; + yp /= divisor; + zp /= divisor; + return *this; +} + +inline bool operator==(const QVector3D &v1, const QVector3D &v2) +{ + return v1.xp == v2.xp && v1.yp == v2.yp && v1.zp == v2.zp; +} + +inline bool operator!=(const QVector3D &v1, const QVector3D &v2) +{ + return v1.xp != v2.xp || v1.yp != v2.yp || v1.zp != v2.zp; +} + +inline const QVector3D operator+(const QVector3D &v1, const QVector3D &v2) +{ + return QVector3D(v1.xp + v2.xp, v1.yp + v2.yp, v1.zp + v2.zp, 1); +} + +inline const QVector3D operator-(const QVector3D &v1, const QVector3D &v2) +{ + return QVector3D(v1.xp - v2.xp, v1.yp - v2.yp, v1.zp - v2.zp, 1); +} + +inline const QVector3D operator*(qreal factor, const QVector3D &vector) +{ + return QVector3D(vector.xp * factor, vector.yp * factor, vector.zp * factor, 1); +} + +inline const QVector3D operator*(const QVector3D &vector, qreal factor) +{ + return QVector3D(vector.xp * factor, vector.yp * factor, vector.zp * factor, 1); +} + +inline const QVector3D operator*(const QVector3D &v1, const QVector3D& v2) +{ + return QVector3D(v1.xp * v2.xp, v1.yp * v2.yp, v1.zp * v2.zp, 1); +} + +inline const QVector3D operator-(const QVector3D &vector) +{ + return QVector3D(-vector.xp, -vector.yp, -vector.zp, 1); +} + +inline const QVector3D operator/(const QVector3D &vector, qreal divisor) +{ + return QVector3D(vector.xp / divisor, vector.yp / divisor, vector.zp / divisor, 1); +} + +inline bool qFuzzyCompare(const QVector3D& v1, const QVector3D& v2) +{ + return qFuzzyCompare(v1.xp, v2.xp) && + qFuzzyCompare(v1.yp, v2.yp) && + qFuzzyCompare(v1.zp, v2.zp); +} + +inline QPoint QVector3D::toPoint() const +{ + return QPoint(qRound(xp), qRound(yp)); +} + +inline QPointF QVector3D::toPointF() const +{ + return QPointF(qreal(xp), qreal(yp)); +} + +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QVector3D &vector); +#endif + +#endif + +QT_END_NAMESPACE + +#ifndef QT_NO_VECTOR3D +Q_DECLARE_METATYPE(QVector3D) +#endif + +QT_END_HEADER + +#endif diff --git a/src/gui/math3d/qvector4d.cpp b/src/gui/math3d/qvector4d.cpp new file mode 100644 index 0000000..c5124e2 --- /dev/null +++ b/src/gui/math3d/qvector4d.cpp @@ -0,0 +1,518 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qvector4d.h" +#include "qvector3d.h" +#include "qvector2d.h" +#include <QtCore/qdebug.h> +#include <QtCore/qmath.h> + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_VECTOR4D + +/*! + \class QVector4D + \brief The QVector4D class represents a vector or vertex in 4D space. + \since 4.6 + + The QVector4D class can also be used to represent vertices in 4D space. + We therefore do not need to provide a separate vertex class. + + The coordinates are stored internally using the most efficient + representation for the GL rendering engine, which will be either + floating-point or fixed-point. + + \sa QQuaternion, QVector2D, QVector3D +*/ + +/*! + \fn QVector4D::QVector4D() + + Constructs a null vector, i.e. with coordinates (0, 0, 0, 0). +*/ + +/*! + \fn QVector4D::QVector4D(qreal xpos, qreal ypos, qreal zpos, qreal wpos) + + Constructs a vector with coordinates (\a xpos, \a ypos, \a zpos, \a wpos). +*/ + +/*! + \fn QVector4D::QVector4D(const QPoint& point) + + Constructs a vector with x and y coordinates from a 2D \a point, and + z and w coordinates of 0. +*/ + +/*! + \fn QVector4D::QVector4D(const QPointF& point) + + Constructs a vector with x and y coordinates from a 2D \a point, and + z and w coordinates of 0. +*/ + +#ifndef QT_NO_VECTOR2D + +/*! + Constructs a 4D vector from the specified 2D \a vector. The z + and w coordinates are set to zero. + + \sa toVector2D() +*/ +QVector4D::QVector4D(const QVector2D& vector) +{ + xp = vector.xp; + yp = vector.yp; + zp = 0.0f; + wp = 0.0f; +} + +/*! + Constructs a 4D vector from the specified 2D \a vector. The z + and w coordinates are set to \a zpos and \a wpos respectively. + + \sa toVector2D() +*/ +QVector4D::QVector4D(const QVector2D& vector, qreal zpos, qreal wpos) +{ + xp = vector.xp; + yp = vector.yp; + zp = zpos; + wp = wpos; +} + +#endif + +#ifndef QT_NO_VECTOR3D + +/*! + Constructs a 4D vector from the specified 3D \a vector. The w + coordinate is set to zero. + + \sa toVector3D() +*/ +QVector4D::QVector4D(const QVector3D& vector) +{ + xp = vector.xp; + yp = vector.yp; + zp = vector.zp; + wp = 0.0f; +} + +/*! + Constructs a 4D vector from the specified 3D \a vector. The w + coordinate is set to \a wpos. + + \sa toVector3D() +*/ +QVector4D::QVector4D(const QVector3D& vector, qreal wpos) +{ + xp = vector.xp; + yp = vector.yp; + zp = vector.zp; + wp = wpos; +} + +#endif + +/*! + \fn bool QVector4D::isNull() const + + Returns true if the x, y, z, and w coordinates are set to 0.0, + otherwise returns false. +*/ + +/*! + \fn qreal QVector4D::x() const + + Returns the x coordinate of this point. + + \sa setX(), y(), z(), w() +*/ + +/*! + \fn qreal QVector4D::y() const + + Returns the y coordinate of this point. + + \sa setY(), x(), z(), w() +*/ + +/*! + \fn qreal QVector4D::z() const + + Returns the z coordinate of this point. + + \sa setZ(), x(), y(), w() +*/ + +/*! + \fn qreal QVector4D::w() const + + Returns the w coordinate of this point. + + \sa setW(), x(), y(), z() +*/ + +/*! + \fn void QVector4D::setX(qreal x) + + Sets the x coordinate of this point to the given \a x coordinate. + + \sa x(), setY(), setZ(), setW() +*/ + +/*! + \fn void QVector4D::setY(qreal y) + + Sets the y coordinate of this point to the given \a y coordinate. + + \sa y(), setX(), setZ(), setW() +*/ + +/*! + \fn void QVector4D::setZ(qreal z) + + Sets the z coordinate of this point to the given \a z coordinate. + + \sa z(), setX(), setY(), setW() +*/ + +/*! + \fn void QVector4D::setW(qreal w) + + Sets the w coordinate of this point to the given \a w coordinate. + + \sa w(), setX(), setY(), setZ() +*/ + +/*! + Returns the length of the vector from the origin. + + \sa lengthSquared(), normalized() +*/ +qreal QVector4D::length() const +{ + return qSqrt(xp * xp + yp * yp + zp * zp + wp * wp); +} + +/*! + Returns the squared length of the vector from the origin. + This is equivalent to the dot product of the vector with itself. + + \sa length(), dotProduct() +*/ +qreal QVector4D::lengthSquared() const +{ + return xp * xp + yp * yp + zp * zp + wp * wp; +} + +/*! + Returns the normalized unit vector form of this vector. + + If this vector is null, then a null vector is returned. If the length + of the vector is very close to 1, then the vector will be returned as-is. + Otherwise the normalized form of the vector of length 1 will be returned. + + \sa length(), normalize() +*/ +QVector4D QVector4D::normalized() const +{ + qreal len = lengthSquared(); + if (qFuzzyIsNull(len - 1.0f)) + return *this; + else if (!qFuzzyIsNull(len)) + return *this / qSqrt(len); + else + return QVector4D(); +} + +/*! + Normalizes the currect vector in place. Nothing happens if this + vector is a null vector or the length of the vector is very close to 1. + + \sa length(), normalized() +*/ +void QVector4D::normalize() +{ + qreal len = lengthSquared(); + if (qFuzzyIsNull(len - 1.0f) || qFuzzyIsNull(len)) + return; + + len = qSqrt(len); + + xp /= len; + yp /= len; + zp /= len; + wp /= len; +} + +/*! + \fn QVector4D &QVector4D::operator+=(const QVector4D &vector) + + Adds the given \a vector to this vector and returns a reference to + this vector. + + \sa operator-=() +*/ + +/*! + \fn QVector4D &QVector4D::operator-=(const QVector4D &vector) + + Subtracts the given \a vector from this vector and returns a reference to + this vector. + + \sa operator+=() +*/ + +/*! + \fn QVector4D &QVector4D::operator*=(qreal factor) + + Multiplies this vector's coordinates by the given \a factor, and + returns a reference to this vector. + + \sa operator/=() +*/ + +/*! + \fn QVector4D &QVector4D::operator*=(const QVector4D &vector) + + Multiplies the components of this vector by the corresponding + components in \a vector. +*/ + +/*! + \fn QVector4D &QVector4D::operator/=(qreal divisor) + + Divides this vector's coordinates by the given \a divisor, and + returns a reference to this vector. + + \sa operator*=() +*/ + +/*! + Returns the dot product of \a v1 and \a v2. +*/ +qreal QVector4D::dotProduct(const QVector4D& v1, const QVector4D& v2) +{ + return v1.xp * v2.xp + v1.yp * v2.yp + v1.zp * v2.zp + v1.wp * v2.wp; +} + +/*! + \fn bool operator==(const QVector4D &v1, const QVector4D &v2) + \relates QVector4D + + Returns true if \a v1 is equal to \a v2; otherwise returns false. + This operator uses an exact floating-point comparison. +*/ + +/*! + \fn bool operator!=(const QVector4D &v1, const QVector4D &v2) + \relates QVector4D + + Returns true if \a v1 is not equal to \a v2; otherwise returns false. + This operator uses an exact floating-point comparison. +*/ + +/*! + \fn const QVector4D operator+(const QVector4D &v1, const QVector4D &v2) + \relates QVector4D + + Returns a QVector4D object that is the sum of the given vectors, \a v1 + and \a v2; each component is added separately. + + \sa QVector4D::operator+=() +*/ + +/*! + \fn const QVector4D operator-(const QVector4D &v1, const QVector4D &v2) + \relates QVector4D + + Returns a QVector4D object that is formed by subtracting \a v2 from \a v1; + each component is subtracted separately. + + \sa QVector4D::operator-=() +*/ + +/*! + \fn const QVector4D operator*(qreal factor, const QVector4D &vector) + \relates QVector4D + + Returns a copy of the given \a vector, multiplied by the given \a factor. + + \sa QVector4D::operator*=() +*/ + +/*! + \fn const QVector4D operator*(const QVector4D &vector, qreal factor) + \relates QVector4D + + Returns a copy of the given \a vector, multiplied by the given \a factor. + + \sa QVector4D::operator*=() +*/ + +/*! + \fn const QVector4D operator*(const QVector4D &v1, const QVector4D& v2) + \relates QVector4D + + Returns the vector consisting of the multiplication of the + components from \a v1 and \a v2. + + \sa QVector4D::operator*=() +*/ + +/*! + \fn const QVector4D operator-(const QVector4D &vector) + \relates QVector4D + \overload + + Returns a QVector4D object that is formed by changing the sign of + all three components of the given \a vector. + + Equivalent to \c {QVector4D(0,0,0,0) - vector}. +*/ + +/*! + \fn const QVector4D operator/(const QVector4D &vector, qreal divisor) + \relates QVector4D + + Returns the QVector4D object formed by dividing all four components of + the given \a vector by the given \a divisor. + + \sa QVector4D::operator/=() +*/ + +/*! + \fn bool qFuzzyCompare(const QVector4D& v1, const QVector4D& v2) + \relates QVector4D + + Returns true if \a v1 and \a v2 are equal, allowing for a small + fuzziness factor for floating-point comparisons; false otherwise. +*/ + +#ifndef QT_NO_VECTOR2D + +/*! + Returns the 2D vector form of this 4D vector, dropping the z and w coordinates. + + \sa toVector2DAffine(), toVector3D(), toPoint() +*/ +QVector2D QVector4D::toVector2D() const +{ + return QVector2D(xp, yp, 1); +} + +/*! + Returns the 2D vector form of this 4D vector, dividing the x and y + coordinates by the w coordinate and dropping the z coordinate. + Returns a null vector if w is zero. + + \sa toVector2D(), toVector3DAffine(), toPoint() +*/ +QVector2D QVector4D::toVector2DAffine() const +{ + if (qIsNull(wp)) + return QVector2D(); + return QVector2D(xp / wp, yp / wp, 1); +} + +#endif + +#ifndef QT_NO_VECTOR3D + +/*! + Returns the 3D vector form of this 4D vector, dropping the w coordinate. + + \sa toVector3DAffine(), toVector2D(), toPoint() +*/ +QVector3D QVector4D::toVector3D() const +{ + return QVector3D(xp, yp, zp, 1); +} + +/*! + Returns the 3D vector form of this 4D vector, dividing the x, y, and + z coordinates by the w coordinate. Returns a null vector if w is zero. + + \sa toVector3D(), toVector2DAffine(), toPoint() +*/ +QVector3D QVector4D::toVector3DAffine() const +{ + if (qIsNull(wp)) + return QVector3D(); + return QVector3D(xp / wp, yp / wp, zp / wp, 1); +} + +#endif + +/*! + \fn QPoint QVector4D::toPoint() const + + Returns the QPoint form of this 4D vector. + + \sa toPointF(), toVector2D() +*/ + +/*! + \fn QPointF QVector4D::toPointF() const + + Returns the QPointF form of this 4D vector. + + \sa toPoint(), toVector2D() +*/ + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<(QDebug dbg, const QVector4D &vector) +{ + dbg.nospace() << "QVector4D(" + << vector.x() << ", " << vector.y() << ", " + << vector.z() << ", " << vector.w() << ')'; + return dbg.space(); +} + +#endif + +#endif + +QT_END_NAMESPACE diff --git a/src/gui/math3d/qvector4d.h b/src/gui/math3d/qvector4d.h new file mode 100644 index 0000000..e38f4e5 --- /dev/null +++ b/src/gui/math3d/qvector4d.h @@ -0,0 +1,289 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QVECTOR4D_H +#define QVECTOR4D_H + +#include <QtCore/qpoint.h> +#include <QtCore/qmetatype.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QMatrix4x4; +class QVector2D; +class QVector3D; +class QQuaternion; + +#ifndef QT_NO_VECTOR4D + +class Q_GUI_EXPORT QVector4D +{ +public: + QVector4D(); + QVector4D(qreal xpos, qreal ypos, qreal zpos, qreal wpos); + explicit QVector4D(const QPoint& point); + explicit QVector4D(const QPointF& point); +#ifndef QT_NO_VECTOR2D + QVector4D(const QVector2D& vector); + QVector4D(const QVector2D& vector, qreal zpos, qreal wpos); +#endif +#ifndef QT_NO_VECTOR3D + QVector4D(const QVector3D& vector); + QVector4D(const QVector3D& vector, qreal wpos); +#endif + + bool isNull() const; + + qreal x() const; + qreal y() const; + qreal z() const; + qreal w() const; + + void setX(qreal x); + void setY(qreal y); + void setZ(qreal z); + void setW(qreal w); + + qreal length() const; + qreal lengthSquared() const; + + QVector4D normalized() const; + void normalize(); + + QVector4D &operator+=(const QVector4D &vector); + QVector4D &operator-=(const QVector4D &vector); + QVector4D &operator*=(qreal factor); + QVector4D &operator*=(const QVector4D &vector); + QVector4D &operator/=(qreal divisor); + + static qreal dotProduct(const QVector4D& v1, const QVector4D& v2); + + friend inline bool operator==(const QVector4D &v1, const QVector4D &v2); + friend inline bool operator!=(const QVector4D &v1, const QVector4D &v2); + friend inline const QVector4D operator+(const QVector4D &v1, const QVector4D &v2); + friend inline const QVector4D operator-(const QVector4D &v1, const QVector4D &v2); + friend inline const QVector4D operator*(qreal factor, const QVector4D &vector); + friend inline const QVector4D operator*(const QVector4D &vector, qreal factor); + friend inline const QVector4D operator*(const QVector4D &v1, const QVector4D& v2); + friend inline const QVector4D operator-(const QVector4D &vector); + friend inline const QVector4D operator/(const QVector4D &vector, qreal divisor); + + friend inline bool qFuzzyCompare(const QVector4D& v1, const QVector4D& v2); + +#ifndef QT_NO_VECTOR2D + QVector2D toVector2D() const; + QVector2D toVector2DAffine() const; +#endif +#ifndef QT_NO_VECTOR3D + QVector3D toVector3D() const; + QVector3D toVector3DAffine() const; +#endif + + QPoint toPoint() const; + QPointF toPointF() const; + +private: + float xp, yp, zp, wp; + + QVector4D(float xpos, float ypos, float zpos, float wpos, int dummy); + + friend class QVector2D; + friend class QVector3D; + friend class QQuaternion; + friend class QMatrix4x4; +#ifndef QT_NO_MATRIX4X4 + friend QVector4D operator*(const QVector4D& vector, const QMatrix4x4& matrix); + friend QVector4D operator*(const QMatrix4x4& matrix, const QVector4D& vector); +#endif +}; + +inline QVector4D::QVector4D() : xp(0.0f), yp(0.0f), zp(0.0f), wp(0.0f) {} + +inline QVector4D::QVector4D(qreal xpos, qreal ypos, qreal zpos, qreal wpos) : xp(xpos), yp(ypos), zp(zpos), wp(wpos) {} + +inline QVector4D::QVector4D(float xpos, float ypos, float zpos, float wpos, int) : xp(xpos), yp(ypos), zp(zpos), wp(wpos) {} + +inline QVector4D::QVector4D(const QPoint& point) : xp(point.x()), yp(point.y()), zp(0.0f), wp(0.0f) {} + +inline QVector4D::QVector4D(const QPointF& point) : xp(point.x()), yp(point.y()), zp(0.0f), wp(0.0f) {} + +inline bool QVector4D::isNull() const +{ + return qIsNull(xp) && qIsNull(yp) && qIsNull(zp) && qIsNull(wp); +} + +inline qreal QVector4D::x() const { return qreal(xp); } +inline qreal QVector4D::y() const { return qreal(yp); } +inline qreal QVector4D::z() const { return qreal(zp); } +inline qreal QVector4D::w() const { return qreal(wp); } + +inline void QVector4D::setX(qreal x) { xp = x; } +inline void QVector4D::setY(qreal y) { yp = y; } +inline void QVector4D::setZ(qreal z) { zp = z; } +inline void QVector4D::setW(qreal w) { wp = w; } + +inline QVector4D &QVector4D::operator+=(const QVector4D &vector) +{ + xp += vector.xp; + yp += vector.yp; + zp += vector.zp; + wp += vector.wp; + return *this; +} + +inline QVector4D &QVector4D::operator-=(const QVector4D &vector) +{ + xp -= vector.xp; + yp -= vector.yp; + zp -= vector.zp; + wp -= vector.wp; + return *this; +} + +inline QVector4D &QVector4D::operator*=(qreal factor) +{ + xp *= factor; + yp *= factor; + zp *= factor; + wp *= factor; + return *this; +} + +inline QVector4D &QVector4D::operator*=(const QVector4D &vector) +{ + xp *= vector.xp; + yp *= vector.yp; + zp *= vector.zp; + wp *= vector.wp; + return *this; +} + +inline QVector4D &QVector4D::operator/=(qreal divisor) +{ + xp /= divisor; + yp /= divisor; + zp /= divisor; + wp /= divisor; + return *this; +} + +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; +} + +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; +} + +inline const QVector4D operator+(const QVector4D &v1, const QVector4D &v2) +{ + return QVector4D(v1.xp + v2.xp, v1.yp + v2.yp, v1.zp + v2.zp, v1.wp + v2.wp, 1); +} + +inline const QVector4D operator-(const QVector4D &v1, const QVector4D &v2) +{ + return QVector4D(v1.xp - v2.xp, v1.yp - v2.yp, v1.zp - v2.zp, v1.wp - v2.wp, 1); +} + +inline const QVector4D operator*(qreal factor, const QVector4D &vector) +{ + return QVector4D(vector.xp * factor, vector.yp * factor, vector.zp * factor, vector.wp * factor, 1); +} + +inline const QVector4D operator*(const QVector4D &vector, qreal factor) +{ + return QVector4D(vector.xp * factor, vector.yp * factor, vector.zp * factor, vector.wp * factor, 1); +} + +inline const QVector4D operator*(const QVector4D &v1, const QVector4D& v2) +{ + return QVector4D(v1.xp * v2.xp, v1.yp * v2.yp, v1.zp * v2.zp, v1.wp * v2.wp, 1); +} + +inline const QVector4D operator-(const QVector4D &vector) +{ + return QVector4D(-vector.xp, -vector.yp, -vector.zp, -vector.wp, 1); +} + +inline const QVector4D operator/(const QVector4D &vector, qreal divisor) +{ + return QVector4D(vector.xp / divisor, vector.yp / divisor, vector.zp / divisor, vector.wp / divisor, 1); +} + +inline bool qFuzzyCompare(const QVector4D& v1, const QVector4D& v2) +{ + return qFuzzyCompare(v1.xp, v2.xp) && + qFuzzyCompare(v1.yp, v2.yp) && + qFuzzyCompare(v1.zp, v2.zp) && + qFuzzyCompare(v1.wp, v2.wp); +} + +inline QPoint QVector4D::toPoint() const +{ + return QPoint(qRound(xp), qRound(yp)); +} + +inline QPointF QVector4D::toPointF() const +{ + return QPointF(qreal(xp), qreal(yp)); +} + +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QVector4D &vector); +#endif + +#endif + +QT_END_NAMESPACE + +#ifndef QT_NO_VECTOR4D +Q_DECLARE_METATYPE(QVector4D) +#endif + +QT_END_HEADER + +#endif |