PageRenderTime 136ms CodeModel.GetById 69ms app.highlight 12ms RepoModel.GetById 34ms app.codeStats 0ms

/Utilities/Math/Range.cs

#
C# | 338 lines | 211 code | 33 blank | 94 comment | 11 complexity | b033a521589d9d2b7d40d09cd1fe452a MD5 | raw file
  1using System.Diagnostics;
  2using System.IO;
  3using System.Runtime.InteropServices;
  4using Delta.Utilities.Helpers;
  5using NUnit.Framework;
  6
  7namespace Delta.Utilities.Math
  8{
  9	/// <summary>
 10	/// The Range object is used to store one or two values, which result in a 
 11	/// range. Getting the value of a range object, will give you a random
 12	/// value between Start and End.
 13	/// </summary>
 14	[StructLayout(LayoutKind.Sequential)]
 15	[DebuggerDisplay("Range(Start={Start}, End={End})")]
 16	public struct Range : ISaveLoadBinary
 17	{
 18		#region Constants
 19		/// <summary>
 20		/// Zero range, will always return 0.
 21		/// </summary>
 22		public static readonly Range Zero = new Range(0, 0);
 23
 24		/// <summary>
 25		/// One range, will always return 1.
 26		/// </summary>
 27		public static readonly Range One = new Range(1, 1);
 28
 29		/// <summary>
 30		/// Zero to One range, will return a random values between 0 and 1.
 31		/// </summary>
 32		public static readonly Range ZeroToOne = new Range(0, 1);
 33		#endregion
 34
 35		#region Start (Public)
 36		/// <summary>
 37		/// Start value of the range.
 38		/// </summary>
 39		public float Start
 40		{
 41			get
 42			{
 43				return internalStart;
 44			}
 45			set
 46			{
 47				if (value != internalStart)
 48				{
 49					internalStart = value;
 50					Difference = internalEnd -
 51					             internalStart;
 52				}
 53			}
 54		}
 55		#endregion
 56
 57		#region End (Public)
 58		/// <summary>
 59		/// End value of the range.
 60		/// </summary>
 61		public float End
 62		{
 63			get
 64			{
 65				return internalEnd;
 66			}
 67			set
 68			{
 69				if (value != internalEnd)
 70				{
 71					internalEnd = value;
 72					Difference = internalEnd -
 73					             internalStart;
 74				}
 75			}
 76		}
 77		#endregion
 78
 79		#region Difference (Public)
 80		/// <summary>
 81		/// Difference between Start and End, always a positive value because
 82		/// Max is always bigger than Min.
 83		/// </summary>
 84		public float Difference
 85		{
 86			get;
 87			private set;
 88		}
 89		#endregion
 90
 91		#region RandomValue (Public)
 92		/// <summary>
 93		/// Returns a random value between Start and End.
 94		/// </summary>
 95		/// <returns>Float</returns>
 96		public float RandomValue
 97		{
 98			get
 99			{
100				// Note: We do not need to randomize if start is the same value as end
101				return (Start != End)
102				       	? RandomHelper.RandomFloat(Start, End)
103				       	: Start;
104			}
105		}
106		#endregion
107
108		#region Private
109
110		#region internalStart (Private)
111		private float internalStart;
112		#endregion
113
114		#region internalEnd (Private)
115		private float internalEnd;
116		#endregion
117
118		#endregion
119
120		#region Constructors
121		/// <summary>
122		/// Create range with fixed value.
123		/// </summary>
124		/// <param name="setStartAndEndValue">Set start and end value</param>
125		public Range(float setStartAndEndValue)
126			: this()
127		{
128			Start = setStartAndEndValue;
129			End = setStartAndEndValue;
130		}
131
132		/// <summary>
133		/// Create range with given minimum and maximum values.
134		/// </summary>
135		/// <param name="setStartValue">Set start value</param>
136		/// <param name="setEndValue">Set end value</param>
137		public Range(float setStartValue, float setEndValue)
138			: this()
139		{
140			Start = setStartValue;
141			End = setEndValue;
142		}
143		#endregion
144
145		#region ISaveLoadBinary Members
146		/// <summary>
147		/// Loads Range from a stream (just Start and End).
148		/// </summary>
149		/// <param name="reader">The stream that will be used.</param>
150		public void Load(BinaryReader reader)
151		{
152			Start = reader.ReadSingle();
153			End = reader.ReadSingle();
154		}
155
156		/// <summary>
157		/// Saves Range to a stream (just Start and End).
158		/// </summary>
159		/// <param name="writer">The stream that will be used.</param>
160		public void Save(BinaryWriter writer)
161		{
162			writer.Write(Start);
163			writer.Write(End);
164		}
165		#endregion
166
167		#region op_Implicit (Operator)
168		/// <summary>
169		/// Operator to assign rango from float value.
170		/// </summary>
171		public static implicit operator Range(float value)
172		{
173			return new Range(value);
174		}
175		#endregion
176
177		#region op_Equality (Operator)
178		/// <summary>
179		/// Operator to check for equality
180		/// </summary>
181		/// <param name="a">First value of the comparison.</param>
182		/// <param name="b">Second value of the comparison.</param>
183		public static bool operator ==(Range a, Range b)
184		{
185			return b.Start == a.Start &&
186			       b.End == a.End;
187		}
188		#endregion
189
190		#region op_Inequality (Operator)
191		/// <summary>
192		/// Operator to check for inequality
193		/// </summary>
194		/// <param name="a">First value of the comparison.</param>
195		/// <param name="b">Second value of the comparison.</param>
196		public static bool operator !=(Range a, Range b)
197		{
198			return b.Start != a.Start ||
199			       b.End != a.End;
200		}
201		#endregion
202
203		#region GetValue (Public)
204		/// <summary>
205		/// Get value
206		/// </summary>
207		/// <param name="percentageFactor">Percentage (in the range of [0,1]) to
208		/// get the value based of the set Start and End. E.g. a factor of "0.5"
209		/// will return "3" for a range of [2,4].</param>
210		public float GetValue(float percentageFactor)
211		{
212			return Start + (Difference * percentageFactor);
213		}
214		#endregion
215
216		#region GetHashCode (Public)
217		/// <summary>
218		/// GetHashCode
219		/// </summary>
220		public override int GetHashCode()
221		{
222			return Start.GetHashCode() ^ End.GetHashCode();
223		}
224		#endregion
225
226		#region Equals (Public)
227		/// <summary>
228		/// Equals
229		/// </summary>
230		/// <param name="obj">Object</param>
231		public override bool Equals(object obj)
232		{
233			return (obj is Range)
234			       	? this == (Range)obj
235			       	: base.Equals(obj);
236		}
237		#endregion
238
239		#region ToString (Public)
240		/// <summary>
241		/// To string
242		/// </summary>
243		public override string ToString()
244		{
245			return "Range(Start=" + Start + ", End=" + End + ")";
246		}
247		#endregion
248
249		/// <summary>
250		/// Tests
251		/// </summary>
252		internal class RangeTests
253		{
254			#region TestConstructor
255			/// <summary>
256			/// Test constructor
257			/// </summary>
258			[Test]
259			public void TestConstructor()
260			{
261				Range testRange = new Range(10, 1);
262				Assert.Equal(testRange.Start, 10);
263				Assert.Equal(testRange.End, 1);
264
265				testRange = new Range(1, 2);
266				Assert.Equal(testRange.Start, 1);
267				Assert.Equal(testRange.End, 2);
268			}
269			#endregion
270
271			#region TestNegativeRanges
272			/// <summary>
273			/// Test negative ranges
274			/// </summary>
275			[Test]
276			public void TestNegativeRanges()
277			{
278				Range negativeRange = new Range(-100, 50);
279				Assert.Equal(negativeRange.Start, -100);
280				Assert.Equal(negativeRange.End, 50);
281
282				Assert.Equal(negativeRange.GetValue(0.0f), -100);
283				Assert.Equal(negativeRange.GetValue(0.5f), -25);
284				Assert.Equal(negativeRange.GetValue(1.0f), 50);
285
286				Assert.Between(negativeRange.RandomValue, -100, 50);
287			}
288			#endregion
289
290			#region TestRanges
291			/// <summary>
292			/// Test different ranges
293			/// </summary>
294			[Test]
295			public void TestRanges()
296			{
297				// Min and Max value both are assigned the given value
298				Range singleRange = new Range(2);
299				Assert.True(singleRange.Start == singleRange.End);
300				Assert.Equal(2, singleRange.Start);
301
302				// Test the implicit conversion from float
303				singleRange = 4.3f;
304				Assert.Equal(singleRange, new Range(4.3f));
305
306				// Both values assigned still work normally too
307				Range range = new Range
308				{
309					Start = 4,
310					End = 6,
311				};
312				Assert.NearlyEqual(range.Start, 4);
313				Assert.NearlyEqual(range.End, 6);
314
315				// Test if the random value is between the boundaries
316				Assert.Between(range.RandomValue, range.Start, range.End);
317				Assert.Between(range.RandomValue, range.Start, range.End);
318				Assert.Between(range.RandomValue, range.Start, range.End);
319			}
320			#endregion
321
322			#region GetValue
323			/// <summary>
324			/// Get value
325			/// </summary>
326			[Test]
327			public void GetValue()
328			{
329				Range range = new Range(10, 0);
330
331				Assert.Equal(range.GetValue(0.0f), 10);
332				Assert.Equal(range.GetValue(1.0f), 0);
333				Assert.Equal(range.GetValue(0.5f), 5);
334			}
335			#endregion
336		}
337	}
338}