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