ContEllipsoid3MinCR.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  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/Matrix3x3.h>
  9. #include <random>
  10. // Compute the minimum-volume ellipsoid, (X-C)^T R D R^T (X-C) = 1, given the
  11. // center C and orientation matrix R. The columns of R are the axes of the
  12. // ellipsoid. The algorithm computes the diagonal matrix D. The minimum
  13. // volume is (4*pi/3)/sqrt(D[0]*D[1]*D[2]), where D = diag(D[0],D[1],D[2]).
  14. // The problem is equivalent to maximizing the product D[0]*D[1]*D[2] for a
  15. // given C and R, and subject to the constraints
  16. // (P[i]-C)^T R D R^T (P[i]-C) <= 1
  17. // for all input points P[i] with 0 <= i < N. Each constraint has the form
  18. // A[0]*D[0] + A[1]*D[1] + A[2]*D[2] <= 1
  19. // where A[0] >= 0, A[1] >= 0, and A[2] >= 0.
  20. namespace WwiseGTE
  21. {
  22. template <typename Real>
  23. class ContEllipsoid3MinCR
  24. {
  25. public:
  26. void operator()(int numPoints, Vector3<Real> const* points,
  27. Vector3<Real> const& C, Matrix3x3<Real> const& R, Real D[3]) const
  28. {
  29. // Compute the constraint coefficients, of the form (A[0],A[1])
  30. // for each i.
  31. std::vector<Vector3<Real>> A(numPoints);
  32. for (int i = 0; i < numPoints; ++i)
  33. {
  34. Vector3<Real> diff = points[i] - C; // P[i] - C
  35. Vector3<Real> prod = diff * R; // R^T*(P[i] - C) = (u,v,w)
  36. A[i] = prod * prod; // (u^2, v^2, w^2)
  37. }
  38. // TODO: Sort the constraints to eliminate redundant ones. It
  39. // is clear how to do this in ContEllipse2MinCR. How to do this
  40. // in 3D?
  41. MaxProduct(A, D);
  42. }
  43. private:
  44. void FindEdgeMax(std::vector<Vector3<Real>>& A, int& plane0, int& plane1, Real D[3]) const
  45. {
  46. // Compute direction to local maximum point on line of
  47. // intersection.
  48. Real xDir = A[plane0][1] * A[plane1][2] - A[plane1][1] * A[plane0][2];
  49. Real yDir = A[plane0][2] * A[plane1][0] - A[plane1][2] * A[plane0][0];
  50. Real zDir = A[plane0][0] * A[plane1][1] - A[plane1][0] * A[plane0][1];
  51. // Build quadratic Q'(t) = (d/dt)(x(t)y(t)z(t)) = a0+a1*t+a2*t^2.
  52. Real a0 = D[0] * D[1] * zDir + D[0] * D[2] * yDir + D[1] * D[2] * xDir;
  53. Real a1 = (Real)2 * (D[2] * xDir * yDir + D[1] * xDir * zDir + D[0] * yDir * zDir);
  54. Real a2 = (Real)3 * (xDir * yDir * zDir);
  55. // Find root to Q'(t) = 0 corresponding to maximum.
  56. Real tFinal;
  57. if (a2 != (Real)0)
  58. {
  59. Real invA2 = (Real)1 / a2;
  60. Real discr = a1 * a1 - (Real)4 * a0 * a2;
  61. discr = std::sqrt(std::max(discr, (Real)0));
  62. tFinal = (Real)-0.5 * (a1 + discr) * invA2;
  63. if (a1 + (Real)2 * a2 * tFinal > (Real)0)
  64. {
  65. tFinal = (Real)0.5 * (-a1 + discr) * invA2;
  66. }
  67. }
  68. else if (a1 != (Real)0)
  69. {
  70. tFinal = -a0 / a1;
  71. }
  72. else if (a0 != (Real)0)
  73. {
  74. Real fmax = std::numeric_limits<Real>::max();
  75. tFinal = (a0 >= (Real)0 ? fmax : -fmax);
  76. }
  77. else
  78. {
  79. return;
  80. }
  81. if (tFinal < (Real)0)
  82. {
  83. // Make (xDir,yDir,zDir) point in direction of increase of Q.
  84. tFinal = -tFinal;
  85. xDir = -xDir;
  86. yDir = -yDir;
  87. zDir = -zDir;
  88. }
  89. // Sort remaining planes along line from current point to local
  90. // maximum.
  91. Real tMax = tFinal;
  92. int plane2 = -1;
  93. int numPoints = static_cast<int>(A.size());
  94. for (int i = 0; i < numPoints; ++i)
  95. {
  96. if (i == plane0 || i == plane1)
  97. {
  98. continue;
  99. }
  100. Real norDotDir = A[i][0] * xDir + A[i][1] * yDir + A[i][2] * zDir;
  101. if (norDotDir <= (Real)0)
  102. {
  103. continue;
  104. }
  105. // Theoretically the numerator must be nonnegative since an
  106. // invariant in the algorithm is that (x0,y0,z0) is on the
  107. // convex hull of the constraints. However, some numerical
  108. // error may make this a small negative number. In that case
  109. // set tmax = 0 (no change in position).
  110. Real numer = (Real)1 - A[i][0] * D[0] - A[i][1] * D[1] - A[i][2] * D[2];
  111. LogAssert(numer >= (Real)0, "Unexpected condition.");
  112. Real t = numer / norDotDir;
  113. if (0 <= t && t < tMax)
  114. {
  115. plane2 = i;
  116. tMax = t;
  117. }
  118. }
  119. D[0] += tMax * xDir;
  120. D[1] += tMax * yDir;
  121. D[2] += tMax * zDir;
  122. if (tMax == tFinal)
  123. {
  124. return;
  125. }
  126. if (tMax > (Real)0)
  127. {
  128. plane0 = plane2;
  129. FindFacetMax(A, plane0, D);
  130. return;
  131. }
  132. // tmax == 0, so return with D[0], D[1], and D[2] unchanged.
  133. }
  134. void FindFacetMax(std::vector<Vector3<Real>>& A, int& plane0, Real D[3]) const
  135. {
  136. Real tFinal, xDir, yDir, zDir;
  137. if (A[plane0][0] > (Real)0
  138. && A[plane0][1] > (Real)0
  139. && A[plane0][2] > (Real)0)
  140. {
  141. // Compute local maximum point on plane.
  142. Real oneThird = (Real)1 / (Real)3;
  143. Real xMax = oneThird / A[plane0][0];
  144. Real yMax = oneThird / A[plane0][1];
  145. Real zMax = oneThird / A[plane0][2];
  146. // Compute direction to local maximum point on plane.
  147. tFinal = (Real)1;
  148. xDir = xMax - D[0];
  149. yDir = yMax - D[1];
  150. zDir = zMax - D[2];
  151. }
  152. else
  153. {
  154. tFinal = std::numeric_limits<Real>::max();
  155. if (A[plane0][0] > (Real)0)
  156. {
  157. xDir = (Real)0;
  158. }
  159. else
  160. {
  161. xDir = (Real)1;
  162. }
  163. if (A[plane0][1] > (Real)0)
  164. {
  165. yDir = (Real)0;
  166. }
  167. else
  168. {
  169. yDir = (Real)1;
  170. }
  171. if (A[plane0][2] > (Real)0)
  172. {
  173. zDir = (Real)0;
  174. }
  175. else
  176. {
  177. zDir = (Real)1;
  178. }
  179. }
  180. // Sort remaining planes along line from current point.
  181. Real tMax = tFinal;
  182. int plane1 = -1;
  183. int numPoints = static_cast<int>(A.size());
  184. for (int i = 0; i < numPoints; ++i)
  185. {
  186. if (i == plane0)
  187. {
  188. continue;
  189. }
  190. Real norDotDir = A[i][0] * xDir + A[i][1] * yDir + A[i][2] * zDir;
  191. if (norDotDir <= (Real)0)
  192. {
  193. continue;
  194. }
  195. // Theoretically the numerator must be nonnegative because an
  196. // invariant in the algorithm is that (x0,y0,z0) is on the
  197. // convex hull of the constraints. However, some numerical
  198. // error may make this a small negative number. In that case,
  199. // set tmax = 0 (no change in position).
  200. Real numer = (Real)1 - A[i][0] * D[0] - A[i][1] * D[1] - A[i][2] * D[2];
  201. LogAssert(numer >= (Real)0, "Unexpected condition.");
  202. Real t = numer / norDotDir;
  203. if (0 <= t && t < tMax)
  204. {
  205. plane1 = i;
  206. tMax = t;
  207. }
  208. }
  209. D[0] += tMax * xDir;
  210. D[1] += tMax * yDir;
  211. D[2] += tMax * zDir;
  212. if (tMax == (Real)1)
  213. {
  214. return;
  215. }
  216. if (tMax > (Real)0)
  217. {
  218. plane0 = plane1;
  219. FindFacetMax(A, plane0, D);
  220. return;
  221. }
  222. FindEdgeMax(A, plane0, plane1, D);
  223. }
  224. void MaxProduct(std::vector<Vector3<Real>>& A, Real D[3]) const
  225. {
  226. // Maximize x*y*z subject to x >= 0, y >= 0, z >= 0, and
  227. // A[i]*x+B[i]*y+C[i]*z <= 1 for 0 <= i < N where A[i] >= 0,
  228. // B[i] >= 0, and C[i] >= 0.
  229. // Jitter the lines to avoid cases where more than three planes
  230. // intersect at the same point. Should also break parallelism
  231. // and planes parallel to the coordinate planes.
  232. std::mt19937 mte;
  233. std::uniform_real_distribution<Real> rnd((Real)0, (Real)1);
  234. Real maxJitter = (Real)1e-12;
  235. int numPoints = static_cast<int>(A.size());
  236. int i;
  237. for (i = 0; i < numPoints; ++i)
  238. {
  239. A[i][0] += maxJitter * rnd(mte);
  240. A[i][1] += maxJitter * rnd(mte);
  241. A[i][2] += maxJitter * rnd(mte);
  242. }
  243. // Sort lines along the z-axis (x = 0 and y = 0).
  244. int plane = -1;
  245. Real zmax = (Real)0;
  246. for (i = 0; i < numPoints; ++i)
  247. {
  248. if (A[i][2] > zmax)
  249. {
  250. zmax = A[i][2];
  251. plane = i;
  252. }
  253. }
  254. LogAssert(plane != -1, "Unexpected condition.");
  255. // Walk along convex hull searching for maximum.
  256. D[0] = (Real)0;
  257. D[1] = (Real)0;
  258. D[2] = (Real)1 / zmax;
  259. FindFacetMax(A, plane, D);
  260. }
  261. };
  262. }