123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- // David Eberly, Geometric Tools, Redmond WA 98052
- // Copyright (c) 1998-2020
- // Distributed under the Boost Software License, Version 1.0.
- // https://www.boost.org/LICENSE_1_0.txt
- // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
- // Version: 4.0.2019.08.13
- #pragma once
- #include <Mathematics/Logger.h>
- #include <Mathematics/Matrix2x2.h>
- // Compute the minimum-area ellipse, (X-C)^T R D R^T (X-C) = 1, given the
- // center C and the orientation matrix R. The columns of R are the axes of
- // the ellipse. The algorithm computes the diagonal matrix D. The minimum
- // area is pi/sqrt(D[0]*D[1]), where D = diag(D[0],D[1]). The problem is
- // equivalent to maximizing the product D[0]*D[1] for a given C and R, and
- // subject to the constraints
- // (P[i]-C)^T R D R^T (P[i]-C) <= 1
- // for all input points P[i] with 0 <= i < N. Each constraint has the form
- // A[0]*D[0] + A[1]*D[1] <= 1
- // where A[0] >= 0 and A[1] >= 0.
- namespace WwiseGTE
- {
- template <typename Real>
- class ContEllipse2MinCR
- {
- public:
- void operator()(int numPoints, Vector2<Real> const* points,
- Vector2<Real> const& C, Matrix2x2<Real> const& R, Real D[2]) const
- {
- // Compute the constraint coefficients, of the form (A[0],A[1])
- // for each i.
- std::vector<Vector2<Real>> A(numPoints);
- for (int i = 0; i < numPoints; ++i)
- {
- Vector2<Real> diff = points[i] - C; // P[i] - C
- Vector2<Real> prod = diff * R; // R^T*(P[i] - C) = (u,v)
- A[i] = prod * prod; // (u^2, v^2)
- }
- // Use a lexicographical sort to eliminate redundant constraints.
- // Remove all but the first entry in blocks with x0 = x1 because
- // the corresponding constraint lines for the first entry hides
- // all the others from the origin.
- std::sort(A.begin(), A.end(),
- [](Vector2<Real> const& P0, Vector2<Real> const& P1)
- {
- if (P0[0] > P1[0]) { return true; }
- if (P0[0] < P1[0]) { return false; }
- return P0[1] > P1[1];
- }
- );
- auto end = std::unique(A.begin(), A.end(),
- [](Vector2<Real> const& P0, Vector2<Real> const& P1)
- {
- return P0[0] == P1[0];
- }
- );
- A.erase(end, A.end());
- // Use a lexicographical sort to eliminate redundant constraints.
- // Remove all but the first entry in blocks/ with y0 = y1 because
- // the corresponding constraint lines for the first entry hides
- // all the others from the origin.
- std::sort(A.begin(), A.end(),
- [](Vector2<Real> const& P0, Vector2<Real> const& P1)
- {
- if (P0[1] > P1[1])
- {
- return true;
- }
- if (P0[1] < P1[1])
- {
- return false;
- }
- return P0[0] > P1[0];
- }
- );
- end = std::unique(A.begin(), A.end(),
- [](Vector2<Real> const& P0, Vector2<Real> const& P1)
- {
- return P0[1] == P1[1];
- }
- );
- A.erase(end, A.end());
- MaxProduct(A, D);
- }
- private:
- static void MaxProduct(std::vector<Vector2<Real>>& A, Real D[2])
- {
- // Keep track of which constraint lines have already been used in
- // the search.
- int numConstraints = static_cast<int>(A.size());
- std::vector<bool> used(A.size());
- std::fill(used.begin(), used.end(), false);
- // Find the constraint line whose y-intercept (0,ymin) is closest
- // to the origin. This line contributes to the convex hull of the
- // constraints and the search for the maximum starts here. Also
- // find the constraint line whose x-intercept (xmin,0) is closest
- // to the origin. This line contributes to the convex hull of the
- // constraints and the search for the maximum terminates before or
- // at this line.
- int i, iYMin = -1;
- int iXMin = -1;
- Real axMax = (Real)0, ayMax = (Real)0; // A[i] >= (0,0) by design
- for (i = 0; i < numConstraints; ++i)
- {
- // The minimum x-intercept is 1/A[iXMin][0] for A[iXMin][0]
- // the maximum of the A[i][0].
- if (A[i][0] > axMax)
- {
- axMax = A[i][0];
- iXMin = i;
- }
- // The minimum y-intercept is 1/A[iYMin][1] for A[iYMin][1]
- // the maximum of the A[i][1].
- if (A[i][1] > ayMax)
- {
- ayMax = A[i][1];
- iYMin = i;
- }
- }
- LogAssert(iXMin != -1 && iYMin != -1, "Unexpected condition.");
- used[iYMin] = true;
- // The convex hull is searched in a clockwise manner starting with
- // the constraint line constructed above. The next vertex of the
- // hull occurs as the closest point to the first vertex on the
- // current constraint line. The following loop finds each
- // consecutive vertex.
- Real x0 = (Real)0, xMax = ((Real)1) / axMax;
- int j;
- for (j = 0; j < numConstraints; ++j)
- {
- // Find the line whose intersection with the current line is
- // closest to the last hull vertex. The last vertex is at
- // (x0,y0) on the current line.
- Real x1 = xMax;
- int line = -1;
- for (i = 0; i < numConstraints; ++i)
- {
- if (!used[i])
- {
- // This line not yet visited, process it. Given
- // current constraint line a0*x+b0*y =1 and candidate
- // line a1*x+b1*y = 1, find the point of intersection.
- // The determinant of the system is d = a0*b1-a1*b0.
- // We care only about lines that have more negative
- // slope than the previous one, that is,
- // -a1/b1 < -a0/b0, in which case we process only
- // lines for which d < 0.
- Real det = DotPerp(A[iYMin], A[i]);
- if (det < (Real)0)
- {
- // Compute the x-value for the point of
- // intersection, (x1,y1). There may be floating
- // point error issues in the comparision
- // 'D[0] <= x1'. Consider modifying to
- // 'D[0] <= x1 + epsilon'.
- D[0] = (A[i][1] - A[iYMin][1]) / det;
- if (x0 < D[0] && D[0] <= x1)
- {
- line = i;
- x1 = D[0];
- }
- }
- }
- }
- // Next vertex is at (x1,y1) whose x-value was computed above.
- // First check for the maximum of x*y on the current line for
- // x in [x0,x1]. On this interval the function is
- // f(x) = x*(1-a0*x)/b0. The derivative is
- // f'(x) = (1-2*a0*x)/b0 and f'(r) = 0 when r = 1/(2*a0).
- // The three candidates for the maximum are f(x0), f(r) and
- // f(x1). Comparisons are made between r and the endpoints
- // x0 and x1. Because a0 = 0 is possible (constraint line is
- // horizontal and f is increasing on line), the division in r
- // is not performed and the comparisons are made between
- // 1/2 = a0*r and a0*x0 or a0*x1.
- // Compare r < x0.
- if ((Real)0.5 < A[iYMin][0] * x0)
- {
- // The maximum is f(x0) since the quadratic f decreases
- // for x > r. The value D[1] is f(x0).
- D[0] = x0;
- D[1] = ((Real)1 - A[iYMin][0] * D[0]) / A[iYMin][1];
- break;
- }
- // Compare r < x1.
- if ((Real)0.5 < A[iYMin][0] * x1)
- {
- // The maximum is f(r). The search ends here because the
- // current line is tangent to the level curve of
- // f(x) = f(r) and x*y can therefore only decrease as we
- // traverse farther around the hull in the clockwise
- // direction. The value D[1] is f(r).
- D[0] = (Real)0.5 / A[iYMin][0];
- D[1] = (Real)0.5 / A[iYMin][1];
- break;
- }
- // The maximum is f(x1). The function x*y is potentially
- // larger on the next line, so continue the search.
- LogAssert(line != -1, "Unexpected condition.");
- x0 = x1;
- x1 = xMax;
- used[line] = true;
- iYMin = line;
- }
- LogAssert(j < numConstraints, "Unexpected condition.");
- }
- };
- }
|