PageRenderTime 46ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/Utilities/Math/Range.cs

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