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