PageRenderTime 41ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/Utilities/Datatypes/BoundingSphere.cs

#
C# | 382 lines | 254 code | 25 blank | 103 comment | 19 complexity | 104a88761d095fd460380071d532bb2a MD5 | raw file
Possible License(s): Apache-2.0
  1. using System;
  2. using System.IO;
  3. using Delta.Utilities.Datatypes.Advanced;
  4. using NUnit.Framework;
  5. namespace Delta.Utilities.Datatypes
  6. {
  7. /// <summary>
  8. /// Bounding sphere struct with basically just a center position in 3D and
  9. /// a radius. Allows quick collision and bounding sphere intersection tests
  10. /// and is sometimes even more useful than bounding boxes.
  11. /// </summary>
  12. public struct BoundingSphere : ISaveLoadBinary, IEquatable<BoundingSphere>
  13. {
  14. #region CreateFromMinMax (Static)
  15. /// <summary>
  16. /// Create from minimum maximum
  17. /// </summary>
  18. /// <param name="max">Max</param>
  19. /// <param name="min">Min</param>
  20. /// <returns>
  21. /// Bounding sphere created from the given minimum and maximum vector.
  22. /// </returns>
  23. public static BoundingSphere CreateFromMinMax(Vector min, Vector max)
  24. {
  25. Vector center = (max + min) * 0.5f;
  26. Vector sidelengths = (max - min) * 0.5f;
  27. float radius = sidelengths.Length;
  28. return new BoundingSphere(center, radius);
  29. }
  30. #endregion
  31. #region Center (Public)
  32. /// <summary>
  33. /// Center
  34. /// </summary>
  35. public Vector Center;
  36. #endregion
  37. #region Radius (Public)
  38. /// <summary>
  39. /// Radius
  40. /// </summary>
  41. public float Radius;
  42. #endregion
  43. #region Constructors
  44. /// <summary>
  45. /// Create bounding sphere
  46. /// </summary>
  47. /// <param name="setCenter">Set Center</param>
  48. /// <param name="setRadius">Set Radius</param>
  49. public BoundingSphere(Vector setCenter, float setRadius)
  50. {
  51. Center = setCenter;
  52. Radius = setRadius;
  53. }
  54. #endregion
  55. #region IEquatable<BoundingSphere> Members
  56. /// <summary>
  57. /// Does another bounding sphere have the same values as this one?
  58. /// </summary>
  59. /// <param name="other">Other</param>
  60. /// <returns>
  61. /// True if the other bounding sphere has the same values, false otherwise.
  62. /// </returns>
  63. public bool Equals(BoundingSphere other)
  64. {
  65. return Center == other.Center &&
  66. Radius == other.Radius;
  67. }
  68. #endregion
  69. #region ISaveLoadBinary Members
  70. /// <summary>
  71. /// Loads the BoundingSphere (Center+Radius) from a stream.
  72. /// </summary>
  73. /// <param name="reader">Reader</param>
  74. public void Load(BinaryReader reader)
  75. {
  76. Center.Load(reader);
  77. Radius = reader.ReadSingle();
  78. }
  79. /// <summary>
  80. /// Saves the BoundingSphere (Center+Radius) to a stream.
  81. /// </summary>
  82. /// <param name="writer">Writer</param>
  83. public void Save(BinaryWriter writer)
  84. {
  85. Center.Save(writer);
  86. writer.Write(Radius);
  87. }
  88. #endregion
  89. #region op_Equality (Operator)
  90. /// <summary>
  91. /// Check for equality
  92. /// </summary>
  93. /// <param name="value1">Value 1</param>
  94. /// <param name="value2">Value 2</param>
  95. /// <returns>True if the spheres are equal, false otherwise.</returns>
  96. public static bool operator ==(BoundingSphere value1,
  97. BoundingSphere value2)
  98. {
  99. return value1.Center == value2.Center &&
  100. value1.Radius == value2.Radius;
  101. }
  102. #endregion
  103. #region op_Inequality (Operator)
  104. /// <summary>
  105. /// Check for inequality
  106. /// </summary>
  107. /// <param name="value1">Value 1</param>
  108. /// <param name="value2">Value 2</param>
  109. /// <returns>True if both spheres are not equal, false otherwise.</returns>
  110. public static bool operator !=(BoundingSphere value1,
  111. BoundingSphere value2)
  112. {
  113. return value1.Center != value2.Center ||
  114. value1.Radius != value2.Radius;
  115. }
  116. #endregion
  117. #region Contains (Public)
  118. /// <summary>
  119. /// Check whether a Sphere contains another sphere
  120. /// </summary>
  121. /// <param name="sphere">The sphere.</param>
  122. /// <returns>
  123. /// Containment type (fully, partial or none)
  124. /// </returns>
  125. public ContainmentType Contains(BoundingSphere sphere)
  126. {
  127. float distance = Vector.Distance(Center, sphere.Center);
  128. if (distance - (sphere.Radius + Radius) > 0)
  129. {
  130. return ContainmentType.None;
  131. }
  132. else
  133. {
  134. if (distance + sphere.Radius < Radius)
  135. {
  136. return ContainmentType.Fully;
  137. }
  138. else
  139. {
  140. return ContainmentType.Partial;
  141. }
  142. }
  143. }
  144. /// <summary>
  145. /// Check whether a Sphere contains a bounding box
  146. /// </summary>
  147. /// <param name="box">The box.</param>
  148. /// <returns>Containment type (fully, partial or none)</returns>
  149. public ContainmentType Contains(BoundingBox box)
  150. {
  151. Vector closestPoint = Vector.Clamp(Center, box.Min, box.Max);
  152. float distance = Vector.DistanceSquared(Center, closestPoint);
  153. if (distance > Radius * Radius)
  154. {
  155. return ContainmentType.None;
  156. }
  157. float radiussquared = Radius * Radius;
  158. Vector objectsDistance = new Vector(
  159. Center.X - box.Min.X, Center.Y - box.Max.Y, Center.Z - box.Max.Z);
  160. if (objectsDistance.LengthSquared > radiussquared)
  161. {
  162. return ContainmentType.Partial;
  163. }
  164. objectsDistance.X = Center.X - box.Max.X;
  165. objectsDistance.Y = Center.Y - box.Max.Y;
  166. objectsDistance.Z = Center.Z - box.Max.Z;
  167. if (objectsDistance.LengthSquared > radiussquared)
  168. {
  169. return ContainmentType.Partial;
  170. }
  171. objectsDistance.X = Center.X - box.Max.X;
  172. objectsDistance.Y = Center.Y - box.Min.Y;
  173. objectsDistance.Z = Center.Z - box.Max.Z;
  174. if (objectsDistance.LengthSquared > radiussquared)
  175. {
  176. return ContainmentType.Partial;
  177. }
  178. objectsDistance.X = Center.X - box.Min.X;
  179. objectsDistance.Y = Center.Y - box.Min.Y;
  180. objectsDistance.Z = Center.Z - box.Max.Z;
  181. if (objectsDistance.LengthSquared > radiussquared)
  182. {
  183. return ContainmentType.Partial;
  184. }
  185. objectsDistance.X = Center.X - box.Min.X;
  186. objectsDistance.Y = Center.Y - box.Max.Y;
  187. objectsDistance.Z = Center.Z - box.Min.Z;
  188. if (objectsDistance.LengthSquared > radiussquared)
  189. {
  190. return ContainmentType.Partial;
  191. }
  192. objectsDistance.X = Center.X - box.Max.X;
  193. objectsDistance.Y = Center.Y - box.Max.Y;
  194. objectsDistance.Z = Center.Z - box.Min.Z;
  195. if (objectsDistance.LengthSquared > radiussquared)
  196. {
  197. return ContainmentType.Partial;
  198. }
  199. objectsDistance.X = Center.X - box.Max.X;
  200. objectsDistance.Y = Center.Y - box.Min.Y;
  201. objectsDistance.Z = Center.Z - box.Min.Z;
  202. if (objectsDistance.LengthSquared > radiussquared)
  203. {
  204. return ContainmentType.Partial;
  205. }
  206. objectsDistance.X = Center.X - box.Min.X;
  207. objectsDistance.Y = Center.Y - box.Min.Y;
  208. objectsDistance.Z = Center.Z - box.Min.Z;
  209. if (objectsDistance.LengthSquared > radiussquared)
  210. {
  211. return ContainmentType.Partial;
  212. }
  213. return ContainmentType.Fully;
  214. }
  215. /// <summary>
  216. /// Check if the Point lays inside this sphere or not.
  217. /// </summary>
  218. /// <param name="point">Point to check containment for.</param>
  219. /// <returns>True if the point lays inside of the sphere.</returns>
  220. public ContainmentType Contains(Vector point)
  221. {
  222. float distance = Vector.Distance(Center, point);
  223. if ((distance - Radius) > 0)
  224. {
  225. return ContainmentType.None;
  226. }
  227. else
  228. {
  229. return ContainmentType.Fully;
  230. }
  231. }
  232. #endregion
  233. #region Equals (Public)
  234. /// <summary>
  235. /// Is another object an bounding sphere and has it the same values?
  236. /// </summary>
  237. /// <param name="obj">Object to compare.</param>
  238. /// <returns>
  239. /// True if the other bounding sphere has the same values, false otherwise.
  240. /// </returns>
  241. public override bool Equals(object obj)
  242. {
  243. if (obj is BoundingSphere)
  244. {
  245. return Equals((BoundingSphere)obj);
  246. }
  247. return base.Equals(obj);
  248. }
  249. #endregion
  250. #region GetHashCode (Public)
  251. /// <summary>
  252. /// Get hash code
  253. /// </summary>
  254. /// <returns>hash code</returns>
  255. public override int GetHashCode()
  256. {
  257. return Center.GetHashCode() ^ Radius.GetHashCode();
  258. }
  259. #endregion
  260. /// <summary>
  261. /// Tests
  262. /// </summary>
  263. internal class BoundingSphereTests
  264. {
  265. #region CreateFromMinMax
  266. /// <summary>
  267. /// Create a sphere from a Min, Max vectors
  268. /// </summary>
  269. [Test]
  270. public void CreateFromMinMax()
  271. {
  272. Vector one = new Vector(1, 2, 3);
  273. Vector two = new Vector(4, 5, 6);
  274. Vector max = (one + two) * 0.5f;
  275. Vector min = (two - one) * 0.5f;
  276. float rad = min.Length;
  277. BoundingSphere bOne = new BoundingSphere(max, rad);
  278. BoundingSphere bTwo = BoundingSphere.CreateFromMinMax(one, two);
  279. Assert.Equals(bOne, bTwo);
  280. }
  281. #endregion
  282. #region TestIntersects
  283. /// <summary>
  284. /// Test Intersection
  285. /// </summary>
  286. [Test]
  287. public void TestIntersects()
  288. {
  289. // Contains Sphere
  290. BoundingSphere one =
  291. BoundingSphere.CreateFromMinMax(
  292. new Vector(1, 2, 3), new Vector(10, 20, 30));
  293. BoundingSphere two =
  294. BoundingSphere.CreateFromMinMax(
  295. new Vector(-1, -2, -3), new Vector(5, 10, 15));
  296. BoundingSphere three =
  297. BoundingSphere.CreateFromMinMax(
  298. new Vector(40, 40, 40), new Vector(45, 45, 45));
  299. BoundingSphere four =
  300. BoundingSphere.CreateFromMinMax(
  301. new Vector(2, 3, 4), new Vector(4, 14, 24));
  302. // Contains Point
  303. Vector pt = new Vector(4, 4, 4);
  304. // Contains Box
  305. BoundingBox box1 = new BoundingBox(new Vector(2, 3, 4),
  306. new Vector(3, 4, 4));
  307. Assert.Equal(one.Contains(two), ContainmentType.Partial);
  308. Assert.Equal(one.Contains(three), ContainmentType.None);
  309. Assert.Equal(one.Contains(four), ContainmentType.Fully);
  310. Assert.Equal(one.Contains(box1), ContainmentType.Fully);
  311. Assert.Equals(one.Contains(pt), true);
  312. }
  313. #endregion
  314. #region Equality
  315. /// <summary>
  316. /// Equality
  317. /// </summary>
  318. [Test]
  319. public void Equality()
  320. {
  321. Assert.Equal(new BoundingSphere(),
  322. new BoundingSphere(Vector.Zero, 0.0f));
  323. }
  324. #endregion
  325. #region SaveAndLoad
  326. /// <summary>
  327. /// Test to save and load spheres into a binary stream
  328. /// </summary>
  329. [Test]
  330. public void SaveAndLoad()
  331. {
  332. BoundingSphere sphere = new BoundingSphere(Vector.Zero, 0.5f);
  333. MemoryStream memHandle = new MemoryStream();
  334. Assert.Equal(0, memHandle.Position);
  335. BinaryWriter writer = new BinaryWriter(memHandle);
  336. // save all the current data
  337. sphere.Save(writer);
  338. // and finally check (for saving) if the file was written correctly
  339. Assert.NotEqual(0, memHandle.Length);
  340. // then we create an "empty" material
  341. BoundingSphere loadsphere = new BoundingSphere
  342. (Vector.Half, 0.8f);
  343. memHandle.Position = 0;
  344. // which we use to load the material values from the the file
  345. // Note: The using closes the file access too
  346. BinaryReader reader = new BinaryReader(memHandle);
  347. loadsphere.Load(reader);
  348. // before we finally check if everything is loaded correctly
  349. Assert.Equal(sphere, loadsphere);
  350. writer.Close();
  351. reader.Close();
  352. memHandle.Close();
  353. }
  354. #endregion
  355. }
  356. }
  357. }