BoundingSphere.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. // MIT License - Copyright (C) The Mono.Xna Team
  2. // This file is subject to the terms and conditions defined in
  3. // file 'LICENSE.txt', which is part of this source code package.
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Globalization;
  7. using System.Runtime.Serialization;
  8. using System.Diagnostics;
  9. namespace CommonLang.Geometry
  10. {
  11. public struct BoundingSphere : IEquatable<BoundingSphere>
  12. {
  13. #region Public Fields
  14. public Vector3 Center;
  15. public float Radius;
  16. #endregion Public Fields
  17. #region Constructors
  18. public BoundingSphere(Vector3 center, float radius)
  19. {
  20. this.Center = center;
  21. this.Radius = radius;
  22. }
  23. #endregion Constructors
  24. #region Public Methods
  25. public BoundingSphere Transform(Matrix matrix)
  26. {
  27. BoundingSphere sphere = new BoundingSphere();
  28. sphere.Center = Vector3.Transform(this.Center, matrix);
  29. sphere.Radius = this.Radius * ((float)Math.Sqrt((double)Math.Max(((matrix.M11 * matrix.M11) + (matrix.M12 * matrix.M12)) + (matrix.M13 * matrix.M13), Math.Max(((matrix.M21 * matrix.M21) + (matrix.M22 * matrix.M22)) + (matrix.M23 * matrix.M23), ((matrix.M31 * matrix.M31) + (matrix.M32 * matrix.M32)) + (matrix.M33 * matrix.M33)))));
  30. return sphere;
  31. }
  32. public void Transform(ref Matrix matrix, out BoundingSphere result)
  33. {
  34. result.Center = Vector3.Transform(this.Center, matrix);
  35. result.Radius = this.Radius * ((float)Math.Sqrt((double)Math.Max(((matrix.M11 * matrix.M11) + (matrix.M12 * matrix.M12)) + (matrix.M13 * matrix.M13), Math.Max(((matrix.M21 * matrix.M21) + (matrix.M22 * matrix.M22)) + (matrix.M23 * matrix.M23), ((matrix.M31 * matrix.M31) + (matrix.M32 * matrix.M32)) + (matrix.M33 * matrix.M33)))));
  36. }
  37. public ContainmentType Contains(BoundingBox box)
  38. {
  39. //check if all corner is in sphere
  40. bool inside = true;
  41. foreach (Vector3 corner in box.GetCorners())
  42. {
  43. if (this.Contains(corner) == ContainmentType.Disjoint)
  44. {
  45. inside = false;
  46. break;
  47. }
  48. }
  49. if (inside)
  50. return ContainmentType.Contains;
  51. //check if the distance from sphere center to cube face < radius
  52. double dmin = 0;
  53. if (Center.X < box.Min.X)
  54. dmin += (Center.X - box.Min.X) * (Center.X - box.Min.X);
  55. else if (Center.X > box.Max.X)
  56. dmin += (Center.X - box.Max.X) * (Center.X - box.Max.X);
  57. if (Center.Y < box.Min.Y)
  58. dmin += (Center.Y - box.Min.Y) * (Center.Y - box.Min.Y);
  59. else if (Center.Y > box.Max.Y)
  60. dmin += (Center.Y - box.Max.Y) * (Center.Y - box.Max.Y);
  61. if (Center.Z < box.Min.Z)
  62. dmin += (Center.Z - box.Min.Z) * (Center.Z - box.Min.Z);
  63. else if (Center.Z > box.Max.Z)
  64. dmin += (Center.Z - box.Max.Z) * (Center.Z - box.Max.Z);
  65. if (dmin <= Radius * Radius)
  66. return ContainmentType.Intersects;
  67. //else disjoint
  68. return ContainmentType.Disjoint;
  69. }
  70. public void Contains(ref BoundingBox box, out ContainmentType result)
  71. {
  72. result = this.Contains(box);
  73. }
  74. public ContainmentType Contains(BoundingFrustum frustum)
  75. {
  76. //check if all corner is in sphere
  77. bool inside = true;
  78. Vector3[] corners = frustum.GetCorners();
  79. foreach (Vector3 corner in corners)
  80. {
  81. if (this.Contains(corner) == ContainmentType.Disjoint)
  82. {
  83. inside = false;
  84. break;
  85. }
  86. }
  87. if (inside)
  88. return ContainmentType.Contains;
  89. //check if the distance from sphere center to frustrum face < radius
  90. double dmin = 0;
  91. //TODO : calcul dmin
  92. if (dmin <= Radius * Radius)
  93. return ContainmentType.Intersects;
  94. //else disjoint
  95. return ContainmentType.Disjoint;
  96. }
  97. public ContainmentType Contains(BoundingSphere sphere)
  98. {
  99. ContainmentType result;
  100. Contains(ref sphere, out result);
  101. return result;
  102. }
  103. public void Contains(ref BoundingSphere sphere, out ContainmentType result)
  104. {
  105. float sqDistance;
  106. Vector3.DistanceSquared(ref sphere.Center, ref Center, out sqDistance);
  107. if (sqDistance > (sphere.Radius + Radius) * (sphere.Radius + Radius))
  108. result = ContainmentType.Disjoint;
  109. else if (sqDistance <= (Radius - sphere.Radius) * (Radius - sphere.Radius))
  110. result = ContainmentType.Contains;
  111. else
  112. result = ContainmentType.Intersects;
  113. }
  114. public ContainmentType Contains(Vector3 point)
  115. {
  116. ContainmentType result;
  117. Contains(ref point, out result);
  118. return result;
  119. }
  120. public void Contains(ref Vector3 point, out ContainmentType result)
  121. {
  122. float sqRadius = Radius * Radius;
  123. float sqDistance;
  124. Vector3.DistanceSquared(ref point, ref Center, out sqDistance);
  125. if (sqDistance > sqRadius)
  126. result = ContainmentType.Disjoint;
  127. else if (sqDistance < sqRadius)
  128. result = ContainmentType.Contains;
  129. else
  130. result = ContainmentType.Intersects;
  131. }
  132. public static BoundingSphere CreateFromBoundingBox(BoundingBox box)
  133. {
  134. BoundingSphere result;
  135. CreateFromBoundingBox(ref box, out result);
  136. return result;
  137. }
  138. public static void CreateFromBoundingBox(ref BoundingBox box, out BoundingSphere result)
  139. {
  140. // Find the center of the box.
  141. Vector3 center = new Vector3((box.Min.X + box.Max.X) / 2.0f,
  142. (box.Min.Y + box.Max.Y) / 2.0f,
  143. (box.Min.Z + box.Max.Z) / 2.0f);
  144. // Find the distance between the center and one of the corners of the box.
  145. float radius = Vector3.Distance(center, box.Max);
  146. result = new BoundingSphere(center, radius);
  147. }
  148. public static BoundingSphere CreateFromFrustum(BoundingFrustum frustum)
  149. {
  150. return BoundingSphere.CreateFromPoints(frustum.GetCorners());
  151. }
  152. public static BoundingSphere CreateFromPoints(IEnumerable<Vector3> points)
  153. {
  154. if (points == null )
  155. throw new ArgumentNullException("points");
  156. // From "Real-Time Collision Detection" (Page 89)
  157. var minx = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
  158. var maxx = -minx;
  159. var miny = minx;
  160. var maxy = -minx;
  161. var minz = minx;
  162. var maxz = -minx;
  163. // Find the most extreme points along the principle axis.
  164. var numPoints = 0;
  165. foreach (var pt in points)
  166. {
  167. ++numPoints;
  168. if (pt.X < minx.X)
  169. minx = pt;
  170. if (pt.X > maxx.X)
  171. maxx = pt;
  172. if (pt.Y < miny.Y)
  173. miny = pt;
  174. if (pt.Y > maxy.Y)
  175. maxy = pt;
  176. if (pt.Z < minz.Z)
  177. minz = pt;
  178. if (pt.Z > maxz.Z)
  179. maxz = pt;
  180. }
  181. if (numPoints == 0)
  182. throw new ArgumentException("You should have at least one point in points.");
  183. var sqDistX = Vector3.DistanceSquared(maxx, minx);
  184. var sqDistY = Vector3.DistanceSquared(maxy, miny);
  185. var sqDistZ = Vector3.DistanceSquared(maxz, minz);
  186. // Pick the pair of most distant points.
  187. var min = minx;
  188. var max = maxx;
  189. if (sqDistY > sqDistX && sqDistY > sqDistZ)
  190. {
  191. max = maxy;
  192. min = miny;
  193. }
  194. if (sqDistZ > sqDistX && sqDistZ > sqDistY)
  195. {
  196. max = maxz;
  197. min = minz;
  198. }
  199. var center = (min + max) * 0.5f;
  200. var radius = Vector3.Distance(max, center);
  201. // Test every point and expand the sphere.
  202. // The current bounding sphere is just a good approximation and may not enclose all points.
  203. // From: Mathematics for 3D Game Programming and Computer Graphics, Eric Lengyel, Third Edition.
  204. // Page 218
  205. float sqRadius = radius * radius;
  206. foreach (var pt in points)
  207. {
  208. Vector3 diff = (pt-center);
  209. float sqDist = diff.LengthSquared();
  210. if (sqDist > sqRadius)
  211. {
  212. float distance = (float)Math.Sqrt(sqDist); // equal to diff.Length();
  213. Vector3 direction = diff / distance;
  214. Vector3 G = center - radius * direction;
  215. center = (G + pt) / 2;
  216. radius = Vector3.Distance(pt, center);
  217. sqRadius = radius * radius;
  218. }
  219. }
  220. return new BoundingSphere(center, radius);
  221. }
  222. public static BoundingSphere CreateMerged(BoundingSphere original, BoundingSphere additional)
  223. {
  224. BoundingSphere result;
  225. CreateMerged(ref original, ref additional, out result);
  226. return result;
  227. }
  228. public static void CreateMerged(ref BoundingSphere original, ref BoundingSphere additional, out BoundingSphere result)
  229. {
  230. Vector3 ocenterToaCenter = Vector3.Subtract(additional.Center, original.Center);
  231. float distance = ocenterToaCenter.Length();
  232. if (distance <= original.Radius + additional.Radius)//intersect
  233. {
  234. if (distance <= original.Radius - additional.Radius)//original contain additional
  235. {
  236. result = original;
  237. return;
  238. }
  239. if (distance <= additional.Radius - original.Radius)//additional contain original
  240. {
  241. result = additional;
  242. return;
  243. }
  244. }
  245. //else find center of new sphere and radius
  246. float leftRadius = Math.Max(original.Radius - distance, additional.Radius);
  247. float Rightradius = Math.Max(original.Radius + distance, additional.Radius);
  248. ocenterToaCenter = ocenterToaCenter + (((leftRadius - Rightradius) / (2 * ocenterToaCenter.Length())) * ocenterToaCenter);//oCenterToResultCenter
  249. result = new BoundingSphere();
  250. result.Center = original.Center + ocenterToaCenter;
  251. result.Radius = (leftRadius + Rightradius) / 2;
  252. }
  253. public bool Equals(BoundingSphere other)
  254. {
  255. return this.Center == other.Center && this.Radius == other.Radius;
  256. }
  257. public override bool Equals(object obj)
  258. {
  259. if (obj is BoundingSphere)
  260. return this.Equals((BoundingSphere)obj);
  261. return false;
  262. }
  263. public override int GetHashCode()
  264. {
  265. return this.Center.GetHashCode() + this.Radius.GetHashCode();
  266. }
  267. public bool Intersects(BoundingBox box)
  268. {
  269. return box.Intersects(this);
  270. }
  271. public void Intersects(ref BoundingBox box, out bool result)
  272. {
  273. box.Intersects(ref this, out result);
  274. }
  275. /*
  276. public bool Intersects(BoundingFrustum frustum)
  277. {
  278. if (frustum == null)
  279. throw new NullReferenceException();
  280. throw new NotImplementedException();
  281. }
  282. */
  283. public bool Intersects(BoundingSphere sphere)
  284. {
  285. bool result;
  286. Intersects(ref sphere, out result);
  287. return result;
  288. }
  289. public void Intersects(ref BoundingSphere sphere, out bool result)
  290. {
  291. float sqDistance;
  292. Vector3.DistanceSquared(ref sphere.Center, ref Center, out sqDistance);
  293. if (sqDistance > (sphere.Radius + Radius) * (sphere.Radius + Radius))
  294. result = false;
  295. else
  296. result = true;
  297. }
  298. public PlaneIntersectionType Intersects(Plane plane)
  299. {
  300. var result = default(PlaneIntersectionType);
  301. // TODO: we might want to inline this for performance reasons
  302. this.Intersects(ref plane, out result);
  303. return result;
  304. }
  305. public void Intersects(ref Plane plane, out PlaneIntersectionType result)
  306. {
  307. var distance = default(float);
  308. // TODO: we might want to inline this for performance reasons
  309. Vector3.Dot(ref plane.Normal, ref this.Center, out distance);
  310. distance += plane.D;
  311. if (distance > this.Radius)
  312. result = PlaneIntersectionType.Front;
  313. else if (distance < -this.Radius)
  314. result = PlaneIntersectionType.Back;
  315. else
  316. result = PlaneIntersectionType.Intersecting;
  317. }
  318. public Nullable<float> Intersects(Ray ray)
  319. {
  320. return ray.Intersects(this);
  321. }
  322. public void Intersects(ref Ray ray, out Nullable<float> result)
  323. {
  324. ray.Intersects(ref this, out result);
  325. }
  326. public static bool operator == (BoundingSphere a, BoundingSphere b)
  327. {
  328. return a.Equals(b);
  329. }
  330. public static bool operator != (BoundingSphere a, BoundingSphere b)
  331. {
  332. return !a.Equals(b);
  333. }
  334. internal string DebugDisplayString
  335. {
  336. get
  337. {
  338. return string.Concat(
  339. "Pos( ", this.Center.DebugDisplayString, " ) \r\n",
  340. "Radius( ", this.Radius.ToString(), " )"
  341. );
  342. }
  343. }
  344. public override string ToString()
  345. {
  346. return "{{Center:" + this.Center.ToString() + " Radius:" + this.Radius.ToString() + "}}";
  347. }
  348. #endregion Public Methods
  349. }
  350. }