ApprEllipsoid3.h 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  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. // The ellipsoid in general form is X^t A X + B^t X + C = 0 where A is a
  9. // positive definite 3x3 matrix, B is a 3x1 vector, C is a scalar, and X is a
  10. // 3x1 vector. Completing the square, (X-U)^t A (X-U) = U^t A U - C where
  11. // U = -0.5 A^{-1} B. Define M = A/(U^t A U - C). The ellipsoid is
  12. // (X-U)^t M (X-U) = 1. Factor M = R^t D R where R is orthonormal and D is
  13. // diagonal with positive diagonal terms. The ellipsoid in factored form is
  14. // (X-U)^t R^t D^t R (X-U) = 1. Find the least squares fit of a set of N
  15. // points P[0] through P[N-1]. The error return value is the least-squares
  16. // energy function at (U,R,D).
  17. #include <Mathematics/ContOrientedBox3.h>
  18. #include <Mathematics/DistPointHyperellipsoid.h>
  19. #include <Mathematics/Matrix3x3.h>
  20. #include <Mathematics/MinimizeN.h>
  21. #include <Mathematics/Rotation.h>
  22. namespace WwiseGTE
  23. {
  24. template <typename Real>
  25. class ApprEllipsoid3
  26. {
  27. public:
  28. Real operator()(int numPoints, Vector3<Real> const* points,
  29. Vector3<Real>& center, Matrix3x3<Real>& rotate, Real diagonal[3]) const
  30. {
  31. // Energy function is E : R^9 -> R where
  32. // V = (V0,V1,V2,V3,V4,V5,V6,V7,V8)
  33. // = (D[0],D[1],D[2],U[0],U,y,U[2],A0,A1,A2).
  34. std::function<Real(Real const*)> energy =
  35. [numPoints, points](Real const* input)
  36. {
  37. return Energy(numPoints, points, input);
  38. };
  39. MinimizeN<Real> minimizer(9, energy, 8, 8, 32);
  40. // The initial guess for the minimizer is based on an oriented box
  41. // that contains the points.
  42. OrientedBox3<Real> box;
  43. GetContainer(numPoints, points, box);
  44. center = box.center;
  45. for (int i = 0; i < 3; ++i)
  46. {
  47. rotate.SetRow(i, box.axis[i]);
  48. diagonal[i] = box.extent[i];
  49. }
  50. Real angle[3];
  51. MatrixToAngles(rotate, angle);
  52. Real extent[3] =
  53. {
  54. diagonal[0] * std::fabs(rotate(0, 0)) +
  55. diagonal[1] * std::fabs(rotate(0, 1)) +
  56. diagonal[2] * std::fabs(rotate(0, 2)),
  57. diagonal[0] * std::fabs(rotate(1, 0)) +
  58. diagonal[1] * std::fabs(rotate(1, 1)) +
  59. diagonal[2] * std::fabs(rotate(1, 2)),
  60. diagonal[0] * std::fabs(rotate(2, 0)) +
  61. diagonal[1] * std::fabs(rotate(2, 1)) +
  62. diagonal[2] * std::fabs(rotate(2, 2))
  63. };
  64. Real v0[9] =
  65. {
  66. (Real)0.5 * diagonal[0],
  67. (Real)0.5 * diagonal[1],
  68. (Real)0.5 * diagonal[2],
  69. center[0] - extent[0],
  70. center[1] - extent[1],
  71. center[2] - extent[2],
  72. -(Real)GTE_C_PI,
  73. (Real)0,
  74. (Real)0
  75. };
  76. Real v1[9] =
  77. {
  78. (Real)2 * diagonal[0],
  79. (Real)2 * diagonal[1],
  80. (Real)2 * diagonal[2],
  81. center[0] + extent[0],
  82. center[1] + extent[1],
  83. center[2] + extent[2],
  84. (Real)GTE_C_PI,
  85. (Real)GTE_C_PI,
  86. (Real)GTE_C_PI
  87. };
  88. Real vInitial[9] =
  89. {
  90. diagonal[0],
  91. diagonal[1],
  92. diagonal[2],
  93. center[0],
  94. center[1],
  95. center[2],
  96. angle[0],
  97. angle[1],
  98. angle[2]
  99. };
  100. Real vMin[9], error;
  101. minimizer.GetMinimum(v0, v1, vInitial, vMin, error);
  102. diagonal[0] = vMin[0];
  103. diagonal[1] = vMin[1];
  104. diagonal[2] = vMin[2];
  105. center[0] = vMin[3];
  106. center[1] = vMin[4];
  107. center[2] = vMin[5];
  108. AnglesToMatrix(&vMin[6], rotate);
  109. return error;
  110. }
  111. private:
  112. static void MatrixToAngles(Matrix3x3<Real> const& rotate, Real angle[3])
  113. {
  114. // rotation axis = (cos(a0)sin(a1),sin(a0)sin(a1),cos(a1))
  115. // a0 in [-pi,pi], a1 in [0,pi], a2 in [0,pi]
  116. Real const zero = (Real)0;
  117. Real const one = (Real)1;
  118. AxisAngle<3, Real> aa = Rotation<3, Real>(rotate);
  119. if (-one < aa.axis[2])
  120. {
  121. if (aa.axis[2] < one)
  122. {
  123. angle[0] = std::atan2(aa.axis[1], aa.axis[0]);
  124. angle[1] = std::acos(aa.axis[2]);
  125. }
  126. else
  127. {
  128. angle[0] = zero;
  129. angle[1] = zero;
  130. }
  131. }
  132. else
  133. {
  134. angle[0] = zero;
  135. angle[1] = (Real)GTE_C_PI;
  136. }
  137. }
  138. static void AnglesToMatrix(Real const angle[3], Matrix3x3<Real>& rotate)
  139. {
  140. // rotation axis = (cos(a0)sin(a1),sin(a0)sin(a1),cos(a1))
  141. // a0 in [-pi,pi], a1 in [0,pi], a2 in [0,pi]
  142. Real cs0 = std::cos(angle[0]);
  143. Real sn0 = std::sin(angle[0]);
  144. Real cs1 = std::cos(angle[1]);
  145. Real sn1 = std::sin(angle[1]);
  146. AxisAngle<3, Real> aa;
  147. aa.axis = { cs0 * sn1, sn0 * sn1, cs1 };
  148. aa.angle = angle[2];
  149. rotate = Rotation<3, Real>(aa);
  150. }
  151. static Real Energy(int numPoints, Vector3<Real> const* points, Real const* input)
  152. {
  153. // Build rotation matrix.
  154. Matrix3x3<Real> rotate;
  155. AnglesToMatrix(&input[6], rotate);
  156. // Uniformly scale the extents to keep reasonable floating point values
  157. // in the distance calculations.
  158. Real maxValue = std::max(std::max(input[0], input[1]), input[2]);
  159. Real invMax = (Real)1 / maxValue;
  160. Ellipsoid3<Real> ellipsoid(Vector3<Real>::Zero(), { Vector3<Real>::Unit(0),
  161. Vector3<Real>::Unit(1), Vector3<Real>::Unit(2) }, { invMax * input[0],
  162. invMax * input[1], invMax * input[2] });
  163. // Transform the points to the coordinate system of center C and columns
  164. // of rotation R.
  165. DCPQuery<Real, Vector3<Real>, Ellipsoid3<Real>> peQuery;
  166. Real energy = (Real)0;
  167. for (int i = 0; i < numPoints; ++i)
  168. {
  169. Vector3<Real> diff = points[i] -
  170. Vector3<Real>{ input[3], input[4], input[5] };
  171. Vector3<Real> prod = invMax * (diff * rotate);
  172. Real dist = peQuery(prod, ellipsoid).distance;
  173. energy += maxValue * dist;
  174. }
  175. return energy;
  176. }
  177. };
  178. }