Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qmatrix4x4.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qmatrix4x4.h"
5#include <QtCore/qmath.h>
6#include <QtCore/qvariant.h>
7#include <QtGui/qtransform.h>
8
9#include <cmath>
10
12
13#ifndef QT_NO_MATRIX4X4
14
39static const float inv_dist_to_plane = 1.0f / 1024.0f;
40
68{
69 for (int row = 0; row < 4; ++row)
70 for (int col = 0; col < 4; ++col)
71 m[col][row] = values[row * 4 + col];
72 flagBits = General;
73}
74
114QMatrix4x4::QMatrix4x4(const float *values, int cols, int rows)
115{
116 for (int col = 0; col < 4; ++col) {
117 for (int row = 0; row < 4; ++row) {
118 if (col < cols && row < rows)
119 m[col][row] = values[col * rows + row];
120 else if (col == row)
121 m[col][row] = 1.0f;
122 else
123 m[col][row] = 0.0f;
124 }
125 }
126 flagBits = General;
127}
128
141{
142 m[0][0] = transform.m11();
143 m[0][1] = transform.m12();
144 m[0][2] = 0.0f;
145 m[0][3] = transform.m13();
146 m[1][0] = transform.m21();
147 m[1][1] = transform.m22();
148 m[1][2] = 0.0f;
149 m[1][3] = transform.m23();
150 m[2][0] = 0.0f;
151 m[2][1] = 0.0f;
152 m[2][2] = 1.0f;
153 m[2][3] = 0.0f;
154 m[3][0] = transform.dx();
155 m[3][1] = transform.dy();
156 m[3][2] = 0.0f;
157 m[3][3] = transform.m33();
158 flagBits = General;
159}
160
245static inline double matrixDet2(const double m[4][4], int col0, int col1, int row0, int row1)
246{
247 return m[col0][row0] * m[col1][row1] - m[col0][row1] * m[col1][row0];
248}
249
250
251// The 4x4 matrix inverse algorithm is based on that described at:
252// http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q24
253// Some optimization has been done to avoid making copies of 3x3
254// sub-matrices and to unroll the loops.
255
256// Calculate the determinant of a 3x3 sub-matrix.
257// | A B C |
258// M = | D E F | det(M) = A * (EI - HF) - B * (DI - GF) + C * (DH - GE)
259// | G H I |
260static inline double matrixDet3
261 (const double m[4][4], int col0, int col1, int col2,
262 int row0, int row1, int row2)
263{
264 return m[col0][row0] * matrixDet2(m, col1, col2, row1, row2)
265 - m[col1][row0] * matrixDet2(m, col0, col2, row1, row2)
266 + m[col2][row0] * matrixDet2(m, col0, col1, row1, row2);
267}
268
269// Calculate the determinant of a 4x4 matrix.
270static inline double matrixDet4(const double m[4][4])
271{
272 double det;
273 det = m[0][0] * matrixDet3(m, 1, 2, 3, 1, 2, 3);
274 det -= m[1][0] * matrixDet3(m, 0, 2, 3, 1, 2, 3);
275 det += m[2][0] * matrixDet3(m, 0, 1, 3, 1, 2, 3);
276 det -= m[3][0] * matrixDet3(m, 0, 1, 2, 1, 2, 3);
277 return det;
278}
279
280static inline void copyToDoubles(const float m[4][4], double mm[4][4])
281{
282 for (int i = 0; i < 4; ++i)
283 for (int j = 0; j < 4; ++j)
284 mm[i][j] = double(m[i][j]);
285}
286
291{
292 if ((flagBits & ~(Translation | Rotation2D | Rotation)) == Identity)
293 return 1.0;
294
295 double mm[4][4];
296 copyToDoubles(m, mm);
297 if (flagBits < Rotation2D)
298 return mm[0][0] * mm[1][1] * mm[2][2]; // Translation | Scale
299 if (flagBits < Perspective)
300 return matrixDet3(mm, 0, 1, 2, 0, 1, 2);
301 return matrixDet4(mm);
302}
303
316QMatrix4x4 QMatrix4x4::inverted(bool *invertible) const
317{
318 // Handle some of the easy cases first.
319 if (flagBits == Identity) {
320 if (invertible)
321 *invertible = true;
322 return QMatrix4x4();
323 } else if (flagBits == Translation) {
324 QMatrix4x4 inv;
325 inv.m[3][0] = -m[3][0];
326 inv.m[3][1] = -m[3][1];
327 inv.m[3][2] = -m[3][2];
328 inv.flagBits = Translation;
329 if (invertible)
330 *invertible = true;
331 return inv;
332 } else if (flagBits < Rotation2D) {
333 // Translation | Scale
334 if (m[0][0] == 0 || m[1][1] == 0 || m[2][2] == 0) {
335 if (invertible)
336 *invertible = false;
337 return QMatrix4x4();
338 }
339 QMatrix4x4 inv;
340 inv.m[0][0] = 1.0f / m[0][0];
341 inv.m[1][1] = 1.0f / m[1][1];
342 inv.m[2][2] = 1.0f / m[2][2];
343 inv.m[3][0] = -m[3][0] * inv.m[0][0];
344 inv.m[3][1] = -m[3][1] * inv.m[1][1];
345 inv.m[3][2] = -m[3][2] * inv.m[2][2];
346 inv.flagBits = flagBits;
347
348 if (invertible)
349 *invertible = true;
350 return inv;
351 } else if ((flagBits & ~(Translation | Rotation2D | Rotation)) == Identity) {
352 if (invertible)
353 *invertible = true;
354 return orthonormalInverse();
355 } else if (flagBits < Perspective) {
357
358 double mm[4][4];
359 copyToDoubles(m, mm);
360
361 double det = matrixDet3(mm, 0, 1, 2, 0, 1, 2);
362 if (det == 0.0f) {
363 if (invertible)
364 *invertible = false;
365 return QMatrix4x4();
366 }
367 det = 1.0f / det;
368
369 inv.m[0][0] = matrixDet2(mm, 1, 2, 1, 2) * det;
370 inv.m[0][1] = -matrixDet2(mm, 0, 2, 1, 2) * det;
371 inv.m[0][2] = matrixDet2(mm, 0, 1, 1, 2) * det;
372 inv.m[0][3] = 0;
373 inv.m[1][0] = -matrixDet2(mm, 1, 2, 0, 2) * det;
374 inv.m[1][1] = matrixDet2(mm, 0, 2, 0, 2) * det;
375 inv.m[1][2] = -matrixDet2(mm, 0, 1, 0, 2) * det;
376 inv.m[1][3] = 0;
377 inv.m[2][0] = matrixDet2(mm, 1, 2, 0, 1) * det;
378 inv.m[2][1] = -matrixDet2(mm, 0, 2, 0, 1) * det;
379 inv.m[2][2] = matrixDet2(mm, 0, 1, 0, 1) * det;
380 inv.m[2][3] = 0;
381 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];
382 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];
383 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];
384 inv.m[3][3] = 1;
385 inv.flagBits = flagBits;
386
387 if (invertible)
388 *invertible = true;
389 return inv;
390 }
391
393
394 double mm[4][4];
395 copyToDoubles(m, mm);
396
397 double det = matrixDet4(mm);
398 if (det == 0.0f) {
399 if (invertible)
400 *invertible = false;
401 return QMatrix4x4();
402 }
403 det = 1.0f / det;
404
405 inv.m[0][0] = matrixDet3(mm, 1, 2, 3, 1, 2, 3) * det;
406 inv.m[0][1] = -matrixDet3(mm, 0, 2, 3, 1, 2, 3) * det;
407 inv.m[0][2] = matrixDet3(mm, 0, 1, 3, 1, 2, 3) * det;
408 inv.m[0][3] = -matrixDet3(mm, 0, 1, 2, 1, 2, 3) * det;
409 inv.m[1][0] = -matrixDet3(mm, 1, 2, 3, 0, 2, 3) * det;
410 inv.m[1][1] = matrixDet3(mm, 0, 2, 3, 0, 2, 3) * det;
411 inv.m[1][2] = -matrixDet3(mm, 0, 1, 3, 0, 2, 3) * det;
412 inv.m[1][3] = matrixDet3(mm, 0, 1, 2, 0, 2, 3) * det;
413 inv.m[2][0] = matrixDet3(mm, 1, 2, 3, 0, 1, 3) * det;
414 inv.m[2][1] = -matrixDet3(mm, 0, 2, 3, 0, 1, 3) * det;
415 inv.m[2][2] = matrixDet3(mm, 0, 1, 3, 0, 1, 3) * det;
416 inv.m[2][3] = -matrixDet3(mm, 0, 1, 2, 0, 1, 3) * det;
417 inv.m[3][0] = -matrixDet3(mm, 1, 2, 3, 0, 1, 2) * det;
418 inv.m[3][1] = matrixDet3(mm, 0, 2, 3, 0, 1, 2) * det;
419 inv.m[3][2] = -matrixDet3(mm, 0, 1, 3, 0, 1, 2) * det;
420 inv.m[3][3] = matrixDet3(mm, 0, 1, 2, 0, 1, 2) * det;
421 inv.flagBits = flagBits;
422
423 if (invertible)
424 *invertible = true;
425 return inv;
426}
427
437{
438 QMatrix3x3 inv;
439
440 // Handle the simple cases first.
441 if (flagBits < Scale) {
442 // Translation
443 return inv;
444 } else if (flagBits < Rotation2D) {
445 // Translation | Scale
446 if (m[0][0] == 0.0f || m[1][1] == 0.0f || m[2][2] == 0.0f)
447 return inv;
448 inv.data()[0] = 1.0f / m[0][0];
449 inv.data()[4] = 1.0f / m[1][1];
450 inv.data()[8] = 1.0f / m[2][2];
451 return inv;
452 } else if ((flagBits & ~(Translation | Rotation2D | Rotation)) == Identity) {
453 float *invm = inv.data();
454 invm[0 + 0 * 3] = m[0][0];
455 invm[1 + 0 * 3] = m[0][1];
456 invm[2 + 0 * 3] = m[0][2];
457 invm[0 + 1 * 3] = m[1][0];
458 invm[1 + 1 * 3] = m[1][1];
459 invm[2 + 1 * 3] = m[1][2];
460 invm[0 + 2 * 3] = m[2][0];
461 invm[1 + 2 * 3] = m[2][1];
462 invm[2 + 2 * 3] = m[2][2];
463 return inv;
464 }
465
466 double mm[4][4];
467 copyToDoubles(m, mm);
468 double det = matrixDet3(mm, 0, 1, 2, 0, 1, 2);
469 if (det == 0.0f)
470 return inv;
471 det = 1.0f / det;
472
473 float *invm = inv.data();
474
475 // Invert and transpose in a single step.
476 invm[0 + 0 * 3] = (mm[1][1] * mm[2][2] - mm[2][1] * mm[1][2]) * det;
477 invm[1 + 0 * 3] = -(mm[1][0] * mm[2][2] - mm[1][2] * mm[2][0]) * det;
478 invm[2 + 0 * 3] = (mm[1][0] * mm[2][1] - mm[1][1] * mm[2][0]) * det;
479 invm[0 + 1 * 3] = -(mm[0][1] * mm[2][2] - mm[2][1] * mm[0][2]) * det;
480 invm[1 + 1 * 3] = (mm[0][0] * mm[2][2] - mm[0][2] * mm[2][0]) * det;
481 invm[2 + 1 * 3] = -(mm[0][0] * mm[2][1] - mm[0][1] * mm[2][0]) * det;
482 invm[0 + 2 * 3] = (mm[0][1] * mm[1][2] - mm[0][2] * mm[1][1]) * det;
483 invm[1 + 2 * 3] = -(mm[0][0] * mm[1][2] - mm[0][2] * mm[1][0]) * det;
484 invm[2 + 2 * 3] = (mm[0][0] * mm[1][1] - mm[1][0] * mm[0][1]) * det;
485
486 return inv;
487}
488
493{
495 for (int row = 0; row < 4; ++row) {
496 for (int col = 0; col < 4; ++col) {
497 result.m[col][row] = m[row][col];
498 }
499 }
500 // When a translation is transposed, it becomes a perspective transformation.
501 result.flagBits = (flagBits & Translation ? General : flagBits);
502 return result;
503}
504
536{
537 m[0][0] /= divisor;
538 m[0][1] /= divisor;
539 m[0][2] /= divisor;
540 m[0][3] /= divisor;
541 m[1][0] /= divisor;
542 m[1][1] /= divisor;
543 m[1][2] /= divisor;
544 m[1][3] /= divisor;
545 m[2][0] /= divisor;
546 m[2][1] /= divisor;
547 m[2][2] /= divisor;
548 m[2][3] /= divisor;
549 m[3][0] /= divisor;
550 m[3][1] /= divisor;
551 m[3][2] /= divisor;
552 m[3][3] /= divisor;
553 flagBits = General;
554 return *this;
555}
556
592#ifndef QT_NO_VECTOR3D
593
594#if QT_DEPRECATED_SINCE(6, 1)
595
617#endif
618
619#endif
620
621#ifndef QT_NO_VECTOR4D
622
639#endif
640
657#if QT_DEPRECATED_SINCE(6, 1)
658
679#endif
680
709{
711 m.m[0][0] = matrix.m[0][0] / divisor;
712 m.m[0][1] = matrix.m[0][1] / divisor;
713 m.m[0][2] = matrix.m[0][2] / divisor;
714 m.m[0][3] = matrix.m[0][3] / divisor;
715 m.m[1][0] = matrix.m[1][0] / divisor;
716 m.m[1][1] = matrix.m[1][1] / divisor;
717 m.m[1][2] = matrix.m[1][2] / divisor;
718 m.m[1][3] = matrix.m[1][3] / divisor;
719 m.m[2][0] = matrix.m[2][0] / divisor;
720 m.m[2][1] = matrix.m[2][1] / divisor;
721 m.m[2][2] = matrix.m[2][2] / divisor;
722 m.m[2][3] = matrix.m[2][3] / divisor;
723 m.m[3][0] = matrix.m[3][0] / divisor;
724 m.m[3][1] = matrix.m[3][1] / divisor;
725 m.m[3][2] = matrix.m[3][2] / divisor;
726 m.m[3][3] = matrix.m[3][3] / divisor;
727 m.flagBits = QMatrix4x4::General;
728 return m;
729}
730
738bool qFuzzyCompare(const QMatrix4x4& m1, const QMatrix4x4& m2)
739{
740 return qFuzzyCompare(m1.m[0][0], m2.m[0][0]) &&
741 qFuzzyCompare(m1.m[0][1], m2.m[0][1]) &&
742 qFuzzyCompare(m1.m[0][2], m2.m[0][2]) &&
743 qFuzzyCompare(m1.m[0][3], m2.m[0][3]) &&
744 qFuzzyCompare(m1.m[1][0], m2.m[1][0]) &&
745 qFuzzyCompare(m1.m[1][1], m2.m[1][1]) &&
746 qFuzzyCompare(m1.m[1][2], m2.m[1][2]) &&
747 qFuzzyCompare(m1.m[1][3], m2.m[1][3]) &&
748 qFuzzyCompare(m1.m[2][0], m2.m[2][0]) &&
749 qFuzzyCompare(m1.m[2][1], m2.m[2][1]) &&
750 qFuzzyCompare(m1.m[2][2], m2.m[2][2]) &&
751 qFuzzyCompare(m1.m[2][3], m2.m[2][3]) &&
752 qFuzzyCompare(m1.m[3][0], m2.m[3][0]) &&
753 qFuzzyCompare(m1.m[3][1], m2.m[3][1]) &&
754 qFuzzyCompare(m1.m[3][2], m2.m[3][2]) &&
755 qFuzzyCompare(m1.m[3][3], m2.m[3][3]);
756}
757
758
759#ifndef QT_NO_VECTOR3D
760
768{
769 float vx = vector.x();
770 float vy = vector.y();
771 float vz = vector.z();
772 if (flagBits < Scale) {
773 m[0][0] = vx;
774 m[1][1] = vy;
775 m[2][2] = vz;
776 } else if (flagBits < Rotation2D) {
777 m[0][0] *= vx;
778 m[1][1] *= vy;
779 m[2][2] *= vz;
780 } else if (flagBits < Rotation) {
781 m[0][0] *= vx;
782 m[0][1] *= vx;
783 m[1][0] *= vy;
784 m[1][1] *= vy;
785 m[2][2] *= vz;
786 } else {
787 m[0][0] *= vx;
788 m[0][1] *= vx;
789 m[0][2] *= vx;
790 m[0][3] *= vx;
791 m[1][0] *= vy;
792 m[1][1] *= vy;
793 m[1][2] *= vy;
794 m[1][3] *= vy;
795 m[2][0] *= vz;
796 m[2][1] *= vz;
797 m[2][2] *= vz;
798 m[2][3] *= vz;
799 }
800 flagBits |= Scale;
801}
802
803#endif
804
813void QMatrix4x4::scale(float x, float y)
814{
815 if (flagBits < Scale) {
816 m[0][0] = x;
817 m[1][1] = y;
818 } else if (flagBits < Rotation2D) {
819 m[0][0] *= x;
820 m[1][1] *= y;
821 } else if (flagBits < Rotation) {
822 m[0][0] *= x;
823 m[0][1] *= x;
824 m[1][0] *= y;
825 m[1][1] *= y;
826 } else {
827 m[0][0] *= x;
828 m[0][1] *= x;
829 m[0][2] *= x;
830 m[0][3] *= x;
831 m[1][0] *= y;
832 m[1][1] *= y;
833 m[1][2] *= y;
834 m[1][3] *= y;
835 }
836 flagBits |= Scale;
837}
838
847void QMatrix4x4::scale(float x, float y, float z)
848{
849 if (flagBits < Scale) {
850 m[0][0] = x;
851 m[1][1] = y;
852 m[2][2] = z;
853 } else if (flagBits < Rotation2D) {
854 m[0][0] *= x;
855 m[1][1] *= y;
856 m[2][2] *= z;
857 } else if (flagBits < Rotation) {
858 m[0][0] *= x;
859 m[0][1] *= x;
860 m[1][0] *= y;
861 m[1][1] *= y;
862 m[2][2] *= z;
863 } else {
864 m[0][0] *= x;
865 m[0][1] *= x;
866 m[0][2] *= x;
867 m[0][3] *= x;
868 m[1][0] *= y;
869 m[1][1] *= y;
870 m[1][2] *= y;
871 m[1][3] *= y;
872 m[2][0] *= z;
873 m[2][1] *= z;
874 m[2][2] *= z;
875 m[2][3] *= z;
876 }
877 flagBits |= Scale;
878}
879
888void QMatrix4x4::scale(float factor)
889{
890 if (flagBits < Scale) {
891 m[0][0] = factor;
892 m[1][1] = factor;
893 m[2][2] = factor;
894 } else if (flagBits < Rotation2D) {
895 m[0][0] *= factor;
896 m[1][1] *= factor;
897 m[2][2] *= factor;
898 } else if (flagBits < Rotation) {
899 m[0][0] *= factor;
900 m[0][1] *= factor;
901 m[1][0] *= factor;
902 m[1][1] *= factor;
903 m[2][2] *= factor;
904 } else {
905 m[0][0] *= factor;
906 m[0][1] *= factor;
907 m[0][2] *= factor;
908 m[0][3] *= factor;
909 m[1][0] *= factor;
910 m[1][1] *= factor;
911 m[1][2] *= factor;
912 m[1][3] *= factor;
913 m[2][0] *= factor;
914 m[2][1] *= factor;
915 m[2][2] *= factor;
916 m[2][3] *= factor;
917 }
918 flagBits |= Scale;
919}
920
921#ifndef QT_NO_VECTOR3D
930{
931 float vx = vector.x();
932 float vy = vector.y();
933 float vz = vector.z();
934 if (flagBits == Identity) {
935 m[3][0] = vx;
936 m[3][1] = vy;
937 m[3][2] = vz;
938 } else if (flagBits == Translation) {
939 m[3][0] += vx;
940 m[3][1] += vy;
941 m[3][2] += vz;
942 } else if (flagBits == Scale) {
943 m[3][0] = m[0][0] * vx;
944 m[3][1] = m[1][1] * vy;
945 m[3][2] = m[2][2] * vz;
946 } else if (flagBits == (Translation | Scale)) {
947 m[3][0] += m[0][0] * vx;
948 m[3][1] += m[1][1] * vy;
949 m[3][2] += m[2][2] * vz;
950 } else if (flagBits < Rotation) {
951 m[3][0] += m[0][0] * vx + m[1][0] * vy;
952 m[3][1] += m[0][1] * vx + m[1][1] * vy;
953 m[3][2] += m[2][2] * vz;
954 } else {
955 m[3][0] += m[0][0] * vx + m[1][0] * vy + m[2][0] * vz;
956 m[3][1] += m[0][1] * vx + m[1][1] * vy + m[2][1] * vz;
957 m[3][2] += m[0][2] * vx + m[1][2] * vy + m[2][2] * vz;
958 m[3][3] += m[0][3] * vx + m[1][3] * vy + m[2][3] * vz;
959 }
960 flagBits |= Translation;
961}
962#endif
963
972void QMatrix4x4::translate(float x, float y)
973{
974 if (flagBits == Identity) {
975 m[3][0] = x;
976 m[3][1] = y;
977 } else if (flagBits == Translation) {
978 m[3][0] += x;
979 m[3][1] += y;
980 } else if (flagBits == Scale) {
981 m[3][0] = m[0][0] * x;
982 m[3][1] = m[1][1] * y;
983 } else if (flagBits == (Translation | Scale)) {
984 m[3][0] += m[0][0] * x;
985 m[3][1] += m[1][1] * y;
986 } else if (flagBits < Rotation) {
987 m[3][0] += m[0][0] * x + m[1][0] * y;
988 m[3][1] += m[0][1] * x + m[1][1] * y;
989 } else {
990 m[3][0] += m[0][0] * x + m[1][0] * y;
991 m[3][1] += m[0][1] * x + m[1][1] * y;
992 m[3][2] += m[0][2] * x + m[1][2] * y;
993 m[3][3] += m[0][3] * x + m[1][3] * y;
994 }
995 flagBits |= Translation;
996}
997
1006void QMatrix4x4::translate(float x, float y, float z)
1007{
1008 if (flagBits == Identity) {
1009 m[3][0] = x;
1010 m[3][1] = y;
1011 m[3][2] = z;
1012 } else if (flagBits == Translation) {
1013 m[3][0] += x;
1014 m[3][1] += y;
1015 m[3][2] += z;
1016 } else if (flagBits == Scale) {
1017 m[3][0] = m[0][0] * x;
1018 m[3][1] = m[1][1] * y;
1019 m[3][2] = m[2][2] * z;
1020 } else if (flagBits == (Translation | Scale)) {
1021 m[3][0] += m[0][0] * x;
1022 m[3][1] += m[1][1] * y;
1023 m[3][2] += m[2][2] * z;
1024 } else if (flagBits < Rotation) {
1025 m[3][0] += m[0][0] * x + m[1][0] * y;
1026 m[3][1] += m[0][1] * x + m[1][1] * y;
1027 m[3][2] += m[2][2] * z;
1028 } else {
1029 m[3][0] += m[0][0] * x + m[1][0] * y + m[2][0] * z;
1030 m[3][1] += m[0][1] * x + m[1][1] * y + m[2][1] * z;
1031 m[3][2] += m[0][2] * x + m[1][2] * y + m[2][2] * z;
1032 m[3][3] += m[0][3] * x + m[1][3] * y + m[2][3] * z;
1033 }
1034 flagBits |= Translation;
1035}
1036
1037#ifndef QT_NO_VECTOR3D
1046{
1047 rotate(angle, vector.x(), vector.y(), vector.z());
1048}
1049
1050#endif
1051
1060void QMatrix4x4::rotate(float angle, float x, float y, float z)
1061{
1062 if (angle == 0.0f)
1063 return;
1064 float c, s;
1065 if (angle == 90.0f || angle == -270.0f) {
1066 s = 1.0f;
1067 c = 0.0f;
1068 } else if (angle == -90.0f || angle == 270.0f) {
1069 s = -1.0f;
1070 c = 0.0f;
1071 } else if (angle == 180.0f || angle == -180.0f) {
1072 s = 0.0f;
1073 c = -1.0f;
1074 } else {
1075 float a = qDegreesToRadians(angle);
1076 c = std::cos(a);
1077 s = std::sin(a);
1078 }
1079 if (x == 0.0f) {
1080 if (y == 0.0f) {
1081 if (z != 0.0f) {
1082 // Rotate around the Z axis.
1083 if (z < 0)
1084 s = -s;
1085 float tmp;
1086 m[0][0] = (tmp = m[0][0]) * c + m[1][0] * s;
1087 m[1][0] = m[1][0] * c - tmp * s;
1088 m[0][1] = (tmp = m[0][1]) * c + m[1][1] * s;
1089 m[1][1] = m[1][1] * c - tmp * s;
1090 m[0][2] = (tmp = m[0][2]) * c + m[1][2] * s;
1091 m[1][2] = m[1][2] * c - tmp * s;
1092 m[0][3] = (tmp = m[0][3]) * c + m[1][3] * s;
1093 m[1][3] = m[1][3] * c - tmp * s;
1094
1095 flagBits |= Rotation2D;
1096 return;
1097 }
1098 } else if (z == 0.0f) {
1099 // Rotate around the Y axis.
1100 if (y < 0)
1101 s = -s;
1102 float tmp;
1103 m[2][0] = (tmp = m[2][0]) * c + m[0][0] * s;
1104 m[0][0] = m[0][0] * c - tmp * s;
1105 m[2][1] = (tmp = m[2][1]) * c + m[0][1] * s;
1106 m[0][1] = m[0][1] * c - tmp * s;
1107 m[2][2] = (tmp = m[2][2]) * c + m[0][2] * s;
1108 m[0][2] = m[0][2] * c - tmp * s;
1109 m[2][3] = (tmp = m[2][3]) * c + m[0][3] * s;
1110 m[0][3] = m[0][3] * c - tmp * s;
1111
1112 flagBits |= Rotation;
1113 return;
1114 }
1115 } else if (y == 0.0f && z == 0.0f) {
1116 // Rotate around the X axis.
1117 if (x < 0)
1118 s = -s;
1119 float tmp;
1120 m[1][0] = (tmp = m[1][0]) * c + m[2][0] * s;
1121 m[2][0] = m[2][0] * c - tmp * s;
1122 m[1][1] = (tmp = m[1][1]) * c + m[2][1] * s;
1123 m[2][1] = m[2][1] * c - tmp * s;
1124 m[1][2] = (tmp = m[1][2]) * c + m[2][2] * s;
1125 m[2][2] = m[2][2] * c - tmp * s;
1126 m[1][3] = (tmp = m[1][3]) * c + m[2][3] * s;
1127 m[2][3] = m[2][3] * c - tmp * s;
1128
1129 flagBits |= Rotation;
1130 return;
1131 }
1132
1133 double len = double(x) * double(x) +
1134 double(y) * double(y) +
1135 double(z) * double(z);
1136 if (!qFuzzyCompare(len, 1.0) && !qFuzzyIsNull(len)) {
1137 len = std::sqrt(len);
1138 x = float(double(x) / len);
1139 y = float(double(y) / len);
1140 z = float(double(z) / len);
1141 }
1142 float ic = 1.0f - c;
1144 rot.m[0][0] = x * x * ic + c;
1145 rot.m[1][0] = x * y * ic - z * s;
1146 rot.m[2][0] = x * z * ic + y * s;
1147 rot.m[3][0] = 0.0f;
1148 rot.m[0][1] = y * x * ic + z * s;
1149 rot.m[1][1] = y * y * ic + c;
1150 rot.m[2][1] = y * z * ic - x * s;
1151 rot.m[3][1] = 0.0f;
1152 rot.m[0][2] = x * z * ic - y * s;
1153 rot.m[1][2] = y * z * ic + x * s;
1154 rot.m[2][2] = z * z * ic + c;
1155 rot.m[3][2] = 0.0f;
1156 rot.m[0][3] = 0.0f;
1157 rot.m[1][3] = 0.0f;
1158 rot.m[2][3] = 0.0f;
1159 rot.m[3][3] = 1.0f;
1160 rot.flagBits = Rotation;
1161 *this *= rot;
1162}
1163
1167void QMatrix4x4::projectedRotate(float angle, float x, float y, float z, float distanceToPlane)
1168{
1169 // Used by QGraphicsRotation::applyTo() to perform a rotation
1170 // and projection back to 2D in a single step.
1171 if (qIsNull(distanceToPlane))
1172 return rotate(angle, x, y, z);
1173 if (angle == 0.0f)
1174 return;
1175
1176 float c, s;
1177 if (angle == 90.0f || angle == -270.0f) {
1178 s = 1.0f;
1179 c = 0.0f;
1180 } else if (angle == -90.0f || angle == 270.0f) {
1181 s = -1.0f;
1182 c = 0.0f;
1183 } else if (angle == 180.0f || angle == -180.0f) {
1184 s = 0.0f;
1185 c = -1.0f;
1186 } else {
1187 float a = qDegreesToRadians(angle);
1188 c = std::cos(a);
1189 s = std::sin(a);
1190 }
1191
1192 const qreal d = 1.0 / distanceToPlane;
1193 if (x == 0.0f) {
1194 if (y == 0.0f) {
1195 if (z != 0.0f) {
1196 // Rotate around the Z axis.
1197 if (z < 0)
1198 s = -s;
1199 float tmp;
1200 m[0][0] = (tmp = m[0][0]) * c + m[1][0] * s;
1201 m[1][0] = m[1][0] * c - tmp * s;
1202 m[0][1] = (tmp = m[0][1]) * c + m[1][1] * s;
1203 m[1][1] = m[1][1] * c - tmp * s;
1204 m[0][2] = (tmp = m[0][2]) * c + m[1][2] * s;
1205 m[1][2] = m[1][2] * c - tmp * s;
1206 m[0][3] = (tmp = m[0][3]) * c + m[1][3] * s;
1207 m[1][3] = m[1][3] * c - tmp * s;
1208
1209 flagBits |= Rotation2D;
1210 return;
1211 }
1212 } else if (z == 0.0f) {
1213 // Rotate around the Y axis.
1214 if (y < 0)
1215 s = -s;
1216 s *= d;
1217 m[0][0] = m[0][0] * c + m[3][0] * s;
1218 m[0][1] = m[0][1] * c + m[3][1] * s;
1219 m[0][2] = m[0][2] * c + m[3][2] * s;
1220 m[0][3] = m[0][3] * c + m[3][3] * s;
1221 flagBits = General;
1222 return;
1223 }
1224 } else if (y == 0.0f && z == 0.0f) {
1225 // Rotate around the X axis.
1226 if (x < 0)
1227 s = -s;
1228 s *= d;
1229 m[1][0] = m[1][0] * c - m[3][0] * s;
1230 m[1][1] = m[1][1] * c - m[3][1] * s;
1231 m[1][2] = m[1][2] * c - m[3][2] * s;
1232 m[1][3] = m[1][3] * c - m[3][3] * s;
1233 flagBits = General;
1234 return;
1235 }
1236 double len = double(x) * double(x) +
1237 double(y) * double(y) +
1238 double(z) * double(z);
1239 if (!qFuzzyCompare(len, 1.0) && !qFuzzyIsNull(len)) {
1240 len = std::sqrt(len);
1241 x = float(double(x) / len);
1242 y = float(double(y) / len);
1243 z = float(double(z) / len);
1244 }
1245 const float ic = 1.0f - c;
1247 rot.m[0][0] = x * x * ic + c;
1248 rot.m[1][0] = x * y * ic - z * s;
1249 rot.m[2][0] = 0.0f;
1250 rot.m[3][0] = 0.0f;
1251 rot.m[0][1] = y * x * ic + z * s;
1252 rot.m[1][1] = y * y * ic + c;
1253 rot.m[2][1] = 0.0f;
1254 rot.m[3][1] = 0.0f;
1255 rot.m[0][2] = 0.0f;
1256 rot.m[1][2] = 0.0f;
1257 rot.m[2][2] = 1.0f;
1258 rot.m[3][2] = 0.0f;
1259 rot.m[0][3] = (x * z * ic - y * s) * -d;
1260 rot.m[1][3] = (y * z * ic + x * s) * -d;
1261 rot.m[2][3] = 0.0f;
1262 rot.m[3][3] = 1.0f;
1263 rot.flagBits = General;
1264 *this *= rot;
1265}
1266
1267#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
1271void QMatrix4x4::projectedRotate(float angle, float x, float y, float z)
1272{
1273 projectedRotate(angle, x, y, z, 1024.0);
1274}
1275#endif
1276
1294#ifndef QT_NO_QUATERNION
1295
1303void QMatrix4x4::rotate(const QQuaternion& quaternion)
1304{
1305 // Algorithm from:
1306 // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q54
1307
1309
1310 const float f2x = quaternion.x() + quaternion.x();
1311 const float f2y = quaternion.y() + quaternion.y();
1312 const float f2z = quaternion.z() + quaternion.z();
1313 const float f2xw = f2x * quaternion.scalar();
1314 const float f2yw = f2y * quaternion.scalar();
1315 const float f2zw = f2z * quaternion.scalar();
1316 const float f2xx = f2x * quaternion.x();
1317 const float f2xy = f2x * quaternion.y();
1318 const float f2xz = f2x * quaternion.z();
1319 const float f2yy = f2y * quaternion.y();
1320 const float f2yz = f2y * quaternion.z();
1321 const float f2zz = f2z * quaternion.z();
1322
1323 m.m[0][0] = 1.0f - (f2yy + f2zz);
1324 m.m[1][0] = f2xy - f2zw;
1325 m.m[2][0] = f2xz + f2yw;
1326 m.m[3][0] = 0.0f;
1327 m.m[0][1] = f2xy + f2zw;
1328 m.m[1][1] = 1.0f - (f2xx + f2zz);
1329 m.m[2][1] = f2yz - f2xw;
1330 m.m[3][1] = 0.0f;
1331 m.m[0][2] = f2xz - f2yw;
1332 m.m[1][2] = f2yz + f2xw;
1333 m.m[2][2] = 1.0f - (f2xx + f2yy);
1334 m.m[3][2] = 0.0f;
1335 m.m[0][3] = 0.0f;
1336 m.m[1][3] = 0.0f;
1337 m.m[2][3] = 0.0f;
1338 m.m[3][3] = 1.0f;
1339 m.flagBits = Rotation;
1340 *this *= m;
1341}
1342
1343#endif
1344
1355{
1356 // Note: rect.right() and rect.bottom() subtract 1 in QRect,
1357 // which gives the location of a pixel within the rectangle,
1358 // instead of the extent of the rectangle. We want the extent.
1359 // QRectF expresses the extent properly.
1360 ortho(rect.x(), rect.x() + rect.width(), rect.y() + rect.height(), rect.y(), -1.0f, 1.0f);
1361}
1362
1373{
1374 ortho(rect.left(), rect.right(), rect.bottom(), rect.top(), -1.0f, 1.0f);
1375}
1376
1385void QMatrix4x4::ortho(float left, float right, float bottom, float top, float nearPlane, float farPlane)
1386{
1387 // Bail out if the projection volume is zero-sized.
1388 if (left == right || bottom == top || nearPlane == farPlane)
1389 return;
1390
1391 // Construct the projection.
1392 const float width = right - left;
1393 const float invheight = top - bottom;
1394 const float clip = farPlane - nearPlane;
1396 m.m[0][0] = 2.0f / width;
1397 m.m[1][0] = 0.0f;
1398 m.m[2][0] = 0.0f;
1399 m.m[3][0] = -(left + right) / width;
1400 m.m[0][1] = 0.0f;
1401 m.m[1][1] = 2.0f / invheight;
1402 m.m[2][1] = 0.0f;
1403 m.m[3][1] = -(top + bottom) / invheight;
1404 m.m[0][2] = 0.0f;
1405 m.m[1][2] = 0.0f;
1406 m.m[2][2] = -2.0f / clip;
1407 m.m[3][2] = -(nearPlane + farPlane) / clip;
1408 m.m[0][3] = 0.0f;
1409 m.m[1][3] = 0.0f;
1410 m.m[2][3] = 0.0f;
1411 m.m[3][3] = 1.0f;
1412 m.flagBits = Translation | Scale;
1413
1414 // Apply the projection.
1415 *this *= m;
1416}
1417
1426void QMatrix4x4::frustum(float left, float right, float bottom, float top, float nearPlane, float farPlane)
1427{
1428 // Bail out if the projection volume is zero-sized.
1429 if (left == right || bottom == top || nearPlane == farPlane)
1430 return;
1431
1432 // Construct the projection.
1434 const float width = right - left;
1435 const float invheight = top - bottom;
1436 const float clip = farPlane - nearPlane;
1437 m.m[0][0] = 2.0f * nearPlane / width;
1438 m.m[1][0] = 0.0f;
1439 m.m[2][0] = (left + right) / width;
1440 m.m[3][0] = 0.0f;
1441 m.m[0][1] = 0.0f;
1442 m.m[1][1] = 2.0f * nearPlane / invheight;
1443 m.m[2][1] = (top + bottom) / invheight;
1444 m.m[3][1] = 0.0f;
1445 m.m[0][2] = 0.0f;
1446 m.m[1][2] = 0.0f;
1447 m.m[2][2] = -(nearPlane + farPlane) / clip;
1448 m.m[3][2] = -2.0f * nearPlane * farPlane / clip;
1449 m.m[0][3] = 0.0f;
1450 m.m[1][3] = 0.0f;
1451 m.m[2][3] = -1.0f;
1452 m.m[3][3] = 0.0f;
1453 m.flagBits = General;
1454
1455 // Apply the projection.
1456 *this *= m;
1457}
1458
1469void QMatrix4x4::perspective(float verticalAngle, float aspectRatio, float nearPlane, float farPlane)
1470{
1471 // Bail out if the projection volume is zero-sized.
1472 if (nearPlane == farPlane || aspectRatio == 0.0f)
1473 return;
1474
1475 // Construct the projection.
1477 const float radians = qDegreesToRadians(verticalAngle / 2.0f);
1478 const float sine = std::sin(radians);
1479 if (sine == 0.0f)
1480 return;
1481 float cotan = std::cos(radians) / sine;
1482 float clip = farPlane - nearPlane;
1483 m.m[0][0] = cotan / aspectRatio;
1484 m.m[1][0] = 0.0f;
1485 m.m[2][0] = 0.0f;
1486 m.m[3][0] = 0.0f;
1487 m.m[0][1] = 0.0f;
1488 m.m[1][1] = cotan;
1489 m.m[2][1] = 0.0f;
1490 m.m[3][1] = 0.0f;
1491 m.m[0][2] = 0.0f;
1492 m.m[1][2] = 0.0f;
1493 m.m[2][2] = -(nearPlane + farPlane) / clip;
1494 m.m[3][2] = -(2.0f * nearPlane * farPlane) / clip;
1495 m.m[0][3] = 0.0f;
1496 m.m[1][3] = 0.0f;
1497 m.m[2][3] = -1.0f;
1498 m.m[3][3] = 0.0f;
1499 m.flagBits = General;
1500
1501 // Apply the projection.
1502 *this *= m;
1503}
1504
1505#ifndef QT_NO_VECTOR3D
1506
1516void QMatrix4x4::lookAt(const QVector3D& eye, const QVector3D& center, const QVector3D& up)
1517{
1518 QVector3D forward = center - eye;
1519 if (qFuzzyIsNull(forward.x()) && qFuzzyIsNull(forward.y()) && qFuzzyIsNull(forward.z()))
1520 return;
1521
1522 forward.normalize();
1523 QVector3D side = QVector3D::crossProduct(forward, up).normalized();
1524 QVector3D upVector = QVector3D::crossProduct(side, forward);
1525
1527 m.m[0][0] = side.x();
1528 m.m[1][0] = side.y();
1529 m.m[2][0] = side.z();
1530 m.m[3][0] = 0.0f;
1531 m.m[0][1] = upVector.x();
1532 m.m[1][1] = upVector.y();
1533 m.m[2][1] = upVector.z();
1534 m.m[3][1] = 0.0f;
1535 m.m[0][2] = -forward.x();
1536 m.m[1][2] = -forward.y();
1537 m.m[2][2] = -forward.z();
1538 m.m[3][2] = 0.0f;
1539 m.m[0][3] = 0.0f;
1540 m.m[1][3] = 0.0f;
1541 m.m[2][3] = 0.0f;
1542 m.m[3][3] = 1.0f;
1543 m.flagBits = Rotation;
1544
1545 *this *= m;
1546 translate(-eye);
1547}
1548
1549#endif
1550
1570void QMatrix4x4::viewport(float left, float bottom, float width, float height, float nearPlane, float farPlane)
1571{
1572 const float w2 = width / 2.0f;
1573 const float h2 = height / 2.0f;
1574
1576 m.m[0][0] = w2;
1577 m.m[1][0] = 0.0f;
1578 m.m[2][0] = 0.0f;
1579 m.m[3][0] = left + w2;
1580 m.m[0][1] = 0.0f;
1581 m.m[1][1] = h2;
1582 m.m[2][1] = 0.0f;
1583 m.m[3][1] = bottom + h2;
1584 m.m[0][2] = 0.0f;
1585 m.m[1][2] = 0.0f;
1586 m.m[2][2] = (farPlane - nearPlane) / 2.0f;
1587 m.m[3][2] = (nearPlane + farPlane) / 2.0f;
1588 m.m[0][3] = 0.0f;
1589 m.m[1][3] = 0.0f;
1590 m.m[2][3] = 0.0f;
1591 m.m[3][3] = 1.0f;
1592 m.flagBits = General;
1593
1594 *this *= m;
1595}
1596
1608{
1609 // Multiplying the y and z coordinates with -1 does NOT flip between right-handed and
1610 // left-handed coordinate systems, it just rotates 180 degrees around the x axis, so
1611 // I'm deprecating this function.
1612 if (flagBits < Rotation2D) {
1613 // Translation | Scale
1614 m[1][1] = -m[1][1];
1615 m[2][2] = -m[2][2];
1616 } else {
1617 m[1][0] = -m[1][0];
1618 m[1][1] = -m[1][1];
1619 m[1][2] = -m[1][2];
1620 m[1][3] = -m[1][3];
1621 m[2][0] = -m[2][0];
1622 m[2][1] = -m[2][1];
1623 m[2][2] = -m[2][2];
1624 m[2][3] = -m[2][3];
1625 }
1626 flagBits |= Scale;
1627}
1628
1634{
1635 for (int row = 0; row < 4; ++row)
1636 for (int col = 0; col < 4; ++col)
1637 values[row * 4 + col] = float(m[col][row]);
1638}
1639
1650{
1651 return QTransform(m[0][0], m[0][1], m[0][3],
1652 m[1][0], m[1][1], m[1][3],
1653 m[3][0], m[3][1], m[3][3]);
1654}
1655
1671QTransform QMatrix4x4::toTransform(float distanceToPlane) const
1672{
1673 if (distanceToPlane == 1024.0f) {
1674 // Optimize the common case with constants.
1675 return QTransform(m[0][0], m[0][1], m[0][3] - m[0][2] * inv_dist_to_plane,
1676 m[1][0], m[1][1], m[1][3] - m[1][2] * inv_dist_to_plane,
1677 m[3][0], m[3][1], m[3][3] - m[3][2] * inv_dist_to_plane);
1678 } else if (distanceToPlane != 0.0f) {
1679 // The following projection matrix is pre-multiplied with "matrix":
1680 // | 1 0 0 0 |
1681 // | 0 1 0 0 |
1682 // | 0 0 1 0 |
1683 // | 0 0 d 1 |
1684 // where d = -1 / distanceToPlane. After projection, row 3 and
1685 // column 3 are dropped to form the final QTransform.
1686 float d = 1.0f / distanceToPlane;
1687 return QTransform(m[0][0], m[0][1], m[0][3] - m[0][2] * d,
1688 m[1][0], m[1][1], m[1][3] - m[1][2] * d,
1689 m[3][0], m[3][1], m[3][3] - m[3][2] * d);
1690 } else {
1691 // Orthographic projection: drop row 3 and column 3.
1692 return QTransform(m[0][0], m[0][1], m[0][3],
1693 m[1][0], m[1][1], m[1][3],
1694 m[3][0], m[3][1], m[3][3]);
1695 }
1696}
1697
1716#ifndef QT_NO_VECTOR3D
1717
1741#endif
1742
1743#ifndef QT_NO_VECTOR4D
1744
1754#endif
1755
1765{
1766 if (flagBits < Scale) {
1767 // Translation
1768 return QRect(qRound(rect.x() + m[3][0]),
1769 qRound(rect.y() + m[3][1]),
1770 rect.width(), rect.height());
1771 } else if (flagBits < Rotation2D) {
1772 // Translation | Scale
1773 float x = rect.x() * m[0][0] + m[3][0];
1774 float y = rect.y() * m[1][1] + m[3][1];
1775 float w = rect.width() * m[0][0];
1776 float h = rect.height() * m[1][1];
1777 if (w < 0) {
1778 w = -w;
1779 x -= w;
1780 }
1781 if (h < 0) {
1782 h = -h;
1783 y -= h;
1784 }
1785 return QRect(qRound(x), qRound(y), qRound(w), qRound(h));
1786 }
1787
1788 QPoint tl = map(rect.topLeft());
1789 QPoint tr = map(QPoint(rect.x() + rect.width(), rect.y()));
1790 QPoint bl = map(QPoint(rect.x(), rect.y() + rect.height()));
1791 QPoint br = map(QPoint(rect.x() + rect.width(),
1792 rect.y() + rect.height()));
1793
1794 int xmin = qMin(qMin(tl.x(), tr.x()), qMin(bl.x(), br.x()));
1795 int xmax = qMax(qMax(tl.x(), tr.x()), qMax(bl.x(), br.x()));
1796 int ymin = qMin(qMin(tl.y(), tr.y()), qMin(bl.y(), br.y()));
1797 int ymax = qMax(qMax(tl.y(), tr.y()), qMax(bl.y(), br.y()));
1798
1799 return QRect(xmin, ymin, xmax - xmin, ymax - ymin);
1800}
1801
1811{
1812 if (flagBits < Scale) {
1813 // Translation
1814 return rect.translated(m[3][0], m[3][1]);
1815 } else if (flagBits < Rotation2D) {
1816 // Translation | Scale
1817 float x = rect.x() * m[0][0] + m[3][0];
1818 float y = rect.y() * m[1][1] + m[3][1];
1819 float w = rect.width() * m[0][0];
1820 float h = rect.height() * m[1][1];
1821 if (w < 0) {
1822 w = -w;
1823 x -= w;
1824 }
1825 if (h < 0) {
1826 h = -h;
1827 y -= h;
1828 }
1829 return QRectF(x, y, w, h);
1830 }
1831
1832 QPointF tl = map(rect.topLeft()); QPointF tr = map(rect.topRight());
1833 QPointF bl = map(rect.bottomLeft()); QPointF br = map(rect.bottomRight());
1834
1835 float xmin = qMin(qMin(tl.x(), tr.x()), qMin(bl.x(), br.x()));
1836 float xmax = qMax(qMax(tl.x(), tr.x()), qMax(bl.x(), br.x()));
1837 float ymin = qMin(qMin(tl.y(), tr.y()), qMin(bl.y(), br.y()));
1838 float ymax = qMax(qMax(tl.y(), tr.y()), qMax(bl.y(), br.y()));
1839
1840 return QRectF(QPointF(xmin, ymin), QPointF(xmax, ymax));
1841}
1842
1869// Helper routine for inverting orthonormal matrices that consist
1870// of just rotations and translations.
1871QMatrix4x4 QMatrix4x4::orthonormalInverse() const
1872{
1874
1875 result.m[0][0] = m[0][0];
1876 result.m[1][0] = m[0][1];
1877 result.m[2][0] = m[0][2];
1878
1879 result.m[0][1] = m[1][0];
1880 result.m[1][1] = m[1][1];
1881 result.m[2][1] = m[1][2];
1882
1883 result.m[0][2] = m[2][0];
1884 result.m[1][2] = m[2][1];
1885 result.m[2][2] = m[2][2];
1886
1887 result.m[0][3] = 0.0f;
1888 result.m[1][3] = 0.0f;
1889 result.m[2][3] = 0.0f;
1890
1891 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]);
1892 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]);
1893 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]);
1894 result.m[3][3] = 1.0f;
1895
1896 result.flagBits = flagBits;
1897
1898 return result;
1899}
1900
1922{
1923 // If the last row is not (0, 0, 0, 1), the matrix is not a special type.
1924 flagBits = General;
1925 if (m[0][3] != 0 || m[1][3] != 0 || m[2][3] != 0 || m[3][3] != 1)
1926 return;
1927
1928 flagBits &= ~Perspective;
1929
1930 // If the last column is (0, 0, 0, 1), then there is no translation.
1931 if (m[3][0] == 0 && m[3][1] == 0 && m[3][2] == 0)
1932 flagBits &= ~Translation;
1933
1934 // If the two first elements of row 3 and column 3 are 0, then any rotation must be about Z.
1935 if (!m[0][2] && !m[1][2] && !m[2][0] && !m[2][1]) {
1936 flagBits &= ~Rotation;
1937 // If the six non-diagonal elements in the top left 3x3 matrix are 0, there is no rotation.
1938 if (!m[0][1] && !m[1][0]) {
1939 flagBits &= ~Rotation2D;
1940 // Check for identity.
1941 if (m[0][0] == 1 && m[1][1] == 1 && m[2][2] == 1)
1942 flagBits &= ~Scale;
1943 } else {
1944 // If the columns are orthonormal and form a right-handed system, then there is no scale.
1945 double mm[4][4];
1946 copyToDoubles(m, mm);
1947 double det = matrixDet2(mm, 0, 1, 0, 1);
1948 double lenX = mm[0][0] * mm[0][0] + mm[0][1] * mm[0][1];
1949 double lenY = mm[1][0] * mm[1][0] + mm[1][1] * mm[1][1];
1950 double lenZ = mm[2][2];
1951 if (qFuzzyCompare(det, 1.0) && qFuzzyCompare(lenX, 1.0)
1952 && qFuzzyCompare(lenY, 1.0) && qFuzzyCompare(lenZ, 1.0))
1953 {
1954 flagBits &= ~Scale;
1955 }
1956 }
1957 } else {
1958 // If the columns are orthonormal and form a right-handed system, then there is no scale.
1959 double mm[4][4];
1960 copyToDoubles(m, mm);
1961 double det = matrixDet3(mm, 0, 1, 2, 0, 1, 2);
1962 double lenX = mm[0][0] * mm[0][0] + mm[0][1] * mm[0][1] + mm[0][2] * mm[0][2];
1963 double lenY = mm[1][0] * mm[1][0] + mm[1][1] * mm[1][1] + mm[1][2] * mm[1][2];
1964 double lenZ = mm[2][0] * mm[2][0] + mm[2][1] * mm[2][1] + mm[2][2] * mm[2][2];
1965 if (qFuzzyCompare(det, 1.0) && qFuzzyCompare(lenX, 1.0)
1966 && qFuzzyCompare(lenY, 1.0) && qFuzzyCompare(lenZ, 1.0))
1967 {
1968 flagBits &= ~Scale;
1969 }
1970 }
1971}
1972
1976QMatrix4x4::operator QVariant() const
1977{
1978 return QVariant::fromValue(*this);
1979}
1980
1981#ifndef QT_NO_DEBUG_STREAM
1982
1984{
1985 QDebugStateSaver saver(dbg);
1986 // Create a string that represents the matrix type.
1988 if (m.flagBits == QMatrix4x4::Identity) {
1989 bits = "Identity";
1990 } else if (m.flagBits == QMatrix4x4::General) {
1991 bits = "General";
1992 } else {
1993 if ((m.flagBits & QMatrix4x4::Translation) != 0)
1994 bits += "Translation,";
1995 if ((m.flagBits & QMatrix4x4::Scale) != 0)
1996 bits += "Scale,";
1997 if ((m.flagBits & QMatrix4x4::Rotation2D) != 0)
1998 bits += "Rotation2D,";
1999 if ((m.flagBits & QMatrix4x4::Rotation) != 0)
2000 bits += "Rotation,";
2001 if ((m.flagBits & QMatrix4x4::Perspective) != 0)
2002 bits += "Perspective,";
2003 bits.chop(1);
2004 }
2005
2006 // Output in row-major order because it is more human-readable.
2007 dbg.nospace() << "QMatrix4x4(type:" << bits.constData() << Qt::endl
2008 << qSetFieldWidth(10)
2009 << m(0, 0) << m(0, 1) << m(0, 2) << m(0, 3) << Qt::endl
2010 << m(1, 0) << m(1, 1) << m(1, 2) << m(1, 3) << Qt::endl
2011 << m(2, 0) << m(2, 1) << m(2, 2) << m(2, 3) << Qt::endl
2012 << m(3, 0) << m(3, 1) << m(3, 2) << m(3, 3) << Qt::endl
2013 << qSetFieldWidth(0) << ')';
2014 return dbg;
2015}
2016
2017#endif
2018
2019#ifndef QT_NO_DATASTREAM
2020
2032{
2033 for (int row = 0; row < 4; ++row)
2034 for (int col = 0; col < 4; ++col)
2035 stream << matrix(row, col);
2036 return stream;
2037}
2038
2050{
2051 float x;
2052 for (int row = 0; row < 4; ++row) {
2053 for (int col = 0; col < 4; ++col) {
2054 stream >> x;
2055 matrix(row, col) = x;
2056 }
2057 }
2058 matrix.optimize();
2059 return stream;
2060}
2061
2062#endif // QT_NO_DATASTREAM
2063
2064#endif // QT_NO_MATRIX4X4
2065
\inmodule QtCore
Definition qbytearray.h:57
void chop(qsizetype n)
Removes n bytes from the end of the byte array.
\inmodule QtCore\reentrant
Definition qdatastream.h:46
\inmodule QtCore
\inmodule QtCore
T * data()
Returns a pointer to the raw data of this matrix.
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
void flipCoordinates()
QMatrix3x3 normalMatrix() const
Returns the normal matrix corresponding to this 4x4 transformation.
void frustum(float left, float right, float bottom, float top, float nearPlane, float farPlane)
Multiplies this matrix by another that applies a perspective frustum projection for a window with low...
void lookAt(const QVector3D &eye, const QVector3D &center, const QVector3D &up)
Multiplies this matrix by a viewing matrix derived from an eye point.
void copyDataTo(float *values) const
Retrieves the 16 items in this matrix and copies them to values in row-major order.
QMatrix4x4()
Constructs an identity matrix.
Definition qmatrix4x4.h:27
void rotate(float angle, const QVector3D &vector)
Multiples this matrix by another that rotates coordinates through angle degrees about vector.
void scale(const QVector3D &vector)
Multiplies this matrix by another that scales coordinates by the components of vector.
void projectedRotate(float angle, float x, float y, float z, float distanceToPlane=1024.0)
QMatrix4x4 & operator/=(float divisor)
This is an overloaded member function, provided for convenience. It differs from the above function o...
QMatrix4x4 transposed() const
Returns this matrix, transposed about its diagonal.
void ortho(const QRect &rect)
This is an overloaded member function, provided for convenience. It differs from the above function o...
QTransform toTransform() const
Returns the conventional Qt 2D transformation matrix that corresponds to this matrix.
friend Q_GUI_EXPORT bool qFuzzyCompare(const QMatrix4x4 &m1, const QMatrix4x4 &m2)
Returns true if m1 and m2 are equal, allowing for a small fuzziness factor for floating-point compari...
void perspective(float verticalAngle, float aspectRatio, float nearPlane, float farPlane)
Multiplies this matrix by another that applies a perspective projection.
QRect mapRect(const QRect &rect) const
Maps rect by multiplying this matrix by the corners of rect and then forming a new rectangle from the...
QMatrix4x4 inverted(bool *invertible=nullptr) const
Returns the inverse of this matrix.
double determinant() const
Returns the determinant of this matrix.
void viewport(const QRectF &rect)
This is an overloaded member function, provided for convenience. It differs from the above function o...
void optimize()
Optimize the usage of this matrix from its current elements.
void translate(const QVector3D &vector)
Multiplies this matrix by another that translates coordinates by the components of vector.
\inmodule QtCore\reentrant
Definition qpoint.h:217
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:343
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:348
\inmodule QtCore\reentrant
Definition qpoint.h:25
constexpr int x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:130
constexpr int y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:135
The QQuaternion class represents a quaternion consisting of a vector and scalar.
\inmodule QtCore\reentrant
Definition qrect.h:484
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr QRect translated(int dx, int dy) const noexcept
Returns a copy of the rectangle that is translated dx along the x axis and dy along the y axis,...
Definition qrect.h:261
The QTransform class specifies 2D transformations of a coordinate system.
Definition qtransform.h:20
\inmodule QtCore
Definition qvariant.h:65
static auto fromValue(T &&value) noexcept(std::is_nothrow_copy_constructible_v< T > &&Private::CanUseInternalSpace< T >) -> std::enable_if_t< std::conjunction_v< std::is_copy_constructible< T >, std::is_destructible< T > >, QVariant >
Definition qvariant.h:536
The QVector3D class represents a vector or vertex in 3D space.
Definition qvectornd.h:171
constexpr float y() const noexcept
Returns the y coordinate of this point.
Definition qvectornd.h:671
constexpr float x() const noexcept
Returns the x coordinate of this point.
Definition qvectornd.h:670
static constexpr QVector3D crossProduct(QVector3D v1, QVector3D v2) noexcept
Returns the cross-product of vectors v1 and v2, which is normal to the plane spanned by v1 and v2.
Definition qvectornd.h:775
void normalize() noexcept
Normalizes the current vector in place.
Definition qvectornd.h:702
constexpr float z() const noexcept
Returns the z coordinate of this point.
Definition qvectornd.h:672
QMap< QString, QString > map
[6]
rect
[4]
Combined button and popup list for selecting options.
constexpr Initialization Uninitialized
QTextStream & endl(QTextStream &stream)
Writes '\n' to the stream and flushes the stream.
EGLStreamKHR stream
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:349
bool qIsNull(qfloat16 f) noexcept
Definition qfloat16.h:354
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:327
constexpr QMargins operator/(const QMargins &margins, int divisor)
Definition qmargins.h:185
constexpr float qDegreesToRadians(float degrees)
Definition qmath.h:260
static double matrixDet4(const double m[4][4])
bool qFuzzyCompare(const QMatrix4x4 &m1, const QMatrix4x4 &m2)
static void copyToDoubles(const float m[4][4], double mm[4][4])
static const float inv_dist_to_plane
QDataStream & operator>>(QDataStream &stream, QMatrix4x4 &matrix)
QDebug operator<<(QDebug dbg, const QMatrix4x4 &m)
static double matrixDet2(const double m[4][4], int col0, int col1, int row0, int row1)
static double matrixDet3(const double m[4][4], int col0, int col1, int col2, int row0, int row1, int row2)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLenum GLsizei GLsizei GLint * values
[15]
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat z
GLint GLint GLint GLint GLint x
[0]
const GLfloat * m
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint divisor
GLdouble GLdouble GLdouble GLdouble top
GLdouble GLdouble right
GLint GLsizei width
GLint left
GLint GLint bottom
GLfloat angle
GLint y
GLfloat GLfloat GLfloat GLfloat h
GLuint GLenum GLenum transform
GLdouble s
[6]
Definition qopenglext.h:235
GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint GLdouble GLdouble w2
const GLubyte * c
GLuint GLenum matrix
GLenum GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const void * bits
GLenum GLenum GLsizei void * row
GLuint64EXT * result
[6]
GLenum GLsizei len
#define tr(X)
QTextStreamManipulator qSetFieldWidth(int width)
double qreal
Definition qtypes.h:187
QList< int > vector
[14]