ApprGreatCircle3.h 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  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/Matrix3x3.h>
  9. #include <Mathematics/SymmetricEigensolver3x3.h>
  10. namespace WwiseGTE
  11. {
  12. // Least-squares fit of a great circle to unit-length vectors (x,y,z) by
  13. // using distance measurements orthogonal (and measured along great
  14. // circles) to the proposed great circle. The inputs akPoint[] are unit
  15. // length. The returned value is unit length, call it N. The fitted
  16. // great circle is defined by Dot(N,X) = 0, where X is a unit-length
  17. // vector on the great circle.
  18. template <typename Real>
  19. class ApprGreatCircle3
  20. {
  21. public:
  22. void operator()(int numPoints, Vector3<Real> const* points, Vector3<Real>& normal) const
  23. {
  24. // Compute the covariance matrix of the vectors.
  25. Real covar00 = (Real)0, covar01 = (Real)0, covar02 = (Real)0;
  26. Real covar11 = (Real)0, covar12 = (Real)0, covar22 = (Real)0;
  27. for (int i = 0; i < numPoints; i++)
  28. {
  29. Vector3<Real> diff = points[i];
  30. covar00 += diff[0] * diff[0];
  31. covar01 += diff[0] * diff[1];
  32. covar02 += diff[0] * diff[2];
  33. covar11 += diff[1] * diff[1];
  34. covar12 += diff[1] * diff[2];
  35. covar22 += diff[2] * diff[2];
  36. }
  37. Real invNumPoints = (Real)1 / static_cast<Real>(numPoints);
  38. covar00 *= invNumPoints;
  39. covar01 *= invNumPoints;
  40. covar02 *= invNumPoints;
  41. covar11 *= invNumPoints;
  42. covar12 *= invNumPoints;
  43. covar22 *= invNumPoints;
  44. // Solve the eigensystem.
  45. SymmetricEigensolver3x3<Real> es;
  46. std::array<Real, 3> eval;
  47. std::array<std::array<Real, 3>, 3> evec;
  48. es(covar00, covar01, covar02, covar11, covar12, covar22, false, +1,
  49. eval, evec);
  50. normal = evec[0];
  51. }
  52. };
  53. // In addition to the least-squares fit of a great circle, the input
  54. // vectors are projected onto that circle. The sector of smallest angle
  55. // (possibly obtuse) that contains the points is computed. The endpoints
  56. // of the arc of the sector are returned. The returned endpoints A0 and
  57. // A1 are perpendicular to the returned normal N. Moreover, when you view
  58. // the arc by looking at the plane of the great circle with a view
  59. // direction of -N, the arc is traversed counterclockwise starting at A0
  60. // and ending at A1.
  61. template <typename Real>
  62. class ApprGreatArc3
  63. {
  64. public:
  65. void operator()(int numPoints, Vector3<Real> const* points,
  66. Vector3<Real>& normal, Vector3<Real>& arcEnd0,
  67. Vector3<Real>& arcEnd1) const
  68. {
  69. // Get the least-squares great circle for the vectors. The circle
  70. // is on the plane Dot(N,X) = 0. Generate a basis from N.
  71. Vector3<Real> basis[3]; // { N, U, V }
  72. ApprGreatCircle3<Real>()(numPoints, points, basis[0]);
  73. ComputeOrthogonalComplement(1, basis);
  74. // The vectors are X[i] = u[i]*U + v[i]*V + w[i]*N. The
  75. // projections are
  76. // P[i] = (u[i]*U + v[i]*V)/sqrt(u[i]*u[i] + v[i]*v[i])
  77. // The great circle is parameterized by
  78. // C(t) = cos(t)*U + sin(t)*V
  79. // Compute the angles t in [-pi,pi] for the projections onto the
  80. // great circle. It is not necesarily to normalize (u[i],v[i]),
  81. // instead computing t = atan2(v[i],u[i]). The items[] represents
  82. // (u, v, angle).
  83. std::vector<std::array<Real, 3>> items(numPoints);
  84. for (int i = 0; i < numPoints; ++i)
  85. {
  86. items[i][0] = Dot(basis[1], points[i]);
  87. items[i][1] = Dot(basis[2], points[i]);
  88. items[i][2] = std::atan2(items[i][1], items[i][0]);
  89. }
  90. std::sort(items.begin(), items.end(),
  91. [](std::array<Real, 3> const& item0, std::array<Real, 3> const& item1)
  92. {
  93. return item0[2] < item1[2];
  94. }
  95. );
  96. // Locate the pair of consecutive angles whose difference is a
  97. // maximum. Effectively, we are constructing a cone of minimum
  98. // angle that contains the unit-length vectors.
  99. int numPointsM1 = numPoints - 1;
  100. Real maxDiff = (Real)GTE_C_TWO_PI + items[0][2] - items[numPointsM1][2];
  101. int end0 = 0, end1 = numPointsM1;
  102. for (int i0 = 0, i1 = 1; i0 < numPointsM1; i0 = i1++)
  103. {
  104. Real diff = items[i1][2] - items[i0][2];
  105. if (diff > maxDiff)
  106. {
  107. maxDiff = diff;
  108. end0 = i1;
  109. end1 = i0;
  110. }
  111. }
  112. normal = basis[0];
  113. arcEnd0 = items[end0][0] * basis[1] + items[end0][1] * basis[2];
  114. arcEnd1 = items[end1][0] * basis[1] + items[end1][1] * basis[2];
  115. Normalize(arcEnd0);
  116. Normalize(arcEnd1);
  117. }
  118. };
  119. }