qt5base-lts/tests/auto/qmatrixnxn/tst_qmatrixnxn.cpp
Qt by Nokia 38be0d1383 Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you
want to look at revision history older than this, please refer to the
Qt Git wiki for how to use Git history grafting. At the time of
writing, this wiki is located here:

http://qt.gitorious.org/qt/pages/GitIntroductionWithQt

If you have already performed the grafting and you don't see any
history beyond this commit, try running "git log" with the "--follow"
argument.

Branched from the monolithic repo, Qt master branch, at commit
896db169ea224deb96c59ce8af800d019de63f12
2011-04-27 12:05:43 +02:00

3306 lines
94 KiB
C++

/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite 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 Technology Preview License Agreement accompanying
** this package.
**
** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QtTest>
#include <QtCore/qmath.h>
#include <QtGui/qmatrix4x4.h>
class tst_QMatrixNxN : public QObject
{
Q_OBJECT
public:
tst_QMatrixNxN() {}
~tst_QMatrixNxN() {}
private slots:
void create2x2();
void create3x3();
void create4x4();
void create4x3();
void isIdentity2x2();
void isIdentity3x3();
void isIdentity4x4();
void isIdentity4x3();
void compare2x2();
void compare3x3();
void compare4x4();
void compare4x3();
void transposed2x2();
void transposed3x3();
void transposed4x4();
void transposed4x3();
void add2x2_data();
void add2x2();
void add3x3_data();
void add3x3();
void add4x4_data();
void add4x4();
void add4x3_data();
void add4x3();
void subtract2x2_data();
void subtract2x2();
void subtract3x3_data();
void subtract3x3();
void subtract4x4_data();
void subtract4x4();
void subtract4x3_data();
void subtract4x3();
void multiply2x2_data();
void multiply2x2();
void multiply3x3_data();
void multiply3x3();
void multiply4x4_data();
void multiply4x4();
void multiply4x3_data();
void multiply4x3();
void multiplyFactor2x2_data();
void multiplyFactor2x2();
void multiplyFactor3x3_data();
void multiplyFactor3x3();
void multiplyFactor4x4_data();
void multiplyFactor4x4();
void multiplyFactor4x3_data();
void multiplyFactor4x3();
void divideFactor2x2_data();
void divideFactor2x2();
void divideFactor3x3_data();
void divideFactor3x3();
void divideFactor4x4_data();
void divideFactor4x4();
void divideFactor4x3_data();
void divideFactor4x3();
void negate2x2_data();
void negate2x2();
void negate3x3_data();
void negate3x3();
void negate4x4_data();
void negate4x4();
void negate4x3_data();
void negate4x3();
void inverted4x4_data();
void inverted4x4();
void orthonormalInverse4x4();
void scale4x4_data();
void scale4x4();
void translate4x4_data();
void translate4x4();
void rotate4x4_data();
void rotate4x4();
void normalMatrix_data();
void normalMatrix();
void optimizedTransforms();
void ortho();
void frustum();
void perspective();
void flipCoordinates();
void convertGeneric();
void optimize_data();
void optimize();
void columnsAndRows();
void convertQMatrix();
void convertQTransform();
void fill();
void mapRect_data();
void mapRect();
void mapVector_data();
void mapVector();
void properties();
void metaTypes();
private:
static void setMatrix(QMatrix2x2& m, const qreal *values);
static void setMatrixDirect(QMatrix2x2& m, const qreal *values);
static bool isSame(const QMatrix2x2& m, const qreal *values);
static bool isIdentity(const QMatrix2x2& m);
static void setMatrix(QMatrix3x3& m, const qreal *values);
static void setMatrixDirect(QMatrix3x3& m, const qreal *values);
static bool isSame(const QMatrix3x3& m, const qreal *values);
static bool isIdentity(const QMatrix3x3& m);
static void setMatrix(QMatrix4x4& m, const qreal *values);
static void setMatrixDirect(QMatrix4x4& m, const qreal *values);
static bool isSame(const QMatrix4x4& m, const qreal *values);
static bool isIdentity(const QMatrix4x4& m);
static void setMatrix(QMatrix4x3& m, const qreal *values);
static void setMatrixDirect(QMatrix4x3& m, const qreal *values);
static bool isSame(const QMatrix4x3& m, const qreal *values);
static bool isIdentity(const QMatrix4x3& m);
};
static const qreal nullValues2[] =
{0.0f, 0.0f,
0.0f, 0.0f};
static qreal const identityValues2[16] =
{1.0f, 0.0f,
0.0f, 1.0f};
static const qreal doubleIdentity2[] =
{2.0f, 0.0f,
0.0f, 2.0f};
static qreal const uniqueValues2[16] =
{1.0f, 2.0f,
5.0f, 6.0f};
static qreal const transposedValues2[16] =
{1.0f, 5.0f,
2.0f, 6.0f};
static const qreal nullValues3[] =
{0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f};
static qreal const identityValues3[16] =
{1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f};
static const qreal doubleIdentity3[] =
{2.0f, 0.0f, 0.0f,
0.0f, 2.0f, 0.0f,
0.0f, 0.0f, 2.0f};
static qreal const uniqueValues3[16] =
{1.0f, 2.0f, 3.0f,
5.0f, 6.0f, 7.0f,
9.0f, 10.0f, 11.0f};
static qreal const transposedValues3[16] =
{1.0f, 5.0f, 9.0f,
2.0f, 6.0f, 10.0f,
3.0f, 7.0f, 11.0f};
static const qreal nullValues4[] =
{0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f};
static qreal const identityValues4[16] =
{1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f};
static const qreal doubleIdentity4[] =
{2.0f, 0.0f, 0.0f, 0.0f,
0.0f, 2.0f, 0.0f, 0.0f,
0.0f, 0.0f, 2.0f, 0.0f,
0.0f, 0.0f, 0.0f, 2.0f};
static qreal const uniqueValues4[16] =
{1.0f, 2.0f, 3.0f, 4.0f,
5.0f, 6.0f, 7.0f, 8.0f,
9.0f, 10.0f, 11.0f, 12.0f,
13.0f, 14.0f, 15.0f, 16.0f};
static qreal const transposedValues4[16] =
{1.0f, 5.0f, 9.0f, 13.0f,
2.0f, 6.0f, 10.0f, 14.0f,
3.0f, 7.0f, 11.0f, 15.0f,
4.0f, 8.0f, 12.0f, 16.0f};
static const qreal nullValues4x3[] =
{0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f};
static qreal const identityValues4x3[12] =
{1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f};
static qreal const doubleIdentity4x3[12] =
{2.0f, 0.0f, 0.0f, 0.0f,
0.0f, 2.0f, 0.0f, 0.0f,
0.0f, 0.0f, 2.0f, 0.0f};
static qreal const uniqueValues4x3[12] =
{1.0f, 2.0f, 3.0f, 4.0f,
5.0f, 6.0f, 7.0f, 8.0f,
9.0f, 10.0f, 11.0f, 12.0f};
static qreal const transposedValues3x4[12] =
{1.0f, 5.0f, 9.0f,
2.0f, 6.0f, 10.0f,
3.0f, 7.0f, 11.0f,
4.0f, 8.0f, 12.0f};
// Set a matrix to a specified array of values, which are assumed
// to be in row-major order. This sets the values using floating-point.
void tst_QMatrixNxN::setMatrix(QMatrix2x2& m, const qreal *values)
{
for (int row = 0; row < 2; ++row)
for (int col = 0; col < 2; ++col)
m(row, col) = values[row * 2 + col];
}
void tst_QMatrixNxN::setMatrix(QMatrix3x3& m, const qreal *values)
{
for (int row = 0; row < 3; ++row)
for (int col = 0; col < 3; ++col)
m(row, col) = values[row * 3 + col];
}
void tst_QMatrixNxN::setMatrix(QMatrix4x4& m, const qreal *values)
{
for (int row = 0; row < 4; ++row)
for (int col = 0; col < 4; ++col)
m(row, col) = values[row * 4 + col];
}
void tst_QMatrixNxN::setMatrix(QMatrix4x3& m, const qreal *values)
{
for (int row = 0; row < 3; ++row)
for (int col = 0; col < 4; ++col)
m(row, col) = values[row * 4 + col];
}
// Set a matrix to a specified array of values, which are assumed
// to be in row-major order. This sets the values directly into
// the internal data() array.
void tst_QMatrixNxN::setMatrixDirect(QMatrix2x2& m, const qreal *values)
{
qreal *data = m.data();
for (int row = 0; row < 2; ++row) {
for (int col = 0; col < 2; ++col) {
data[row + col * 2] = values[row * 2 + col];
}
}
}
void tst_QMatrixNxN::setMatrixDirect(QMatrix3x3& m, const qreal *values)
{
qreal *data = m.data();
for (int row = 0; row < 3; ++row) {
for (int col = 0; col < 3; ++col) {
data[row + col * 3] = values[row * 3 + col];
}
}
}
void tst_QMatrixNxN::setMatrixDirect(QMatrix4x4& m, const qreal *values)
{
qreal *data = m.data();
for (int row = 0; row < 4; ++row) {
for (int col = 0; col < 4; ++col) {
data[row + col * 4] = values[row * 4 + col];
}
}
}
void tst_QMatrixNxN::setMatrixDirect(QMatrix4x3& m, const qreal *values)
{
qreal *data = m.data();
for (int row = 0; row < 3; ++row) {
for (int col = 0; col < 4; ++col) {
data[row + col * 3] = values[row * 4 + col];
}
}
}
// QVector2/3/4D use float internally, which can sometimes lead
// to precision issues when converting to and from qreal during
// operations involving QMatrix4x4. This fuzzy compare is slightly
// "fuzzier" than the default qFuzzyCompare for qreal to compensate.
static bool fuzzyCompare(qreal x, qreal y)
{
return qFuzzyIsNull((float)(x - y));
}
static bool fuzzyCompare(const QVector3D &v1, const QVector3D &v2)
{
if (!fuzzyCompare(v1.x(), v2.x()))
return false;
if (!fuzzyCompare(v1.y(), v2.y()))
return false;
if (!fuzzyCompare(v1.z(), v2.z()))
return false;
return true;
}
// Determine if a matrix is the same as a specified array of values.
// The values are assumed to be specified in row-major order.
bool tst_QMatrixNxN::isSame(const QMatrix2x2& m, const qreal *values)
{
const qreal *mv = m.constData();
for (int row = 0; row < 2; ++row) {
for (int col = 0; col < 2; ++col) {
// Check the values using the operator() function.
if (!fuzzyCompare(m(row, col), values[row * 2 + col])) {
qDebug() << "floating-point failure at" << row << col << "actual =" << m(row, col) << "expected =" << values[row * 2 + col];
return false;
}
// Check the values using direct access, which verifies that the values
// are stored internally in column-major order.
if (!fuzzyCompare(mv[col * 2 + row], values[row * 2 + col])) {
qDebug() << "column floating-point failure at" << row << col << "actual =" << mv[col * 2 + row] << "expected =" << values[row * 2 + col];
return false;
}
}
}
return true;
}
bool tst_QMatrixNxN::isSame(const QMatrix3x3& m, const qreal *values)
{
const qreal *mv = m.constData();
for (int row = 0; row < 3; ++row) {
for (int col = 0; col < 3; ++col) {
// Check the values using the operator() access function.
if (!fuzzyCompare(m(row, col), values[row * 3 + col])) {
qDebug() << "floating-point failure at" << row << col << "actual =" << m(row, col) << "expected =" << values[row * 3 + col];
return false;
}
// Check the values using direct access, which verifies that the values
// are stored internally in column-major order.
if (!fuzzyCompare(mv[col * 3 + row], values[row * 3 + col])) {
qDebug() << "column floating-point failure at" << row << col << "actual =" << mv[col * 3 + row] << "expected =" << values[row * 3 + col];
return false;
}
}
}
return true;
}
bool tst_QMatrixNxN::isSame(const QMatrix4x4& m, const qreal *values)
{
const qreal *mv = m.constData();
for (int row = 0; row < 4; ++row) {
for (int col = 0; col < 4; ++col) {
// Check the values using the operator() access function.
if (!fuzzyCompare(m(row, col), values[row * 4 + col])) {
qDebug() << "floating-point failure at" << row << col << "actual =" << m(row, col) << "expected =" << values[row * 4 + col];
return false;
}
// Check the values using direct access, which verifies that the values
// are stored internally in column-major order.
if (!fuzzyCompare(mv[col * 4 + row], values[row * 4 + col])) {
qDebug() << "column floating-point failure at" << row << col << "actual =" << mv[col * 4 + row] << "expected =" << values[row * 4 + col];
return false;
}
}
}
return true;
}
bool tst_QMatrixNxN::isSame(const QMatrix4x3& m, const qreal *values)
{
const qreal *mv = m.constData();
for (int row = 0; row < 3; ++row) {
for (int col = 0; col < 4; ++col) {
// Check the values using the operator() access function.
if (!fuzzyCompare(m(row, col), values[row * 4 + col])) {
qDebug() << "floating-point failure at" << row << col << "actual =" << m(row, col) << "expected =" << values[row * 4 + col];
return false;
}
// Check the values using direct access, which verifies that the values
// are stored internally in column-major order.
if (!fuzzyCompare(mv[col * 3 + row], values[row * 4 + col])) {
qDebug() << "column floating-point failure at" << row << col << "actual =" << mv[col * 3 + row] << "expected =" << values[row * 4 + col];
return false;
}
}
}
return true;
}
// Determine if a matrix is the identity.
bool tst_QMatrixNxN::isIdentity(const QMatrix2x2& m)
{
return isSame(m, identityValues2);
}
bool tst_QMatrixNxN::isIdentity(const QMatrix3x3& m)
{
return isSame(m, identityValues3);
}
bool tst_QMatrixNxN::isIdentity(const QMatrix4x4& m)
{
return isSame(m, identityValues4);
}
bool tst_QMatrixNxN::isIdentity(const QMatrix4x3& m)
{
return isSame(m, identityValues4x3);
}
// Test the creation of QMatrix2x2 objects in various ways:
// construct, copy, and modify.
void tst_QMatrixNxN::create2x2()
{
QMatrix2x2 m1;
QVERIFY(isIdentity(m1));
QVERIFY(m1.isIdentity());
QMatrix2x2 m2;
setMatrix(m2, uniqueValues2);
QVERIFY(isSame(m2, uniqueValues2));
QVERIFY(!m2.isIdentity());
QMatrix2x2 m3;
setMatrixDirect(m3, uniqueValues2);
QVERIFY(isSame(m3, uniqueValues2));
QMatrix2x2 m4(m3);
QVERIFY(isSame(m4, uniqueValues2));
QMatrix2x2 m5;
m5 = m3;
QVERIFY(isSame(m5, uniqueValues2));
m5.setToIdentity();
QVERIFY(isIdentity(m5));
QMatrix2x2 m6(uniqueValues2);
QVERIFY(isSame(m6, uniqueValues2));
qreal vals[4];
m6.copyDataTo(vals);
for (int index = 0; index < 4; ++index)
QCOMPARE(vals[index], uniqueValues2[index]);
}
// Test the creation of QMatrix3x3 objects in various ways:
// construct, copy, and modify.
void tst_QMatrixNxN::create3x3()
{
QMatrix3x3 m1;
QVERIFY(isIdentity(m1));
QVERIFY(m1.isIdentity());
QMatrix3x3 m2;
setMatrix(m2, uniqueValues3);
QVERIFY(isSame(m2, uniqueValues3));
QVERIFY(!m2.isIdentity());
QMatrix3x3 m3;
setMatrixDirect(m3, uniqueValues3);
QVERIFY(isSame(m3, uniqueValues3));
QMatrix3x3 m4(m3);
QVERIFY(isSame(m4, uniqueValues3));
QMatrix3x3 m5;
m5 = m3;
QVERIFY(isSame(m5, uniqueValues3));
m5.setToIdentity();
QVERIFY(isIdentity(m5));
QMatrix3x3 m6(uniqueValues3);
QVERIFY(isSame(m6, uniqueValues3));
qreal vals[9];
m6.copyDataTo(vals);
for (int index = 0; index < 9; ++index)
QCOMPARE(vals[index], uniqueValues3[index]);
}
// Test the creation of QMatrix4x4 objects in various ways:
// construct, copy, and modify.
void tst_QMatrixNxN::create4x4()
{
QMatrix4x4 m1;
QVERIFY(isIdentity(m1));
QVERIFY(m1.isIdentity());
QMatrix4x4 m2;
setMatrix(m2, uniqueValues4);
QVERIFY(isSame(m2, uniqueValues4));
QVERIFY(!m2.isIdentity());
QMatrix4x4 m3;
setMatrixDirect(m3, uniqueValues4);
QVERIFY(isSame(m3, uniqueValues4));
QMatrix4x4 m4(m3);
QVERIFY(isSame(m4, uniqueValues4));
QMatrix4x4 m5;
m5 = m3;
QVERIFY(isSame(m5, uniqueValues4));
m5.setToIdentity();
QVERIFY(isIdentity(m5));
QMatrix4x4 m6(uniqueValues4);
QVERIFY(isSame(m6, uniqueValues4));
qreal vals[16];
m6.copyDataTo(vals);
for (int index = 0; index < 16; ++index)
QCOMPARE(vals[index], uniqueValues4[index]);
QMatrix4x4 m8
(uniqueValues4[0], uniqueValues4[1], uniqueValues4[2], uniqueValues4[3],
uniqueValues4[4], uniqueValues4[5], uniqueValues4[6], uniqueValues4[7],
uniqueValues4[8], uniqueValues4[9], uniqueValues4[10], uniqueValues4[11],
uniqueValues4[12], uniqueValues4[13], uniqueValues4[14], uniqueValues4[15]);
QVERIFY(isSame(m8, uniqueValues4));
}
// Test the creation of QMatrix4x3 objects in various ways:
// construct, copy, and modify.
void tst_QMatrixNxN::create4x3()
{
QMatrix4x3 m1;
QVERIFY(isIdentity(m1));
QVERIFY(m1.isIdentity());
QMatrix4x3 m2;
setMatrix(m2, uniqueValues4x3);
QVERIFY(isSame(m2, uniqueValues4x3));
QVERIFY(!m2.isIdentity());
QMatrix4x3 m3;
setMatrixDirect(m3, uniqueValues4x3);
QVERIFY(isSame(m3, uniqueValues4x3));
QMatrix4x3 m4(m3);
QVERIFY(isSame(m4, uniqueValues4x3));
QMatrix4x3 m5;
m5 = m3;
QVERIFY(isSame(m5, uniqueValues4x3));
m5.setToIdentity();
QVERIFY(isIdentity(m5));
QMatrix4x3 m6(uniqueValues4x3);
QVERIFY(isSame(m6, uniqueValues4x3));
qreal vals[12];
m6.copyDataTo(vals);
for (int index = 0; index < 12; ++index)
QCOMPARE(vals[index], uniqueValues4x3[index]);
}
// Test isIdentity() for 2x2 matrices.
void tst_QMatrixNxN::isIdentity2x2()
{
for (int i = 0; i < 2 * 2; ++i) {
QMatrix2x2 m;
QVERIFY(m.isIdentity());
m.data()[i] = 42.0f;
QVERIFY(!m.isIdentity());
}
}
// Test isIdentity() for 3x3 matrices.
void tst_QMatrixNxN::isIdentity3x3()
{
for (int i = 0; i < 3 * 3; ++i) {
QMatrix3x3 m;
QVERIFY(m.isIdentity());
m.data()[i] = 42.0f;
QVERIFY(!m.isIdentity());
}
}
// Test isIdentity() for 4x4 matrices.
void tst_QMatrixNxN::isIdentity4x4()
{
for (int i = 0; i < 4 * 4; ++i) {
QMatrix4x4 m;
QVERIFY(m.isIdentity());
m.data()[i] = 42.0f;
QVERIFY(!m.isIdentity());
}
// Force the "Identity" flag bit to be lost and check again.
QMatrix4x4 m2;
m2.data()[0] = 1.0f;
QVERIFY(m2.isIdentity());
}
// Test isIdentity() for 4x3 matrices.
void tst_QMatrixNxN::isIdentity4x3()
{
for (int i = 0; i < 4 * 3; ++i) {
QMatrix4x3 m;
QVERIFY(m.isIdentity());
m.data()[i] = 42.0f;
QVERIFY(!m.isIdentity());
}
}
// Test 2x2 matrix comparisons.
void tst_QMatrixNxN::compare2x2()
{
QMatrix2x2 m1(uniqueValues2);
QMatrix2x2 m2(uniqueValues2);
QMatrix2x2 m3(transposedValues2);
QVERIFY(m1 == m2);
QVERIFY(!(m1 != m2));
QVERIFY(m1 != m3);
QVERIFY(!(m1 == m3));
}
// Test 3x3 matrix comparisons.
void tst_QMatrixNxN::compare3x3()
{
QMatrix3x3 m1(uniqueValues3);
QMatrix3x3 m2(uniqueValues3);
QMatrix3x3 m3(transposedValues3);
QVERIFY(m1 == m2);
QVERIFY(!(m1 != m2));
QVERIFY(m1 != m3);
QVERIFY(!(m1 == m3));
}
// Test 4x4 matrix comparisons.
void tst_QMatrixNxN::compare4x4()
{
QMatrix4x4 m1(uniqueValues4);
QMatrix4x4 m2(uniqueValues4);
QMatrix4x4 m3(transposedValues4);
QVERIFY(m1 == m2);
QVERIFY(!(m1 != m2));
QVERIFY(m1 != m3);
QVERIFY(!(m1 == m3));
}
// Test 4x3 matrix comparisons.
void tst_QMatrixNxN::compare4x3()
{
QMatrix4x3 m1(uniqueValues4x3);
QMatrix4x3 m2(uniqueValues4x3);
QMatrix4x3 m3(transposedValues3x4);
QVERIFY(m1 == m2);
QVERIFY(!(m1 != m2));
QVERIFY(m1 != m3);
QVERIFY(!(m1 == m3));
}
// Test matrix 2x2 transpose operations.
void tst_QMatrixNxN::transposed2x2()
{
// Transposing the identity should result in the identity.
QMatrix2x2 m1;
QMatrix2x2 m2 = m1.transposed();
QVERIFY(isIdentity(m2));
// Transpose a more interesting matrix that allows us to track
// exactly where each source element ends up.
QMatrix2x2 m3(uniqueValues2);
QMatrix2x2 m4 = m3.transposed();
QVERIFY(isSame(m4, transposedValues2));
// Transpose in-place, just to check that the compiler is sane.
m3 = m3.transposed();
QVERIFY(isSame(m3, transposedValues2));
}
// Test matrix 3x3 transpose operations.
void tst_QMatrixNxN::transposed3x3()
{
// Transposing the identity should result in the identity.
QMatrix3x3 m1;
QMatrix3x3 m2 = m1.transposed();
QVERIFY(isIdentity(m2));
// Transpose a more interesting matrix that allows us to track
// exactly where each source element ends up.
QMatrix3x3 m3(uniqueValues3);
QMatrix3x3 m4 = m3.transposed();
QVERIFY(isSame(m4, transposedValues3));
// Transpose in-place, just to check that the compiler is sane.
m3 = m3.transposed();
QVERIFY(isSame(m3, transposedValues3));
}
// Test matrix 4x4 transpose operations.
void tst_QMatrixNxN::transposed4x4()
{
// Transposing the identity should result in the identity.
QMatrix4x4 m1;
QMatrix4x4 m2 = m1.transposed();
QVERIFY(isIdentity(m2));
// Transpose a more interesting matrix that allows us to track
// exactly where each source element ends up.
QMatrix4x4 m3(uniqueValues4);
QMatrix4x4 m4 = m3.transposed();
QVERIFY(isSame(m4, transposedValues4));
// Transpose in-place, just to check that the compiler is sane.
m3 = m3.transposed();
QVERIFY(isSame(m3, transposedValues4));
}
// Test matrix 4x3 transpose operations.
void tst_QMatrixNxN::transposed4x3()
{
QMatrix4x3 m3(uniqueValues4x3);
QMatrix3x4 m4 = m3.transposed();
qreal values[12];
m4.copyDataTo(values);
for (int index = 0; index < 12; ++index)
QCOMPARE(values[index], transposedValues3x4[index]);
}
// Test matrix addition for 2x2 matrices.
void tst_QMatrixNxN::add2x2_data()
{
QTest::addColumn<void *>("m1Values");
QTest::addColumn<void *>("m2Values");
QTest::addColumn<void *>("m3Values");
QTest::newRow("null")
<< (void *)nullValues2 << (void *)nullValues2 << (void *)nullValues2;
QTest::newRow("identity/null")
<< (void *)identityValues2 << (void *)nullValues2 << (void *)identityValues2;
QTest::newRow("identity/identity")
<< (void *)identityValues2 << (void *)identityValues2 << (void *)doubleIdentity2;
static qreal const sumValues[16] =
{2.0f, 7.0f,
7.0f, 12.0f};
QTest::newRow("unique")
<< (void *)uniqueValues2 << (void *)transposedValues2 << (void *)sumValues;
}
void tst_QMatrixNxN::add2x2()
{
QFETCH(void *, m1Values);
QFETCH(void *, m2Values);
QFETCH(void *, m3Values);
QMatrix2x2 m1((const qreal *)m1Values);
QMatrix2x2 m2((const qreal *)m2Values);
QMatrix2x2 m4(m1);
m4 += m2;
QVERIFY(isSame(m4, (const qreal *)m3Values));
QMatrix2x2 m5;
m5 = m1 + m2;
QVERIFY(isSame(m5, (const qreal *)m3Values));
}
// Test matrix addition for 3x3 matrices.
void tst_QMatrixNxN::add3x3_data()
{
QTest::addColumn<void *>("m1Values");
QTest::addColumn<void *>("m2Values");
QTest::addColumn<void *>("m3Values");
QTest::newRow("null")
<< (void *)nullValues3 << (void *)nullValues3 << (void *)nullValues3;
QTest::newRow("identity/null")
<< (void *)identityValues3 << (void *)nullValues3 << (void *)identityValues3;
QTest::newRow("identity/identity")
<< (void *)identityValues3 << (void *)identityValues3 << (void *)doubleIdentity3;
static qreal const sumValues[16] =
{2.0f, 7.0f, 12.0f,
7.0f, 12.0f, 17.0f,
12.0f, 17.0f, 22.0f};
QTest::newRow("unique")
<< (void *)uniqueValues3 << (void *)transposedValues3 << (void *)sumValues;
}
void tst_QMatrixNxN::add3x3()
{
QFETCH(void *, m1Values);
QFETCH(void *, m2Values);
QFETCH(void *, m3Values);
QMatrix3x3 m1((const qreal *)m1Values);
QMatrix3x3 m2((const qreal *)m2Values);
QMatrix3x3 m4(m1);
m4 += m2;
QVERIFY(isSame(m4, (const qreal *)m3Values));
QMatrix3x3 m5;
m5 = m1 + m2;
QVERIFY(isSame(m5, (const qreal *)m3Values));
}
// Test matrix addition for 4x4 matrices.
void tst_QMatrixNxN::add4x4_data()
{
QTest::addColumn<void *>("m1Values");
QTest::addColumn<void *>("m2Values");
QTest::addColumn<void *>("m3Values");
QTest::newRow("null")
<< (void *)nullValues4 << (void *)nullValues4 << (void *)nullValues4;
QTest::newRow("identity/null")
<< (void *)identityValues4 << (void *)nullValues4 << (void *)identityValues4;
QTest::newRow("identity/identity")
<< (void *)identityValues4 << (void *)identityValues4 << (void *)doubleIdentity4;
static qreal const sumValues[16] =
{2.0f, 7.0f, 12.0f, 17.0f,
7.0f, 12.0f, 17.0f, 22.0f,
12.0f, 17.0f, 22.0f, 27.0f,
17.0f, 22.0f, 27.0f, 32.0f};
QTest::newRow("unique")
<< (void *)uniqueValues4 << (void *)transposedValues4 << (void *)sumValues;
}
void tst_QMatrixNxN::add4x4()
{
QFETCH(void *, m1Values);
QFETCH(void *, m2Values);
QFETCH(void *, m3Values);
QMatrix4x4 m1((const qreal *)m1Values);
QMatrix4x4 m2((const qreal *)m2Values);
QMatrix4x4 m4(m1);
m4 += m2;
QVERIFY(isSame(m4, (const qreal *)m3Values));
QMatrix4x4 m5;
m5 = m1 + m2;
QVERIFY(isSame(m5, (const qreal *)m3Values));
}
// Test matrix addition for 4x3 matrices.
void tst_QMatrixNxN::add4x3_data()
{
QTest::addColumn<void *>("m1Values");
QTest::addColumn<void *>("m2Values");
QTest::addColumn<void *>("m3Values");
QTest::newRow("null")
<< (void *)nullValues4x3 << (void *)nullValues4x3 << (void *)nullValues4x3;
QTest::newRow("identity/null")
<< (void *)identityValues4x3 << (void *)nullValues4x3 << (void *)identityValues4x3;
QTest::newRow("identity/identity")
<< (void *)identityValues4x3 << (void *)identityValues4x3 << (void *)doubleIdentity4x3;
static qreal const sumValues[16] =
{2.0f, 7.0f, 12.0f, 6.0f,
11.0f, 16.0f, 10.0f, 15.0f,
20.0f, 14.0f, 19.0f, 24.0f};
QTest::newRow("unique")
<< (void *)uniqueValues4x3 << (void *)transposedValues3x4 << (void *)sumValues;
}
void tst_QMatrixNxN::add4x3()
{
QFETCH(void *, m1Values);
QFETCH(void *, m2Values);
QFETCH(void *, m3Values);
QMatrix4x3 m1((const qreal *)m1Values);
QMatrix4x3 m2((const qreal *)m2Values);
QMatrix4x3 m4(m1);
m4 += m2;
QVERIFY(isSame(m4, (const qreal *)m3Values));
QMatrix4x3 m5;
m5 = m1 + m2;
QVERIFY(isSame(m5, (const qreal *)m3Values));
}
// Test matrix subtraction for 2x2 matrices.
void tst_QMatrixNxN::subtract2x2_data()
{
// Use the same test cases as the add test.
add2x2_data();
}
void tst_QMatrixNxN::subtract2x2()
{
QFETCH(void *, m1Values);
QFETCH(void *, m2Values);
QFETCH(void *, m3Values);
QMatrix2x2 m1((const qreal *)m1Values);
QMatrix2x2 m2((const qreal *)m2Values);
QMatrix2x2 m3((const qreal *)m3Values);
QMatrix2x2 m4(m3);
m4 -= m1;
QVERIFY(isSame(m4, (const qreal *)m2Values));
QMatrix2x2 m5;
m5 = m3 - m1;
QVERIFY(isSame(m5, (const qreal *)m2Values));
QMatrix2x2 m6(m3);
m6 -= m2;
QVERIFY(isSame(m6, (const qreal *)m1Values));
QMatrix2x2 m7;
m7 = m3 - m2;
QVERIFY(isSame(m7, (const qreal *)m1Values));
}
// Test matrix subtraction for 3x3 matrices.
void tst_QMatrixNxN::subtract3x3_data()
{
// Use the same test cases as the add test.
add3x3_data();
}
void tst_QMatrixNxN::subtract3x3()
{
QFETCH(void *, m1Values);
QFETCH(void *, m2Values);
QFETCH(void *, m3Values);
QMatrix3x3 m1((const qreal *)m1Values);
QMatrix3x3 m2((const qreal *)m2Values);
QMatrix3x3 m3((const qreal *)m3Values);
QMatrix3x3 m4(m3);
m4 -= m1;
QVERIFY(isSame(m4, (const qreal *)m2Values));
QMatrix3x3 m5;
m5 = m3 - m1;
QVERIFY(isSame(m5, (const qreal *)m2Values));
QMatrix3x3 m6(m3);
m6 -= m2;
QVERIFY(isSame(m6, (const qreal *)m1Values));
QMatrix3x3 m7;
m7 = m3 - m2;
QVERIFY(isSame(m7, (const qreal *)m1Values));
}
// Test matrix subtraction for 4x4 matrices.
void tst_QMatrixNxN::subtract4x4_data()
{
// Use the same test cases as the add test.
add4x4_data();
}
void tst_QMatrixNxN::subtract4x4()
{
QFETCH(void *, m1Values);
QFETCH(void *, m2Values);
QFETCH(void *, m3Values);
QMatrix4x4 m1((const qreal *)m1Values);
QMatrix4x4 m2((const qreal *)m2Values);
QMatrix4x4 m3((const qreal *)m3Values);
QMatrix4x4 m4(m3);
m4 -= m1;
QVERIFY(isSame(m4, (const qreal *)m2Values));
QMatrix4x4 m5;
m5 = m3 - m1;
QVERIFY(isSame(m5, (const qreal *)m2Values));
QMatrix4x4 m6(m3);
m6 -= m2;
QVERIFY(isSame(m6, (const qreal *)m1Values));
QMatrix4x4 m7;
m7 = m3 - m2;
QVERIFY(isSame(m7, (const qreal *)m1Values));
}
// Test matrix subtraction for 4x3 matrices.
void tst_QMatrixNxN::subtract4x3_data()
{
// Use the same test cases as the add test.
add4x3_data();
}
void tst_QMatrixNxN::subtract4x3()
{
QFETCH(void *, m1Values);
QFETCH(void *, m2Values);
QFETCH(void *, m3Values);
QMatrix4x3 m1((const qreal *)m1Values);
QMatrix4x3 m2((const qreal *)m2Values);
QMatrix4x3 m3((const qreal *)m3Values);
QMatrix4x3 m4(m3);
m4 -= m1;
QVERIFY(isSame(m4, (const qreal *)m2Values));
QMatrix4x3 m5;
m5 = m3 - m1;
QVERIFY(isSame(m5, (const qreal *)m2Values));
QMatrix4x3 m6(m3);
m6 -= m2;
QVERIFY(isSame(m6, (const qreal *)m1Values));
QMatrix4x3 m7;
m7 = m3 - m2;
QVERIFY(isSame(m7, (const qreal *)m1Values));
}
// Test matrix multiplication for 2x2 matrices.
void tst_QMatrixNxN::multiply2x2_data()
{
QTest::addColumn<void *>("m1Values");
QTest::addColumn<void *>("m2Values");
QTest::addColumn<void *>("m3Values");
QTest::newRow("null")
<< (void *)nullValues2 << (void *)nullValues2 << (void *)nullValues2;
QTest::newRow("null/unique")
<< (void *)nullValues2 << (void *)uniqueValues2 << (void *)nullValues2;
QTest::newRow("unique/null")
<< (void *)uniqueValues2 << (void *)nullValues2 << (void *)nullValues2;
QTest::newRow("unique/identity")
<< (void *)uniqueValues2 << (void *)identityValues2 << (void *)uniqueValues2;
QTest::newRow("identity/unique")
<< (void *)identityValues2 << (void *)uniqueValues2 << (void *)uniqueValues2;
static qreal uniqueResult[4];
for (int row = 0; row < 2; ++row) {
for (int col = 0; col < 2; ++col) {
qreal sum = 0.0f;
for (int j = 0; j < 2; ++j)
sum += uniqueValues2[row * 2 + j] * transposedValues2[j * 2 + col];
uniqueResult[row * 2 + col] = sum;
}
}
QTest::newRow("unique/transposed")
<< (void *)uniqueValues2 << (void *)transposedValues2 << (void *)uniqueResult;
}
void tst_QMatrixNxN::multiply2x2()
{
QFETCH(void *, m1Values);
QFETCH(void *, m2Values);
QFETCH(void *, m3Values);
QMatrix2x2 m1((const qreal *)m1Values);
QMatrix2x2 m2((const qreal *)m2Values);
QMatrix2x2 m5;
m5 = m1 * m2;
QVERIFY(isSame(m5, (const qreal *)m3Values));
}
// Test matrix multiplication for 3x3 matrices.
void tst_QMatrixNxN::multiply3x3_data()
{
QTest::addColumn<void *>("m1Values");
QTest::addColumn<void *>("m2Values");
QTest::addColumn<void *>("m3Values");
QTest::newRow("null")
<< (void *)nullValues3 << (void *)nullValues3 << (void *)nullValues3;
QTest::newRow("null/unique")
<< (void *)nullValues3 << (void *)uniqueValues3 << (void *)nullValues3;
QTest::newRow("unique/null")
<< (void *)uniqueValues3 << (void *)nullValues3 << (void *)nullValues3;
QTest::newRow("unique/identity")
<< (void *)uniqueValues3 << (void *)identityValues3 << (void *)uniqueValues3;
QTest::newRow("identity/unique")
<< (void *)identityValues3 << (void *)uniqueValues3 << (void *)uniqueValues3;
static qreal uniqueResult[9];
for (int row = 0; row < 3; ++row) {
for (int col = 0; col < 3; ++col) {
qreal sum = 0.0f;
for (int j = 0; j < 3; ++j)
sum += uniqueValues3[row * 3 + j] * transposedValues3[j * 3 + col];
uniqueResult[row * 3 + col] = sum;
}
}
QTest::newRow("unique/transposed")
<< (void *)uniqueValues3 << (void *)transposedValues3 << (void *)uniqueResult;
}
void tst_QMatrixNxN::multiply3x3()
{
QFETCH(void *, m1Values);
QFETCH(void *, m2Values);
QFETCH(void *, m3Values);
QMatrix3x3 m1((const qreal *)m1Values);
QMatrix3x3 m2((const qreal *)m2Values);
QMatrix3x3 m5;
m5 = m1 * m2;
QVERIFY(isSame(m5, (const qreal *)m3Values));
}
// Test matrix multiplication for 4x4 matrices.
void tst_QMatrixNxN::multiply4x4_data()
{
QTest::addColumn<void *>("m1Values");
QTest::addColumn<void *>("m2Values");
QTest::addColumn<void *>("m3Values");
QTest::newRow("null")
<< (void *)nullValues4 << (void *)nullValues4 << (void *)nullValues4;
QTest::newRow("null/unique")
<< (void *)nullValues4 << (void *)uniqueValues4 << (void *)nullValues4;
QTest::newRow("unique/null")
<< (void *)uniqueValues4 << (void *)nullValues4 << (void *)nullValues4;
QTest::newRow("unique/identity")
<< (void *)uniqueValues4 << (void *)identityValues4 << (void *)uniqueValues4;
QTest::newRow("identity/unique")
<< (void *)identityValues4 << (void *)uniqueValues4 << (void *)uniqueValues4;
static qreal uniqueResult[16];
for (int row = 0; row < 4; ++row) {
for (int col = 0; col < 4; ++col) {
qreal sum = 0.0f;
for (int j = 0; j < 4; ++j)
sum += uniqueValues4[row * 4 + j] * transposedValues4[j * 4 + col];
uniqueResult[row * 4 + col] = sum;
}
}
QTest::newRow("unique/transposed")
<< (void *)uniqueValues4 << (void *)transposedValues4 << (void *)uniqueResult;
}
void tst_QMatrixNxN::multiply4x4()
{
QFETCH(void *, m1Values);
QFETCH(void *, m2Values);
QFETCH(void *, m3Values);
QMatrix4x4 m1((const qreal *)m1Values);
QMatrix4x4 m2((const qreal *)m2Values);
QMatrix4x4 m4;
m4 = m1;
m4 *= m2;
QVERIFY(isSame(m4, (const qreal *)m3Values));
QMatrix4x4 m5;
m5 = m1 * m2;
QVERIFY(isSame(m5, (const qreal *)m3Values));
}
// Test matrix multiplication for 4x3 matrices.
void tst_QMatrixNxN::multiply4x3_data()
{
QTest::addColumn<void *>("m1Values");
QTest::addColumn<void *>("m2Values");
QTest::addColumn<void *>("m3Values");
QTest::newRow("null")
<< (void *)nullValues4x3 << (void *)nullValues4x3 << (void *)nullValues3;
QTest::newRow("null/unique")
<< (void *)nullValues4x3 << (void *)uniqueValues4x3 << (void *)nullValues3;
QTest::newRow("unique/null")
<< (void *)uniqueValues4x3 << (void *)nullValues4x3 << (void *)nullValues3;
static qreal uniqueResult[9];
for (int row = 0; row < 3; ++row) {
for (int col = 0; col < 3; ++col) {
qreal sum = 0.0f;
for (int j = 0; j < 4; ++j)
sum += uniqueValues4x3[row * 4 + j] * transposedValues3x4[j * 3 + col];
uniqueResult[row * 3 + col] = sum;
}
}
QTest::newRow("unique/transposed")
<< (void *)uniqueValues4x3 << (void *)transposedValues3x4 << (void *)uniqueResult;
}
void tst_QMatrixNxN::multiply4x3()
{
QFETCH(void *, m1Values);
QFETCH(void *, m2Values);
QFETCH(void *, m3Values);
QMatrix4x3 m1((const qreal *)m1Values);
QMatrix3x4 m2((const qreal *)m2Values);
QGenericMatrix<3, 3, qreal> m4;
m4 = m1 * m2;
qreal values[9];
m4.copyDataTo(values);
for (int index = 0; index < 9; ++index)
QCOMPARE(values[index], ((const qreal *)m3Values)[index]);
}
// Test matrix multiplication by a factor for 2x2 matrices.
void tst_QMatrixNxN::multiplyFactor2x2_data()
{
QTest::addColumn<void *>("m1Values");
QTest::addColumn<qreal>("factor");
QTest::addColumn<void *>("m2Values");
QTest::newRow("null")
<< (void *)nullValues2 << (qreal)1.0f << (void *)nullValues2;
QTest::newRow("double identity")
<< (void *)identityValues2 << (qreal)2.0f << (void *)doubleIdentity2;
static qreal const values[16] =
{1.0f, 2.0f,
5.0f, 6.0f};
static qreal const doubleValues[16] =
{2.0f, 4.0f,
10.0f, 12.0f};
static qreal const negDoubleValues[16] =
{-2.0f, -4.0f,
-10.0f, -12.0f};
QTest::newRow("unique")
<< (void *)values << (qreal)2.0f << (void *)doubleValues;
QTest::newRow("neg")
<< (void *)values << (qreal)-2.0f << (void *)negDoubleValues;
QTest::newRow("zero")
<< (void *)values << (qreal)0.0f << (void *)nullValues4;
}
void tst_QMatrixNxN::multiplyFactor2x2()
{
QFETCH(void *, m1Values);
QFETCH(qreal, factor);
QFETCH(void *, m2Values);
QMatrix2x2 m1((const qreal *)m1Values);
QMatrix2x2 m3;
m3 = m1;
m3 *= factor;
QVERIFY(isSame(m3, (const qreal *)m2Values));
QMatrix2x2 m4;
m4 = m1 * factor;
QVERIFY(isSame(m4, (const qreal *)m2Values));
QMatrix2x2 m5;
m5 = factor * m1;
QVERIFY(isSame(m5, (const qreal *)m2Values));
}
// Test matrix multiplication by a factor for 3x3 matrices.
void tst_QMatrixNxN::multiplyFactor3x3_data()
{
QTest::addColumn<void *>("m1Values");
QTest::addColumn<qreal>("factor");
QTest::addColumn<void *>("m2Values");
QTest::newRow("null")
<< (void *)nullValues3 << (qreal)1.0f << (void *)nullValues3;
QTest::newRow("double identity")
<< (void *)identityValues3 << (qreal)2.0f << (void *)doubleIdentity3;
static qreal const values[16] =
{1.0f, 2.0f, 3.0f,
5.0f, 6.0f, 7.0f,
9.0f, 10.0f, 11.0f};
static qreal const doubleValues[16] =
{2.0f, 4.0f, 6.0f,
10.0f, 12.0f, 14.0f,
18.0f, 20.0f, 22.0f};
static qreal const negDoubleValues[16] =
{-2.0f, -4.0f, -6.0f,
-10.0f, -12.0f, -14.0f,
-18.0f, -20.0f, -22.0f};
QTest::newRow("unique")
<< (void *)values << (qreal)2.0f << (void *)doubleValues;
QTest::newRow("neg")
<< (void *)values << (qreal)-2.0f << (void *)negDoubleValues;
QTest::newRow("zero")
<< (void *)values << (qreal)0.0f << (void *)nullValues4;
}
void tst_QMatrixNxN::multiplyFactor3x3()
{
QFETCH(void *, m1Values);
QFETCH(qreal, factor);
QFETCH(void *, m2Values);
QMatrix3x3 m1((const qreal *)m1Values);
QMatrix3x3 m3;
m3 = m1;
m3 *= factor;
QVERIFY(isSame(m3, (const qreal *)m2Values));
QMatrix3x3 m4;
m4 = m1 * factor;
QVERIFY(isSame(m4, (const qreal *)m2Values));
QMatrix3x3 m5;
m5 = factor * m1;
QVERIFY(isSame(m5, (const qreal *)m2Values));
}
// Test matrix multiplication by a factor for 4x4 matrices.
void tst_QMatrixNxN::multiplyFactor4x4_data()
{
QTest::addColumn<void *>("m1Values");
QTest::addColumn<qreal>("factor");
QTest::addColumn<void *>("m2Values");
QTest::newRow("null")
<< (void *)nullValues4 << (qreal)1.0f << (void *)nullValues4;
QTest::newRow("double identity")
<< (void *)identityValues4 << (qreal)2.0f << (void *)doubleIdentity4;
static qreal const values[16] =
{1.0f, 2.0f, 3.0f, 4.0f,
5.0f, 6.0f, 7.0f, 8.0f,
9.0f, 10.0f, 11.0f, 12.0f,
13.0f, 14.0f, 15.0f, 16.0f};
static qreal const doubleValues[16] =
{2.0f, 4.0f, 6.0f, 8.0f,
10.0f, 12.0f, 14.0f, 16.0f,
18.0f, 20.0f, 22.0f, 24.0f,
26.0f, 28.0f, 30.0f, 32.0f};
static qreal const negDoubleValues[16] =
{-2.0f, -4.0f, -6.0f, -8.0f,
-10.0f, -12.0f, -14.0f, -16.0f,
-18.0f, -20.0f, -22.0f, -24.0f,
-26.0f, -28.0f, -30.0f, -32.0f};
QTest::newRow("unique")
<< (void *)values << (qreal)2.0f << (void *)doubleValues;
QTest::newRow("neg")
<< (void *)values << (qreal)-2.0f << (void *)negDoubleValues;
QTest::newRow("zero")
<< (void *)values << (qreal)0.0f << (void *)nullValues4;
}
void tst_QMatrixNxN::multiplyFactor4x4()
{
QFETCH(void *, m1Values);
QFETCH(qreal, factor);
QFETCH(void *, m2Values);
QMatrix4x4 m1((const qreal *)m1Values);
QMatrix4x4 m3;
m3 = m1;
m3 *= factor;
QVERIFY(isSame(m3, (const qreal *)m2Values));
QMatrix4x4 m4;
m4 = m1 * factor;
QVERIFY(isSame(m4, (const qreal *)m2Values));
QMatrix4x4 m5;
m5 = factor * m1;
QVERIFY(isSame(m5, (const qreal *)m2Values));
}
// Test matrix multiplication by a factor for 4x3 matrices.
void tst_QMatrixNxN::multiplyFactor4x3_data()
{
QTest::addColumn<void *>("m1Values");
QTest::addColumn<qreal>("factor");
QTest::addColumn<void *>("m2Values");
QTest::newRow("null")
<< (void *)nullValues4x3 << (qreal)1.0f << (void *)nullValues4x3;
QTest::newRow("double identity")
<< (void *)identityValues4x3 << (qreal)2.0f << (void *)doubleIdentity4x3;
static qreal const values[12] =
{1.0f, 2.0f, 3.0f, 4.0f,
5.0f, 6.0f, 7.0f, 8.0f,
9.0f, 10.0f, 11.0f, 12.0f};
static qreal const doubleValues[12] =
{2.0f, 4.0f, 6.0f, 8.0f,
10.0f, 12.0f, 14.0f, 16.0f,
18.0f, 20.0f, 22.0f, 24.0f};
static qreal const negDoubleValues[12] =
{-2.0f, -4.0f, -6.0f, -8.0f,
-10.0f, -12.0f, -14.0f, -16.0f,
-18.0f, -20.0f, -22.0f, -24.0f};
QTest::newRow("unique")
<< (void *)values << (qreal)2.0f << (void *)doubleValues;
QTest::newRow("neg")
<< (void *)values << (qreal)-2.0f << (void *)negDoubleValues;
QTest::newRow("zero")
<< (void *)values << (qreal)0.0f << (void *)nullValues4x3;
}
void tst_QMatrixNxN::multiplyFactor4x3()
{
QFETCH(void *, m1Values);
QFETCH(qreal, factor);
QFETCH(void *, m2Values);
QMatrix4x3 m1((const qreal *)m1Values);
QMatrix4x3 m3;
m3 = m1;
m3 *= factor;
QVERIFY(isSame(m3, (const qreal *)m2Values));
QMatrix4x3 m4;
m4 = m1 * factor;
QVERIFY(isSame(m4, (const qreal *)m2Values));
QMatrix4x3 m5;
m5 = factor * m1;
QVERIFY(isSame(m5, (const qreal *)m2Values));
}
// Test matrix division by a factor for 2x2 matrices.
void tst_QMatrixNxN::divideFactor2x2_data()
{
// Use the same test cases as the multiplyFactor test.
multiplyFactor2x2_data();
}
void tst_QMatrixNxN::divideFactor2x2()
{
QFETCH(void *, m1Values);
QFETCH(qreal, factor);
QFETCH(void *, m2Values);
if (factor == 0.0f)
return;
QMatrix2x2 m2((const qreal *)m2Values);
QMatrix2x2 m3;
m3 = m2;
m3 /= factor;
QVERIFY(isSame(m3, (const qreal *)m1Values));
QMatrix2x2 m4;
m4 = m2 / factor;
QVERIFY(isSame(m4, (const qreal *)m1Values));
}
// Test matrix division by a factor for 3x3 matrices.
void tst_QMatrixNxN::divideFactor3x3_data()
{
// Use the same test cases as the multiplyFactor test.
multiplyFactor3x3_data();
}
void tst_QMatrixNxN::divideFactor3x3()
{
QFETCH(void *, m1Values);
QFETCH(qreal, factor);
QFETCH(void *, m2Values);
if (factor == 0.0f)
return;
QMatrix3x3 m2((const qreal *)m2Values);
QMatrix3x3 m3;
m3 = m2;
m3 /= factor;
QVERIFY(isSame(m3, (const qreal *)m1Values));
QMatrix3x3 m4;
m4 = m2 / factor;
QVERIFY(isSame(m4, (const qreal *)m1Values));
}
// Test matrix division by a factor for 4x4 matrices.
void tst_QMatrixNxN::divideFactor4x4_data()
{
// Use the same test cases as the multiplyFactor test.
multiplyFactor4x4_data();
}
void tst_QMatrixNxN::divideFactor4x4()
{
QFETCH(void *, m1Values);
QFETCH(qreal, factor);
QFETCH(void *, m2Values);
if (factor == 0.0f)
return;
QMatrix4x4 m2((const qreal *)m2Values);
QMatrix4x4 m3;
m3 = m2;
m3 /= factor;
QVERIFY(isSame(m3, (const qreal *)m1Values));
QMatrix4x4 m4;
m4 = m2 / factor;
QVERIFY(isSame(m4, (const qreal *)m1Values));
}
// Test matrix division by a factor for 4x3 matrices.
void tst_QMatrixNxN::divideFactor4x3_data()
{
// Use the same test cases as the multiplyFactor test.
multiplyFactor4x3_data();
}
void tst_QMatrixNxN::divideFactor4x3()
{
QFETCH(void *, m1Values);
QFETCH(qreal, factor);
QFETCH(void *, m2Values);
if (factor == 0.0f)
return;
QMatrix4x3 m2((const qreal *)m2Values);
QMatrix4x3 m3;
m3 = m2;
m3 /= factor;
QVERIFY(isSame(m3, (const qreal *)m1Values));
QMatrix4x3 m4;
m4 = m2 / factor;
QVERIFY(isSame(m4, (const qreal *)m1Values));
}
// Test matrix negation for 2x2 matrices.
void tst_QMatrixNxN::negate2x2_data()
{
// Use the same test cases as the multiplyFactor test.
multiplyFactor2x2_data();
}
void tst_QMatrixNxN::negate2x2()
{
QFETCH(void *, m1Values);
const qreal *values = (const qreal *)m1Values;
QMatrix2x2 m1(values);
qreal negated[4];
for (int index = 0; index < 4; ++index)
negated[index] = -values[index];
QMatrix2x2 m2;
m2 = -m1;
QVERIFY(isSame(m2, negated));
}
// Test matrix negation for 3x3 matrices.
void tst_QMatrixNxN::negate3x3_data()
{
// Use the same test cases as the multiplyFactor test.
multiplyFactor3x3_data();
}
void tst_QMatrixNxN::negate3x3()
{
QFETCH(void *, m1Values);
const qreal *values = (const qreal *)m1Values;
QMatrix3x3 m1(values);
qreal negated[9];
for (int index = 0; index < 9; ++index)
negated[index] = -values[index];
QMatrix3x3 m2;
m2 = -m1;
QVERIFY(isSame(m2, negated));
}
// Test matrix negation for 4x4 matrices.
void tst_QMatrixNxN::negate4x4_data()
{
// Use the same test cases as the multiplyFactor test.
multiplyFactor4x4_data();
}
void tst_QMatrixNxN::negate4x4()
{
QFETCH(void *, m1Values);
const qreal *values = (const qreal *)m1Values;
QMatrix4x4 m1(values);
qreal negated[16];
for (int index = 0; index < 16; ++index)
negated[index] = -values[index];
QMatrix4x4 m2;
m2 = -m1;
QVERIFY(isSame(m2, negated));
}
// Test matrix negation for 4x3 matrices.
void tst_QMatrixNxN::negate4x3_data()
{
// Use the same test cases as the multiplyFactor test.
multiplyFactor4x3_data();
}
void tst_QMatrixNxN::negate4x3()
{
QFETCH(void *, m1Values);
const qreal *values = (const qreal *)m1Values;
QMatrix4x3 m1(values);
qreal negated[12];
for (int index = 0; index < 12; ++index)
negated[index] = -values[index];
QMatrix4x3 m2;
m2 = -m1;
QVERIFY(isSame(m2, negated));
}
// Matrix inverted. This is a more straight-forward implementation
// of the algorithm at http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q24
// than the optimized version in the QMatrix4x4 code. Hopefully it is
// easier to verify that this version is the same as the reference.
struct Matrix3
{
qreal v[9];
};
struct Matrix4
{
qreal v[16];
};
static qreal m3Determinant(const Matrix3& m)
{
return m.v[0] * (m.v[4] * m.v[8] - m.v[7] * m.v[5]) -
m.v[1] * (m.v[3] * m.v[8] - m.v[6] * m.v[5]) +
m.v[2] * (m.v[3] * m.v[7] - m.v[6] * m.v[4]);
}
static bool m3Inverse(const Matrix3& min, Matrix3& mout)
{
qreal det = m3Determinant(min);
if (det == 0.0f)
return false;
mout.v[0] = (min.v[4] * min.v[8] - min.v[5] * min.v[7]) / det;
mout.v[1] = -(min.v[1] * min.v[8] - min.v[2] * min.v[7]) / det;
mout.v[2] = (min.v[1] * min.v[5] - min.v[4] * min.v[2]) / det;
mout.v[3] = -(min.v[3] * min.v[8] - min.v[5] * min.v[6]) / det;
mout.v[4] = (min.v[0] * min.v[8] - min.v[6] * min.v[2]) / det;
mout.v[5] = -(min.v[0] * min.v[5] - min.v[3] * min.v[2]) / det;
mout.v[6] = (min.v[3] * min.v[7] - min.v[6] * min.v[4]) / det;
mout.v[7] = -(min.v[0] * min.v[7] - min.v[6] * min.v[1]) / det;
mout.v[8] = (min.v[0] * min.v[4] - min.v[1] * min.v[3]) / det;
return true;
}
static void m3Transpose(Matrix3& m)
{
qSwap(m.v[1], m.v[3]);
qSwap(m.v[2], m.v[6]);
qSwap(m.v[5], m.v[7]);
}
static void m4Submatrix(const Matrix4& min, Matrix3& mout, int i, int j)
{
for (int di = 0; di < 3; ++di) {
for (int dj = 0; dj < 3; ++dj) {
int si = di + ((di >= i) ? 1 : 0);
int sj = dj + ((dj >= j) ? 1 : 0);
mout.v[di * 3 + dj] = min.v[si * 4 + sj];
}
}
}
static qreal m4Determinant(const Matrix4& m)
{
qreal det;
qreal result = 0.0f;
qreal i = 1.0f;
Matrix3 msub;
for (int n = 0; n < 4; ++n, i *= -1.0f) {
m4Submatrix(m, msub, 0, n);
det = m3Determinant(msub);
result += m.v[n] * det * i;
}
return result;
}
static void m4Inverse(const Matrix4& min, Matrix4& mout)
{
qreal det = m4Determinant(min);
Matrix3 msub;
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
qreal sign = 1.0f - ((i + j) % 2) * 2.0f;
m4Submatrix(min, msub, i, j);
mout.v[i + j * 4] = (m3Determinant(msub) * sign) / det;
}
}
}
// Test matrix inverted for 4x4 matrices.
void tst_QMatrixNxN::inverted4x4_data()
{
QTest::addColumn<void *>("m1Values");
QTest::addColumn<void *>("m2Values");
QTest::addColumn<bool>("invertible");
QTest::newRow("null")
<< (void *)nullValues4 << (void *)identityValues4 << false;
QTest::newRow("identity")
<< (void *)identityValues4 << (void *)identityValues4 << true;
QTest::newRow("unique")
<< (void *)uniqueValues4 << (void *)identityValues4 << false;
static Matrix4 const invertible = {
{5.0f, 0.0f, 0.0f, 2.0f,
0.0f, 6.0f, 0.0f, 3.0f,
0.0f, 0.0f, 7.0f, 4.0f,
0.0f, 0.0f, 0.0f, 1.0f}
};
static Matrix4 inverted;
m4Inverse(invertible, inverted);
QTest::newRow("invertible")
<< (void *)invertible.v << (void *)inverted.v << true;
static Matrix4 const translate = {
{1.0f, 0.0f, 0.0f, 2.0f,
0.0f, 1.0f, 0.0f, 3.0f,
0.0f, 0.0f, 1.0f, 4.0f,
0.0f, 0.0f, 0.0f, 1.0f}
};
static Matrix4 const inverseTranslate = {
{1.0f, 0.0f, 0.0f, -2.0f,
0.0f, 1.0f, 0.0f, -3.0f,
0.0f, 0.0f, 1.0f, -4.0f,
0.0f, 0.0f, 0.0f, 1.0f}
};
QTest::newRow("translate")
<< (void *)translate.v << (void *)inverseTranslate.v << true;
}
void tst_QMatrixNxN::inverted4x4()
{
QFETCH(void *, m1Values);
QFETCH(void *, m2Values);
QFETCH(bool, invertible);
QMatrix4x4 m1((const qreal *)m1Values);
if (invertible)
QVERIFY(m1.determinant() != 0.0f);
else
QVERIFY(m1.determinant() == 0.0f);
Matrix4 m1alt;
memcpy(m1alt.v, (const qreal *)m1Values, sizeof(m1alt.v));
QCOMPARE(m1.determinant(), m4Determinant(m1alt));
QMatrix4x4 m2;
bool inv;
m2 = m1.inverted(&inv);
QVERIFY(isSame(m2, (const qreal *)m2Values));
if (invertible) {
QVERIFY(inv);
Matrix4 m2alt;
m4Inverse(m1alt, m2alt);
QVERIFY(isSame(m2, m2alt.v));
QMatrix4x4 m3;
m3 = m1 * m2;
QVERIFY(isIdentity(m3));
QMatrix4x4 m4;
m4 = m2 * m1;
QVERIFY(isIdentity(m4));
} else {
QVERIFY(!inv);
}
// Test again, after inferring the special matrix type.
m1.optimize();
m2 = m1.inverted(&inv);
QVERIFY(isSame(m2, (const qreal *)m2Values));
QCOMPARE(inv, invertible);
}
void tst_QMatrixNxN::orthonormalInverse4x4()
{
QMatrix4x4 m1;
QVERIFY(qFuzzyCompare(m1.inverted(), m1));
QMatrix4x4 m2;
m2.rotate(45.0, 1.0, 0.0, 0.0);
m2.translate(10.0, 0.0, 0.0);
// Use optimize() to drop the internal flags that
// mark the matrix as orthonormal. This will force inverted()
// to compute m3.inverted() the long way. We can then compare
// the result to what the faster algorithm produces on m2.
QMatrix4x4 m3 = m2;
m3.optimize();
bool invertible;
QVERIFY(qFuzzyCompare(m2.inverted(&invertible), m3.inverted()));
QVERIFY(invertible);
QMatrix4x4 m4;
m4.rotate(45.0, 0.0, 1.0, 0.0);
QMatrix4x4 m5 = m4;
m5.optimize();
QVERIFY(qFuzzyCompare(m4.inverted(), m5.inverted()));
QMatrix4x4 m6;
m1.rotate(88, 0.0, 0.0, 1.0);
m1.translate(-20.0, 20.0, 15.0);
m1.rotate(25, 1.0, 0.0, 0.0);
QMatrix4x4 m7 = m6;
m7.optimize();
QVERIFY(qFuzzyCompare(m6.inverted(), m7.inverted()));
}
// Test the generation and use of 4x4 scale matrices.
void tst_QMatrixNxN::scale4x4_data()
{
QTest::addColumn<qreal>("x");
QTest::addColumn<qreal>("y");
QTest::addColumn<qreal>("z");
QTest::addColumn<void *>("resultValues");
static const qreal nullScale[] =
{0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f};
QTest::newRow("null")
<< (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (void *)nullScale;
QTest::newRow("identity")
<< (qreal)1.0f << (qreal)1.0f << (qreal)1.0f << (void *)identityValues4;
static const qreal doubleScale[] =
{2.0f, 0.0f, 0.0f, 0.0f,
0.0f, 2.0f, 0.0f, 0.0f,
0.0f, 0.0f, 2.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f};
QTest::newRow("double")
<< (qreal)2.0f << (qreal)2.0f << (qreal)2.0f << (void *)doubleScale;
static const qreal complexScale[] =
{2.0f, 0.0f, 0.0f, 0.0f,
0.0f, 11.0f, 0.0f, 0.0f,
0.0f, 0.0f, -6.5f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f};
QTest::newRow("complex")
<< (qreal)2.0f << (qreal)11.0f << (qreal)-6.5f << (void *)complexScale;
static const qreal complexScale2D[] =
{2.0f, 0.0f, 0.0f, 0.0f,
0.0f, -11.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f};
QTest::newRow("complex2D")
<< (qreal)2.0f << (qreal)-11.0f << (qreal)1.0f << (void *)complexScale2D;
}
void tst_QMatrixNxN::scale4x4()
{
QFETCH(qreal, x);
QFETCH(qreal, y);
QFETCH(qreal, z);
QFETCH(void *, resultValues);
QMatrix4x4 result((const qreal *)resultValues);
QMatrix4x4 m1;
m1.scale(QVector3D(x, y, z));
QVERIFY(isSame(m1, (const qreal *)resultValues));
QMatrix4x4 m2;
m2.scale(x, y, z);
QVERIFY(isSame(m2, (const qreal *)resultValues));
if (z == 1.0f) {
QMatrix4x4 m2b;
m2b.scale(x, y);
QVERIFY(m2b == m2);
}
QVector3D v1(2.0f, 3.0f, -4.0f);
QVector3D v2 = m1 * v1;
QCOMPARE(v2.x(), (qreal)(2.0f * x));
QCOMPARE(v2.y(), (qreal)(3.0f * y));
QCOMPARE(v2.z(), (qreal)(-4.0f * z));
v2 = v1 * m1;
QCOMPARE(v2.x(), (qreal)(2.0f * x));
QCOMPARE(v2.y(), (qreal)(3.0f * y));
QCOMPARE(v2.z(), (qreal)(-4.0f * z));
QVector4D v3(2.0f, 3.0f, -4.0f, 34.0f);
QVector4D v4 = m1 * v3;
QCOMPARE(v4.x(), (qreal)(2.0f * x));
QCOMPARE(v4.y(), (qreal)(3.0f * y));
QCOMPARE(v4.z(), (qreal)(-4.0f * z));
QCOMPARE(v4.w(), (qreal)34.0f);
v4 = v3 * m1;
QCOMPARE(v4.x(), (qreal)(2.0f * x));
QCOMPARE(v4.y(), (qreal)(3.0f * y));
QCOMPARE(v4.z(), (qreal)(-4.0f * z));
QCOMPARE(v4.w(), (qreal)34.0f);
QPoint p1(2, 3);
QPoint p2 = m1 * p1;
QCOMPARE(p2.x(), (int)(2.0f * x));
QCOMPARE(p2.y(), (int)(3.0f * y));
p2 = p1 * m1;
QCOMPARE(p2.x(), (int)(2.0f * x));
QCOMPARE(p2.y(), (int)(3.0f * y));
QPointF p3(2.0f, 3.0f);
QPointF p4 = m1 * p3;
QCOMPARE(p4.x(), (qreal)(2.0f * x));
QCOMPARE(p4.y(), (qreal)(3.0f * y));
p4 = p3 * m1;
QCOMPARE(p4.x(), (qreal)(2.0f * x));
QCOMPARE(p4.y(), (qreal)(3.0f * y));
QMatrix4x4 m3(uniqueValues4);
QMatrix4x4 m4(m3);
m4.scale(x, y, z);
QVERIFY(m4 == m3 * m1);
if (x == y && y == z) {
QMatrix4x4 m5;
m5.scale(x);
QVERIFY(isSame(m5, (const qreal *)resultValues));
}
if (z == 1.0f) {
QMatrix4x4 m4b(m3);
m4b.scale(x, y);
QVERIFY(m4b == m4);
}
// Test coverage when the special matrix type is unknown.
QMatrix4x4 m6;
m6(0, 0) = 1.0f;
m6.scale(QVector3D(x, y, z));
QVERIFY(isSame(m6, (const qreal *)resultValues));
QMatrix4x4 m7;
m7(0, 0) = 1.0f;
m7.scale(x, y, z);
QVERIFY(isSame(m7, (const qreal *)resultValues));
if (x == y && y == z) {
QMatrix4x4 m8;
m8(0, 0) = 1.0f;
m8.scale(x);
QVERIFY(isSame(m8, (const qreal *)resultValues));
m8.optimize();
m8.scale(1.0f);
QVERIFY(isSame(m8, (const qreal *)resultValues));
QMatrix4x4 m9;
m9.translate(0.0f, 0.0f, 0.0f);
m9.scale(x);
QVERIFY(isSame(m9, (const qreal *)resultValues));
}
}
// Test the generation and use of 4x4 translation matrices.
void tst_QMatrixNxN::translate4x4_data()
{
QTest::addColumn<qreal>("x");
QTest::addColumn<qreal>("y");
QTest::addColumn<qreal>("z");
QTest::addColumn<void *>("resultValues");
QTest::newRow("null")
<< (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (void *)identityValues4;
static const qreal identityTranslate[] =
{1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f,
0.0f, 0.0f, 0.0f, 1.0f};
QTest::newRow("identity")
<< (qreal)1.0f << (qreal)1.0f << (qreal)1.0f << (void *)identityTranslate;
static const qreal complexTranslate[] =
{1.0f, 0.0f, 0.0f, 2.0f,
0.0f, 1.0f, 0.0f, 11.0f,
0.0f, 0.0f, 1.0f, -6.5f,
0.0f, 0.0f, 0.0f, 1.0f};
QTest::newRow("complex")
<< (qreal)2.0f << (qreal)11.0f << (qreal)-6.5f << (void *)complexTranslate;
static const qreal complexTranslate2D[] =
{1.0f, 0.0f, 0.0f, 2.0f,
0.0f, 1.0f, 0.0f, -11.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f};
QTest::newRow("complex2D")
<< (qreal)2.0f << (qreal)-11.0f << (qreal)0.0f << (void *)complexTranslate2D;
}
void tst_QMatrixNxN::translate4x4()
{
QFETCH(qreal, x);
QFETCH(qreal, y);
QFETCH(qreal, z);
QFETCH(void *, resultValues);
QMatrix4x4 result((const qreal *)resultValues);
QMatrix4x4 m1;
m1.translate(QVector3D(x, y, z));
QVERIFY(isSame(m1, (const qreal *)resultValues));
QMatrix4x4 m2;
m2.translate(x, y, z);
QVERIFY(isSame(m2, (const qreal *)resultValues));
if (z == 0.0f) {
QMatrix4x4 m2b;
m2b.translate(x, y);
QVERIFY(m2b == m2);
}
QVector3D v1(2.0f, 3.0f, -4.0f);
QVector3D v2 = m1 * v1;
QCOMPARE(v2.x(), (qreal)(2.0f + x));
QCOMPARE(v2.y(), (qreal)(3.0f + y));
QCOMPARE(v2.z(), (qreal)(-4.0f + z));
QVector4D v3(2.0f, 3.0f, -4.0f, 1.0f);
QVector4D v4 = m1 * v3;
QCOMPARE(v4.x(), (qreal)(2.0f + x));
QCOMPARE(v4.y(), (qreal)(3.0f + y));
QCOMPARE(v4.z(), (qreal)(-4.0f + z));
QCOMPARE(v4.w(), (qreal)1.0f);
QVector4D v5(2.0f, 3.0f, -4.0f, 34.0f);
QVector4D v6 = m1 * v5;
QCOMPARE(v6.x(), (qreal)(2.0f + x * 34.0f));
QCOMPARE(v6.y(), (qreal)(3.0f + y * 34.0f));
QCOMPARE(v6.z(), (qreal)(-4.0f + z * 34.0f));
QCOMPARE(v6.w(), (qreal)34.0f);
QPoint p1(2, 3);
QPoint p2 = m1 * p1;
QCOMPARE(p2.x(), (int)(2.0f + x));
QCOMPARE(p2.y(), (int)(3.0f + y));
QPointF p3(2.0f, 3.0f);
QPointF p4 = m1 * p3;
QCOMPARE(p4.x(), (qreal)(2.0f + x));
QCOMPARE(p4.y(), (qreal)(3.0f + y));
QMatrix4x4 m3(uniqueValues4);
QMatrix4x4 m4(m3);
m4.translate(x, y, z);
QVERIFY(m4 == m3 * m1);
if (z == 0.0f) {
QMatrix4x4 m4b(m3);
m4b.translate(x, y);
QVERIFY(m4b == m4);
}
}
// Test the generation and use of 4x4 rotation matrices.
void tst_QMatrixNxN::rotate4x4_data()
{
QTest::addColumn<qreal>("angle");
QTest::addColumn<qreal>("x");
QTest::addColumn<qreal>("y");
QTest::addColumn<qreal>("z");
QTest::addColumn<void *>("resultValues");
static const qreal nullRotate[] =
{0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f};
QTest::newRow("null")
<< (qreal)90.0f
<< (qreal)0.0f << (qreal)0.0f << (qreal)0.0f
<< (void *)nullRotate;
static const qreal noRotate[] =
{1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f};
QTest::newRow("zerodegrees")
<< (qreal)0.0f
<< (qreal)2.0f << (qreal)3.0f << (qreal)-4.0f
<< (void *)noRotate;
static const qreal xRotate[] =
{1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f};
QTest::newRow("xrotate")
<< (qreal)90.0f
<< (qreal)1.0f << (qreal)0.0f << (qreal)0.0f
<< (void *)xRotate;
static const qreal xRotateNeg[] =
{1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, -1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f};
QTest::newRow("-xrotate")
<< (qreal)90.0f
<< (qreal)-1.0f << (qreal)0.0f << (qreal)0.0f
<< (void *)xRotateNeg;
static const qreal yRotate[] =
{0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f};
QTest::newRow("yrotate")
<< (qreal)90.0f
<< (qreal)0.0f << (qreal)1.0f << (qreal)0.0f
<< (void *)yRotate;
static const qreal yRotateNeg[] =
{0.0f, 0.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f};
QTest::newRow("-yrotate")
<< (qreal)90.0f
<< (qreal)0.0f << (qreal)-1.0f << (qreal)0.0f
<< (void *)yRotateNeg;
static const qreal zRotate[] =
{0.0f, -1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f};
QTest::newRow("zrotate")
<< (qreal)90.0f
<< (qreal)0.0f << (qreal)0.0f << (qreal)1.0f
<< (void *)zRotate;
static const qreal zRotateNeg[] =
{0.0f, 1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f};
QTest::newRow("-zrotate")
<< (qreal)90.0f
<< (qreal)0.0f << (qreal)0.0f << (qreal)-1.0f
<< (void *)zRotateNeg;
// Algorithm from http://en.wikipedia.org/wiki/Rotation_matrix.
// Deliberately different from the one in the code for cross-checking.
static qreal complexRotate[16];
qreal x = 1.0f;
qreal y = 2.0f;
qreal z = -6.0f;
qreal angle = -45.0f;
qreal c = qCos(angle * M_PI / 180.0f);
qreal s = qSin(angle * M_PI / 180.0f);
qreal len = qSqrt(x * x + y * y + z * z);
qreal xu = x / len;
qreal yu = y / len;
qreal zu = z / len;
complexRotate[0] = (qreal)((1 - xu * xu) * c + xu * xu);
complexRotate[1] = (qreal)(-zu * s - xu * yu * c + xu * yu);
complexRotate[2] = (qreal)(yu * s - xu * zu * c + xu * zu);
complexRotate[3] = 0;
complexRotate[4] = (qreal)(zu * s - xu * yu * c + xu * yu);
complexRotate[5] = (qreal)((1 - yu * yu) * c + yu * yu);
complexRotate[6] = (qreal)(-xu * s - yu * zu * c + yu * zu);
complexRotate[7] = 0;
complexRotate[8] = (qreal)(-yu * s - xu * zu * c + xu * zu);
complexRotate[9] = (qreal)(xu * s - yu * zu * c + yu * zu);
complexRotate[10] = (qreal)((1 - zu * zu) * c + zu * zu);
complexRotate[11] = 0;
complexRotate[12] = 0;
complexRotate[13] = 0;
complexRotate[14] = 0;
complexRotate[15] = 1;
QTest::newRow("complex")
<< (qreal)angle
<< (qreal)x << (qreal)y << (qreal)z
<< (void *)complexRotate;
}
void tst_QMatrixNxN::rotate4x4()
{
QFETCH(qreal, angle);
QFETCH(qreal, x);
QFETCH(qreal, y);
QFETCH(qreal, z);
QFETCH(void *, resultValues);
QMatrix4x4 m1;
m1.rotate(angle, QVector3D(x, y, z));
QVERIFY(isSame(m1, (const qreal *)resultValues));
QMatrix4x4 m2;
m2.rotate(angle, x, y, z);
QVERIFY(isSame(m2, (const qreal *)resultValues));
QMatrix4x4 m3(uniqueValues4);
QMatrix4x4 m4(m3);
m4.rotate(angle, x, y, z);
QVERIFY(qFuzzyCompare(m4, m3 * m1));
// Null vectors don't make sense for quaternion rotations.
if (x != 0 || y != 0 || z != 0) {
QMatrix4x4 m5;
m5.rotate(QQuaternion::fromAxisAndAngle(QVector3D(x, y, z), angle));
QVERIFY(isSame(m5, (const qreal *)resultValues));
}
#define ROTATE4(xin,yin,zin,win,xout,yout,zout,wout) \
do { \
xout = ((const qreal *)resultValues)[0] * xin + \
((const qreal *)resultValues)[1] * yin + \
((const qreal *)resultValues)[2] * zin + \
((const qreal *)resultValues)[3] * win; \
yout = ((const qreal *)resultValues)[4] * xin + \
((const qreal *)resultValues)[5] * yin + \
((const qreal *)resultValues)[6] * zin + \
((const qreal *)resultValues)[7] * win; \
zout = ((const qreal *)resultValues)[8] * xin + \
((const qreal *)resultValues)[9] * yin + \
((const qreal *)resultValues)[10] * zin + \
((const qreal *)resultValues)[11] * win; \
wout = ((const qreal *)resultValues)[12] * xin + \
((const qreal *)resultValues)[13] * yin + \
((const qreal *)resultValues)[14] * zin + \
((const qreal *)resultValues)[15] * win; \
} while (0)
// Rotate various test vectors using the straight-forward approach.
qreal v1x, v1y, v1z, v1w;
ROTATE4(2.0f, 3.0f, -4.0f, 1.0f, v1x, v1y, v1z, v1w);
v1x /= v1w;
v1y /= v1w;
v1z /= v1w;
qreal v3x, v3y, v3z, v3w;
ROTATE4(2.0f, 3.0f, -4.0f, 1.0f, v3x, v3y, v3z, v3w);
qreal v5x, v5y, v5z, v5w;
ROTATE4(2.0f, 3.0f, -4.0f, 34.0f, v5x, v5y, v5z, v5w);
qreal p1x, p1y, p1z, p1w;
ROTATE4(2.0f, 3.0f, 0.0f, 1.0f, p1x, p1y, p1z, p1w);
p1x /= p1w;
p1y /= p1w;
p1z /= p1w;
QVector3D v1(2.0f, 3.0f, -4.0f);
QVector3D v2 = m1 * v1;
QVERIFY(fuzzyCompare(v2.x(), v1x));
QVERIFY(fuzzyCompare(v2.y(), v1y));
QVERIFY(fuzzyCompare(v2.z(), v1z));
QVector4D v3(2.0f, 3.0f, -4.0f, 1.0f);
QVector4D v4 = m1 * v3;
QVERIFY(fuzzyCompare(v4.x(), v3x));
QVERIFY(fuzzyCompare(v4.y(), v3y));
QVERIFY(fuzzyCompare(v4.z(), v3z));
QVERIFY(fuzzyCompare(v4.w(), v3w));
QVector4D v5(2.0f, 3.0f, -4.0f, 34.0f);
QVector4D v6 = m1 * v5;
QVERIFY(fuzzyCompare(v6.x(), v5x));
QVERIFY(fuzzyCompare(v6.y(), v5y));
QVERIFY(fuzzyCompare(v6.z(), v5z));
QVERIFY(fuzzyCompare(v6.w(), v5w));
QPoint p1(2, 3);
QPoint p2 = m1 * p1;
QCOMPARE(p2.x(), qRound(p1x));
QCOMPARE(p2.y(), qRound(p1y));
QPointF p3(2.0f, 3.0f);
QPointF p4 = m1 * p3;
QVERIFY(fuzzyCompare(p4.x(), p1x));
QVERIFY(fuzzyCompare(p4.y(), p1y));
if (x != 0 || y != 0 || z != 0) {
QQuaternion q = QQuaternion::fromAxisAndAngle(QVector3D(x, y, z), angle);
QVector3D vq = q.rotatedVector(v1);
QVERIFY(fuzzyCompare(vq.x(), v1x));
QVERIFY(fuzzyCompare(vq.y(), v1y));
QVERIFY(fuzzyCompare(vq.z(), v1z));
}
}
static bool isSame(const QMatrix3x3& m1, const Matrix3& m2)
{
for (int row = 0; row < 3; ++row) {
for (int col = 0; col < 3; ++col) {
if (!fuzzyCompare(m1(row, col), m2.v[row * 3 + col]))
return false;
}
}
return true;
}
// Test the computation of normal matrices from 4x4 transformation matrices.
void tst_QMatrixNxN::normalMatrix_data()
{
QTest::addColumn<void *>("mValues");
QTest::newRow("identity")
<< (void *)identityValues4;
QTest::newRow("unique")
<< (void *)uniqueValues4; // Not invertible because determinant == 0.
static qreal const translateValues[16] =
{1.0f, 0.0f, 0.0f, 4.0f,
0.0f, 1.0f, 0.0f, 5.0f,
0.0f, 0.0f, 1.0f, -3.0f,
0.0f, 0.0f, 0.0f, 1.0f};
static qreal const scaleValues[16] =
{2.0f, 0.0f, 0.0f, 0.0f,
0.0f, 7.0f, 0.0f, 0.0f,
0.0f, 0.0f, 9.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f};
static qreal const bothValues[16] =
{2.0f, 0.0f, 0.0f, 4.0f,
0.0f, 7.0f, 0.0f, 5.0f,
0.0f, 0.0f, 9.0f, -3.0f,
0.0f, 0.0f, 0.0f, 1.0f};
static qreal const nullScaleValues1[16] =
{0.0f, 0.0f, 0.0f, 4.0f,
0.0f, 7.0f, 0.0f, 5.0f,
0.0f, 0.0f, 9.0f, -3.0f,
0.0f, 0.0f, 0.0f, 1.0f};
static qreal const nullScaleValues2[16] =
{2.0f, 0.0f, 0.0f, 4.0f,
0.0f, 0.0f, 0.0f, 5.0f,
0.0f, 0.0f, 9.0f, -3.0f,
0.0f, 0.0f, 0.0f, 1.0f};
static qreal const nullScaleValues3[16] =
{2.0f, 0.0f, 0.0f, 4.0f,
0.0f, 7.0f, 0.0f, 5.0f,
0.0f, 0.0f, 0.0f, -3.0f,
0.0f, 0.0f, 0.0f, 1.0f};
QTest::newRow("translate") << (void *)translateValues;
QTest::newRow("scale") << (void *)scaleValues;
QTest::newRow("both") << (void *)bothValues;
QTest::newRow("null scale 1") << (void *)nullScaleValues1;
QTest::newRow("null scale 2") << (void *)nullScaleValues2;
QTest::newRow("null scale 3") << (void *)nullScaleValues3;
}
void tst_QMatrixNxN::normalMatrix()
{
QFETCH(void *, mValues);
const qreal *values = (const qreal *)mValues;
// Compute the expected answer the long way.
Matrix3 min;
Matrix3 answer;
min.v[0] = values[0];
min.v[1] = values[1];
min.v[2] = values[2];
min.v[3] = values[4];
min.v[4] = values[5];
min.v[5] = values[6];
min.v[6] = values[8];
min.v[7] = values[9];
min.v[8] = values[10];
bool invertible = m3Inverse(min, answer);
m3Transpose(answer);
// Perform the test.
QMatrix4x4 m1(values);
QMatrix3x3 n1 = m1.normalMatrix();
if (invertible)
QVERIFY(::isSame(n1, answer));
else
QVERIFY(isIdentity(n1));
// Perform the test again, after inferring special matrix types.
// This tests the optimized paths in the normalMatrix() function.
m1.optimize();
n1 = m1.normalMatrix();
if (invertible)
QVERIFY(::isSame(n1, answer));
else
QVERIFY(isIdentity(n1));
}
// Test optimized transformations on 4x4 matrices.
void tst_QMatrixNxN::optimizedTransforms()
{
static qreal const translateValues[16] =
{1.0f, 0.0f, 0.0f, 4.0f,
0.0f, 1.0f, 0.0f, 5.0f,
0.0f, 0.0f, 1.0f, -3.0f,
0.0f, 0.0f, 0.0f, 1.0f};
static qreal const translateDoubleValues[16] =
{1.0f, 0.0f, 0.0f, 8.0f,
0.0f, 1.0f, 0.0f, 10.0f,
0.0f, 0.0f, 1.0f, -6.0f,
0.0f, 0.0f, 0.0f, 1.0f};
static qreal const scaleValues[16] =
{2.0f, 0.0f, 0.0f, 0.0f,
0.0f, 7.0f, 0.0f, 0.0f,
0.0f, 0.0f, 9.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f};
static qreal const scaleDoubleValues[16] =
{4.0f, 0.0f, 0.0f, 0.0f,
0.0f, 49.0f, 0.0f, 0.0f,
0.0f, 0.0f, 81.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f};
static qreal const bothValues[16] =
{2.0f, 0.0f, 0.0f, 4.0f,
0.0f, 7.0f, 0.0f, 5.0f,
0.0f, 0.0f, 9.0f, -3.0f,
0.0f, 0.0f, 0.0f, 1.0f};
static qreal const bothReverseValues[16] =
{2.0f, 0.0f, 0.0f, 4.0f * 2.0f,
0.0f, 7.0f, 0.0f, 5.0f * 7.0f,
0.0f, 0.0f, 9.0f, -3.0f * 9.0f,
0.0f, 0.0f, 0.0f, 1.0f};
static qreal const bothThenTranslateValues[16] =
{2.0f, 0.0f, 0.0f, 4.0f + 2.0f * 4.0f,
0.0f, 7.0f, 0.0f, 5.0f + 7.0f * 5.0f,
0.0f, 0.0f, 9.0f, -3.0f + 9.0f * -3.0f,
0.0f, 0.0f, 0.0f, 1.0f};
static qreal const bothThenScaleValues[16] =
{4.0f, 0.0f, 0.0f, 4.0f,
0.0f, 49.0f, 0.0f, 5.0f,
0.0f, 0.0f, 81.0f, -3.0f,
0.0f, 0.0f, 0.0f, 1.0f};
QMatrix4x4 translate(translateValues);
QMatrix4x4 scale(scaleValues);
QMatrix4x4 both(bothValues);
QMatrix4x4 m1;
m1.translate(4.0f, 5.0f, -3.0f);
QVERIFY(isSame(m1, translateValues));
m1.translate(4.0f, 5.0f, -3.0f);
QVERIFY(isSame(m1, translateDoubleValues));
QMatrix4x4 m2;
m2.translate(QVector3D(4.0f, 5.0f, -3.0f));
QVERIFY(isSame(m2, translateValues));
m2.translate(QVector3D(4.0f, 5.0f, -3.0f));
QVERIFY(isSame(m2, translateDoubleValues));
QMatrix4x4 m3;
m3.scale(2.0f, 7.0f, 9.0f);
QVERIFY(isSame(m3, scaleValues));
m3.scale(2.0f, 7.0f, 9.0f);
QVERIFY(isSame(m3, scaleDoubleValues));
QMatrix4x4 m4;
m4.scale(QVector3D(2.0f, 7.0f, 9.0f));
QVERIFY(isSame(m4, scaleValues));
m4.scale(QVector3D(2.0f, 7.0f, 9.0f));
QVERIFY(isSame(m4, scaleDoubleValues));
QMatrix4x4 m5;
m5.translate(4.0f, 5.0f, -3.0f);
m5.scale(2.0f, 7.0f, 9.0f);
QVERIFY(isSame(m5, bothValues));
m5.translate(4.0f, 5.0f, -3.0f);
QVERIFY(isSame(m5, bothThenTranslateValues));
QMatrix4x4 m6;
m6.translate(QVector3D(4.0f, 5.0f, -3.0f));
m6.scale(QVector3D(2.0f, 7.0f, 9.0f));
QVERIFY(isSame(m6, bothValues));
m6.translate(QVector3D(4.0f, 5.0f, -3.0f));
QVERIFY(isSame(m6, bothThenTranslateValues));
QMatrix4x4 m7;
m7.scale(2.0f, 7.0f, 9.0f);
m7.translate(4.0f, 5.0f, -3.0f);
QVERIFY(isSame(m7, bothReverseValues));
QMatrix4x4 m8;
m8.scale(QVector3D(2.0f, 7.0f, 9.0f));
m8.translate(QVector3D(4.0f, 5.0f, -3.0f));
QVERIFY(isSame(m8, bothReverseValues));
QMatrix4x4 m9;
m9.translate(4.0f, 5.0f, -3.0f);
m9.scale(2.0f, 7.0f, 9.0f);
QVERIFY(isSame(m9, bothValues));
m9.scale(2.0f, 7.0f, 9.0f);
QVERIFY(isSame(m9, bothThenScaleValues));
QMatrix4x4 m10;
m10.translate(QVector3D(4.0f, 5.0f, -3.0f));
m10.scale(QVector3D(2.0f, 7.0f, 9.0f));
QVERIFY(isSame(m10, bothValues));
m10.scale(QVector3D(2.0f, 7.0f, 9.0f));
QVERIFY(isSame(m10, bothThenScaleValues));
}
// Test orthographic projections.
void tst_QMatrixNxN::ortho()
{
QMatrix4x4 m1;
m1.ortho(QRect(0, 0, 300, 150));
QPointF p1 = m1 * QPointF(0, 0);
QPointF p2 = m1 * QPointF(300, 0);
QPointF p3 = m1 * QPointF(0, 150);
QPointF p4 = m1 * QPointF(300, 150);
QVector3D p5 = m1 * QVector3D(300, 150, 1);
QVERIFY(fuzzyCompare(p1.x(), -1.0));
QVERIFY(fuzzyCompare(p1.y(), 1.0));
QVERIFY(fuzzyCompare(p2.x(), 1.0));
QVERIFY(fuzzyCompare(p2.y(), 1.0));
QVERIFY(fuzzyCompare(p3.x(), -1.0));
QVERIFY(fuzzyCompare(p3.y(), -1.0));
QVERIFY(fuzzyCompare(p4.x(), 1.0));
QVERIFY(fuzzyCompare(p4.y(), -1.0));
QVERIFY(fuzzyCompare(p5.x(), (qreal)1.0));
QVERIFY(fuzzyCompare(p5.y(), (qreal)-1.0));
QVERIFY(fuzzyCompare(p5.z(), (qreal)-1.0));
QMatrix4x4 m2;
m2.ortho(QRectF(0, 0, 300, 150));
p1 = m2 * QPointF(0, 0);
p2 = m2 * QPointF(300, 0);
p3 = m2 * QPointF(0, 150);
p4 = m2 * QPointF(300, 150);
p5 = m2 * QVector3D(300, 150, 1);
QVERIFY(fuzzyCompare(p1.x(), -1.0));
QVERIFY(fuzzyCompare(p1.y(), 1.0));
QVERIFY(fuzzyCompare(p2.x(), 1.0));
QVERIFY(fuzzyCompare(p2.y(), 1.0));
QVERIFY(fuzzyCompare(p3.x(), -1.0));
QVERIFY(fuzzyCompare(p3.y(), -1.0));
QVERIFY(fuzzyCompare(p4.x(), 1.0));
QVERIFY(fuzzyCompare(p4.y(), -1.0));
QVERIFY(fuzzyCompare(p5.x(), (qreal)1.0));
QVERIFY(fuzzyCompare(p5.y(), (qreal)-1.0));
QVERIFY(fuzzyCompare(p5.z(), (qreal)-1.0));
QMatrix4x4 m3;
m3.ortho(0, 300, 150, 0, -1, 1);
p1 = m3 * QPointF(0, 0);
p2 = m3 * QPointF(300, 0);
p3 = m3 * QPointF(0, 150);
p4 = m3 * QPointF(300, 150);
p5 = m3 * QVector3D(300, 150, 1);
QVERIFY(fuzzyCompare(p1.x(), -1.0));
QVERIFY(fuzzyCompare(p1.y(), 1.0));
QVERIFY(fuzzyCompare(p2.x(), 1.0));
QVERIFY(fuzzyCompare(p2.y(), 1.0));
QVERIFY(fuzzyCompare(p3.x(), -1.0));
QVERIFY(fuzzyCompare(p3.y(), -1.0));
QVERIFY(fuzzyCompare(p4.x(), 1.0));
QVERIFY(fuzzyCompare(p4.y(), -1.0));
QVERIFY(fuzzyCompare(p5.x(), (qreal)1.0));
QVERIFY(fuzzyCompare(p5.y(), (qreal)-1.0));
QVERIFY(fuzzyCompare(p5.z(), (qreal)-1.0));
QMatrix4x4 m4;
m4.ortho(0, 300, 150, 0, -2, 3);
p1 = m4 * QPointF(0, 0);
p2 = m4 * QPointF(300, 0);
p3 = m4 * QPointF(0, 150);
p4 = m4 * QPointF(300, 150);
p5 = m4 * QVector3D(300, 150, 1);
QVERIFY(fuzzyCompare(p1.x(), -1.0));
QVERIFY(fuzzyCompare(p1.y(), 1.0));
QVERIFY(fuzzyCompare(p2.x(), 1.0));
QVERIFY(fuzzyCompare(p2.y(), 1.0));
QVERIFY(fuzzyCompare(p3.x(), -1.0));
QVERIFY(fuzzyCompare(p3.y(), -1.0));
QVERIFY(fuzzyCompare(p4.x(), 1.0));
QVERIFY(fuzzyCompare(p4.y(), -1.0));
QVERIFY(fuzzyCompare(p5.x(), (qreal)1.0));
QVERIFY(fuzzyCompare(p5.y(), (qreal)-1.0));
QVERIFY(fuzzyCompare(p5.z(), (qreal)-0.6));
// An empty view volume should leave the matrix alone.
QMatrix4x4 m5;
m5.ortho(0, 0, 150, 0, -2, 3);
QVERIFY(m5.isIdentity());
m5.ortho(0, 300, 150, 150, -2, 3);
QVERIFY(m5.isIdentity());
m5.ortho(0, 300, 150, 0, 2, 2);
QVERIFY(m5.isIdentity());
}
// Test perspective frustum projections.
void tst_QMatrixNxN::frustum()
{
QMatrix4x4 m1;
m1.frustum(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f);
QVector3D p1 = m1 * QVector3D(-1.0f, -1.0f, 1.0f);
QVector3D p2 = m1 * QVector3D(1.0f, -1.0f, 1.0f);
QVector3D p3 = m1 * QVector3D(-1.0f, 1.0f, 1.0f);
QVector3D p4 = m1 * QVector3D(1.0f, 1.0f, 1.0f);
QVector3D p5 = m1 * QVector3D(0.0f, 0.0f, 2.0f);
QVERIFY(fuzzyCompare(p1.x(), -1.0f));
QVERIFY(fuzzyCompare(p1.y(), -1.0f));
QVERIFY(fuzzyCompare(p1.z(), -1.0f));
QVERIFY(fuzzyCompare(p2.x(), 1.0f));
QVERIFY(fuzzyCompare(p2.y(), -1.0f));
QVERIFY(fuzzyCompare(p2.z(), -1.0f));
QVERIFY(fuzzyCompare(p3.x(), -1.0f));
QVERIFY(fuzzyCompare(p3.y(), 1.0f));
QVERIFY(fuzzyCompare(p3.z(), -1.0f));
QVERIFY(fuzzyCompare(p4.x(), 1.0f));
QVERIFY(fuzzyCompare(p4.y(), 1.0f));
QVERIFY(fuzzyCompare(p4.z(), -1.0f));
QVERIFY(fuzzyCompare(p5.x(), 0.0f));
QVERIFY(fuzzyCompare(p5.y(), 0.0f));
QVERIFY(fuzzyCompare(p5.z(), -0.5f));
// An empty view volume should leave the matrix alone.
QMatrix4x4 m5;
m5.frustum(0, 0, 150, 0, -2, 3);
QVERIFY(m5.isIdentity());
m5.frustum(0, 300, 150, 150, -2, 3);
QVERIFY(m5.isIdentity());
m5.frustum(0, 300, 150, 0, 2, 2);
QVERIFY(m5.isIdentity());
}
// Test perspective field-of-view projections.
void tst_QMatrixNxN::perspective()
{
QMatrix4x4 m1;
m1.perspective(45.0f, 1.0f, -1.0f, 1.0f);
QVector3D p1 = m1 * QVector3D(-1.0f, -1.0f, 1.0f);
QVector3D p2 = m1 * QVector3D(1.0f, -1.0f, 1.0f);
QVector3D p3 = m1 * QVector3D(-1.0f, 1.0f, 1.0f);
QVector3D p4 = m1 * QVector3D(1.0f, 1.0f, 1.0f);
QVector3D p5 = m1 * QVector3D(0.0f, 0.0f, 2.0f);
QVERIFY(fuzzyCompare(p1.x(), 2.41421));
QVERIFY(fuzzyCompare(p1.y(), 2.41421));
QVERIFY(fuzzyCompare(p1.z(), -1));
QVERIFY(fuzzyCompare(p2.x(), -2.41421));
QVERIFY(fuzzyCompare(p2.y(), 2.41421));
QVERIFY(fuzzyCompare(p2.z(), -1.0f));
QVERIFY(fuzzyCompare(p3.x(), 2.41421));
QVERIFY(fuzzyCompare(p3.y(), -2.41421));
QVERIFY(fuzzyCompare(p3.z(), -1.0f));
QVERIFY(fuzzyCompare(p4.x(), -2.41421));
QVERIFY(fuzzyCompare(p4.y(), -2.41421));
QVERIFY(fuzzyCompare(p4.z(), -1.0f));
QVERIFY(fuzzyCompare(p5.x(), 0.0f));
QVERIFY(fuzzyCompare(p5.y(), 0.0f));
QVERIFY(fuzzyCompare(p5.z(), -0.5f));
// An empty view volume should leave the matrix alone.
QMatrix4x4 m5;
m5.perspective(45.0f, 1.0f, 0.0f, 0.0f);
QVERIFY(m5.isIdentity());
m5.perspective(45.0f, 0.0f, -1.0f, 1.0f);
QVERIFY(m5.isIdentity());
m5.perspective(0.0f, 1.0f, -1.0f, 1.0f);
QVERIFY(m5.isIdentity());
}
// Test left-handed vs right-handed coordinate flipping.
void tst_QMatrixNxN::flipCoordinates()
{
QMatrix4x4 m1;
m1.flipCoordinates();
QVector3D p1 = m1 * QVector3D(2, 3, 4);
QVERIFY(p1 == QVector3D(2, -3, -4));
QMatrix4x4 m2;
m2.scale(2.0f, 3.0f, 1.0f);
m2.flipCoordinates();
QVector3D p2 = m2 * QVector3D(2, 3, 4);
QVERIFY(p2 == QVector3D(4, -9, -4));
QMatrix4x4 m3;
m3.translate(2.0f, 3.0f, 1.0f);
m3.flipCoordinates();
QVector3D p3 = m3 * QVector3D(2, 3, 4);
QVERIFY(p3 == QVector3D(4, 0, -3));
QMatrix4x4 m4;
m4.rotate(90.0f, 0.0f, 0.0f, 1.0f);
m4.flipCoordinates();
QVector3D p4 = m4 * QVector3D(2, 3, 4);
QVERIFY(p4 == QVector3D(3, 2, -4));
}
// Test conversion of generic matrices to and from the non-generic types.
void tst_QMatrixNxN::convertGeneric()
{
QMatrix4x3 m1(uniqueValues4x3);
static qreal const unique4x4[16] = {
1.0f, 2.0f, 3.0f, 4.0f,
5.0f, 6.0f, 7.0f, 8.0f,
9.0f, 10.0f, 11.0f, 12.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
QMatrix4x4 m4(m1);
QVERIFY(isSame(m4, unique4x4));
QMatrix4x4 m5 = qGenericMatrixToMatrix4x4(m1);
QVERIFY(isSame(m5, unique4x4));
static qreal const conv4x4[12] = {
1.0f, 2.0f, 3.0f, 4.0f,
5.0f, 6.0f, 7.0f, 8.0f,
9.0f, 10.0f, 11.0f, 12.0f
};
QMatrix4x4 m9(uniqueValues4);
QMatrix4x3 m10 = m9.toGenericMatrix<4, 3>();
QVERIFY(isSame(m10, conv4x4));
QMatrix4x3 m11 = qGenericMatrixFromMatrix4x4<4, 3>(m9);
QVERIFY(isSame(m11, conv4x4));
}
// Copy of "flagBits" in qmatrix4x4.h.
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
};
// Structure that allows direct access to "flagBits" for testing.
struct Matrix4x4
{
qreal m[4][4];
int flagBits;
};
// Test the inferring of special matrix types.
void tst_QMatrixNxN::optimize_data()
{
QTest::addColumn<void *>("mValues");
QTest::addColumn<int>("flagBits");
QTest::newRow("null")
<< (void *)nullValues4 << (int)General;
QTest::newRow("identity")
<< (void *)identityValues4 << (int)Identity;
QTest::newRow("unique")
<< (void *)uniqueValues4 << (int)General;
static qreal scaleValues[16] = {
2.0f, 0.0f, 0.0f, 0.0f,
0.0f, 3.0f, 0.0f, 0.0f,
0.0f, 0.0f, 4.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
QTest::newRow("scale")
<< (void *)scaleValues << (int)Scale;
static qreal translateValues[16] = {
1.0f, 0.0f, 0.0f, 2.0f,
0.0f, 1.0f, 0.0f, 3.0f,
0.0f, 0.0f, 1.0f, 4.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
QTest::newRow("scale")
<< (void *)translateValues << (int)Translation;
static qreal bothValues[16] = {
1.0f, 0.0f, 0.0f, 2.0f,
0.0f, 2.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 4.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
QTest::newRow("both")
<< (void *)bothValues << (int)(Scale | Translation);
static qreal belowValues[16] = {
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
4.0f, 0.0f, 0.0f, 1.0f
};
QTest::newRow("below")
<< (void *)belowValues << (int)General;
}
void tst_QMatrixNxN::optimize()
{
QFETCH(void *, mValues);
QFETCH(int, flagBits);
QMatrix4x4 m((const qreal *)mValues);
m.optimize();
QCOMPARE(reinterpret_cast<Matrix4x4 *>(&m)->flagBits, flagBits);
}
void tst_QMatrixNxN::columnsAndRows()
{
QMatrix4x4 m1(uniqueValues4);
QVERIFY(m1.column(0) == QVector4D(1, 5, 9, 13));
QVERIFY(m1.column(1) == QVector4D(2, 6, 10, 14));
QVERIFY(m1.column(2) == QVector4D(3, 7, 11, 15));
QVERIFY(m1.column(3) == QVector4D(4, 8, 12, 16));
QVERIFY(m1.row(0) == QVector4D(1, 2, 3, 4));
QVERIFY(m1.row(1) == QVector4D(5, 6, 7, 8));
QVERIFY(m1.row(2) == QVector4D(9, 10, 11, 12));
QVERIFY(m1.row(3) == QVector4D(13, 14, 15, 16));
m1.setColumn(0, QVector4D(-1, -5, -9, -13));
m1.setColumn(1, QVector4D(-2, -6, -10, -14));
m1.setColumn(2, QVector4D(-3, -7, -11, -15));
m1.setColumn(3, QVector4D(-4, -8, -12, -16));
QVERIFY(m1.column(0) == QVector4D(-1, -5, -9, -13));
QVERIFY(m1.column(1) == QVector4D(-2, -6, -10, -14));
QVERIFY(m1.column(2) == QVector4D(-3, -7, -11, -15));
QVERIFY(m1.column(3) == QVector4D(-4, -8, -12, -16));
QVERIFY(m1.row(0) == QVector4D(-1, -2, -3, -4));
QVERIFY(m1.row(1) == QVector4D(-5, -6, -7, -8));
QVERIFY(m1.row(2) == QVector4D(-9, -10, -11, -12));
QVERIFY(m1.row(3) == QVector4D(-13, -14, -15, -16));
m1.setRow(0, QVector4D(1, 5, 9, 13));
m1.setRow(1, QVector4D(2, 6, 10, 14));
m1.setRow(2, QVector4D(3, 7, 11, 15));
m1.setRow(3, QVector4D(4, 8, 12, 16));
QVERIFY(m1.column(0) == QVector4D(1, 2, 3, 4));
QVERIFY(m1.column(1) == QVector4D(5, 6, 7, 8));
QVERIFY(m1.column(2) == QVector4D(9, 10, 11, 12));
QVERIFY(m1.column(3) == QVector4D(13, 14, 15, 16));
QVERIFY(m1.row(0) == QVector4D(1, 5, 9, 13));
QVERIFY(m1.row(1) == QVector4D(2, 6, 10, 14));
QVERIFY(m1.row(2) == QVector4D(3, 7, 11, 15));
QVERIFY(m1.row(3) == QVector4D(4, 8, 12, 16));
}
// Test converting QMatrix objects into QMatrix4x4 and then
// checking that transformations in the original perform the
// equivalent transformations in the new matrix.
void tst_QMatrixNxN::convertQMatrix()
{
QMatrix m1;
m1.translate(-3.5, 2.0);
QPointF p1 = m1.map(QPointF(100.0, 150.0));
QCOMPARE(p1.x(), 100.0 - 3.5);
QCOMPARE(p1.y(), 150.0 + 2.0);
QMatrix4x4 m2(m1);
QPointF p2 = m2 * QPointF(100.0, 150.0);
QCOMPARE((double)p2.x(), 100.0 - 3.5);
QCOMPARE((double)p2.y(), 150.0 + 2.0);
QVERIFY(m1 == m2.toAffine());
QMatrix m3;
m3.scale(1.5, -2.0);
QPointF p3 = m3.map(QPointF(100.0, 150.0));
QCOMPARE(p3.x(), 1.5 * 100.0);
QCOMPARE(p3.y(), -2.0 * 150.0);
QMatrix4x4 m4(m3);
QPointF p4 = m4 * QPointF(100.0, 150.0);
QCOMPARE((double)p4.x(), 1.5 * 100.0);
QCOMPARE((double)p4.y(), -2.0 * 150.0);
QVERIFY(m3 == m4.toAffine());
QMatrix m5;
m5.rotate(45.0);
QPointF p5 = m5.map(QPointF(100.0, 150.0));
QMatrix4x4 m6(m5);
QPointF p6 = m6 * QPointF(100.0, 150.0);
QVERIFY(fuzzyCompare(p5.x(), p6.x()));
QVERIFY(fuzzyCompare(p5.y(), p6.y()));
QMatrix m7 = m6.toAffine();
QVERIFY(fuzzyCompare(m5.m11(), m7.m11()));
QVERIFY(fuzzyCompare(m5.m12(), m7.m12()));
QVERIFY(fuzzyCompare(m5.m21(), m7.m21()));
QVERIFY(fuzzyCompare(m5.m22(), m7.m22()));
QVERIFY(fuzzyCompare(m5.dx(), m7.dx()));
QVERIFY(fuzzyCompare(m5.dy(), m7.dy()));
}
// Test converting QTransform objects into QMatrix4x4 and then
// checking that transformations in the original perform the
// equivalent transformations in the new matrix.
void tst_QMatrixNxN::convertQTransform()
{
QTransform m1;
m1.translate(-3.5, 2.0);
QPointF p1 = m1.map(QPointF(100.0, 150.0));
QCOMPARE(p1.x(), 100.0 - 3.5);
QCOMPARE(p1.y(), 150.0 + 2.0);
QMatrix4x4 m2(m1);
QPointF p2 = m2 * QPointF(100.0, 150.0);
QCOMPARE((double)p2.x(), 100.0 - 3.5);
QCOMPARE((double)p2.y(), 150.0 + 2.0);
QVERIFY(m1 == m2.toTransform());
QTransform m3;
m3.scale(1.5, -2.0);
QPointF p3 = m3.map(QPointF(100.0, 150.0));
QCOMPARE(p3.x(), 1.5 * 100.0);
QCOMPARE(p3.y(), -2.0 * 150.0);
QMatrix4x4 m4(m3);
QPointF p4 = m4 * QPointF(100.0, 150.0);
QCOMPARE((double)p4.x(), 1.5 * 100.0);
QCOMPARE((double)p4.y(), -2.0 * 150.0);
QVERIFY(m3 == m4.toTransform());
QTransform m5;
m5.rotate(45.0);
QPointF p5 = m5.map(QPointF(100.0, 150.0));
QMatrix4x4 m6(m5);
QPointF p6 = m6 * QPointF(100.0, 150.0);
QVERIFY(fuzzyCompare(p5.x(), p6.x()));
QVERIFY(fuzzyCompare(p5.y(), p6.y()));
QTransform m7 = m6.toTransform();
QVERIFY(fuzzyCompare(m5.m11(), m7.m11()));
QVERIFY(fuzzyCompare(m5.m12(), m7.m12()));
QVERIFY(fuzzyCompare(m5.m21(), m7.m21()));
QVERIFY(fuzzyCompare(m5.m22(), m7.m22()));
QVERIFY(fuzzyCompare(m5.dx(), m7.dx()));
QVERIFY(fuzzyCompare(m5.dy(), m7.dy()));
QVERIFY(fuzzyCompare(m5.m13(), m7.m13()));
QVERIFY(fuzzyCompare(m5.m23(), m7.m23()));
QVERIFY(fuzzyCompare(m5.m33(), m7.m33()));
}
// Test filling matrices with specific values.
void tst_QMatrixNxN::fill()
{
QMatrix4x4 m1;
m1.fill(0.0f);
QVERIFY(isSame(m1, nullValues4));
static const qreal fillValues4[] =
{2.5f, 2.5f, 2.5f, 2.5f,
2.5f, 2.5f, 2.5f, 2.5f,
2.5f, 2.5f, 2.5f, 2.5f,
2.5f, 2.5f, 2.5f, 2.5f};
m1.fill(2.5f);
QVERIFY(isSame(m1, fillValues4));
QMatrix4x3 m2;
m2.fill(0.0f);
QVERIFY(isSame(m2, nullValues4x3));
static const qreal fillValues4x3[] =
{2.5f, 2.5f, 2.5f, 2.5f,
2.5f, 2.5f, 2.5f, 2.5f,
2.5f, 2.5f, 2.5f, 2.5f};
m2.fill(2.5f);
QVERIFY(isSame(m2, fillValues4x3));
}
// Test the mapRect() function for QRect and QRectF.
void tst_QMatrixNxN::mapRect_data()
{
QTest::addColumn<qreal>("x");
QTest::addColumn<qreal>("y");
QTest::addColumn<qreal>("width");
QTest::addColumn<qreal>("height");
QTest::newRow("null")
<< (qreal)0.0f << (qreal)0.0f << (qreal)0.0f << (qreal)0.0f;
QTest::newRow("rect")
<< (qreal)1.0f << (qreal)-20.5f << (qreal)100.0f << (qreal)63.75f;
}
void tst_QMatrixNxN::mapRect()
{
QFETCH(qreal, x);
QFETCH(qreal, y);
QFETCH(qreal, width);
QFETCH(qreal, height);
QRectF rect(x, y, width, height);
QRect recti(qRound(x), qRound(y), qRound(width), qRound(height));
QMatrix4x4 m1;
QVERIFY(m1.mapRect(rect) == rect);
QVERIFY(m1.mapRect(recti) == recti);
QMatrix4x4 m2;
m2.translate(-100.5f, 64.0f);
QRectF translated = rect.translated(-100.5f, 64.0f);
QRect translatedi = QRect(qRound(recti.x() - 100.5f), recti.y() + 64,
recti.width(), recti.height());
QVERIFY(m2.mapRect(rect) == translated);
QVERIFY(m2.mapRect(recti) == translatedi);
QMatrix4x4 m3;
m3.scale(-100.5f, 64.0f);
qreal scalex = x * -100.5f;
qreal scaley = y * 64.0f;
qreal scalewid = width * -100.5f;
qreal scaleht = height * 64.0f;
if (scalewid < 0.0f) {
scalewid = -scalewid;
scalex -= scalewid;
}
if (scaleht < 0.0f) {
scaleht = -scaleht;
scaley -= scaleht;
}
QRectF scaled(scalex, scaley, scalewid, scaleht);
QVERIFY(m3.mapRect(rect) == scaled);
scalex = recti.x() * -100.5f;
scaley = recti.y() * 64.0f;
scalewid = recti.width() * -100.5f;
scaleht = recti.height() * 64.0f;
if (scalewid < 0.0f) {
scalewid = -scalewid;
scalex -= scalewid;
}
if (scaleht < 0.0f) {
scaleht = -scaleht;
scaley -= scaleht;
}
QRect scaledi(qRound(scalex), qRound(scaley),
qRound(scalewid), qRound(scaleht));
QVERIFY(m3.mapRect(recti) == scaledi);
QMatrix4x4 m4;
m4.translate(-100.5f, 64.0f);
m4.scale(-2.5f, 4.0f);
qreal transx1 = x * -2.5f - 100.5f;
qreal transy1 = y * 4.0f + 64.0f;
qreal transx2 = (x + width) * -2.5f - 100.5f;
qreal transy2 = (y + height) * 4.0f + 64.0f;
if (transx1 > transx2)
qSwap(transx1, transx2);
if (transy1 > transy2)
qSwap(transy1, transy2);
QRectF trans(transx1, transy1, transx2 - transx1, transy2 - transy1);
QVERIFY(m4.mapRect(rect) == trans);
transx1 = recti.x() * -2.5f - 100.5f;
transy1 = recti.y() * 4.0f + 64.0f;
transx2 = (recti.x() + recti.width()) * -2.5f - 100.5f;
transy2 = (recti.y() + recti.height()) * 4.0f + 64.0f;
if (transx1 > transx2)
qSwap(transx1, transx2);
if (transy1 > transy2)
qSwap(transy1, transy2);
QRect transi(qRound(transx1), qRound(transy1),
qRound(transx2) - qRound(transx1),
qRound(transy2) - qRound(transy1));
QVERIFY(m4.mapRect(recti) == transi);
m4.rotate(45.0f, 0.0f, 0.0f, 1.0f);
QTransform t4;
t4.translate(-100.5f, 64.0f);
t4.scale(-2.5f, 4.0f);
t4.rotate(45.0f);
QRectF mr = m4.mapRect(rect);
QRectF tr = t4.mapRect(rect);
QVERIFY(fuzzyCompare(mr.x(), tr.x()));
QVERIFY(fuzzyCompare(mr.y(), tr.y()));
QVERIFY(fuzzyCompare(mr.width(), tr.width()));
QVERIFY(fuzzyCompare(mr.height(), tr.height()));
QRect mri = m4.mapRect(recti);
QRect tri = t4.mapRect(recti);
QVERIFY(mri == tri);
}
void tst_QMatrixNxN::mapVector_data()
{
QTest::addColumn<void *>("mValues");
QTest::newRow("null")
<< (void *)nullValues4;
QTest::newRow("identity")
<< (void *)identityValues4;
QTest::newRow("unique")
<< (void *)uniqueValues4;
static const qreal scale[] =
{2.0f, 0.0f, 0.0f, 0.0f,
0.0f, 11.0f, 0.0f, 0.0f,
0.0f, 0.0f, -6.5f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f};
QTest::newRow("scale")
<< (void *)scale;
static const qreal scaleTranslate[] =
{2.0f, 0.0f, 0.0f, 1.0f,
0.0f, 11.0f, 0.0f, 2.0f,
0.0f, 0.0f, -6.5f, 3.0f,
0.0f, 0.0f, 0.0f, 1.0f};
QTest::newRow("scaleTranslate")
<< (void *)scaleTranslate;
static const qreal translate[] =
{1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 1.0f, 0.0f, 2.0f,
0.0f, 0.0f, 1.0f, 3.0f,
0.0f, 0.0f, 0.0f, 1.0f};
QTest::newRow("translate")
<< (void *)translate;
}
void tst_QMatrixNxN::mapVector()
{
QFETCH(void *, mValues);
QMatrix4x4 m1((const qreal *)mValues);
m1.optimize();
QVector3D v(3.5f, -1.0f, 2.5f);
QVector3D expected
(v.x() * m1(0, 0) + v.y() * m1(0, 1) + v.z() * m1(0, 2),
v.x() * m1(1, 0) + v.y() * m1(1, 1) + v.z() * m1(1, 2),
v.x() * m1(2, 0) + v.y() * m1(2, 1) + v.z() * m1(2, 2));
QVector3D actual = m1.mapVector(v);
QVERIFY(fuzzyCompare(actual.x(), expected.x()));
QVERIFY(fuzzyCompare(actual.y(), expected.y()));
QVERIFY(fuzzyCompare(actual.z(), expected.z()));
}
class tst_QMatrixNxN4x4Properties : public QObject
{
Q_OBJECT
Q_PROPERTY(QMatrix4x4 matrix READ matrix WRITE setMatrix)
public:
tst_QMatrixNxN4x4Properties(QObject *parent = 0) : QObject(parent) {}
QMatrix4x4 matrix() const { return m; }
void setMatrix(const QMatrix4x4& value) { m = value; }
private:
QMatrix4x4 m;
};
// Test getting and setting matrix properties via the metaobject system.
void tst_QMatrixNxN::properties()
{
tst_QMatrixNxN4x4Properties obj;
QMatrix4x4 m1(uniqueValues4);
obj.setMatrix(m1);
QMatrix4x4 m2 = qVariantValue<QMatrix4x4>(obj.property("matrix"));
QVERIFY(isSame(m2, uniqueValues4));
QMatrix4x4 m3(transposedValues4);
obj.setProperty("matrix", qVariantFromValue(m3));
m2 = qVariantValue<QMatrix4x4>(obj.property("matrix"));
QVERIFY(isSame(m2, transposedValues4));
}
void tst_QMatrixNxN::metaTypes()
{
QVERIFY(QMetaType::type("QMatrix4x4") == QMetaType::QMatrix4x4);
QCOMPARE(QByteArray(QMetaType::typeName(QMetaType::QMatrix4x4)),
QByteArray("QMatrix4x4"));
QVERIFY(QMetaType::isRegistered(QMetaType::QMatrix4x4));
QVERIFY(qMetaTypeId<QMatrix4x4>() == QMetaType::QMatrix4x4);
}
QTEST_APPLESS_MAIN(tst_QMatrixNxN)
#include "tst_qmatrixnxn.moc"