Atoms Crowd  7.0.0
ImathMatrixAlgo.h
1 //
2 // SPDX-License-Identifier: BSD-3-Clause
3 // Copyright Contributors to the OpenEXR Project.
4 //
5 
6 //
7 //
8 // Functions operating on Matrix22, Matrix33, and Matrix44 types
9 //
10 // This file also defines a few predefined constant matrices.
11 //
12 
13 #ifndef INCLUDED_ATOMSMATHMATRIXALGO_H
14 #define INCLUDED_ATOMSMATHMATRIXALGO_H
15 
16 #include <AtomsMath/ImathEuler.h>
17 #include <AtomsMath/ImathExport.h>
18 #include <AtomsMath/ImathMatrix.h>
19 #include <AtomsMath/ImathNamespace.h>
20 #include <AtomsMath/ImathQuat.h>
21 #include <AtomsMath/ImathVec.h>
22 #include <math.h>
23 
24 ATOMSMATH_INTERNAL_NAMESPACE_HEADER_ENTER
25 
26 //------------------
27 // Identity matrices
28 //------------------
29 
31 ATOMSMATH_EXPORT_CONST M22f identity22f;
33 ATOMSMATH_EXPORT_CONST M33f identity33f;
35 ATOMSMATH_EXPORT_CONST M44f identity44f;
37 ATOMSMATH_EXPORT_CONST M22d identity22d;
39 ATOMSMATH_EXPORT_CONST M33d identity33d;
41 ATOMSMATH_EXPORT_CONST M44d identity44d;
42 
43 //----------------------------------------------------------------------
44 // Extract scale, shear, rotation, and translation values from a matrix:
45 //
46 // Notes:
47 //
48 // This implementation follows the technique described in the paper by
49 // Spencer W. Thomas in the Graphics Gems II article: "Decomposing a
50 // Matrix into Simple Transformations", p. 320.
51 //
52 // - Some of the functions below have an optional exc parameter
53 // that determines the functions' behavior when the matrix'
54 // scaling is very close to zero:
55 //
56 // If exc is true, the functions throw a std::domain_error exception.
57 //
58 // If exc is false:
59 //
60 // extractScaling (m, s) returns false, s is invalid
61 // sansScaling (m) returns m
62 // removeScaling (m) returns false, m is unchanged
63 // sansScalingAndShear (m) returns m
64 // removeScalingAndShear (m) returns false, m is unchanged
65 // extractAndRemoveScalingAndShear (m, s, h)
66 // returns false, m is unchanged,
67 // (sh) are invalid
68 // checkForZeroScaleInRow () returns false
69 // extractSHRT (m, s, h, r, t) returns false, (shrt) are invalid
70 //
71 // - Functions extractEuler(), extractEulerXYZ() and extractEulerZYX()
72 // assume that the matrix does not include shear or non-uniform scaling,
73 // but they do not examine the matrix to verify this assumption.
74 // Matrices with shear or non-uniform scaling are likely to produce
75 // meaningless results. Therefore, you should use the
76 // removeScalingAndShear() routine, if necessary, prior to calling
77 // extractEuler...() .
78 //
79 // - All functions assume that the matrix does not include perspective
80 // transformation(s), but they do not examine the matrix to verify
81 // this assumption. Matrices with perspective transformations are
82 // likely to produce meaningless results.
83 //
84 //----------------------------------------------------------------------
85 
86 //
87 // Declarations for 4x4 matrix.
88 //
89 
96 template <class T> bool extractScaling (const Matrix44<T>& mat, Vec3<T>& scl, bool exc = true);
97 
102 template <class T> Matrix44<T> sansScaling (const Matrix44<T>& mat, bool exc = true);
103 
107 //
111 template <class T> bool removeScaling (Matrix44<T>& mat, bool exc = true);
112 
122 template <class T> bool extractScalingAndShear (const Matrix44<T>& mat, Vec3<T>& scl, Vec3<T>& shr, bool exc = true);
123 
128 template <class T> Matrix44<T> sansScalingAndShear (const Matrix44<T>& mat, bool exc = true);
129 
135 template <class T>
136 void sansScalingAndShear (Matrix44<T>& result, const Matrix44<T>& mat, bool exc = true);
137 
139 //
143 template <class T> bool removeScalingAndShear (Matrix44<T>& mat, bool exc = true);
144 
147 //
153 template <class T>
154 bool
155 extractAndRemoveScalingAndShear (Matrix44<T>& mat, Vec3<T>& scl, Vec3<T>& shr, bool exc = true);
156 
162 template <class T> void extractEulerXYZ (const Matrix44<T>& mat, Vec3<T>& rot);
163 
169 template <class T> void extractEulerZYX (const Matrix44<T>& mat, Vec3<T>& rot);
170 
175 template <class T> Quat<T> extractQuat (const Matrix44<T>& mat);
176 
190 template <class T>
191 bool extractSHRT (const Matrix44<T>& mat,
192  Vec3<T>& s,
193  Vec3<T>& h,
194  Vec3<T>& r,
195  Vec3<T>& t,
196  bool exc /*= true*/,
197  typename Euler<T>::Order rOrder);
198 
209 template <class T>
210 bool extractSHRT (const Matrix44<T>& mat,
211  Vec3<T>& s,
212  Vec3<T>& h,
213  Vec3<T>& r,
214  Vec3<T>& t,
215  bool exc = true);
216 
227 template <class T>
228 bool extractSHRT (const Matrix44<T>& mat,
229  Vec3<T>& s,
230  Vec3<T>& h,
231  Euler<T>& r,
232  Vec3<T>& t,
233  bool exc = true);
234 
238 template <class T> bool checkForZeroScaleInRow (const T& scl, const Vec3<T>& row, bool exc = true);
239 
241 template <class T> Matrix44<T> outerProduct (const Vec4<T>& a, const Vec4<T>& b);
242 
246 template <class T>
247 Matrix44<T> rotationMatrix (const Vec3<T>& fromDirection, const Vec3<T>& toDirection);
248 
254 template <class T>
256 rotationMatrixWithUpDir (const Vec3<T>& fromDir, const Vec3<T>& toDir, const Vec3<T>& upDir);
257 
271 template <class T>
272 void alignZAxisWithTargetDir (Matrix44<T>& result, Vec3<T> targetDir, Vec3<T> upDir);
273 
283 template <class T>
284 Matrix44<T> computeLocalFrame (const Vec3<T>& p, const Vec3<T>& xDir, const Vec3<T>& normal);
285 
295 template <class T>
296 Matrix44<T> addOffset (const Matrix44<T>& inMat,
297  const Vec3<T>& tOffset,
298  const Vec3<T>& rOffset,
299  const Vec3<T>& sOffset,
300  const Vec3<T>& ref);
301 
310 template <class T>
312 computeRSMatrix (bool keepRotateA, bool keepScaleA, const Matrix44<T>& A, const Matrix44<T>& B);
313 
314 //
315 // Declarations for 3x3 matrix.
316 //
317 
324 template <class T> bool extractScaling (const Matrix33<T>& mat, Vec2<T>& scl, bool exc = true);
325 
330 template <class T> Matrix33<T> sansScaling (const Matrix33<T>& mat, bool exc = true);
331 
335 //
339 template <class T> bool removeScaling (Matrix33<T>& mat, bool exc = true);
340 
350 template <class T>
351 bool extractScalingAndShear (const Matrix33<T>& mat, Vec2<T>& scl, T& shr, bool exc = true);
352 
357 template <class T> Matrix33<T> sansScalingAndShear (const Matrix33<T>& mat, bool exc = true);
358 
359 
361 //
365 template <class T> bool removeScalingAndShear (Matrix33<T>& mat, bool exc = true);
366 
369 //
375 template <class T>
376 bool extractAndRemoveScalingAndShear (Matrix33<T>& mat, Vec2<T>& scl, T& shr, bool exc = true);
377 
382 template <class T> void extractEuler (const Matrix22<T>& mat, T& rot);
383 
388 template <class T> void extractEuler (const Matrix33<T>& mat, T& rot);
389 
402 template <class T>
403 bool extractSHRT (const Matrix33<T>& mat, Vec2<T>& s, T& h, T& r, Vec2<T>& t, bool exc = true);
404 
408 template <class T> bool checkForZeroScaleInRow (const T& scl, const Vec2<T>& row, bool exc = true);
409 
411 template <class T> Matrix33<T> outerProduct (const Vec3<T>& a, const Vec3<T>& b);
412 
413 //------------------------------
414 // Implementation for 4x4 Matrix
415 //------------------------------
416 
417 template <class T>
418 bool
419 extractScaling (const Matrix44<T>& mat, Vec3<T>& scl, bool exc)
420 {
421  Vec3<T> shr;
422  Matrix44<T> M (mat);
423 
424  if (!extractAndRemoveScalingAndShear (M, scl, shr, exc))
425  return false;
426 
427  return true;
428 }
429 
430 template <class T>
432 sansScaling (const Matrix44<T>& mat, bool exc)
433 {
434  Vec3<T> scl;
435  Vec3<T> shr;
436  Vec3<T> rot;
437  Vec3<T> tran;
438 
439  if (!extractSHRT (mat, scl, shr, rot, tran, exc))
440  return mat;
441 
442  Matrix44<T> M;
443 
444  M.translate (tran);
445  M.rotate (rot);
446  M.shear (shr);
447 
448  return M;
449 }
450 
451 template <class T>
452 bool
453 removeScaling (Matrix44<T>& mat, bool exc)
454 {
455  Vec3<T> scl;
456  Vec3<T> shr;
457  Vec3<T> rot;
458  Vec3<T> tran;
459 
460  if (!extractSHRT (mat, scl, shr, rot, tran, exc))
461  return false;
462 
463  mat.makeIdentity();
464  mat.translate (tran);
465  mat.rotate (rot);
466  mat.shear (shr);
467 
468  return true;
469 }
470 
471 template <class T>
472 bool
473 extractScalingAndShear (const Matrix44<T>& mat, Vec3<T>& scl, Vec3<T>& shr, bool exc)
474 {
475  Matrix44<T> M (mat);
476 
477  if (!extractAndRemoveScalingAndShear (M, scl, shr, exc))
478  return false;
479 
480  return true;
481 }
482 
483 template <class T>
485 sansScalingAndShear (const Matrix44<T>& mat, bool exc)
486 {
487  Vec3<T> scl;
488  Vec3<T> shr;
489  Matrix44<T> M (mat);
490 
491  if (!extractAndRemoveScalingAndShear (M, scl, shr, exc))
492  return mat;
493 
494  return M;
495 }
496 
497 template <class T>
498 void
499 sansScalingAndShear (Matrix44<T>& result, const Matrix44<T>& mat, bool exc)
500 {
501  Vec3<T> scl;
502  Vec3<T> shr;
503 
504  if (!extractAndRemoveScalingAndShear (result, scl, shr, exc))
505  result = mat;
506 }
507 
508 template <class T>
509 bool
510 removeScalingAndShear (Matrix44<T>& mat, bool exc)
511 {
512  Vec3<T> scl;
513  Vec3<T> shr;
514 
515  if (!extractAndRemoveScalingAndShear (mat, scl, shr, exc))
516  return false;
517 
518  return true;
519 }
520 
521 template <class T>
522 bool
523 extractAndRemoveScalingAndShear (Matrix44<T>& mat, Vec3<T>& scl, Vec3<T>& shr, bool exc)
524 {
525  //
526  // This implementation follows the technique described in the paper by
527  // Spencer W. Thomas in the Graphics Gems II article: "Decomposing a
528  // Matrix into Simple Transformations", p. 320.
529  //
530 
531  Vec3<T> row[3];
532 
533  row[0] = Vec3<T> (mat[0][0], mat[0][1], mat[0][2]);
534  row[1] = Vec3<T> (mat[1][0], mat[1][1], mat[1][2]);
535  row[2] = Vec3<T> (mat[2][0], mat[2][1], mat[2][2]);
536 
537  T maxVal = 0;
538  for (int i = 0; i < 3; i++)
539  for (int j = 0; j < 3; j++)
540  if (ATOMSMATH_INTERNAL_NAMESPACE::abs (row[i][j]) > maxVal)
541  maxVal = ATOMSMATH_INTERNAL_NAMESPACE::abs (row[i][j]);
542 
543  //
544  // We normalize the 3x3 matrix here.
545  // It was noticed that this can improve numerical stability significantly,
546  // especially when many of the upper 3x3 matrix's coefficients are very
547  // close to zero; we correct for this step at the end by multiplying the
548  // scaling factors by maxVal at the end (shear and rotation are not
549  // affected by the normalization).
550 
551  if (maxVal != 0)
552  {
553  for (int i = 0; i < 3; i++)
554  if (!checkForZeroScaleInRow (maxVal, row[i], exc))
555  return false;
556  else
557  row[i] /= maxVal;
558  }
559 
560  // Compute X scale factor.
561  scl.x = row[0].length();
562  if (!checkForZeroScaleInRow (scl.x, row[0], exc))
563  return false;
564 
565  // Normalize first row.
566  row[0] /= scl.x;
567 
568  // An XY shear factor will shear the X coord. as the Y coord. changes.
569  // There are 6 combinations (XY, XZ, YZ, YX, ZX, ZY), although we only
570  // extract the first 3 because we can effect the last 3 by shearing in
571  // XY, XZ, YZ combined rotations and scales.
572  //
573  // shear matrix < 1, YX, ZX, 0,
574  // XY, 1, ZY, 0,
575  // XZ, YZ, 1, 0,
576  // 0, 0, 0, 1 >
577 
578  // Compute XY shear factor and make 2nd row orthogonal to 1st.
579  shr[0] = row[0].dot (row[1]);
580  row[1] -= shr[0] * row[0];
581 
582  // Now, compute Y scale.
583  scl.y = row[1].length();
584  if (!checkForZeroScaleInRow (scl.y, row[1], exc))
585  return false;
586 
587  // Normalize 2nd row and correct the XY shear factor for Y scaling.
588  row[1] /= scl.y;
589  shr[0] /= scl.y;
590 
591  // Compute XZ and YZ shears, orthogonalize 3rd row.
592  shr[1] = row[0].dot (row[2]);
593  row[2] -= shr[1] * row[0];
594  shr[2] = row[1].dot (row[2]);
595  row[2] -= shr[2] * row[1];
596 
597  // Next, get Z scale.
598  scl.z = row[2].length();
599  if (!checkForZeroScaleInRow (scl.z, row[2], exc))
600  return false;
601 
602  // Normalize 3rd row and correct the XZ and YZ shear factors for Z scaling.
603  row[2] /= scl.z;
604  shr[1] /= scl.z;
605  shr[2] /= scl.z;
606 
607  // At this point, the upper 3x3 matrix in mat is orthonormal.
608  // Check for a coordinate system flip. If the determinant
609  // is less than zero, then negate the matrix and the scaling factors.
610  if (row[0].dot (row[1].cross (row[2])) < 0)
611  for (int i = 0; i < 3; i++)
612  {
613  scl[i] *= -1;
614  row[i] *= -1;
615  }
616 
617  // Copy over the orthonormal rows into the returned matrix.
618  // The upper 3x3 matrix in mat is now a rotation matrix.
619  for (int i = 0; i < 3; i++)
620  {
621  mat[i][0] = row[i][0];
622  mat[i][1] = row[i][1];
623  mat[i][2] = row[i][2];
624  }
625 
626  // Correct the scaling factors for the normalization step that we
627  // performed above; shear and rotation are not affected by the
628  // normalization.
629  scl *= maxVal;
630 
631  return true;
632 }
633 
634 template <class T>
635 void
636 extractEulerXYZ (const Matrix44<T>& mat, Vec3<T>& rot)
637 {
638  //
639  // Normalize the local x, y and z axes to remove scaling.
640  //
641 
642  Vec3<T> i (mat[0][0], mat[0][1], mat[0][2]);
643  Vec3<T> j (mat[1][0], mat[1][1], mat[1][2]);
644  Vec3<T> k (mat[2][0], mat[2][1], mat[2][2]);
645 
646  i.normalize();
647  j.normalize();
648  k.normalize();
649 
650  Matrix44<T> M (i[0], i[1], i[2], 0, j[0], j[1], j[2], 0, k[0], k[1], k[2], 0, 0, 0, 0, 1);
651 
652  //
653  // Extract the first angle, rot.x.
654  //
655 
656  rot.x = std::atan2 (M[1][2], M[2][2]);
657 
658  //
659  // Remove the rot.x rotation from M, so that the remaining
660  // rotation, N, is only around two axes, and gimbal lock
661  // cannot occur.
662  //
663 
664  Matrix44<T> N;
665  N.rotate (Vec3<T> (-rot.x, 0, 0));
666  N = N * M;
667 
668  //
669  // Extract the other two angles, rot.y and rot.z, from N.
670  //
671 
672  T cy = std::sqrt (N[0][0] * N[0][0] + N[0][1] * N[0][1]);
673  rot.y = std::atan2 (-N[0][2], cy);
674  rot.z = std::atan2 (-N[1][0], N[1][1]);
675 }
676 
677 template <class T>
678 void
679 extractEulerZYX (const Matrix44<T>& mat, Vec3<T>& rot)
680 {
681  //
682  // Normalize the local x, y and z axes to remove scaling.
683  //
684 
685  Vec3<T> i (mat[0][0], mat[0][1], mat[0][2]);
686  Vec3<T> j (mat[1][0], mat[1][1], mat[1][2]);
687  Vec3<T> k (mat[2][0], mat[2][1], mat[2][2]);
688 
689  i.normalize();
690  j.normalize();
691  k.normalize();
692 
693  Matrix44<T> M (i[0], i[1], i[2], 0, j[0], j[1], j[2], 0, k[0], k[1], k[2], 0, 0, 0, 0, 1);
694 
695  //
696  // Extract the first angle, rot.x.
697  //
698 
699  rot.x = -std::atan2 (M[1][0], M[0][0]);
700 
701  //
702  // Remove the x rotation from M, so that the remaining
703  // rotation, N, is only around two axes, and gimbal lock
704  // cannot occur.
705  //
706 
707  Matrix44<T> N;
708  N.rotate (Vec3<T> (0, 0, -rot.x));
709  N = N * M;
710 
711  //
712  // Extract the other two angles, rot.y and rot.z, from N.
713  //
714 
715  T cy = std::sqrt (N[2][2] * N[2][2] + N[2][1] * N[2][1]);
716  rot.y = -std::atan2 (-N[2][0], cy);
717  rot.z = -std::atan2 (-N[1][2], N[1][1]);
718 }
719 
720 template <class T>
721 Quat<T>
722 extractQuat (const Matrix44<T>& mat)
723 {
724  T tr, s;
725  T q[4];
726  int i, j, k;
727  Quat<T> quat;
728 
729  int nxt[3] = { 1, 2, 0 };
730  tr = mat[0][0] + mat[1][1] + mat[2][2];
731 
732  // check the diagonal
733  if (tr > 0.0)
734  {
735  s = std::sqrt (tr + T (1.0));
736  quat.r = s / T (2.0);
737  s = T (0.5) / s;
738 
739  quat.v.x = (mat[1][2] - mat[2][1]) * s;
740  quat.v.y = (mat[2][0] - mat[0][2]) * s;
741  quat.v.z = (mat[0][1] - mat[1][0]) * s;
742  }
743  else
744  {
745  // diagonal is negative
746  i = 0;
747  if (mat[1][1] > mat[0][0])
748  i = 1;
749  if (mat[2][2] > mat[i][i])
750  i = 2;
751 
752  j = nxt[i];
753  k = nxt[j];
754  s = std::sqrt ((mat[i][i] - (mat[j][j] + mat[k][k])) + T (1.0));
755 
756  q[i] = s * T (0.5);
757  if (s != T (0.0))
758  s = T (0.5) / s;
759 
760  q[3] = (mat[j][k] - mat[k][j]) * s;
761  q[j] = (mat[i][j] + mat[j][i]) * s;
762  q[k] = (mat[i][k] + mat[k][i]) * s;
763 
764  quat.v.x = q[0];
765  quat.v.y = q[1];
766  quat.v.z = q[2];
767  quat.r = q[3];
768  }
769 
770  return quat;
771 }
772 
773 template <class T>
774 bool
775 extractSHRT (const Matrix44<T>& mat,
776  Vec3<T>& s,
777  Vec3<T>& h,
778  Vec3<T>& r,
779  Vec3<T>& t,
780  bool exc /* = true */,
781  typename Euler<T>::Order rOrder /* = Euler<T>::XYZ */)
782 {
783  Matrix44<T> rot;
784 
785  rot = mat;
786  if (!extractAndRemoveScalingAndShear (rot, s, h, exc))
787  return false;
788 
789  extractEulerXYZ (rot, r);
790 
791  t.x = mat[3][0];
792  t.y = mat[3][1];
793  t.z = mat[3][2];
794 
795  if (rOrder != Euler<T>::XYZ)
796  {
797  Euler<T> eXYZ (r, Euler<T>::XYZ);
798  Euler<T> e (eXYZ, rOrder);
799  r = e.toXYZVector();
800  }
801 
802  return true;
803 }
804 
805 template <class T>
806 bool
807 extractSHRT (const Matrix44<T>& mat, Vec3<T>& s, Vec3<T>& h, Vec3<T>& r, Vec3<T>& t, bool exc)
808 {
809  return extractSHRT (mat, s, h, r, t, exc, Euler<T>::XYZ);
810 }
811 
812 template <class T>
813 bool
814 extractSHRT (const Matrix44<T>& mat,
815  Vec3<T>& s,
816  Vec3<T>& h,
817  Euler<T>& r,
818  Vec3<T>& t,
819  bool exc /* = true */)
820 {
821  return extractSHRT (mat, s, h, r, t, exc, r.order());
822 }
823 
824 template <class T>
825 bool
826 checkForZeroScaleInRow (const T& scl, const Vec3<T>& row, bool exc /* = true */)
827 {
828  for (int i = 0; i < 3; i++)
829  {
830  if ((abs (scl) < 1 && abs (row[i]) >= std::numeric_limits<T>::max() * abs (scl)))
831  {
832  #ifdef ATOMS_USE_EXCEPTIONS
833  if (exc)
834  throw std::domain_error ("Cannot remove zero scaling "
835  "from matrix.");
836  else
837  #endif
838  return false;
839  }
840  }
841 
842  return true;
843 }
844 
845 template <class T>
847 outerProduct (const Vec4<T>& a, const Vec4<T>& b)
848 {
849  return Matrix44<T> (a.x * b.x,
850  a.x * b.y,
851  a.x * b.z,
852  a.x * b.w,
853  a.y * b.x,
854  a.y * b.y,
855  a.y * b.z,
856  a.x * b.w,
857  a.z * b.x,
858  a.z * b.y,
859  a.z * b.z,
860  a.x * b.w,
861  a.w * b.x,
862  a.w * b.y,
863  a.w * b.z,
864  a.w * b.w);
865 }
866 
867 template <class T>
869 rotationMatrix (const Vec3<T>& from, const Vec3<T>& to)
870 {
871  Quat<T> q;
872  q.setRotation (from, to);
873  return q.toMatrix44();
874 }
875 
876 template <class T>
878 rotationMatrixWithUpDir (const Vec3<T>& fromDir, const Vec3<T>& toDir, const Vec3<T>& upDir)
879 {
880  //
881  // The goal is to obtain a rotation matrix that takes
882  // "fromDir" to "toDir". We do this in two steps and
883  // compose the resulting rotation matrices;
884  // (a) rotate "fromDir" into the z-axis
885  // (b) rotate the z-axis into "toDir"
886  //
887 
888  // The from direction must be non-zero; but we allow zero to and up dirs.
889  if (fromDir.length() == 0)
890  return Matrix44<T>();
891 
892  else
893  {
894  Matrix44<T> zAxis2FromDir (UNINITIALIZED);
895  alignZAxisWithTargetDir (zAxis2FromDir, fromDir, Vec3<T> (0, 1, 0));
896 
897  Matrix44<T> fromDir2zAxis = zAxis2FromDir.transposed();
898 
899  Matrix44<T> zAxis2ToDir (UNINITIALIZED);
900  alignZAxisWithTargetDir (zAxis2ToDir, toDir, upDir);
901 
902  return fromDir2zAxis * zAxis2ToDir;
903  }
904 }
905 
906 template <class T>
907 void
908 alignZAxisWithTargetDir (Matrix44<T>& result, Vec3<T> targetDir, Vec3<T> upDir)
909 {
910  //
911  // Ensure that the target direction is non-zero.
912  //
913 
914  if (targetDir.length() == 0)
915  targetDir = Vec3<T> (0, 0, 1);
916 
917  //
918  // Ensure that the up direction is non-zero.
919  //
920 
921  if (upDir.length() == 0)
922  upDir = Vec3<T> (0, 1, 0);
923 
924  //
925  // Check for degeneracies. If the upDir and targetDir are parallel
926  // or opposite, then compute a new, arbitrary up direction that is
927  // not parallel or opposite to the targetDir.
928  //
929 
930  if (upDir.cross (targetDir).length() == 0)
931  {
932  upDir = targetDir.cross (Vec3<T> (1, 0, 0));
933  if (upDir.length() == 0)
934  upDir = targetDir.cross (Vec3<T> (0, 0, 1));
935  }
936 
937  //
938  // Compute the x-, y-, and z-axis vectors of the new coordinate system.
939  //
940 
941  Vec3<T> targetPerpDir = upDir.cross (targetDir);
942  Vec3<T> targetUpDir = targetDir.cross (targetPerpDir);
943 
944  //
945  // Rotate the x-axis into targetPerpDir (row 0),
946  // rotate the y-axis into targetUpDir (row 1),
947  // rotate the z-axis into targetDir (row 2).
948  //
949 
950  Vec3<T> row[3];
951  row[0] = targetPerpDir.normalized();
952  row[1] = targetUpDir.normalized();
953  row[2] = targetDir.normalized();
954 
955  result.x[0][0] = row[0][0];
956  result.x[0][1] = row[0][1];
957  result.x[0][2] = row[0][2];
958  result.x[0][3] = (T) 0;
959 
960  result.x[1][0] = row[1][0];
961  result.x[1][1] = row[1][1];
962  result.x[1][2] = row[1][2];
963  result.x[1][3] = (T) 0;
964 
965  result.x[2][0] = row[2][0];
966  result.x[2][1] = row[2][1];
967  result.x[2][2] = row[2][2];
968  result.x[2][3] = (T) 0;
969 
970  result.x[3][0] = (T) 0;
971  result.x[3][1] = (T) 0;
972  result.x[3][2] = (T) 0;
973  result.x[3][3] = (T) 1;
974 }
975 
976 // Compute an orthonormal direct frame from : a position, an x axis direction and a normal to the y axis
977 // If the x axis and normal are perpendicular, then the normal will have the same direction as the z axis.
978 // Inputs are :
979 // -the position of the frame
980 // -the x axis direction of the frame
981 // -a normal to the y axis of the frame
982 // Return is the orthonormal frame
983 template <class T>
985 computeLocalFrame (const Vec3<T>& p, const Vec3<T>& xDir, const Vec3<T>& normal)
986 {
987  Vec3<T> _xDir (xDir);
988  Vec3<T> x = _xDir.normalize();
989  Vec3<T> y = (normal % x).normalize();
990  Vec3<T> z = (x % y).normalize();
991 
992  Matrix44<T> L;
993  L[0][0] = x[0];
994  L[0][1] = x[1];
995  L[0][2] = x[2];
996  L[0][3] = 0.0;
997 
998  L[1][0] = y[0];
999  L[1][1] = y[1];
1000  L[1][2] = y[2];
1001  L[1][3] = 0.0;
1002 
1003  L[2][0] = z[0];
1004  L[2][1] = z[1];
1005  L[2][2] = z[2];
1006  L[2][3] = 0.0;
1007 
1008  L[3][0] = p[0];
1009  L[3][1] = p[1];
1010  L[3][2] = p[2];
1011  L[3][3] = 1.0;
1012 
1013  return L;
1014 }
1015 
1025 template <class T>
1027 addOffset (const Matrix44<T>& inMat,
1028  const Vec3<T>& tOffset,
1029  const Vec3<T>& rOffset,
1030  const Vec3<T>& sOffset,
1031  const Matrix44<T>& ref)
1032 {
1033  Matrix44<T> O;
1034 
1035  Vec3<T> _rOffset (rOffset);
1036  _rOffset *= M_PI / 180.0;
1037  O.rotate (_rOffset);
1038 
1039  O[3][0] = tOffset[0];
1040  O[3][1] = tOffset[1];
1041  O[3][2] = tOffset[2];
1042 
1043  Matrix44<T> S;
1044  S.scale (sOffset);
1045 
1046  Matrix44<T> X = S * O * inMat * ref;
1047 
1048  return X;
1049 }
1050 
1051 // Compute Translate/Rotate/Scale matrix from matrix A with the Rotate/Scale of Matrix B
1052 // Inputs are :
1053 // -keepRotateA : if true keep rotate from matrix A, use B otherwise
1054 // -keepScaleA : if true keep scale from matrix A, use B otherwise
1055 // -Matrix A
1056 // -Matrix B
1057 // Return Matrix A with tweaked rotation/scale
1058 template <class T>
1060 computeRSMatrix (bool keepRotateA, bool keepScaleA, const Matrix44<T>& A, const Matrix44<T>& B)
1061 {
1062  Vec3<T> as, ah, ar, at;
1063  extractSHRT (A, as, ah, ar, at);
1064 
1065  Vec3<T> bs, bh, br, bt;
1066  extractSHRT (B, bs, bh, br, bt);
1067 
1068  if (!keepRotateA)
1069  ar = br;
1070 
1071  if (!keepScaleA)
1072  as = bs;
1073 
1074  Matrix44<T> mat;
1075  mat.makeIdentity();
1076  mat.translate (at);
1077  mat.rotate (ar);
1078  mat.scale (as);
1079 
1080  return mat;
1081 }
1082 
1083 //-----------------------------------------------------------------------------
1084 // Implementation for 3x3 Matrix
1085 //------------------------------
1086 
1087 template <class T>
1088 bool
1089 extractScaling (const Matrix33<T>& mat, Vec2<T>& scl, bool exc)
1090 {
1091  T shr;
1092  Matrix33<T> M (mat);
1093 
1094  if (!extractAndRemoveScalingAndShear (M, scl, shr, exc))
1095  return false;
1096 
1097  return true;
1098 }
1099 
1100 template <class T>
1102 sansScaling (const Matrix33<T>& mat, bool exc)
1103 {
1104  Vec2<T> scl;
1105  T shr;
1106  T rot;
1107  Vec2<T> tran;
1108 
1109  if (!extractSHRT (mat, scl, shr, rot, tran, exc))
1110  return mat;
1111 
1112  Matrix33<T> M;
1113 
1114  M.translate (tran);
1115  M.rotate (rot);
1116  M.shear (shr);
1117 
1118  return M;
1119 }
1120 
1121 template <class T>
1122 bool
1123 removeScaling (Matrix33<T>& mat, bool exc)
1124 {
1125  Vec2<T> scl;
1126  T shr;
1127  T rot;
1128  Vec2<T> tran;
1129 
1130  if (!extractSHRT (mat, scl, shr, rot, tran, exc))
1131  return false;
1132 
1133  mat.makeIdentity();
1134  mat.translate (tran);
1135  mat.rotate (rot);
1136  mat.shear (shr);
1137 
1138  return true;
1139 }
1140 
1141 template <class T>
1142 bool
1143 extractScalingAndShear (const Matrix33<T>& mat, Vec2<T>& scl, T& shr, bool exc)
1144 {
1145  Matrix33<T> M (mat);
1146 
1147  if (!extractAndRemoveScalingAndShear (M, scl, shr, exc))
1148  return false;
1149 
1150  return true;
1151 }
1152 
1153 template <class T>
1155 sansScalingAndShear (const Matrix33<T>& mat, bool exc)
1156 {
1157  Vec2<T> scl;
1158  T shr;
1159  Matrix33<T> M (mat);
1160 
1161  if (!extractAndRemoveScalingAndShear (M, scl, shr, exc))
1162  return mat;
1163 
1164  return M;
1165 }
1166 
1167 template <class T>
1168 bool
1169 removeScalingAndShear (Matrix33<T>& mat, bool exc)
1170 {
1171  Vec2<T> scl;
1172  T shr;
1173 
1174  if (!extractAndRemoveScalingAndShear (mat, scl, shr, exc))
1175  return false;
1176 
1177  return true;
1178 }
1179 
1180 template <class T>
1181 bool
1182 extractAndRemoveScalingAndShear (Matrix33<T>& mat, Vec2<T>& scl, T& shr, bool exc)
1183 {
1184  Vec2<T> row[2];
1185 
1186  row[0] = Vec2<T> (mat[0][0], mat[0][1]);
1187  row[1] = Vec2<T> (mat[1][0], mat[1][1]);
1188 
1189  T maxVal = 0;
1190  for (int i = 0; i < 2; i++)
1191  for (int j = 0; j < 2; j++)
1192  if (ATOMSMATH_INTERNAL_NAMESPACE::abs (row[i][j]) > maxVal)
1193  maxVal = ATOMSMATH_INTERNAL_NAMESPACE::abs (row[i][j]);
1194 
1195  //
1196  // We normalize the 2x2 matrix here.
1197  // It was noticed that this can improve numerical stability significantly,
1198  // especially when many of the upper 2x2 matrix's coefficients are very
1199  // close to zero; we correct for this step at the end by multiplying the
1200  // scaling factors by maxVal at the end (shear and rotation are not
1201  // affected by the normalization).
1202 
1203  if (maxVal != 0)
1204  {
1205  for (int i = 0; i < 2; i++)
1206  if (!checkForZeroScaleInRow (maxVal, row[i], exc))
1207  return false;
1208  else
1209  row[i] /= maxVal;
1210  }
1211 
1212  // Compute X scale factor.
1213  scl.x = row[0].length();
1214  if (!checkForZeroScaleInRow (scl.x, row[0], exc))
1215  return false;
1216 
1217  // Normalize first row.
1218  row[0] /= scl.x;
1219 
1220  // An XY shear factor will shear the X coord. as the Y coord. changes.
1221  // There are 2 combinations (XY, YX), although we only extract the XY
1222  // shear factor because we can effect the an YX shear factor by
1223  // shearing in XY combined with rotations and scales.
1224  //
1225  // shear matrix < 1, YX, 0,
1226  // XY, 1, 0,
1227  // 0, 0, 1 >
1228 
1229  // Compute XY shear factor and make 2nd row orthogonal to 1st.
1230  shr = row[0].dot (row[1]);
1231  row[1] -= shr * row[0];
1232 
1233  // Now, compute Y scale.
1234  scl.y = row[1].length();
1235  if (!checkForZeroScaleInRow (scl.y, row[1], exc))
1236  return false;
1237 
1238  // Normalize 2nd row and correct the XY shear factor for Y scaling.
1239  row[1] /= scl.y;
1240  shr /= scl.y;
1241 
1242  // At this point, the upper 2x2 matrix in mat is orthonormal.
1243  // Check for a coordinate system flip. If the determinant
1244  // is -1, then flip the rotation matrix and adjust the scale(Y)
1245  // and shear(XY) factors to compensate.
1246  if (row[0][0] * row[1][1] - row[0][1] * row[1][0] < 0)
1247  {
1248  row[1][0] *= -1;
1249  row[1][1] *= -1;
1250  scl[1] *= -1;
1251  shr *= -1;
1252  }
1253 
1254  // Copy over the orthonormal rows into the returned matrix.
1255  // The upper 2x2 matrix in mat is now a rotation matrix.
1256  for (int i = 0; i < 2; i++)
1257  {
1258  mat[i][0] = row[i][0];
1259  mat[i][1] = row[i][1];
1260  }
1261 
1262  scl *= maxVal;
1263 
1264  return true;
1265 }
1266 
1267 template <class T>
1268 void
1269 extractEuler (const Matrix22<T>& mat, T& rot)
1270 {
1271  //
1272  // Normalize the local x and y axes to remove scaling.
1273  //
1274 
1275  Vec2<T> i (mat[0][0], mat[0][1]);
1276  Vec2<T> j (mat[1][0], mat[1][1]);
1277 
1278  i.normalize();
1279  j.normalize();
1280 
1281  //
1282  // Extract the angle, rot.
1283  //
1284 
1285  rot = -std::atan2 (j[0], i[0]);
1286 }
1287 
1288 template <class T>
1289 void
1290 extractEuler (const Matrix33<T>& mat, T& rot)
1291 {
1292  //
1293  // Normalize the local x and y axes to remove scaling.
1294  //
1295 
1296  Vec2<T> i (mat[0][0], mat[0][1]);
1297  Vec2<T> j (mat[1][0], mat[1][1]);
1298 
1299  i.normalize();
1300  j.normalize();
1301 
1302  //
1303  // Extract the angle, rot.
1304  //
1305 
1306  rot = -std::atan2 (j[0], i[0]);
1307 }
1308 
1309 template <class T>
1310 bool
1311 extractSHRT (const Matrix33<T>& mat, Vec2<T>& s, T& h, T& r, Vec2<T>& t, bool exc)
1312 {
1313  Matrix33<T> rot;
1314 
1315  rot = mat;
1316  if (!extractAndRemoveScalingAndShear (rot, s, h, exc))
1317  return false;
1318 
1319  extractEuler (rot, r);
1320 
1321  t.x = mat[2][0];
1322  t.y = mat[2][1];
1323 
1324  return true;
1325 }
1326 
1328 template <class T>
1329 bool
1330 checkForZeroScaleInRow (const T& scl, const Vec2<T>& row, bool exc /* = true */)
1331 {
1332  for (int i = 0; i < 2; i++)
1333  {
1334  if ((abs (scl) < 1 && abs (row[i]) >= std::numeric_limits<T>::max() * abs (scl)))
1335  {
1336  #ifdef ATOMS_USE_EXCEPTIONS
1337  if (exc)
1338  throw std::domain_error ("Cannot remove zero scaling from matrix.");
1339  else
1340  #endif
1341  return false;
1342  }
1343  }
1344 
1345  return true;
1346 }
1348 
1349 template <class T>
1351 outerProduct (const Vec3<T>& a, const Vec3<T>& b)
1352 {
1353  return Matrix33<T> (a.x * b.x,
1354  a.x * b.y,
1355  a.x * b.z,
1356  a.y * b.x,
1357  a.y * b.y,
1358  a.y * b.z,
1359  a.z * b.x,
1360  a.z * b.y,
1361  a.z * b.z);
1362 }
1363 
1379 template <typename T>
1380 M44d
1381 procrustesRotationAndTranslation (const Vec3<T>* A,
1382  const Vec3<T>* B,
1383  const T* weights,
1384  const size_t numPoints,
1385  const bool doScaling = false);
1386 
1401 template <typename T>
1402 M44d
1403 procrustesRotationAndTranslation (const Vec3<T>* A,
1404  const Vec3<T>* B,
1405  const size_t numPoints,
1406  const bool doScaling = false);
1407 
1423 template <typename T>
1424 void jacobiSVD (const Matrix33<T>& A,
1425  Matrix33<T>& U,
1426  Vec3<T>& S,
1427  Matrix33<T>& V,
1428  const T tol = std::numeric_limits<T>::epsilon(),
1429  const bool forcePositiveDeterminant = false);
1430 
1446 template <typename T>
1447 void jacobiSVD (const Matrix44<T>& A,
1448  Matrix44<T>& U,
1449  Vec4<T>& S,
1450  Matrix44<T>& V,
1451  const T tol = std::numeric_limits<T>::epsilon(),
1452  const bool forcePositiveDeterminant = false);
1453 
1463 template <typename T>
1464 void jacobiEigenSolver (Matrix33<T>& A, Vec3<T>& S, Matrix33<T>& V, const T tol);
1465 
1474 template <typename T>
1475 inline void
1476 jacobiEigenSolver (Matrix33<T>& A, Vec3<T>& S, Matrix33<T>& V)
1477 {
1478  jacobiEigenSolver (A, S, V, std::numeric_limits<T>::epsilon());
1479 }
1480 
1490 template <typename T>
1491 void jacobiEigenSolver (Matrix44<T>& A, Vec4<T>& S, Matrix44<T>& V, const T tol);
1492 
1501 template <typename T>
1502 inline void
1503 jacobiEigenSolver (Matrix44<T>& A, Vec4<T>& S, Matrix44<T>& V)
1504 {
1505  jacobiEigenSolver (A, S, V, std::numeric_limits<T>::epsilon());
1506 }
1507 
1510 template <typename TM, typename TV> void maxEigenVector (TM& A, TV& S);
1511 
1514 template <typename TM, typename TV> void minEigenVector (TM& A, TV& S);
1515 
1516 ATOMSMATH_INTERNAL_NAMESPACE_HEADER_EXIT
1517 
1518 #endif // INCLUDED_ATOMSMATHMATRIXALGO_H
Definition: ImathEuler.h:118
ATOMSMATH_HOSTDEVICE ATOMSMATH_CONSTEXPR14 Order order() const noexcept
Return the order.
Order
Definition: ImathEuler.h:129
Definition: ImathMatrix.h:44
Definition: ImathMatrix.h:305
ATOMSMATH_HOSTDEVICE ATOMSMATH_CONSTEXPR14 const Matrix33 & translate(const Vec2< S > &t) noexcept
ATOMSMATH_HOSTDEVICE ATOMSMATH_CONSTEXPR14 const Matrix33 & shear(const S &xy) noexcept
ATOMSMATH_HOSTDEVICE void makeIdentity() noexcept
Set to the identity matrix.
Definition: ImathMatrix.h:1875
ATOMSMATH_HOSTDEVICE ATOMSMATH_CONSTEXPR14 const Matrix33 & rotate(S r) noexcept
Definition: ImathMatrix.h:631
constexpr ATOMSMATH_HOSTDEVICE Matrix44 transposed() const noexcept
Return the transpose.
Definition: ImathMatrix.h:3708
ATOMSMATH_HOSTDEVICE ATOMSMATH_CONSTEXPR14 const Matrix44 & scale(const Vec3< S > &s) noexcept
ATOMSMATH_HOSTDEVICE void makeIdentity() noexcept
Set to the identity matrix.
Definition: ImathMatrix.h:3223
T x[4][4]
Matrix elements.
Definition: ImathMatrix.h:638
ATOMSMATH_HOSTDEVICE ATOMSMATH_CONSTEXPR14 const Matrix44 & translate(const Vec3< S > &t) noexcept
ATOMSMATH_HOSTDEVICE const Matrix44 & rotate(const Vec3< S > &r) noexcept
ATOMSMATH_HOSTDEVICE ATOMSMATH_CONSTEXPR14 const Matrix44 & shear(const Vec3< S > &h) noexcept
Definition: ImathQuat.h:41
ATOMSMATH_HOSTDEVICE ATOMSMATH_CONSTEXPR14 Quat< T > & setRotation(const Vec3< T > &fromDirection, const Vec3< T > &toDirection) noexcept
Definition: ImathQuat.h:673
Vec3< T > v
The imaginary vector.
Definition: ImathQuat.h:51
T r
The real part.
Definition: ImathQuat.h:48
constexpr ATOMSMATH_HOSTDEVICE Matrix44< T > toMatrix44() const noexcept
Return a 4x4 rotation matrix.
Definition: ImathQuat.h:795
Definition: ImathVec.h:43
constexpr ATOMSMATH_HOSTDEVICE T dot(const Vec2 &v) const noexcept
Dot product.
Definition: ImathVec.h:931
ATOMSMATH_HOSTDEVICE T length() const noexcept
Return the Euclidean norm.
Definition: ImathVec.h:1098
Definition: ImathVec.h:260
constexpr ATOMSMATH_HOSTDEVICE Vec3 cross(const Vec3 &v) const noexcept
Right-handed cross product.
Definition: ImathVec.h:1384
constexpr ATOMSMATH_HOSTDEVICE T dot(const Vec3 &v) const noexcept
Dot product.
Definition: ImathVec.h:1370
ATOMSMATH_HOSTDEVICE Vec3< T > normalized() const noexcept
Return a normalized vector. Does not modify *this.
Definition: ImathVec.h:1629
ATOMSMATH_HOSTDEVICE T length() const noexcept
Return the Euclidean norm.
Definition: ImathVec.h:1562
ATOMSMATH_HOSTDEVICE const Vec3 & normalize() noexcept
Normalize in place. If length()==0, return a null vector.
Definition: ImathVec.h:1581
Definition: ImathVec.h:486