ApprCircle2.h 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  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/Vector2.h>
  10. // Least-squares fit of a circle 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 ApprCircle2
  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 circle
  23. // center and radius are set to zero values.
  24. bool FitUsingSquaredLengths(int numPoints, Vector2<Real> const* points, Circle2<Real>& circle)
  25. {
  26. // Compute the average of the data points.
  27. Real const zero(0);
  28. Vector2<Real> A = { 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, M11 = zero;
  38. Vector2<Real> R = { zero, zero };
  39. for (int i = 0; i < numPoints; ++i)
  40. {
  41. Vector2<Real> Y = points[i] - A;
  42. Real Y0Y0 = Y[0] * Y[0];
  43. Real Y0Y1 = Y[0] * Y[1];
  44. Real Y1Y1 = Y[1] * Y[1];
  45. M00 += Y0Y0;
  46. M01 += Y0Y1;
  47. M11 += Y1Y1;
  48. R += (Y0Y0 + Y1Y1) * Y;
  49. }
  50. R *= (Real)0.5;
  51. // Solve the linear system M*(C-A) = R for the center C.
  52. Real det = M00 * M11 - M01 * M01;
  53. if (det != zero)
  54. {
  55. circle.center[0] = A[0] + (M11 * R[0] - M01 * R[1]) / det;
  56. circle.center[1] = A[1] + (M00 * R[1] - M01 * R[0]) / det;
  57. Real rsqr = zero;
  58. for (int i = 0; i < numPoints; ++i)
  59. {
  60. Vector2<Real> delta = points[i] - circle.center;
  61. rsqr += Dot(delta, delta);
  62. }
  63. rsqr *= invNumPoints;
  64. circle.radius = std::sqrt(rsqr);
  65. return true;
  66. }
  67. else
  68. {
  69. circle.center = { zero, zero };
  70. circle.radius = zero;
  71. return false;
  72. }
  73. }
  74. // Fit the points using lengths to drive the least-squares algorithm.
  75. // If initialCenterIsAverage is set to 'false', the initial guess for
  76. // the initial circle center is computed as the average of the data
  77. // points. If the data points are clustered along a small arc, the
  78. // algorithm is slow to converge. If initialCenterIsAverage is set to
  79. // 'true', the incoming circle center is used as-is to start the
  80. // iterative algorithm. This approach tends to converge more rapidly
  81. // than when using the average of points but can be much slower than
  82. // FitUsingSquaredLengths.
  83. //
  84. // The value epsilon may be chosen as a positive number for the
  85. // comparison of consecutive estimated circle centers, terminating the
  86. // iterations when the center difference has length less than or equal
  87. // to epsilon.
  88. //
  89. // The return value is the number of iterations used. If is is the
  90. // input maxIterations, you can either accept the result or polish the
  91. // result by calling the function again with initialCenterIsAverage
  92. // set to 'true'.
  93. unsigned int FitUsingLengths(int numPoints, Vector2<Real> const* points,
  94. unsigned int maxIterations, bool initialCenterIsAverage,
  95. Circle2<Real>& circle, Real epsilon = (Real)0)
  96. {
  97. // Compute the average of the data points.
  98. Vector2<Real> average = points[0];
  99. for (int i = 1; i < numPoints; ++i)
  100. {
  101. average += points[i];
  102. }
  103. Real invNumPoints = ((Real)1) / static_cast<Real>(numPoints);
  104. average *= invNumPoints;
  105. // The initial guess for the center.
  106. if (initialCenterIsAverage)
  107. {
  108. circle.center = average;
  109. }
  110. Real epsilonSqr = epsilon * epsilon;
  111. unsigned int iteration;
  112. for (iteration = 0; iteration < maxIterations; ++iteration)
  113. {
  114. // Update the iterates.
  115. Vector2<Real> current = circle.center;
  116. // Compute average L, dL/da, dL/db.
  117. Real lenAverage = (Real)0;
  118. Vector2<Real> derLenAverage = Vector2<Real>::Zero();
  119. for (int i = 0; i < numPoints; ++i)
  120. {
  121. Vector2<Real> diff = points[i] - circle.center;
  122. Real length = Length(diff);
  123. if (length > (Real)0)
  124. {
  125. lenAverage += length;
  126. Real invLength = ((Real)1) / length;
  127. derLenAverage -= invLength * diff;
  128. }
  129. }
  130. lenAverage *= invNumPoints;
  131. derLenAverage *= invNumPoints;
  132. circle.center = average + lenAverage * derLenAverage;
  133. circle.radius = lenAverage;
  134. Vector2<Real> diff = circle.center - current;
  135. Real diffSqrLen = Dot(diff, diff);
  136. if (diffSqrLen <= epsilonSqr)
  137. {
  138. break;
  139. }
  140. }
  141. return ++iteration;
  142. }
  143. };
  144. }