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
46 QT_BEGIN_NAMESPACE
47
48 static const double inv_dist_to_plane = 1.0 / 1024.0;
49
QDoubleMatrix4x4(const double * values)50 QDoubleMatrix4x4::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
QDoubleMatrix4x4(const double * values,int cols,int rows)58 QDoubleMatrix4x4::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
matrixDet2(const double m[4][4],int col0,int col1,int row0,int row1)73 static 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
matrixDet3(const double m[4][4],int col0,int col1,int col2,int row0,int row1,int row2)78 static 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, col1, col2, row1, row2)
83 - m[col1][row0] * matrixDet2(m, col0, col2, row1, row2)
84 + m[col2][row0] * matrixDet2(m, col0, col1, row1, row2);
85 }
86
matrixDet4(const double m[4][4])87 static inline double matrixDet4(const double m[4][4])
88 {
89 double det;
90 det = m[0][0] * matrixDet3(m, 1, 2, 3, 1, 2, 3);
91 det -= m[1][0] * matrixDet3(m, 0, 2, 3, 1, 2, 3);
92 det += m[2][0] * matrixDet3(m, 0, 1, 3, 1, 2, 3);
93 det -= m[3][0] * matrixDet3(m, 0, 1, 2, 1, 2, 3);
94 return det;
95 }
96
determinant() const97 double 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, 0, 1, 2, 0, 1, 2);
106 return matrixDet4(m);
107 }
108
inverted(bool * invertible) const109 QDoubleMatrix4x4 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, 0, 1, 2, 0, 1, 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, 1, 2, 1, 2) * det;
160 inv.m[0][1] = -matrixDet2(m, 0, 2, 1, 2) * det;
161 inv.m[0][2] = matrixDet2(m, 0, 1, 1, 2) * det;
162 inv.m[0][3] = 0;
163 inv.m[1][0] = -matrixDet2(m, 1, 2, 0, 2) * det;
164 inv.m[1][1] = matrixDet2(m, 0, 2, 0, 2) * det;
165 inv.m[1][2] = -matrixDet2(m, 0, 1, 0, 2) * det;
166 inv.m[1][3] = 0;
167 inv.m[2][0] = matrixDet2(m, 1, 2, 0, 1) * det;
168 inv.m[2][1] = -matrixDet2(m, 0, 2, 0, 1) * det;
169 inv.m[2][2] = matrixDet2(m, 0, 1, 0, 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, 1, 2, 3, 1, 2, 3) * det;
193 inv.m[0][1] = -matrixDet3(m, 0, 2, 3, 1, 2, 3) * det;
194 inv.m[0][2] = matrixDet3(m, 0, 1, 3, 1, 2, 3) * det;
195 inv.m[0][3] = -matrixDet3(m, 0, 1, 2, 1, 2, 3) * det;
196 inv.m[1][0] = -matrixDet3(m, 1, 2, 3, 0, 2, 3) * det;
197 inv.m[1][1] = matrixDet3(m, 0, 2, 3, 0, 2, 3) * det;
198 inv.m[1][2] = -matrixDet3(m, 0, 1, 3, 0, 2, 3) * det;
199 inv.m[1][3] = matrixDet3(m, 0, 1, 2, 0, 2, 3) * det;
200 inv.m[2][0] = matrixDet3(m, 1, 2, 3, 0, 1, 3) * det;
201 inv.m[2][1] = -matrixDet3(m, 0, 2, 3, 0, 1, 3) * det;
202 inv.m[2][2] = matrixDet3(m, 0, 1, 3, 0, 1, 3) * det;
203 inv.m[2][3] = -matrixDet3(m, 0, 1, 2, 0, 1, 3) * det;
204 inv.m[3][0] = -matrixDet3(m, 1, 2, 3, 0, 1, 2) * det;
205 inv.m[3][1] = matrixDet3(m, 0, 2, 3, 0, 1, 2) * det;
206 inv.m[3][2] = -matrixDet3(m, 0, 1, 3, 0, 1, 2) * det;
207 inv.m[3][3] = matrixDet3(m, 0, 1, 2, 0, 1, 2) * det;
208 inv.flagBits = flagBits;
209
210 if (invertible)
211 *invertible = true;
212 return inv;
213 }
214
transposed() const215 QDoubleMatrix4x4 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
operator /=(double divisor)228 QDoubleMatrix4x4& 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
operator /(const QDoubleMatrix4x4 & matrix,double divisor)250 QDoubleMatrix4x4 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
scale(const QDoubleVector3D & vector)273 void 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
scale(double x,double y)309 void 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
scale(double x,double y,double z)335 void 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
scale(double factor)368 void 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
translate(const QDoubleVector3D & vector)401 void 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
translate(double x,double y)435 void 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
translate(double x,double y,double z)461 void 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
rotate(double angle,const QDoubleVector3D & vector)492 void QDoubleMatrix4x4::rotate(double angle, const QDoubleVector3D& vector)
493 {
494 rotate(angle, vector.x(), vector.y(), vector.z());
495 }
496
rotate(double angle,double x,double y,double z)497 void 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(angle);
513 c = std::cos(a);
514 s = std::sin(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(len, 1.0) && !qFuzzyIsNull(len)) {
574 len = std::sqrt(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
projectedRotate(double angle,double x,double y,double z)601 void 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(angle);
619 c = std::cos(a);
620 s = std::sin(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(len, 1.0) && !qFuzzyIsNull(len)) {
667 len = std::sqrt(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
ortho(const QRect & rect)694 void 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(rect.x(), rect.x() + rect.width(), rect.y() + rect.height(), rect.y(), -1.0, 1.0);
701 }
702
ortho(const QRectF & rect)703 void QDoubleMatrix4x4::ortho(const QRectF& rect)
704 {
705 ortho(rect.left(), rect.right(), rect.bottom(), rect.top(), -1.0, 1.0);
706 }
707
ortho(double left,double right,double bottom,double top,double nearPlane,double farPlane)708 void 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
frustum(double left,double right,double bottom,double top,double nearPlane,double farPlane)741 void 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
perspective(double verticalAngle,double aspectRatio,double nearPlane,double farPlane)774 void 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(verticalAngle / 2.0);
783 double sine = std::sin(radians);
784 if (sine == 0.0)
785 return;
786 double cotan = std::cos(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
lookAt(const QDoubleVector3D & eye,const QDoubleVector3D & center,const QDoubleVector3D & up)810 void QDoubleMatrix4x4::lookAt(const QDoubleVector3D& eye, const QDoubleVector3D& center, const QDoubleVector3D& up)
811 {
812 QDoubleVector3D forward = center - eye;
813 if (qFuzzyIsNull(forward.x()) && qFuzzyIsNull(forward.y()) && qFuzzyIsNull(forward.z()))
814 return;
815
816 forward.normalize();
817 QDoubleVector3D side = QDoubleVector3D::crossProduct(forward, up).normalized();
818 QDoubleVector3D upVector = QDoubleVector3D::crossProduct(side, 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(-eye);
841 }
842
viewport(double left,double bottom,double width,double height,double nearPlane,double farPlane)843 void 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
flipCoordinates()870 void 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
copyDataTo(double * values) const892 void 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
mapRect(const QRect & rect) const899 QRect QDoubleMatrix4x4::mapRect(const QRect& rect) const
900 {
901 if (flagBits < Scale) {
902 // Translation
903 return QRect(qRound(rect.x() + m[3][0]),
904 qRound(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(x), qRound(y), qRound(w), qRound(h));
921 }
922
923 QPoint tl = map(rect.topLeft());
924 QPoint tr = map(QPoint(rect.x() + rect.width(), rect.y()));
925 QPoint bl = map(QPoint(rect.x(), rect.y() + rect.height()));
926 QPoint br = map(QPoint(rect.x() + rect.width(),
927 rect.y() + rect.height()));
928
929 int xmin = qMin(qMin(tl.x(), tr.x()), qMin(bl.x(), br.x()));
930 int xmax = qMax(qMax(tl.x(), tr.x()), qMax(bl.x(), br.x()));
931 int ymin = qMin(qMin(tl.y(), tr.y()), qMin(bl.y(), br.y()));
932 int ymax = qMax(qMax(tl.y(), tr.y()), qMax(bl.y(), br.y()));
933
934 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
935 }
936
mapRect(const QRectF & rect) const937 QRectF QDoubleMatrix4x4::mapRect(const QRectF& rect) const
938 {
939 if (flagBits < Scale) {
940 // Translation
941 return rect.translated(m[3][0], 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(rect.topLeft()); QPointF tr = map(rect.topRight());
960 QPointF bl = map(rect.bottomLeft()); QPointF br = map(rect.bottomRight());
961
962 double xmin = qMin(qMin(tl.x(), tr.x()), qMin(bl.x(), br.x()));
963 double xmax = qMax(qMax(tl.x(), tr.x()), qMax(bl.x(), br.x()));
964 double ymin = qMin(qMin(tl.y(), tr.y()), qMin(bl.y(), br.y()));
965 double ymax = qMax(qMax(tl.y(), tr.y()), qMax(bl.y(), br.y()));
966
967 return QRectF(QPointF(xmin, ymin), QPointF(xmax, ymax));
968 }
969
orthonormalInverse() const970 QDoubleMatrix4x4 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
optimize()1000 void 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, 0, 1, 0, 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(det, 1.0) && qFuzzyCompare(lenX, 1.0)
1029 && qFuzzyCompare(lenY, 1.0) && qFuzzyCompare(lenZ, 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, 0, 1, 2, 0, 1, 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(det, 1.0) && qFuzzyCompare(lenX, 1.0)
1041 && qFuzzyCompare(lenY, 1.0) && qFuzzyCompare(lenZ, 1.0))
1042 {
1043 flagBits &= ~Scale;
1044 }
1045 }
1046 }
1047
1048 #ifndef QT_NO_DEBUG_STREAM
1049
operator <<(QDebug dbg,const QDoubleMatrix4x4 & m)1050 QDebug 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(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(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(0) << ')';
1082 return dbg;
1083 }
1084
1085 #endif
1086
1087 #ifndef QT_NO_DATASTREAM
1088
operator <<(QDataStream & stream,const QDoubleMatrix4x4 & matrix)1089 QDataStream &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
operator >>(QDataStream & stream,QDoubleMatrix4x4 & matrix)1097 QDataStream &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
1112 QT_END_NAMESPACE
1113