SymmetricEigensolver3x3.h 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  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.16
  7. #pragma once
  8. #include <Mathematics/Math.h>
  9. #include <algorithm>
  10. #include <array>
  11. // The document
  12. // https://www.geometrictools.com/Documentation/RobustEigenSymmetric3x3.pdf
  13. // describes algorithms for solving the eigensystem associated with a 3x3
  14. // symmetric real-valued matrix. The iterative algorithm is implemented
  15. // by class SymmmetricEigensolver3x3. The noniterative algorithm is
  16. // implemented by class NISymmetricEigensolver3x3. The code does not use
  17. // GTEngine objects.
  18. namespace WwiseGTE
  19. {
  20. template <typename Real>
  21. class SortEigenstuff
  22. {
  23. public:
  24. void operator()(int sortType, bool isRotation,
  25. std::array<Real, 3>& eval, std::array<std::array<Real, 3>, 3>& evec)
  26. {
  27. if (sortType != 0)
  28. {
  29. // Sort the eigenvalues to eval[0] <= eval[1] <= eval[2].
  30. std::array<size_t, 3> index;
  31. if (eval[0] < eval[1])
  32. {
  33. if (eval[2] < eval[0])
  34. {
  35. // even permutation
  36. index[0] = 2;
  37. index[1] = 0;
  38. index[2] = 1;
  39. }
  40. else if (eval[2] < eval[1])
  41. {
  42. // odd permutation
  43. index[0] = 0;
  44. index[1] = 2;
  45. index[2] = 1;
  46. isRotation = !isRotation;
  47. }
  48. else
  49. {
  50. // even permutation
  51. index[0] = 0;
  52. index[1] = 1;
  53. index[2] = 2;
  54. }
  55. }
  56. else
  57. {
  58. if (eval[2] < eval[1])
  59. {
  60. // odd permutation
  61. index[0] = 2;
  62. index[1] = 1;
  63. index[2] = 0;
  64. isRotation = !isRotation;
  65. }
  66. else if (eval[2] < eval[0])
  67. {
  68. // even permutation
  69. index[0] = 1;
  70. index[1] = 2;
  71. index[2] = 0;
  72. }
  73. else
  74. {
  75. // odd permutation
  76. index[0] = 1;
  77. index[1] = 0;
  78. index[2] = 2;
  79. isRotation = !isRotation;
  80. }
  81. }
  82. if (sortType == -1)
  83. {
  84. // The request is for eval[0] >= eval[1] >= eval[2]. This
  85. // requires an odd permutation, (i0,i1,i2) -> (i2,i1,i0).
  86. std::swap(index[0], index[2]);
  87. isRotation = !isRotation;
  88. }
  89. std::array<Real, 3> unorderedEVal = eval;
  90. std::array<std::array<Real, 3>, 3> unorderedEVec = evec;
  91. for (size_t j = 0; j < 3; ++j)
  92. {
  93. size_t i = index[j];
  94. eval[j] = unorderedEVal[i];
  95. evec[j] = unorderedEVec[i];
  96. }
  97. }
  98. // Ensure the ordered eigenvectors form a right-handed basis.
  99. if (!isRotation)
  100. {
  101. for (size_t j = 0; j < 3; ++j)
  102. {
  103. evec[2][j] = -evec[2][j];
  104. }
  105. }
  106. }
  107. };
  108. template <typename Real>
  109. class SymmetricEigensolver3x3
  110. {
  111. public:
  112. // The input matrix must be symmetric, so only the unique elements
  113. // must be specified: a00, a01, a02, a11, a12, and a22.
  114. //
  115. // If 'aggressive' is 'true', the iterations occur until a
  116. // superdiagonal entry is exactly zero. If 'aggressive' is 'false',
  117. // the iterations occur until a superdiagonal entry is effectively
  118. // zero compared to the/ sum of magnitudes of its diagonal neighbors.
  119. // Generally, the nonaggressive convergence is acceptable.
  120. //
  121. // The order of the eigenvalues is specified by sortType:
  122. // -1 (decreasing), 0 (no sorting) or +1 (increasing). When sorted,
  123. // the eigenvectors are ordered accordingly, and
  124. // {evec[0], evec[1], evec[2]} is guaranteed to/ be a right-handed
  125. // orthonormal set. The return value is the number of iterations
  126. // used by the algorithm.
  127. int operator()(Real a00, Real a01, Real a02, Real a11, Real a12, Real a22,
  128. bool aggressive, int sortType, std::array<Real, 3>& eval,
  129. std::array<std::array<Real, 3>, 3>& evec) const
  130. {
  131. // Compute the Householder reflection H and B = H*A*H, where
  132. // b02 = 0.
  133. Real const zero = (Real)0, one = (Real)1, half = (Real)0.5;
  134. bool isRotation = false;
  135. Real c, s;
  136. GetCosSin(a12, -a02, c, s);
  137. Real Q[3][3] = { { c, s, zero }, { s, -c, zero }, { zero, zero, one } };
  138. Real term0 = c * a00 + s * a01;
  139. Real term1 = c * a01 + s * a11;
  140. Real b00 = c * term0 + s * term1;
  141. Real b01 = s * term0 - c * term1;
  142. term0 = s * a00 - c * a01;
  143. term1 = s * a01 - c * a11;
  144. Real b11 = s * term0 - c * term1;
  145. Real b12 = s * a02 - c * a12;
  146. Real b22 = a22;
  147. // Givens reflections, B' = G^T*B*G, preserve tridiagonal
  148. // matrices.
  149. int const maxIteration = 2 * (1 + std::numeric_limits<Real>::digits -
  150. std::numeric_limits<Real>::min_exponent);
  151. int iteration;
  152. Real c2, s2;
  153. if (std::fabs(b12) <= std::fabs(b01))
  154. {
  155. Real saveB00, saveB01, saveB11;
  156. for (iteration = 0; iteration < maxIteration; ++iteration)
  157. {
  158. // Compute the Givens reflection.
  159. GetCosSin(half * (b00 - b11), b01, c2, s2);
  160. s = std::sqrt(half * (one - c2)); // >= 1/sqrt(2)
  161. c = half * s2 / s;
  162. // Update Q by the Givens reflection.
  163. Update0(Q, c, s);
  164. isRotation = !isRotation;
  165. // Update B <- Q^T*B*Q, ensuring that b02 is zero and
  166. // |b12| has strictly decreased.
  167. saveB00 = b00;
  168. saveB01 = b01;
  169. saveB11 = b11;
  170. term0 = c * saveB00 + s * saveB01;
  171. term1 = c * saveB01 + s * saveB11;
  172. b00 = c * term0 + s * term1;
  173. b11 = b22;
  174. term0 = c * saveB01 - s * saveB00;
  175. term1 = c * saveB11 - s * saveB01;
  176. b22 = c * term1 - s * term0;
  177. b01 = s * b12;
  178. b12 = c * b12;
  179. if (Converged(aggressive, b00, b11, b01))
  180. {
  181. // Compute the Householder reflection.
  182. GetCosSin(half * (b00 - b11), b01, c2, s2);
  183. s = std::sqrt(half * (one - c2));
  184. c = half * s2 / s; // >= 1/sqrt(2)
  185. // Update Q by the Householder reflection.
  186. Update2(Q, c, s);
  187. isRotation = !isRotation;
  188. // Update D = Q^T*B*Q.
  189. saveB00 = b00;
  190. saveB01 = b01;
  191. saveB11 = b11;
  192. term0 = c * saveB00 + s * saveB01;
  193. term1 = c * saveB01 + s * saveB11;
  194. b00 = c * term0 + s * term1;
  195. term0 = s * saveB00 - c * saveB01;
  196. term1 = s * saveB01 - c * saveB11;
  197. b11 = s * term0 - c * term1;
  198. break;
  199. }
  200. }
  201. }
  202. else
  203. {
  204. Real saveB11, saveB12, saveB22;
  205. for (iteration = 0; iteration < maxIteration; ++iteration)
  206. {
  207. // Compute the Givens reflection.
  208. GetCosSin(half * (b22 - b11), b12, c2, s2);
  209. s = std::sqrt(half * (one - c2)); // >= 1/sqrt(2)
  210. c = half * s2 / s;
  211. // Update Q by the Givens reflection.
  212. Update1(Q, c, s);
  213. isRotation = !isRotation;
  214. // Update B <- Q^T*B*Q, ensuring that b02 is zero and
  215. // |b12| has strictly decreased. MODIFY...
  216. saveB11 = b11;
  217. saveB12 = b12;
  218. saveB22 = b22;
  219. term0 = c * saveB22 + s * saveB12;
  220. term1 = c * saveB12 + s * saveB11;
  221. b22 = c * term0 + s * term1;
  222. b11 = b00;
  223. term0 = c * saveB12 - s * saveB22;
  224. term1 = c * saveB11 - s * saveB12;
  225. b00 = c * term1 - s * term0;
  226. b12 = s * b01;
  227. b01 = c * b01;
  228. if (Converged(aggressive, b11, b22, b12))
  229. {
  230. // Compute the Householder reflection.
  231. GetCosSin(half * (b11 - b22), b12, c2, s2);
  232. s = std::sqrt(half * (one - c2));
  233. c = half * s2 / s; // >= 1/sqrt(2)
  234. // Update Q by the Householder reflection.
  235. Update3(Q, c, s);
  236. isRotation = !isRotation;
  237. // Update D = Q^T*B*Q.
  238. saveB11 = b11;
  239. saveB12 = b12;
  240. saveB22 = b22;
  241. term0 = c * saveB11 + s * saveB12;
  242. term1 = c * saveB12 + s * saveB22;
  243. b11 = c * term0 + s * term1;
  244. term0 = s * saveB11 - c * saveB12;
  245. term1 = s * saveB12 - c * saveB22;
  246. b22 = s * term0 - c * term1;
  247. break;
  248. }
  249. }
  250. }
  251. eval = { b00, b11, b22 };
  252. for (size_t row = 0; row < 3; ++row)
  253. {
  254. for (size_t col = 0; col < 3; ++col)
  255. {
  256. evec[row][col] = Q[col][row];
  257. }
  258. }
  259. SortEigenstuff<Real>()(sortType, isRotation, eval, evec);
  260. return iteration;
  261. }
  262. private:
  263. // Update Q = Q*G in-place using G = {{c,0,-s},{s,0,c},{0,0,1}}.
  264. void Update0(Real Q[3][3], Real c, Real s) const
  265. {
  266. for (int r = 0; r < 3; ++r)
  267. {
  268. Real tmp0 = c * Q[r][0] + s * Q[r][1];
  269. Real tmp1 = Q[r][2];
  270. Real tmp2 = c * Q[r][1] - s * Q[r][0];
  271. Q[r][0] = tmp0;
  272. Q[r][1] = tmp1;
  273. Q[r][2] = tmp2;
  274. }
  275. }
  276. // Update Q = Q*G in-place using G = {{0,1,0},{c,0,s},{-s,0,c}}.
  277. void Update1(Real Q[3][3], Real c, Real s) const
  278. {
  279. for (int r = 0; r < 3; ++r)
  280. {
  281. Real tmp0 = c * Q[r][1] - s * Q[r][2];
  282. Real tmp1 = Q[r][0];
  283. Real tmp2 = c * Q[r][2] + s * Q[r][1];
  284. Q[r][0] = tmp0;
  285. Q[r][1] = tmp1;
  286. Q[r][2] = tmp2;
  287. }
  288. }
  289. // Update Q = Q*H in-place using H = {{c,s,0},{s,-c,0},{0,0,1}}.
  290. void Update2(Real Q[3][3], Real c, Real s) const
  291. {
  292. for (int r = 0; r < 3; ++r)
  293. {
  294. Real tmp0 = c * Q[r][0] + s * Q[r][1];
  295. Real tmp1 = s * Q[r][0] - c * Q[r][1];
  296. Q[r][0] = tmp0;
  297. Q[r][1] = tmp1;
  298. }
  299. }
  300. // Update Q = Q*H in-place using H = {{1,0,0},{0,c,s},{0,s,-c}}.
  301. void Update3(Real Q[3][3], Real c, Real s) const
  302. {
  303. for (int r = 0; r < 3; ++r)
  304. {
  305. Real tmp0 = c * Q[r][1] + s * Q[r][2];
  306. Real tmp1 = s * Q[r][1] - c * Q[r][2];
  307. Q[r][1] = tmp0;
  308. Q[r][2] = tmp1;
  309. }
  310. }
  311. // Normalize (u,v) robustly, avoiding floating-point overflow in the
  312. // sqrt call. The normalized pair is (cs,sn) with cs <= 0. If
  313. // (u,v) = (0,0), the function returns (cs,sn) = (-1,0). When used
  314. // to generate a Householder reflection, it does not matter whether
  315. // (cs,sn) or (-cs,-sn) is used. When generating a Givens reflection,
  316. // cs = cos(2*theta) and sn = sin(2*theta). Having a negative cosine
  317. // for the double-angle term ensures that the single-angle terms
  318. // c = cos(theta) and s = sin(theta) satisfy |c| <= |s|.
  319. void GetCosSin(Real u, Real v, Real& cs, Real& sn) const
  320. {
  321. Real maxAbsComp = std::max(std::fabs(u), std::fabs(v));
  322. if (maxAbsComp > (Real)0)
  323. {
  324. u /= maxAbsComp; // in [-1,1]
  325. v /= maxAbsComp; // in [-1,1]
  326. Real length = std::sqrt(u * u + v * v);
  327. cs = u / length;
  328. sn = v / length;
  329. if (cs > (Real)0)
  330. {
  331. cs = -cs;
  332. sn = -sn;
  333. }
  334. }
  335. else
  336. {
  337. cs = (Real)-1;
  338. sn = (Real)0;
  339. }
  340. }
  341. // The convergence test. When 'aggressive' is 'true', the
  342. // superdiagonal test is "bSuper == 0". When 'aggressive' is 'false',
  343. // the superdiagonal test is
  344. // |bDiag0| + |bDiag1| + |bSuper| == |bDiag0| + |bDiag1|
  345. // which means bSuper is effectively zero compared to the sizes of the
  346. // diagonal entries.
  347. bool Converged(bool aggressive, Real bDiag0, Real bDiag1, Real bSuper) const
  348. {
  349. if (aggressive)
  350. {
  351. return bSuper == (Real)0;
  352. }
  353. else
  354. {
  355. Real sum = std::fabs(bDiag0) + std::fabs(bDiag1);
  356. return sum + std::fabs(bSuper) == sum;
  357. }
  358. }
  359. };
  360. template <typename Real>
  361. class NISymmetricEigensolver3x3
  362. {
  363. public:
  364. // The input matrix must be symmetric, so only the unique elements
  365. // must be specified: a00, a01, a02, a11, a12, and a22. The
  366. // eigenvalues are sorted in ascending order: eval0 <= eval1 <= eval2.
  367. void operator()(Real a00, Real a01, Real a02, Real a11, Real a12, Real a22,
  368. int sortType, std::array<Real, 3>& eval, std::array<std::array<Real, 3>, 3>& evec) const
  369. {
  370. // Precondition the matrix by factoring out the maximum absolute
  371. // value of the components. This guards against floating-point
  372. // overflow when computing the eigenvalues.
  373. Real max0 = std::max(std::fabs(a00), std::fabs(a01));
  374. Real max1 = std::max(std::fabs(a02), std::fabs(a11));
  375. Real max2 = std::max(std::fabs(a12), std::fabs(a22));
  376. Real maxAbsElement = std::max(std::max(max0, max1), max2);
  377. if (maxAbsElement == (Real)0)
  378. {
  379. // A is the zero matrix.
  380. eval[0] = (Real)0;
  381. eval[1] = (Real)0;
  382. eval[2] = (Real)0;
  383. evec[0] = { (Real)1, (Real)0, (Real)0 };
  384. evec[1] = { (Real)0, (Real)1, (Real)0 };
  385. evec[2] = { (Real)0, (Real)0, (Real)1 };
  386. return;
  387. }
  388. Real invMaxAbsElement = (Real)1 / maxAbsElement;
  389. a00 *= invMaxAbsElement;
  390. a01 *= invMaxAbsElement;
  391. a02 *= invMaxAbsElement;
  392. a11 *= invMaxAbsElement;
  393. a12 *= invMaxAbsElement;
  394. a22 *= invMaxAbsElement;
  395. Real norm = a01 * a01 + a02 * a02 + a12 * a12;
  396. if (norm > (Real)0)
  397. {
  398. // Compute the eigenvalues of A.
  399. // In the PDF mentioned previously, B = (A - q*I)/p, where
  400. // q = tr(A)/3 with tr(A) the trace of A (sum of the diagonal
  401. // entries of A) and where p = sqrt(tr((A - q*I)^2)/6).
  402. Real q = (a00 + a11 + a22) / (Real)3;
  403. // The matrix A - q*I is represented by the following, where
  404. // b00, b11 and b22 are computed after these comments,
  405. // +- -+
  406. // | b00 a01 a02 |
  407. // | a01 b11 a12 |
  408. // | a02 a12 b22 |
  409. // +- -+
  410. Real b00 = a00 - q;
  411. Real b11 = a11 - q;
  412. Real b22 = a22 - q;
  413. // The is the variable p mentioned in the PDF.
  414. Real p = std::sqrt((b00 * b00 + b11 * b11 + b22 * b22 + norm * (Real)2) / (Real)6);
  415. // We need det(B) = det((A - q*I)/p) = det(A - q*I)/p^3. The
  416. // value det(A - q*I) is computed using a cofactor expansion
  417. // by the first row of A - q*I. The cofactors are c00, c01
  418. // and c02 and the determinant is b00*c00 - a01*c01 + a02*c02.
  419. // The det(B) is then computed finally by the division
  420. // with p^3.
  421. Real c00 = b11 * b22 - a12 * a12;
  422. Real c01 = a01 * b22 - a12 * a02;
  423. Real c02 = a01 * a12 - b11 * a02;
  424. Real det = (b00 * c00 - a01 * c01 + a02 * c02) / (p * p * p);
  425. // The halfDet value is cos(3*theta) mentioned in the PDF. The
  426. // acos(z) function requires |z| <= 1, but will fail silently
  427. // and return NaN if the input is larger than 1 in magnitude.
  428. // To avoid this problem due to rounding errors, the halfDet
  429. // value is clamped to [-1,1].
  430. Real halfDet = det * (Real)0.5;
  431. halfDet = std::min(std::max(halfDet, (Real)-1), (Real)1);
  432. // The eigenvalues of B are ordered as
  433. // beta0 <= beta1 <= beta2. The number of digits in
  434. // twoThirdsPi is chosen so that, whether float or double,
  435. // the floating-point number is the closest to theoretical
  436. // 2*pi/3.
  437. Real angle = std::acos(halfDet) / (Real)3;
  438. Real const twoThirdsPi = (Real)2.09439510239319549;
  439. Real beta2 = std::cos(angle) * (Real)2;
  440. Real beta0 = std::cos(angle + twoThirdsPi) * (Real)2;
  441. Real beta1 = -(beta0 + beta2);
  442. // The eigenvalues of A are ordered as
  443. // alpha0 <= alpha1 <= alpha2.
  444. eval[0] = q + p * beta0;
  445. eval[1] = q + p * beta1;
  446. eval[2] = q + p * beta2;
  447. // Compute the eigenvectors so that the set
  448. // {evec[0], evec[1], evec[2]} is right handed and
  449. // orthonormal.
  450. if (halfDet >= (Real)0)
  451. {
  452. ComputeEigenvector0(a00, a01, a02, a11, a12, a22, eval[2], evec[2]);
  453. ComputeEigenvector1(a00, a01, a02, a11, a12, a22, evec[2], eval[1], evec[1]);
  454. evec[0] = Cross(evec[1], evec[2]);
  455. }
  456. else
  457. {
  458. ComputeEigenvector0(a00, a01, a02, a11, a12, a22, eval[0], evec[0]);
  459. ComputeEigenvector1(a00, a01, a02, a11, a12, a22, evec[0], eval[1], evec[1]);
  460. evec[2] = Cross(evec[0], evec[1]);
  461. }
  462. }
  463. else
  464. {
  465. // The matrix is diagonal.
  466. eval[0] = a00;
  467. eval[1] = a11;
  468. eval[2] = a22;
  469. evec[0] = { (Real)1, (Real)0, (Real)0 };
  470. evec[1] = { (Real)0, (Real)1, (Real)0 };
  471. evec[2] = { (Real)0, (Real)0, (Real)1 };
  472. }
  473. // The preconditioning scaled the matrix A, which scales the
  474. // eigenvalues. Revert the scaling.
  475. eval[0] *= maxAbsElement;
  476. eval[1] *= maxAbsElement;
  477. eval[2] *= maxAbsElement;
  478. SortEigenstuff<Real>()(sortType, true, eval, evec);
  479. }
  480. private:
  481. static std::array<Real, 3> Multiply(Real s, std::array<Real, 3> const& U)
  482. {
  483. std::array<Real, 3> product = { s * U[0], s * U[1], s * U[2] };
  484. return product;
  485. }
  486. static std::array<Real, 3> Subtract(std::array<Real, 3> const& U, std::array<Real, 3> const& V)
  487. {
  488. std::array<Real, 3> difference = { U[0] - V[0], U[1] - V[1], U[2] - V[2] };
  489. return difference;
  490. }
  491. static std::array<Real, 3> Divide(std::array<Real, 3> const& U, Real s)
  492. {
  493. Real invS = (Real)1 / s;
  494. std::array<Real, 3> division = { U[0] * invS, U[1] * invS, U[2] * invS };
  495. return division;
  496. }
  497. static Real Dot(std::array<Real, 3> const& U, std::array<Real, 3> const& V)
  498. {
  499. Real dot = U[0] * V[0] + U[1] * V[1] + U[2] * V[2];
  500. return dot;
  501. }
  502. static std::array<Real, 3> Cross(std::array<Real, 3> const& U, std::array<Real, 3> const& V)
  503. {
  504. std::array<Real, 3> cross =
  505. {
  506. U[1] * V[2] - U[2] * V[1],
  507. U[2] * V[0] - U[0] * V[2],
  508. U[0] * V[1] - U[1] * V[0]
  509. };
  510. return cross;
  511. }
  512. void ComputeOrthogonalComplement(std::array<Real, 3> const& W,
  513. std::array<Real, 3>& U, std::array<Real, 3>& V) const
  514. {
  515. // Robustly compute a right-handed orthonormal set { U, V, W }.
  516. // The vector W is guaranteed to be unit-length, in which case
  517. // there is no need to worry about a division by zero when
  518. // computing invLength.
  519. Real invLength;
  520. if (std::fabs(W[0]) > std::fabs(W[1]))
  521. {
  522. // The component of maximum absolute value is either W[0]
  523. // or W[2].
  524. invLength = (Real)1 / std::sqrt(W[0] * W[0] + W[2] * W[2]);
  525. U = { -W[2] * invLength, (Real)0, +W[0] * invLength };
  526. }
  527. else
  528. {
  529. // The component of maximum absolute value is either W[1]
  530. // or W[2].
  531. invLength = (Real)1 / std::sqrt(W[1] * W[1] + W[2] * W[2]);
  532. U = { (Real)0, +W[2] * invLength, -W[1] * invLength };
  533. }
  534. V = Cross(W, U);
  535. }
  536. void ComputeEigenvector0(Real a00, Real a01, Real a02, Real a11, Real a12, Real a22,
  537. Real eval0, std::array<Real, 3>& evec0) const
  538. {
  539. // Compute a unit-length eigenvector for eigenvalue[i0]. The
  540. // matrix is rank 2, so two of the rows are linearly independent.
  541. // For a robust computation of the eigenvector, select the two
  542. // rows whose cross product has largest length of all pairs of
  543. // rows.
  544. std::array<Real, 3> row0 = { a00 - eval0, a01, a02 };
  545. std::array<Real, 3> row1 = { a01, a11 - eval0, a12 };
  546. std::array<Real, 3> row2 = { a02, a12, a22 - eval0 };
  547. std::array<Real, 3> r0xr1 = Cross(row0, row1);
  548. std::array<Real, 3> r0xr2 = Cross(row0, row2);
  549. std::array<Real, 3> r1xr2 = Cross(row1, row2);
  550. Real d0 = Dot(r0xr1, r0xr1);
  551. Real d1 = Dot(r0xr2, r0xr2);
  552. Real d2 = Dot(r1xr2, r1xr2);
  553. Real dmax = d0;
  554. int imax = 0;
  555. if (d1 > dmax)
  556. {
  557. dmax = d1;
  558. imax = 1;
  559. }
  560. if (d2 > dmax)
  561. {
  562. imax = 2;
  563. }
  564. if (imax == 0)
  565. {
  566. evec0 = Divide(r0xr1, std::sqrt(d0));
  567. }
  568. else if (imax == 1)
  569. {
  570. evec0 = Divide(r0xr2, std::sqrt(d1));
  571. }
  572. else
  573. {
  574. evec0 = Divide(r1xr2, std::sqrt(d2));
  575. }
  576. }
  577. void ComputeEigenvector1(Real a00, Real a01, Real a02, Real a11, Real a12, Real a22,
  578. std::array<Real, 3> const& evec0, Real eval1, std::array<Real, 3>& evec1) const
  579. {
  580. // Robustly compute a right-handed orthonormal set
  581. // { U, V, evec0 }.
  582. std::array<Real, 3> U, V;
  583. ComputeOrthogonalComplement(evec0, U, V);
  584. // Let e be eval1 and let E be a corresponding eigenvector which
  585. // is a solution to the linear system (A - e*I)*E = 0. The matrix
  586. // (A - e*I) is 3x3, not invertible (so infinitely many
  587. // solutions), and has rank 2 when eval1 and eval are different.
  588. // It has rank 1 when eval1 and eval2 are equal. Numerically, it
  589. // is difficult to compute robustly the rank of a matrix. Instead,
  590. // the 3x3 linear system is reduced to a 2x2 system as follows.
  591. // Define the 3x2 matrix J = [U V] whose columns are the U and V
  592. // computed previously. Define the 2x1 vector X = J*E. The 2x2
  593. // system is 0 = M * X = (J^T * (A - e*I) * J) * X where J^T is
  594. // the transpose of J and M = J^T * (A - e*I) * J is a 2x2 matrix.
  595. // The system may be written as
  596. // +- -++- -+ +- -+
  597. // | U^T*A*U - e U^T*A*V || x0 | = e * | x0 |
  598. // | V^T*A*U V^T*A*V - e || x1 | | x1 |
  599. // +- -++ -+ +- -+
  600. // where X has row entries x0 and x1.
  601. std::array<Real, 3> AU =
  602. {
  603. a00 * U[0] + a01 * U[1] + a02 * U[2],
  604. a01 * U[0] + a11 * U[1] + a12 * U[2],
  605. a02 * U[0] + a12 * U[1] + a22 * U[2]
  606. };
  607. std::array<Real, 3> AV =
  608. {
  609. a00 * V[0] + a01 * V[1] + a02 * V[2],
  610. a01 * V[0] + a11 * V[1] + a12 * V[2],
  611. a02 * V[0] + a12 * V[1] + a22 * V[2]
  612. };
  613. Real m00 = U[0] * AU[0] + U[1] * AU[1] + U[2] * AU[2] - eval1;
  614. Real m01 = U[0] * AV[0] + U[1] * AV[1] + U[2] * AV[2];
  615. Real m11 = V[0] * AV[0] + V[1] * AV[1] + V[2] * AV[2] - eval1;
  616. // For robustness, choose the largest-length row of M to compute
  617. // the eigenvector. The 2-tuple of coefficients of U and V in the
  618. // assignments to eigenvector[1] lies on a circle, and U and V are
  619. // unit length and perpendicular, so eigenvector[1] is unit length
  620. // (within numerical tolerance).
  621. Real absM00 = std::fabs(m00);
  622. Real absM01 = std::fabs(m01);
  623. Real absM11 = std::fabs(m11);
  624. Real maxAbsComp;
  625. if (absM00 >= absM11)
  626. {
  627. maxAbsComp = std::max(absM00, absM01);
  628. if (maxAbsComp > (Real)0)
  629. {
  630. if (absM00 >= absM01)
  631. {
  632. m01 /= m00;
  633. m00 = (Real)1 / std::sqrt((Real)1 + m01 * m01);
  634. m01 *= m00;
  635. }
  636. else
  637. {
  638. m00 /= m01;
  639. m01 = (Real)1 / std::sqrt((Real)1 + m00 * m00);
  640. m00 *= m01;
  641. }
  642. evec1 = Subtract(Multiply(m01, U), Multiply(m00, V));
  643. }
  644. else
  645. {
  646. evec1 = U;
  647. }
  648. }
  649. else
  650. {
  651. maxAbsComp = std::max(absM11, absM01);
  652. if (maxAbsComp > (Real)0)
  653. {
  654. if (absM11 >= absM01)
  655. {
  656. m01 /= m11;
  657. m11 = (Real)1 / std::sqrt((Real)1 + m01 * m01);
  658. m01 *= m11;
  659. }
  660. else
  661. {
  662. m11 /= m01;
  663. m01 = (Real)1 / std::sqrt((Real)1 + m11 * m11);
  664. m11 *= m01;
  665. }
  666. evec1 = Subtract(Multiply(m11, U), Multiply(m01, V));
  667. }
  668. else
  669. {
  670. evec1 = U;
  671. }
  672. }
  673. }
  674. };
  675. }