ApprQuadratic3.h 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  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/Vector3.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]*Z + C[4]*X^2 + C[5]*Y^2
  16. // + C[6]*Z^2 + C[7]*X*Y + C[8]*X*Z + C[9]*Y*Z
  17. // subject to Length(C) = 1. Minimize E(C) = C^t M C with Length(C) = 1
  18. // and M = (sum_i V_i)(sum_i V_i)^t where
  19. // V = (1, X, Y, Z, X^2, Y^2, Z^2, X*Y, X*Z, Y*Z)
  20. // The minimum value is the smallest eigenvalue of M and C is a
  21. // corresponding unit length eigenvector.
  22. //
  23. // Input:
  24. // n = number of points to fit
  25. // p[0..n-1] = array of points to fit
  26. //
  27. // Output:
  28. // c[0..9] = coefficients of quadratic fit (the eigenvector)
  29. // return value of function is nonnegative and a measure of the fit
  30. // (the minimum eigenvalue; 0 = exact fit, positive otherwise)
  31. //
  32. // Canonical forms. The quadratic equation can be factored into
  33. // P^T A P + B^T P + K = 0 where P = (X,Y,Z), K = C[0],
  34. // B = (C[1],C[2],C[3]), and A is a 3x3 symmetric matrix with
  35. // A00 = C[4], A11 = C[5], A22 = C[6], A01 = C[7]/2, A02 = C[8]/2, and
  36. // A12 = C[9]/2. Matrix A = R^T D R where R is orthogonal and D is
  37. // diagonal (using an eigendecomposition). Define V = R P = (v0,v1,v2),
  38. // E = R B = (e0,e1,e2), D = diag(d0,d1,d2), 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 ApprQuadratic3
  43. {
  44. public:
  45. Real operator()(int numPoints, Vector3<Real> const* points, Real coefficients[10])
  46. {
  47. Matrix<10, 10, 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 z = points[i][2];
  53. Real x2 = x * x;
  54. Real y2 = y * y;
  55. Real z2 = z * z;
  56. Real xy = x * y;
  57. Real xz = x * z;
  58. Real yz = y * z;
  59. Real x3 = x * x2;
  60. Real xy2 = x * y2;
  61. Real xz2 = x * z2;
  62. Real x2y = x * xy;
  63. Real x2z = x * xz;
  64. Real xyz = x * y * z;
  65. Real y3 = y * y2;
  66. Real yz2 = y * z2;
  67. Real y2z = y * yz;
  68. Real z3 = z * z2;
  69. Real x4 = x * x3;
  70. Real x2y2 = x * xy2;
  71. Real x2z2 = x * xz2;
  72. Real x3y = x * x2y;
  73. Real x3z = x * x2z;
  74. Real x2yz = x * xyz;
  75. Real y4 = y * y3;
  76. Real y2z2 = y * yz2;
  77. Real xy3 = x * y3;
  78. Real xy2z = x * y2z;
  79. Real y3z = y * y2z;
  80. Real z4 = z * z3;
  81. Real xyz2 = x * yz2;
  82. Real xz3 = x * z3;
  83. Real yz3 = y * z3;
  84. A(0, 1) += x;
  85. A(0, 2) += y;
  86. A(0, 3) += z;
  87. A(0, 4) += x2;
  88. A(0, 5) += y2;
  89. A(0, 6) += z2;
  90. A(0, 7) += xy;
  91. A(0, 8) += xz;
  92. A(0, 9) += yz;
  93. A(1, 4) += x3;
  94. A(1, 5) += xy2;
  95. A(1, 6) += xz2;
  96. A(1, 7) += x2y;
  97. A(1, 8) += x2z;
  98. A(1, 9) += xyz;
  99. A(2, 5) += y3;
  100. A(2, 6) += yz2;
  101. A(2, 9) += y2z;
  102. A(3, 6) += z3;
  103. A(4, 4) += x4;
  104. A(4, 5) += x2y2;
  105. A(4, 6) += x2z2;
  106. A(4, 7) += x3y;
  107. A(4, 8) += x3z;
  108. A(4, 9) += x2yz;
  109. A(5, 5) += y4;
  110. A(5, 6) += y2z2;
  111. A(5, 7) += xy3;
  112. A(5, 8) += xy2z;
  113. A(5, 9) += y3z;
  114. A(6, 6) += z4;
  115. A(6, 7) += xyz2;
  116. A(6, 8) += xz3;
  117. A(6, 9) += yz3;
  118. A(9, 9) += y2z2;
  119. }
  120. A(0, 0) = static_cast<Real>(numPoints);
  121. A(1, 1) = A(0, 4);
  122. A(1, 2) = A(0, 7);
  123. A(1, 3) = A(0, 8);
  124. A(2, 2) = A(0, 5);
  125. A(2, 3) = A(0, 9);
  126. A(2, 4) = A(1, 7);
  127. A(2, 7) = A(1, 5);
  128. A(2, 8) = A(1, 9);
  129. A(3, 3) = A(0, 6);
  130. A(3, 4) = A(1, 8);
  131. A(3, 5) = A(2, 9);
  132. A(3, 7) = A(1, 9);
  133. A(3, 8) = A(1, 6);
  134. A(3, 9) = A(2, 6);
  135. A(7, 7) = A(4, 5);
  136. A(7, 8) = A(4, 9);
  137. A(7, 9) = A(5, 8);
  138. A(8, 8) = A(4, 6);
  139. A(8, 9) = A(6, 7);
  140. A(9, 9) = A(5, 6);
  141. for (int row = 0; row < 10; ++row)
  142. {
  143. for (int col = 0; col < row; ++col)
  144. {
  145. A(row, col) = A(col, row);
  146. }
  147. }
  148. Real invNumPoints = (Real)1 / static_cast<Real>(numPoints);
  149. for (int row = 0; row < 10; ++row)
  150. {
  151. for (int col = 0; col < 10; ++col)
  152. {
  153. A(row, col) *= invNumPoints;
  154. }
  155. }
  156. SymmetricEigensolver<Real> es(10, 1024);
  157. es.Solve(&A[0], +1);
  158. es.GetEigenvector(0, &coefficients[0]);
  159. // For an exact fit, numeric round-off errors might make the
  160. // minimum eigenvalue just slightly negative. Return the absolute
  161. // value because the application might rely on the return value
  162. // being nonnegative.
  163. return std::fabs(es.GetEigenvalue(0));
  164. }
  165. };
  166. // If you think your points are nearly spherical, use this. The sphere is
  167. // of form C'[0]+C'[1]*X+C'[2]*Y+C'[3]*Z+C'[4]*(X^2+Y^2+Z^2) where
  168. // Length(C') = 1. The function returns
  169. // C = (C'[0]/C'[4],C'[1]/C'[4],C'[2]/C'[4],C'[3]/C'[4]), so the fitted
  170. // sphere is C[0]+C[1]*X+C[2]*Y+C[3]*Z+X^2+Y^2+Z^2. The center is
  171. // (xc,yc,zc) = -0.5*(C[1],C[2],C[3]) and the radius is
  172. // r = sqrt(xc*xc+yc*yc+zc*zc-C[0]).
  173. template <typename Real>
  174. class ApprQuadraticSphere3
  175. {
  176. public:
  177. Real operator()(int numPoints, Vector3<Real> const* points, Sphere3<Real>& sphere)
  178. {
  179. Matrix<5, 5, Real> A; // constructor sets A to zero
  180. for (int i = 0; i < numPoints; ++i)
  181. {
  182. Real x = points[i][0];
  183. Real y = points[i][1];
  184. Real z = points[i][2];
  185. Real x2 = x * x;
  186. Real y2 = y * y;
  187. Real z2 = z * z;
  188. Real xy = x * y;
  189. Real xz = x * z;
  190. Real yz = y * z;
  191. Real r2 = x2 + y2 + z2;
  192. Real xr2 = x * r2;
  193. Real yr2 = y * r2;
  194. Real zr2 = z * r2;
  195. Real r4 = r2 * r2;
  196. A(0, 1) += x;
  197. A(0, 2) += y;
  198. A(0, 3) += z;
  199. A(0, 4) += r2;
  200. A(1, 1) += x2;
  201. A(1, 2) += xy;
  202. A(1, 3) += xz;
  203. A(1, 4) += xr2;
  204. A(2, 2) += y2;
  205. A(2, 3) += yz;
  206. A(2, 4) += yr2;
  207. A(3, 3) += z2;
  208. A(3, 4) += zr2;
  209. A(4, 4) += r4;
  210. }
  211. A(0, 0) = static_cast<Real>(numPoints);
  212. for (int row = 0; row < 5; ++row)
  213. {
  214. for (int col = 0; col < row; ++col)
  215. {
  216. A(row, col) = A(col, row);
  217. }
  218. }
  219. Real invNumPoints = (Real)1 / static_cast<Real>(numPoints);
  220. for (int row = 0; row < 5; ++row)
  221. {
  222. for (int col = 0; col < 5; ++col)
  223. {
  224. A(row, col) *= invNumPoints;
  225. }
  226. }
  227. SymmetricEigensolver<Real> es(5, 1024);
  228. es.Solve(&A[0], +1);
  229. Vector<5, Real> evector;
  230. es.GetEigenvector(0, &evector[0]);
  231. // TODO: Guard against zero divide?
  232. Real inv = (Real)1 / evector[4];
  233. Real coefficients[4];
  234. for (int row = 0; row < 4; ++row)
  235. {
  236. coefficients[row] = inv * evector[row];
  237. }
  238. sphere.center[0] = (Real)-0.5 * coefficients[1];
  239. sphere.center[1] = (Real)-0.5 * coefficients[2];
  240. sphere.center[2] = (Real)-0.5 * coefficients[3];
  241. sphere.radius = std::sqrt(std::fabs(Dot(sphere.center, sphere.center) - coefficients[0]));
  242. // For an exact fit, numeric round-off errors might make the
  243. // minimum eigenvalue just slightly negative. Return the
  244. // absolute value because the application might rely on the
  245. // return value being nonnegative.
  246. return std::fabs(es.GetEigenvalue(0));
  247. }
  248. };
  249. }