ContEllipse2MinCR.h 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  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/Logger.h>
  9. #include <Mathematics/Matrix2x2.h>
  10. // Compute the minimum-area ellipse, (X-C)^T R D R^T (X-C) = 1, given the
  11. // center C and the orientation matrix R. The columns of R are the axes of
  12. // the ellipse. The algorithm computes the diagonal matrix D. The minimum
  13. // area is pi/sqrt(D[0]*D[1]), where D = diag(D[0],D[1]). The problem is
  14. // equivalent to maximizing the product D[0]*D[1] for a given C and R, and
  15. // 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] <= 1
  19. // where A[0] >= 0 and A[1] >= 0.
  20. namespace WwiseGTE
  21. {
  22. template <typename Real>
  23. class ContEllipse2MinCR
  24. {
  25. public:
  26. void operator()(int numPoints, Vector2<Real> const* points,
  27. Vector2<Real> const& C, Matrix2x2<Real> const& R, Real D[2]) const
  28. {
  29. // Compute the constraint coefficients, of the form (A[0],A[1])
  30. // for each i.
  31. std::vector<Vector2<Real>> A(numPoints);
  32. for (int i = 0; i < numPoints; ++i)
  33. {
  34. Vector2<Real> diff = points[i] - C; // P[i] - C
  35. Vector2<Real> prod = diff * R; // R^T*(P[i] - C) = (u,v)
  36. A[i] = prod * prod; // (u^2, v^2)
  37. }
  38. // Use a lexicographical sort to eliminate redundant constraints.
  39. // Remove all but the first entry in blocks with x0 = x1 because
  40. // the corresponding constraint lines for the first entry hides
  41. // all the others from the origin.
  42. std::sort(A.begin(), A.end(),
  43. [](Vector2<Real> const& P0, Vector2<Real> const& P1)
  44. {
  45. if (P0[0] > P1[0]) { return true; }
  46. if (P0[0] < P1[0]) { return false; }
  47. return P0[1] > P1[1];
  48. }
  49. );
  50. auto end = std::unique(A.begin(), A.end(),
  51. [](Vector2<Real> const& P0, Vector2<Real> const& P1)
  52. {
  53. return P0[0] == P1[0];
  54. }
  55. );
  56. A.erase(end, A.end());
  57. // Use a lexicographical sort to eliminate redundant constraints.
  58. // Remove all but the first entry in blocks/ with y0 = y1 because
  59. // the corresponding constraint lines for the first entry hides
  60. // all the others from the origin.
  61. std::sort(A.begin(), A.end(),
  62. [](Vector2<Real> const& P0, Vector2<Real> const& P1)
  63. {
  64. if (P0[1] > P1[1])
  65. {
  66. return true;
  67. }
  68. if (P0[1] < P1[1])
  69. {
  70. return false;
  71. }
  72. return P0[0] > P1[0];
  73. }
  74. );
  75. end = std::unique(A.begin(), A.end(),
  76. [](Vector2<Real> const& P0, Vector2<Real> const& P1)
  77. {
  78. return P0[1] == P1[1];
  79. }
  80. );
  81. A.erase(end, A.end());
  82. MaxProduct(A, D);
  83. }
  84. private:
  85. static void MaxProduct(std::vector<Vector2<Real>>& A, Real D[2])
  86. {
  87. // Keep track of which constraint lines have already been used in
  88. // the search.
  89. int numConstraints = static_cast<int>(A.size());
  90. std::vector<bool> used(A.size());
  91. std::fill(used.begin(), used.end(), false);
  92. // Find the constraint line whose y-intercept (0,ymin) is closest
  93. // to the origin. This line contributes to the convex hull of the
  94. // constraints and the search for the maximum starts here. Also
  95. // find the constraint line whose x-intercept (xmin,0) is closest
  96. // to the origin. This line contributes to the convex hull of the
  97. // constraints and the search for the maximum terminates before or
  98. // at this line.
  99. int i, iYMin = -1;
  100. int iXMin = -1;
  101. Real axMax = (Real)0, ayMax = (Real)0; // A[i] >= (0,0) by design
  102. for (i = 0; i < numConstraints; ++i)
  103. {
  104. // The minimum x-intercept is 1/A[iXMin][0] for A[iXMin][0]
  105. // the maximum of the A[i][0].
  106. if (A[i][0] > axMax)
  107. {
  108. axMax = A[i][0];
  109. iXMin = i;
  110. }
  111. // The minimum y-intercept is 1/A[iYMin][1] for A[iYMin][1]
  112. // the maximum of the A[i][1].
  113. if (A[i][1] > ayMax)
  114. {
  115. ayMax = A[i][1];
  116. iYMin = i;
  117. }
  118. }
  119. LogAssert(iXMin != -1 && iYMin != -1, "Unexpected condition.");
  120. used[iYMin] = true;
  121. // The convex hull is searched in a clockwise manner starting with
  122. // the constraint line constructed above. The next vertex of the
  123. // hull occurs as the closest point to the first vertex on the
  124. // current constraint line. The following loop finds each
  125. // consecutive vertex.
  126. Real x0 = (Real)0, xMax = ((Real)1) / axMax;
  127. int j;
  128. for (j = 0; j < numConstraints; ++j)
  129. {
  130. // Find the line whose intersection with the current line is
  131. // closest to the last hull vertex. The last vertex is at
  132. // (x0,y0) on the current line.
  133. Real x1 = xMax;
  134. int line = -1;
  135. for (i = 0; i < numConstraints; ++i)
  136. {
  137. if (!used[i])
  138. {
  139. // This line not yet visited, process it. Given
  140. // current constraint line a0*x+b0*y =1 and candidate
  141. // line a1*x+b1*y = 1, find the point of intersection.
  142. // The determinant of the system is d = a0*b1-a1*b0.
  143. // We care only about lines that have more negative
  144. // slope than the previous one, that is,
  145. // -a1/b1 < -a0/b0, in which case we process only
  146. // lines for which d < 0.
  147. Real det = DotPerp(A[iYMin], A[i]);
  148. if (det < (Real)0)
  149. {
  150. // Compute the x-value for the point of
  151. // intersection, (x1,y1). There may be floating
  152. // point error issues in the comparision
  153. // 'D[0] <= x1'. Consider modifying to
  154. // 'D[0] <= x1 + epsilon'.
  155. D[0] = (A[i][1] - A[iYMin][1]) / det;
  156. if (x0 < D[0] && D[0] <= x1)
  157. {
  158. line = i;
  159. x1 = D[0];
  160. }
  161. }
  162. }
  163. }
  164. // Next vertex is at (x1,y1) whose x-value was computed above.
  165. // First check for the maximum of x*y on the current line for
  166. // x in [x0,x1]. On this interval the function is
  167. // f(x) = x*(1-a0*x)/b0. The derivative is
  168. // f'(x) = (1-2*a0*x)/b0 and f'(r) = 0 when r = 1/(2*a0).
  169. // The three candidates for the maximum are f(x0), f(r) and
  170. // f(x1). Comparisons are made between r and the endpoints
  171. // x0 and x1. Because a0 = 0 is possible (constraint line is
  172. // horizontal and f is increasing on line), the division in r
  173. // is not performed and the comparisons are made between
  174. // 1/2 = a0*r and a0*x0 or a0*x1.
  175. // Compare r < x0.
  176. if ((Real)0.5 < A[iYMin][0] * x0)
  177. {
  178. // The maximum is f(x0) since the quadratic f decreases
  179. // for x > r. The value D[1] is f(x0).
  180. D[0] = x0;
  181. D[1] = ((Real)1 - A[iYMin][0] * D[0]) / A[iYMin][1];
  182. break;
  183. }
  184. // Compare r < x1.
  185. if ((Real)0.5 < A[iYMin][0] * x1)
  186. {
  187. // The maximum is f(r). The search ends here because the
  188. // current line is tangent to the level curve of
  189. // f(x) = f(r) and x*y can therefore only decrease as we
  190. // traverse farther around the hull in the clockwise
  191. // direction. The value D[1] is f(r).
  192. D[0] = (Real)0.5 / A[iYMin][0];
  193. D[1] = (Real)0.5 / A[iYMin][1];
  194. break;
  195. }
  196. // The maximum is f(x1). The function x*y is potentially
  197. // larger on the next line, so continue the search.
  198. LogAssert(line != -1, "Unexpected condition.");
  199. x0 = x1;
  200. x1 = xMax;
  201. used[line] = true;
  202. iYMin = line;
  203. }
  204. LogAssert(j < numConstraints, "Unexpected condition.");
  205. }
  206. };
  207. }