DistPointHyperellipsoid.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  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/DCPQuery.h>
  9. #include <Mathematics/Hyperellipsoid.h>
  10. #include <Mathematics/Vector.h>
  11. // Compute the distance from a point to a hyperellipsoid. In 2D, this is a
  12. // point-ellipse distance query. In 3D, this is a point-ellipsoid distance
  13. // query. The following document describes the algorithm.
  14. // https://www.geometrictools.com/Documentation/DistancePointEllipseEllipsoid.pdf
  15. // The hyperellipsoid can have arbitrary center and orientation; that is, it
  16. // does not have to be axis-aligned with center at the origin.
  17. //
  18. // For the 2D query,
  19. // Vector2<Real> point; // initialized to something
  20. // Ellipse2<Real> ellipse; // initialized to something
  21. // DCPPoint2Ellipse2<Real> query;
  22. // auto result = query(point, ellipse);
  23. // Real distance = result.distance;
  24. // Vector2<Real> closestEllipsePoint = result.closest;
  25. //
  26. // For the 3D query,
  27. // Vector3<Real> point; // initialized to something
  28. // Ellipsoid3<Real> ellipsoid; // initialized to something
  29. // DCPPoint3Ellipsoid3<Real> query;
  30. // auto result = query(point, ellipsoid);
  31. // Real distance = result.distance;
  32. // Vector3<Real> closestEllipsoidPoint = result.closest;
  33. namespace WwiseGTE
  34. {
  35. template <int N, typename Real>
  36. class DCPQuery<Real, Vector<N, Real>, Hyperellipsoid<N, Real>>
  37. {
  38. public:
  39. struct Result
  40. {
  41. Real distance, sqrDistance;
  42. Vector<N, Real> closest;
  43. };
  44. // The query for any hyperellipsoid.
  45. Result operator()(Vector<N, Real> const& point,
  46. Hyperellipsoid<N, Real> const& hyperellipsoid)
  47. {
  48. Result result;
  49. // Compute the coordinates of Y in the hyperellipsoid coordinate
  50. // system.
  51. Vector<N, Real> diff = point - hyperellipsoid.center;
  52. Vector<N, Real> y;
  53. for (int i = 0; i < N; ++i)
  54. {
  55. y[i] = Dot(diff, hyperellipsoid.axis[i]);
  56. }
  57. // Compute the closest hyperellipsoid point in the axis-aligned
  58. // coordinate system.
  59. Vector<N, Real> x;
  60. result.sqrDistance = SqrDistance(hyperellipsoid.extent, y, x);
  61. result.distance = std::sqrt(result.sqrDistance);
  62. // Convert back to the original coordinate system.
  63. result.closest = hyperellipsoid.center;
  64. for (int i = 0; i < N; ++i)
  65. {
  66. result.closest += x[i] * hyperellipsoid.axis[i];
  67. }
  68. return result;
  69. }
  70. // The 'hyperellipsoid' is assumed to be axis-aligned and centered at the
  71. // origin , so only the extent[] values are used.
  72. Result operator()(Vector<N, Real> const& point, Vector<N, Real> const& extent)
  73. {
  74. Result result;
  75. result.sqrDistance = SqrDistance(extent, point, result.closest);
  76. result.distance = std::sqrt(result.sqrDistance);
  77. return result;
  78. }
  79. private:
  80. // The hyperellipsoid is sum_{d=0}^{N-1} (x[d]/e[d])^2 = 1 with no
  81. // constraints on the orderind of the e[d]. The query point is
  82. // (y[0],...,y[N-1]) with no constraints on the signs of the components.
  83. // The function returns the squared distance from the query point to the
  84. // hyperellipsoid. It also computes the hyperellipsoid point
  85. // (x[0],...,x[N-1]) that is closest to (y[0],...,y[N-1]).
  86. Real SqrDistance(Vector<N, Real> const& e,
  87. Vector<N, Real> const& y, Vector<N, Real>& x)
  88. {
  89. // Determine negations for y to the first octant.
  90. std::array<bool, N> negate;
  91. int i, j;
  92. for (i = 0; i < N; ++i)
  93. {
  94. negate[i] = (y[i] < (Real)0);
  95. }
  96. // Determine the axis order for decreasing extents.
  97. std::array<std::pair<Real, int>, N> permute;
  98. for (i = 0; i < N; ++i)
  99. {
  100. permute[i].first = -e[i];
  101. permute[i].second = i;
  102. }
  103. std::sort(permute.begin(), permute.end());
  104. std::array<int, N> invPermute;
  105. for (i = 0; i < N; ++i)
  106. {
  107. invPermute[permute[i].second] = i;
  108. }
  109. Vector<N, Real> locE, locY;
  110. for (i = 0; i < N; ++i)
  111. {
  112. j = permute[i].second;
  113. locE[i] = e[j];
  114. locY[i] = std::fabs(y[j]);
  115. }
  116. Vector<N, Real> locX;
  117. Real sqrDistance = SqrDistanceSpecial(locE, locY, locX);
  118. // Restore the axis order and reflections.
  119. for (i = 0; i < N; ++i)
  120. {
  121. j = invPermute[i];
  122. if (negate[i])
  123. {
  124. locX[j] = -locX[j];
  125. }
  126. x[i] = locX[j];
  127. }
  128. return sqrDistance;
  129. }
  130. // The hyperellipsoid is sum_{d=0}^{N-1} (x[d]/e[d])^2 = 1 with the e[d]
  131. // positive and nonincreasing: e[d] >= e[d + 1] for all d. The query
  132. // point is (y[0],...,y[N-1]) with y[d] >= 0 for all d. The function
  133. // returns the squared distance from the query point to the
  134. // hyperellipsoid. It also computes the hyperellipsoid point
  135. // (x[0],...,x[N-1]) that is closest to (y[0],...,y[N-1]), where
  136. // x[d] >= 0 for all d.
  137. Real SqrDistanceSpecial(Vector<N, Real> const& e,
  138. Vector<N, Real> const& y, Vector<N, Real>& x)
  139. {
  140. Real sqrDistance = (Real)0;
  141. Vector<N, Real> ePos, yPos, xPos;
  142. int numPos = 0;
  143. int i;
  144. for (i = 0; i < N; ++i)
  145. {
  146. if (y[i] > (Real)0)
  147. {
  148. ePos[numPos] = e[i];
  149. yPos[numPos] = y[i];
  150. ++numPos;
  151. }
  152. else
  153. {
  154. x[i] = (Real)0;
  155. }
  156. }
  157. if (y[N - 1] > (Real)0)
  158. {
  159. sqrDistance = Bisector(numPos, ePos, yPos, xPos);
  160. }
  161. else // y[N-1] = 0
  162. {
  163. Vector<N - 1, Real> numer, denom;
  164. Real eNm1Sqr = e[N - 1] * e[N - 1];
  165. for (i = 0; i < numPos; ++i)
  166. {
  167. numer[i] = ePos[i] * yPos[i];
  168. denom[i] = ePos[i] * ePos[i] - eNm1Sqr;
  169. }
  170. bool inSubHyperbox = true;
  171. for (i = 0; i < numPos; ++i)
  172. {
  173. if (numer[i] >= denom[i])
  174. {
  175. inSubHyperbox = false;
  176. break;
  177. }
  178. }
  179. bool inSubHyperellipsoid = false;
  180. if (inSubHyperbox)
  181. {
  182. // yPos[] is inside the axis-aligned bounding box of the
  183. // subhyperellipsoid. This intermediate test is designed
  184. // to guard against the division by zero when
  185. // ePos[i] == e[N-1] for some i.
  186. Vector<N - 1, Real> xde;
  187. Real discr = (Real)1;
  188. for (i = 0; i < numPos; ++i)
  189. {
  190. xde[i] = numer[i] / denom[i];
  191. discr -= xde[i] * xde[i];
  192. }
  193. if (discr > (Real)0)
  194. {
  195. // yPos[] is inside the subhyperellipsoid. The
  196. // closest hyperellipsoid point has x[N-1] > 0.
  197. sqrDistance = (Real)0;
  198. for (i = 0; i < numPos; ++i)
  199. {
  200. xPos[i] = ePos[i] * xde[i];
  201. Real diff = xPos[i] - yPos[i];
  202. sqrDistance += diff * diff;
  203. }
  204. x[N - 1] = e[N - 1] * std::sqrt(discr);
  205. sqrDistance += x[N - 1] * x[N - 1];
  206. inSubHyperellipsoid = true;
  207. }
  208. }
  209. if (!inSubHyperellipsoid)
  210. {
  211. // yPos[] is outside the subhyperellipsoid. The closest
  212. // hyperellipsoid point has x[N-1] == 0 and is on the
  213. // domain-boundary hyperellipsoid.
  214. x[N - 1] = (Real)0;
  215. sqrDistance = Bisector(numPos, ePos, yPos, xPos);
  216. }
  217. }
  218. // Fill in those x[] values that were not zeroed out initially.
  219. for (i = 0, numPos = 0; i < N; ++i)
  220. {
  221. if (y[i] > (Real)0)
  222. {
  223. x[i] = xPos[numPos];
  224. ++numPos;
  225. }
  226. }
  227. return sqrDistance;
  228. }
  229. // The bisection algorithm to find the unique root of F(t).
  230. Real Bisector(int numComponents, Vector<N, Real> const& e,
  231. Vector<N, Real> const& y, Vector<N, Real>& x)
  232. {
  233. Vector<N, Real> z;
  234. Real sumZSqr = (Real)0;
  235. int i;
  236. for (i = 0; i < numComponents; ++i)
  237. {
  238. z[i] = y[i] / e[i];
  239. sumZSqr += z[i] * z[i];
  240. }
  241. if (sumZSqr == (Real)1)
  242. {
  243. // The point is on the hyperellipsoid.
  244. for (i = 0; i < numComponents; ++i)
  245. {
  246. x[i] = y[i];
  247. }
  248. return (Real)0;
  249. }
  250. Real emin = e[numComponents - 1];
  251. Vector<N, Real> pSqr, numerator;
  252. for (i = 0; i < numComponents; ++i)
  253. {
  254. Real p = e[i] / emin;
  255. pSqr[i] = p * p;
  256. numerator[i] = pSqr[i] * z[i];
  257. }
  258. Real s = (Real)0, smin = z[numComponents - 1] - (Real)1, smax;
  259. if (sumZSqr < (Real)1)
  260. {
  261. // The point is strictly inside the hyperellipsoid.
  262. smax = (Real)0;
  263. }
  264. else
  265. {
  266. // The point is strictly outside the hyperellipsoid.
  267. smax = Length(numerator, true) - (Real)1;
  268. }
  269. // The use of 'double' is intentional in case Real is a BSNumber
  270. // or BSRational type. We want the bisections to terminate in a
  271. // reasonable/ amount of time.
  272. unsigned int const jmax = GTE_C_MAX_BISECTIONS_GENERIC;
  273. for (unsigned int j = 0; j < jmax; ++j)
  274. {
  275. s = (smin + smax) * (Real)0.5;
  276. if (s == smin || s == smax)
  277. {
  278. break;
  279. }
  280. Real g = (Real)-1;
  281. for (i = 0; i < numComponents; ++i)
  282. {
  283. Real ratio = numerator[i] / (s + pSqr[i]);
  284. g += ratio * ratio;
  285. }
  286. if (g > (Real)0)
  287. {
  288. smin = s;
  289. }
  290. else if (g < (Real)0)
  291. {
  292. smax = s;
  293. }
  294. else
  295. {
  296. break;
  297. }
  298. }
  299. Real sqrDistance = (Real)0;
  300. for (i = 0; i < numComponents; ++i)
  301. {
  302. x[i] = pSqr[i] * y[i] / (s + pSqr[i]);
  303. Real diff = x[i] - y[i];
  304. sqrDistance += diff * diff;
  305. }
  306. return sqrDistance;
  307. }
  308. };
  309. // Template aliases for convenience.
  310. template <int N, typename Real>
  311. using DCPPointHyperellipsoid = DCPQuery<Real, Vector<N, Real>, Hyperellipsoid<N, Real>>;
  312. template <typename Real>
  313. using DCPPoint2Ellipse2 = DCPPointHyperellipsoid<2, Real>;
  314. template <typename Real>
  315. using DCPPoint3Ellipsoid3 = DCPPointHyperellipsoid<3, Real>;
  316. }