IntrLine3Cone3.h 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480
  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/Vector3.h>
  9. #include <Mathematics/Cone.h>
  10. #include <Mathematics/Line.h>
  11. #include <Mathematics/QFNumber.h>
  12. #include <Mathematics/IntrIntervals.h>
  13. // The queries consider the cone to be single sided and solid. The
  14. // cone height range is [hmin,hmax]. The cone can be infinite where
  15. // hmin = 0 and hmax = +infinity, infinite truncated where hmin > 0
  16. // and hmax = +infinity, finite where hmin = 0 and hmax < +infinity,
  17. // or a cone frustum where hmin > 0 and hmax < +infinity. The
  18. // algorithm details are found in
  19. // https://www.geometrictools.com/Documentation/IntersectionLineCone.pdf
  20. namespace WwiseGTE
  21. {
  22. template <typename Real>
  23. class FIQuery<Real, Line3<Real>, Cone3<Real>>
  24. {
  25. public:
  26. // The rational quadratic field type with elements x + y * sqrt(d).
  27. // This type supports error-free computation.
  28. using QFN1 = QFNumber<Real, 1>;
  29. // Convenient naming for interval find-intersection queries.
  30. using IIQuery = FIIntervalInterval<QFN1>;
  31. struct Result
  32. {
  33. // Because the intersection of line and cone with infinite height
  34. // can be a ray or a line, we use a 'type' value that allows you
  35. // to decide how to interpret the t[] and P[] values.
  36. // No interesection.
  37. static int const isEmpty = 0;
  38. // t[0] is finite, t[1] is set to t[0], P[0] is the point of
  39. // intersection, P[1] is set to P[0].
  40. static int const isPoint = 1;
  41. // t[0] and t[1] are finite with t[0] < t[1], P[0] and P[1] are
  42. // the endpoints of the segment of intersection.
  43. static int const isSegment = 2;
  44. // Dot(line.direction, cone.ray.direction) > 0:
  45. // t[0] is finite, t[1] is +infinity (set to +1), P[0] is the ray
  46. // origin, P[1] is the ray direction (set to line.direction).
  47. // NOTE: The ray starts at P[0] and you walk away from it in the
  48. // line direction.
  49. static int const isRayPositive = 3;
  50. // Dot(line.direction, cone.ray.direction) < 0:
  51. // t[0] is -infinity (set to -1), t[1] is finite, P[0] is the ray
  52. // endpoint, P[1] is the ray direction (set to line.direction).
  53. // NOTE: The ray ends at P[1] and you walk towards it in the line
  54. // direction.
  55. static int const isRayNegative = 4;
  56. Result()
  57. :
  58. intersect(false),
  59. type(Result::isEmpty)
  60. {
  61. // t[], h[] and P[] are initialized to zero via QFN1 constructors
  62. }
  63. void ComputePoints(Vector3<Real> const& origin, Vector3<Real> const& direction)
  64. {
  65. switch (type)
  66. {
  67. case Result::isEmpty:
  68. for (int i = 0; i < 3; ++i)
  69. {
  70. P[0][i] = QFN1();
  71. P[1][i] = P[0][i];
  72. }
  73. break;
  74. case Result::isPoint:
  75. for (int i = 0; i < 3; ++i)
  76. {
  77. P[0][i] = origin[i] + direction[i] * t[0];
  78. P[1][i] = P[0][i];
  79. }
  80. break;
  81. case Result::isSegment:
  82. for (int i = 0; i < 3; ++i)
  83. {
  84. P[0][i] = origin[i] + direction[i] * t[0];
  85. P[1][i] = origin[i] + direction[i] * t[1];
  86. }
  87. break;
  88. case Result::isRayPositive:
  89. for (int i = 0; i < 3; ++i)
  90. {
  91. P[0][i] = origin[i] + direction[i] * t[0];
  92. P[1][i] = QFN1(direction[i], 0, t[0].d);
  93. }
  94. break;
  95. case Result::isRayNegative:
  96. for (int i = 0; i < 3; ++i)
  97. {
  98. P[0][i] = origin[i] + direction[i] * t[1];
  99. P[1][i] = QFN1(direction[i], 0, t[1].d);
  100. }
  101. break;
  102. default:
  103. LogError("Invalid case.");
  104. break;
  105. }
  106. }
  107. template <typename OutputType>
  108. static void Convert(QFN1 const& input, OutputType& output)
  109. {
  110. output = static_cast<Real>(input);
  111. }
  112. template <typename OutputType>
  113. static void Convert(Vector3<QFN1> const& input, Vector3<OutputType>& output)
  114. {
  115. for (int i = 0; i < 3; ++i)
  116. {
  117. output[i] = static_cast<Real>(input[i]);
  118. }
  119. }
  120. bool intersect;
  121. int type;
  122. std::array<QFN1, 2> t;
  123. std::array<Vector3<QFN1>, 2> P;
  124. };
  125. Result operator()(Line3<Real> const& line, Cone3<Real> const& cone)
  126. {
  127. Result result;
  128. DoQuery(line.origin, line.direction, cone, result);
  129. result.ComputePoints(line.origin, line.direction);
  130. result.intersect = (result.type != Result::isEmpty);
  131. return result;
  132. }
  133. protected:
  134. // The result.type and result.t[] values are computed by DoQuery. The
  135. // result.P[] and result.intersect values are computed from them in
  136. // the operator()(...) function.
  137. void DoQuery(Vector3<Real> const& lineOrigin, Vector3<Real> const& lineDirection,
  138. Cone3<Real> const& cone, Result& result)
  139. {
  140. // The algorithm implemented in DoQuery avoids extra branches if
  141. // we choose a line whose direction forms an acute angle with the
  142. // cone direction.
  143. if (Dot(lineDirection, cone.ray.direction) >= (Real)0)
  144. {
  145. DoQuerySpecial(lineOrigin, lineDirection, cone, result);
  146. }
  147. else
  148. {
  149. DoQuerySpecial(lineOrigin, -lineDirection, cone, result);
  150. result.t[0] = -result.t[0];
  151. result.t[1] = -result.t[1];
  152. std::swap(result.t[0], result.t[1]);
  153. if (result.type == Result::isRayPositive)
  154. {
  155. result.type = Result::isRayNegative;
  156. }
  157. }
  158. }
  159. void DoQuerySpecial(Vector3<Real> const& lineOrigin, Vector3<Real> const& lineDirection,
  160. Cone3<Real> const& cone, Result& result)
  161. {
  162. // Compute the number of real-valued roots and represent them
  163. // using rational quadratic field elements to support when Real
  164. // is an exact rational arithmetic type. TODO: Adjust by noting
  165. // that we should use D/|D| because a normalized floating-point
  166. // D still might not have |D| = 1 (although it is close to 1).
  167. Vector3<Real> PmV = lineOrigin - cone.ray.origin;
  168. Real UdU = Dot(lineDirection, lineDirection);
  169. Real DdU = Dot(cone.ray.direction, lineDirection); // >= 0
  170. Real DdPmV = Dot(cone.ray.direction, PmV);
  171. Real UdPmV = Dot(lineDirection, PmV);
  172. Real PmVdPmV = Dot(PmV, PmV);
  173. Real c2 = DdU * DdU - cone.cosAngleSqr * UdU;
  174. Real c1 = DdU * DdPmV - cone.cosAngleSqr * UdPmV;
  175. Real c0 = DdPmV * DdPmV - cone.cosAngleSqr * PmVdPmV;
  176. if (c2 != (Real)0)
  177. {
  178. Real discr = c1 * c1 - c0 * c2;
  179. if (discr < (Real)0)
  180. {
  181. CaseC2NotZeroDiscrNeg(result);
  182. }
  183. else if (discr > (Real)0)
  184. {
  185. CaseC2NotZeroDiscrPos(c1, c2, discr, DdU, DdPmV, cone, result);
  186. }
  187. else // discr == 0
  188. {
  189. CaseC2NotZeroDiscrZero(c1, c2, UdU, UdPmV, DdU, DdPmV, cone, result);
  190. }
  191. }
  192. else if (c1 != (Real)0)
  193. {
  194. CaseC2ZeroC1NotZero(c0, c1, DdU, DdPmV, cone, result);
  195. }
  196. else
  197. {
  198. CaseC2ZeroC1Zero(c0, UdU, UdPmV, DdU, DdPmV, cone, result);
  199. }
  200. }
  201. void CaseC2NotZeroDiscrNeg(Result& result)
  202. {
  203. // Block 0. The quadratic has no real-valued roots. The line does
  204. // not intersect the double-sided cone.
  205. SetEmpty(result);
  206. }
  207. void CaseC2NotZeroDiscrPos(Real const& c1, Real const& c2, Real const& discr,
  208. Real const& DdU, Real const& DdPmV, Cone3<Real> const& cone, Result& result)
  209. {
  210. // The quadratic has two distinct real-valued roots, t[0] and t[1]
  211. // with t[0] < t[1].
  212. Real x = -c1 / c2;
  213. Real y = (c2 > (Real)0 ? (Real)1 / c2 : (Real)-1 / c2);
  214. std::array<QFN1, 2> t = { QFN1(x, -y, discr), QFN1(x, y, discr) };
  215. // Compute the signed heights at the intersection points, h[0] and
  216. // h[1] with h[0] <= h[1]. The ordering is guaranteed because we
  217. // have arranged for the input line to satisfy Dot(D,U) >= 0.
  218. std::array<QFN1, 2> h = { t[0] * DdU + DdPmV, t[1] * DdU + DdPmV };
  219. QFN1 zero(0, 0, discr);
  220. if (h[0] >= zero)
  221. {
  222. // Block 1. The line intersects the positive cone in two
  223. // points.
  224. SetSegmentClamp(t, h, DdU, DdPmV, cone, result);
  225. }
  226. else if (h[1] <= zero)
  227. {
  228. // Block 2. The line intersects the negative cone in two
  229. // points.
  230. SetEmpty(result);
  231. }
  232. else // h[0] < 0 < h[1]
  233. {
  234. // Block 3. The line intersects the positive cone in a single
  235. // point and the negative cone in a single point.
  236. SetRayClamp(h[1], DdU, DdPmV, cone, result);
  237. }
  238. }
  239. void CaseC2NotZeroDiscrZero(Real const& c1, Real const& c2,
  240. Real const& UdU, Real const& UdPmV, Real const& DdU, Real const& DdPmV,
  241. Cone3<Real> const& cone, Result& result)
  242. {
  243. Real t = -c1 / c2;
  244. if (t * UdU + UdPmV == (Real)0)
  245. {
  246. // To get here, it must be that V = P + (-c1/c2) * U, where
  247. // U is not necessarily a unit-length vector. The line
  248. // intersects the cone vertex.
  249. if (c2 < (Real)0)
  250. {
  251. // Block 4. The line is outside the double-sided cone and
  252. // intersects it only at V.
  253. SetPointClamp(QFN1(t, 0, 0), QFN1(0, 0, 0), cone, result);
  254. }
  255. else
  256. {
  257. // Block 5. The line is inside the double-sided cone, so
  258. // the intersection is a ray with origin V.
  259. SetRayClamp(QFN1(0, 0, 0), DdU, DdPmV, cone, result);
  260. }
  261. }
  262. else
  263. {
  264. // The line is tangent to the cone at a point different from
  265. // the vertex.
  266. Real h = t * DdU + DdPmV;
  267. if (h >= (Real)0)
  268. {
  269. // Block 6. The line is tangent to the positive cone.
  270. SetPointClamp(QFN1(t, 0, 0), QFN1(h, 0, 0), cone, result);
  271. }
  272. else
  273. {
  274. // Block 7. The line is tangent to the negative cone.
  275. SetEmpty(result);
  276. }
  277. }
  278. }
  279. void CaseC2ZeroC1NotZero(Real const& c0, Real const& c1, Real const& DdU,
  280. Real const& DdPmV, Cone3<Real> const& cone, Result& result)
  281. {
  282. // U is a direction vector on the cone boundary. Compute the
  283. // t-value for the intersection point and compute the
  284. // corresponding height h to determine whether that point is on
  285. // the positive cone or negative cone.
  286. Real t = (Real)-0.5 * c0 / c1;
  287. Real h = t * DdU + DdPmV;
  288. if (h > (Real)0)
  289. {
  290. // Block 8. The line intersects the positive cone and the ray
  291. // of intersection is interior to the positive cone. The
  292. // intersection is a ray or segment.
  293. SetRayClamp(QFN1(h, 0, 0), DdU, DdPmV, cone, result);
  294. }
  295. else
  296. {
  297. // Block 9. The line intersects the negative cone and the ray
  298. // of intersection is interior to the negative cone.
  299. SetEmpty(result);
  300. }
  301. }
  302. void CaseC2ZeroC1Zero(Real const& c0, Real const& UdU, Real const& UdPmV,
  303. Real const& DdU, Real const& DdPmV, Cone3<Real> const& cone, Result& result)
  304. {
  305. if (c0 != (Real)0)
  306. {
  307. // Block 10. The line does not intersect the double-sided
  308. // cone.
  309. SetEmpty(result);
  310. }
  311. else
  312. {
  313. // Block 11. The line is on the cone boundary. The
  314. // intersection with the positive cone is a ray that contains
  315. // the cone vertex. The intersection is either a ray or
  316. // segment.
  317. Real t = -UdPmV / UdU;
  318. Real h = t * DdU + DdPmV;
  319. SetRayClamp(QFN1(h, 0, 0), DdU, DdPmV, cone, result);
  320. }
  321. }
  322. void SetEmpty(Result& result)
  323. {
  324. result.type = Result::isEmpty;
  325. result.t[0] = QFN1();
  326. result.t[1] = QFN1();
  327. }
  328. void SetPoint(QFN1 const& t, Result& result)
  329. {
  330. result.type = Result::isPoint;
  331. result.t[0] = t;
  332. result.t[1] = result.t[0];
  333. }
  334. void SetSegment(QFN1 const& t0, QFN1 const& t1, Result& result)
  335. {
  336. result.type = Result::isSegment;
  337. result.t[0] = t0;
  338. result.t[1] = t1;
  339. }
  340. void SetRayPositive(QFN1 const& t, Result& result)
  341. {
  342. result.type = Result::isRayPositive;
  343. result.t[0] = t;
  344. result.t[1] = QFN1(+1, 0, t.d); // +infinity
  345. }
  346. void SetRayNegative(QFN1 const& t, Result& result)
  347. {
  348. result.type = Result::isRayNegative;
  349. result.t[0] = QFN1(-1, 0, t.d); // +infinity
  350. result.t[1] = t;
  351. }
  352. void SetPointClamp(QFN1 const& t, QFN1 const& h,
  353. Cone3<Real> const& cone, Result& result)
  354. {
  355. if (cone.HeightInRange(h.x[0]))
  356. {
  357. // P0.
  358. SetPoint(t, result);
  359. }
  360. else
  361. {
  362. // P1.
  363. SetEmpty(result);
  364. }
  365. }
  366. void SetSegmentClamp(std::array<QFN1, 2> const& t, std::array<QFN1, 2> const& h,
  367. Real const& DdU, Real const& DdPmV, Cone3<Real> const& cone, Result& result)
  368. {
  369. std::array<QFN1, 2> hrange =
  370. {
  371. QFN1(cone.GetMinHeight(), 0, h[0].d),
  372. QFN1(cone.GetMaxHeight(), 0, h[0].d)
  373. };
  374. if (h[1] > h[0])
  375. {
  376. auto iir = (cone.IsFinite() ? IIQuery()(h, hrange) : IIQuery()(h, hrange[0], true));
  377. if (iir.numIntersections == 2)
  378. {
  379. // S0.
  380. SetSegment((iir.overlap[0] - DdPmV) / DdU, (iir.overlap[1] - DdPmV) / DdU, result);
  381. }
  382. else if (iir.numIntersections == 1)
  383. {
  384. // S1.
  385. SetPoint((iir.overlap[0] - DdPmV) / DdU, result);
  386. }
  387. else // iir.numIntersections == 0
  388. {
  389. // S2.
  390. SetEmpty(result);
  391. }
  392. }
  393. else // h[1] == h[0]
  394. {
  395. if (hrange[0] <= h[0] && (cone.IsFinite() ? h[0] <= hrange[1] : true))
  396. {
  397. // S3. DdU > 0 and the line is not perpendicular to the
  398. // cone axis.
  399. SetSegment(t[0], t[1], result);
  400. }
  401. else
  402. {
  403. // S4. DdU == 0 and the line is perpendicular to the
  404. // cone axis.
  405. SetEmpty(result);
  406. }
  407. }
  408. }
  409. void SetRayClamp(QFN1 const& h, Real const& DdU, Real const& DdPmV,
  410. Cone3<Real> const& cone, Result& result)
  411. {
  412. std::array<QFN1, 2> hrange =
  413. {
  414. QFN1(cone.GetMinHeight(), 0, h.d),
  415. QFN1(cone.GetMaxHeight(), 0, h.d)
  416. };
  417. if (cone.IsFinite())
  418. {
  419. auto iir = IIQuery()(hrange, h, true);
  420. if (iir.numIntersections == 2)
  421. {
  422. // R0.
  423. SetSegment((iir.overlap[0] - DdPmV) / DdU, (iir.overlap[1] - DdPmV) / DdU, result);
  424. }
  425. else if (iir.numIntersections == 1)
  426. {
  427. // R1.
  428. SetPoint((iir.overlap[0] - DdPmV) / DdU, result);
  429. }
  430. else // iir.numIntersections == 0
  431. {
  432. // R2.
  433. SetEmpty(result);
  434. }
  435. }
  436. else
  437. {
  438. // R3.
  439. SetRayPositive((std::max(hrange[0], h) - DdPmV) / DdU, result);
  440. }
  441. }
  442. };
  443. }