ApprSphere3.h 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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/Hypersphere.h>
  9. #include <Mathematics/Vector3.h>
  10. // Least-squares fit of a sphere to a set of points. The algorithms are
  11. // described in Section 5 of
  12. // https://www.geometrictools.com/Documentation/LeastSquaresFitting.pdf
  13. // FitUsingLengths uses the algorithm of Section 5.1.
  14. // FitUsingSquaredLengths uses the algorithm of Section 5.2.
  15. namespace WwiseGTE
  16. {
  17. template <typename Real>
  18. class ApprSphere3
  19. {
  20. public:
  21. // The return value is 'true' when the linear system of the algorithm
  22. // is solvable, 'false' otherwise. If 'false' is returned, the sphere
  23. // center and radius are set to zero values.
  24. bool FitUsingSquaredLengths(int numPoints, Vector3<Real> const* points, Sphere3<Real>& sphere)
  25. {
  26. // Compute the average of the data points.
  27. Real const zero(0);
  28. Vector3<Real> A = { zero, zero, zero };
  29. for (int i = 0; i < numPoints; ++i)
  30. {
  31. A += points[i];
  32. }
  33. Real invNumPoints = ((Real)1) / static_cast<Real>(numPoints);
  34. A *= invNumPoints;
  35. // Compute the covariance matrix M of the Y[i] = X[i]-A and the
  36. // right-hand side R of the linear system M*(C-A) = R.
  37. Real M00 = zero, M01 = zero, M02 = zero, M11 = zero, M12 = zero, M22 = zero;
  38. Vector3<Real> R = { zero, zero, zero };
  39. for (int i = 0; i < numPoints; ++i)
  40. {
  41. Vector3<Real> Y = points[i] - A;
  42. Real Y0Y0 = Y[0] * Y[0];
  43. Real Y0Y1 = Y[0] * Y[1];
  44. Real Y0Y2 = Y[0] * Y[2];
  45. Real Y1Y1 = Y[1] * Y[1];
  46. Real Y1Y2 = Y[1] * Y[2];
  47. Real Y2Y2 = Y[2] * Y[2];
  48. M00 += Y0Y0;
  49. M01 += Y0Y1;
  50. M02 += Y0Y2;
  51. M11 += Y1Y1;
  52. M12 += Y1Y2;
  53. M22 += Y2Y2;
  54. R += (Y0Y0 + Y1Y1 + Y2Y2) * Y;
  55. }
  56. R *= (Real)0.5;
  57. // Solve the linear system M*(C-A) = R for the center C.
  58. Real cof00 = M11 * M22 - M12 * M12;
  59. Real cof01 = M02 * M12 - M01 * M22;
  60. Real cof02 = M01 * M12 - M02 * M11;
  61. Real det = M00 * cof00 + M01 * cof01 + M02 * cof02;
  62. if (det != zero)
  63. {
  64. Real cof11 = M00 * M22 - M02 * M02;
  65. Real cof12 = M01 * M02 - M00 * M12;
  66. Real cof22 = M00 * M11 - M01 * M01;
  67. sphere.center[0] = A[0] + (cof00 * R[0] + cof01 * R[1] + cof02 * R[2]) / det;
  68. sphere.center[1] = A[1] + (cof01 * R[0] + cof11 * R[1] + cof12 * R[2]) / det;
  69. sphere.center[2] = A[2] + (cof02 * R[0] + cof12 * R[1] + cof22 * R[2]) / det;
  70. Real rsqr = zero;
  71. for (int i = 0; i < numPoints; ++i)
  72. {
  73. Vector3<Real> delta = points[i] - sphere.center;
  74. rsqr += Dot(delta, delta);
  75. }
  76. rsqr *= invNumPoints;
  77. sphere.radius = std::sqrt(rsqr);
  78. return true;
  79. }
  80. else
  81. {
  82. sphere.center = { zero, zero, zero };
  83. sphere.radius = zero;
  84. return false;
  85. }
  86. }
  87. // Fit the points using lengths to drive the least-squares algorithm.
  88. // If initialCenterIsAverage is set to 'false', the initial guess for
  89. // the initial sphere center is computed as the average of the data
  90. // points. If the data points are clustered along a small solid angle,
  91. // the algorithm is slow to converge. If initialCenterIsAverage is set
  92. // to 'true', the incoming sphere center is used as-is to start the
  93. // iterative algorithm. This approach tends to converge more rapidly
  94. // than when using the average of points but can be much slower than
  95. // FitUsingSquaredLengths.
  96. //
  97. // The value epsilon may be chosen as a positive number for the
  98. // comparison of consecutive estimated sphere centers, terminating the
  99. // iterations when the center difference has length less than or equal
  100. // to epsilon.
  101. //
  102. // The return value is the number of iterations used. If is is the
  103. // input maxIterations, you can either accept the result or polish the
  104. // result by calling the function again with initialCenterIsAverage
  105. // set to 'true'.
  106. unsigned int FitUsingLengths(int numPoints, Vector3<Real> const* points,
  107. unsigned int maxIterations, bool initialCenterIsAverage,
  108. Sphere3<Real>& sphere, Real epsilon = (Real)0)
  109. {
  110. // Compute the average of the data points.
  111. Vector3<Real> average = points[0];
  112. for (int i = 1; i < numPoints; ++i)
  113. {
  114. average += points[i];
  115. }
  116. Real invNumPoints = ((Real)1) / static_cast<Real>(numPoints);
  117. average *= invNumPoints;
  118. // The initial guess for the center.
  119. if (initialCenterIsAverage)
  120. {
  121. sphere.center = average;
  122. }
  123. Real epsilonSqr = epsilon * epsilon;
  124. unsigned int iteration;
  125. for (iteration = 0; iteration < maxIterations; ++iteration)
  126. {
  127. // Update the iterates.
  128. Vector3<Real> current = sphere.center;
  129. // Compute average L, dL/da, dL/db, dL/dc.
  130. Real lenAverage = (Real)0;
  131. Vector3<Real> derLenAverage = Vector3<Real>::Zero();
  132. for (int i = 0; i < numPoints; ++i)
  133. {
  134. Vector3<Real> diff = points[i] - sphere.center;
  135. Real length = Length(diff);
  136. if (length > (Real)0)
  137. {
  138. lenAverage += length;
  139. Real invLength = ((Real)1) / length;
  140. derLenAverage -= invLength * diff;
  141. }
  142. }
  143. lenAverage *= invNumPoints;
  144. derLenAverage *= invNumPoints;
  145. sphere.center = average + lenAverage * derLenAverage;
  146. sphere.radius = lenAverage;
  147. Vector3<Real> diff = sphere.center - current;
  148. Real diffSqrLen = Dot(diff, diff);
  149. if (diffSqrLen <= epsilonSqr)
  150. {
  151. break;
  152. }
  153. }
  154. return ++iteration;
  155. }
  156. };
  157. }