ApprQuadratic2.h 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  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/Matrix.h>
  9. #include <Mathematics/Vector2.h>
  10. #include <Mathematics/Hypersphere.h>
  11. #include <Mathematics/SymmetricEigensolver.h>
  12. namespace WwiseGTE
  13. {
  14. // The quadratic fit is
  15. // 0 = C[0] + C[1]*X + C[2]*Y + C[3]*X^2 + C[4]*Y^2 + C[5]*X*Y
  16. // subject to Length(C) = 1. Minimize E(C) = C^t M C with Length(C) = 1
  17. // and M = (sum_i V_i)(sum_i V_i)^t where
  18. // V = (1, X, Y, X^2, Y^2, X*Y)
  19. // The minimum value is the smallest eigenvalue of M and C is a
  20. // corresponding unit length eigenvector.
  21. //
  22. // Input:
  23. // n = number of points to fit
  24. // p[0..n-1] = array of points to fit
  25. //
  26. // Output:
  27. // c[0..5] = coefficients of quadratic fit (the eigenvector)
  28. // return value of function is nonnegative and a measure of the fit
  29. // (the minimum eigenvalue; 0 = exact fit, positive otherwise)
  30. //
  31. // Canonical forms. The quadratic equation can be factored into
  32. // P^T A P + B^T P + K = 0 where P = (X,Y,Z), K = C[0],
  33. // B = (C[1],C[2],C[3]), and A is a 3x3 symmetric matrix with
  34. // A00 = C[4], A11 = C[5], A22 = C[6], A01 = C[7]/2, A02 = C[8]/2,
  35. // and A12 = C[9]/2. Matrix A = R^T D R where R is orthogonal and
  36. // D is diagonal (using an eigendecomposition). Define
  37. // V = R P = (v0,v1,v2), E = R B = (e0,e1,e2), D = diag(d0,d1,d2)
  38. // and f = K to obtain
  39. // d0 v0^2 + d1 v1^2 + d2 v^2 + e0 v0 + e1 v1 + e2 v2 + f = 0
  40. // The characterization depends on the signs of the d_i.
  41. template <typename Real>
  42. class ApprQuadratic2
  43. {
  44. public:
  45. Real operator()(int numPoints, Vector2<Real> const* points, Real coefficients[6])
  46. {
  47. Matrix<6, 6, Real> A; // constructor sets A to zero
  48. for (int i = 0; i < numPoints; ++i)
  49. {
  50. Real x = points[i][0];
  51. Real y = points[i][1];
  52. Real x2 = x * x;
  53. Real y2 = y * y;
  54. Real xy = x * y;
  55. Real x3 = x * x2;
  56. Real xy2 = x * y2;
  57. Real x2y = x * xy;
  58. Real y3 = y * y2;
  59. Real x4 = x * x3;
  60. Real x2y2 = x * xy2;
  61. Real x3y = x * x2y;
  62. Real y4 = y * y3;
  63. Real xy3 = x * y3;
  64. A(0, 1) += x;
  65. A(0, 2) += y;
  66. A(0, 3) += x2;
  67. A(0, 4) += y2;
  68. A(0, 5) += xy;
  69. A(1, 3) += x3;
  70. A(1, 4) += xy2;
  71. A(1, 5) += x2y;
  72. A(2, 4) += y3;
  73. A(3, 3) += x4;
  74. A(3, 4) += x2y2;
  75. A(3, 5) += x3y;
  76. A(4, 4) += y4;
  77. A(4, 5) += xy3;
  78. }
  79. A(0, 0) = static_cast<Real>(numPoints);
  80. A(1, 1) = A(0, 3);
  81. A(1, 2) = A(0, 5);
  82. A(2, 2) = A(0, 4);
  83. A(2, 3) = A(1, 5);
  84. A(2, 5) = A(1, 4);
  85. A(5, 5) = A(3, 4);
  86. for (int row = 0; row < 6; ++row)
  87. {
  88. for (int col = 0; col < row; ++col)
  89. {
  90. A(row, col) = A(col, row);
  91. }
  92. }
  93. Real invNumPoints = (Real)1 / static_cast<Real>(numPoints);
  94. for (int row = 0; row < 6; ++row)
  95. {
  96. for (int col = 0; col < 6; ++col)
  97. {
  98. A(row, col) *= invNumPoints;
  99. }
  100. }
  101. SymmetricEigensolver<Real> es(6, 1024);
  102. es.Solve(&A[0], +1);
  103. es.GetEigenvector(0, &coefficients[0]);
  104. // For an exact fit, numeric round-off errors might make the
  105. // minimum eigenvalue just slightly negative. Return the
  106. // absolute value because the application might rely on the
  107. // return value being nonnegative.
  108. return std::fabs(es.GetEigenvalue(0));
  109. }
  110. };
  111. // If you think your points are nearly circular, use this. The circle is
  112. // of the form C'[0]+C'[1]*X+C'[2]*Y+C'[3]*(X^2+Y^2), where
  113. // Length(C') = 1. The function returns
  114. // C = (C'[0]/C'[3],C'[1]/C'[3],C'[2]/C'[3]), so the fitted circle is
  115. // C[0]+C[1]*X+C[2]*Y+X^2+Y^2. The center is (xc,yc) = -0.5*(C[1],C[2])
  116. // and the radius is r = sqrt(xc*xc+yc*yc-C[0]).
  117. template <typename Real>
  118. class ApprQuadraticCircle2
  119. {
  120. public:
  121. Real operator()(int numPoints, Vector2<Real> const* points, Circle2<Real>& circle)
  122. {
  123. Matrix<4, 4, Real> A; // constructor sets A to zero
  124. for (int i = 0; i < numPoints; ++i)
  125. {
  126. Real x = points[i][0];
  127. Real y = points[i][1];
  128. Real x2 = x * x;
  129. Real y2 = y * y;
  130. Real xy = x * y;
  131. Real r2 = x2 + y2;
  132. Real xr2 = x * r2;
  133. Real yr2 = y * r2;
  134. Real r4 = r2 * r2;
  135. A(0, 1) += x;
  136. A(0, 2) += y;
  137. A(0, 3) += r2;
  138. A(1, 1) += x2;
  139. A(1, 2) += xy;
  140. A(1, 3) += xr2;
  141. A(2, 2) += y2;
  142. A(2, 3) += yr2;
  143. A(3, 3) += r4;
  144. }
  145. A(0, 0) = static_cast<Real>(numPoints);
  146. for (int row = 0; row < 4; ++row)
  147. {
  148. for (int col = 0; col < row; ++col)
  149. {
  150. A(row, col) = A(col, row);
  151. }
  152. }
  153. Real invNumPoints = (Real)1 / static_cast<Real>(numPoints);
  154. for (int row = 0; row < 4; ++row)
  155. {
  156. for (int col = 0; col < 4; ++col)
  157. {
  158. A(row, col) *= invNumPoints;
  159. }
  160. }
  161. SymmetricEigensolver<Real> es(4, 1024);
  162. es.Solve(&A[0], +1);
  163. Vector<4, Real> evector;
  164. es.GetEigenvector(0, &evector[0]);
  165. // TODO: Guard against zero divide?
  166. Real inv = (Real)1 / evector[3];
  167. Real coefficients[3];
  168. for (int row = 0; row < 3; ++row)
  169. {
  170. coefficients[row] = inv * evector[row];
  171. }
  172. circle.center[0] = (Real)-0.5 * coefficients[1];
  173. circle.center[1] = (Real)-0.5 * coefficients[2];
  174. circle.radius = std::sqrt(std::fabs(Dot(circle.center, circle.center) - coefficients[0]));
  175. // For an exact fit, numeric round-off errors might make the
  176. // minimum eigenvalue just slightly negative. Return the
  177. // absolute value because the application might rely on the
  178. // return value being nonnegative.
  179. return std::fabs(es.GetEigenvalue(0));
  180. }
  181. };
  182. }