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 | |
33 | class tst_QMatrixNxN : public QObject |
34 | { |
35 | Q_OBJECT |
36 | public: |
37 | tst_QMatrixNxN() {} |
38 | ~tst_QMatrixNxN() {} |
39 | |
40 | private 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 | |
161 | private: |
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 | |
183 | static const float nullValues2[] = |
184 | {0.0f, 0.0f, |
185 | 0.0f, 0.0f}; |
186 | |
187 | static float const identityValues2[16] = |
188 | {1.0f, 0.0f, |
189 | 0.0f, 1.0f}; |
190 | |
191 | static const float doubleIdentity2[] = |
192 | {2.0f, 0.0f, |
193 | 0.0f, 2.0f}; |
194 | |
195 | static float const uniqueValues2[16] = |
196 | {1.0f, 2.0f, |
197 | 5.0f, 6.0f}; |
198 | |
199 | static float const transposedValues2[16] = |
200 | {1.0f, 5.0f, |
201 | 2.0f, 6.0f}; |
202 | |
203 | static 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 | |
208 | static 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 | |
213 | static 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 | |
218 | static 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 | |
223 | static 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 | |
228 | static 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 | |
234 | static 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 | |
240 | static 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 | |
246 | static 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 | |
252 | static 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 | |
258 | static 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 | |
263 | static 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 | |
268 | static 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 | |
273 | static 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 | |
278 | static 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 |
286 | static 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. |
298 | void 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 | } |
304 | void 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 | } |
310 | void 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 | } |
316 | void 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. |
326 | void 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 | } |
335 | void 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 | } |
344 | void 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 | } |
353 | void 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. |
365 | bool 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 | } |
386 | bool 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 | } |
407 | bool 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 | } |
428 | bool 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. |
451 | bool tst_QMatrixNxN::isIdentity(const QMatrix2x2& m) |
452 | { |
453 | return isSame(m, values: identityValues2); |
454 | } |
455 | bool tst_QMatrixNxN::isIdentity(const QMatrix3x3& m) |
456 | { |
457 | return isSame(m, values: identityValues3); |
458 | } |
459 | bool tst_QMatrixNxN::isIdentity(const QMatrix4x4& m) |
460 | { |
461 | return isSame(m, values: identityValues4); |
462 | } |
463 | bool 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. |
470 | void 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. |
505 | void 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. |
540 | void 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. |
582 | void 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. |
616 | void 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. |
627 | void 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. |
638 | void 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. |
654 | void 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. |
665 | void 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. |
678 | void 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. |
691 | void 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. |
704 | void 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. |
717 | void 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. |
736 | void 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. |
755 | void 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. |
774 | void 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. |
785 | void 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 | } |
806 | void 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. |
825 | void 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 | } |
847 | void 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. |
866 | void 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 | } |
889 | void 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. |
908 | void 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 | } |
930 | void 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. |
949 | void tst_QMatrixNxN::subtract2x2_data() |
950 | { |
951 | // Use the same test cases as the add test. |
952 | add2x2_data(); |
953 | } |
954 | void 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. |
982 | void tst_QMatrixNxN::subtract3x3_data() |
983 | { |
984 | // Use the same test cases as the add test. |
985 | add3x3_data(); |
986 | } |
987 | void 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. |
1015 | void tst_QMatrixNxN::subtract4x4_data() |
1016 | { |
1017 | // Use the same test cases as the add test. |
1018 | add4x4_data(); |
1019 | } |
1020 | void 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. |
1048 | void tst_QMatrixNxN::subtract4x3_data() |
1049 | { |
1050 | // Use the same test cases as the add test. |
1051 | add4x3_data(); |
1052 | } |
1053 | void 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. |
1081 | void 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 | } |
1115 | void 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. |
1130 | void 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 | } |
1164 | void 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. |
1179 | void 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 | } |
1213 | void 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. |
1237 | void 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 | } |
1265 | void 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. |
1283 | void 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 | } |
1314 | void 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. |
1337 | void 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 | } |
1371 | void 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. |
1394 | void 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 | } |
1431 | void 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. |
1454 | void 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 | } |
1488 | void 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. |
1511 | void tst_QMatrixNxN::divideFactor2x2_data() |
1512 | { |
1513 | // Use the same test cases as the multiplyFactor test. |
1514 | multiplyFactor2x2_data(); |
1515 | } |
1516 | void 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. |
1538 | void tst_QMatrixNxN::divideFactor3x3_data() |
1539 | { |
1540 | // Use the same test cases as the multiplyFactor test. |
1541 | multiplyFactor3x3_data(); |
1542 | } |
1543 | void 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. |
1565 | void tst_QMatrixNxN::divideFactor4x4_data() |
1566 | { |
1567 | // Use the same test cases as the multiplyFactor test. |
1568 | multiplyFactor4x4_data(); |
1569 | } |
1570 | void 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. |
1592 | void tst_QMatrixNxN::divideFactor4x3_data() |
1593 | { |
1594 | // Use the same test cases as the multiplyFactor test. |
1595 | multiplyFactor4x3_data(); |
1596 | } |
1597 | void 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. |
1619 | void tst_QMatrixNxN::negate2x2_data() |
1620 | { |
1621 | // Use the same test cases as the multiplyFactor test. |
1622 | multiplyFactor2x2_data(); |
1623 | } |
1624 | void 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. |
1642 | void tst_QMatrixNxN::negate3x3_data() |
1643 | { |
1644 | // Use the same test cases as the multiplyFactor test. |
1645 | multiplyFactor3x3_data(); |
1646 | } |
1647 | void 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. |
1665 | void tst_QMatrixNxN::negate4x4_data() |
1666 | { |
1667 | // Use the same test cases as the multiplyFactor test. |
1668 | multiplyFactor4x4_data(); |
1669 | } |
1670 | void 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. |
1688 | void tst_QMatrixNxN::negate4x3_data() |
1689 | { |
1690 | // Use the same test cases as the multiplyFactor test. |
1691 | multiplyFactor4x3_data(); |
1692 | } |
1693 | void 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 | |
1715 | struct Matrix3 |
1716 | { |
1717 | float v[9]; |
1718 | }; |
1719 | struct Matrix4 |
1720 | { |
1721 | float v[16]; |
1722 | }; |
1723 | |
1724 | static 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 | |
1731 | static 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 | |
1748 | static 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 | |
1755 | static 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 | |
1766 | static 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 | |
1780 | static 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. |
1794 | void 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 | } |
1849 | void 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 | |
1897 | void 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. |
1932 | void 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 | } |
1974 | void 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. |
2086 | void 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 [] = |
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 [] = |
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 | } |
2120 | void 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. |
2186 | void 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 | } |
2309 | void 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 | |
2412 | static 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. |
2424 | void 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 | } |
2477 | void 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. |
2518 | void 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. |
2629 | void 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. |
2718 | void 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. |
2754 | void 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 |
2790 | void 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. |
2824 | void 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. |
2851 | void 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. |
2886 | enum { |
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. |
2897 | struct Matrix4x4 |
2898 | { |
2899 | float m[4][4]; |
2900 | int flagBits; |
2901 | }; |
2902 | |
2903 | // Test the inferring of special matrix types. |
2904 | void 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 | } |
3008 | void 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 | |
3019 | void 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) |
3065 | QT_WARNING_PUSH |
3066 | QT_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. |
3070 | void 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 | } |
3113 | QT_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. |
3119 | void 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. |
3167 | void 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. |
3194 | void 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 | } |
3206 | void 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 | |
3304 | void 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 | } |
3341 | void 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 | |
3366 | class tst_QMatrixNxN4x4Properties : public QObject |
3367 | { |
3368 | Q_OBJECT |
3369 | Q_PROPERTY(QMatrix4x4 matrix READ matrix WRITE setMatrix) |
3370 | public: |
3371 | tst_QMatrixNxN4x4Properties(QObject *parent = 0) : QObject(parent) {} |
3372 | |
3373 | QMatrix4x4 matrix() const { return m; } |
3374 | void setMatrix(const QMatrix4x4& value) { m = value; } |
3375 | |
3376 | private: |
3377 | QMatrix4x4 m; |
3378 | }; |
3379 | |
3380 | // Test getting and setting matrix properties via the metaobject system. |
3381 | void 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 | |
3398 | void 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 | |
3410 | QTEST_APPLESS_MAIN(tst_QMatrixNxN) |
3411 | |
3412 | #include "tst_qmatrixnxn.moc" |
3413 | |