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

source code of qtlocation/src/positioning/qdoublematrix4x4.cpp