SymmetricEigensolver2x2.h 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. // David Eberly, Geometric Tools, Redmond WA 98052
  2. // Copyright (c) 1998-2020
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // https://www.boost.org/LICENSE_1_0.txt
  5. // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
  6. // Version: 4.0.2019.08.13
  7. #pragma once
  8. #include <Mathematics/Math.h>
  9. #include <algorithm>
  10. #include <array>
  11. namespace WwiseGTE
  12. {
  13. template <typename Real>
  14. class SymmetricEigensolver2x2
  15. {
  16. public:
  17. // The input matrix must be symmetric, so only the unique elements
  18. // must be specified: a00, a01, and a11.
  19. //
  20. // The order of the eigenvalues is specified by sortType:
  21. // -1 (decreasing), 0 (no sorting) or +1 (increasing). When sorted,
  22. // the eigenvectors are ordered accordingly, and {evec[0], evec[1]}
  23. // is guaranteed to be a right-handed orthonormal set.
  24. void operator()(Real a00, Real a01, Real a11, int sortType,
  25. std::array<Real, 2>& eval, std::array<std::array<Real, 2>, 2>& evec) const
  26. {
  27. // Normalize (c2,s2) robustly, avoiding floating-point overflow
  28. // in the sqrt call.
  29. Real const zero = (Real)0, one = (Real)1, half = (Real)0.5;
  30. Real c2 = half * (a00 - a11), s2 = a01;
  31. Real maxAbsComp = std::max(std::fabs(c2), std::fabs(s2));
  32. if (maxAbsComp > zero)
  33. {
  34. c2 /= maxAbsComp; // in [-1,1]
  35. s2 /= maxAbsComp; // in [-1,1]
  36. Real length = std::sqrt(c2 * c2 + s2 * s2);
  37. c2 /= length;
  38. s2 /= length;
  39. if (c2 > zero)
  40. {
  41. c2 = -c2;
  42. s2 = -s2;
  43. }
  44. }
  45. else
  46. {
  47. c2 = -one;
  48. s2 = zero;
  49. }
  50. Real s = std::sqrt(half * (one - c2)); // >= 1/sqrt(2)
  51. Real c = half * s2 / s;
  52. Real diagonal[2];
  53. Real csqr = c * c, ssqr = s * s, mid = s2 * a01;
  54. diagonal[0] = csqr * a00 + mid + ssqr * a11;
  55. diagonal[1] = csqr * a11 - mid + ssqr * a00;
  56. if (sortType == 0 || sortType * diagonal[0] <= sortType * diagonal[1])
  57. {
  58. eval[0] = diagonal[0];
  59. eval[1] = diagonal[1];
  60. evec[0][0] = c;
  61. evec[0][1] = s;
  62. evec[1][0] = -s;
  63. evec[1][1] = c;
  64. }
  65. else
  66. {
  67. eval[0] = diagonal[1];
  68. eval[1] = diagonal[0];
  69. evec[0][0] = s;
  70. evec[0][1] = -c;
  71. evec[1][0] = c;
  72. evec[1][1] = s;
  73. }
  74. }
  75. };
  76. }