DistCircle3Circle3.h 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  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/DCPQuery.h>
  10. #include <Mathematics/Circle3.h>
  11. #include <Mathematics/Polynomial1.h>
  12. #include <Mathematics/RootsPolynomial.h>
  13. #include <set>
  14. // The 3D circle-circle distance algorithm is described in
  15. // https://www.geometrictools.com/Documentation/DistanceToCircle3.pdf
  16. // The notation used in the code matches that of the document.
  17. namespace WwiseGTE
  18. {
  19. template <typename Real>
  20. class DCPQuery<Real, Circle3<Real>, Circle3<Real>>
  21. {
  22. public:
  23. struct Result
  24. {
  25. Real distance, sqrDistance;
  26. int numClosestPairs;
  27. Vector3<Real> circle0Closest[2], circle1Closest[2];
  28. bool equidistant;
  29. };
  30. Result operator()(Circle3<Real> const& circle0, Circle3<Real> const& circle1)
  31. {
  32. Result result;
  33. Vector3<Real> const vzero = Vector3<Real>::Zero();
  34. Real const zero = (Real)0;
  35. Vector3<Real> N0 = circle0.normal, N1 = circle1.normal;
  36. Real r0 = circle0.radius, r1 = circle1.radius;
  37. Vector3<Real> D = circle1.center - circle0.center;
  38. Vector3<Real> N0xN1 = Cross(N0, N1);
  39. if (N0xN1 != vzero)
  40. {
  41. // Get parameters for constructing the degree-8 polynomial phi.
  42. Real const one = (Real)1, two = (Real)2;
  43. Real r0sqr = r0 * r0, r1sqr = r1 * r1;
  44. // Compute U1 and V1 for the plane of circle1.
  45. Vector3<Real> basis[3];
  46. basis[0] = circle1.normal;
  47. ComputeOrthogonalComplement(1, basis);
  48. Vector3<Real> U1 = basis[1], V1 = basis[2];
  49. // Construct the polynomial phi(cos(theta)).
  50. Vector3<Real> N0xD = Cross(N0, D);
  51. Vector3<Real> N0xU1 = Cross(N0, U1), N0xV1 = Cross(N0, V1);
  52. Real a0 = r1 * Dot(D, U1), a1 = r1 * Dot(D, V1);
  53. Real a2 = Dot(N0xD, N0xD), a3 = r1 * Dot(N0xD, N0xU1);
  54. Real a4 = r1 * Dot(N0xD, N0xV1), a5 = r1sqr * Dot(N0xU1, N0xU1);
  55. Real a6 = r1sqr * Dot(N0xU1, N0xV1), a7 = r1sqr * Dot(N0xV1, N0xV1);
  56. Polynomial1<Real> p0{ a2 + a7, two * a3, a5 - a7 };
  57. Polynomial1<Real> p1{ two * a4, two * a6 };
  58. Polynomial1<Real> p2{ zero, a1 };
  59. Polynomial1<Real> p3{ -a0 };
  60. Polynomial1<Real> p4{ -a6, a4, two * a6 };
  61. Polynomial1<Real> p5{ -a3, a7 - a5 };
  62. Polynomial1<Real> tmp0{ one, zero, -one };
  63. Polynomial1<Real> tmp1 = p2 * p2 + tmp0 * p3 * p3;
  64. Polynomial1<Real> tmp2 = two * p2 * p3;
  65. Polynomial1<Real> tmp3 = p4 * p4 + tmp0 * p5 * p5;
  66. Polynomial1<Real> tmp4 = two * p4 * p5;
  67. Polynomial1<Real> p6 = p0 * tmp1 + tmp0 * p1 * tmp2 - r0sqr * tmp3;
  68. Polynomial1<Real> p7 = p0 * tmp2 + p1 * tmp1 - r0sqr * tmp4;
  69. // The use of 'double' is intentional in case Real is a BSNumber or
  70. // BSRational type. We want the bisections to terminate in a
  71. // reasonable amount of time.
  72. unsigned int const maxIterations = GTE_C_MAX_BISECTIONS_GENERIC;
  73. Real roots[8], sn, temp;
  74. int i, degree, numRoots;
  75. // The RootsPolynomial<Real>::Find(...) function currently does not
  76. // combine duplicate roots. We need only the unique ones here.
  77. std::set<Real> uniqueRoots;
  78. std::array<std::pair<Real, Real>, 16> pairs;
  79. int numPairs = 0;
  80. if (p7.GetDegree() > 0 || p7[0] != zero)
  81. {
  82. // H(cs,sn) = p6(cs) + sn * p7(cs)
  83. Polynomial1<Real> phi = p6 * p6 - tmp0 * p7 * p7;
  84. degree = static_cast<int>(phi.GetDegree());
  85. LogAssert(degree > 0, "Unexpected degree for phi.");
  86. numRoots = RootsPolynomial<Real>::Find(degree, &phi[0], maxIterations, roots);
  87. for (i = 0; i < numRoots; ++i)
  88. {
  89. uniqueRoots.insert(roots[i]);
  90. }
  91. for (auto cs : uniqueRoots)
  92. {
  93. if (std::fabs(cs) <= one)
  94. {
  95. temp = p7(cs);
  96. if (temp != zero)
  97. {
  98. sn = -p6(cs) / temp;
  99. pairs[numPairs++] = std::make_pair(cs, sn);
  100. }
  101. else
  102. {
  103. temp = std::max(one - cs * cs, zero);
  104. sn = std::sqrt(temp);
  105. pairs[numPairs++] = std::make_pair(cs, sn);
  106. if (sn != zero)
  107. {
  108. pairs[numPairs++] = std::make_pair(cs, -sn);
  109. }
  110. }
  111. }
  112. }
  113. }
  114. else
  115. {
  116. // H(cs,sn) = p6(cs)
  117. degree = static_cast<int>(p6.GetDegree());
  118. LogAssert(degree > 0, "Unexpected degree for p6.");
  119. numRoots = RootsPolynomial<Real>::Find(degree, &p6[0], maxIterations, roots);
  120. for (i = 0; i < numRoots; ++i)
  121. {
  122. uniqueRoots.insert(roots[i]);
  123. }
  124. for (auto cs : uniqueRoots)
  125. {
  126. if (std::fabs(cs) <= one)
  127. {
  128. temp = std::max(one - cs * cs, zero);
  129. sn = std::sqrt(temp);
  130. pairs[numPairs++] = std::make_pair(cs, sn);
  131. if (sn != zero)
  132. {
  133. pairs[numPairs++] = std::make_pair(cs, -sn);
  134. }
  135. }
  136. }
  137. }
  138. std::array<ClosestInfo, 16> candidates;
  139. for (i = 0; i < numPairs; ++i)
  140. {
  141. ClosestInfo& info = candidates[i];
  142. Vector3<Real> delta =
  143. D + r1 * (pairs[i].first * U1 + pairs[i].second * V1);
  144. info.circle1Closest = circle0.center + delta;
  145. Real N0dDelta = Dot(N0, delta);
  146. Real lenN0xDelta = Length(Cross(N0, delta));
  147. if (lenN0xDelta > 0)
  148. {
  149. Real diff = lenN0xDelta - r0;
  150. info.sqrDistance = N0dDelta * N0dDelta + diff * diff;
  151. delta -= N0dDelta * circle0.normal;
  152. Normalize(delta);
  153. info.circle0Closest = circle0.center + r0 * delta;
  154. info.equidistant = false;
  155. }
  156. else
  157. {
  158. Vector3<Real> r0U0 = r0 * GetOrthogonal(N0, true);
  159. Vector3<Real> diff = delta - r0U0;
  160. info.sqrDistance = Dot(diff, diff);
  161. info.circle0Closest = circle0.center + r0U0;
  162. info.equidistant = true;
  163. }
  164. }
  165. std::sort(candidates.begin(), candidates.begin() + numPairs);
  166. result.numClosestPairs = 1;
  167. result.sqrDistance = candidates[0].sqrDistance;
  168. result.circle0Closest[0] = candidates[0].circle0Closest;
  169. result.circle1Closest[0] = candidates[0].circle1Closest;
  170. result.equidistant = candidates[0].equidistant;
  171. if (numRoots > 1
  172. && candidates[1].sqrDistance == candidates[0].sqrDistance)
  173. {
  174. result.numClosestPairs = 2;
  175. result.circle0Closest[1] = candidates[1].circle0Closest;
  176. result.circle1Closest[1] = candidates[1].circle1Closest;
  177. }
  178. }
  179. else
  180. {
  181. // The planes of the circles are parallel. Whether the planes
  182. // are the same or different, the problem reduces to
  183. // determining how two circles in the same plane are
  184. // separated, tangent with one circle outside the other,
  185. // overlapping, or one circle contained inside the other
  186. // circle.
  187. DoQueryParallelPlanes(circle0, circle1, D, result);
  188. }
  189. result.distance = std::sqrt(result.sqrDistance);
  190. return result;
  191. }
  192. private:
  193. class SCPolynomial
  194. {
  195. public:
  196. SCPolynomial()
  197. {
  198. }
  199. SCPolynomial(Real oneTerm, Real cosTerm, Real sinTerm)
  200. {
  201. mPoly[0] = Polynomial1<Real>{ oneTerm, cosTerm };
  202. mPoly[1] = Polynomial1<Real>{ sinTerm };
  203. }
  204. inline Polynomial1<Real> const& operator[] (unsigned int i) const
  205. {
  206. return mPoly[i];
  207. }
  208. inline Polynomial1<Real>& operator[] (unsigned int i)
  209. {
  210. return mPoly[i];
  211. }
  212. SCPolynomial operator+(SCPolynomial const& object) const
  213. {
  214. SCPolynomial result;
  215. result.mPoly[0] = mPoly[0] + object.mPoly[0];
  216. result.mPoly[1] = mPoly[1] + object.mPoly[1];
  217. return result;
  218. }
  219. SCPolynomial operator-(SCPolynomial const& object) const
  220. {
  221. SCPolynomial result;
  222. result.mPoly[0] = mPoly[0] - object.mPoly[0];
  223. result.mPoly[1] = mPoly[1] - object.mPoly[1];
  224. return result;
  225. }
  226. SCPolynomial operator*(SCPolynomial const& object) const
  227. {
  228. // 1 - c^2
  229. Polynomial1<Real> omcsqr{ (Real)1, (Real)0, (Real)-1 };
  230. SCPolynomial result;
  231. result.mPoly[0] = mPoly[0] * object.mPoly[0] + omcsqr * mPoly[1] * object.mPoly[1];
  232. result.mPoly[1] = mPoly[0] * object.mPoly[1] + mPoly[1] * object.mPoly[0];
  233. return result;
  234. }
  235. SCPolynomial operator*(Real scalar) const
  236. {
  237. SCPolynomial result;
  238. result.mPoly[0] = scalar * mPoly[0];
  239. result.mPoly[1] = scalar * mPoly[1];
  240. return result;
  241. }
  242. private:
  243. // poly0(c) + s * poly1(c)
  244. Polynomial1<Real> mPoly[2];
  245. };
  246. struct ClosestInfo
  247. {
  248. Real sqrDistance;
  249. Vector3<Real> circle0Closest, circle1Closest;
  250. bool equidistant;
  251. inline bool operator< (ClosestInfo const& info) const
  252. {
  253. return sqrDistance < info.sqrDistance;
  254. }
  255. };
  256. // The two circles are in parallel planes where D = C1 - C0, the
  257. // difference of circle centers.
  258. void DoQueryParallelPlanes(Circle3<Real> const& circle0,
  259. Circle3<Real> const& circle1, Vector3<Real> const& D, Result& result)
  260. {
  261. Real N0dD = Dot(circle0.normal, D);
  262. Vector3<Real> normProj = N0dD * circle0.normal;
  263. Vector3<Real> compProj = D - normProj;
  264. Vector3<Real> U = compProj;
  265. Real d = Normalize(U);
  266. // The configuration is determined by the relative location of the
  267. // intervals of projection of the circles on to the D-line.
  268. // Circle0 projects to [-r0,r0] and circle1 projects to
  269. // [d-r1,d+r1].
  270. Real r0 = circle0.radius, r1 = circle1.radius;
  271. Real dmr1 = d - r1;
  272. Real distance;
  273. if (dmr1 >= r0) // d >= r0 + r1
  274. {
  275. // The circles are separated (d > r0 + r1) or tangent with one
  276. // outside the other (d = r0 + r1).
  277. distance = dmr1 - r0;
  278. result.numClosestPairs = 1;
  279. result.circle0Closest[0] = circle0.center + r0 * U;
  280. result.circle1Closest[0] = circle1.center - r1 * U;
  281. result.equidistant = false;
  282. }
  283. else // d < r0 + r1
  284. {
  285. // The cases implicitly use the knowledge that d >= 0.
  286. Real dpr1 = d + r1;
  287. if (dpr1 <= r0)
  288. {
  289. // Circle1 is inside circle0.
  290. distance = r0 - dpr1;
  291. result.numClosestPairs = 1;
  292. if (d > (Real)0)
  293. {
  294. result.circle0Closest[0] = circle0.center + r0 * U;
  295. result.circle1Closest[0] = circle1.center + r1 * U;
  296. result.equidistant = false;
  297. }
  298. else
  299. {
  300. // The circles are concentric, so U = (0,0,0).
  301. // Construct a vector perpendicular to N0 to use for
  302. // closest points.
  303. U = GetOrthogonal(circle0.normal, true);
  304. result.circle0Closest[0] = circle0.center + r0 * U;
  305. result.circle1Closest[0] = circle1.center + r1 * U;
  306. result.equidistant = true;
  307. }
  308. }
  309. else if (dmr1 <= -r0)
  310. {
  311. // Circle0 is inside circle1.
  312. distance = -r0 - dmr1;
  313. result.numClosestPairs = 1;
  314. if (d > (Real)0)
  315. {
  316. result.circle0Closest[0] = circle0.center - r0 * U;
  317. result.circle1Closest[0] = circle1.center - r1 * U;
  318. result.equidistant = false;
  319. }
  320. else
  321. {
  322. // The circles are concentric, so U = (0,0,0).
  323. // Construct a vector perpendicular to N0 to use for
  324. // closest points.
  325. U = GetOrthogonal(circle0.normal, true);
  326. result.circle0Closest[0] = circle0.center + r0 * U;
  327. result.circle1Closest[0] = circle1.center + r1 * U;
  328. result.equidistant = true;
  329. }
  330. }
  331. else
  332. {
  333. // The circles are overlapping. The two points of
  334. // intersection are C0 + s*(C1-C0) +/- h*Cross(N,U), where
  335. // s = (1 + (r0^2 - r1^2)/d^2)/2 and
  336. // h = sqrt(r0^2 - s^2 * d^2).
  337. Real r0sqr = r0 * r0, r1sqr = r1 * r1, dsqr = d * d;
  338. Real s = ((Real)1 + (r0sqr - r1sqr) / dsqr) / (Real)2;
  339. Real arg = std::max(r0sqr - dsqr * s * s, (Real)0);
  340. Real h = std::sqrt(arg);
  341. Vector3<Real> midpoint = circle0.center + s * compProj;
  342. Vector3<Real> hNxU = h * Cross(circle0.normal, U);
  343. distance = (Real)0;
  344. result.numClosestPairs = 2;
  345. result.circle0Closest[0] = midpoint + hNxU;
  346. result.circle0Closest[1] = midpoint - hNxU;
  347. result.circle1Closest[0] = result.circle0Closest[0] + normProj;
  348. result.circle1Closest[1] = result.circle0Closest[1] + normProj;
  349. result.equidistant = false;
  350. }
  351. }
  352. result.sqrDistance = distance * distance + N0dD * N0dD;
  353. }
  354. };
  355. }