1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtTest/QtTest>
30#include <QtCore/qmath.h>
31#include <QtGui/qmatrix4x4.h>
32
33class tst_QMatrixNxN : public QObject
34{
35 Q_OBJECT
36public:
37 tst_QMatrixNxN() {}
38 ~tst_QMatrixNxN() {}
39
40private slots:
41 void create2x2();
42 void create3x3();
43 void create4x4();
44 void create4x3();
45
46 void isIdentity2x2();
47 void isIdentity3x3();
48 void isIdentity4x4();
49 void isIdentity4x3();
50
51 void compare2x2();
52 void compare3x3();
53 void compare4x4();
54 void compare4x3();
55
56 void transposed2x2();
57 void transposed3x3();
58 void transposed4x4();
59 void transposed4x3();
60
61 void add2x2_data();
62 void add2x2();
63 void add3x3_data();
64 void add3x3();
65 void add4x4_data();
66 void add4x4();
67 void add4x3_data();
68 void add4x3();
69
70 void subtract2x2_data();
71 void subtract2x2();
72 void subtract3x3_data();
73 void subtract3x3();
74 void subtract4x4_data();
75 void subtract4x4();
76 void subtract4x3_data();
77 void subtract4x3();
78
79 void multiply2x2_data();
80 void multiply2x2();
81 void multiply3x3_data();
82 void multiply3x3();
83 void multiply4x4_data();
84 void multiply4x4();
85 void multiply4x3_data();
86 void multiply4x3();
87
88 void multiplyFactor2x2_data();
89 void multiplyFactor2x2();
90 void multiplyFactor3x3_data();
91 void multiplyFactor3x3();
92 void multiplyFactor4x4_data();
93 void multiplyFactor4x4();
94 void multiplyFactor4x3_data();
95 void multiplyFactor4x3();
96
97 void divideFactor2x2_data();
98 void divideFactor2x2();
99 void divideFactor3x3_data();
100 void divideFactor3x3();
101 void divideFactor4x4_data();
102 void divideFactor4x4();
103 void divideFactor4x3_data();
104 void divideFactor4x3();
105
106 void negate2x2_data();
107 void negate2x2();
108 void negate3x3_data();
109 void negate3x3();
110 void negate4x4_data();
111 void negate4x4();
112 void negate4x3_data();
113 void negate4x3();
114
115 void inverted4x4_data();
116 void inverted4x4();
117
118 void orthonormalInverse4x4();
119
120 void scale4x4_data();
121 void scale4x4();
122
123 void translate4x4_data();
124 void translate4x4();
125
126 void rotate4x4_data();
127 void rotate4x4();
128
129 void normalMatrix_data();
130 void normalMatrix();
131
132 void optimizedTransforms();
133
134 void ortho();
135 void frustum();
136 void perspective();
137 void viewport();
138 void flipCoordinates();
139
140 void convertGeneric();
141
142 void optimize_data();
143 void optimize();
144
145 void columnsAndRows();
146
147 void convertQMatrix();
148 void convertQTransform();
149
150 void fill();
151
152 void mapRect_data();
153 void mapRect();
154
155 void mapVector_data();
156 void mapVector();
157
158 void properties();
159 void metaTypes();
160
161private:
162 static void setMatrix(QMatrix2x2& m, const float *values);
163 static void setMatrixDirect(QMatrix2x2& m, const float *values);
164 static bool isSame(const QMatrix2x2& m, const float *values);
165 static bool isIdentity(const QMatrix2x2& m);
166
167 static void setMatrix(QMatrix3x3& m, const float *values);
168 static void setMatrixDirect(QMatrix3x3& m, const float *values);
169 static bool isSame(const QMatrix3x3& m, const float *values);
170 static bool isIdentity(const QMatrix3x3& m);
171
172 static void setMatrix(QMatrix4x4& m, const float *values);
173 static void setMatrixDirect(QMatrix4x4& m, const float *values);
174 static bool isSame(const QMatrix4x4& m, const float *values);
175 static bool isIdentity(const QMatrix4x4& m);
176
177 static void setMatrix(QMatrix4x3& m, const float *values);
178 static void setMatrixDirect(QMatrix4x3& m, const float *values);
179 static bool isSame(const QMatrix4x3& m, const float *values);
180 static bool isIdentity(const QMatrix4x3& m);
181};
182
183static const float nullValues2[] =
184 {0.0f, 0.0f,
185 0.0f, 0.0f};
186
187static float const identityValues2[16] =
188 {1.0f, 0.0f,
189 0.0f, 1.0f};
190
191static const float doubleIdentity2[] =
192 {2.0f, 0.0f,
193 0.0f, 2.0f};
194
195static float const uniqueValues2[16] =
196 {1.0f, 2.0f,
197 5.0f, 6.0f};
198
199static float const transposedValues2[16] =
200 {1.0f, 5.0f,
201 2.0f, 6.0f};
202
203static const float nullValues3[] =
204 {0.0f, 0.0f, 0.0f,
205 0.0f, 0.0f, 0.0f,
206 0.0f, 0.0f, 0.0f};
207
208static float const identityValues3[16] =
209 {1.0f, 0.0f, 0.0f,
210 0.0f, 1.0f, 0.0f,
211 0.0f, 0.0f, 1.0f};
212
213static const float doubleIdentity3[] =
214 {2.0f, 0.0f, 0.0f,
215 0.0f, 2.0f, 0.0f,
216 0.0f, 0.0f, 2.0f};
217
218static float const uniqueValues3[16] =
219 {1.0f, 2.0f, 3.0f,
220 5.0f, 6.0f, 7.0f,
221 9.0f, 10.0f, 11.0f};
222
223static float const transposedValues3[16] =
224 {1.0f, 5.0f, 9.0f,
225 2.0f, 6.0f, 10.0f,
226 3.0f, 7.0f, 11.0f};
227
228static const float nullValues4[] =
229 {0.0f, 0.0f, 0.0f, 0.0f,
230 0.0f, 0.0f, 0.0f, 0.0f,
231 0.0f, 0.0f, 0.0f, 0.0f,
232 0.0f, 0.0f, 0.0f, 0.0f};
233
234static float const identityValues4[16] =
235 {1.0f, 0.0f, 0.0f, 0.0f,
236 0.0f, 1.0f, 0.0f, 0.0f,
237 0.0f, 0.0f, 1.0f, 0.0f,
238 0.0f, 0.0f, 0.0f, 1.0f};
239
240static const float doubleIdentity4[] =
241 {2.0f, 0.0f, 0.0f, 0.0f,
242 0.0f, 2.0f, 0.0f, 0.0f,
243 0.0f, 0.0f, 2.0f, 0.0f,
244 0.0f, 0.0f, 0.0f, 2.0f};
245
246static float const uniqueValues4[16] =
247 {1.0f, 2.0f, 3.0f, 4.0f,
248 5.0f, 6.0f, 7.0f, 8.0f,
249 9.0f, 10.0f, 11.0f, 12.0f,
250 13.0f, 14.0f, 15.0f, 16.0f};
251
252static float const transposedValues4[16] =
253 {1.0f, 5.0f, 9.0f, 13.0f,
254 2.0f, 6.0f, 10.0f, 14.0f,
255 3.0f, 7.0f, 11.0f, 15.0f,
256 4.0f, 8.0f, 12.0f, 16.0f};
257
258static const float nullValues4x3[] =
259 {0.0f, 0.0f, 0.0f, 0.0f,
260 0.0f, 0.0f, 0.0f, 0.0f,
261 0.0f, 0.0f, 0.0f, 0.0f};
262
263static float const identityValues4x3[12] =
264 {1.0f, 0.0f, 0.0f, 0.0f,
265 0.0f, 1.0f, 0.0f, 0.0f,
266 0.0f, 0.0f, 1.0f, 0.0f};
267
268static float const doubleIdentity4x3[12] =
269 {2.0f, 0.0f, 0.0f, 0.0f,
270 0.0f, 2.0f, 0.0f, 0.0f,
271 0.0f, 0.0f, 2.0f, 0.0f};
272
273static float const uniqueValues4x3[12] =
274 {1.0f, 2.0f, 3.0f, 4.0f,
275 5.0f, 6.0f, 7.0f, 8.0f,
276 9.0f, 10.0f, 11.0f, 12.0f};
277
278static float const transposedValues3x4[12] =
279 {1.0f, 5.0f, 9.0f,
280 2.0f, 6.0f, 10.0f,
281 3.0f, 7.0f, 11.0f,
282 4.0f, 8.0f, 12.0f};
283
284// We use a slightly better implementation of qFuzzyCompare here that
285// handles the case where one of the values is exactly 0
286static inline bool fuzzyCompare(float p1, float p2)
287{
288 if (qFuzzyIsNull(f: p1))
289 return qFuzzyIsNull(f: p2);
290 else if (qFuzzyIsNull(f: p2))
291 return false;
292 else
293 return qFuzzyCompare(p1, p2);
294}
295
296// Set a matrix to a specified array of values, which are assumed
297// to be in row-major order. This sets the values using floating-point.
298void tst_QMatrixNxN::setMatrix(QMatrix2x2& m, const float *values)
299{
300 for (int row = 0; row < 2; ++row)
301 for (int col = 0; col < 2; ++col)
302 m(row, col) = values[row * 2 + col];
303}
304void tst_QMatrixNxN::setMatrix(QMatrix3x3& m, const float *values)
305{
306 for (int row = 0; row < 3; ++row)
307 for (int col = 0; col < 3; ++col)
308 m(row, col) = values[row * 3 + col];
309}
310void tst_QMatrixNxN::setMatrix(QMatrix4x4& m, const float *values)
311{
312 for (int row = 0; row < 4; ++row)
313 for (int col = 0; col < 4; ++col)
314 m(row, col) = values[row * 4 + col];
315}
316void tst_QMatrixNxN::setMatrix(QMatrix4x3& m, const float *values)
317{
318 for (int row = 0; row < 3; ++row)
319 for (int col = 0; col < 4; ++col)
320 m(row, col) = values[row * 4 + col];
321}
322
323// Set a matrix to a specified array of values, which are assumed
324// to be in row-major order. This sets the values directly into
325// the internal data() array.
326void tst_QMatrixNxN::setMatrixDirect(QMatrix2x2& m, const float *values)
327{
328 float *data = m.data();
329 for (int row = 0; row < 2; ++row) {
330 for (int col = 0; col < 2; ++col) {
331 data[row + col * 2] = values[row * 2 + col];
332 }
333 }
334}
335void tst_QMatrixNxN::setMatrixDirect(QMatrix3x3& m, const float *values)
336{
337 float *data = m.data();
338 for (int row = 0; row < 3; ++row) {
339 for (int col = 0; col < 3; ++col) {
340 data[row + col * 3] = values[row * 3 + col];
341 }
342 }
343}
344void tst_QMatrixNxN::setMatrixDirect(QMatrix4x4& m, const float *values)
345{
346 float *data = m.data();
347 for (int row = 0; row < 4; ++row) {
348 for (int col = 0; col < 4; ++col) {
349 data[row + col * 4] = values[row * 4 + col];
350 }
351 }
352}
353void tst_QMatrixNxN::setMatrixDirect(QMatrix4x3& m, const float *values)
354{
355 float *data = m.data();
356 for (int row = 0; row < 3; ++row) {
357 for (int col = 0; col < 4; ++col) {
358 data[row + col * 3] = values[row * 4 + col];
359 }
360 }
361}
362
363// Determine if a matrix is the same as a specified array of values.
364// The values are assumed to be specified in row-major order.
365bool tst_QMatrixNxN::isSame(const QMatrix2x2& m, const float *values)
366{
367 const float *mv = m.constData();
368 for (int row = 0; row < 2; ++row) {
369 for (int col = 0; col < 2; ++col) {
370 // Check the values using the operator() function.
371 if (!fuzzyCompare(p1: m(row, col), p2: values[row * 2 + col])) {
372 qDebug() << "floating-point failure at" << row << col << "actual =" << m(row, col) << "expected =" << values[row * 2 + col];
373 return false;
374 }
375
376 // Check the values using direct access, which verifies that the values
377 // are stored internally in column-major order.
378 if (!fuzzyCompare(p1: mv[col * 2 + row], p2: values[row * 2 + col])) {
379 qDebug() << "column floating-point failure at" << row << col << "actual =" << mv[col * 2 + row] << "expected =" << values[row * 2 + col];
380 return false;
381 }
382 }
383 }
384 return true;
385}
386bool tst_QMatrixNxN::isSame(const QMatrix3x3& m, const float *values)
387{
388 const float *mv = m.constData();
389 for (int row = 0; row < 3; ++row) {
390 for (int col = 0; col < 3; ++col) {
391 // Check the values using the operator() access function.
392 if (!fuzzyCompare(p1: m(row, col), p2: values[row * 3 + col])) {
393 qDebug() << "floating-point failure at" << row << col << "actual =" << m(row, col) << "expected =" << values[row * 3 + col];
394 return false;
395 }
396
397 // Check the values using direct access, which verifies that the values
398 // are stored internally in column-major order.
399 if (!fuzzyCompare(p1: mv[col * 3 + row], p2: values[row * 3 + col])) {
400 qDebug() << "column floating-point failure at" << row << col << "actual =" << mv[col * 3 + row] << "expected =" << values[row * 3 + col];
401 return false;
402 }
403 }
404 }
405 return true;
406}
407bool tst_QMatrixNxN::isSame(const QMatrix4x4& m, const float *values)
408{
409 const float *mv = m.constData();
410 for (int row = 0; row < 4; ++row) {
411 for (int col = 0; col < 4; ++col) {
412 // Check the values using the operator() access function.
413 if (!fuzzyCompare(p1: m(row, col), p2: values[row * 4 + col])) {
414 qDebug() << "floating-point failure at" << row << col << "actual =" << m(row, col) << "expected =" << values[row * 4 + col];
415 return false;
416 }
417
418 // Check the values using direct access, which verifies that the values
419 // are stored internally in column-major order.
420 if (!fuzzyCompare(p1: mv[col * 4 + row], p2: values[row * 4 + col])) {
421 qDebug() << "column floating-point failure at" << row << col << "actual =" << mv[col * 4 + row] << "expected =" << values[row * 4 + col];
422 return false;
423 }
424 }
425 }
426 return true;
427}
428bool tst_QMatrixNxN::isSame(const QMatrix4x3& m, const float *values)
429{
430 const float *mv = m.constData();
431 for (int row = 0; row < 3; ++row) {
432 for (int col = 0; col < 4; ++col) {
433 // Check the values using the operator() access function.
434 if (!fuzzyCompare(p1: m(row, col), p2: values[row * 4 + col])) {
435 qDebug() << "floating-point failure at" << row << col << "actual =" << m(row, col) << "expected =" << values[row * 4 + col];
436 return false;
437 }
438
439 // Check the values using direct access, which verifies that the values
440 // are stored internally in column-major order.
441 if (!fuzzyCompare(p1: mv[col * 3 + row], p2: values[row * 4 + col])) {
442 qDebug() << "column floating-point failure at" << row << col << "actual =" << mv[col * 3 + row] << "expected =" << values[row * 4 + col];
443 return false;
444 }
445 }
446 }
447 return true;
448}
449
450// Determine if a matrix is the identity.
451bool tst_QMatrixNxN::isIdentity(const QMatrix2x2& m)
452{
453 return isSame(m, values: identityValues2);
454}
455bool tst_QMatrixNxN::isIdentity(const QMatrix3x3& m)
456{
457 return isSame(m, values: identityValues3);
458}
459bool tst_QMatrixNxN::isIdentity(const QMatrix4x4& m)
460{
461 return isSame(m, values: identityValues4);
462}
463bool tst_QMatrixNxN::isIdentity(const QMatrix4x3& m)
464{
465 return isSame(m, values: identityValues4x3);
466}
467
468// Test the creation of QMatrix2x2 objects in various ways:
469// construct, copy, and modify.
470void tst_QMatrixNxN::create2x2()
471{
472 QMatrix2x2 m1;
473 QVERIFY(isIdentity(m1));
474 QVERIFY(m1.isIdentity());
475
476 QMatrix2x2 m2;
477 setMatrix(m&: m2, values: uniqueValues2);
478 QVERIFY(isSame(m2, uniqueValues2));
479 QVERIFY(!m2.isIdentity());
480
481 QMatrix2x2 m3;
482 setMatrixDirect(m&: m3, values: uniqueValues2);
483 QVERIFY(isSame(m3, uniqueValues2));
484
485 QMatrix2x2 m4(m3);
486 QVERIFY(isSame(m4, uniqueValues2));
487
488 QMatrix2x2 m5;
489 m5 = m3;
490 QVERIFY(isSame(m5, uniqueValues2));
491
492 m5.setToIdentity();
493 QVERIFY(isIdentity(m5));
494
495 QMatrix2x2 m6(uniqueValues2);
496 QVERIFY(isSame(m6, uniqueValues2));
497 float vals[4];
498 m6.copyDataTo(values: vals);
499 for (int index = 0; index < 4; ++index)
500 QCOMPARE(vals[index], uniqueValues2[index]);
501}
502
503// Test the creation of QMatrix3x3 objects in various ways:
504// construct, copy, and modify.
505void tst_QMatrixNxN::create3x3()
506{
507 QMatrix3x3 m1;
508 QVERIFY(isIdentity(m1));
509 QVERIFY(m1.isIdentity());
510
511 QMatrix3x3 m2;
512 setMatrix(m&: m2, values: uniqueValues3);
513 QVERIFY(isSame(m2, uniqueValues3));
514 QVERIFY(!m2.isIdentity());
515
516 QMatrix3x3 m3;
517 setMatrixDirect(m&: m3, values: uniqueValues3);
518 QVERIFY(isSame(m3, uniqueValues3));
519
520 QMatrix3x3 m4(m3);
521 QVERIFY(isSame(m4, uniqueValues3));
522
523 QMatrix3x3 m5;
524 m5 = m3;
525 QVERIFY(isSame(m5, uniqueValues3));
526
527 m5.setToIdentity();
528 QVERIFY(isIdentity(m5));
529
530 QMatrix3x3 m6(uniqueValues3);
531 QVERIFY(isSame(m6, uniqueValues3));
532 float vals[9];
533 m6.copyDataTo(values: vals);
534 for (int index = 0; index < 9; ++index)
535 QCOMPARE(vals[index], uniqueValues3[index]);
536}
537
538// Test the creation of QMatrix4x4 objects in various ways:
539// construct, copy, and modify.
540void tst_QMatrixNxN::create4x4()
541{
542 QMatrix4x4 m1;
543 QVERIFY(isIdentity(m1));
544 QVERIFY(m1.isIdentity());
545
546 QMatrix4x4 m2;
547 setMatrix(m&: m2, values: uniqueValues4);
548 QVERIFY(isSame(m2, uniqueValues4));
549 QVERIFY(!m2.isIdentity());
550
551 QMatrix4x4 m3;
552 setMatrixDirect(m&: m3, values: uniqueValues4);
553 QVERIFY(isSame(m3, uniqueValues4));
554
555 QMatrix4x4 m4(m3);
556 QVERIFY(isSame(m4, uniqueValues4));
557
558 QMatrix4x4 m5;
559 m5 = m3;
560 QVERIFY(isSame(m5, uniqueValues4));
561
562 m5.setToIdentity();
563 QVERIFY(isIdentity(m5));
564
565 QMatrix4x4 m6(uniqueValues4);
566 QVERIFY(isSame(m6, uniqueValues4));
567 float vals[16];
568 m6.copyDataTo(values: vals);
569 for (int index = 0; index < 16; ++index)
570 QCOMPARE(vals[index], uniqueValues4[index]);
571
572 QMatrix4x4 m8
573 (uniqueValues4[0], uniqueValues4[1], uniqueValues4[2], uniqueValues4[3],
574 uniqueValues4[4], uniqueValues4[5], uniqueValues4[6], uniqueValues4[7],
575 uniqueValues4[8], uniqueValues4[9], uniqueValues4[10], uniqueValues4[11],
576 uniqueValues4[12], uniqueValues4[13], uniqueValues4[14], uniqueValues4[15]);
577 QVERIFY(isSame(m8, uniqueValues4));
578}
579
580// Test the creation of QMatrix4x3 objects in various ways:
581// construct, copy, and modify.
582void tst_QMatrixNxN::create4x3()
583{
584 QMatrix4x3 m1;
585 QVERIFY(isIdentity(m1));
586 QVERIFY(m1.isIdentity());
587
588 QMatrix4x3 m2;
589 setMatrix(m&: m2, values: uniqueValues4x3);
590 QVERIFY(isSame(m2, uniqueValues4x3));
591 QVERIFY(!m2.isIdentity());
592
593 QMatrix4x3 m3;
594 setMatrixDirect(m&: m3, values: uniqueValues4x3);
595 QVERIFY(isSame(m3, uniqueValues4x3));
596
597 QMatrix4x3 m4(m3);
598 QVERIFY(isSame(m4, uniqueValues4x3));
599
600 QMatrix4x3 m5;
601 m5 = m3;
602 QVERIFY(isSame(m5, uniqueValues4x3));
603
604 m5.setToIdentity();
605 QVERIFY(isIdentity(m5));
606
607 QMatrix4x3 m6(uniqueValues4x3);
608 QVERIFY(isSame(m6, uniqueValues4x3));
609 float vals[12];
610 m6.copyDataTo(values: vals);
611 for (int index = 0; index < 12; ++index)
612 QCOMPARE(vals[index], uniqueValues4x3[index]);
613}
614
615// Test isIdentity() for 2x2 matrices.
616void tst_QMatrixNxN::isIdentity2x2()
617{
618 for (int i = 0; i < 2 * 2; ++i) {
619 QMatrix2x2 m;
620 QVERIFY(m.isIdentity());
621 m.data()[i] = 42.0f;
622 QVERIFY(!m.isIdentity());
623 }
624}
625
626// Test isIdentity() for 3x3 matrices.
627void tst_QMatrixNxN::isIdentity3x3()
628{
629 for (int i = 0; i < 3 * 3; ++i) {
630 QMatrix3x3 m;
631 QVERIFY(m.isIdentity());
632 m.data()[i] = 42.0f;
633 QVERIFY(!m.isIdentity());
634 }
635}
636
637// Test isIdentity() for 4x4 matrices.
638void tst_QMatrixNxN::isIdentity4x4()
639{
640 for (int i = 0; i < 4 * 4; ++i) {
641 QMatrix4x4 m;
642 QVERIFY(m.isIdentity());
643 m.data()[i] = 42.0f;
644 QVERIFY(!m.isIdentity());
645 }
646
647 // Force the "Identity" flag bit to be lost and check again.
648 QMatrix4x4 m2;
649 m2.data()[0] = 1.0f;
650 QVERIFY(m2.isIdentity());
651}
652
653// Test isIdentity() for 4x3 matrices.
654void tst_QMatrixNxN::isIdentity4x3()
655{
656 for (int i = 0; i < 4 * 3; ++i) {
657 QMatrix4x3 m;
658 QVERIFY(m.isIdentity());
659 m.data()[i] = 42.0f;
660 QVERIFY(!m.isIdentity());
661 }
662}
663
664// Test 2x2 matrix comparisons.
665void tst_QMatrixNxN::compare2x2()
666{
667 QMatrix2x2 m1(uniqueValues2);
668 QMatrix2x2 m2(uniqueValues2);
669 QMatrix2x2 m3(transposedValues2);
670
671 QCOMPARE(m1, m2);
672 QVERIFY(!(m1 != m2));
673 QVERIFY(m1 != m3);
674 QVERIFY(!(m1 == m3));
675}
676
677// Test 3x3 matrix comparisons.
678void tst_QMatrixNxN::compare3x3()
679{
680 QMatrix3x3 m1(uniqueValues3);
681 QMatrix3x3 m2(uniqueValues3);
682 QMatrix3x3 m3(transposedValues3);
683
684 QCOMPARE(m1, m2);
685 QVERIFY(!(m1 != m2));
686 QVERIFY(m1 != m3);
687 QVERIFY(!(m1 == m3));
688}
689
690// Test 4x4 matrix comparisons.
691void tst_QMatrixNxN::compare4x4()
692{
693 QMatrix4x4 m1(uniqueValues4);
694 QMatrix4x4 m2(uniqueValues4);
695 QMatrix4x4 m3(transposedValues4);
696
697 QCOMPARE(m1, m2);
698 QVERIFY(!(m1 != m2));
699 QVERIFY(m1 != m3);
700 QVERIFY(!(m1 == m3));
701}
702
703// Test 4x3 matrix comparisons.
704void tst_QMatrixNxN::compare4x3()
705{
706 QMatrix4x3 m1(uniqueValues4x3);
707 QMatrix4x3 m2(uniqueValues4x3);
708 QMatrix4x3 m3(transposedValues3x4);
709
710 QCOMPARE(m1, m2);
711 QVERIFY(!(m1 != m2));
712 QVERIFY(m1 != m3);
713 QVERIFY(!(m1 == m3));
714}
715
716// Test matrix 2x2 transpose operations.
717void tst_QMatrixNxN::transposed2x2()
718{
719 // Transposing the identity should result in the identity.
720 QMatrix2x2 m1;
721 QMatrix2x2 m2 = m1.transposed();
722 QVERIFY(isIdentity(m2));
723
724 // Transpose a more interesting matrix that allows us to track
725 // exactly where each source element ends up.
726 QMatrix2x2 m3(uniqueValues2);
727 QMatrix2x2 m4 = m3.transposed();
728 QVERIFY(isSame(m4, transposedValues2));
729
730 // Transpose in-place, just to check that the compiler is sane.
731 m3 = m3.transposed();
732 QVERIFY(isSame(m3, transposedValues2));
733}
734
735// Test matrix 3x3 transpose operations.
736void tst_QMatrixNxN::transposed3x3()
737{
738 // Transposing the identity should result in the identity.
739 QMatrix3x3 m1;
740 QMatrix3x3 m2 = m1.transposed();
741 QVERIFY(isIdentity(m2));
742
743 // Transpose a more interesting matrix that allows us to track
744 // exactly where each source element ends up.
745 QMatrix3x3 m3(uniqueValues3);
746 QMatrix3x3 m4 = m3.transposed();
747 QVERIFY(isSame(m4, transposedValues3));
748
749 // Transpose in-place, just to check that the compiler is sane.
750 m3 = m3.transposed();
751 QVERIFY(isSame(m3, transposedValues3));
752}
753
754// Test matrix 4x4 transpose operations.
755void tst_QMatrixNxN::transposed4x4()
756{
757 // Transposing the identity should result in the identity.
758 QMatrix4x4 m1;
759 QMatrix4x4 m2 = m1.transposed();
760 QVERIFY(isIdentity(m2));
761
762 // Transpose a more interesting matrix that allows us to track
763 // exactly where each source element ends up.
764 QMatrix4x4 m3(uniqueValues4);
765 QMatrix4x4 m4 = m3.transposed();
766 QVERIFY(isSame(m4, transposedValues4));
767
768 // Transpose in-place, just to check that the compiler is sane.
769 m3 = m3.transposed();
770 QVERIFY(isSame(m3, transposedValues4));
771}
772
773// Test matrix 4x3 transpose operations.
774void tst_QMatrixNxN::transposed4x3()
775{
776 QMatrix4x3 m3(uniqueValues4x3);
777 QMatrix3x4 m4 = m3.transposed();
778 float values[12];
779 m4.copyDataTo(values);
780 for (int index = 0; index < 12; ++index)
781 QCOMPARE(values[index], transposedValues3x4[index]);
782}
783
784// Test matrix addition for 2x2 matrices.
785void tst_QMatrixNxN::add2x2_data()
786{
787 QTest::addColumn<void *>(name: "m1Values");
788 QTest::addColumn<void *>(name: "m2Values");
789 QTest::addColumn<void *>(name: "m3Values");
790
791 QTest::newRow(dataTag: "null")
792 << (void *)nullValues2 << (void *)nullValues2 << (void *)nullValues2;
793
794 QTest::newRow(dataTag: "identity/null")
795 << (void *)identityValues2 << (void *)nullValues2 << (void *)identityValues2;
796
797 QTest::newRow(dataTag: "identity/identity")
798 << (void *)identityValues2 << (void *)identityValues2 << (void *)doubleIdentity2;
799
800 static float const sumValues[16] =
801 {2.0f, 7.0f,
802 7.0f, 12.0f};
803 QTest::newRow(dataTag: "unique")
804 << (void *)uniqueValues2 << (void *)transposedValues2 << (void *)sumValues;
805}
806void tst_QMatrixNxN::add2x2()
807{
808 QFETCH(void *, m1Values);
809 QFETCH(void *, m2Values);
810 QFETCH(void *, m3Values);
811
812 QMatrix2x2 m1((const float *)m1Values);
813 QMatrix2x2 m2((const float *)m2Values);
814
815 QMatrix2x2 m4(m1);
816 m4 += m2;
817 QVERIFY(isSame(m4, (const float *)m3Values));
818
819 QMatrix2x2 m5;
820 m5 = m1 + m2;
821 QVERIFY(isSame(m5, (const float *)m3Values));
822}
823
824// Test matrix addition for 3x3 matrices.
825void tst_QMatrixNxN::add3x3_data()
826{
827 QTest::addColumn<void *>(name: "m1Values");
828 QTest::addColumn<void *>(name: "m2Values");
829 QTest::addColumn<void *>(name: "m3Values");
830
831 QTest::newRow(dataTag: "null")
832 << (void *)nullValues3 << (void *)nullValues3 << (void *)nullValues3;
833
834 QTest::newRow(dataTag: "identity/null")
835 << (void *)identityValues3 << (void *)nullValues3 << (void *)identityValues3;
836
837 QTest::newRow(dataTag: "identity/identity")
838 << (void *)identityValues3 << (void *)identityValues3 << (void *)doubleIdentity3;
839
840 static float const sumValues[16] =
841 {2.0f, 7.0f, 12.0f,
842 7.0f, 12.0f, 17.0f,
843 12.0f, 17.0f, 22.0f};
844 QTest::newRow(dataTag: "unique")
845 << (void *)uniqueValues3 << (void *)transposedValues3 << (void *)sumValues;
846}
847void tst_QMatrixNxN::add3x3()
848{
849 QFETCH(void *, m1Values);
850 QFETCH(void *, m2Values);
851 QFETCH(void *, m3Values);
852
853 QMatrix3x3 m1((const float *)m1Values);
854 QMatrix3x3 m2((const float *)m2Values);
855
856 QMatrix3x3 m4(m1);
857 m4 += m2;
858 QVERIFY(isSame(m4, (const float *)m3Values));
859
860 QMatrix3x3 m5;
861 m5 = m1 + m2;
862 QVERIFY(isSame(m5, (const float *)m3Values));
863}
864
865// Test matrix addition for 4x4 matrices.
866void tst_QMatrixNxN::add4x4_data()
867{
868 QTest::addColumn<void *>(name: "m1Values");
869 QTest::addColumn<void *>(name: "m2Values");
870 QTest::addColumn<void *>(name: "m3Values");
871
872 QTest::newRow(dataTag: "null")
873 << (void *)nullValues4 << (void *)nullValues4 << (void *)nullValues4;
874
875 QTest::newRow(dataTag: "identity/null")
876 << (void *)identityValues4 << (void *)nullValues4 << (void *)identityValues4;
877
878 QTest::newRow(dataTag: "identity/identity")
879 << (void *)identityValues4 << (void *)identityValues4 << (void *)doubleIdentity4;
880
881 static float const sumValues[16] =
882 {2.0f, 7.0f, 12.0f, 17.0f,
883 7.0f, 12.0f, 17.0f, 22.0f,
884 12.0f, 17.0f, 22.0f, 27.0f,
885 17.0f, 22.0f, 27.0f, 32.0f};
886 QTest::newRow(dataTag: "unique")
887 << (void *)uniqueValues4 << (void *)transposedValues4 << (void *)sumValues;
888}
889void tst_QMatrixNxN::add4x4()
890{
891 QFETCH(void *, m1Values);
892 QFETCH(void *, m2Values);
893 QFETCH(void *, m3Values);
894
895 QMatrix4x4 m1((const float *)m1Values);
896 QMatrix4x4 m2((const float *)m2Values);
897
898 QMatrix4x4 m4(m1);
899 m4 += m2;
900 QVERIFY(isSame(m4, (const float *)m3Values));
901
902 QMatrix4x4 m5;
903 m5 = m1 + m2;
904 QVERIFY(isSame(m5, (const float *)m3Values));
905}
906
907// Test matrix addition for 4x3 matrices.
908void tst_QMatrixNxN::add4x3_data()
909{
910 QTest::addColumn<void *>(name: "m1Values");
911 QTest::addColumn<void *>(name: "m2Values");
912 QTest::addColumn<void *>(name: "m3Values");
913
914 QTest::newRow(dataTag: "null")
915 << (void *)nullValues4x3 << (void *)nullValues4x3 << (void *)nullValues4x3;
916
917 QTest::newRow(dataTag: "identity/null")
918 << (void *)identityValues4x3 << (void *)nullValues4x3 << (void *)identityValues4x3;
919
920 QTest::newRow(dataTag: "identity/identity")
921 << (void *)identityValues4x3 << (void *)identityValues4x3 << (void *)doubleIdentity4x3;
922
923 static float const sumValues[16] =
924 {2.0f, 7.0f, 12.0f, 6.0f,
925 11.0f, 16.0f, 10.0f, 15.0f,
926 20.0f, 14.0f, 19.0f, 24.0f};
927 QTest::newRow(dataTag: "unique")
928 << (void *)uniqueValues4x3 << (void *)transposedValues3x4 << (void *)sumValues;
929}
930void tst_QMatrixNxN::add4x3()
931{
932 QFETCH(void *, m1Values);
933 QFETCH(void *, m2Values);
934 QFETCH(void *, m3Values);
935
936 QMatrix4x3 m1((const float *)m1Values);
937 QMatrix4x3 m2((const float *)m2Values);
938
939 QMatrix4x3 m4(m1);
940 m4 += m2;
941 QVERIFY(isSame(m4, (const float *)m3Values));
942
943 QMatrix4x3 m5;
944 m5 = m1 + m2;
945 QVERIFY(isSame(m5, (const float *)m3Values));
946}
947
948// Test matrix subtraction for 2x2 matrices.
949void tst_QMatrixNxN::subtract2x2_data()
950{
951 // Use the same test cases as the add test.
952 add2x2_data();
953}
954void tst_QMatrixNxN::subtract2x2()
955{
956 QFETCH(void *, m1Values);
957 QFETCH(void *, m2Values);
958 QFETCH(void *, m3Values);
959
960 QMatrix2x2 m1((const float *)m1Values);
961 QMatrix2x2 m2((const float *)m2Values);
962 QMatrix2x2 m3((const float *)m3Values);
963
964 QMatrix2x2 m4(m3);
965 m4 -= m1;
966 QVERIFY(isSame(m4, (const float *)m2Values));
967
968 QMatrix2x2 m5;
969 m5 = m3 - m1;
970 QVERIFY(isSame(m5, (const float *)m2Values));
971
972 QMatrix2x2 m6(m3);
973 m6 -= m2;
974 QVERIFY(isSame(m6, (const float *)m1Values));
975
976 QMatrix2x2 m7;
977 m7 = m3 - m2;
978 QVERIFY(isSame(m7, (const float *)m1Values));
979}
980
981// Test matrix subtraction for 3x3 matrices.
982void tst_QMatrixNxN::subtract3x3_data()
983{
984 // Use the same test cases as the add test.
985 add3x3_data();
986}
987void tst_QMatrixNxN::subtract3x3()
988{
989 QFETCH(void *, m1Values);
990 QFETCH(void *, m2Values);
991 QFETCH(void *, m3Values);
992
993 QMatrix3x3 m1((const float *)m1Values);
994 QMatrix3x3 m2((const float *)m2Values);
995 QMatrix3x3 m3((const float *)m3Values);
996
997 QMatrix3x3 m4(m3);
998 m4 -= m1;
999 QVERIFY(isSame(m4, (const float *)m2Values));
1000
1001 QMatrix3x3 m5;
1002 m5 = m3 - m1;
1003 QVERIFY(isSame(m5, (const float *)m2Values));
1004
1005 QMatrix3x3 m6(m3);
1006 m6 -= m2;
1007 QVERIFY(isSame(m6, (const float *)m1Values));
1008
1009 QMatrix3x3 m7;
1010 m7 = m3 - m2;
1011 QVERIFY(isSame(m7, (const float *)m1Values));
1012}
1013
1014// Test matrix subtraction for 4x4 matrices.
1015void tst_QMatrixNxN::subtract4x4_data()
1016{
1017 // Use the same test cases as the add test.
1018 add4x4_data();
1019}
1020void tst_QMatrixNxN::subtract4x4()
1021{
1022 QFETCH(void *, m1Values);
1023 QFETCH(void *, m2Values);
1024 QFETCH(void *, m3Values);
1025
1026 QMatrix4x4 m1((const float *)m1Values);
1027 QMatrix4x4 m2((const float *)m2Values);
1028 QMatrix4x4 m3((const float *)m3Values);
1029
1030 QMatrix4x4 m4(m3);
1031 m4 -= m1;
1032 QVERIFY(isSame(m4, (const float *)m2Values));
1033
1034 QMatrix4x4 m5;
1035 m5 = m3 - m1;
1036 QVERIFY(isSame(m5, (const float *)m2Values));
1037
1038 QMatrix4x4 m6(m3);
1039 m6 -= m2;
1040 QVERIFY(isSame(m6, (const float *)m1Values));
1041
1042 QMatrix4x4 m7;
1043 m7 = m3 - m2;
1044 QVERIFY(isSame(m7, (const float *)m1Values));
1045}
1046
1047// Test matrix subtraction for 4x3 matrices.
1048void tst_QMatrixNxN::subtract4x3_data()
1049{
1050 // Use the same test cases as the add test.
1051 add4x3_data();
1052}
1053void tst_QMatrixNxN::subtract4x3()
1054{
1055 QFETCH(void *, m1Values);
1056 QFETCH(void *, m2Values);
1057 QFETCH(void *, m3Values);
1058
1059 QMatrix4x3 m1((const float *)m1Values);
1060 QMatrix4x3 m2((const float *)m2Values);
1061 QMatrix4x3 m3((const float *)m3Values);
1062
1063 QMatrix4x3 m4(m3);
1064 m4 -= m1;
1065 QVERIFY(isSame(m4, (const float *)m2Values));
1066
1067 QMatrix4x3 m5;
1068 m5 = m3 - m1;
1069 QVERIFY(isSame(m5, (const float *)m2Values));
1070
1071 QMatrix4x3 m6(m3);
1072 m6 -= m2;
1073 QVERIFY(isSame(m6, (const float *)m1Values));
1074
1075 QMatrix4x3 m7;
1076 m7 = m3 - m2;
1077 QVERIFY(isSame(m7, (const float *)m1Values));
1078}
1079
1080// Test matrix multiplication for 2x2 matrices.
1081void tst_QMatrixNxN::multiply2x2_data()
1082{
1083 QTest::addColumn<void *>(name: "m1Values");
1084 QTest::addColumn<void *>(name: "m2Values");
1085 QTest::addColumn<void *>(name: "m3Values");
1086
1087 QTest::newRow(dataTag: "null")
1088 << (void *)nullValues2 << (void *)nullValues2 << (void *)nullValues2;
1089
1090 QTest::newRow(dataTag: "null/unique")
1091 << (void *)nullValues2 << (void *)uniqueValues2 << (void *)nullValues2;
1092
1093 QTest::newRow(dataTag: "unique/null")
1094 << (void *)uniqueValues2 << (void *)nullValues2 << (void *)nullValues2;
1095
1096 QTest::newRow(dataTag: "unique/identity")
1097 << (void *)uniqueValues2 << (void *)identityValues2 << (void *)uniqueValues2;
1098
1099 QTest::newRow(dataTag: "identity/unique")
1100 << (void *)identityValues2 << (void *)uniqueValues2 << (void *)uniqueValues2;
1101
1102 static float uniqueResult[4];
1103 for (int row = 0; row < 2; ++row) {
1104 for (int col = 0; col < 2; ++col) {
1105 float sum = 0.0f;
1106 for (int j = 0; j < 2; ++j)
1107 sum += uniqueValues2[row * 2 + j] * transposedValues2[j * 2 + col];
1108 uniqueResult[row * 2 + col] = sum;
1109 }
1110 }
1111
1112 QTest::newRow(dataTag: "unique/transposed")
1113 << (void *)uniqueValues2 << (void *)transposedValues2 << (void *)uniqueResult;
1114}
1115void tst_QMatrixNxN::multiply2x2()
1116{
1117 QFETCH(void *, m1Values);
1118 QFETCH(void *, m2Values);
1119 QFETCH(void *, m3Values);
1120
1121 QMatrix2x2 m1((const float *)m1Values);
1122 QMatrix2x2 m2((const float *)m2Values);
1123
1124 QMatrix2x2 m5;
1125 m5 = m1 * m2;
1126 QVERIFY(isSame(m5, (const float *)m3Values));
1127}
1128
1129// Test matrix multiplication for 3x3 matrices.
1130void tst_QMatrixNxN::multiply3x3_data()
1131{
1132 QTest::addColumn<void *>(name: "m1Values");
1133 QTest::addColumn<void *>(name: "m2Values");
1134 QTest::addColumn<void *>(name: "m3Values");
1135
1136 QTest::newRow(dataTag: "null")
1137 << (void *)nullValues3 << (void *)nullValues3 << (void *)nullValues3;
1138
1139 QTest::newRow(dataTag: "null/unique")
1140 << (void *)nullValues3 << (void *)uniqueValues3 << (void *)nullValues3;
1141
1142 QTest::newRow(dataTag: "unique/null")
1143 << (void *)uniqueValues3 << (void *)nullValues3 << (void *)nullValues3;
1144
1145 QTest::newRow(dataTag: "unique/identity")
1146 << (void *)uniqueValues3 << (void *)identityValues3 << (void *)uniqueValues3;
1147
1148 QTest::newRow(dataTag: "identity/unique")
1149 << (void *)identityValues3 << (void *)uniqueValues3 << (void *)uniqueValues3;
1150
1151 static float uniqueResult[9];
1152 for (int row = 0; row < 3; ++row) {
1153 for (int col = 0; col < 3; ++col) {
1154 float sum = 0.0f;
1155 for (int j = 0; j < 3; ++j)
1156 sum += uniqueValues3[row * 3 + j] * transposedValues3[j * 3 + col];
1157 uniqueResult[row * 3 + col] = sum;
1158 }
1159 }
1160
1161 QTest::newRow(dataTag: "unique/transposed")
1162 << (void *)uniqueValues3 << (void *)transposedValues3 << (void *)uniqueResult;
1163}
1164void tst_QMatrixNxN::multiply3x3()
1165{
1166 QFETCH(void *, m1Values);
1167 QFETCH(void *, m2Values);
1168 QFETCH(void *, m3Values);
1169
1170 QMatrix3x3 m1((const float *)m1Values);
1171 QMatrix3x3 m2((const float *)m2Values);
1172
1173 QMatrix3x3 m5;
1174 m5 = m1 * m2;
1175 QVERIFY(isSame(m5, (const float *)m3Values));
1176}
1177
1178// Test matrix multiplication for 4x4 matrices.
1179void tst_QMatrixNxN::multiply4x4_data()
1180{
1181 QTest::addColumn<void *>(name: "m1Values");
1182 QTest::addColumn<void *>(name: "m2Values");
1183 QTest::addColumn<void *>(name: "m3Values");
1184
1185 QTest::newRow(dataTag: "null")
1186 << (void *)nullValues4 << (void *)nullValues4 << (void *)nullValues4;
1187
1188 QTest::newRow(dataTag: "null/unique")
1189 << (void *)nullValues4 << (void *)uniqueValues4 << (void *)nullValues4;
1190
1191 QTest::newRow(dataTag: "unique/null")
1192 << (void *)uniqueValues4 << (void *)nullValues4 << (void *)nullValues4;
1193
1194 QTest::newRow(dataTag: "unique/identity")
1195 << (void *)uniqueValues4 << (void *)identityValues4 << (void *)uniqueValues4;
1196
1197 QTest::newRow(dataTag: "identity/unique")
1198 << (void *)identityValues4 << (void *)uniqueValues4 << (void *)uniqueValues4;
1199
1200 static float uniqueResult[16];
1201 for (int row = 0; row < 4; ++row) {
1202 for (int col = 0; col < 4; ++col) {
1203 float sum = 0.0f;
1204 for (int j = 0; j < 4; ++j)
1205 sum += uniqueValues4[row * 4 + j] * transposedValues4[j * 4 + col];
1206 uniqueResult[row * 4 + col] = sum;
1207 }
1208 }
1209
1210 QTest::newRow(dataTag: "unique/transposed")
1211 << (void *)uniqueValues4 << (void *)transposedValues4 << (void *)uniqueResult;
1212}
1213void tst_QMatrixNxN::multiply4x4()
1214{
1215 QFETCH(void *, m1Values);
1216 QFETCH(void *, m2Values);
1217 QFETCH(void *, m3Values);
1218
1219 QMatrix4x4 m1((const float *)m1Values);
1220 QMatrix4x4 m2((const float *)m2Values);
1221
1222 QMatrix4x4 m4;
1223 m4 = m1;
1224 m4 *= m2;
1225 QVERIFY(isSame(m4, (const float *)m3Values));
1226
1227 QMatrix4x4 m5;
1228 m5 = m1 * m2;
1229 QVERIFY(isSame(m5, (const float *)m3Values));
1230
1231 QMatrix4x4 m1xm1 = m1 * m1;
1232 m1 *= m1;
1233 QCOMPARE(m1, m1xm1);
1234}
1235
1236// Test matrix multiplication for 4x3 matrices.
1237void tst_QMatrixNxN::multiply4x3_data()
1238{
1239 QTest::addColumn<void *>(name: "m1Values");
1240 QTest::addColumn<void *>(name: "m2Values");
1241 QTest::addColumn<void *>(name: "m3Values");
1242
1243 QTest::newRow(dataTag: "null")
1244 << (void *)nullValues4x3 << (void *)nullValues4x3 << (void *)nullValues3;
1245
1246 QTest::newRow(dataTag: "null/unique")
1247 << (void *)nullValues4x3 << (void *)uniqueValues4x3 << (void *)nullValues3;
1248
1249 QTest::newRow(dataTag: "unique/null")
1250 << (void *)uniqueValues4x3 << (void *)nullValues4x3 << (void *)nullValues3;
1251
1252 static float uniqueResult[9];
1253 for (int row = 0; row < 3; ++row) {
1254 for (int col = 0; col < 3; ++col) {
1255 float sum = 0.0f;
1256 for (int j = 0; j < 4; ++j)
1257 sum += uniqueValues4x3[row * 4 + j] * transposedValues3x4[j * 3 + col];
1258 uniqueResult[row * 3 + col] = sum;
1259 }
1260 }
1261
1262 QTest::newRow(dataTag: "unique/transposed")
1263 << (void *)uniqueValues4x3 << (void *)transposedValues3x4 << (void *)uniqueResult;
1264}
1265void tst_QMatrixNxN::multiply4x3()
1266{
1267 QFETCH(void *, m1Values);
1268 QFETCH(void *, m2Values);
1269 QFETCH(void *, m3Values);
1270
1271 QMatrix4x3 m1((const float *)m1Values);
1272 QMatrix3x4 m2((const float *)m2Values);
1273
1274 QGenericMatrix<3, 3, float> m4;
1275 m4 = m1 * m2;
1276 float values[9];
1277 m4.copyDataTo(values);
1278 for (int index = 0; index < 9; ++index)
1279 QCOMPARE(values[index], ((const float *)m3Values)[index]);
1280}
1281
1282// Test matrix multiplication by a factor for 2x2 matrices.
1283void tst_QMatrixNxN::multiplyFactor2x2_data()
1284{
1285 QTest::addColumn<void *>(name: "m1Values");
1286 QTest::addColumn<float>(name: "factor");
1287 QTest::addColumn<void *>(name: "m2Values");
1288
1289 QTest::newRow(dataTag: "null")
1290 << (void *)nullValues2 << (float)1.0f << (void *)nullValues2;
1291
1292 QTest::newRow(dataTag: "double identity")
1293 << (void *)identityValues2 << (float)2.0f << (void *)doubleIdentity2;
1294
1295 static float const values[16] =
1296 {1.0f, 2.0f,
1297 5.0f, 6.0f};
1298 static float const doubleValues[16] =
1299 {2.0f, 4.0f,
1300 10.0f, 12.0f};
1301 static float const negDoubleValues[16] =
1302 {-2.0f, -4.0f,
1303 -10.0f, -12.0f};
1304
1305 QTest::newRow(dataTag: "unique")
1306 << (void *)values << (float)2.0f << (void *)doubleValues;
1307
1308 QTest::newRow(dataTag: "neg")
1309 << (void *)values << (float)-2.0f << (void *)negDoubleValues;
1310
1311 QTest::newRow(dataTag: "zero")
1312 << (void *)values << (float)0.0f << (void *)nullValues4;
1313}
1314void tst_QMatrixNxN::multiplyFactor2x2()
1315{
1316 QFETCH(void *, m1Values);
1317 QFETCH(float, factor);
1318 QFETCH(void *, m2Values);
1319
1320 QMatrix2x2 m1((const float *)m1Values);
1321
1322 QMatrix2x2 m3;
1323 m3 = m1;
1324 m3 *= factor;
1325 QVERIFY(isSame(m3, (const float *)m2Values));
1326
1327 QMatrix2x2 m4;
1328 m4 = m1 * factor;
1329 QVERIFY(isSame(m4, (const float *)m2Values));
1330
1331 QMatrix2x2 m5;
1332 m5 = factor * m1;
1333 QVERIFY(isSame(m5, (const float *)m2Values));
1334}
1335
1336// Test matrix multiplication by a factor for 3x3 matrices.
1337void tst_QMatrixNxN::multiplyFactor3x3_data()
1338{
1339 QTest::addColumn<void *>(name: "m1Values");
1340 QTest::addColumn<float>(name: "factor");
1341 QTest::addColumn<void *>(name: "m2Values");
1342
1343 QTest::newRow(dataTag: "null")
1344 << (void *)nullValues3 << (float)1.0f << (void *)nullValues3;
1345
1346 QTest::newRow(dataTag: "double identity")
1347 << (void *)identityValues3 << (float)2.0f << (void *)doubleIdentity3;
1348
1349 static float const values[16] =
1350 {1.0f, 2.0f, 3.0f,
1351 5.0f, 6.0f, 7.0f,
1352 9.0f, 10.0f, 11.0f};
1353 static float const doubleValues[16] =
1354 {2.0f, 4.0f, 6.0f,
1355 10.0f, 12.0f, 14.0f,
1356 18.0f, 20.0f, 22.0f};
1357 static float const negDoubleValues[16] =
1358 {-2.0f, -4.0f, -6.0f,
1359 -10.0f, -12.0f, -14.0f,
1360 -18.0f, -20.0f, -22.0f};
1361
1362 QTest::newRow(dataTag: "unique")
1363 << (void *)values << (float)2.0f << (void *)doubleValues;
1364
1365 QTest::newRow(dataTag: "neg")
1366 << (void *)values << (float)-2.0f << (void *)negDoubleValues;
1367
1368 QTest::newRow(dataTag: "zero")
1369 << (void *)values << (float)0.0f << (void *)nullValues4;
1370}
1371void tst_QMatrixNxN::multiplyFactor3x3()
1372{
1373 QFETCH(void *, m1Values);
1374 QFETCH(float, factor);
1375 QFETCH(void *, m2Values);
1376
1377 QMatrix3x3 m1((const float *)m1Values);
1378
1379 QMatrix3x3 m3;
1380 m3 = m1;
1381 m3 *= factor;
1382 QVERIFY(isSame(m3, (const float *)m2Values));
1383
1384 QMatrix3x3 m4;
1385 m4 = m1 * factor;
1386 QVERIFY(isSame(m4, (const float *)m2Values));
1387
1388 QMatrix3x3 m5;
1389 m5 = factor * m1;
1390 QVERIFY(isSame(m5, (const float *)m2Values));
1391}
1392
1393// Test matrix multiplication by a factor for 4x4 matrices.
1394void tst_QMatrixNxN::multiplyFactor4x4_data()
1395{
1396 QTest::addColumn<void *>(name: "m1Values");
1397 QTest::addColumn<float>(name: "factor");
1398 QTest::addColumn<void *>(name: "m2Values");
1399
1400 QTest::newRow(dataTag: "null")
1401 << (void *)nullValues4 << (float)1.0f << (void *)nullValues4;
1402
1403 QTest::newRow(dataTag: "double identity")
1404 << (void *)identityValues4 << (float)2.0f << (void *)doubleIdentity4;
1405
1406 static float const values[16] =
1407 {1.0f, 2.0f, 3.0f, 4.0f,
1408 5.0f, 6.0f, 7.0f, 8.0f,
1409 9.0f, 10.0f, 11.0f, 12.0f,
1410 13.0f, 14.0f, 15.0f, 16.0f};
1411 static float const doubleValues[16] =
1412 {2.0f, 4.0f, 6.0f, 8.0f,
1413 10.0f, 12.0f, 14.0f, 16.0f,
1414 18.0f, 20.0f, 22.0f, 24.0f,
1415 26.0f, 28.0f, 30.0f, 32.0f};
1416 static float const negDoubleValues[16] =
1417 {-2.0f, -4.0f, -6.0f, -8.0f,
1418 -10.0f, -12.0f, -14.0f, -16.0f,
1419 -18.0f, -20.0f, -22.0f, -24.0f,
1420 -26.0f, -28.0f, -30.0f, -32.0f};
1421
1422 QTest::newRow(dataTag: "unique")
1423 << (void *)values << (float)2.0f << (void *)doubleValues;
1424
1425 QTest::newRow(dataTag: "neg")
1426 << (void *)values << (float)-2.0f << (void *)negDoubleValues;
1427
1428 QTest::newRow(dataTag: "zero")
1429 << (void *)values << (float)0.0f << (void *)nullValues4;
1430}
1431void tst_QMatrixNxN::multiplyFactor4x4()
1432{
1433 QFETCH(void *, m1Values);
1434 QFETCH(float, factor);
1435 QFETCH(void *, m2Values);
1436
1437 QMatrix4x4 m1((const float *)m1Values);
1438
1439 QMatrix4x4 m3;
1440 m3 = m1;
1441 m3 *= factor;
1442 QVERIFY(isSame(m3, (const float *)m2Values));
1443
1444 QMatrix4x4 m4;
1445 m4 = m1 * factor;
1446 QVERIFY(isSame(m4, (const float *)m2Values));
1447
1448 QMatrix4x4 m5;
1449 m5 = factor * m1;
1450 QVERIFY(isSame(m5, (const float *)m2Values));
1451}
1452
1453// Test matrix multiplication by a factor for 4x3 matrices.
1454void tst_QMatrixNxN::multiplyFactor4x3_data()
1455{
1456 QTest::addColumn<void *>(name: "m1Values");
1457 QTest::addColumn<float>(name: "factor");
1458 QTest::addColumn<void *>(name: "m2Values");
1459
1460 QTest::newRow(dataTag: "null")
1461 << (void *)nullValues4x3 << (float)1.0f << (void *)nullValues4x3;
1462
1463 QTest::newRow(dataTag: "double identity")
1464 << (void *)identityValues4x3 << (float)2.0f << (void *)doubleIdentity4x3;
1465
1466 static float const values[12] =
1467 {1.0f, 2.0f, 3.0f, 4.0f,
1468 5.0f, 6.0f, 7.0f, 8.0f,
1469 9.0f, 10.0f, 11.0f, 12.0f};
1470 static float const doubleValues[12] =
1471 {2.0f, 4.0f, 6.0f, 8.0f,
1472 10.0f, 12.0f, 14.0f, 16.0f,
1473 18.0f, 20.0f, 22.0f, 24.0f};
1474 static float const negDoubleValues[12] =
1475 {-2.0f, -4.0f, -6.0f, -8.0f,
1476 -10.0f, -12.0f, -14.0f, -16.0f,
1477 -18.0f, -20.0f, -22.0f, -24.0f};
1478
1479 QTest::newRow(dataTag: "unique")
1480 << (void *)values << (float)2.0f << (void *)doubleValues;
1481
1482 QTest::newRow(dataTag: "neg")
1483 << (void *)values << (float)-2.0f << (void *)negDoubleValues;
1484
1485 QTest::newRow(dataTag: "zero")
1486 << (void *)values << (float)0.0f << (void *)nullValues4x3;
1487}
1488void tst_QMatrixNxN::multiplyFactor4x3()
1489{
1490 QFETCH(void *, m1Values);
1491 QFETCH(float, factor);
1492 QFETCH(void *, m2Values);
1493
1494 QMatrix4x3 m1((const float *)m1Values);
1495
1496 QMatrix4x3 m3;
1497 m3 = m1;
1498 m3 *= factor;
1499 QVERIFY(isSame(m3, (const float *)m2Values));
1500
1501 QMatrix4x3 m4;
1502 m4 = m1 * factor;
1503 QVERIFY(isSame(m4, (const float *)m2Values));
1504
1505 QMatrix4x3 m5;
1506 m5 = factor * m1;
1507 QVERIFY(isSame(m5, (const float *)m2Values));
1508}
1509
1510// Test matrix division by a factor for 2x2 matrices.
1511void tst_QMatrixNxN::divideFactor2x2_data()
1512{
1513 // Use the same test cases as the multiplyFactor test.
1514 multiplyFactor2x2_data();
1515}
1516void tst_QMatrixNxN::divideFactor2x2()
1517{
1518 QFETCH(void *, m1Values);
1519 QFETCH(float, factor);
1520 QFETCH(void *, m2Values);
1521
1522 if (factor == 0.0f)
1523 return;
1524
1525 QMatrix2x2 m2((const float *)m2Values);
1526
1527 QMatrix2x2 m3;
1528 m3 = m2;
1529 m3 /= factor;
1530 QVERIFY(isSame(m3, (const float *)m1Values));
1531
1532 QMatrix2x2 m4;
1533 m4 = m2 / factor;
1534 QVERIFY(isSame(m4, (const float *)m1Values));
1535}
1536
1537// Test matrix division by a factor for 3x3 matrices.
1538void tst_QMatrixNxN::divideFactor3x3_data()
1539{
1540 // Use the same test cases as the multiplyFactor test.
1541 multiplyFactor3x3_data();
1542}
1543void tst_QMatrixNxN::divideFactor3x3()
1544{
1545 QFETCH(void *, m1Values);
1546 QFETCH(float, factor);
1547 QFETCH(void *, m2Values);
1548
1549 if (factor == 0.0f)
1550 return;
1551
1552 QMatrix3x3 m2((const float *)m2Values);
1553
1554 QMatrix3x3 m3;
1555 m3 = m2;
1556 m3 /= factor;
1557 QVERIFY(isSame(m3, (const float *)m1Values));
1558
1559 QMatrix3x3 m4;
1560 m4 = m2 / factor;
1561 QVERIFY(isSame(m4, (const float *)m1Values));
1562}
1563
1564// Test matrix division by a factor for 4x4 matrices.
1565void tst_QMatrixNxN::divideFactor4x4_data()
1566{
1567 // Use the same test cases as the multiplyFactor test.
1568 multiplyFactor4x4_data();
1569}
1570void tst_QMatrixNxN::divideFactor4x4()
1571{
1572 QFETCH(void *, m1Values);
1573 QFETCH(float, factor);
1574 QFETCH(void *, m2Values);
1575
1576 if (factor == 0.0f)
1577 return;
1578
1579 QMatrix4x4 m2((const float *)m2Values);
1580
1581 QMatrix4x4 m3;
1582 m3 = m2;
1583 m3 /= factor;
1584 QVERIFY(isSame(m3, (const float *)m1Values));
1585
1586 QMatrix4x4 m4;
1587 m4 = m2 / factor;
1588 QVERIFY(isSame(m4, (const float *)m1Values));
1589}
1590
1591// Test matrix division by a factor for 4x3 matrices.
1592void tst_QMatrixNxN::divideFactor4x3_data()
1593{
1594 // Use the same test cases as the multiplyFactor test.
1595 multiplyFactor4x3_data();
1596}
1597void tst_QMatrixNxN::divideFactor4x3()
1598{
1599 QFETCH(void *, m1Values);
1600 QFETCH(float, factor);
1601 QFETCH(void *, m2Values);
1602
1603 if (factor == 0.0f)
1604 return;
1605
1606 QMatrix4x3 m2((const float *)m2Values);
1607
1608 QMatrix4x3 m3;
1609 m3 = m2;
1610 m3 /= factor;
1611 QVERIFY(isSame(m3, (const float *)m1Values));
1612
1613 QMatrix4x3 m4;
1614 m4 = m2 / factor;
1615 QVERIFY(isSame(m4, (const float *)m1Values));
1616}
1617
1618// Test matrix negation for 2x2 matrices.
1619void tst_QMatrixNxN::negate2x2_data()
1620{
1621 // Use the same test cases as the multiplyFactor test.
1622 multiplyFactor2x2_data();
1623}
1624void tst_QMatrixNxN::negate2x2()
1625{
1626 QFETCH(void *, m1Values);
1627
1628 const float *values = (const float *)m1Values;
1629
1630 QMatrix2x2 m1(values);
1631
1632 float negated[4];
1633 for (int index = 0; index < 4; ++index)
1634 negated[index] = -values[index];
1635
1636 QMatrix2x2 m2;
1637 m2 = -m1;
1638 QVERIFY(isSame(m2, negated));
1639}
1640
1641// Test matrix negation for 3x3 matrices.
1642void tst_QMatrixNxN::negate3x3_data()
1643{
1644 // Use the same test cases as the multiplyFactor test.
1645 multiplyFactor3x3_data();
1646}
1647void tst_QMatrixNxN::negate3x3()
1648{
1649 QFETCH(void *, m1Values);
1650
1651 const float *values = (const float *)m1Values;
1652
1653 QMatrix3x3 m1(values);
1654
1655 float negated[9];
1656 for (int index = 0; index < 9; ++index)
1657 negated[index] = -values[index];
1658
1659 QMatrix3x3 m2;
1660 m2 = -m1;
1661 QVERIFY(isSame(m2, negated));
1662}
1663
1664// Test matrix negation for 4x4 matrices.
1665void tst_QMatrixNxN::negate4x4_data()
1666{
1667 // Use the same test cases as the multiplyFactor test.
1668 multiplyFactor4x4_data();
1669}
1670void tst_QMatrixNxN::negate4x4()
1671{
1672 QFETCH(void *, m1Values);
1673
1674 const float *values = (const float *)m1Values;
1675
1676 QMatrix4x4 m1(values);
1677
1678 float negated[16];
1679 for (int index = 0; index < 16; ++index)
1680 negated[index] = -values[index];
1681
1682 QMatrix4x4 m2;
1683 m2 = -m1;
1684 QVERIFY(isSame(m2, negated));
1685}
1686
1687// Test matrix negation for 4x3 matrices.
1688void tst_QMatrixNxN::negate4x3_data()
1689{
1690 // Use the same test cases as the multiplyFactor test.
1691 multiplyFactor4x3_data();
1692}
1693void tst_QMatrixNxN::negate4x3()
1694{
1695 QFETCH(void *, m1Values);
1696
1697 const float *values = (const float *)m1Values;
1698
1699 QMatrix4x3 m1(values);
1700
1701 float negated[12];
1702 for (int index = 0; index < 12; ++index)
1703 negated[index] = -values[index];
1704
1705 QMatrix4x3 m2;
1706 m2 = -m1;
1707 QVERIFY(isSame(m2, negated));
1708}
1709
1710// Matrix inverted. This is a more straight-forward implementation
1711// of the algorithm at http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q24
1712// than the optimized version in the QMatrix4x4 code. Hopefully it is
1713// easier to verify that this version is the same as the reference.
1714
1715struct Matrix3
1716{
1717 float v[9];
1718};
1719struct Matrix4
1720{
1721 float v[16];
1722};
1723
1724static float m3Determinant(const Matrix3& m)
1725{
1726 return m.v[0] * (m.v[4] * m.v[8] - m.v[7] * m.v[5]) -
1727 m.v[1] * (m.v[3] * m.v[8] - m.v[6] * m.v[5]) +
1728 m.v[2] * (m.v[3] * m.v[7] - m.v[6] * m.v[4]);
1729}
1730
1731static bool m3Inverse(const Matrix3& min, Matrix3& mout)
1732{
1733 float det = m3Determinant(m: min);
1734 if (det == 0.0f)
1735 return false;
1736 mout.v[0] = (min.v[4] * min.v[8] - min.v[5] * min.v[7]) / det;
1737 mout.v[1] = -(min.v[1] * min.v[8] - min.v[2] * min.v[7]) / det;
1738 mout.v[2] = (min.v[1] * min.v[5] - min.v[4] * min.v[2]) / det;
1739 mout.v[3] = -(min.v[3] * min.v[8] - min.v[5] * min.v[6]) / det;
1740 mout.v[4] = (min.v[0] * min.v[8] - min.v[6] * min.v[2]) / det;
1741 mout.v[5] = -(min.v[0] * min.v[5] - min.v[3] * min.v[2]) / det;
1742 mout.v[6] = (min.v[3] * min.v[7] - min.v[6] * min.v[4]) / det;
1743 mout.v[7] = -(min.v[0] * min.v[7] - min.v[6] * min.v[1]) / det;
1744 mout.v[8] = (min.v[0] * min.v[4] - min.v[1] * min.v[3]) / det;
1745 return true;
1746}
1747
1748static void m3Transpose(Matrix3& m)
1749{
1750 qSwap(value1&: m.v[1], value2&: m.v[3]);
1751 qSwap(value1&: m.v[2], value2&: m.v[6]);
1752 qSwap(value1&: m.v[5], value2&: m.v[7]);
1753}
1754
1755static void m4Submatrix(const Matrix4& min, Matrix3& mout, int i, int j)
1756{
1757 for (int di = 0; di < 3; ++di) {
1758 for (int dj = 0; dj < 3; ++dj) {
1759 int si = di + ((di >= i) ? 1 : 0);
1760 int sj = dj + ((dj >= j) ? 1 : 0);
1761 mout.v[di * 3 + dj] = min.v[si * 4 + sj];
1762 }
1763 }
1764}
1765
1766static float m4Determinant(const Matrix4& m)
1767{
1768 float det;
1769 float result = 0.0f;
1770 float i = 1.0f;
1771 Matrix3 msub;
1772 for (int n = 0; n < 4; ++n, i *= -1.0f) {
1773 m4Submatrix(min: m, mout&: msub, i: 0, j: n);
1774 det = m3Determinant(m: msub);
1775 result += m.v[n] * det * i;
1776 }
1777 return result;
1778}
1779
1780static void m4Inverse(const Matrix4& min, Matrix4& mout)
1781{
1782 float det = m4Determinant(m: min);
1783 Matrix3 msub;
1784 for (int i = 0; i < 4; ++i) {
1785 for (int j = 0; j < 4; ++j) {
1786 float sign = 1.0f - ((i + j) % 2) * 2.0f;
1787 m4Submatrix(min, mout&: msub, i, j);
1788 mout.v[i + j * 4] = (m3Determinant(m: msub) * sign) / det;
1789 }
1790 }
1791}
1792
1793// Test matrix inverted for 4x4 matrices.
1794void tst_QMatrixNxN::inverted4x4_data()
1795{
1796 QTest::addColumn<void *>(name: "m1Values");
1797 QTest::addColumn<void *>(name: "m2Values");
1798 QTest::addColumn<bool>(name: "invertible");
1799
1800 QTest::newRow(dataTag: "null")
1801 << (void *)nullValues4 << (void *)identityValues4 << false;
1802
1803 QTest::newRow(dataTag: "identity")
1804 << (void *)identityValues4 << (void *)identityValues4 << true;
1805
1806 QTest::newRow(dataTag: "unique")
1807 << (void *)uniqueValues4 << (void *)identityValues4 << false;
1808
1809 static Matrix4 const invertible = {
1810 .v: {5.0f, 0.0f, 0.0f, 2.0f,
1811 0.0f, 6.0f, 0.0f, 3.0f,
1812 0.0f, 0.0f, 7.0f, 4.0f,
1813 0.0f, 0.0f, 0.0f, 1.0f}
1814 };
1815 static Matrix4 inverted;
1816 m4Inverse(min: invertible, mout&: inverted);
1817
1818 QTest::newRow(dataTag: "invertible")
1819 << (void *)invertible.v << (void *)inverted.v << true;
1820
1821 static Matrix4 const invertible2 = {
1822 .v: {1.0f, 2.0f, 4.0f, 2.0f,
1823 8.0f, 3.0f, 5.0f, 3.0f,
1824 6.0f, 7.0f, 9.0f, 4.0f,
1825 0.0f, 0.0f, 0.0f, 1.0f}
1826 };
1827 static Matrix4 inverted2;
1828 m4Inverse(min: invertible2, mout&: inverted2);
1829
1830 QTest::newRow(dataTag: "invertible2")
1831 << (void *)invertible2.v << (void *)inverted2.v << true;
1832
1833 static Matrix4 const translate = {
1834 .v: {1.0f, 0.0f, 0.0f, 2.0f,
1835 0.0f, 1.0f, 0.0f, 3.0f,
1836 0.0f, 0.0f, 1.0f, 4.0f,
1837 0.0f, 0.0f, 0.0f, 1.0f}
1838 };
1839 static Matrix4 const inverseTranslate = {
1840 .v: {1.0f, 0.0f, 0.0f, -2.0f,
1841 0.0f, 1.0f, 0.0f, -3.0f,
1842 0.0f, 0.0f, 1.0f, -4.0f,
1843 0.0f, 0.0f, 0.0f, 1.0f}
1844 };
1845
1846 QTest::newRow(dataTag: "translate")
1847 << (void *)translate.v << (void *)inverseTranslate.v << true;
1848}
1849void tst_QMatrixNxN::inverted4x4()
1850{
1851 QFETCH(void *, m1Values);
1852 QFETCH(void *, m2Values);
1853 QFETCH(bool, invertible);
1854
1855 QMatrix4x4 m1((const float *)m1Values);
1856
1857 if (invertible)
1858 QVERIFY(m1.determinant() != 0.0f);
1859 else
1860 QCOMPARE(m1.determinant(), 0.0f);
1861
1862 Matrix4 m1alt;
1863 memcpy(dest: m1alt.v, src: (const float *)m1Values, n: sizeof(m1alt.v));
1864
1865 QCOMPARE(m1.determinant(), m4Determinant(m1alt));
1866
1867 QMatrix4x4 m2;
1868 bool inv;
1869 m2 = m1.inverted(invertible: &inv);
1870 QVERIFY(isSame(m2, (const float *)m2Values));
1871
1872 if (invertible) {
1873 QVERIFY(inv);
1874
1875 Matrix4 m2alt;
1876 m4Inverse(min: m1alt, mout&: m2alt);
1877 QVERIFY(isSame(m2, m2alt.v));
1878
1879 QMatrix4x4 m3;
1880 m3 = m1 * m2;
1881 QVERIFY(isIdentity(m3));
1882
1883 QMatrix4x4 m4;
1884 m4 = m2 * m1;
1885 QVERIFY(isIdentity(m4));
1886 } else {
1887 QVERIFY(!inv);
1888 }
1889
1890 // Test again, after inferring the special matrix type.
1891 m1.optimize();
1892 m2 = m1.inverted(invertible: &inv);
1893 QVERIFY(isSame(m2, (const float *)m2Values));
1894 QCOMPARE(inv, invertible);
1895}
1896
1897void tst_QMatrixNxN::orthonormalInverse4x4()
1898{
1899 QMatrix4x4 m1;
1900 QVERIFY(qFuzzyCompare(m1.inverted(), m1));
1901
1902 QMatrix4x4 m2;
1903 m2.rotate(angle: 45.0, x: 1.0, y: 0.0, z: 0.0);
1904 m2.translate(x: 10.0, y: 0.0, z: 0.0);
1905
1906 // Use operator() to drop the internal flags that
1907 // mark the matrix as orthonormal. This will force inverted()
1908 // to compute m3.inverted() the long way. We can then compare
1909 // the result to what the faster algorithm produces on m2.
1910 QMatrix4x4 m3 = m2;
1911 m3(0, 0);
1912 bool invertible;
1913 QVERIFY(qFuzzyCompare(m2.inverted(&invertible), m3.inverted()));
1914 QVERIFY(invertible);
1915
1916 QMatrix4x4 m4;
1917 m4.rotate(angle: 45.0, x: 0.0, y: 1.0, z: 0.0);
1918 QMatrix4x4 m5 = m4;
1919 m5(0, 0);
1920 QVERIFY(qFuzzyCompare(m4.inverted(), m5.inverted()));
1921
1922 QMatrix4x4 m6;
1923 m1.rotate(angle: 88, x: 0.0, y: 0.0, z: 1.0);
1924 m1.translate(x: -20.0, y: 20.0, z: 15.0);
1925 m1.rotate(angle: 25, x: 1.0, y: 0.0, z: 0.0);
1926 QMatrix4x4 m7 = m6;
1927 m7(0, 0);
1928 QVERIFY(qFuzzyCompare(m6.inverted(), m7.inverted()));
1929}
1930
1931// Test the generation and use of 4x4 scale matrices.
1932void tst_QMatrixNxN::scale4x4_data()
1933{
1934 QTest::addColumn<float>(name: "x");
1935 QTest::addColumn<float>(name: "y");
1936 QTest::addColumn<float>(name: "z");
1937 QTest::addColumn<void *>(name: "resultValues");
1938
1939 static const float nullScale[] =
1940 {0.0f, 0.0f, 0.0f, 0.0f,
1941 0.0f, 0.0f, 0.0f, 0.0f,
1942 0.0f, 0.0f, 0.0f, 0.0f,
1943 0.0f, 0.0f, 0.0f, 1.0f};
1944 QTest::newRow(dataTag: "null")
1945 << (float)0.0f << (float)0.0f << (float)0.0f << (void *)nullScale;
1946
1947 QTest::newRow(dataTag: "identity")
1948 << (float)1.0f << (float)1.0f << (float)1.0f << (void *)identityValues4;
1949
1950 static const float doubleScale[] =
1951 {2.0f, 0.0f, 0.0f, 0.0f,
1952 0.0f, 2.0f, 0.0f, 0.0f,
1953 0.0f, 0.0f, 2.0f, 0.0f,
1954 0.0f, 0.0f, 0.0f, 1.0f};
1955 QTest::newRow(dataTag: "double")
1956 << (float)2.0f << (float)2.0f << (float)2.0f << (void *)doubleScale;
1957
1958 static const float complexScale[] =
1959 {2.0f, 0.0f, 0.0f, 0.0f,
1960 0.0f, 11.0f, 0.0f, 0.0f,
1961 0.0f, 0.0f, -6.5f, 0.0f,
1962 0.0f, 0.0f, 0.0f, 1.0f};
1963 QTest::newRow(dataTag: "complex")
1964 << (float)2.0f << (float)11.0f << (float)-6.5f << (void *)complexScale;
1965
1966 static const float complexScale2D[] =
1967 {2.0f, 0.0f, 0.0f, 0.0f,
1968 0.0f, -11.0f, 0.0f, 0.0f,
1969 0.0f, 0.0f, 1.0f, 0.0f,
1970 0.0f, 0.0f, 0.0f, 1.0f};
1971 QTest::newRow(dataTag: "complex2D")
1972 << (float)2.0f << (float)-11.0f << (float)1.0f << (void *)complexScale2D;
1973}
1974void tst_QMatrixNxN::scale4x4()
1975{
1976 QFETCH(float, x);
1977 QFETCH(float, y);
1978 QFETCH(float, z);
1979 QFETCH(void *, resultValues);
1980
1981 QMatrix4x4 result((const float *)resultValues);
1982
1983 QMatrix4x4 m1;
1984 m1.scale(vector: QVector3D(x, y, z));
1985 QVERIFY(isSame(m1, (const float *)resultValues));
1986
1987 QMatrix4x4 m2;
1988 m2.scale(x, y, z);
1989 QVERIFY(isSame(m2, (const float *)resultValues));
1990
1991 if (z == 1.0f) {
1992 QMatrix4x4 m2b;
1993 m2b.scale(x, y);
1994 QCOMPARE(m2b, m2);
1995 }
1996
1997 QVector3D v1(2.0f, 3.0f, -4.0f);
1998 QVector3D v2 = m1 * v1;
1999 QCOMPARE(v2.x(), (float)(2.0f * x));
2000 QCOMPARE(v2.y(), (float)(3.0f * y));
2001 QCOMPARE(v2.z(), (float)(-4.0f * z));
2002
2003 v2 = v1 * m1;
2004 QCOMPARE(v2.x(), (float)(2.0f * x));
2005 QCOMPARE(v2.y(), (float)(3.0f * y));
2006 QCOMPARE(v2.z(), (float)(-4.0f * z));
2007
2008 QVector4D v3(2.0f, 3.0f, -4.0f, 34.0f);
2009 QVector4D v4 = m1 * v3;
2010 QCOMPARE(v4.x(), (float)(2.0f * x));
2011 QCOMPARE(v4.y(), (float)(3.0f * y));
2012 QCOMPARE(v4.z(), (float)(-4.0f * z));
2013 QCOMPARE(v4.w(), (float)34.0f);
2014
2015 v4 = v3 * m1;
2016 QCOMPARE(v4.x(), (float)(2.0f * x));
2017 QCOMPARE(v4.y(), (float)(3.0f * y));
2018 QCOMPARE(v4.z(), (float)(-4.0f * z));
2019 QCOMPARE(v4.w(), (float)34.0f);
2020
2021 QPoint p1(2, 3);
2022 QPoint p2 = m1 * p1;
2023 QCOMPARE(p2.x(), (int)(2.0f * x));
2024 QCOMPARE(p2.y(), (int)(3.0f * y));
2025
2026 p2 = p1 * m1;
2027 QCOMPARE(p2.x(), (int)(2.0f * x));
2028 QCOMPARE(p2.y(), (int)(3.0f * y));
2029
2030 QPointF p3(2.0f, 3.0f);
2031 QPointF p4 = m1 * p3;
2032 QCOMPARE(p4.x(), (float)(2.0f * x));
2033 QCOMPARE(p4.y(), (float)(3.0f * y));
2034
2035 p4 = p3 * m1;
2036 QCOMPARE(p4.x(), (float)(2.0f * x));
2037 QCOMPARE(p4.y(), (float)(3.0f * y));
2038
2039 QMatrix4x4 m3(uniqueValues4);
2040 QMatrix4x4 m4(m3);
2041 m4.scale(x, y, z);
2042 QVERIFY(m4 == m3 * m1);
2043
2044 if (x == y && y == z) {
2045 QMatrix4x4 m5;
2046 m5.scale(factor: x);
2047 QVERIFY(isSame(m5, (const float *)resultValues));
2048 }
2049
2050 if (z == 1.0f) {
2051 QMatrix4x4 m4b(m3);
2052 m4b.scale(x, y);
2053 QCOMPARE(m4b, m4);
2054 }
2055
2056 // Test coverage when the special matrix type is unknown.
2057
2058 QMatrix4x4 m6;
2059 m6(0, 0) = 1.0f;
2060 m6.scale(vector: QVector3D(x, y, z));
2061 QVERIFY(isSame(m6, (const float *)resultValues));
2062
2063 QMatrix4x4 m7;
2064 m7(0, 0) = 1.0f;
2065 m7.scale(x, y, z);
2066 QVERIFY(isSame(m7, (const float *)resultValues));
2067
2068 if (x == y && y == z) {
2069 QMatrix4x4 m8;
2070 m8(0, 0) = 1.0f;
2071 m8.scale(factor: x);
2072 QVERIFY(isSame(m8, (const float *)resultValues));
2073
2074 m8.optimize();
2075 m8.scale(factor: 1.0f);
2076 QVERIFY(isSame(m8, (const float *)resultValues));
2077
2078 QMatrix4x4 m9;
2079 m9.translate(x: 0.0f, y: 0.0f, z: 0.0f);
2080 m9.scale(factor: x);
2081 QVERIFY(isSame(m9, (const float *)resultValues));
2082 }
2083}
2084
2085// Test the generation and use of 4x4 translation matrices.
2086void tst_QMatrixNxN::translate4x4_data()
2087{
2088 QTest::addColumn<float>(name: "x");
2089 QTest::addColumn<float>(name: "y");
2090 QTest::addColumn<float>(name: "z");
2091 QTest::addColumn<void *>(name: "resultValues");
2092
2093 QTest::newRow(dataTag: "null")
2094 << (float)0.0f << (float)0.0f << (float)0.0f << (void *)identityValues4;
2095
2096 static const float identityTranslate[] =
2097 {1.0f, 0.0f, 0.0f, 1.0f,
2098 0.0f, 1.0f, 0.0f, 1.0f,
2099 0.0f, 0.0f, 1.0f, 1.0f,
2100 0.0f, 0.0f, 0.0f, 1.0f};
2101 QTest::newRow(dataTag: "identity")
2102 << (float)1.0f << (float)1.0f << (float)1.0f << (void *)identityTranslate;
2103
2104 static const float complexTranslate[] =
2105 {1.0f, 0.0f, 0.0f, 2.0f,
2106 0.0f, 1.0f, 0.0f, 11.0f,
2107 0.0f, 0.0f, 1.0f, -6.5f,
2108 0.0f, 0.0f, 0.0f, 1.0f};
2109 QTest::newRow(dataTag: "complex")
2110 << (float)2.0f << (float)11.0f << (float)-6.5f << (void *)complexTranslate;
2111
2112 static const float complexTranslate2D[] =
2113 {1.0f, 0.0f, 0.0f, 2.0f,
2114 0.0f, 1.0f, 0.0f, -11.0f,
2115 0.0f, 0.0f, 1.0f, 0.0f,
2116 0.0f, 0.0f, 0.0f, 1.0f};
2117 QTest::newRow(dataTag: "complex2D")
2118 << (float)2.0f << (float)-11.0f << (float)0.0f << (void *)complexTranslate2D;
2119}
2120void tst_QMatrixNxN::translate4x4()
2121{
2122 QFETCH(float, x);
2123 QFETCH(float, y);
2124 QFETCH(float, z);
2125 QFETCH(void *, resultValues);
2126
2127 QMatrix4x4 result((const float *)resultValues);
2128
2129 QMatrix4x4 m1;
2130 m1.translate(vector: QVector3D(x, y, z));
2131 QVERIFY(isSame(m1, (const float *)resultValues));
2132
2133 QMatrix4x4 m2;
2134 m2.translate(x, y, z);
2135 QVERIFY(isSame(m2, (const float *)resultValues));
2136
2137 if (z == 0.0f) {
2138 QMatrix4x4 m2b;
2139 m2b.translate(x, y);
2140 QCOMPARE(m2b, m2);
2141 }
2142
2143 QVector3D v1(2.0f, 3.0f, -4.0f);
2144 QVector3D v2 = m1 * v1;
2145 QCOMPARE(v2.x(), (float)(2.0f + x));
2146 QCOMPARE(v2.y(), (float)(3.0f + y));
2147 QCOMPARE(v2.z(), (float)(-4.0f + z));
2148
2149 QVector4D v3(2.0f, 3.0f, -4.0f, 1.0f);
2150 QVector4D v4 = m1 * v3;
2151 QCOMPARE(v4.x(), (float)(2.0f + x));
2152 QCOMPARE(v4.y(), (float)(3.0f + y));
2153 QCOMPARE(v4.z(), (float)(-4.0f + z));
2154 QCOMPARE(v4.w(), (float)1.0f);
2155
2156 QVector4D v5(2.0f, 3.0f, -4.0f, 34.0f);
2157 QVector4D v6 = m1 * v5;
2158 QCOMPARE(v6.x(), (float)(2.0f + x * 34.0f));
2159 QCOMPARE(v6.y(), (float)(3.0f + y * 34.0f));
2160 QCOMPARE(v6.z(), (float)(-4.0f + z * 34.0f));
2161 QCOMPARE(v6.w(), (float)34.0f);
2162
2163 QPoint p1(2, 3);
2164 QPoint p2 = m1 * p1;
2165 QCOMPARE(p2.x(), (int)(2.0f + x));
2166 QCOMPARE(p2.y(), (int)(3.0f + y));
2167
2168 QPointF p3(2.0f, 3.0f);
2169 QPointF p4 = m1 * p3;
2170 QCOMPARE(p4.x(), (float)(2.0f + x));
2171 QCOMPARE(p4.y(), (float)(3.0f + y));
2172
2173 QMatrix4x4 m3(uniqueValues4);
2174 QMatrix4x4 m4(m3);
2175 m4.translate(x, y, z);
2176 QVERIFY(m4 == m3 * m1);
2177
2178 if (z == 0.0f) {
2179 QMatrix4x4 m4b(m3);
2180 m4b.translate(x, y);
2181 QCOMPARE(m4b, m4);
2182 }
2183}
2184
2185// Test the generation and use of 4x4 rotation matrices.
2186void tst_QMatrixNxN::rotate4x4_data()
2187{
2188 QTest::addColumn<float>(name: "angle");
2189 QTest::addColumn<float>(name: "x");
2190 QTest::addColumn<float>(name: "y");
2191 QTest::addColumn<float>(name: "z");
2192 QTest::addColumn<void *>(name: "resultValues");
2193
2194 static const float nullRotate[] =
2195 {0.0f, 0.0f, 0.0f, 0.0f,
2196 0.0f, 0.0f, 0.0f, 0.0f,
2197 0.0f, 0.0f, 0.0f, 0.0f,
2198 0.0f, 0.0f, 0.0f, 1.0f};
2199 QTest::newRow(dataTag: "null")
2200 << (float)90.0f
2201 << (float)0.0f << (float)0.0f << (float)0.0f
2202 << (void *)nullRotate;
2203
2204 static const float noRotate[] =
2205 {1.0f, 0.0f, 0.0f, 0.0f,
2206 0.0f, 1.0f, 0.0f, 0.0f,
2207 0.0f, 0.0f, 1.0f, 0.0f,
2208 0.0f, 0.0f, 0.0f, 1.0f};
2209 QTest::newRow(dataTag: "zerodegrees")
2210 << (float)0.0f
2211 << (float)2.0f << (float)3.0f << (float)-4.0f
2212 << (void *)noRotate;
2213
2214 static const float xRotate[] =
2215 {1.0f, 0.0f, 0.0f, 0.0f,
2216 0.0f, 0.0f, -1.0f, 0.0f,
2217 0.0f, 1.0f, 0.0f, 0.0f,
2218 0.0f, 0.0f, 0.0f, 1.0f};
2219 QTest::newRow(dataTag: "xrotate")
2220 << (float)90.0f
2221 << (float)1.0f << (float)0.0f << (float)0.0f
2222 << (void *)xRotate;
2223
2224 static const float xRotateNeg[] =
2225 {1.0f, 0.0f, 0.0f, 0.0f,
2226 0.0f, 0.0f, 1.0f, 0.0f,
2227 0.0f, -1.0f, 0.0f, 0.0f,
2228 0.0f, 0.0f, 0.0f, 1.0f};
2229 QTest::newRow(dataTag: "-xrotate")
2230 << (float)90.0f
2231 << (float)-1.0f << (float)0.0f << (float)0.0f
2232 << (void *)xRotateNeg;
2233
2234 static const float yRotate[] =
2235 {0.0f, 0.0f, 1.0f, 0.0f,
2236 0.0f, 1.0f, 0.0f, 0.0f,
2237 -1.0f, 0.0f, 0.0f, 0.0f,
2238 0.0f, 0.0f, 0.0f, 1.0f};
2239 QTest::newRow(dataTag: "yrotate")
2240 << (float)90.0f
2241 << (float)0.0f << (float)1.0f << (float)0.0f
2242 << (void *)yRotate;
2243
2244 static const float yRotateNeg[] =
2245 {0.0f, 0.0f, -1.0f, 0.0f,
2246 0.0f, 1.0f, 0.0f, 0.0f,
2247 1.0f, 0.0f, 0.0f, 0.0f,
2248 0.0f, 0.0f, 0.0f, 1.0f};
2249 QTest::newRow(dataTag: "-yrotate")
2250 << (float)90.0f
2251 << (float)0.0f << (float)-1.0f << (float)0.0f
2252 << (void *)yRotateNeg;
2253
2254 static const float zRotate[] =
2255 {0.0f, -1.0f, 0.0f, 0.0f,
2256 1.0f, 0.0f, 0.0f, 0.0f,
2257 0.0f, 0.0f, 1.0f, 0.0f,
2258 0.0f, 0.0f, 0.0f, 1.0f};
2259 QTest::newRow(dataTag: "zrotate")
2260 << (float)90.0f
2261 << (float)0.0f << (float)0.0f << (float)1.0f
2262 << (void *)zRotate;
2263
2264 static const float zRotateNeg[] =
2265 {0.0f, 1.0f, 0.0f, 0.0f,
2266 -1.0f, 0.0f, 0.0f, 0.0f,
2267 0.0f, 0.0f, 1.0f, 0.0f,
2268 0.0f, 0.0f, 0.0f, 1.0f};
2269 QTest::newRow(dataTag: "-zrotate")
2270 << (float)90.0f
2271 << (float)0.0f << (float)0.0f << (float)-1.0f
2272 << (void *)zRotateNeg;
2273
2274 // Algorithm from http://en.wikipedia.org/wiki/Rotation_matrix.
2275 // Deliberately different from the one in the code for cross-checking.
2276 static float complexRotate[16];
2277 float x = 1.0f;
2278 float y = 2.0f;
2279 float z = -6.0f;
2280 float angle = -45.0f;
2281 float c = std::cos(x: qDegreesToRadians(degrees: angle));
2282 float s = std::sin(x: qDegreesToRadians(degrees: angle));
2283 float len = std::sqrt(x: x * x + y * y + z * z);
2284 float xu = x / len;
2285 float yu = y / len;
2286 float zu = z / len;
2287 complexRotate[0] = (float)((1 - xu * xu) * c + xu * xu);
2288 complexRotate[1] = (float)(-zu * s - xu * yu * c + xu * yu);
2289 complexRotate[2] = (float)(yu * s - xu * zu * c + xu * zu);
2290 complexRotate[3] = 0;
2291 complexRotate[4] = (float)(zu * s - xu * yu * c + xu * yu);
2292 complexRotate[5] = (float)((1 - yu * yu) * c + yu * yu);
2293 complexRotate[6] = (float)(-xu * s - yu * zu * c + yu * zu);
2294 complexRotate[7] = 0;
2295 complexRotate[8] = (float)(-yu * s - xu * zu * c + xu * zu);
2296 complexRotate[9] = (float)(xu * s - yu * zu * c + yu * zu);
2297 complexRotate[10] = (float)((1 - zu * zu) * c + zu * zu);
2298 complexRotate[11] = 0;
2299 complexRotate[12] = 0;
2300 complexRotate[13] = 0;
2301 complexRotate[14] = 0;
2302 complexRotate[15] = 1;
2303
2304 QTest::newRow(dataTag: "complex")
2305 << (float)angle
2306 << (float)x << (float)y << (float)z
2307 << (void *)complexRotate;
2308}
2309void tst_QMatrixNxN::rotate4x4()
2310{
2311 QFETCH(float, angle);
2312 QFETCH(float, x);
2313 QFETCH(float, y);
2314 QFETCH(float, z);
2315 QFETCH(void *, resultValues);
2316
2317 QMatrix4x4 m1;
2318 m1.rotate(angle, vector: QVector3D(x, y, z));
2319 QVERIFY(isSame(m1, (const float *)resultValues));
2320
2321 QMatrix4x4 m2;
2322 m2.rotate(angle, x, y, z);
2323 QVERIFY(isSame(m2, (const float *)resultValues));
2324
2325 QMatrix4x4 m3(uniqueValues4);
2326 QMatrix4x4 m4(m3);
2327 m4.rotate(angle, x, y, z);
2328 QVERIFY(qFuzzyCompare(m4, m3 * m1));
2329
2330 // Null vectors don't make sense for quaternion rotations.
2331 if (x != 0 || y != 0 || z != 0) {
2332 QMatrix4x4 m5;
2333 m5.rotate(quaternion: QQuaternion::fromAxisAndAngle(axis: QVector3D(x, y, z), angle));
2334 QVERIFY(isSame(m5, (const float *)resultValues));
2335 }
2336
2337#define ROTATE4(xin,yin,zin,win,xout,yout,zout,wout) \
2338 do { \
2339 xout = ((const float *)resultValues)[0] * xin + \
2340 ((const float *)resultValues)[1] * yin + \
2341 ((const float *)resultValues)[2] * zin + \
2342 ((const float *)resultValues)[3] * win; \
2343 yout = ((const float *)resultValues)[4] * xin + \
2344 ((const float *)resultValues)[5] * yin + \
2345 ((const float *)resultValues)[6] * zin + \
2346 ((const float *)resultValues)[7] * win; \
2347 zout = ((const float *)resultValues)[8] * xin + \
2348 ((const float *)resultValues)[9] * yin + \
2349 ((const float *)resultValues)[10] * zin + \
2350 ((const float *)resultValues)[11] * win; \
2351 wout = ((const float *)resultValues)[12] * xin + \
2352 ((const float *)resultValues)[13] * yin + \
2353 ((const float *)resultValues)[14] * zin + \
2354 ((const float *)resultValues)[15] * win; \
2355 } while (0)
2356
2357 // Rotate various test vectors using the straight-forward approach.
2358 float v1x, v1y, v1z, v1w;
2359 ROTATE4(2.0f, 3.0f, -4.0f, 1.0f, v1x, v1y, v1z, v1w);
2360 v1x /= v1w;
2361 v1y /= v1w;
2362 v1z /= v1w;
2363 float v3x, v3y, v3z, v3w;
2364 ROTATE4(2.0f, 3.0f, -4.0f, 1.0f, v3x, v3y, v3z, v3w);
2365 float v5x, v5y, v5z, v5w;
2366 ROTATE4(2.0f, 3.0f, -4.0f, 34.0f, v5x, v5y, v5z, v5w);
2367 float p1x, p1y, p1z, p1w;
2368 ROTATE4(2.0f, 3.0f, 0.0f, 1.0f, p1x, p1y, p1z, p1w);
2369 p1x /= p1w;
2370 p1y /= p1w;
2371 p1z /= p1w;
2372
2373 QVector3D v1(2.0f, 3.0f, -4.0f);
2374 QVector3D v2 = m1 * v1;
2375 QVERIFY(qFuzzyCompare(v2.x(), v1x));
2376 QVERIFY(qFuzzyCompare(v2.y(), v1y));
2377 QVERIFY(qFuzzyCompare(v2.z(), v1z));
2378
2379 QVector4D v3(2.0f, 3.0f, -4.0f, 1.0f);
2380 QVector4D v4 = m1 * v3;
2381 QVERIFY(qFuzzyCompare(v4.x(), v3x));
2382 QVERIFY(qFuzzyCompare(v4.y(), v3y));
2383 QVERIFY(qFuzzyCompare(v4.z(), v3z));
2384 QVERIFY(qFuzzyCompare(v4.w(), v3w));
2385
2386 QVector4D v5(2.0f, 3.0f, -4.0f, 34.0f);
2387 QVector4D v6 = m1 * v5;
2388 QVERIFY(qFuzzyCompare(v6.x(), v5x));
2389 QVERIFY(qFuzzyCompare(v6.y(), v5y));
2390 QVERIFY(qFuzzyCompare(v6.z(), v5z));
2391 QVERIFY(qFuzzyCompare(v6.w(), v5w));
2392
2393 QPoint p1(2, 3);
2394 QPoint p2 = m1 * p1;
2395 QCOMPARE(p2.x(), qRound(p1x));
2396 QCOMPARE(p2.y(), qRound(p1y));
2397
2398 QPointF p3(2.0f, 3.0f);
2399 QPointF p4 = m1 * p3;
2400 QVERIFY(qFuzzyCompare(float(p4.x()), p1x));
2401 QVERIFY(qFuzzyCompare(float(p4.y()), p1y));
2402
2403 if (x != 0 || y != 0 || z != 0) {
2404 QQuaternion q = QQuaternion::fromAxisAndAngle(axis: QVector3D(x, y, z), angle);
2405 QVector3D vq = q.rotatedVector(vector: v1);
2406 QVERIFY(qFuzzyCompare(vq.x(), v1x));
2407 QVERIFY(qFuzzyCompare(vq.y(), v1y));
2408 QVERIFY(qFuzzyCompare(vq.z(), v1z));
2409 }
2410}
2411
2412static bool isSame(const QMatrix3x3& m1, const Matrix3& m2)
2413{
2414 for (int row = 0; row < 3; ++row) {
2415 for (int col = 0; col < 3; ++col) {
2416 if (!qFuzzyCompare(p1: m1(row, col), p2: m2.v[row * 3 + col]))
2417 return false;
2418 }
2419 }
2420 return true;
2421}
2422
2423// Test the computation of normal matrices from 4x4 transformation matrices.
2424void tst_QMatrixNxN::normalMatrix_data()
2425{
2426 QTest::addColumn<void *>(name: "mValues");
2427
2428 QTest::newRow(dataTag: "identity")
2429 << (void *)identityValues4;
2430 QTest::newRow(dataTag: "unique")
2431 << (void *)uniqueValues4; // Not invertible because determinant == 0.
2432
2433 static float const translateValues[16] =
2434 {1.0f, 0.0f, 0.0f, 4.0f,
2435 0.0f, 1.0f, 0.0f, 5.0f,
2436 0.0f, 0.0f, 1.0f, -3.0f,
2437 0.0f, 0.0f, 0.0f, 1.0f};
2438 static float const scaleValues[16] =
2439 {2.0f, 0.0f, 0.0f, 0.0f,
2440 0.0f, 7.0f, 0.0f, 0.0f,
2441 0.0f, 0.0f, 9.0f, 0.0f,
2442 0.0f, 0.0f, 0.0f, 1.0f};
2443 static float const bothValues[16] =
2444 {2.0f, 0.0f, 0.0f, 4.0f,
2445 0.0f, 7.0f, 0.0f, 5.0f,
2446 0.0f, 0.0f, 9.0f, -3.0f,
2447 0.0f, 0.0f, 0.0f, 1.0f};
2448 static float const rotateValues[16] =
2449 {0.0f, 0.0f, 1.0f, 0.0f,
2450 1.0f, 0.0f, 0.0f, 0.0f,
2451 0.0f, 1.0f, 0.0f, 0.0f,
2452 0.0f, 0.0f, 0.0f, 1.0f};
2453 static float const nullScaleValues1[16] =
2454 {0.0f, 0.0f, 0.0f, 4.0f,
2455 0.0f, 7.0f, 0.0f, 5.0f,
2456 0.0f, 0.0f, 9.0f, -3.0f,
2457 0.0f, 0.0f, 0.0f, 1.0f};
2458 static float const nullScaleValues2[16] =
2459 {2.0f, 0.0f, 0.0f, 4.0f,
2460 0.0f, 0.0f, 0.0f, 5.0f,
2461 0.0f, 0.0f, 9.0f, -3.0f,
2462 0.0f, 0.0f, 0.0f, 1.0f};
2463 static float const nullScaleValues3[16] =
2464 {2.0f, 0.0f, 0.0f, 4.0f,
2465 0.0f, 7.0f, 0.0f, 5.0f,
2466 0.0f, 0.0f, 0.0f, -3.0f,
2467 0.0f, 0.0f, 0.0f, 1.0f};
2468
2469 QTest::newRow(dataTag: "translate") << (void *)translateValues;
2470 QTest::newRow(dataTag: "scale") << (void *)scaleValues;
2471 QTest::newRow(dataTag: "both") << (void *)bothValues;
2472 QTest::newRow(dataTag: "rotate") << (void *)rotateValues;
2473 QTest::newRow(dataTag: "null scale 1") << (void *)nullScaleValues1;
2474 QTest::newRow(dataTag: "null scale 2") << (void *)nullScaleValues2;
2475 QTest::newRow(dataTag: "null scale 3") << (void *)nullScaleValues3;
2476}
2477void tst_QMatrixNxN::normalMatrix()
2478{
2479 QFETCH(void *, mValues);
2480 const float *values = (const float *)mValues;
2481
2482 // Compute the expected answer the long way.
2483 Matrix3 min;
2484 Matrix3 answer;
2485 min.v[0] = values[0];
2486 min.v[1] = values[1];
2487 min.v[2] = values[2];
2488 min.v[3] = values[4];
2489 min.v[4] = values[5];
2490 min.v[5] = values[6];
2491 min.v[6] = values[8];
2492 min.v[7] = values[9];
2493 min.v[8] = values[10];
2494 bool invertible = m3Inverse(min, mout&: answer);
2495 m3Transpose(m&: answer);
2496
2497 // Perform the test.
2498 QMatrix4x4 m1(values);
2499 QMatrix3x3 n1 = m1.normalMatrix();
2500
2501 if (invertible)
2502 QVERIFY(::isSame(n1, answer));
2503 else
2504 QVERIFY(isIdentity(n1));
2505
2506 // Perform the test again, after inferring special matrix types.
2507 // This tests the optimized paths in the normalMatrix() function.
2508 m1.optimize();
2509 n1 = m1.normalMatrix();
2510
2511 if (invertible)
2512 QVERIFY(::isSame(n1, answer));
2513 else
2514 QVERIFY(isIdentity(n1));
2515}
2516
2517// Test optimized transformations on 4x4 matrices.
2518void tst_QMatrixNxN::optimizedTransforms()
2519{
2520 static float const translateValues[16] =
2521 {1.0f, 0.0f, 0.0f, 4.0f,
2522 0.0f, 1.0f, 0.0f, 5.0f,
2523 0.0f, 0.0f, 1.0f, -3.0f,
2524 0.0f, 0.0f, 0.0f, 1.0f};
2525 static float const translateDoubleValues[16] =
2526 {1.0f, 0.0f, 0.0f, 8.0f,
2527 0.0f, 1.0f, 0.0f, 10.0f,
2528 0.0f, 0.0f, 1.0f, -6.0f,
2529 0.0f, 0.0f, 0.0f, 1.0f};
2530 static float const scaleValues[16] =
2531 {2.0f, 0.0f, 0.0f, 0.0f,
2532 0.0f, 7.0f, 0.0f, 0.0f,
2533 0.0f, 0.0f, 9.0f, 0.0f,
2534 0.0f, 0.0f, 0.0f, 1.0f};
2535 static float const scaleDoubleValues[16] =
2536 {4.0f, 0.0f, 0.0f, 0.0f,
2537 0.0f, 49.0f, 0.0f, 0.0f,
2538 0.0f, 0.0f, 81.0f, 0.0f,
2539 0.0f, 0.0f, 0.0f, 1.0f};
2540 static float const bothValues[16] =
2541 {2.0f, 0.0f, 0.0f, 4.0f,
2542 0.0f, 7.0f, 0.0f, 5.0f,
2543 0.0f, 0.0f, 9.0f, -3.0f,
2544 0.0f, 0.0f, 0.0f, 1.0f};
2545 static float const bothReverseValues[16] =
2546 {2.0f, 0.0f, 0.0f, 4.0f * 2.0f,
2547 0.0f, 7.0f, 0.0f, 5.0f * 7.0f,
2548 0.0f, 0.0f, 9.0f, -3.0f * 9.0f,
2549 0.0f, 0.0f, 0.0f, 1.0f};
2550 static float const bothThenTranslateValues[16] =
2551 {2.0f, 0.0f, 0.0f, 4.0f + 2.0f * 4.0f,
2552 0.0f, 7.0f, 0.0f, 5.0f + 7.0f * 5.0f,
2553 0.0f, 0.0f, 9.0f, -3.0f + 9.0f * -3.0f,
2554 0.0f, 0.0f, 0.0f, 1.0f};
2555 static float const bothThenScaleValues[16] =
2556 {4.0f, 0.0f, 0.0f, 4.0f,
2557 0.0f, 49.0f, 0.0f, 5.0f,
2558 0.0f, 0.0f, 81.0f, -3.0f,
2559 0.0f, 0.0f, 0.0f, 1.0f};
2560
2561 QMatrix4x4 translate(translateValues);
2562 QMatrix4x4 scale(scaleValues);
2563 QMatrix4x4 both(bothValues);
2564
2565 QMatrix4x4 m1;
2566 m1.translate(x: 4.0f, y: 5.0f, z: -3.0f);
2567 QVERIFY(isSame(m1, translateValues));
2568 m1.translate(x: 4.0f, y: 5.0f, z: -3.0f);
2569 QVERIFY(isSame(m1, translateDoubleValues));
2570
2571 QMatrix4x4 m2;
2572 m2.translate(vector: QVector3D(4.0f, 5.0f, -3.0f));
2573 QVERIFY(isSame(m2, translateValues));
2574 m2.translate(vector: QVector3D(4.0f, 5.0f, -3.0f));
2575 QVERIFY(isSame(m2, translateDoubleValues));
2576
2577 QMatrix4x4 m3;
2578 m3.scale(x: 2.0f, y: 7.0f, z: 9.0f);
2579 QVERIFY(isSame(m3, scaleValues));
2580 m3.scale(x: 2.0f, y: 7.0f, z: 9.0f);
2581 QVERIFY(isSame(m3, scaleDoubleValues));
2582
2583 QMatrix4x4 m4;
2584 m4.scale(vector: QVector3D(2.0f, 7.0f, 9.0f));
2585 QVERIFY(isSame(m4, scaleValues));
2586 m4.scale(vector: QVector3D(2.0f, 7.0f, 9.0f));
2587 QVERIFY(isSame(m4, scaleDoubleValues));
2588
2589 QMatrix4x4 m5;
2590 m5.translate(x: 4.0f, y: 5.0f, z: -3.0f);
2591 m5.scale(x: 2.0f, y: 7.0f, z: 9.0f);
2592 QVERIFY(isSame(m5, bothValues));
2593 m5.translate(x: 4.0f, y: 5.0f, z: -3.0f);
2594 QVERIFY(isSame(m5, bothThenTranslateValues));
2595
2596 QMatrix4x4 m6;
2597 m6.translate(vector: QVector3D(4.0f, 5.0f, -3.0f));
2598 m6.scale(vector: QVector3D(2.0f, 7.0f, 9.0f));
2599 QVERIFY(isSame(m6, bothValues));
2600 m6.translate(vector: QVector3D(4.0f, 5.0f, -3.0f));
2601 QVERIFY(isSame(m6, bothThenTranslateValues));
2602
2603 QMatrix4x4 m7;
2604 m7.scale(x: 2.0f, y: 7.0f, z: 9.0f);
2605 m7.translate(x: 4.0f, y: 5.0f, z: -3.0f);
2606 QVERIFY(isSame(m7, bothReverseValues));
2607
2608 QMatrix4x4 m8;
2609 m8.scale(vector: QVector3D(2.0f, 7.0f, 9.0f));
2610 m8.translate(vector: QVector3D(4.0f, 5.0f, -3.0f));
2611 QVERIFY(isSame(m8, bothReverseValues));
2612
2613 QMatrix4x4 m9;
2614 m9.translate(x: 4.0f, y: 5.0f, z: -3.0f);
2615 m9.scale(x: 2.0f, y: 7.0f, z: 9.0f);
2616 QVERIFY(isSame(m9, bothValues));
2617 m9.scale(x: 2.0f, y: 7.0f, z: 9.0f);
2618 QVERIFY(isSame(m9, bothThenScaleValues));
2619
2620 QMatrix4x4 m10;
2621 m10.translate(vector: QVector3D(4.0f, 5.0f, -3.0f));
2622 m10.scale(vector: QVector3D(2.0f, 7.0f, 9.0f));
2623 QVERIFY(isSame(m10, bothValues));
2624 m10.scale(vector: QVector3D(2.0f, 7.0f, 9.0f));
2625 QVERIFY(isSame(m10, bothThenScaleValues));
2626}
2627
2628// Test orthographic projections.
2629void tst_QMatrixNxN::ortho()
2630{
2631 QMatrix4x4 m1;
2632 m1.ortho(rect: QRect(0, 0, 300, 150));
2633 QPointF p1 = m1 * QPointF(0, 0);
2634 QPointF p2 = m1 * QPointF(300, 0);
2635 QPointF p3 = m1 * QPointF(0, 150);
2636 QPointF p4 = m1 * QPointF(300, 150);
2637 QVector3D p5 = m1 * QVector3D(300, 150, 1);
2638 QVERIFY(qFuzzyCompare(float(p1.x()), -1.0f));
2639 QVERIFY(qFuzzyCompare(float(p1.y()), 1.0f));
2640 QVERIFY(qFuzzyCompare(float(p2.x()), 1.0f));
2641 QVERIFY(qFuzzyCompare(float(p2.y()), 1.0f));
2642 QVERIFY(qFuzzyCompare(float(p3.x()), -1.0f));
2643 QVERIFY(qFuzzyCompare(float(p3.y()), -1.0f));
2644 QVERIFY(qFuzzyCompare(float(p4.x()), 1.0f));
2645 QVERIFY(qFuzzyCompare(float(p4.y()), -1.0f));
2646 QVERIFY(qFuzzyCompare(float(p5.x()), 1.0f));
2647 QVERIFY(qFuzzyCompare(float(p5.y()), -1.0f));
2648 QVERIFY(qFuzzyCompare(float(p5.z()), -1.0f));
2649
2650 QMatrix4x4 m2;
2651 m2.ortho(rect: QRectF(0, 0, 300, 150));
2652 p1 = m2 * QPointF(0, 0);
2653 p2 = m2 * QPointF(300, 0);
2654 p3 = m2 * QPointF(0, 150);
2655 p4 = m2 * QPointF(300, 150);
2656 p5 = m2 * QVector3D(300, 150, 1);
2657 QVERIFY(qFuzzyCompare(float(p1.x()), -1.0f));
2658 QVERIFY(qFuzzyCompare(float(p1.y()), 1.0f));
2659 QVERIFY(qFuzzyCompare(float(p2.x()), 1.0f));
2660 QVERIFY(qFuzzyCompare(float(p2.y()), 1.0f));
2661 QVERIFY(qFuzzyCompare(float(p3.x()), -1.0f));
2662 QVERIFY(qFuzzyCompare(float(p3.y()), -1.0f));
2663 QVERIFY(qFuzzyCompare(float(p4.x()), 1.0f));
2664 QVERIFY(qFuzzyCompare(float(p4.y()), -1.0f));
2665 QVERIFY(qFuzzyCompare(float(p5.x()), 1.0f));
2666 QVERIFY(qFuzzyCompare(float(p5.y()), -1.0f));
2667 QVERIFY(qFuzzyCompare(float(p5.z()), -1.0f));
2668
2669 QMatrix4x4 m3;
2670 m3.ortho(left: 0, right: 300, bottom: 150, top: 0, nearPlane: -1, farPlane: 1);
2671 p1 = m3 * QPointF(0, 0);
2672 p2 = m3 * QPointF(300, 0);
2673 p3 = m3 * QPointF(0, 150);
2674 p4 = m3 * QPointF(300, 150);
2675 p5 = m3 * QVector3D(300, 150, 1);
2676 QVERIFY(qFuzzyCompare(float(p1.x()), -1.0f));
2677 QVERIFY(qFuzzyCompare(float(p1.y()), 1.0f));
2678 QVERIFY(qFuzzyCompare(float(p2.x()), 1.0f));
2679 QVERIFY(qFuzzyCompare(float(p2.y()), 1.0f));
2680 QVERIFY(qFuzzyCompare(float(p3.x()), -1.0f));
2681 QVERIFY(qFuzzyCompare(float(p3.y()), -1.0f));
2682 QVERIFY(qFuzzyCompare(float(p4.x()), 1.0f));
2683 QVERIFY(qFuzzyCompare(float(p4.y()), -1.0f));
2684 QVERIFY(qFuzzyCompare(float(p5.x()), 1.0f));
2685 QVERIFY(qFuzzyCompare(float(p5.y()), -1.0f));
2686 QVERIFY(qFuzzyCompare(float(p5.z()), -1.0f));
2687
2688 QMatrix4x4 m4;
2689 m4.ortho(left: 0, right: 300, bottom: 150, top: 0, nearPlane: -2, farPlane: 3);
2690 p1 = m4 * QPointF(0, 0);
2691 p2 = m4 * QPointF(300, 0);
2692 p3 = m4 * QPointF(0, 150);
2693 p4 = m4 * QPointF(300, 150);
2694 p5 = m4 * QVector3D(300, 150, 1);
2695 QVERIFY(qFuzzyCompare(float(p1.x()), -1.0f));
2696 QVERIFY(qFuzzyCompare(float(p1.y()), 1.0f));
2697 QVERIFY(qFuzzyCompare(float(p2.x()), 1.0f));
2698 QVERIFY(qFuzzyCompare(float(p2.y()), 1.0f));
2699 QVERIFY(qFuzzyCompare(float(p3.x()), -1.0f));
2700 QVERIFY(qFuzzyCompare(float(p3.y()), -1.0f));
2701 QVERIFY(qFuzzyCompare(float(p4.x()), 1.0f));
2702 QVERIFY(qFuzzyCompare(float(p4.y()), -1.0f));
2703 QVERIFY(qFuzzyCompare(float(p5.x()), 1.0f));
2704 QVERIFY(qFuzzyCompare(float(p5.y()), -1.0f));
2705 QVERIFY(qFuzzyCompare(float(p5.z()), -0.6f));
2706
2707 // An empty view volume should leave the matrix alone.
2708 QMatrix4x4 m5;
2709 m5.ortho(left: 0, right: 0, bottom: 150, top: 0, nearPlane: -2, farPlane: 3);
2710 QVERIFY(m5.isIdentity());
2711 m5.ortho(left: 0, right: 300, bottom: 150, top: 150, nearPlane: -2, farPlane: 3);
2712 QVERIFY(m5.isIdentity());
2713 m5.ortho(left: 0, right: 300, bottom: 150, top: 0, nearPlane: 2, farPlane: 2);
2714 QVERIFY(m5.isIdentity());
2715}
2716
2717// Test perspective frustum projections.
2718void tst_QMatrixNxN::frustum()
2719{
2720 QMatrix4x4 m1;
2721 m1.frustum(left: -1.0f, right: 1.0f, bottom: -1.0f, top: 1.0f, nearPlane: -1.0f, farPlane: 1.0f);
2722 QVector3D p1 = m1 * QVector3D(-1.0f, -1.0f, 1.0f);
2723 QVector3D p2 = m1 * QVector3D(1.0f, -1.0f, 1.0f);
2724 QVector3D p3 = m1 * QVector3D(-1.0f, 1.0f, 1.0f);
2725 QVector3D p4 = m1 * QVector3D(1.0f, 1.0f, 1.0f);
2726 QVector3D p5 = m1 * QVector3D(0.0f, 0.0f, 2.0f);
2727 QVERIFY(qFuzzyCompare(p1.x(), -1.0f));
2728 QVERIFY(qFuzzyCompare(p1.y(), -1.0f));
2729 QVERIFY(qFuzzyCompare(p1.z(), -1.0f));
2730 QVERIFY(qFuzzyCompare(p2.x(), 1.0f));
2731 QVERIFY(qFuzzyCompare(p2.y(), -1.0f));
2732 QVERIFY(qFuzzyCompare(p2.z(), -1.0f));
2733 QVERIFY(qFuzzyCompare(p3.x(), -1.0f));
2734 QVERIFY(qFuzzyCompare(p3.y(), 1.0f));
2735 QVERIFY(qFuzzyCompare(p3.z(), -1.0f));
2736 QVERIFY(qFuzzyCompare(p4.x(), 1.0f));
2737 QVERIFY(qFuzzyCompare(p4.y(), 1.0f));
2738 QVERIFY(qFuzzyCompare(p4.z(), -1.0f));
2739 QVERIFY(qFuzzyCompare(p5.x(), 0.0f));
2740 QVERIFY(qFuzzyCompare(p5.y(), 0.0f));
2741 QVERIFY(qFuzzyCompare(p5.z(), -0.5f));
2742
2743 // An empty view volume should leave the matrix alone.
2744 QMatrix4x4 m5;
2745 m5.frustum(left: 0, right: 0, bottom: 150, top: 0, nearPlane: -2, farPlane: 3);
2746 QVERIFY(m5.isIdentity());
2747 m5.frustum(left: 0, right: 300, bottom: 150, top: 150, nearPlane: -2, farPlane: 3);
2748 QVERIFY(m5.isIdentity());
2749 m5.frustum(left: 0, right: 300, bottom: 150, top: 0, nearPlane: 2, farPlane: 2);
2750 QVERIFY(m5.isIdentity());
2751}
2752
2753// Test perspective field-of-view projections.
2754void tst_QMatrixNxN::perspective()
2755{
2756 QMatrix4x4 m1;
2757 m1.perspective(verticalAngle: 45.0f, aspectRatio: 1.0f, nearPlane: -1.0f, farPlane: 1.0f);
2758 QVector3D p1 = m1 * QVector3D(-1.0f, -1.0f, 1.0f);
2759 QVector3D p2 = m1 * QVector3D(1.0f, -1.0f, 1.0f);
2760 QVector3D p3 = m1 * QVector3D(-1.0f, 1.0f, 1.0f);
2761 QVector3D p4 = m1 * QVector3D(1.0f, 1.0f, 1.0f);
2762 QVector3D p5 = m1 * QVector3D(0.0f, 0.0f, 2.0f);
2763 QVERIFY(qFuzzyCompare(p1.x(), 2.41421f));
2764 QVERIFY(qFuzzyCompare(p1.y(), 2.41421f));
2765 QVERIFY(qFuzzyCompare(p1.z(), -1.0f));
2766 QVERIFY(qFuzzyCompare(p2.x(), -2.41421f));
2767 QVERIFY(qFuzzyCompare(p2.y(), 2.41421f));
2768 QVERIFY(qFuzzyCompare(p2.z(), -1.0f));
2769 QVERIFY(qFuzzyCompare(p3.x(), 2.41421f));
2770 QVERIFY(qFuzzyCompare(p3.y(), -2.41421f));
2771 QVERIFY(qFuzzyCompare(p3.z(), -1.0f));
2772 QVERIFY(qFuzzyCompare(p4.x(), -2.41421f));
2773 QVERIFY(qFuzzyCompare(p4.y(), -2.41421f));
2774 QVERIFY(qFuzzyCompare(p4.z(), -1.0f));
2775 QVERIFY(qFuzzyCompare(p5.x(), 0.0f));
2776 QVERIFY(qFuzzyCompare(p5.y(), 0.0f));
2777 QVERIFY(qFuzzyCompare(p5.z(), -0.5f));
2778
2779 // An empty view volume should leave the matrix alone.
2780 QMatrix4x4 m5;
2781 m5.perspective(verticalAngle: 45.0f, aspectRatio: 1.0f, nearPlane: 0.0f, farPlane: 0.0f);
2782 QVERIFY(m5.isIdentity());
2783 m5.perspective(verticalAngle: 45.0f, aspectRatio: 0.0f, nearPlane: -1.0f, farPlane: 1.0f);
2784 QVERIFY(m5.isIdentity());
2785 m5.perspective(verticalAngle: 0.0f, aspectRatio: 1.0f, nearPlane: -1.0f, farPlane: 1.0f);
2786 QVERIFY(m5.isIdentity());
2787}
2788
2789// Test viewport transformations
2790void tst_QMatrixNxN::viewport()
2791{
2792 // Uses default depth range of 0->1
2793 QMatrix4x4 m1;
2794 m1.viewport(left: 0.0f, bottom: 0.0f, width: 1024.0f, height: 768.0f);
2795
2796 // Lower left
2797 QVector4D p1 = m1 * QVector4D(-1.0f, -1.0f, 0.0f, 1.0f);
2798 QVERIFY(qFuzzyIsNull(p1.x()));
2799 QVERIFY(qFuzzyIsNull(p1.y()));
2800 QVERIFY(qFuzzyCompare(p1.z(), 0.5f));
2801
2802 // Lower right
2803 QVector4D p2 = m1 * QVector4D(1.0f, -1.0f, 0.0f, 1.0f);
2804 QVERIFY(qFuzzyCompare(p2.x(), 1024.0f));
2805 QVERIFY(qFuzzyIsNull(p2.y()));
2806
2807 // Upper right
2808 QVector4D p3 = m1 * QVector4D(1.0f, 1.0f, 0.0f, 1.0f);
2809 QVERIFY(qFuzzyCompare(p3.x(), 1024.0f));
2810 QVERIFY(qFuzzyCompare(p3.y(), 768.0f));
2811
2812 // Upper left
2813 QVector4D p4 = m1 * QVector4D(-1.0f, 1.0f, 0.0f, 1.0f);
2814 QVERIFY(qFuzzyIsNull(p4.x()));
2815 QVERIFY(qFuzzyCompare(p4.y(), 768.0f));
2816
2817 // Center
2818 QVector4D p5 = m1 * QVector4D(0.0f, 0.0f, 0.0f, 1.0f);
2819 QVERIFY(qFuzzyCompare(p5.x(), 1024.0f / 2.0f));
2820 QVERIFY(qFuzzyCompare(p5.y(), 768.0f / 2.0f));
2821}
2822
2823// Test left-handed vs right-handed coordinate flipping.
2824void tst_QMatrixNxN::flipCoordinates()
2825{
2826 QMatrix4x4 m1;
2827 m1.flipCoordinates();
2828 QVector3D p1 = m1 * QVector3D(2, 3, 4);
2829 QVERIFY(p1 == QVector3D(2, -3, -4));
2830
2831 QMatrix4x4 m2;
2832 m2.scale(x: 2.0f, y: 3.0f, z: 1.0f);
2833 m2.flipCoordinates();
2834 QVector3D p2 = m2 * QVector3D(2, 3, 4);
2835 QVERIFY(p2 == QVector3D(4, -9, -4));
2836
2837 QMatrix4x4 m3;
2838 m3.translate(x: 2.0f, y: 3.0f, z: 1.0f);
2839 m3.flipCoordinates();
2840 QVector3D p3 = m3 * QVector3D(2, 3, 4);
2841 QVERIFY(p3 == QVector3D(4, 0, -3));
2842
2843 QMatrix4x4 m4;
2844 m4.rotate(angle: 90.0f, x: 0.0f, y: 0.0f, z: 1.0f);
2845 m4.flipCoordinates();
2846 QVector3D p4 = m4 * QVector3D(2, 3, 4);
2847 QVERIFY(p4 == QVector3D(3, 2, -4));
2848}
2849
2850// Test conversion of generic matrices to and from the non-generic types.
2851void tst_QMatrixNxN::convertGeneric()
2852{
2853 QMatrix4x3 m1(uniqueValues4x3);
2854
2855 static float const unique4x4[16] = {
2856 1.0f, 2.0f, 3.0f, 4.0f,
2857 5.0f, 6.0f, 7.0f, 8.0f,
2858 9.0f, 10.0f, 11.0f, 12.0f,
2859 0.0f, 0.0f, 0.0f, 1.0f
2860 };
2861 QMatrix4x4 m4(m1);
2862 QVERIFY(isSame(m4, unique4x4));
2863
2864#if QT_DEPRECATED_SINCE(5, 0)
2865 QMatrix4x4 m5 = qGenericMatrixToMatrix4x4(matrix: m1);
2866 QVERIFY(isSame(m5, unique4x4));
2867#endif
2868
2869 static float const conv4x4[12] = {
2870 1.0f, 2.0f, 3.0f, 4.0f,
2871 5.0f, 6.0f, 7.0f, 8.0f,
2872 9.0f, 10.0f, 11.0f, 12.0f
2873 };
2874 QMatrix4x4 m9(uniqueValues4);
2875
2876 QMatrix4x3 m10 = m9.toGenericMatrix<4, 3>();
2877 QVERIFY(isSame(m10, conv4x4));
2878
2879#if QT_DEPRECATED_SINCE(5, 0)
2880 QMatrix4x3 m11 = qGenericMatrixFromMatrix4x4<4, 3>(matrix: m9);
2881 QVERIFY(isSame(m11, conv4x4));
2882#endif
2883}
2884
2885// Copy of "flagBits" in qmatrix4x4.h.
2886enum {
2887 Identity = 0x0000, // Identity matrix
2888 Translation = 0x0001, // Contains a translation
2889 Scale = 0x0002, // Contains a scale
2890 Rotation2D = 0x0004, // Contains a rotation about the Z axis
2891 Rotation = 0x0008, // Contains an arbitrary rotation
2892 Perspective = 0x0010, // Last row is different from (0, 0, 0, 1)
2893 General = 0x001f // General matrix, unknown contents
2894};
2895
2896// Structure that allows direct access to "flagBits" for testing.
2897struct Matrix4x4
2898{
2899 float m[4][4];
2900 int flagBits;
2901};
2902
2903// Test the inferring of special matrix types.
2904void tst_QMatrixNxN::optimize_data()
2905{
2906 QTest::addColumn<void *>(name: "mValues");
2907 QTest::addColumn<int>(name: "flagBits");
2908
2909 QTest::newRow(dataTag: "null")
2910 << (void *)nullValues4 << (int)General;
2911 QTest::newRow(dataTag: "identity")
2912 << (void *)identityValues4 << (int)Identity;
2913 QTest::newRow(dataTag: "unique")
2914 << (void *)uniqueValues4 << (int)General;
2915
2916 static float scaleValues[16] = {
2917 2.0f, 0.0f, 0.0f, 0.0f,
2918 0.0f, 3.0f, 0.0f, 0.0f,
2919 0.0f, 0.0f, 4.0f, 0.0f,
2920 0.0f, 0.0f, 0.0f, 1.0f
2921 };
2922 QTest::newRow(dataTag: "scale")
2923 << (void *)scaleValues << (int)Scale;
2924
2925 static float translateValues[16] = {
2926 1.0f, 0.0f, 0.0f, 2.0f,
2927 0.0f, 1.0f, 0.0f, 3.0f,
2928 0.0f, 0.0f, 1.0f, 4.0f,
2929 0.0f, 0.0f, 0.0f, 1.0f
2930 };
2931 QTest::newRow(dataTag: "translate")
2932 << (void *)translateValues << (int)Translation;
2933
2934 static float scaleTranslateValues[16] = {
2935 1.0f, 0.0f, 0.0f, 2.0f,
2936 0.0f, 2.0f, 0.0f, 0.0f,
2937 0.0f, 0.0f, 1.0f, 4.0f,
2938 0.0f, 0.0f, 0.0f, 1.0f
2939 };
2940 QTest::newRow(dataTag: "scaleTranslate")
2941 << (void *)scaleTranslateValues << (int)(Scale | Translation);
2942
2943 static float rotateValues[16] = {
2944 0.0f, 1.0f, 0.0f, 0.0f,
2945 -1.0f, 0.0f, 0.0f, 0.0f,
2946 0.0f, 0.0f, 1.0f, 0.0f,
2947 0.0f, 0.0f, 0.0f, 1.0f
2948 };
2949 QTest::newRow(dataTag: "rotate")
2950 << (void *)rotateValues << (int)Rotation2D;
2951
2952 // Left-handed system, not a simple rotation.
2953 static float scaleRotateValues[16] = {
2954 0.0f, 1.0f, 0.0f, 0.0f,
2955 1.0f, 0.0f, 0.0f, 0.0f,
2956 0.0f, 0.0f, 1.0f, 0.0f,
2957 0.0f, 0.0f, 0.0f, 1.0f
2958 };
2959 QTest::newRow(dataTag: "scaleRotate")
2960 << (void *)scaleRotateValues << (int)(Scale | Rotation2D);
2961
2962 static float matrix2x2Values[16] = {
2963 1.0f, 2.0f, 0.0f, 0.0f,
2964 8.0f, 3.0f, 0.0f, 0.0f,
2965 0.0f, 0.0f, 9.0f, 0.0f,
2966 0.0f, 0.0f, 0.0f, 1.0f
2967 };
2968 QTest::newRow(dataTag: "matrix2x2")
2969 << (void *)matrix2x2Values << (int)(Scale | Rotation2D);
2970
2971 static float matrix3x3Values[16] = {
2972 1.0f, 2.0f, 4.0f, 0.0f,
2973 8.0f, 3.0f, 5.0f, 0.0f,
2974 6.0f, 7.0f, 9.0f, 0.0f,
2975 0.0f, 0.0f, 0.0f, 1.0f
2976 };
2977 QTest::newRow(dataTag: "matrix3x3")
2978 << (void *)matrix3x3Values << (int)(Scale | Rotation2D | Rotation);
2979
2980 static float rotateTranslateValues[16] = {
2981 0.0f, 1.0f, 0.0f, 1.0f,
2982 -1.0f, 0.0f, 0.0f, 2.0f,
2983 0.0f, 0.0f, 1.0f, 3.0f,
2984 0.0f, 0.0f, 0.0f, 1.0f
2985 };
2986 QTest::newRow(dataTag: "rotateTranslate")
2987 << (void *)rotateTranslateValues << (int)(Translation | Rotation2D);
2988
2989 // Left-handed system, not a simple rotation.
2990 static float scaleRotateTranslateValues[16] = {
2991 0.0f, 1.0f, 0.0f, 1.0f,
2992 1.0f, 0.0f, 0.0f, 2.0f,
2993 0.0f, 0.0f, 1.0f, 3.0f,
2994 0.0f, 0.0f, 0.0f, 1.0f
2995 };
2996 QTest::newRow(dataTag: "scaleRotateTranslate")
2997 << (void *)scaleRotateTranslateValues << (int)(Translation | Scale | Rotation2D);
2998
2999 static float belowValues[16] = {
3000 1.0f, 0.0f, 0.0f, 0.0f,
3001 0.0f, 1.0f, 0.0f, 0.0f,
3002 0.0f, 0.0f, 1.0f, 0.0f,
3003 4.0f, 0.0f, 0.0f, 1.0f
3004 };
3005 QTest::newRow(dataTag: "below")
3006 << (void *)belowValues << (int)General;
3007}
3008void tst_QMatrixNxN::optimize()
3009{
3010 QFETCH(void *, mValues);
3011 QFETCH(int, flagBits);
3012
3013 QMatrix4x4 m((const float *)mValues);
3014 m.optimize();
3015
3016 QCOMPARE(reinterpret_cast<Matrix4x4 *>(&m)->flagBits, flagBits);
3017}
3018
3019void tst_QMatrixNxN::columnsAndRows()
3020{
3021 QMatrix4x4 m1(uniqueValues4);
3022
3023 QVERIFY(m1.column(0) == QVector4D(1, 5, 9, 13));
3024 QVERIFY(m1.column(1) == QVector4D(2, 6, 10, 14));
3025 QVERIFY(m1.column(2) == QVector4D(3, 7, 11, 15));
3026 QVERIFY(m1.column(3) == QVector4D(4, 8, 12, 16));
3027
3028 QVERIFY(m1.row(0) == QVector4D(1, 2, 3, 4));
3029 QVERIFY(m1.row(1) == QVector4D(5, 6, 7, 8));
3030 QVERIFY(m1.row(2) == QVector4D(9, 10, 11, 12));
3031 QVERIFY(m1.row(3) == QVector4D(13, 14, 15, 16));
3032
3033 m1.setColumn(index: 0, value: QVector4D(-1, -5, -9, -13));
3034 m1.setColumn(index: 1, value: QVector4D(-2, -6, -10, -14));
3035 m1.setColumn(index: 2, value: QVector4D(-3, -7, -11, -15));
3036 m1.setColumn(index: 3, value: QVector4D(-4, -8, -12, -16));
3037
3038 QVERIFY(m1.column(0) == QVector4D(-1, -5, -9, -13));
3039 QVERIFY(m1.column(1) == QVector4D(-2, -6, -10, -14));
3040 QVERIFY(m1.column(2) == QVector4D(-3, -7, -11, -15));
3041 QVERIFY(m1.column(3) == QVector4D(-4, -8, -12, -16));
3042
3043 QVERIFY(m1.row(0) == QVector4D(-1, -2, -3, -4));
3044 QVERIFY(m1.row(1) == QVector4D(-5, -6, -7, -8));
3045 QVERIFY(m1.row(2) == QVector4D(-9, -10, -11, -12));
3046 QVERIFY(m1.row(3) == QVector4D(-13, -14, -15, -16));
3047
3048 m1.setRow(index: 0, value: QVector4D(1, 5, 9, 13));
3049 m1.setRow(index: 1, value: QVector4D(2, 6, 10, 14));
3050 m1.setRow(index: 2, value: QVector4D(3, 7, 11, 15));
3051 m1.setRow(index: 3, value: QVector4D(4, 8, 12, 16));
3052
3053 QVERIFY(m1.column(0) == QVector4D(1, 2, 3, 4));
3054 QVERIFY(m1.column(1) == QVector4D(5, 6, 7, 8));
3055 QVERIFY(m1.column(2) == QVector4D(9, 10, 11, 12));
3056 QVERIFY(m1.column(3) == QVector4D(13, 14, 15, 16));
3057
3058 QVERIFY(m1.row(0) == QVector4D(1, 5, 9, 13));
3059 QVERIFY(m1.row(1) == QVector4D(2, 6, 10, 14));
3060 QVERIFY(m1.row(2) == QVector4D(3, 7, 11, 15));
3061 QVERIFY(m1.row(3) == QVector4D(4, 8, 12, 16));
3062}
3063
3064#if QT_DEPRECATED_SINCE(5, 15)
3065QT_WARNING_PUSH
3066QT_WARNING_DISABLE_DEPRECATED
3067// Test converting QMatrix objects into QMatrix4x4 and then
3068// checking that transformations in the original perform the
3069// equivalent transformations in the new matrix.
3070void tst_QMatrixNxN::convertQMatrix()
3071{
3072 QMatrix m1;
3073 m1.translate(dx: -3.5, dy: 2.0);
3074 QPointF p1 = m1.map(p: QPointF(100.0, 150.0));
3075 QCOMPARE(p1.x(), 100.0 - 3.5);
3076 QCOMPARE(p1.y(), 150.0 + 2.0);
3077
3078 QMatrix4x4 m2(m1);
3079 QPointF p2 = m2 * QPointF(100.0, 150.0);
3080 QCOMPARE((double)p2.x(), 100.0 - 3.5);
3081 QCOMPARE((double)p2.y(), 150.0 + 2.0);
3082 QCOMPARE(m1, m2.toAffine());
3083
3084 QMatrix m3;
3085 m3.scale(sx: 1.5, sy: -2.0);
3086 QPointF p3 = m3.map(p: QPointF(100.0, 150.0));
3087 QCOMPARE(p3.x(), 1.5 * 100.0);
3088 QCOMPARE(p3.y(), -2.0 * 150.0);
3089
3090 QMatrix4x4 m4(m3);
3091 QPointF p4 = m4 * QPointF(100.0, 150.0);
3092 QCOMPARE((double)p4.x(), 1.5 * 100.0);
3093 QCOMPARE((double)p4.y(), -2.0 * 150.0);
3094 QCOMPARE(m3, m4.toAffine());
3095
3096 QMatrix m5;
3097 m5.rotate(a: 45.0);
3098 QPointF p5 = m5.map(p: QPointF(100.0, 150.0));
3099
3100 QMatrix4x4 m6(m5);
3101 QPointF p6 = m6 * QPointF(100.0, 150.0);
3102 QVERIFY(qFuzzyCompare(float(p5.x()), float(p6.x())));
3103 QVERIFY(qFuzzyCompare(float(p5.y()), float(p6.y())));
3104
3105 QMatrix m7 = m6.toAffine();
3106 QVERIFY(qFuzzyCompare(float(m5.m11()), float(m7.m11())));
3107 QVERIFY(qFuzzyCompare(float(m5.m12()), float(m7.m12())));
3108 QVERIFY(qFuzzyCompare(float(m5.m21()), float(m7.m21())));
3109 QVERIFY(qFuzzyCompare(float(m5.m22()), float(m7.m22())));
3110 QVERIFY(qFuzzyCompare(float(m5.dx()), float(m7.dx())));
3111 QVERIFY(qFuzzyCompare(float(m5.dy()), float(m7.dy())));
3112}
3113QT_WARNING_POP
3114#endif
3115
3116// Test converting QTransform objects into QMatrix4x4 and then
3117// checking that transformations in the original perform the
3118// equivalent transformations in the new matrix.
3119void tst_QMatrixNxN::convertQTransform()
3120{
3121 QTransform m1;
3122 m1.translate(dx: -3.5, dy: 2.0);
3123 QPointF p1 = m1.map(p: QPointF(100.0, 150.0));
3124 QCOMPARE(p1.x(), 100.0 - 3.5);
3125 QCOMPARE(p1.y(), 150.0 + 2.0);
3126
3127 QMatrix4x4 m2(m1);
3128 QPointF p2 = m2 * QPointF(100.0, 150.0);
3129 QCOMPARE((double)p2.x(), 100.0 - 3.5);
3130 QCOMPARE((double)p2.y(), 150.0 + 2.0);
3131 QCOMPARE(m1, m2.toTransform());
3132
3133 QTransform m3;
3134 m3.scale(sx: 1.5, sy: -2.0);
3135 QPointF p3 = m3.map(p: QPointF(100.0, 150.0));
3136 QCOMPARE(p3.x(), 1.5 * 100.0);
3137 QCOMPARE(p3.y(), -2.0 * 150.0);
3138
3139 QMatrix4x4 m4(m3);
3140 QPointF p4 = m4 * QPointF(100.0, 150.0);
3141 QCOMPARE((double)p4.x(), 1.5 * 100.0);
3142 QCOMPARE((double)p4.y(), -2.0 * 150.0);
3143 QCOMPARE(m3, m4.toTransform());
3144
3145 QTransform m5;
3146 m5.rotate(a: 45.0);
3147 QPointF p5 = m5.map(p: QPointF(100.0, 150.0));
3148
3149 QMatrix4x4 m6(m5);
3150 QPointF p6 = m6 * QPointF(100.0, 150.0);
3151 QVERIFY(qFuzzyCompare(float(p5.x()), float(p6.x())));
3152 QVERIFY(qFuzzyCompare(float(p5.y()), float(p6.y())));
3153
3154 QTransform m7 = m6.toTransform();
3155 QVERIFY(qFuzzyCompare(float(m5.m11()), float(m7.m11())));
3156 QVERIFY(qFuzzyCompare(float(m5.m12()), float(m7.m12())));
3157 QVERIFY(qFuzzyCompare(float(m5.m21()), float(m7.m21())));
3158 QVERIFY(qFuzzyCompare(float(m5.m22()), float(m7.m22())));
3159 QVERIFY(qFuzzyCompare(float(m5.dx()), float(m7.dx())));
3160 QVERIFY(qFuzzyCompare(float(m5.dy()), float(m7.dy())));
3161 QVERIFY(qFuzzyCompare(float(m5.m13()), float(m7.m13())));
3162 QVERIFY(qFuzzyCompare(float(m5.m23()), float(m7.m23())));
3163 QVERIFY(qFuzzyCompare(float(m5.m33()), float(m7.m33())));
3164}
3165
3166// Test filling matrices with specific values.
3167void tst_QMatrixNxN::fill()
3168{
3169 QMatrix4x4 m1;
3170 m1.fill(value: 0.0f);
3171 QVERIFY(isSame(m1, nullValues4));
3172
3173 static const float fillValues4[] =
3174 {2.5f, 2.5f, 2.5f, 2.5f,
3175 2.5f, 2.5f, 2.5f, 2.5f,
3176 2.5f, 2.5f, 2.5f, 2.5f,
3177 2.5f, 2.5f, 2.5f, 2.5f};
3178 m1.fill(value: 2.5f);
3179 QVERIFY(isSame(m1, fillValues4));
3180
3181 QMatrix4x3 m2;
3182 m2.fill(value: 0.0f);
3183 QVERIFY(isSame(m2, nullValues4x3));
3184
3185 static const float fillValues4x3[] =
3186 {2.5f, 2.5f, 2.5f, 2.5f,
3187 2.5f, 2.5f, 2.5f, 2.5f,
3188 2.5f, 2.5f, 2.5f, 2.5f};
3189 m2.fill(value: 2.5f);
3190 QVERIFY(isSame(m2, fillValues4x3));
3191}
3192
3193// Test the mapRect() function for QRect and QRectF.
3194void tst_QMatrixNxN::mapRect_data()
3195{
3196 QTest::addColumn<float>(name: "x");
3197 QTest::addColumn<float>(name: "y");
3198 QTest::addColumn<float>(name: "width");
3199 QTest::addColumn<float>(name: "height");
3200
3201 QTest::newRow(dataTag: "null")
3202 << (float)0.0f << (float)0.0f << (float)0.0f << (float)0.0f;
3203 QTest::newRow(dataTag: "rect")
3204 << (float)1.0f << (float)-20.5f << (float)100.0f << (float)63.75f;
3205}
3206void tst_QMatrixNxN::mapRect()
3207{
3208 QFETCH(float, x);
3209 QFETCH(float, y);
3210 QFETCH(float, width);
3211 QFETCH(float, height);
3212
3213 QRectF rect(x, y, width, height);
3214 QRect recti(qRound(d: x), qRound(d: y), qRound(d: width), qRound(d: height));
3215
3216 QMatrix4x4 m1;
3217 QCOMPARE(m1.mapRect(rect), rect);
3218 QCOMPARE(m1.mapRect(recti), recti);
3219
3220 QMatrix4x4 m2;
3221 m2.translate(x: -100.5f, y: 64.0f);
3222 QRectF translated = rect.translated(dx: -100.5f, dy: 64.0f);
3223 QRect translatedi = QRect(qRound(d: recti.x() - 100.5f), recti.y() + 64,
3224 recti.width(), recti.height());
3225 QCOMPARE(m2.mapRect(rect), translated);
3226 QCOMPARE(m2.mapRect(recti), translatedi);
3227
3228 QMatrix4x4 m3;
3229 m3.scale(x: -100.5f, y: 64.0f);
3230 float scalex = x * -100.5f;
3231 float scaley = y * 64.0f;
3232 float scalewid = width * -100.5f;
3233 float scaleht = height * 64.0f;
3234 if (scalewid < 0.0f) {
3235 scalewid = -scalewid;
3236 scalex -= scalewid;
3237 }
3238 if (scaleht < 0.0f) {
3239 scaleht = -scaleht;
3240 scaley -= scaleht;
3241 }
3242 QRectF scaled(scalex, scaley, scalewid, scaleht);
3243 QCOMPARE(m3.mapRect(rect), scaled);
3244 scalex = recti.x() * -100.5f;
3245 scaley = recti.y() * 64.0f;
3246 scalewid = recti.width() * -100.5f;
3247 scaleht = recti.height() * 64.0f;
3248 if (scalewid < 0.0f) {
3249 scalewid = -scalewid;
3250 scalex -= scalewid;
3251 }
3252 if (scaleht < 0.0f) {
3253 scaleht = -scaleht;
3254 scaley -= scaleht;
3255 }
3256 QRect scaledi(qRound(d: scalex), qRound(d: scaley),
3257 qRound(d: scalewid), qRound(d: scaleht));
3258 QCOMPARE(m3.mapRect(recti), scaledi);
3259
3260 QMatrix4x4 m4;
3261 m4.translate(x: -100.5f, y: 64.0f);
3262 m4.scale(x: -2.5f, y: 4.0f);
3263 float transx1 = x * -2.5f - 100.5f;
3264 float transy1 = y * 4.0f + 64.0f;
3265 float transx2 = (x + width) * -2.5f - 100.5f;
3266 float transy2 = (y + height) * 4.0f + 64.0f;
3267 if (transx1 > transx2)
3268 qSwap(value1&: transx1, value2&: transx2);
3269 if (transy1 > transy2)
3270 qSwap(value1&: transy1, value2&: transy2);
3271 QRectF trans(transx1, transy1, transx2 - transx1, transy2 - transy1);
3272 QCOMPARE(m4.mapRect(rect), trans);
3273 transx1 = recti.x() * -2.5f - 100.5f;
3274 transy1 = recti.y() * 4.0f + 64.0f;
3275 transx2 = (recti.x() + recti.width()) * -2.5f - 100.5f;
3276 transy2 = (recti.y() + recti.height()) * 4.0f + 64.0f;
3277 if (transx1 > transx2)
3278 qSwap(value1&: transx1, value2&: transx2);
3279 if (transy1 > transy2)
3280 qSwap(value1&: transy1, value2&: transy2);
3281 QRect transi(qRound(d: transx1), qRound(d: transy1),
3282 qRound(d: transx2) - qRound(d: transx1),
3283 qRound(d: transy2) - qRound(d: transy1));
3284 QCOMPARE(m4.mapRect(recti), transi);
3285
3286 m4.rotate(angle: 45.0f, x: 0.0f, y: 0.0f, z: 1.0f);
3287
3288 QTransform t4;
3289 t4.translate(dx: -100.5f, dy: 64.0f);
3290 t4.scale(sx: -2.5f, sy: 4.0f);
3291 t4.rotate(a: 45.0f);
3292 QRectF mr = m4.mapRect(rect);
3293 QRectF tr = t4.mapRect(rect);
3294 QVERIFY(qFuzzyCompare(float(mr.x()), float(tr.x())));
3295 QVERIFY(qFuzzyCompare(float(mr.y()), float(tr.y())));
3296 QVERIFY(qFuzzyCompare(float(mr.width()), float(tr.width())));
3297 QVERIFY(qFuzzyCompare(float(mr.height()), float(tr.height())));
3298
3299 QRect mri = m4.mapRect(rect: recti);
3300 QRect tri = t4.mapRect(recti);
3301 QCOMPARE(mri, tri);
3302}
3303
3304void tst_QMatrixNxN::mapVector_data()
3305{
3306 QTest::addColumn<void *>(name: "mValues");
3307
3308 QTest::newRow(dataTag: "null")
3309 << (void *)nullValues4;
3310
3311 QTest::newRow(dataTag: "identity")
3312 << (void *)identityValues4;
3313
3314 QTest::newRow(dataTag: "unique")
3315 << (void *)uniqueValues4;
3316
3317 static const float scale[] =
3318 {2.0f, 0.0f, 0.0f, 0.0f,
3319 0.0f, 11.0f, 0.0f, 0.0f,
3320 0.0f, 0.0f, -6.5f, 0.0f,
3321 0.0f, 0.0f, 0.0f, 1.0f};
3322 QTest::newRow(dataTag: "scale")
3323 << (void *)scale;
3324
3325 static const float scaleTranslate[] =
3326 {2.0f, 0.0f, 0.0f, 1.0f,
3327 0.0f, 11.0f, 0.0f, 2.0f,
3328 0.0f, 0.0f, -6.5f, 3.0f,
3329 0.0f, 0.0f, 0.0f, 1.0f};
3330 QTest::newRow(dataTag: "scaleTranslate")
3331 << (void *)scaleTranslate;
3332
3333 static const float translate[] =
3334 {1.0f, 0.0f, 0.0f, 1.0f,
3335 0.0f, 1.0f, 0.0f, 2.0f,
3336 0.0f, 0.0f, 1.0f, 3.0f,
3337 0.0f, 0.0f, 0.0f, 1.0f};
3338 QTest::newRow(dataTag: "translate")
3339 << (void *)translate;
3340}
3341void tst_QMatrixNxN::mapVector()
3342{
3343 QFETCH(void *, mValues);
3344
3345 QMatrix4x4 m1((const float *)mValues);
3346
3347 QVector3D v(3.5f, -1.0f, 2.5f);
3348
3349 QVector3D expected
3350 (v.x() * m1(0, 0) + v.y() * m1(0, 1) + v.z() * m1(0, 2),
3351 v.x() * m1(1, 0) + v.y() * m1(1, 1) + v.z() * m1(1, 2),
3352 v.x() * m1(2, 0) + v.y() * m1(2, 1) + v.z() * m1(2, 2));
3353
3354 QVector3D actual = m1.mapVector(vector: v);
3355 m1.optimize();
3356 QVector3D actual2 = m1.mapVector(vector: v);
3357
3358 QVERIFY(qFuzzyCompare(actual.x(), expected.x()));
3359 QVERIFY(qFuzzyCompare(actual.y(), expected.y()));
3360 QVERIFY(qFuzzyCompare(actual.z(), expected.z()));
3361 QVERIFY(qFuzzyCompare(actual2.x(), expected.x()));
3362 QVERIFY(qFuzzyCompare(actual2.y(), expected.y()));
3363 QVERIFY(qFuzzyCompare(actual2.z(), expected.z()));
3364}
3365
3366class tst_QMatrixNxN4x4Properties : public QObject
3367{
3368 Q_OBJECT
3369 Q_PROPERTY(QMatrix4x4 matrix READ matrix WRITE setMatrix)
3370public:
3371 tst_QMatrixNxN4x4Properties(QObject *parent = 0) : QObject(parent) {}
3372
3373 QMatrix4x4 matrix() const { return m; }
3374 void setMatrix(const QMatrix4x4& value) { m = value; }
3375
3376private:
3377 QMatrix4x4 m;
3378};
3379
3380// Test getting and setting matrix properties via the metaobject system.
3381void tst_QMatrixNxN::properties()
3382{
3383 tst_QMatrixNxN4x4Properties obj;
3384
3385 QMatrix4x4 m1(uniqueValues4);
3386 obj.setMatrix(m1);
3387
3388 QMatrix4x4 m2 = qvariant_cast<QMatrix4x4>(v: obj.property(name: "matrix"));
3389 QVERIFY(isSame(m2, uniqueValues4));
3390
3391 QMatrix4x4 m3(transposedValues4);
3392 obj.setProperty(name: "matrix", value: QVariant::fromValue(value: m3));
3393
3394 m2 = qvariant_cast<QMatrix4x4>(v: obj.property(name: "matrix"));
3395 QVERIFY(isSame(m2, transposedValues4));
3396}
3397
3398void tst_QMatrixNxN::metaTypes()
3399{
3400 QCOMPARE(QMetaType::type("QMatrix4x4"), int(QMetaType::QMatrix4x4));
3401
3402 QCOMPARE(QByteArray(QMetaType::typeName(QMetaType::QMatrix4x4)),
3403 QByteArray("QMatrix4x4"));
3404
3405 QVERIFY(QMetaType::isRegistered(QMetaType::QMatrix4x4));
3406
3407 QCOMPARE(qMetaTypeId<QMatrix4x4>(), int(QMetaType::QMatrix4x4));
3408}
3409
3410QTEST_APPLESS_MAIN(tst_QMatrixNxN)
3411
3412#include "tst_qmatrixnxn.moc"
3413

source code of qtbase/tests/auto/gui/math3d/qmatrixnxn/tst_qmatrixnxn.cpp