MinimumVolumeSphere3.h 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
  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/ContSphere3.h>
  10. #include <Mathematics/LinearSystem.h>
  11. #include <functional>
  12. #include <random>
  13. // Compute the minimum volume sphere containing the input set of points. The
  14. // algorithm randomly permutes the input points so that the construction
  15. // occurs in 'expected' O(N) time. All internal minimal sphere calculations
  16. // store the squared radius in the radius member of Sphere3. Only at
  17. // the end is a sqrt computed.
  18. //
  19. // The most robust choice for ComputeType is BSRational<T> for exact rational
  20. // arithmetic. As long as this code is a correct implementation of the theory
  21. // (which I hope it is), you will obtain the minimum-volume sphere
  22. // containing the points.
  23. //
  24. // Instead, if you choose ComputeType to be float or double, floating-point
  25. // rounding errors can cause the UpdateSupport{2,3,4} functions to fail.
  26. // The failure is trapped in those functions and a simple bounding sphere is
  27. // computed using GetContainer in file GteContSphere3.h. This sphere is
  28. // generally not the minimum-volume sphere containing the points. The
  29. // minimum-volume algorithm is terminated immediately. The sphere is
  30. // returned as well as a bool value of 'true' when the sphere is minimum
  31. // volume or 'false' when the failure is trapped. When 'false' is returned,
  32. // you can try another call to the operator()(...) function. The random
  33. // shuffle that occurs is highly likely to be different from the previous
  34. // shuffle, and there is a chance that the algorithm can succeed just because
  35. // of the different ordering of points.
  36. namespace WwiseGTE
  37. {
  38. template <typename InputType, typename ComputeType>
  39. class MinimumVolumeSphere3
  40. {
  41. public:
  42. bool operator()(int numPoints, Vector3<InputType> const* points, Sphere3<InputType>& minimal)
  43. {
  44. if (numPoints >= 1 && points)
  45. {
  46. // Function array to avoid switch statement in the main loop.
  47. std::function<UpdateResult(int)> update[5];
  48. update[1] = [this](int i) { return UpdateSupport1(i); };
  49. update[2] = [this](int i) { return UpdateSupport2(i); };
  50. update[3] = [this](int i) { return UpdateSupport3(i); };
  51. update[4] = [this](int i) { return UpdateSupport4(i); };
  52. // Process only the unique points.
  53. std::vector<int> permuted(numPoints);
  54. for (int i = 0; i < numPoints; ++i)
  55. {
  56. permuted[i] = i;
  57. }
  58. std::sort(permuted.begin(), permuted.end(),
  59. [points](int i0, int i1) { return points[i0] < points[i1]; });
  60. auto end = std::unique(permuted.begin(), permuted.end(),
  61. [points](int i0, int i1) { return points[i0] == points[i1]; });
  62. permuted.erase(end, permuted.end());
  63. numPoints = static_cast<int>(permuted.size());
  64. // Create a random permutation of the points.
  65. std::shuffle(permuted.begin(), permuted.end(), mDRE);
  66. // Convert to the compute type, which is a simple copy when
  67. // ComputeType is the same as InputType.
  68. mComputePoints.resize(numPoints);
  69. for (int i = 0; i < numPoints; ++i)
  70. {
  71. for (int j = 0; j < 3; ++j)
  72. {
  73. mComputePoints[i][j] = points[permuted[i]][j];
  74. }
  75. }
  76. // Start with the first point.
  77. Sphere3<ComputeType> ctMinimal = ExactSphere1(0);
  78. mNumSupport = 1;
  79. mSupport[0] = 0;
  80. // The loop restarts from the beginning of the point list each
  81. // time the sphere needs updating. Linus Källberg (Computer
  82. // Science at Mälardalen University in Sweden) discovered that
  83. // performance is/ better when the remaining points in the
  84. // array are processed before restarting. The points
  85. // processed before the point that caused the/ update are
  86. // likely to be enclosed by the new sphere (or near the sphere
  87. // boundary) because they were enclosed by the previous
  88. // sphere. The chances are better that points after the
  89. // current one will cause growth of the bounding sphere.
  90. for (int i = 1 % numPoints, n = 0; i != n; i = (i + 1) % numPoints)
  91. {
  92. if (!SupportContains(i))
  93. {
  94. if (!Contains(i, ctMinimal))
  95. {
  96. auto result = update[mNumSupport](i);
  97. if (result.second == true)
  98. {
  99. if (result.first.radius > ctMinimal.radius)
  100. {
  101. ctMinimal = result.first;
  102. n = i;
  103. }
  104. }
  105. else
  106. {
  107. // This case can happen when ComputeType is
  108. // float or double. See the comments at the
  109. // beginning of this file. ComputeType is not
  110. // exact and failure occurred. Returning
  111. // non-minimal circle. TODO: Should we throw
  112. // an exception?
  113. GetContainer(numPoints, points, minimal);
  114. mNumSupport = 0;
  115. mSupport.fill(0);
  116. return false;
  117. }
  118. }
  119. }
  120. }
  121. for (int j = 0; j < 3; ++j)
  122. {
  123. minimal.center[j] = static_cast<InputType>(ctMinimal.center[j]);
  124. }
  125. minimal.radius = static_cast<InputType>(ctMinimal.radius);
  126. minimal.radius = std::sqrt(minimal.radius);
  127. for (int i = 0; i < mNumSupport; ++i)
  128. {
  129. mSupport[i] = permuted[mSupport[i]];
  130. }
  131. return true;
  132. }
  133. else
  134. {
  135. LogError("Input must contain points.");
  136. }
  137. }
  138. // Member access.
  139. inline int GetNumSupport() const
  140. {
  141. return mNumSupport;
  142. }
  143. inline std::array<int, 4> const& GetSupport() const
  144. {
  145. return mSupport;
  146. }
  147. private:
  148. // Test whether point P is inside sphere S using squared distance and
  149. // squared radius.
  150. bool Contains(int i, Sphere3<ComputeType> const& sphere) const
  151. {
  152. // NOTE: In this algorithm, sphere.radius is the *squared radius*
  153. // until the function returns at which time a square root is
  154. // applied.
  155. Vector3<ComputeType> diff = mComputePoints[i] - sphere.center;
  156. return Dot(diff, diff) <= sphere.radius;
  157. }
  158. Sphere3<ComputeType> ExactSphere1(int i0) const
  159. {
  160. Sphere3<ComputeType> minimal;
  161. minimal.center = mComputePoints[i0];
  162. minimal.radius = (ComputeType)0;
  163. return minimal;
  164. }
  165. Sphere3<ComputeType> ExactSphere2(int i0, int i1) const
  166. {
  167. Vector3<ComputeType> const& P0 = mComputePoints[i0];
  168. Vector3<ComputeType> const& P1 = mComputePoints[i1];
  169. Sphere3<ComputeType> minimal;
  170. minimal.center = (ComputeType)0.5 * (P0 + P1);
  171. Vector3<ComputeType> diff = P1 - P0;
  172. minimal.radius = (ComputeType)0.25 * Dot(diff, diff);
  173. return minimal;
  174. }
  175. Sphere3<ComputeType> ExactSphere3(int i0, int i1, int i2) const
  176. {
  177. // Compute the 2D circle containing P0, P1, and P2. The center in
  178. // barycentric coordinates is C = x0*P0 + x1*P1 + x2*P2, where
  179. // x0 + x1 + x2 = 1. The center is equidistant from the three
  180. // points, so |C - P0| = |C - P1| = |C - P2| = R, where R is the
  181. // radius of the circle. From these conditions,
  182. // C - P0 = x0*E0 + x1*E1 - E0
  183. // C - P1 = x0*E0 + x1*E1 - E1
  184. // C - P2 = x0*E0 + x1*E1
  185. // where E0 = P0 - P2 and E1 = P1 - P2, which leads to
  186. // r^2 = |x0*E0 + x1*E1|^2 - 2*Dot(E0, x0*E0 + x1*E1) + |E0|^2
  187. // r^2 = |x0*E0 + x1*E1|^2 - 2*Dot(E1, x0*E0 + x1*E1) + |E1|^2
  188. // r^2 = |x0*E0 + x1*E1|^2
  189. // Subtracting the last equation from the first two and writing
  190. // the equations as a linear system,
  191. //
  192. // +- -++ -+ +- -+
  193. // | Dot(E0,E0) Dot(E0,E1) || x0 | = 0.5 | Dot(E0,E0) |
  194. // | Dot(E1,E0) Dot(E1,E1) || x1 | | Dot(E1,E1) |
  195. // +- -++ -+ +- -+
  196. //
  197. // The following code solves this system for x0 and x1 and then
  198. // evaluates the third equation in r^2 to obtain r.
  199. Vector3<ComputeType> const& P0 = mComputePoints[i0];
  200. Vector3<ComputeType> const& P1 = mComputePoints[i1];
  201. Vector3<ComputeType> const& P2 = mComputePoints[i2];
  202. Vector3<ComputeType> E0 = P0 - P2;
  203. Vector3<ComputeType> E1 = P1 - P2;
  204. Matrix2x2<ComputeType> A;
  205. A(0, 0) = Dot(E0, E0);
  206. A(0, 1) = Dot(E0, E1);
  207. A(1, 0) = A(0, 1);
  208. A(1, 1) = Dot(E1, E1);
  209. ComputeType const half = (ComputeType)0.5;
  210. Vector2<ComputeType> B{ half * A(0, 0), half * A(1, 1) };
  211. Sphere3<ComputeType> minimal;
  212. Vector2<ComputeType> X;
  213. if (LinearSystem<ComputeType>::Solve(A, B, X))
  214. {
  215. ComputeType x2 = (ComputeType)1 - X[0] - X[1];
  216. minimal.center = X[0] * P0 + X[1] * P1 + x2 * P2;
  217. Vector3<ComputeType> tmp = X[0] * E0 + X[1] * E1;
  218. minimal.radius = Dot(tmp, tmp);
  219. }
  220. else
  221. {
  222. minimal.center = Vector3<ComputeType>::Zero();
  223. minimal.radius = (ComputeType)std::numeric_limits<InputType>::max();
  224. }
  225. return minimal;
  226. }
  227. Sphere3<ComputeType> ExactSphere4(int i0, int i1, int i2, int i3) const
  228. {
  229. // Compute the sphere containing P0, P1, P2, and P3. The center
  230. // in barycentric coordinates is
  231. // C = x0*P0 + x1*P1 + x2*P2 + x3*P3,
  232. // where x0 + x1 + x2 + x3 = 1. The center is equidistant from
  233. // the three points, so |C - P0| = |C - P1| = |C - P2| = |C - P3|
  234. // = R, where R is the radius of the sphere. From these
  235. // conditions,
  236. // C - P0 = x0*E0 + x1*E1 + x2*E2 - E0
  237. // C - P1 = x0*E0 + x1*E1 + x2*E2 - E1
  238. // C - P2 = x0*E0 + x1*E1 + x2*E2 - E2
  239. // C - P3 = x0*E0 + x1*E1 + x2*E2
  240. // where E0 = P0 - P3, E1 = P1 - P3, and E2 = P2 - P3, which
  241. // leads to
  242. // r^2 = |x0*E0+x1*E1+x2*E2|^2-2*Dot(E0,x0*E0+x1*E1+x2*E2)+|E0|^2
  243. // r^2 = |x0*E0+x1*E1+x2*E2|^2-2*Dot(E1,x0*E0+x1*E1+x2*E2)+|E1|^2
  244. // r^2 = |x0*E0+x1*E1+x2*E2|^2-2*Dot(E2,x0*E0+x1*E1+x2*E2)+|E2|^2
  245. // r^2 = |x0*E0+x1*E1+x2*E2|^2
  246. // Subtracting the last equation from the first three and writing
  247. // the equations as a linear system,
  248. //
  249. // +- -++ -+ +- -+
  250. // | Dot(E0,E0) Dot(E0,E1) Dot(E0,E2) || x0 | = 0.5 | Dot(E0,E0) |
  251. // | Dot(E1,E0) Dot(E1,E1) Dot(E1,E2) || x1 | | Dot(E1,E1) |
  252. // | Dot(E2,E0) Dot(E2,E1) Dot(E2,E2) || x2 | | Dot(E2,E2) |
  253. // +- -++ -+ +- -+
  254. //
  255. // The following code solves this system for x0, x1, and x2 and
  256. // then evaluates the fourth equation in r^2 to obtain r.
  257. Vector3<ComputeType> const& P0 = mComputePoints[i0];
  258. Vector3<ComputeType> const& P1 = mComputePoints[i1];
  259. Vector3<ComputeType> const& P2 = mComputePoints[i2];
  260. Vector3<ComputeType> const& P3 = mComputePoints[i3];
  261. Vector3<ComputeType> E0 = P0 - P3;
  262. Vector3<ComputeType> E1 = P1 - P3;
  263. Vector3<ComputeType> E2 = P2 - P3;
  264. Matrix3x3<ComputeType> A;
  265. A(0, 0) = Dot(E0, E0);
  266. A(0, 1) = Dot(E0, E1);
  267. A(0, 2) = Dot(E0, E2);
  268. A(1, 0) = A(0, 1);
  269. A(1, 1) = Dot(E1, E1);
  270. A(1, 2) = Dot(E1, E2);
  271. A(2, 0) = A(0, 2);
  272. A(2, 1) = A(1, 2);
  273. A(2, 2) = Dot(E2, E2);
  274. ComputeType const half = (ComputeType)0.5;
  275. Vector3<ComputeType> B{ half * A(0, 0), half * A(1, 1), half * A(2, 2) };
  276. Sphere3<ComputeType> minimal;
  277. Vector3<ComputeType> X;
  278. if (LinearSystem<ComputeType>::Solve(A, B, X))
  279. {
  280. ComputeType x3 = (ComputeType)1 - X[0] - X[1] - X[2];
  281. minimal.center = X[0] * P0 + X[1] * P1 + X[2] * P2 + x3 * P3;
  282. Vector3<ComputeType> tmp = X[0] * E0 + X[1] * E1 + X[2] * E2;
  283. minimal.radius = Dot(tmp, tmp);
  284. }
  285. else
  286. {
  287. minimal.center = Vector3<ComputeType>::Zero();
  288. minimal.radius = (ComputeType)std::numeric_limits<InputType>::max();
  289. }
  290. return minimal;
  291. }
  292. typedef std::pair<Sphere3<ComputeType>, bool> UpdateResult;
  293. UpdateResult UpdateSupport1(int i)
  294. {
  295. Sphere3<ComputeType> minimal = ExactSphere2(mSupport[0], i);
  296. mNumSupport = 2;
  297. mSupport[1] = i;
  298. return std::make_pair(minimal, true);
  299. }
  300. UpdateResult UpdateSupport2(int i)
  301. {
  302. // Permutations of type 2, used for calling ExactSphere2(...).
  303. int const numType2 = 2;
  304. int const type2[numType2][2] =
  305. {
  306. { 0, /*2*/ 1 },
  307. { 1, /*2*/ 0 }
  308. };
  309. // Permutations of type 3, used for calling ExactSphere3(...).
  310. int const numType3 = 1; // {0, 1, 2}
  311. Sphere3<ComputeType> sphere[numType2 + numType3];
  312. ComputeType minRSqr = (ComputeType)std::numeric_limits<InputType>::max();
  313. int iSphere = 0, iMinRSqr = -1;
  314. int k0, k1;
  315. // Permutations of type 2.
  316. for (int j = 0; j < numType2; ++j, ++iSphere)
  317. {
  318. k0 = mSupport[type2[j][0]];
  319. sphere[iSphere] = ExactSphere2(k0, i);
  320. if (sphere[iSphere].radius < minRSqr)
  321. {
  322. k1 = mSupport[type2[j][1]];
  323. if (Contains(k1, sphere[iSphere]))
  324. {
  325. minRSqr = sphere[iSphere].radius;
  326. iMinRSqr = iSphere;
  327. }
  328. }
  329. }
  330. // Permutations of type 3.
  331. k0 = mSupport[0];
  332. k1 = mSupport[1];
  333. sphere[iSphere] = ExactSphere3(k0, k1, i);
  334. if (sphere[iSphere].radius < minRSqr)
  335. {
  336. minRSqr = sphere[iSphere].radius;
  337. iMinRSqr = iSphere;
  338. }
  339. switch (iMinRSqr)
  340. {
  341. case 0:
  342. mSupport[1] = i;
  343. break;
  344. case 1:
  345. mSupport[0] = i;
  346. break;
  347. case 2:
  348. mNumSupport = 3;
  349. mSupport[2] = i;
  350. break;
  351. case -1:
  352. // For exact arithmetic, iMinRSqr >= 0, but for floating-point
  353. // arithmetic, round-off errors can lead to iMinRSqr == -1.
  354. // When this happens, use a simple bounding sphere for the
  355. // result and terminate the minimum-volume algorithm.
  356. return std::make_pair(Sphere3<ComputeType>(), false);
  357. }
  358. return std::make_pair(sphere[iMinRSqr], true);
  359. }
  360. UpdateResult UpdateSupport3(int i)
  361. {
  362. // Permutations of type 2, used for calling ExactSphere2(...).
  363. int const numType2 = 3;
  364. int const type2[numType2][3] =
  365. {
  366. { 0, /*3*/ 1, 2 },
  367. { 1, /*3*/ 0, 2 },
  368. { 2, /*3*/ 0, 1 }
  369. };
  370. // Permutations of type 3, used for calling ExactSphere3(...).
  371. int const numType3 = 3;
  372. int const type3[numType3][3] =
  373. {
  374. { 0, 1, /*3*/ 2 },
  375. { 0, 2, /*3*/ 1 },
  376. { 1, 2, /*3*/ 0 }
  377. };
  378. // Permutations of type 4, used for calling ExactSphere4(...).
  379. int const numType4 = 1; // {0, 1, 2, 3}
  380. Sphere3<ComputeType> sphere[numType2 + numType3 + numType4];
  381. ComputeType minRSqr = (ComputeType)std::numeric_limits<InputType>::max();
  382. int iSphere = 0, iMinRSqr = -1;
  383. int k0, k1, k2;
  384. // Permutations of type 2.
  385. for (int j = 0; j < numType2; ++j, ++iSphere)
  386. {
  387. k0 = mSupport[type2[j][0]];
  388. sphere[iSphere] = ExactSphere2(k0, i);
  389. if (sphere[iSphere].radius < minRSqr)
  390. {
  391. k1 = mSupport[type2[j][1]];
  392. k2 = mSupport[type2[j][2]];
  393. if (Contains(k1, sphere[iSphere]) && Contains(k2, sphere[iSphere]))
  394. {
  395. minRSqr = sphere[iSphere].radius;
  396. iMinRSqr = iSphere;
  397. }
  398. }
  399. }
  400. // Permutations of type 3.
  401. for (int j = 0; j < numType3; ++j, ++iSphere)
  402. {
  403. k0 = mSupport[type3[j][0]];
  404. k1 = mSupport[type3[j][1]];
  405. sphere[iSphere] = ExactSphere3(k0, k1, i);
  406. if (sphere[iSphere].radius < minRSqr)
  407. {
  408. k2 = mSupport[type3[j][2]];
  409. if (Contains(k2, sphere[iSphere]))
  410. {
  411. minRSqr = sphere[iSphere].radius;
  412. iMinRSqr = iSphere;
  413. }
  414. }
  415. }
  416. // Permutations of type 4.
  417. k0 = mSupport[0];
  418. k1 = mSupport[1];
  419. k2 = mSupport[2];
  420. sphere[iSphere] = ExactSphere4(k0, k1, k2, i);
  421. if (sphere[iSphere].radius < minRSqr)
  422. {
  423. minRSqr = sphere[iSphere].radius;
  424. iMinRSqr = iSphere;
  425. }
  426. switch (iMinRSqr)
  427. {
  428. case 0:
  429. mNumSupport = 2;
  430. mSupport[1] = i;
  431. break;
  432. case 1:
  433. mNumSupport = 2;
  434. mSupport[0] = i;
  435. break;
  436. case 2:
  437. mNumSupport = 2;
  438. mSupport[0] = mSupport[2];
  439. mSupport[1] = i;
  440. break;
  441. case 3:
  442. mSupport[2] = i;
  443. break;
  444. case 4:
  445. mSupport[1] = i;
  446. break;
  447. case 5:
  448. mSupport[0] = i;
  449. break;
  450. case 6:
  451. mNumSupport = 4;
  452. mSupport[3] = i;
  453. break;
  454. case -1:
  455. // For exact arithmetic, iMinRSqr >= 0, but for floating-point
  456. // arithmetic, round-off errors can lead to iMinRSqr == -1.
  457. // When this happens, use a simple bounding sphere for the
  458. // result and terminate the minimum-area algorithm.
  459. return std::make_pair(Sphere3<ComputeType>(), false);
  460. }
  461. return std::make_pair(sphere[iMinRSqr], true);
  462. }
  463. UpdateResult UpdateSupport4(int i)
  464. {
  465. // Permutations of type 2, used for calling ExactSphere2(...).
  466. int const numType2 = 4;
  467. int const type2[numType2][4] =
  468. {
  469. { 0, /*4*/ 1, 2, 3 },
  470. { 1, /*4*/ 0, 2, 3 },
  471. { 2, /*4*/ 0, 1, 3 },
  472. { 3, /*4*/ 0, 1, 2 }
  473. };
  474. // Permutations of type 3, used for calling ExactSphere3(...).
  475. int const numType3 = 6;
  476. int const type3[numType3][4] =
  477. {
  478. { 0, 1, /*4*/ 2, 3 },
  479. { 0, 2, /*4*/ 1, 3 },
  480. { 0, 3, /*4*/ 1, 2 },
  481. { 1, 2, /*4*/ 0, 3 },
  482. { 1, 3, /*4*/ 0, 2 },
  483. { 2, 3, /*4*/ 0, 1 }
  484. };
  485. // Permutations of type 4, used for calling ExactSphere4(...).
  486. int const numType4 = 4;
  487. int const type4[numType4][4] =
  488. {
  489. { 0, 1, 2, /*4*/ 3 },
  490. { 0, 1, 3, /*4*/ 2 },
  491. { 0, 2, 3, /*4*/ 1 },
  492. { 1, 2, 3, /*4*/ 0 }
  493. };
  494. Sphere3<ComputeType> sphere[numType2 + numType3 + numType4];
  495. ComputeType minRSqr = (ComputeType)std::numeric_limits<InputType>::max();
  496. int iSphere = 0, iMinRSqr = -1;
  497. int k0, k1, k2, k3;
  498. // Permutations of type 2.
  499. for (int j = 0; j < numType2; ++j, ++iSphere)
  500. {
  501. k0 = mSupport[type2[j][0]];
  502. sphere[iSphere] = ExactSphere2(k0, i);
  503. if (sphere[iSphere].radius < minRSqr)
  504. {
  505. k1 = mSupport[type2[j][1]];
  506. k2 = mSupport[type2[j][2]];
  507. k3 = mSupport[type2[j][3]];
  508. if (Contains(k1, sphere[iSphere]) && Contains(k2, sphere[iSphere]) && Contains(k3, sphere[iSphere]))
  509. {
  510. minRSqr = sphere[iSphere].radius;
  511. iMinRSqr = iSphere;
  512. }
  513. }
  514. }
  515. // Permutations of type 3.
  516. for (int j = 0; j < numType3; ++j, ++iSphere)
  517. {
  518. k0 = mSupport[type3[j][0]];
  519. k1 = mSupport[type3[j][1]];
  520. sphere[iSphere] = ExactSphere3(k0, k1, i);
  521. if (sphere[iSphere].radius < minRSqr)
  522. {
  523. k2 = mSupport[type3[j][2]];
  524. k3 = mSupport[type3[j][3]];
  525. if (Contains(k2, sphere[iSphere]) && Contains(k3, sphere[iSphere]))
  526. {
  527. minRSqr = sphere[iSphere].radius;
  528. iMinRSqr = iSphere;
  529. }
  530. }
  531. }
  532. // Permutations of type 4.
  533. for (int j = 0; j < numType4; ++j, ++iSphere)
  534. {
  535. k0 = mSupport[type4[j][0]];
  536. k1 = mSupport[type4[j][1]];
  537. k2 = mSupport[type4[j][2]];
  538. sphere[iSphere] = ExactSphere4(k0, k1, k2, i);
  539. if (sphere[iSphere].radius < minRSqr)
  540. {
  541. k3 = mSupport[type4[j][3]];
  542. if (Contains(k3, sphere[iSphere]))
  543. {
  544. minRSqr = sphere[iSphere].radius;
  545. iMinRSqr = iSphere;
  546. }
  547. }
  548. }
  549. switch (iMinRSqr)
  550. {
  551. case 0:
  552. mNumSupport = 2;
  553. mSupport[1] = i;
  554. break;
  555. case 1:
  556. mNumSupport = 2;
  557. mSupport[0] = i;
  558. break;
  559. case 2:
  560. mNumSupport = 2;
  561. mSupport[0] = mSupport[2];
  562. mSupport[1] = i;
  563. break;
  564. case 3:
  565. mNumSupport = 2;
  566. mSupport[0] = mSupport[3];
  567. mSupport[1] = i;
  568. break;
  569. case 4:
  570. mNumSupport = 3;
  571. mSupport[2] = i;
  572. break;
  573. case 5:
  574. mNumSupport = 3;
  575. mSupport[1] = i;
  576. break;
  577. case 6:
  578. mNumSupport = 3;
  579. mSupport[1] = mSupport[3];
  580. mSupport[2] = i;
  581. break;
  582. case 7:
  583. mNumSupport = 3;
  584. mSupport[0] = i;
  585. break;
  586. case 8:
  587. mNumSupport = 3;
  588. mSupport[0] = mSupport[3];
  589. mSupport[2] = i;
  590. break;
  591. case 9:
  592. mNumSupport = 3;
  593. mSupport[0] = mSupport[3];
  594. mSupport[1] = i;
  595. break;
  596. case 10:
  597. mSupport[3] = i;
  598. break;
  599. case 11:
  600. mSupport[2] = i;
  601. break;
  602. case 12:
  603. mSupport[1] = i;
  604. break;
  605. case 13:
  606. mSupport[0] = i;
  607. break;
  608. case -1:
  609. // For exact arithmetic, iMinRSqr >= 0, but for floating-point
  610. // arithmetic, round-off errors can lead to iMinRSqr == -1.
  611. // When this happens, use a simple bounding sphere for the
  612. // result and terminate the minimum-area algorithm.
  613. return std::make_pair(Sphere3<ComputeType>(), false);
  614. }
  615. return std::make_pair(sphere[iMinRSqr], true);
  616. }
  617. // Indices of points that support current minimum volume sphere.
  618. bool SupportContains(int j) const
  619. {
  620. for (int i = 0; i < mNumSupport; ++i)
  621. {
  622. if (j == mSupport[i])
  623. {
  624. return true;
  625. }
  626. }
  627. return false;
  628. }
  629. int mNumSupport;
  630. std::array<int, 4> mSupport;
  631. // Random permutation of the unique input points to produce expected
  632. // linear time for the algorithm.
  633. std::default_random_engine mDRE;
  634. std::vector<Vector3<ComputeType>> mComputePoints;
  635. };
  636. }