ContCapsule3.h 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  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/ApprOrthogonalLine3.h>
  9. #include <Mathematics/Capsule.h>
  10. #include <Mathematics/DistPointLine.h>
  11. #include <Mathematics/DistPointSegment.h>
  12. #include <Mathematics/Hypersphere.h>
  13. namespace WwiseGTE
  14. {
  15. // Compute the axis of the capsule segment using least-squares fitting.
  16. // The radius is the maximum distance from the points to the axis.
  17. // Hemispherical caps are chosen as close together as possible.
  18. template <typename Real>
  19. bool GetContainer(int numPoints, Vector3<Real> const* points, Capsule3<Real>& capsule)
  20. {
  21. ApprOrthogonalLine3<Real> fitter;
  22. fitter.Fit(numPoints, points);
  23. Line3<Real> line = fitter.GetParameters();
  24. DCPQuery<Real, Vector3<Real>, Line3<Real>> plQuery;
  25. Real maxRadiusSqr = (Real)0;
  26. for (int i = 0; i < numPoints; ++i)
  27. {
  28. auto result = plQuery(points[i], line);
  29. if (result.sqrDistance > maxRadiusSqr)
  30. {
  31. maxRadiusSqr = result.sqrDistance;
  32. }
  33. }
  34. Vector3<Real> basis[3];
  35. basis[0] = line.direction;
  36. ComputeOrthogonalComplement(1, basis);
  37. Real minValue = std::numeric_limits<Real>::max();
  38. Real maxValue = -std::numeric_limits<Real>::max();
  39. for (int i = 0; i < numPoints; ++i)
  40. {
  41. Vector3<Real> diff = points[i] - line.origin;
  42. Real uDotDiff = Dot(diff, basis[1]);
  43. Real vDotDiff = Dot(diff, basis[2]);
  44. Real wDotDiff = Dot(diff, basis[0]);
  45. Real discr = maxRadiusSqr - (uDotDiff * uDotDiff + vDotDiff * vDotDiff);
  46. Real radical = std::sqrt(std::max(discr, (Real)0));
  47. Real test = wDotDiff + radical;
  48. if (test < minValue)
  49. {
  50. minValue = test;
  51. }
  52. test = wDotDiff - radical;
  53. if (test > maxValue)
  54. {
  55. maxValue = test;
  56. }
  57. }
  58. Vector3<Real> center = line.origin + ((Real)0.5 * (minValue + maxValue)) * line.direction;
  59. Real extent;
  60. if (maxValue > minValue)
  61. {
  62. // Container is a capsule.
  63. extent = (Real)0.5 * (maxValue - minValue);
  64. }
  65. else
  66. {
  67. // Container is a sphere.
  68. extent = (Real)0;
  69. }
  70. capsule.segment = Segment3<Real>(center, line.direction, extent);
  71. capsule.radius = std::sqrt(maxRadiusSqr);
  72. return true;
  73. }
  74. // Test for containment of a point by a capsule.
  75. template <typename Real>
  76. bool InContainer(Vector3<Real> const& point, Capsule3<Real> const& capsule)
  77. {
  78. DCPQuery<Real, Vector3<Real>, Segment3<Real>> psQuery;
  79. auto result = psQuery(point, capsule.segment);
  80. return result.distance <= capsule.radius;
  81. }
  82. // Test for containment of a sphere by a capsule.
  83. template <typename Real>
  84. bool InContainer(Sphere3<Real> const& sphere, Capsule3<Real> const& capsule)
  85. {
  86. Real rDiff = capsule.radius - sphere.radius;
  87. if (rDiff >= (Real)0)
  88. {
  89. DCPQuery<Real, Vector3<Real>, Segment3<Real>> psQuery;
  90. auto result = psQuery(sphere.center, capsule.segment);
  91. return result.distance <= rDiff;
  92. }
  93. return false;
  94. }
  95. // Test for containment of a capsule by a capsule.
  96. template <typename Real>
  97. bool InContainer(Capsule3<Real> const& testCapsule, Capsule3<Real> const& capsule)
  98. {
  99. Sphere3<Real> spherePosEnd(testCapsule.segment.p[1], testCapsule.radius);
  100. Sphere3<Real> sphereNegEnd(testCapsule.segment.p[0], testCapsule.radius);
  101. return InContainer<Real>(spherePosEnd, capsule)
  102. && InContainer<Real>(sphereNegEnd, capsule);
  103. }
  104. // Compute a capsule that contains the input capsules. The returned
  105. // capsule is not necessarily the one of smallest volume that contains
  106. // the inputs.
  107. template <typename Real>
  108. bool MergeContainers(Capsule3<Real> const& capsule0,
  109. Capsule3<Real> const& capsule1, Capsule3<Real>& merge)
  110. {
  111. if (InContainer<Real>(capsule0, capsule1))
  112. {
  113. merge = capsule1;
  114. return true;
  115. }
  116. if (InContainer<Real>(capsule1, capsule0))
  117. {
  118. merge = capsule0;
  119. return true;
  120. }
  121. Vector3<Real> P0, P1, D0, D1;
  122. Real extent0, extent1;
  123. capsule0.segment.GetCenteredForm(P0, D0, extent0);
  124. capsule1.segment.GetCenteredForm(P1, D1, extent1);
  125. // Axis of final capsule.
  126. Line3<Real> line;
  127. // Axis center is average of input axis centers.
  128. line.origin = (Real)0.5 * (P0 + P1);
  129. // Axis unit direction is average of input axis unit directions.
  130. if (Dot(D0, D1) >= (Real)0)
  131. {
  132. line.direction = D0 + D1;
  133. }
  134. else
  135. {
  136. line.direction = D0 - D1;
  137. }
  138. Normalize(line.direction);
  139. // Cylinder with axis 'line' must contain the spheres centered at the
  140. // endpoints of the input capsules.
  141. DCPQuery<Real, Vector3<Real>, Line3<Real>> plQuery;
  142. Vector3<Real> posEnd0 = capsule0.segment.p[1];
  143. Real radius = plQuery(posEnd0, line).distance + capsule0.radius;
  144. Vector3<Real> negEnd0 = capsule0.segment.p[0];
  145. Real tmp = plQuery(negEnd0, line).distance + capsule0.radius;
  146. Vector3<Real> posEnd1 = capsule1.segment.p[1];
  147. tmp = plQuery(posEnd1, line).distance + capsule1.radius;
  148. if (tmp > radius)
  149. {
  150. radius = tmp;
  151. }
  152. Vector3<Real> negEnd1 = capsule1.segment.p[0];
  153. tmp = plQuery(negEnd1, line).distance + capsule1.radius;
  154. if (tmp > radius)
  155. {
  156. radius = tmp;
  157. }
  158. // In the following blocks of code, theoretically k1*k1-k0 >= 0, but
  159. // numerical rounding errors can make it slightly negative. Guard
  160. // against this.
  161. // Process sphere <posEnd0,r0>.
  162. Real rDiff = radius - capsule0.radius;
  163. Real rDiffSqr = rDiff * rDiff;
  164. Vector3<Real> diff = line.origin - posEnd0;
  165. Real k0 = Dot(diff, diff) - rDiffSqr;
  166. Real k1 = Dot(diff, line.direction);
  167. Real discr = k1 * k1 - k0;
  168. Real root = std::sqrt(std::max(discr, (Real)0));
  169. Real tPos = -k1 - root;
  170. Real tNeg = -k1 + root;
  171. // Process sphere <negEnd0,r0>.
  172. diff = line.origin - negEnd0;
  173. k0 = Dot(diff, diff) - rDiffSqr;
  174. k1 = Dot(diff, line.direction);
  175. discr = k1 * k1 - k0;
  176. root = std::sqrt(std::max(discr, (Real)0));
  177. tmp = -k1 - root;
  178. if (tmp > tPos)
  179. {
  180. tPos = tmp;
  181. }
  182. tmp = -k1 + root;
  183. if (tmp < tNeg)
  184. {
  185. tNeg = tmp;
  186. }
  187. // Process sphere <posEnd1,r1>.
  188. rDiff = radius - capsule1.radius;
  189. rDiffSqr = rDiff * rDiff;
  190. diff = line.origin - posEnd1;
  191. k0 = Dot(diff, diff) - rDiffSqr;
  192. k1 = Dot(diff, line.direction);
  193. discr = k1 * k1 - k0;
  194. root = std::sqrt(std::max(discr, (Real)0));
  195. tmp = -k1 - root;
  196. if (tmp > tPos)
  197. {
  198. tPos = tmp;
  199. }
  200. tmp = -k1 + root;
  201. if (tmp < tNeg)
  202. {
  203. tNeg = tmp;
  204. }
  205. // Process sphere <negEnd1,r1>.
  206. diff = line.origin - negEnd1;
  207. k0 = Dot(diff, diff) - rDiffSqr;
  208. k1 = Dot(diff, line.direction);
  209. discr = k1 * k1 - k0;
  210. root = std::sqrt(std::max(discr, (Real)0));
  211. tmp = -k1 - root;
  212. if (tmp > tPos)
  213. {
  214. tPos = tmp;
  215. }
  216. tmp = -k1 + root;
  217. if (tmp < tNeg)
  218. {
  219. tNeg = tmp;
  220. }
  221. Vector3<Real> center = line.origin + (Real)0.5 * (tPos + tNeg) * line.direction;
  222. Real extent;
  223. if (tPos > tNeg)
  224. {
  225. // Container is a capsule.
  226. extent = (Real)0.5 * (tPos - tNeg);
  227. }
  228. else
  229. {
  230. // Container is a sphere.
  231. extent = (Real)0;
  232. }
  233. merge.segment = Segment3<Real>(center, line.direction, extent);
  234. merge.radius = radius;
  235. return true;
  236. }
  237. }