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


geometric_tools_engine
Author(s): Yijiang Huang
autogenerated on Thu Jul 18 2019 04:00:01