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 | |
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 | |
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 | |
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 | |
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, 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 | |
87 | static 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 | |
97 | 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, col0: 0, col1: 1, col2: 2, row0: 0, row1: 1, row2: 2); |
106 | return matrixDet4(m); |
107 | } |
108 | |
109 | 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, 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 | |
215 | 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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
492 | void QDoubleMatrix4x4::rotate(double angle, const QDoubleVector3D& vector) |
493 | { |
494 | rotate(angle, x: vector.x(), y: vector.y(), z: vector.z()); |
495 | } |
496 | |
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(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 | |
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(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 | |
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(left: rect.x(), right: rect.x() + rect.width(), bottom: rect.y() + rect.height(), top: rect.y(), nearPlane: -1.0, farPlane: 1.0); |
701 | } |
702 | |
703 | void 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 | |
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 | |
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 | |
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(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 | |
810 | void 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 | |
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 | |
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 | |
892 | 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 | |
899 | QRect 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 | |
937 | QRectF 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 | |
970 | 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 | |
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, 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 | |
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(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 | |
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 | |
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 | |