IntrLine3Cylinder3.h 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  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/FIQuery.h>
  9. #include <Mathematics/Cylinder3.h>
  10. #include <Mathematics/Vector3.h>
  11. // The queries consider the cylinder to be a solid.
  12. namespace WwiseGTE
  13. {
  14. template <typename Real>
  15. class FIQuery<Real, Line3<Real>, Cylinder3<Real>>
  16. {
  17. public:
  18. struct Result
  19. {
  20. bool intersect;
  21. int numIntersections;
  22. std::array<Real, 2> parameter;
  23. std::array<Vector3<Real>, 2> point;
  24. };
  25. Result operator()(Line3<Real> const& line, Cylinder3<Real> const& cylinder)
  26. {
  27. Result result;
  28. DoQuery(line.origin, line.direction, cylinder, result);
  29. for (int i = 0; i < result.numIntersections; ++i)
  30. {
  31. result.point[i] = line.origin + result.parameter[i] * line.direction;
  32. }
  33. return result;
  34. }
  35. protected:
  36. void DoQuery(Vector3<Real> const& lineOrigin,
  37. Vector3<Real> const& lineDirection, Cylinder3<Real> const& cylinder,
  38. Result& result)
  39. {
  40. // Initialize the result as if there is no intersection. If we
  41. // discover an intersection, these values will be modified
  42. // accordingly.
  43. result.intersect = false;
  44. result.numIntersections = 0;
  45. // Create a coordinate system for the cylinder. In this system,
  46. // the cylinder segment center C is the origin and the cylinder
  47. // axis direction W is the z-axis. U and V are the other
  48. // coordinate axis directions. If P = x*U+y*V+z*W, the cylinder
  49. // is x^2 + y^2 = r^2, where r is the cylinder radius. The end
  50. // caps are |z| = h/2, where h is the cylinder height.
  51. Vector3<Real> basis[3]; // {W, U, V}
  52. basis[0] = cylinder.axis.direction;
  53. ComputeOrthogonalComplement(1, basis);
  54. Real halfHeight = (Real)0.5 * cylinder.height;
  55. Real rSqr = cylinder.radius * cylinder.radius;
  56. // Convert incoming line origin to capsule coordinates.
  57. Vector3<Real> diff = lineOrigin - cylinder.axis.origin;
  58. Vector3<Real> P{ Dot(basis[1], diff), Dot(basis[2], diff), Dot(basis[0], diff) };
  59. // Get the z-value, in cylinder coordinates, of the incoming
  60. // line's unit-length direction.
  61. Real dz = Dot(basis[0], lineDirection);
  62. if (std::fabs(dz) == (Real)1)
  63. {
  64. // The line is parallel to the cylinder axis. Determine
  65. // whether the line intersects the cylinder end disks.
  66. Real radialSqrDist = rSqr - P[0] * P[0] - P[1] * P[1];
  67. if (radialSqrDist >= (Real)0)
  68. {
  69. // The line intersects the cylinder end disks.
  70. result.intersect = true;
  71. result.numIntersections = 2;
  72. if (dz > (Real)0)
  73. {
  74. result.parameter[0] = -P[2] - halfHeight;
  75. result.parameter[1] = -P[2] + halfHeight;
  76. }
  77. else
  78. {
  79. result.parameter[0] = P[2] - halfHeight;
  80. result.parameter[1] = P[2] + halfHeight;
  81. }
  82. }
  83. // else: The line is outside the cylinder, no intersection.
  84. return;
  85. }
  86. // Convert the incoming line unit-length direction to cylinder
  87. // coordinates.
  88. Vector3<Real> D{ Dot(basis[1], lineDirection), Dot(basis[2], lineDirection), dz };
  89. Real a0, a1, a2, discr, root, inv, tValue;
  90. if (D[2] == (Real)0)
  91. {
  92. // The line is perpendicular to the cylinder axis.
  93. if (std::fabs(P[2]) <= halfHeight)
  94. {
  95. // Test intersection of line P+t*D with infinite cylinder
  96. // x^2+y^2 = r^2. This reduces to computing the roots of
  97. // a quadratic equation. If P = (px,py,pz) and
  98. // D = (dx,dy,dz), then the quadratic equation is
  99. // (dx^2+dy^2)*t^2 + 2*(px*dx+py*dy)*t
  100. // + (px^2+py^2-r^2) = 0
  101. a0 = P[0] * P[0] + P[1] * P[1] - rSqr;
  102. a1 = P[0] * D[0] + P[1] * D[1];
  103. a2 = D[0] * D[0] + D[1] * D[1];
  104. discr = a1 * a1 - a0 * a2;
  105. if (discr > (Real)0)
  106. {
  107. // The line intersects the cylinder in two places.
  108. result.intersect = true;
  109. result.numIntersections = 2;
  110. root = std::sqrt(discr);
  111. inv = ((Real)1) / a2;
  112. result.parameter[0] = (-a1 - root) * inv;
  113. result.parameter[1] = (-a1 + root) * inv;
  114. }
  115. else if (discr == (Real)0)
  116. {
  117. // The line is tangent to the cylinder.
  118. result.intersect = true;
  119. result.numIntersections = 1;
  120. result.parameter[0] = -a1 / a2;
  121. // Used by derived classes.
  122. result.parameter[1] = result.parameter[0];
  123. }
  124. // else: The line does not intersect the cylinder.
  125. }
  126. // else: The line is outside the planes of the cylinder end
  127. // disks.
  128. return;
  129. }
  130. // Test for intersections with the planes of the end disks.
  131. inv = (Real)1 / D[2];
  132. Real t0 = (-halfHeight - P[2]) * inv;
  133. Real xTmp = P[0] + t0 * D[0];
  134. Real yTmp = P[1] + t0 * D[1];
  135. if (xTmp * xTmp + yTmp * yTmp <= rSqr)
  136. {
  137. // Plane intersection inside the top cylinder end disk.
  138. result.parameter[result.numIntersections++] = t0;
  139. }
  140. Real t1 = (+halfHeight - P[2]) * inv;
  141. xTmp = P[0] + t1 * D[0];
  142. yTmp = P[1] + t1 * D[1];
  143. if (xTmp * xTmp + yTmp * yTmp <= rSqr)
  144. {
  145. // Plane intersection inside the bottom cylinder end disk.
  146. result.parameter[result.numIntersections++] = t1;
  147. }
  148. if (result.numIntersections < 2)
  149. {
  150. // Test for intersection with the cylinder wall.
  151. a0 = P[0] * P[0] + P[1] * P[1] - rSqr;
  152. a1 = P[0] * D[0] + P[1] * D[1];
  153. a2 = D[0] * D[0] + D[1] * D[1];
  154. discr = a1 * a1 - a0 * a2;
  155. if (discr > (Real)0)
  156. {
  157. root = std::sqrt(discr);
  158. inv = (Real)1 / a2;
  159. tValue = (-a1 - root) * inv;
  160. if (t0 <= t1)
  161. {
  162. if (t0 <= tValue && tValue <= t1)
  163. {
  164. result.parameter[result.numIntersections++] = tValue;
  165. }
  166. }
  167. else
  168. {
  169. if (t1 <= tValue && tValue <= t0)
  170. {
  171. result.parameter[result.numIntersections++] = tValue;
  172. }
  173. }
  174. if (result.numIntersections < 2)
  175. {
  176. tValue = (-a1 + root) * inv;
  177. if (t0 <= t1)
  178. {
  179. if (t0 <= tValue && tValue <= t1)
  180. {
  181. result.parameter[result.numIntersections++] = tValue;
  182. }
  183. }
  184. else
  185. {
  186. if (t1 <= tValue && tValue <= t0)
  187. {
  188. result.parameter[result.numIntersections++] = tValue;
  189. }
  190. }
  191. }
  192. // else: Line intersects end disk and cylinder wall.
  193. }
  194. else if (discr == (Real)0)
  195. {
  196. tValue = -a1 / a2;
  197. if (t0 <= t1)
  198. {
  199. if (t0 <= tValue && tValue <= t1)
  200. {
  201. result.parameter[result.numIntersections++] = tValue;
  202. }
  203. }
  204. else
  205. {
  206. if (t1 <= tValue && tValue <= t0)
  207. {
  208. result.parameter[result.numIntersections++] = tValue;
  209. }
  210. }
  211. }
  212. // else: Line does not intersect cylinder wall.
  213. }
  214. // else: Line intersects both top and bottom cylinder end disks.
  215. if (result.numIntersections == 2)
  216. {
  217. result.intersect = true;
  218. if (result.parameter[0] > result.parameter[1])
  219. {
  220. std::swap(result.parameter[0], result.parameter[1]);
  221. }
  222. }
  223. else if (result.numIntersections == 1)
  224. {
  225. result.intersect = true;
  226. // Used by derived classes.
  227. result.parameter[1] = result.parameter[0];
  228. }
  229. }
  230. };
  231. }