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
5#include <QtCore/qmath.h>
6//#include <QtCore/qvariant.h>
7#include <QtCore/qdatastream.h>
8#include <cmath>
9
10QT_BEGIN_NAMESPACE
11
12QDoubleMatrix4x4::QDoubleMatrix4x4(const double *values)
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
59double QDoubleMatrix4x4::determinant() const
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
71QDoubleMatrix4x4 QDoubleMatrix4x4::inverted(bool *invertible) const
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) {
79 QDoubleMatrix4x4 inv;
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 }
94 QDoubleMatrix4x4 inv;
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
177QDoubleMatrix4x4 QDoubleMatrix4x4::transposed() const
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
190QDoubleMatrix4x4& QDoubleMatrix4x4::operator/=(double divisor)
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
212QDoubleMatrix4x4 operator/(const QDoubleMatrix4x4& matrix, double divisor)
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
235void QDoubleMatrix4x4::scale(const QDoubleVector3D& vector)
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
363void QDoubleMatrix4x4::translate(const QDoubleVector3D& vector)
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
454void QDoubleMatrix4x4::rotate(double angle, const QDoubleVector3D& vector)
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 if (double len = qHypot(x, y, z);
533 !qFuzzyIsNull(len) && !qFuzzyCompare(len, 1.0)) {
534 x /= len;
535 y /= len;
536 z /= len;
537 }
538 double ic = 1.0 - c;
539 QDoubleMatrix4x4 rot(1); // The "1" says to not load the identity.
540 rot.m[0][0] = x * x * ic + c;
541 rot.m[1][0] = x * y * ic - z * s;
542 rot.m[2][0] = x * z * ic + y * s;
543 rot.m[3][0] = 0.0;
544 rot.m[0][1] = y * x * ic + z * s;
545 rot.m[1][1] = y * y * ic + c;
546 rot.m[2][1] = y * z * ic - x * s;
547 rot.m[3][1] = 0.0;
548 rot.m[0][2] = x * z * ic - y * s;
549 rot.m[1][2] = y * z * ic + x * s;
550 rot.m[2][2] = z * z * ic + c;
551 rot.m[3][2] = 0.0;
552 rot.m[0][3] = 0.0;
553 rot.m[1][3] = 0.0;
554 rot.m[2][3] = 0.0;
555 rot.m[3][3] = 1.0;
556 rot.flagBits = Rotation;
557 *this *= rot;
558}
559
560void QDoubleMatrix4x4::projectedRotate(double angle, double x, double y, double z,
561 double distanceToPlane)
562{
563 // Used by QGraphicsRotation::applyTo() to perform a rotation
564 // and projection back to 2D in a single step.
565 if (qIsNull(distanceToPlane))
566 return rotate(angle, x, y, z);
567 if (angle == 0.0)
568 return;
569 double c, s;
570 if (angle == 90.0 || angle == -270.0) {
571 s = 1.0;
572 c = 0.0;
573 } else if (angle == -90.0 || angle == 270.0) {
574 s = -1.0;
575 c = 0.0;
576 } else if (angle == 180.0 || angle == -180.0) {
577 s = 0.0;
578 c = -1.0;
579 } else {
580 double a = qDegreesToRadians(angle);
581 c = std::cos(a);
582 s = std::sin(a);
583 }
584
585 const double d = 1.0 / distanceToPlane;
586 if (x == 0.0) {
587 if (y == 0.0) {
588 if (z != 0.0) {
589 // Rotate around the Z axis.
590 if (z < 0)
591 s = -s;
592 double tmp;
593 m[0][0] = (tmp = m[0][0]) * c + m[1][0] * s;
594 m[1][0] = m[1][0] * c - tmp * s;
595 m[0][1] = (tmp = m[0][1]) * c + m[1][1] * s;
596 m[1][1] = m[1][1] * c - tmp * s;
597 m[0][2] = (tmp = m[0][2]) * c + m[1][2] * s;
598 m[1][2] = m[1][2] * c - tmp * s;
599 m[0][3] = (tmp = m[0][3]) * c + m[1][3] * s;
600 m[1][3] = m[1][3] * c - tmp * s;
601
602 flagBits |= Rotation2D;
603 return;
604 }
605 } else if (z == 0.0) {
606 // Rotate around the Y axis.
607 if (y < 0)
608 s = -s;
609 s *= d;
610 m[0][0] = m[0][0] * c + m[3][0] * s;
611 m[0][1] = m[0][1] * c + m[3][1] * s;
612 m[0][2] = m[0][2] * c + m[3][2] * s;
613 m[0][3] = m[0][3] * c + m[3][3] * s;
614 flagBits = General;
615 return;
616 }
617 } else if (y == 0.0 && z == 0.0) {
618 // Rotate around the X axis.
619 if (x < 0)
620 s = -s;
621 s *= d;
622 m[1][0] = m[1][0] * c - m[3][0] * s;
623 m[1][1] = m[1][1] * c - m[3][1] * s;
624 m[1][2] = m[1][2] * c - m[3][2] * s;
625 m[1][3] = m[1][3] * c - m[3][3] * s;
626 flagBits = General;
627 return;
628 }
629
630 if (const double len = qHypot(x, y, z);
631 !qFuzzyIsNull(len) && !qFuzzyCompare(len, 1.0)) {
632 x /= len;
633 y /= len;
634 z /= len;
635 }
636 double ic = 1.0 - c;
637 QDoubleMatrix4x4 rot(1); // The "1" says to not load the identity.
638 rot.m[0][0] = x * x * ic + c;
639 rot.m[1][0] = x * y * ic - z * s;
640 rot.m[2][0] = 0.0;
641 rot.m[3][0] = 0.0;
642 rot.m[0][1] = y * x * ic + z * s;
643 rot.m[1][1] = y * y * ic + c;
644 rot.m[2][1] = 0.0;
645 rot.m[3][1] = 0.0;
646 rot.m[0][2] = 0.0;
647 rot.m[1][2] = 0.0;
648 rot.m[2][2] = 1.0;
649 rot.m[3][2] = 0.0;
650 rot.m[0][3] = (x * z * ic - y * s) * -d;
651 rot.m[1][3] = (y * z * ic + x * s) * -d;
652 rot.m[2][3] = 0.0;
653 rot.m[3][3] = 1.0;
654 rot.flagBits = General;
655 *this *= rot;
656}
657
658void QDoubleMatrix4x4::ortho(const QRect& rect)
659{
660 // Note: rect.right() and rect.bottom() subtract 1 in QRect,
661 // which gives the location of a pixel within the rectangle,
662 // instead of the extent of the rectangle. We want the extent.
663 // QRectF expresses the extent properly.
664 ortho(rect.x(), rect.x() + rect.width(), rect.y() + rect.height(), rect.y(), -1.0, 1.0);
665}
666
667void QDoubleMatrix4x4::ortho(const QRectF& rect)
668{
669 ortho(rect.left(), rect.right(), rect.bottom(), rect.top(), -1.0, 1.0);
670}
671
672void QDoubleMatrix4x4::ortho(double left, double right, double bottom, double top, double nearPlane, double farPlane)
673{
674 // Bail out if the projection volume is zero-sized.
675 if (left == right || bottom == top || nearPlane == farPlane)
676 return;
677
678 // Construct the projection.
679 double width = right - left;
680 double invheight = top - bottom;
681 double clip = farPlane - nearPlane;
682 QDoubleMatrix4x4 m(1);
683 m.m[0][0] = 2.0 / width;
684 m.m[1][0] = 0.0;
685 m.m[2][0] = 0.0;
686 m.m[3][0] = -(left + right) / width;
687 m.m[0][1] = 0.0;
688 m.m[1][1] = 2.0 / invheight;
689 m.m[2][1] = 0.0;
690 m.m[3][1] = -(top + bottom) / invheight;
691 m.m[0][2] = 0.0;
692 m.m[1][2] = 0.0;
693 m.m[2][2] = -2.0 / clip;
694 m.m[3][2] = -(nearPlane + farPlane) / clip;
695 m.m[0][3] = 0.0;
696 m.m[1][3] = 0.0;
697 m.m[2][3] = 0.0;
698 m.m[3][3] = 1.0;
699 m.flagBits = Translation | Scale;
700
701 // Apply the projection.
702 *this *= m;
703}
704
705void QDoubleMatrix4x4::frustum(double left, double right, double bottom, double top, double nearPlane, double farPlane)
706{
707 // Bail out if the projection volume is zero-sized.
708 if (left == right || bottom == top || nearPlane == farPlane)
709 return;
710
711 // Construct the projection.
712 QDoubleMatrix4x4 m(1);
713 double width = right - left;
714 double invheight = top - bottom;
715 double clip = farPlane - nearPlane;
716 m.m[0][0] = 2.0 * nearPlane / width;
717 m.m[1][0] = 0.0;
718 m.m[2][0] = (left + right) / width;
719 m.m[3][0] = 0.0;
720 m.m[0][1] = 0.0;
721 m.m[1][1] = 2.0 * nearPlane / invheight;
722 m.m[2][1] = (top + bottom) / invheight;
723 m.m[3][1] = 0.0;
724 m.m[0][2] = 0.0;
725 m.m[1][2] = 0.0;
726 m.m[2][2] = -(nearPlane + farPlane) / clip;
727 m.m[3][2] = -2.0 * nearPlane * farPlane / clip;
728 m.m[0][3] = 0.0;
729 m.m[1][3] = 0.0;
730 m.m[2][3] = -1.0;
731 m.m[3][3] = 0.0;
732 m.flagBits = General;
733
734 // Apply the projection.
735 *this *= m;
736}
737
738void QDoubleMatrix4x4::perspective(double verticalAngle, double aspectRatio, double nearPlane, double farPlane)
739{
740 // Bail out if the projection volume is zero-sized.
741 if (nearPlane == farPlane || aspectRatio == 0.0)
742 return;
743
744 // Construct the projection.
745 QDoubleMatrix4x4 m(1);
746 double radians = qDegreesToRadians(verticalAngle / 2.0);
747 double sine = std::sin(radians);
748 if (sine == 0.0)
749 return;
750 double cotan = std::cos(radians) / sine;
751 double clip = farPlane - nearPlane;
752 m.m[0][0] = cotan / aspectRatio;
753 m.m[1][0] = 0.0;
754 m.m[2][0] = 0.0;
755 m.m[3][0] = 0.0;
756 m.m[0][1] = 0.0;
757 m.m[1][1] = cotan;
758 m.m[2][1] = 0.0;
759 m.m[3][1] = 0.0;
760 m.m[0][2] = 0.0;
761 m.m[1][2] = 0.0;
762 m.m[2][2] = -(nearPlane + farPlane) / clip;
763 m.m[3][2] = -(2.0 * nearPlane * farPlane) / clip;
764 m.m[0][3] = 0.0;
765 m.m[1][3] = 0.0;
766 m.m[2][3] = -1.0;
767 m.m[3][3] = 0.0;
768 m.flagBits = General;
769
770 // Apply the projection.
771 *this *= m;
772}
773
774void QDoubleMatrix4x4::lookAt(const QDoubleVector3D& eye, const QDoubleVector3D& center, const QDoubleVector3D& up)
775{
776 QDoubleVector3D forward = center - eye;
777 if (qFuzzyIsNull(forward.x()) && qFuzzyIsNull(forward.y()) && qFuzzyIsNull(forward.z()))
778 return;
779
780 forward.normalize();
781 QDoubleVector3D side = QDoubleVector3D::crossProduct(forward, up).normalized();
782 QDoubleVector3D upVector = QDoubleVector3D::crossProduct(side, forward);
783
784 QDoubleMatrix4x4 m(1);
785 m.m[0][0] = side.x();
786 m.m[1][0] = side.y();
787 m.m[2][0] = side.z();
788 m.m[3][0] = 0.0;
789 m.m[0][1] = upVector.x();
790 m.m[1][1] = upVector.y();
791 m.m[2][1] = upVector.z();
792 m.m[3][1] = 0.0;
793 m.m[0][2] = -forward.x();
794 m.m[1][2] = -forward.y();
795 m.m[2][2] = -forward.z();
796 m.m[3][2] = 0.0;
797 m.m[0][3] = 0.0;
798 m.m[1][3] = 0.0;
799 m.m[2][3] = 0.0;
800 m.m[3][3] = 1.0;
801 m.flagBits = Rotation;
802
803 *this *= m;
804 translate(-eye);
805}
806
807void QDoubleMatrix4x4::viewport(double left, double bottom, double width, double height, double nearPlane, double farPlane)
808{
809 const double w2 = width / 2.0;
810 const double h2 = height / 2.0;
811
812 QDoubleMatrix4x4 m(1);
813 m.m[0][0] = w2;
814 m.m[1][0] = 0.0;
815 m.m[2][0] = 0.0;
816 m.m[3][0] = left + w2;
817 m.m[0][1] = 0.0;
818 m.m[1][1] = h2;
819 m.m[2][1] = 0.0;
820 m.m[3][1] = bottom + h2;
821 m.m[0][2] = 0.0;
822 m.m[1][2] = 0.0;
823 m.m[2][2] = (farPlane - nearPlane) / 2.0;
824 m.m[3][2] = (nearPlane + farPlane) / 2.0;
825 m.m[0][3] = 0.0;
826 m.m[1][3] = 0.0;
827 m.m[2][3] = 0.0;
828 m.m[3][3] = 1.0;
829 m.flagBits = General;
830
831 *this *= m;
832}
833
834void QDoubleMatrix4x4::flipCoordinates()
835{
836 // Multiplying the y and z coordinates with -1 does NOT flip between right-handed and
837 // left-handed coordinate systems, it just rotates 180 degrees around the x axis, so
838 // I'm deprecating this function.
839 if (flagBits < Rotation2D) {
840 // Translation | Scale
841 m[1][1] = -m[1][1];
842 m[2][2] = -m[2][2];
843 } else {
844 m[1][0] = -m[1][0];
845 m[1][1] = -m[1][1];
846 m[1][2] = -m[1][2];
847 m[1][3] = -m[1][3];
848 m[2][0] = -m[2][0];
849 m[2][1] = -m[2][1];
850 m[2][2] = -m[2][2];
851 m[2][3] = -m[2][3];
852 }
853 flagBits |= Scale;
854}
855
856void QDoubleMatrix4x4::copyDataTo(double *values) const
857{
858 for (int row = 0; row < 4; ++row)
859 for (int col = 0; col < 4; ++col)
860 values[row * 4 + col] = double(m[col][row]);
861}
862
863QRect QDoubleMatrix4x4::mapRect(const QRect& rect) const
864{
865 if (flagBits < Scale) {
866 // Translation
867 return QRect(qRound(rect.x() + m[3][0]),
868 qRound(rect.y() + m[3][1]),
869 rect.width(), rect.height());
870 } else if (flagBits < Rotation2D) {
871 // Translation | Scale
872 double x = rect.x() * m[0][0] + m[3][0];
873 double y = rect.y() * m[1][1] + m[3][1];
874 double w = rect.width() * m[0][0];
875 double h = rect.height() * m[1][1];
876 if (w < 0) {
877 w = -w;
878 x -= w;
879 }
880 if (h < 0) {
881 h = -h;
882 y -= h;
883 }
884 return QRect(qRound(x), qRound(y), qRound(w), qRound(h));
885 }
886
887 QPoint tl = map(rect.topLeft());
888 QPoint tr = map(QPoint(rect.x() + rect.width(), rect.y()));
889 QPoint bl = map(QPoint(rect.x(), rect.y() + rect.height()));
890 QPoint br = map(QPoint(rect.x() + rect.width(),
891 rect.y() + rect.height()));
892
893 int xmin = qMin(qMin(tl.x(), tr.x()), qMin(bl.x(), br.x()));
894 int xmax = qMax(qMax(tl.x(), tr.x()), qMax(bl.x(), br.x()));
895 int ymin = qMin(qMin(tl.y(), tr.y()), qMin(bl.y(), br.y()));
896 int ymax = qMax(qMax(tl.y(), tr.y()), qMax(bl.y(), br.y()));
897
898 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
899}
900
901QRectF QDoubleMatrix4x4::mapRect(const QRectF& rect) const
902{
903 if (flagBits < Scale) {
904 // Translation
905 return rect.translated(m[3][0], m[3][1]);
906 } else if (flagBits < Rotation2D) {
907 // Translation | Scale
908 double x = rect.x() * m[0][0] + m[3][0];
909 double y = rect.y() * m[1][1] + m[3][1];
910 double w = rect.width() * m[0][0];
911 double h = rect.height() * m[1][1];
912 if (w < 0) {
913 w = -w;
914 x -= w;
915 }
916 if (h < 0) {
917 h = -h;
918 y -= h;
919 }
920 return QRectF(x, y, w, h);
921 }
922
923 QPointF tl = map(rect.topLeft()); QPointF tr = map(rect.topRight());
924 QPointF bl = map(rect.bottomLeft()); QPointF br = map(rect.bottomRight());
925
926 double xmin = qMin(qMin(tl.x(), tr.x()), qMin(bl.x(), br.x()));
927 double xmax = qMax(qMax(tl.x(), tr.x()), qMax(bl.x(), br.x()));
928 double ymin = qMin(qMin(tl.y(), tr.y()), qMin(bl.y(), br.y()));
929 double ymax = qMax(qMax(tl.y(), tr.y()), qMax(bl.y(), br.y()));
930
931 return QRectF(QPointF(xmin, ymin), QPointF(xmax, ymax));
932}
933
934QDoubleMatrix4x4 QDoubleMatrix4x4::orthonormalInverse() const
935{
936 QDoubleMatrix4x4 result(1); // The '1' says not to load identity
937
938 result.m[0][0] = m[0][0];
939 result.m[1][0] = m[0][1];
940 result.m[2][0] = m[0][2];
941
942 result.m[0][1] = m[1][0];
943 result.m[1][1] = m[1][1];
944 result.m[2][1] = m[1][2];
945
946 result.m[0][2] = m[2][0];
947 result.m[1][2] = m[2][1];
948 result.m[2][2] = m[2][2];
949
950 result.m[0][3] = 0.0;
951 result.m[1][3] = 0.0;
952 result.m[2][3] = 0.0;
953
954 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]);
955 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]);
956 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]);
957 result.m[3][3] = 1.0;
958
959 result.flagBits = flagBits;
960
961 return result;
962}
963
964void QDoubleMatrix4x4::optimize()
965{
966 // If the last row is not (0, 0, 0, 1), the matrix is not a special type.
967 flagBits = General;
968 if (m[0][3] != 0 || m[1][3] != 0 || m[2][3] != 0 || m[3][3] != 1)
969 return;
970
971 flagBits &= ~Perspective;
972
973 // If the last column is (0, 0, 0, 1), then there is no translation.
974 if (m[3][0] == 0 && m[3][1] == 0 && m[3][2] == 0)
975 flagBits &= ~Translation;
976
977 // If the two first elements of row 3 and column 3 are 0, then any rotation must be about Z.
978 if (!m[0][2] && !m[1][2] && !m[2][0] && !m[2][1]) {
979 flagBits &= ~Rotation;
980 // If the six non-diagonal elements in the top left 3x3 matrix are 0, there is no rotation.
981 if (!m[0][1] && !m[1][0]) {
982 flagBits &= ~Rotation2D;
983 // Check for identity.
984 if (m[0][0] == 1 && m[1][1] == 1 && m[2][2] == 1)
985 flagBits &= ~Scale;
986 } else {
987 // If the columns are orthonormal and form a right-handed system, then there is no scale.
988 double det = matrixDet2(m, 0, 1, 0, 1);
989 double lenX = m[0][0] * m[0][0] + m[0][1] * m[0][1];
990 double lenY = m[1][0] * m[1][0] + m[1][1] * m[1][1];
991 double lenZ = m[2][2];
992 if (qFuzzyCompare(det, 1.0) && qFuzzyCompare(lenX, 1.0)
993 && qFuzzyCompare(lenY, 1.0) && qFuzzyCompare(lenZ, 1.0))
994 {
995 flagBits &= ~Scale;
996 }
997 }
998 } else {
999 // If the columns are orthonormal and form a right-handed system, then there is no scale.
1000 double det = matrixDet3(m, 0, 1, 2, 0, 1, 2);
1001 double lenX = m[0][0] * m[0][0] + m[0][1] * m[0][1] + m[0][2] * m[0][2];
1002 double lenY = m[1][0] * m[1][0] + m[1][1] * m[1][1] + m[1][2] * m[1][2];
1003 double lenZ = m[2][0] * m[2][0] + m[2][1] * m[2][1] + m[2][2] * m[2][2];
1004 if (qFuzzyCompare(det, 1.0) && qFuzzyCompare(lenX, 1.0)
1005 && qFuzzyCompare(lenY, 1.0) && qFuzzyCompare(lenZ, 1.0))
1006 {
1007 flagBits &= ~Scale;
1008 }
1009 }
1010}
1011
1012#ifndef QT_NO_DEBUG_STREAM
1013
1014QDebug operator<<(QDebug dbg, const QDoubleMatrix4x4 &m)
1015{
1016 QDebugStateSaver saver(dbg);
1017 // Create a string that represents the matrix type.
1018 QByteArray bits;
1019 if (m.flagBits == QDoubleMatrix4x4::Identity) {
1020 bits = "Identity";
1021 } else if (m.flagBits == QDoubleMatrix4x4::General) {
1022 bits = "General";
1023 } else {
1024 if ((m.flagBits & QDoubleMatrix4x4::Translation) != 0)
1025 bits += "Translation,";
1026 if ((m.flagBits & QDoubleMatrix4x4::Scale) != 0)
1027 bits += "Scale,";
1028 if ((m.flagBits & QDoubleMatrix4x4::Rotation2D) != 0)
1029 bits += "Rotation2D,";
1030 if ((m.flagBits & QDoubleMatrix4x4::Rotation) != 0)
1031 bits += "Rotation,";
1032 if ((m.flagBits & QDoubleMatrix4x4::Perspective) != 0)
1033 bits += "Perspective,";
1034 if (!bits.isEmpty())
1035 bits = bits.left(bits.size() - 1);
1036 }
1037
1038 // Output in row-major order because it is more human-readable.
1039 dbg.nospace() << "QDoubleMatrix4x4(type:" << bits.constData() << Qt::endl
1040 << qSetFieldWidth(10)
1041 << m(0, 0) << m(0, 1) << m(0, 2) << m(0, 3) << Qt::endl
1042 << m(1, 0) << m(1, 1) << m(1, 2) << m(1, 3) << Qt::endl
1043 << m(2, 0) << m(2, 1) << m(2, 2) << m(2, 3) << Qt::endl
1044 << m(3, 0) << m(3, 1) << m(3, 2) << m(3, 3) << Qt::endl
1045 << qSetFieldWidth(0) << ')';
1046 return dbg;
1047}
1048
1049#endif
1050
1051#ifndef QT_NO_DATASTREAM
1052
1053QDataStream &operator<<(QDataStream &stream, const QDoubleMatrix4x4 &matrix)
1054{
1055 for (int row = 0; row < 4; ++row)
1056 for (int col = 0; col < 4; ++col)
1057 stream << matrix(row, col);
1058 return stream;
1059}
1060
1061QDataStream &operator>>(QDataStream &stream, QDoubleMatrix4x4 &matrix)
1062{
1063 double x;
1064 for (int row = 0; row < 4; ++row) {
1065 for (int col = 0; col < 4; ++col) {
1066 stream >> x;
1067 matrix(row, col) = x;
1068 }
1069 }
1070 matrix.optimize();
1071 return stream;
1072}
1073
1074#endif // QT_NO_DATASTREAM
1075
1076QT_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:4009
QDataStream & operator>>(QDataStream &stream, QImage &image)
Definition qimage.cpp:4035