MinimumAreaCircle2.h 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  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/ContCircle2.h>
  10. #include <Mathematics/Hypersphere.h>
  11. #include <Mathematics/LinearSystem.h>
  12. #include <functional>
  13. #include <random>
  14. // Compute the minimum area circle containing the input set of points. The
  15. // algorithm randomly permutes the input points so that the construction
  16. // occurs in 'expected' O(N) time. All internal minimal circle calculations
  17. // store the squared radius in the radius member of Circle2. Only at the
  18. // end is a sqrt computed.
  19. //
  20. // The most robust choice for ComputeType is BSRational<T> for exact rational
  21. // arithmetic. As long as this code is a correct implementation of the theory
  22. // (which I hope it is), you will obtain the minimum-area circle containing
  23. // the points.
  24. //
  25. // Instead, if you choose ComputeType to be float or double, floating-point
  26. // rounding errors can cause the UpdateSupport{2,3} functions to fail.
  27. // The failure is trapped in those functions and a simple bounding circle is
  28. // computed using GetContainer in file GteContCircle2.h. This circle is
  29. // generally not the minimum-area circle containing the points. The
  30. // minimum-area algorithm is terminated immediately. The circle is returned
  31. // as well as a bool value of 'true' when the circle is minimum area or
  32. // 'false' when the failure is trapped. When 'false' is returned, you can
  33. // try another call to the operator()(...) function. The random shuffle
  34. // that occurs is highly likely to be different from the previous shuffle,
  35. // and there is a chance that the algorithm can succeed just because of the
  36. // different ordering of points.
  37. namespace WwiseGTE
  38. {
  39. template <typename InputType, typename ComputeType>
  40. class MinimumAreaCircle2
  41. {
  42. public:
  43. bool operator()(int numPoints, Vector2<InputType> const* points, Circle2<InputType>& minimal)
  44. {
  45. if (numPoints >= 1 && points)
  46. {
  47. // Function array to avoid switch statement in the main loop.
  48. std::function<UpdateResult(int)> update[4];
  49. update[1] = [this](int i) { return UpdateSupport1(i); };
  50. update[2] = [this](int i) { return UpdateSupport2(i); };
  51. update[3] = [this](int i) { return UpdateSupport3(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 < 2; ++j)
  72. {
  73. mComputePoints[i][j] = points[permuted[i]][j];
  74. }
  75. }
  76. // Start with the first point.
  77. Circle2<ComputeType> ctMinimal = ExactCircle1(0);
  78. mNumSupport = 1;
  79. mSupport[0] = 0;
  80. // The loop restarts from the beginning of the point list each
  81. // time the circle 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 circle (or near the circle
  87. // boundary) because they were enclosed by the previous
  88. // circle. The chances are better that points after the
  89. // current one will cause growth of the bounding circle.
  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 < 2; ++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, 3> const& GetSupport() const
  144. {
  145. return mSupport;
  146. }
  147. private:
  148. // Test whether point P is inside circle C using squared distance and
  149. // squared radius.
  150. bool Contains(int i, Circle2<ComputeType> const& circle) const
  151. {
  152. // NOTE: In this algorithm, circle.radius is the *squared radius*
  153. // until the function returns at which time a square root is
  154. // applied.
  155. Vector2<ComputeType> diff = mComputePoints[i] - circle.center;
  156. return Dot(diff, diff) <= circle.radius;
  157. }
  158. Circle2<ComputeType> ExactCircle1(int i0) const
  159. {
  160. Circle2<ComputeType> minimal;
  161. minimal.center = mComputePoints[i0];
  162. minimal.radius = (ComputeType)0;
  163. return minimal;
  164. }
  165. Circle2<ComputeType> ExactCircle2(int i0, int i1) const
  166. {
  167. Vector2<ComputeType> const& P0 = mComputePoints[i0];
  168. Vector2<ComputeType> const& P1 = mComputePoints[i1];
  169. Vector2<ComputeType> diff = P1 - P0;
  170. Circle2<ComputeType> minimal;
  171. minimal.center = ((ComputeType)0.5)*(P0 + P1);
  172. minimal.radius = ((ComputeType)0.25)*Dot(diff, diff);
  173. return minimal;
  174. }
  175. Circle2<ComputeType> ExactCircle3(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. Vector2<ComputeType> const& P0 = mComputePoints[i0];
  200. Vector2<ComputeType> const& P1 = mComputePoints[i1];
  201. Vector2<ComputeType> const& P2 = mComputePoints[i2];
  202. Vector2<ComputeType> E0 = P0 - P2;
  203. Vector2<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. Circle2<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. Vector2<ComputeType> tmp = X[0] * E0 + X[1] * E1;
  218. minimal.radius = Dot(tmp, tmp);
  219. }
  220. else
  221. {
  222. minimal.center = Vector2<ComputeType>::Zero();
  223. minimal.radius = (ComputeType)std::numeric_limits<InputType>::max();
  224. }
  225. return minimal;
  226. }
  227. typedef std::pair<Circle2<ComputeType>, bool> UpdateResult;
  228. UpdateResult UpdateSupport1(int i)
  229. {
  230. Circle2<ComputeType> minimal = ExactCircle2(mSupport[0], i);
  231. mNumSupport = 2;
  232. mSupport[1] = i;
  233. return std::make_pair(minimal, true);
  234. }
  235. UpdateResult UpdateSupport2(int i)
  236. {
  237. // Permutations of type 2, used for calling ExactCircle2(...).
  238. int const numType2 = 2;
  239. int const type2[numType2][2] =
  240. {
  241. { 0, /*2*/ 1 },
  242. { 1, /*2*/ 0 }
  243. };
  244. // Permutations of type 3, used for calling ExactCircle3(...).
  245. int const numType3 = 1; // {0, 1, 2}
  246. Circle2<ComputeType> circle[numType2 + numType3];
  247. ComputeType minRSqr = (ComputeType)std::numeric_limits<InputType>::max();
  248. int iCircle = 0, iMinRSqr = -1;
  249. int k0, k1;
  250. // Permutations of type 2.
  251. for (int j = 0; j < numType2; ++j, ++iCircle)
  252. {
  253. k0 = mSupport[type2[j][0]];
  254. circle[iCircle] = ExactCircle2(k0, i);
  255. if (circle[iCircle].radius < minRSqr)
  256. {
  257. k1 = mSupport[type2[j][1]];
  258. if (Contains(k1, circle[iCircle]))
  259. {
  260. minRSqr = circle[iCircle].radius;
  261. iMinRSqr = iCircle;
  262. }
  263. }
  264. }
  265. // Permutations of type 3.
  266. k0 = mSupport[0];
  267. k1 = mSupport[1];
  268. circle[iCircle] = ExactCircle3(k0, k1, i);
  269. if (circle[iCircle].radius < minRSqr)
  270. {
  271. minRSqr = circle[iCircle].radius;
  272. iMinRSqr = iCircle;
  273. }
  274. switch (iMinRSqr)
  275. {
  276. case 0:
  277. mSupport[1] = i;
  278. break;
  279. case 1:
  280. mSupport[0] = i;
  281. break;
  282. case 2:
  283. mNumSupport = 3;
  284. mSupport[2] = i;
  285. break;
  286. case -1:
  287. // For exact arithmetic, iMinRSqr >= 0, but for floating-point
  288. // arithmetic, round-off errors can lead to iMinRSqr == -1.
  289. // When this happens, use a simple bounding circle for the
  290. // result and terminate the minimum-area algorithm.
  291. return std::make_pair(Circle2<ComputeType>(), false);
  292. }
  293. return std::make_pair(circle[iMinRSqr], true);
  294. }
  295. UpdateResult UpdateSupport3(int i)
  296. {
  297. // Permutations of type 2, used for calling ExactCircle2(...).
  298. int const numType2 = 3;
  299. int const type2[numType2][3] =
  300. {
  301. { 0, /*3*/ 1, 2 },
  302. { 1, /*3*/ 0, 2 },
  303. { 2, /*3*/ 0, 1 }
  304. };
  305. // Permutations of type 2, used for calling ExactCircle3(...).
  306. int const numType3 = 3;
  307. int const type3[numType3][3] =
  308. {
  309. { 0, 1, /*3*/ 2 },
  310. { 0, 2, /*3*/ 1 },
  311. { 1, 2, /*3*/ 0 }
  312. };
  313. Circle2<ComputeType> circle[numType2 + numType3];
  314. ComputeType minRSqr = (ComputeType)std::numeric_limits<InputType>::max();
  315. int iCircle = 0, iMinRSqr = -1;
  316. int k0, k1, k2;
  317. // Permutations of type 2.
  318. for (int j = 0; j < numType2; ++j, ++iCircle)
  319. {
  320. k0 = mSupport[type2[j][0]];
  321. circle[iCircle] = ExactCircle2(k0, i);
  322. if (circle[iCircle].radius < minRSqr)
  323. {
  324. k1 = mSupport[type2[j][1]];
  325. k2 = mSupport[type2[j][2]];
  326. if (Contains(k1, circle[iCircle]) && Contains(k2, circle[iCircle]))
  327. {
  328. minRSqr = circle[iCircle].radius;
  329. iMinRSqr = iCircle;
  330. }
  331. }
  332. }
  333. // Permutations of type 3.
  334. for (int j = 0; j < numType3; ++j, ++iCircle)
  335. {
  336. k0 = mSupport[type3[j][0]];
  337. k1 = mSupport[type3[j][1]];
  338. circle[iCircle] = ExactCircle3(k0, k1, i);
  339. if (circle[iCircle].radius < minRSqr)
  340. {
  341. k2 = mSupport[type3[j][2]];
  342. if (Contains(k2, circle[iCircle]))
  343. {
  344. minRSqr = circle[iCircle].radius;
  345. iMinRSqr = iCircle;
  346. }
  347. }
  348. }
  349. switch (iMinRSqr)
  350. {
  351. case 0:
  352. mNumSupport = 2;
  353. mSupport[1] = i;
  354. break;
  355. case 1:
  356. mNumSupport = 2;
  357. mSupport[0] = i;
  358. break;
  359. case 2:
  360. mNumSupport = 2;
  361. mSupport[0] = mSupport[2];
  362. mSupport[1] = i;
  363. break;
  364. case 3:
  365. mSupport[2] = i;
  366. break;
  367. case 4:
  368. mSupport[1] = i;
  369. break;
  370. case 5:
  371. mSupport[0] = i;
  372. break;
  373. case -1:
  374. // For exact arithmetic, iMinRSqr >= 0, but for floating-point
  375. // arithmetic, round-off errors can lead to iMinRSqr == -1.
  376. // When this happens, use a simple bounding circle for the
  377. // result and terminate the minimum-area algorithm.
  378. return std::make_pair(Circle2<ComputeType>(), false);
  379. }
  380. return std::make_pair(circle[iMinRSqr], true);
  381. }
  382. // Indices of points that support the current minimum area circle.
  383. bool SupportContains(int j) const
  384. {
  385. for (int i = 0; i < mNumSupport; ++i)
  386. {
  387. if (j == mSupport[i])
  388. {
  389. return true;
  390. }
  391. }
  392. return false;
  393. }
  394. int mNumSupport;
  395. std::array<int, 3> mSupport;
  396. // Random permutation of the unique input points to produce expected
  397. // linear time for the algorithm.
  398. std::default_random_engine mDRE;
  399. std::vector<Vector2<ComputeType>> mComputePoints;
  400. };
  401. }