Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qdoublematrix4x4.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5#include <QtCore/qmath.h>
6//#include <QtCore/qvariant.h>
7#include <QtCore/qdatastream.h>
8#include <cmath>
9
11
13{
14 for (int row = 0; row < 4; ++row)
15 for (int col = 0; col < 4; ++col)
16 m[col][row] = values[row * 4 + col];
17 flagBits = General;
18}
19
20QDoubleMatrix4x4::QDoubleMatrix4x4(const double *values, int cols, int rows)
21{
22 for (int col = 0; col < 4; ++col) {
23 for (int row = 0; row < 4; ++row) {
24 if (col < cols && row < rows)
25 m[col][row] = values[col * rows + row];
26 else if (col == row)
27 m[col][row] = 1.0;
28 else
29 m[col][row] = 0.0;
30 }
31 }
32 flagBits = General;
33}
34
35static inline double matrixDet2(const double m[4][4], int col0, int col1, int row0, int row1)
36{
37 return m[col0][row0] * m[col1][row1] - m[col0][row1] * m[col1][row0];
38}
39
40static inline double matrixDet3
41 (const double m[4][4], int col0, int col1, int col2,
42 int row0, int row1, int row2)
43{
44 return m[col0][row0] * matrixDet2(m, col1, col2, row1, row2)
45 - m[col1][row0] * matrixDet2(m, col0, col2, row1, row2)
46 + m[col2][row0] * matrixDet2(m, col0, col1, row1, row2);
47}
48
49static inline double matrixDet4(const double m[4][4])
50{
51 double det;
52 det = m[0][0] * matrixDet3(m, 1, 2, 3, 1, 2, 3);
53 det -= m[1][0] * matrixDet3(m, 0, 2, 3, 1, 2, 3);
54 det += m[2][0] * matrixDet3(m, 0, 1, 3, 1, 2, 3);
55 det -= m[3][0] * matrixDet3(m, 0, 1, 2, 1, 2, 3);
56 return det;
57}
58
60{
61 if ((flagBits & ~(Translation | Rotation2D | Rotation)) == Identity)
62 return 1.0;
63
64 if (flagBits < Rotation2D)
65 return m[0][0] * m[1][1] * m[2][2]; // Translation | Scale
66 if (flagBits < Perspective)
67 return matrixDet3(m, 0, 1, 2, 0, 1, 2);
68 return matrixDet4(m);
69}
70
72{
73 // Handle some of the easy cases first.
74 if (flagBits == Identity) {
75 if (invertible)
76 *invertible = true;
77 return QDoubleMatrix4x4();
78 } else if (flagBits == Translation) {
80 inv.m[3][0] = -m[3][0];
81 inv.m[3][1] = -m[3][1];
82 inv.m[3][2] = -m[3][2];
83 inv.flagBits = Translation;
84 if (invertible)
85 *invertible = true;
86 return inv;
87 } else if (flagBits < Rotation2D) {
88 // Translation | Scale
89 if (m[0][0] == 0 || m[1][1] == 0 || m[2][2] == 0) {
90 if (invertible)
91 *invertible = false;
92 return QDoubleMatrix4x4();
93 }
95 inv.m[0][0] = 1.0 / m[0][0];
96 inv.m[1][1] = 1.0 / m[1][1];
97 inv.m[2][2] = 1.0 / m[2][2];
98 inv.m[3][0] = -m[3][0] * inv.m[0][0];
99 inv.m[3][1] = -m[3][1] * inv.m[1][1];
100 inv.m[3][2] = -m[3][2] * inv.m[2][2];
101 inv.flagBits = flagBits;
102
103 if (invertible)
104 *invertible = true;
105 return inv;
106 } else if ((flagBits & ~(Translation | Rotation2D | Rotation)) == Identity) {
107 if (invertible)
108 *invertible = true;
109 return orthonormalInverse();
110 } else if (flagBits < Perspective) {
111 QDoubleMatrix4x4 inv(1); // The "1" says to not load the identity.
112
113 double det = matrixDet3(m, 0, 1, 2, 0, 1, 2);
114 if (det == 0.0) {
115 if (invertible)
116 *invertible = false;
117 return QDoubleMatrix4x4();
118 }
119 det = 1.0 / det;
120
121 inv.m[0][0] = matrixDet2(m, 1, 2, 1, 2) * det;
122 inv.m[0][1] = -matrixDet2(m, 0, 2, 1, 2) * det;
123 inv.m[0][2] = matrixDet2(m, 0, 1, 1, 2) * det;
124 inv.m[0][3] = 0;
125 inv.m[1][0] = -matrixDet2(m, 1, 2, 0, 2) * det;
126 inv.m[1][1] = matrixDet2(m, 0, 2, 0, 2) * det;
127 inv.m[1][2] = -matrixDet2(m, 0, 1, 0, 2) * det;
128 inv.m[1][3] = 0;
129 inv.m[2][0] = matrixDet2(m, 1, 2, 0, 1) * det;
130 inv.m[2][1] = -matrixDet2(m, 0, 2, 0, 1) * det;
131 inv.m[2][2] = matrixDet2(m, 0, 1, 0, 1) * det;
132 inv.m[2][3] = 0;
133 inv.m[3][0] = -inv.m[0][0] * m[3][0] - inv.m[1][0] * m[3][1] - inv.m[2][0] * m[3][2];
134 inv.m[3][1] = -inv.m[0][1] * m[3][0] - inv.m[1][1] * m[3][1] - inv.m[2][1] * m[3][2];
135 inv.m[3][2] = -inv.m[0][2] * m[3][0] - inv.m[1][2] * m[3][1] - inv.m[2][2] * m[3][2];
136 inv.m[3][3] = 1;
137 inv.flagBits = flagBits;
138
139 if (invertible)
140 *invertible = true;
141 return inv;
142 }
143
144 QDoubleMatrix4x4 inv(1); // The "1" says to not load the identity.
145
146 double det = matrixDet4(m);
147 if (det == 0.0) {
148 if (invertible)
149 *invertible = false;
150 return QDoubleMatrix4x4();
151 }
152 det = 1.0 / det;
153
154 inv.m[0][0] = matrixDet3(m, 1, 2, 3, 1, 2, 3) * det;
155 inv.m[0][1] = -matrixDet3(m, 0, 2, 3, 1, 2, 3) * det;
156 inv.m[0][2] = matrixDet3(m, 0, 1, 3, 1, 2, 3) * det;
157 inv.m[0][3] = -matrixDet3(m, 0, 1, 2, 1, 2, 3) * det;
158 inv.m[1][0] = -matrixDet3(m, 1, 2, 3, 0, 2, 3) * det;
159 inv.m[1][1] = matrixDet3(m, 0, 2, 3, 0, 2, 3) * det;
160 inv.m[1][2] = -matrixDet3(m, 0, 1, 3, 0, 2, 3) * det;
161 inv.m[1][3] = matrixDet3(m, 0, 1, 2, 0, 2, 3) * det;
162 inv.m[2][0] = matrixDet3(m, 1, 2, 3, 0, 1, 3) * det;
163 inv.m[2][1] = -matrixDet3(m, 0, 2, 3, 0, 1, 3) * det;
164 inv.m[2][2] = matrixDet3(m, 0, 1, 3, 0, 1, 3) * det;
165 inv.m[2][3] = -matrixDet3(m, 0, 1, 2, 0, 1, 3) * det;
166 inv.m[3][0] = -matrixDet3(m, 1, 2, 3, 0, 1, 2) * det;
167 inv.m[3][1] = matrixDet3(m, 0, 2, 3, 0, 1, 2) * det;
168 inv.m[3][2] = -matrixDet3(m, 0, 1, 3, 0, 1, 2) * det;
169 inv.m[3][3] = matrixDet3(m, 0, 1, 2, 0, 1, 2) * det;
170 inv.flagBits = flagBits;
171
172 if (invertible)
173 *invertible = true;
174 return inv;
175}
176
178{
179 QDoubleMatrix4x4 result(1); // The "1" says to not load the identity.
180 for (int row = 0; row < 4; ++row) {
181 for (int col = 0; col < 4; ++col) {
182 result.m[col][row] = m[row][col];
183 }
184 }
185 // When a translation is transposed, it becomes a perspective transformation.
186 result.flagBits = (flagBits & Translation ? General : flagBits);
187 return result;
188}
189
191{
192 m[0][0] /= divisor;
193 m[0][1] /= divisor;
194 m[0][2] /= divisor;
195 m[0][3] /= divisor;
196 m[1][0] /= divisor;
197 m[1][1] /= divisor;
198 m[1][2] /= divisor;
199 m[1][3] /= divisor;
200 m[2][0] /= divisor;
201 m[2][1] /= divisor;
202 m[2][2] /= divisor;
203 m[2][3] /= divisor;
204 m[3][0] /= divisor;
205 m[3][1] /= divisor;
206 m[3][2] /= divisor;
207 m[3][3] /= divisor;
208 flagBits = General;
209 return *this;
210}
211
213{
214 QDoubleMatrix4x4 m(1); // The "1" says to not load the identity.
215 m.m[0][0] = matrix.m[0][0] / divisor;
216 m.m[0][1] = matrix.m[0][1] / divisor;
217 m.m[0][2] = matrix.m[0][2] / divisor;
218 m.m[0][3] = matrix.m[0][3] / divisor;
219 m.m[1][0] = matrix.m[1][0] / divisor;
220 m.m[1][1] = matrix.m[1][1] / divisor;
221 m.m[1][2] = matrix.m[1][2] / divisor;
222 m.m[1][3] = matrix.m[1][3] / divisor;
223 m.m[2][0] = matrix.m[2][0] / divisor;
224 m.m[2][1] = matrix.m[2][1] / divisor;
225 m.m[2][2] = matrix.m[2][2] / divisor;
226 m.m[2][3] = matrix.m[2][3] / divisor;
227 m.m[3][0] = matrix.m[3][0] / divisor;
228 m.m[3][1] = matrix.m[3][1] / divisor;
229 m.m[3][2] = matrix.m[3][2] / divisor;
230 m.m[3][3] = matrix.m[3][3] / divisor;
231 m.flagBits = QDoubleMatrix4x4::General;
232 return m;
233}
234
236{
237 double vx = vector.x();
238 double vy = vector.y();
239 double vz = vector.z();
240 if (flagBits < Scale) {
241 m[0][0] = vx;
242 m[1][1] = vy;
243 m[2][2] = vz;
244 } else if (flagBits < Rotation2D) {
245 m[0][0] *= vx;
246 m[1][1] *= vy;
247 m[2][2] *= vz;
248 } else if (flagBits < Rotation) {
249 m[0][0] *= vx;
250 m[0][1] *= vx;
251 m[1][0] *= vy;
252 m[1][1] *= vy;
253 m[2][2] *= vz;
254 } else {
255 m[0][0] *= vx;
256 m[0][1] *= vx;
257 m[0][2] *= vx;
258 m[0][3] *= vx;
259 m[1][0] *= vy;
260 m[1][1] *= vy;
261 m[1][2] *= vy;
262 m[1][3] *= vy;
263 m[2][0] *= vz;
264 m[2][1] *= vz;
265 m[2][2] *= vz;
266 m[2][3] *= vz;
267 }
268 flagBits |= Scale;
269}
270
271void QDoubleMatrix4x4::scale(double x, double y)
272{
273 if (flagBits < Scale) {
274 m[0][0] = x;
275 m[1][1] = y;
276 } else if (flagBits < Rotation2D) {
277 m[0][0] *= x;
278 m[1][1] *= y;
279 } else if (flagBits < Rotation) {
280 m[0][0] *= x;
281 m[0][1] *= x;
282 m[1][0] *= y;
283 m[1][1] *= y;
284 } else {
285 m[0][0] *= x;
286 m[0][1] *= x;
287 m[0][2] *= x;
288 m[0][3] *= x;
289 m[1][0] *= y;
290 m[1][1] *= y;
291 m[1][2] *= y;
292 m[1][3] *= y;
293 }
294 flagBits |= Scale;
295}
296
297void QDoubleMatrix4x4::scale(double x, double y, double z)
298{
299 if (flagBits < Scale) {
300 m[0][0] = x;
301 m[1][1] = y;
302 m[2][2] = z;
303 } else if (flagBits < Rotation2D) {
304 m[0][0] *= x;
305 m[1][1] *= y;
306 m[2][2] *= z;
307 } else if (flagBits < Rotation) {
308 m[0][0] *= x;
309 m[0][1] *= x;
310 m[1][0] *= y;
311 m[1][1] *= y;
312 m[2][2] *= z;
313 } else {
314 m[0][0] *= x;
315 m[0][1] *= x;
316 m[0][2] *= x;
317 m[0][3] *= x;
318 m[1][0] *= y;
319 m[1][1] *= y;
320 m[1][2] *= y;
321 m[1][3] *= y;
322 m[2][0] *= z;
323 m[2][1] *= z;
324 m[2][2] *= z;
325 m[2][3] *= z;
326 }
327 flagBits |= Scale;
328}
329
330void QDoubleMatrix4x4::scale(double factor)
331{
332 if (flagBits < Scale) {
333 m[0][0] = factor;
334 m[1][1] = factor;
335 m[2][2] = factor;
336 } else if (flagBits < Rotation2D) {
337 m[0][0] *= factor;
338 m[1][1] *= factor;
339 m[2][2] *= factor;
340 } else if (flagBits < Rotation) {
341 m[0][0] *= factor;
342 m[0][1] *= factor;
343 m[1][0] *= factor;
344 m[1][1] *= factor;
345 m[2][2] *= factor;
346 } else {
347 m[0][0] *= factor;
348 m[0][1] *= factor;
349 m[0][2] *= factor;
350 m[0][3] *= factor;
351 m[1][0] *= factor;
352 m[1][1] *= factor;
353 m[1][2] *= factor;
354 m[1][3] *= factor;
355 m[2][0] *= factor;
356 m[2][1] *= factor;
357 m[2][2] *= factor;
358 m[2][3] *= factor;
359 }
360 flagBits |= Scale;
361}
362
364{
365 double vx = vector.x();
366 double vy = vector.y();
367 double vz = vector.z();
368 if (flagBits == Identity) {
369 m[3][0] = vx;
370 m[3][1] = vy;
371 m[3][2] = vz;
372 } else if (flagBits == Translation) {
373 m[3][0] += vx;
374 m[3][1] += vy;
375 m[3][2] += vz;
376 } else if (flagBits == Scale) {
377 m[3][0] = m[0][0] * vx;
378 m[3][1] = m[1][1] * vy;
379 m[3][2] = m[2][2] * vz;
380 } else if (flagBits == (Translation | Scale)) {
381 m[3][0] += m[0][0] * vx;
382 m[3][1] += m[1][1] * vy;
383 m[3][2] += m[2][2] * vz;
384 } else if (flagBits < Rotation) {
385 m[3][0] += m[0][0] * vx + m[1][0] * vy;
386 m[3][1] += m[0][1] * vx + m[1][1] * vy;
387 m[3][2] += m[2][2] * vz;
388 } else {
389 m[3][0] += m[0][0] * vx + m[1][0] * vy + m[2][0] * vz;
390 m[3][1] += m[0][1] * vx + m[1][1] * vy + m[2][1] * vz;
391 m[3][2] += m[0][2] * vx + m[1][2] * vy + m[2][2] * vz;
392 m[3][3] += m[0][3] * vx + m[1][3] * vy + m[2][3] * vz;
393 }
394 flagBits |= Translation;
395}
396
397void QDoubleMatrix4x4::translate(double x, double y)
398{
399 if (flagBits == Identity) {
400 m[3][0] = x;
401 m[3][1] = y;
402 } else if (flagBits == Translation) {
403 m[3][0] += x;
404 m[3][1] += y;
405 } else if (flagBits == Scale) {
406 m[3][0] = m[0][0] * x;
407 m[3][1] = m[1][1] * y;
408 } else if (flagBits == (Translation | Scale)) {
409 m[3][0] += m[0][0] * x;
410 m[3][1] += m[1][1] * y;
411 } else if (flagBits < Rotation) {
412 m[3][0] += m[0][0] * x + m[1][0] * y;
413 m[3][1] += m[0][1] * x + m[1][1] * y;
414 } else {
415 m[3][0] += m[0][0] * x + m[1][0] * y;
416 m[3][1] += m[0][1] * x + m[1][1] * y;
417 m[3][2] += m[0][2] * x + m[1][2] * y;
418 m[3][3] += m[0][3] * x + m[1][3] * y;
419 }
420 flagBits |= Translation;
421}
422
423void QDoubleMatrix4x4::translate(double x, double y, double z)
424{
425 if (flagBits == Identity) {
426 m[3][0] = x;
427 m[3][1] = y;
428 m[3][2] = z;
429 } else if (flagBits == Translation) {
430 m[3][0] += x;
431 m[3][1] += y;
432 m[3][2] += z;
433 } else if (flagBits == Scale) {
434 m[3][0] = m[0][0] * x;
435 m[3][1] = m[1][1] * y;
436 m[3][2] = m[2][2] * z;
437 } else if (flagBits == (Translation | Scale)) {
438 m[3][0] += m[0][0] * x;
439 m[3][1] += m[1][1] * y;
440 m[3][2] += m[2][2] * z;
441 } else if (flagBits < Rotation) {
442 m[3][0] += m[0][0] * x + m[1][0] * y;
443 m[3][1] += m[0][1] * x + m[1][1] * y;
444 m[3][2] += m[2][2] * z;
445 } else {
446 m[3][0] += m[0][0] * x + m[1][0] * y + m[2][0] * z;
447 m[3][1] += m[0][1] * x + m[1][1] * y + m[2][1] * z;
448 m[3][2] += m[0][2] * x + m[1][2] * y + m[2][2] * z;
449 m[3][3] += m[0][3] * x + m[1][3] * y + m[2][3] * z;
450 }
451 flagBits |= Translation;
452}
453
455{
456 rotate(angle, vector.x(), vector.y(), vector.z());
457}
458
459void QDoubleMatrix4x4::rotate(double angle, double x, double y, double z)
460{
461 if (angle == 0.0)
462 return;
463 double c, s;
464 if (angle == 90.0 || angle == -270.0) {
465 s = 1.0;
466 c = 0.0;
467 } else if (angle == -90.0 || angle == 270.0) {
468 s = -1.0;
469 c = 0.0;
470 } else if (angle == 180.0 || angle == -180.0) {
471 s = 0.0;
472 c = -1.0;
473 } else {
474 double a = qDegreesToRadians(angle);
475 c = std::cos(a);
476 s = std::sin(a);
477 }
478 if (x == 0.0) {
479 if (y == 0.0) {
480 if (z != 0.0) {
481 // Rotate around the Z axis.
482 if (z < 0)
483 s = -s;
484 double tmp;
485 m[0][0] = (tmp = m[0][0]) * c + m[1][0] * s;
486 m[1][0] = m[1][0] * c - tmp * s;
487 m[0][1] = (tmp = m[0][1]) * c + m[1][1] * s;
488 m[1][1] = m[1][1] * c - tmp * s;
489 m[0][2] = (tmp = m[0][2]) * c + m[1][2] * s;
490 m[1][2] = m[1][2] * c - tmp * s;
491 m[0][3] = (tmp = m[0][3]) * c + m[1][3] * s;
492 m[1][3] = m[1][3] * c - tmp * s;
493
494 flagBits |= Rotation2D;
495 return;
496 }
497 } else if (z == 0.0) {
498 // Rotate around the Y axis.
499 if (y < 0)
500 s = -s;
501 double tmp;
502 m[2][0] = (tmp = m[2][0]) * c + m[0][0] * s;
503 m[0][0] = m[0][0] * c - tmp * s;
504 m[2][1] = (tmp = m[2][1]) * c + m[0][1] * s;
505 m[0][1] = m[0][1] * c - tmp * s;
506 m[2][2] = (tmp = m[2][2]) * c + m[0][2] * s;
507 m[0][2] = m[0][2] * c - tmp * s;
508 m[2][3] = (tmp = m[2][3]) * c + m[0][3] * s;
509 m[0][3] = m[0][3] * c - tmp * s;
510
511 flagBits |= Rotation;
512 return;
513 }
514 } else if (y == 0.0 && z == 0.0) {
515 // Rotate around the X axis.
516 if (x < 0)
517 s = -s;
518 double tmp;
519 m[1][0] = (tmp = m[1][0]) * c + m[2][0] * s;
520 m[2][0] = m[2][0] * c - tmp * s;
521 m[1][1] = (tmp = m[1][1]) * c + m[2][1] * s;
522 m[2][1] = m[2][1] * c - tmp * s;
523 m[1][2] = (tmp = m[1][2]) * c + m[2][2] * s;
524 m[2][2] = m[2][2] * c - tmp * s;
525 m[1][3] = (tmp = m[1][3]) * c + m[2][3] * s;
526 m[2][3] = m[2][3] * c - tmp * s;
527
528 flagBits |= Rotation;
529 return;
530 }
531
532 double len = double(x) * double(x) +
533 double(y) * double(y) +
534 double(z) * double(z);
535 if (!qFuzzyCompare(len, 1.0) && !qFuzzyIsNull(len)) {
536 len = std::sqrt(len);
537 x = double(double(x) / len);
538 y = double(double(y) / len);
539 z = double(double(z) / len);
540 }
541 double ic = 1.0 - c;
542 QDoubleMatrix4x4 rot(1); // The "1" says to not load the identity.
543 rot.m[0][0] = x * x * ic + c;
544 rot.m[1][0] = x * y * ic - z * s;
545 rot.m[2][0] = x * z * ic + y * s;
546 rot.m[3][0] = 0.0;
547 rot.m[0][1] = y * x * ic + z * s;
548 rot.m[1][1] = y * y * ic + c;
549 rot.m[2][1] = y * z * ic - x * s;
550 rot.m[3][1] = 0.0;
551 rot.m[0][2] = x * z * ic - y * s;
552 rot.m[1][2] = y * z * ic + x * s;
553 rot.m[2][2] = z * z * ic + c;
554 rot.m[3][2] = 0.0;
555 rot.m[0][3] = 0.0;
556 rot.m[1][3] = 0.0;
557 rot.m[2][3] = 0.0;
558 rot.m[3][3] = 1.0;
559 rot.flagBits = Rotation;
560 *this *= rot;
561}
562
563void QDoubleMatrix4x4::projectedRotate(double angle, double x, double y, double z,
564 double distanceToPlane)
565{
566 // Used by QGraphicsRotation::applyTo() to perform a rotation
567 // and projection back to 2D in a single step.
568 if (qIsNull(distanceToPlane))
569 return rotate(angle, x, y, z);
570 if (angle == 0.0)
571 return;
572 double c, s;
573 if (angle == 90.0 || angle == -270.0) {
574 s = 1.0;
575 c = 0.0;
576 } else if (angle == -90.0 || angle == 270.0) {
577 s = -1.0;
578 c = 0.0;
579 } else if (angle == 180.0 || angle == -180.0) {
580 s = 0.0;
581 c = -1.0;
582 } else {
583 double a = qDegreesToRadians(angle);
584 c = std::cos(a);
585 s = std::sin(a);
586 }
587
588 const double d = 1.0 / distanceToPlane;
589 if (x == 0.0) {
590 if (y == 0.0) {
591 if (z != 0.0) {
592 // Rotate around the Z axis.
593 if (z < 0)
594 s = -s;
595 double tmp;
596 m[0][0] = (tmp = m[0][0]) * c + m[1][0] * s;
597 m[1][0] = m[1][0] * c - tmp * s;
598 m[0][1] = (tmp = m[0][1]) * c + m[1][1] * s;
599 m[1][1] = m[1][1] * c - tmp * s;
600 m[0][2] = (tmp = m[0][2]) * c + m[1][2] * s;
601 m[1][2] = m[1][2] * c - tmp * s;
602 m[0][3] = (tmp = m[0][3]) * c + m[1][3] * s;
603 m[1][3] = m[1][3] * c - tmp * s;
604
605 flagBits |= Rotation2D;
606 return;
607 }
608 } else if (z == 0.0) {
609 // Rotate around the Y axis.
610 if (y < 0)
611 s = -s;
612 s *= d;
613 m[0][0] = m[0][0] * c + m[3][0] * s;
614 m[0][1] = m[0][1] * c + m[3][1] * s;
615 m[0][2] = m[0][2] * c + m[3][2] * s;
616 m[0][3] = m[0][3] * c + m[3][3] * s;
617 flagBits = General;
618 return;
619 }
620 } else if (y == 0.0 && z == 0.0) {
621 // Rotate around the X axis.
622 if (x < 0)
623 s = -s;
624 s *= d;
625 m[1][0] = m[1][0] * c - m[3][0] * s;
626 m[1][1] = m[1][1] * c - m[3][1] * s;
627 m[1][2] = m[1][2] * c - m[3][2] * s;
628 m[1][3] = m[1][3] * c - m[3][3] * s;
629 flagBits = General;
630 return;
631 }
632 double len = double(x) * double(x) +
633 double(y) * double(y) +
634 double(z) * double(z);
635 if (!qFuzzyCompare(len, 1.0) && !qFuzzyIsNull(len)) {
636 len = std::sqrt(len);
637 x = double(double(x) / len);
638 y = double(double(y) / len);
639 z = double(double(z) / len);
640 }
641 double ic = 1.0 - c;
642 QDoubleMatrix4x4 rot(1); // The "1" says to not load the identity.
643 rot.m[0][0] = x * x * ic + c;
644 rot.m[1][0] = x * y * ic - z * s;
645 rot.m[2][0] = 0.0;
646 rot.m[3][0] = 0.0;
647 rot.m[0][1] = y * x * ic + z * s;
648 rot.m[1][1] = y * y * ic + c;
649 rot.m[2][1] = 0.0;
650 rot.m[3][1] = 0.0;
651 rot.m[0][2] = 0.0;
652 rot.m[1][2] = 0.0;
653 rot.m[2][2] = 1.0;
654 rot.m[3][2] = 0.0;
655 rot.m[0][3] = (x * z * ic - y * s) * -d;
656 rot.m[1][3] = (y * z * ic + x * s) * -d;
657 rot.m[2][3] = 0.0;
658 rot.m[3][3] = 1.0;
659 rot.flagBits = General;
660 *this *= rot;
661}
662
664{
665 // Note: rect.right() and rect.bottom() subtract 1 in QRect,
666 // which gives the location of a pixel within the rectangle,
667 // instead of the extent of the rectangle. We want the extent.
668 // QRectF expresses the extent properly.
669 ortho(rect.x(), rect.x() + rect.width(), rect.y() + rect.height(), rect.y(), -1.0, 1.0);
670}
671
673{
674 ortho(rect.left(), rect.right(), rect.bottom(), rect.top(), -1.0, 1.0);
675}
676
677void QDoubleMatrix4x4::ortho(double left, double right, double bottom, double top, double nearPlane, double farPlane)
678{
679 // Bail out if the projection volume is zero-sized.
680 if (left == right || bottom == top || nearPlane == farPlane)
681 return;
682
683 // Construct the projection.
684 double width = right - left;
685 double invheight = top - bottom;
686 double clip = farPlane - nearPlane;
688 m.m[0][0] = 2.0 / width;
689 m.m[1][0] = 0.0;
690 m.m[2][0] = 0.0;
691 m.m[3][0] = -(left + right) / width;
692 m.m[0][1] = 0.0;
693 m.m[1][1] = 2.0 / invheight;
694 m.m[2][1] = 0.0;
695 m.m[3][1] = -(top + bottom) / invheight;
696 m.m[0][2] = 0.0;
697 m.m[1][2] = 0.0;
698 m.m[2][2] = -2.0 / clip;
699 m.m[3][2] = -(nearPlane + farPlane) / clip;
700 m.m[0][3] = 0.0;
701 m.m[1][3] = 0.0;
702 m.m[2][3] = 0.0;
703 m.m[3][3] = 1.0;
704 m.flagBits = Translation | Scale;
705
706 // Apply the projection.
707 *this *= m;
708}
709
710void QDoubleMatrix4x4::frustum(double left, double right, double bottom, double top, double nearPlane, double farPlane)
711{
712 // Bail out if the projection volume is zero-sized.
713 if (left == right || bottom == top || nearPlane == farPlane)
714 return;
715
716 // Construct the projection.
718 double width = right - left;
719 double invheight = top - bottom;
720 double clip = farPlane - nearPlane;
721 m.m[0][0] = 2.0 * nearPlane / width;
722 m.m[1][0] = 0.0;
723 m.m[2][0] = (left + right) / width;
724 m.m[3][0] = 0.0;
725 m.m[0][1] = 0.0;
726 m.m[1][1] = 2.0 * nearPlane / invheight;
727 m.m[2][1] = (top + bottom) / invheight;
728 m.m[3][1] = 0.0;
729 m.m[0][2] = 0.0;
730 m.m[1][2] = 0.0;
731 m.m[2][2] = -(nearPlane + farPlane) / clip;
732 m.m[3][2] = -2.0 * nearPlane * farPlane / clip;
733 m.m[0][3] = 0.0;
734 m.m[1][3] = 0.0;
735 m.m[2][3] = -1.0;
736 m.m[3][3] = 0.0;
737 m.flagBits = General;
738
739 // Apply the projection.
740 *this *= m;
741}
742
743void QDoubleMatrix4x4::perspective(double verticalAngle, double aspectRatio, double nearPlane, double farPlane)
744{
745 // Bail out if the projection volume is zero-sized.
746 if (nearPlane == farPlane || aspectRatio == 0.0)
747 return;
748
749 // Construct the projection.
751 double radians = qDegreesToRadians(verticalAngle / 2.0);
752 double sine = std::sin(radians);
753 if (sine == 0.0)
754 return;
755 double cotan = std::cos(radians) / sine;
756 double clip = farPlane - nearPlane;
757 m.m[0][0] = cotan / aspectRatio;
758 m.m[1][0] = 0.0;
759 m.m[2][0] = 0.0;
760 m.m[3][0] = 0.0;
761 m.m[0][1] = 0.0;
762 m.m[1][1] = cotan;
763 m.m[2][1] = 0.0;
764 m.m[3][1] = 0.0;
765 m.m[0][2] = 0.0;
766 m.m[1][2] = 0.0;
767 m.m[2][2] = -(nearPlane + farPlane) / clip;
768 m.m[3][2] = -(2.0 * nearPlane * farPlane) / clip;
769 m.m[0][3] = 0.0;
770 m.m[1][3] = 0.0;
771 m.m[2][3] = -1.0;
772 m.m[3][3] = 0.0;
773 m.flagBits = General;
774
775 // Apply the projection.
776 *this *= m;
777}
778
780{
781 QDoubleVector3D forward = center - eye;
782 if (qFuzzyIsNull(forward.x()) && qFuzzyIsNull(forward.y()) && qFuzzyIsNull(forward.z()))
783 return;
784
785 forward.normalize();
786 QDoubleVector3D side = QDoubleVector3D::crossProduct(forward, up).normalized();
787 QDoubleVector3D upVector = QDoubleVector3D::crossProduct(side, forward);
788
790 m.m[0][0] = side.x();
791 m.m[1][0] = side.y();
792 m.m[2][0] = side.z();
793 m.m[3][0] = 0.0;
794 m.m[0][1] = upVector.x();
795 m.m[1][1] = upVector.y();
796 m.m[2][1] = upVector.z();
797 m.m[3][1] = 0.0;
798 m.m[0][2] = -forward.x();
799 m.m[1][2] = -forward.y();
800 m.m[2][2] = -forward.z();
801 m.m[3][2] = 0.0;
802 m.m[0][3] = 0.0;
803 m.m[1][3] = 0.0;
804 m.m[2][3] = 0.0;
805 m.m[3][3] = 1.0;
806 m.flagBits = Rotation;
807
808 *this *= m;
809 translate(-eye);
810}
811
812void QDoubleMatrix4x4::viewport(double left, double bottom, double width, double height, double nearPlane, double farPlane)
813{
814 const double w2 = width / 2.0;
815 const double h2 = height / 2.0;
816
818 m.m[0][0] = w2;
819 m.m[1][0] = 0.0;
820 m.m[2][0] = 0.0;
821 m.m[3][0] = left + w2;
822 m.m[0][1] = 0.0;
823 m.m[1][1] = h2;
824 m.m[2][1] = 0.0;
825 m.m[3][1] = bottom + h2;
826 m.m[0][2] = 0.0;
827 m.m[1][2] = 0.0;
828 m.m[2][2] = (farPlane - nearPlane) / 2.0;
829 m.m[3][2] = (nearPlane + farPlane) / 2.0;
830 m.m[0][3] = 0.0;
831 m.m[1][3] = 0.0;
832 m.m[2][3] = 0.0;
833 m.m[3][3] = 1.0;
834 m.flagBits = General;
835
836 *this *= m;
837}
838
840{
841 // Multiplying the y and z coordinates with -1 does NOT flip between right-handed and
842 // left-handed coordinate systems, it just rotates 180 degrees around the x axis, so
843 // I'm deprecating this function.
844 if (flagBits < Rotation2D) {
845 // Translation | Scale
846 m[1][1] = -m[1][1];
847 m[2][2] = -m[2][2];
848 } else {
849 m[1][0] = -m[1][0];
850 m[1][1] = -m[1][1];
851 m[1][2] = -m[1][2];
852 m[1][3] = -m[1][3];
853 m[2][0] = -m[2][0];
854 m[2][1] = -m[2][1];
855 m[2][2] = -m[2][2];
856 m[2][3] = -m[2][3];
857 }
858 flagBits |= Scale;
859}
860
862{
863 for (int row = 0; row < 4; ++row)
864 for (int col = 0; col < 4; ++col)
865 values[row * 4 + col] = double(m[col][row]);
866}
867
869{
870 if (flagBits < Scale) {
871 // Translation
872 return QRect(qRound(rect.x() + m[3][0]),
873 qRound(rect.y() + m[3][1]),
874 rect.width(), rect.height());
875 } else if (flagBits < Rotation2D) {
876 // Translation | Scale
877 double x = rect.x() * m[0][0] + m[3][0];
878 double y = rect.y() * m[1][1] + m[3][1];
879 double w = rect.width() * m[0][0];
880 double h = rect.height() * m[1][1];
881 if (w < 0) {
882 w = -w;
883 x -= w;
884 }
885 if (h < 0) {
886 h = -h;
887 y -= h;
888 }
889 return QRect(qRound(x), qRound(y), qRound(w), qRound(h));
890 }
891
892 QPoint tl = map(rect.topLeft());
893 QPoint tr = map(QPoint(rect.x() + rect.width(), rect.y()));
894 QPoint bl = map(QPoint(rect.x(), rect.y() + rect.height()));
895 QPoint br = map(QPoint(rect.x() + rect.width(),
896 rect.y() + rect.height()));
897
898 int xmin = qMin(qMin(tl.x(), tr.x()), qMin(bl.x(), br.x()));
899 int xmax = qMax(qMax(tl.x(), tr.x()), qMax(bl.x(), br.x()));
900 int ymin = qMin(qMin(tl.y(), tr.y()), qMin(bl.y(), br.y()));
901 int ymax = qMax(qMax(tl.y(), tr.y()), qMax(bl.y(), br.y()));
902
903 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
904}
905
907{
908 if (flagBits < Scale) {
909 // Translation
910 return rect.translated(m[3][0], m[3][1]);
911 } else if (flagBits < Rotation2D) {
912 // Translation | Scale
913 double x = rect.x() * m[0][0] + m[3][0];
914 double y = rect.y() * m[1][1] + m[3][1];
915 double w = rect.width() * m[0][0];
916 double h = rect.height() * m[1][1];
917 if (w < 0) {
918 w = -w;
919 x -= w;
920 }
921 if (h < 0) {
922 h = -h;
923 y -= h;
924 }
925 return QRectF(x, y, w, h);
926 }
927
928 QPointF tl = map(rect.topLeft()); QPointF tr = map(rect.topRight());
929 QPointF bl = map(rect.bottomLeft()); QPointF br = map(rect.bottomRight());
930
931 double xmin = qMin(qMin(tl.x(), tr.x()), qMin(bl.x(), br.x()));
932 double xmax = qMax(qMax(tl.x(), tr.x()), qMax(bl.x(), br.x()));
933 double ymin = qMin(qMin(tl.y(), tr.y()), qMin(bl.y(), br.y()));
934 double ymax = qMax(qMax(tl.y(), tr.y()), qMax(bl.y(), br.y()));
935
936 return QRectF(QPointF(xmin, ymin), QPointF(xmax, ymax));
937}
938
939QDoubleMatrix4x4 QDoubleMatrix4x4::orthonormalInverse() const
940{
941 QDoubleMatrix4x4 result(1); // The '1' says not to load identity
942
943 result.m[0][0] = m[0][0];
944 result.m[1][0] = m[0][1];
945 result.m[2][0] = m[0][2];
946
947 result.m[0][1] = m[1][0];
948 result.m[1][1] = m[1][1];
949 result.m[2][1] = m[1][2];
950
951 result.m[0][2] = m[2][0];
952 result.m[1][2] = m[2][1];
953 result.m[2][2] = m[2][2];
954
955 result.m[0][3] = 0.0;
956 result.m[1][3] = 0.0;
957 result.m[2][3] = 0.0;
958
959 result.m[3][0] = -(result.m[0][0] * m[3][0] + result.m[1][0] * m[3][1] + result.m[2][0] * m[3][2]);
960 result.m[3][1] = -(result.m[0][1] * m[3][0] + result.m[1][1] * m[3][1] + result.m[2][1] * m[3][2]);
961 result.m[3][2] = -(result.m[0][2] * m[3][0] + result.m[1][2] * m[3][1] + result.m[2][2] * m[3][2]);
962 result.m[3][3] = 1.0;
963
964 result.flagBits = flagBits;
965
966 return result;
967}
968
970{
971 // If the last row is not (0, 0, 0, 1), the matrix is not a special type.
972 flagBits = General;
973 if (m[0][3] != 0 || m[1][3] != 0 || m[2][3] != 0 || m[3][3] != 1)
974 return;
975
976 flagBits &= ~Perspective;
977
978 // If the last column is (0, 0, 0, 1), then there is no translation.
979 if (m[3][0] == 0 && m[3][1] == 0 && m[3][2] == 0)
980 flagBits &= ~Translation;
981
982 // If the two first elements of row 3 and column 3 are 0, then any rotation must be about Z.
983 if (!m[0][2] && !m[1][2] && !m[2][0] && !m[2][1]) {
984 flagBits &= ~Rotation;
985 // If the six non-diagonal elements in the top left 3x3 matrix are 0, there is no rotation.
986 if (!m[0][1] && !m[1][0]) {
987 flagBits &= ~Rotation2D;
988 // Check for identity.
989 if (m[0][0] == 1 && m[1][1] == 1 && m[2][2] == 1)
990 flagBits &= ~Scale;
991 } else {
992 // If the columns are orthonormal and form a right-handed system, then there is no scale.
993 double det = matrixDet2(m, 0, 1, 0, 1);
994 double lenX = m[0][0] * m[0][0] + m[0][1] * m[0][1];
995 double lenY = m[1][0] * m[1][0] + m[1][1] * m[1][1];
996 double lenZ = m[2][2];
997 if (qFuzzyCompare(det, 1.0) && qFuzzyCompare(lenX, 1.0)
998 && qFuzzyCompare(lenY, 1.0) && qFuzzyCompare(lenZ, 1.0))
999 {
1000 flagBits &= ~Scale;
1001 }
1002 }
1003 } else {
1004 // If the columns are orthonormal and form a right-handed system, then there is no scale.
1005 double det = matrixDet3(m, 0, 1, 2, 0, 1, 2);
1006 double lenX = m[0][0] * m[0][0] + m[0][1] * m[0][1] + m[0][2] * m[0][2];
1007 double lenY = m[1][0] * m[1][0] + m[1][1] * m[1][1] + m[1][2] * m[1][2];
1008 double lenZ = m[2][0] * m[2][0] + m[2][1] * m[2][1] + m[2][2] * m[2][2];
1009 if (qFuzzyCompare(det, 1.0) && qFuzzyCompare(lenX, 1.0)
1010 && qFuzzyCompare(lenY, 1.0) && qFuzzyCompare(lenZ, 1.0))
1011 {
1012 flagBits &= ~Scale;
1013 }
1014 }
1015}
1016
1017#ifndef QT_NO_DEBUG_STREAM
1018
1020{
1021 QDebugStateSaver saver(dbg);
1022 // Create a string that represents the matrix type.
1024 if (m.flagBits == QDoubleMatrix4x4::Identity) {
1025 bits = "Identity";
1026 } else if (m.flagBits == QDoubleMatrix4x4::General) {
1027 bits = "General";
1028 } else {
1029 if ((m.flagBits & QDoubleMatrix4x4::Translation) != 0)
1030 bits += "Translation,";
1031 if ((m.flagBits & QDoubleMatrix4x4::Scale) != 0)
1032 bits += "Scale,";
1033 if ((m.flagBits & QDoubleMatrix4x4::Rotation2D) != 0)
1034 bits += "Rotation2D,";
1035 if ((m.flagBits & QDoubleMatrix4x4::Rotation) != 0)
1036 bits += "Rotation,";
1037 if ((m.flagBits & QDoubleMatrix4x4::Perspective) != 0)
1038 bits += "Perspective,";
1039 if (!bits.isEmpty())
1040 bits = bits.left(bits.size() - 1);
1041 }
1042
1043 // Output in row-major order because it is more human-readable.
1044 dbg.nospace() << "QDoubleMatrix4x4(type:" << bits.constData() << Qt::endl
1045 << qSetFieldWidth(10)
1046 << m(0, 0) << m(0, 1) << m(0, 2) << m(0, 3) << Qt::endl
1047 << m(1, 0) << m(1, 1) << m(1, 2) << m(1, 3) << Qt::endl
1048 << m(2, 0) << m(2, 1) << m(2, 2) << m(2, 3) << Qt::endl
1049 << m(3, 0) << m(3, 1) << m(3, 2) << m(3, 3) << Qt::endl
1050 << qSetFieldWidth(0) << ')';
1051 return dbg;
1052}
1053
1054#endif
1055
1056#ifndef QT_NO_DATASTREAM
1057
1059{
1060 for (int row = 0; row < 4; ++row)
1061 for (int col = 0; col < 4; ++col)
1062 stream << matrix(row, col);
1063 return stream;
1064}
1065
1067{
1068 double x;
1069 for (int row = 0; row < 4; ++row) {
1070 for (int col = 0; col < 4; ++col) {
1071 stream >> x;
1072 matrix(row, col) = x;
1073 }
1074 }
1075 matrix.optimize();
1076 return stream;
1077}
1078
1079#endif // QT_NO_DATASTREAM
1080
\inmodule QtCore
Definition qbytearray.h:57
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
QByteArray left(qsizetype n) const &
Definition qbytearray.h:169
\inmodule QtCore\reentrant
Definition qdatastream.h:46
\inmodule QtCore
\inmodule QtCore
QDoubleMatrix4x4 transposed() const
void translate(const QDoubleVector3D &vector)
void viewport(const QRectF &rect)
void copyDataTo(double *values) const
void frustum(double left, double right, double bottom, double top, double nearPlane, double farPlane)
double determinant() const
friend bool qFuzzyCompare(const QDoubleMatrix4x4 &m1, const QDoubleMatrix4x4 &m2)
QDoubleMatrix4x4 inverted(bool *invertible=nullptr) const
void scale(const QDoubleVector3D &vector)
QRect mapRect(const QRect &rect) const
QDoubleMatrix4x4 & operator/=(double divisor)
void ortho(const QRect &rect)
void lookAt(const QDoubleVector3D &eye, const QDoubleVector3D &center, const QDoubleVector3D &up)
void perspective(double verticalAngle, double aspectRatio, double nearPlane, double farPlane)
void rotate(double angle, const QDoubleVector3D &vector)
Q_DECL_CONSTEXPR double x() const
Q_DECL_CONSTEXPR double y() const
Q_DECL_CONSTEXPR double z() const
static Q_DECL_CONSTEXPR QDoubleVector3D crossProduct(const QDoubleVector3D &v1, const QDoubleVector3D &v2)
\inmodule QtCore\reentrant
Definition qpoint.h:217
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:343
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:348
\inmodule QtCore\reentrant
Definition qpoint.h:25
constexpr int x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:130
constexpr int y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:135
\inmodule QtCore\reentrant
Definition qrect.h:484
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr QRect translated(int dx, int dy) const noexcept
Returns a copy of the rectangle that is translated dx along the x axis and dy along the y axis,...
Definition qrect.h:261
QMap< QString, QString > map
[6]
rect
[4]
Combined button and popup list for selecting options.
QTextStream & endl(QTextStream &stream)
Writes '\n' to the stream and flushes the stream.
static double matrixDet4(const double m[4][4])
QDoubleMatrix4x4 operator/(const QDoubleMatrix4x4 &matrix, double divisor)
QDebug operator<<(QDebug dbg, const QDoubleMatrix4x4 &m)
static double matrixDet2(const double m[4][4], int col0, int col1, int row0, int row1)
QDataStream & operator>>(QDataStream &stream, QDoubleMatrix4x4 &matrix)
static double matrixDet3(const double m[4][4], int col0, int col1, int col2, int row0, int row1, int row2)
EGLStreamKHR stream
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:349
bool qIsNull(qfloat16 f) noexcept
Definition qfloat16.h:354
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:327
constexpr float qDegreesToRadians(float degrees)
Definition qmath.h:260
static double matrixDet4(const double m[4][4])
static double matrixDet2(const double m[4][4], int col0, int col1, int row0, int row1)
static double matrixDet3(const double m[4][4], int col0, int col1, int col2, int row0, int row1, int row2)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLenum GLsizei GLsizei GLint * values
[15]
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat z
GLint GLint GLint GLint GLint x
[0]
const GLfloat * m
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint divisor
GLdouble GLdouble GLdouble GLdouble top
GLdouble GLdouble right
GLint GLsizei width
GLint left
GLint GLint bottom
GLfloat angle
GLint y
GLfloat GLfloat GLfloat GLfloat h
GLdouble s
[6]
Definition qopenglext.h:235
GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint GLdouble GLdouble w2
const GLubyte * c
GLuint GLenum matrix
GLenum GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const void * bits
GLenum GLenum GLsizei void * row
GLuint64EXT * result
[6]
GLenum GLsizei len
#define tr(X)
QTextStreamManipulator qSetFieldWidth(int width)
QList< int > vector
[14]