PageRenderTime 95ms CodeModel.GetById 40ms app.highlight 19ms RepoModel.GetById 32ms app.codeStats 0ms

/Utilities/Datatypes/BoundingSphere.cs

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