PageRenderTime 44ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/Utilities/Datatypes/Size.cs

#
C# | 581 lines | 322 code | 47 blank | 212 comment | 7 complexity | b9ea9ac8b3c862ea335f1b0af1f824e4 MD5 | raw file
Possible License(s): Apache-2.0
  1. using System;
  2. using System.ComponentModel;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Runtime.InteropServices;
  6. using Delta.Utilities.Helpers;
  7. using NUnit.Framework;
  8. namespace Delta.Utilities.Datatypes
  9. {
  10. /// <summary>
  11. /// Size struct, has just Width and Height as floats and is used to manage
  12. /// sizes, most importantly for our quadratic screen space and the
  13. /// Rectangle struct.
  14. /// </summary>
  15. [Serializable]
  16. [StructLayout(LayoutKind.Explicit)]
  17. [DebuggerDisplay("Size=({Width}, {Height})")]
  18. [Description("Expand to edit this Size")]
  19. [TypeConverter(typeof(ExpandableObjectConverter))]
  20. public struct Size : ISaveLoadBinary, IEquatable<Size>
  21. {
  22. #region Constants
  23. /// <summary>
  24. /// Zero size, both Width and Height are 0.
  25. /// </summary>
  26. /// <returns>Size</returns>
  27. public static readonly Size Zero = new Size(0.0f);
  28. /// <summary>
  29. /// One size, has both Width and Height set to 1.
  30. /// </summary>
  31. public static readonly Size One = new Size(1.0f);
  32. /// <summary>
  33. /// Half size, has both Width and Height set to 0.5.
  34. /// </summary>
  35. public static readonly Size Half = new Size(0.5f);
  36. /// <summary>
  37. /// Quarter size, has both Width and Height set to 0.25.
  38. /// </summary>
  39. public static readonly Size Quarter = new Size(0.25f);
  40. #endregion
  41. #region Max (Static)
  42. /// <summary>
  43. /// Returns the max. dimesions based on both values.
  44. /// e.g. Max((2,5) (4,4)) -> (4,5)
  45. /// </summary>
  46. /// <param name="value1">value1</param>
  47. /// <param name="value2">value2</param>
  48. /// <returns>the max. dimesions based on both values</returns>
  49. public static Size Max(Size value1, Size value2)
  50. {
  51. return new Size(MathHelper.Max(value1.Width, value2.Width),
  52. MathHelper.Max(value1.Height, value2.Height));
  53. }
  54. #endregion
  55. #region FromString (Static)
  56. /// <summary>
  57. /// Convert a string to a Size, works the same way as for Point.FromString
  58. /// </summary>
  59. /// <param name="pointString">The string in the correct format
  60. /// (with or without brackets, comma or space seperated).</param>
  61. /// <returns>
  62. /// Size from the given string or Zero if parsing failed.
  63. /// </returns>
  64. public static Size FromString(string pointString)
  65. {
  66. return (Size)Point.FromString(pointString);
  67. }
  68. #endregion
  69. #region Width (Public)
  70. /// <summary>
  71. /// Width
  72. /// </summary>
  73. [FieldOffset(0)]
  74. public float Width;
  75. #endregion
  76. #region Height (Public)
  77. /// <summary>
  78. /// Height
  79. /// </summary>
  80. [FieldOffset(4)]
  81. public float Height;
  82. #endregion
  83. #region WidthProperty (Public)
  84. /// <summary>
  85. /// Property-wrapper for using the X field in the editor.
  86. /// </summary>
  87. [Browsable(true)]
  88. [DisplayName("Width")]
  89. public float WidthProperty
  90. {
  91. get
  92. {
  93. return Width;
  94. }
  95. set
  96. {
  97. Width = value;
  98. }
  99. }
  100. #endregion
  101. #region HeightProperty (Public)
  102. /// <summary>
  103. /// Property-wrapper for using the Y field in the editor
  104. /// </summary>
  105. [Browsable(true)]
  106. [DisplayName("Height")]
  107. public float HeightProperty
  108. {
  109. get
  110. {
  111. return Height;
  112. }
  113. set
  114. {
  115. Height = value;
  116. }
  117. }
  118. #endregion
  119. #region WidthHalf (Public)
  120. /// <summary>
  121. /// The half width of the size.
  122. /// </summary>
  123. [Browsable(false)]
  124. public float WidthHalf
  125. {
  126. get
  127. {
  128. return Width * 0.5f;
  129. }
  130. }
  131. #endregion
  132. #region HeightHalf (Public)
  133. /// <summary>
  134. /// The half height of the size.
  135. /// </summary>
  136. [Browsable(false)]
  137. public float HeightHalf
  138. {
  139. get
  140. {
  141. return Height * 0.5f;
  142. }
  143. }
  144. #endregion
  145. #region IsZero (Public)
  146. /// <summary>
  147. /// Returns "true" if the size hasn't any width AND height.
  148. /// </summary>
  149. [Browsable(false)]
  150. public bool IsZero
  151. {
  152. get
  153. {
  154. return Width == 0.0f && Height == 0.0f;
  155. }
  156. }
  157. #endregion
  158. #region Constructors
  159. /// <summary>
  160. /// Create size
  161. /// </summary>
  162. /// <param name="setDimension">setDimension</param>
  163. public Size(float setDimension)
  164. : this(setDimension, setDimension)
  165. {
  166. }
  167. /// <summary>
  168. /// Create size
  169. /// </summary>
  170. /// <param name="setHeight">setHeight</param>
  171. /// <param name="setWidth">setWidht</param>
  172. public Size(float setWidth, float setHeight)
  173. : this()
  174. {
  175. Width = setWidth;
  176. Height = setHeight;
  177. }
  178. /// <summary>
  179. /// Create size
  180. /// </summary>
  181. /// <param name="dataReader">Data reader</param>
  182. public Size(BinaryReader dataReader)
  183. : this()
  184. {
  185. Load(dataReader);
  186. }
  187. #endregion
  188. #region IEquatable<Size> Members
  189. /// <summary>
  190. /// Check if another size has almost the same values (using
  191. /// MathHelper.Epsilon).
  192. /// </summary>
  193. /// <param name="other">Other size to compare to</param>
  194. /// <returns>True if the other size has nearly the same values</returns>
  195. public bool Equals(Size other)
  196. {
  197. return
  198. // Allow a difference of the Epsilon (in both directions)
  199. // for the width value range
  200. Width - MathHelper.Epsilon <= other.Width &&
  201. Width + MathHelper.Epsilon >= other.Width &&
  202. // and height value range
  203. Height - MathHelper.Epsilon <= other.Height &&
  204. Height + MathHelper.Epsilon >= other.Height;
  205. }
  206. #endregion
  207. #region ISaveLoadBinary Members
  208. /// <summary>
  209. /// Load size from binary stream (8 bytes, 2 floats)
  210. /// </summary>
  211. /// <param name="reader">reader</param>
  212. public void Load(BinaryReader reader)
  213. {
  214. Width = reader.ReadSingle();
  215. Height = reader.ReadSingle();
  216. }
  217. /// <summary>
  218. /// Save size to binary stream (8 bytes, 2 floats)
  219. /// </summary>
  220. /// <param name="writer">writer</param>
  221. public void Save(BinaryWriter writer)
  222. {
  223. writer.Write(Width);
  224. writer.Write(Height);
  225. }
  226. #endregion
  227. #region op_Equality (Operator)
  228. /// <summary>
  229. /// Operator for equality
  230. /// </summary>
  231. /// <param name="value1">Size 1</param>
  232. /// <param name="value2">Size 2</param>
  233. /// <returns>True if both sizes are the same</returns>
  234. public static bool operator ==(Size value1, Size value2)
  235. {
  236. return value1.Width == value2.Width &&
  237. value1.Height == value2.Height;
  238. }
  239. #endregion
  240. #region op_Inequality (Operator)
  241. /// <summary>
  242. /// Operator for inequality
  243. /// </summary>
  244. /// <param name="value1">Size 1</param>
  245. /// <param name="value2">Size 2</param>
  246. /// <returns>True if both sizes are not the same</returns>
  247. public static bool operator !=(Size value1, Size value2)
  248. {
  249. return value1.Width != value2.Width ||
  250. value1.Height != value2.Height;
  251. }
  252. #endregion
  253. #region op_Addition (Operator)
  254. /// <summary>
  255. /// Operator for addition
  256. /// </summary>
  257. /// <param name="value1">Size 1</param>
  258. /// <param name="value2">Size 2</param>
  259. /// <returns>Added size from both sizes</returns>
  260. public static Size operator +(Size value1, Size value2)
  261. {
  262. return new Size(value1.Width + value2.Width,
  263. value1.Height + value2.Height);
  264. }
  265. #endregion
  266. #region op_Subtraction (Operator)
  267. /// <summary>
  268. /// Operator for subtraction
  269. /// </summary>
  270. /// <param name="value1">Size 1</param>
  271. /// <param name="value2">Size 2</param>
  272. /// <returns>Subtracted size from both sizes</returns>
  273. public static Size operator -(Size value1, Size value2)
  274. {
  275. return new Size(value1.Width - value2.Width,
  276. value1.Height - value2.Height);
  277. }
  278. #endregion
  279. #region op_Multiply (Operator)
  280. /// <summary>
  281. /// Operator for multiplication points
  282. /// </summary>
  283. /// <param name="value1">Size 1</param>
  284. /// <param name="value2">Size 2</param>
  285. /// <returns>
  286. /// Size with both Width multiplied and both Heights multiplied.
  287. /// </returns>
  288. public static Size operator *(Size value1, Size value2)
  289. {
  290. return new Size(value1.Width * value2.Width,
  291. value1.Height * value2.Height);
  292. }
  293. /// <summary>
  294. /// Operator for multiplying a Size with a scale factor.
  295. /// </summary>
  296. /// <param name="value">Size value</param>
  297. /// <param name="scaleFactor">Scale Factor</param>
  298. /// <returns>
  299. /// Size with both Width and Height multiplied by scaleFactor
  300. /// </returns>
  301. public static Size operator *(Size value, float scaleFactor)
  302. {
  303. return new Size(value.Width * scaleFactor, value.Height * scaleFactor);
  304. }
  305. /// <summary>
  306. /// Operator for multiplying a Size with a scale factor.
  307. /// </summary>
  308. /// <param name="scaleFactor">Scale Factor</param>
  309. /// <param name="value">Size value</param>
  310. /// <returns>
  311. /// Size with both Width and Height multiplied by scaleFactor
  312. /// </returns>
  313. public static Size operator *(float scaleFactor, Size value)
  314. {
  315. return new Size(value.Width * scaleFactor, value.Height * scaleFactor);
  316. }
  317. #endregion
  318. #region op_Division (Operator)
  319. /// <summary>
  320. /// Operator to divide a Size with a float.
  321. /// </summary>
  322. /// <param name="value">Value</param>
  323. /// <param name="divisor">Divisor</param>
  324. /// <returns>value.Width / divisor, value.Height / divisor</returns>
  325. public static Size operator /(Size value, float divisor)
  326. {
  327. return new Size(value.Width / divisor, value.Height / divisor);
  328. }
  329. /// <summary>
  330. /// Operator to divide a float with a Size.
  331. /// </summary>
  332. /// <param name="divisor">Divisor</param>
  333. /// <param name="value">Value</param>
  334. /// <returns>value / divisor.Width, value / divisor.Height</returns>
  335. public static Size operator /(float value, Size divisor)
  336. {
  337. return new Size(value / divisor.Width, value / divisor.Height);
  338. }
  339. /// <summary>
  340. /// Operator to divide a Size with another Size.
  341. /// </summary>
  342. /// <param name="divisor">Divisor</param>
  343. /// <param name="value">Value</param>
  344. /// <returns>value.Width / divisor.Width, value.Height / divisor.Height
  345. /// </returns>
  346. public static Size operator /(Size value, Size divisor)
  347. {
  348. return new Size(value.Width / divisor.Width,
  349. value.Height / divisor.Height);
  350. }
  351. #endregion
  352. #region op_Explicit (Operator)
  353. /// <summary>
  354. /// Operator to implicit convert Point to Size.
  355. /// </summary>
  356. /// <param name="anyPoint">Any point value</param>
  357. /// <returns>Point converted to size</returns>
  358. public static explicit operator Size(Point anyPoint)
  359. {
  360. return new Size(anyPoint.X, anyPoint.Y);
  361. }
  362. #endregion
  363. #region Equals (Public)
  364. /// <summary>
  365. /// Check in another object is a Size and equal to this size.
  366. /// </summary>
  367. /// <param name="obj">Object to compare to</param>
  368. /// <returns>True if both sizes are the same</returns>
  369. public override bool Equals(object obj)
  370. {
  371. return (obj is Size)
  372. ? Equals((Size)obj)
  373. : base.Equals(obj);
  374. }
  375. #endregion
  376. #region GetHashCode (Public)
  377. /// <summary>
  378. /// Get hash code
  379. /// </summary>
  380. /// <returns>Hash code from Width and Height</returns>
  381. public override int GetHashCode()
  382. {
  383. return Width.GetHashCode() + Height.GetHashCode();
  384. }
  385. #endregion
  386. #region Round (Public)
  387. /// <summary>
  388. /// Round the width and height values to the nearest integer value.
  389. /// </summary>
  390. /// <returns>Size with rounded width and height values.</returns>
  391. public Size Round()
  392. {
  393. return new Size(MathHelper.Round(Width), MathHelper.Round(Height));
  394. }
  395. #endregion
  396. #region ToString (Public)
  397. /// <summary>
  398. /// To string
  399. /// </summary>
  400. /// <returns>string</returns>
  401. public override string ToString()
  402. {
  403. return "(" + Width.ToInvariantString() + ", " +
  404. Height.ToInvariantString() + ")";
  405. }
  406. #endregion
  407. #region ToCommaString (Public)
  408. /// <summary>
  409. /// Returns the vector as a string that can be used in a Setting files,
  410. /// which is just using the x, y format (and works fine with FromString).
  411. /// </summary>
  412. /// <returns>vector</returns>
  413. public string ToCommaString()
  414. {
  415. return Width.ToInvariantString() + ", " +
  416. Height.ToInvariantString();
  417. }
  418. #endregion
  419. /// <summary>
  420. /// Tests
  421. /// </summary>
  422. internal class SizeTests
  423. {
  424. #region SizeOf
  425. /// <summary>
  426. /// Checks if the size of Point is exactly 8 bytes (2 floats: X and Y)
  427. /// </summary>
  428. [Test]
  429. public void SizeOf()
  430. {
  431. // Size consists of 2 floats: Width and Height
  432. Assert.Equal(2 * 4, Marshal.SizeOf(typeof(Size)));
  433. }
  434. #endregion
  435. #region SizeTest
  436. /// <summary>
  437. /// Size test
  438. /// </summary>
  439. [Test]
  440. public void SizeTest()
  441. {
  442. Size Size1 = new Size(2, 2);
  443. Size Size2 = new Size(1, 1);
  444. Assert.Equal(Size2.Width, Size1.WidthHalf);
  445. Assert.Equal(Size2.Height, Size1.HeightHalf);
  446. }
  447. #endregion
  448. #region Equality
  449. /// <summary>
  450. /// Equality
  451. /// </summary>
  452. [Test]
  453. public void Equality()
  454. {
  455. Assert.Equal(new Size(10, 10), new Size(10));
  456. Assert.NotEqual(new Size(10, 5), new Size(10f, 10f));
  457. }
  458. #endregion
  459. #region Addition
  460. /// <summary>
  461. /// Addition
  462. /// </summary>
  463. [Test]
  464. public void Addition()
  465. {
  466. Assert.Equal(new Size(9, 8.8f), new Size(5, 3) + new Size(4, 5.8f));
  467. Assert.Equal(new Size(-1, -2.8f),
  468. new Size(-5, 3) + new Size(4, -5.8f));
  469. }
  470. #endregion
  471. #region Substraction
  472. /// <summary>
  473. /// Substraction
  474. /// </summary>
  475. [Test]
  476. public void Substraction()
  477. {
  478. Assert.Equal(new Size(1, -2.8f), new Size(5, 3) - new Size(4, 5.8f));
  479. Assert.Equal(new Size(-9, 8.8f), new Size(-5, 3) - new Size(4, -5.8f));
  480. }
  481. #endregion
  482. #region Multiplication
  483. /// <summary>
  484. /// Multiplication
  485. /// </summary>
  486. [Test]
  487. public void Multiplication()
  488. {
  489. // with a scale factor
  490. Assert.Equal(new Size(10, 20), new Size(2, 4) * 5);
  491. Assert.Equal(new Size(-1, -2), new Size(2, 4) * -0.5f);
  492. Assert.Equal(new Size(0.5f, 1), 0.25f * new Size(2, 4));
  493. }
  494. #endregion
  495. #region NearlyEquals
  496. /// <summary>
  497. /// Nearly equals
  498. /// </summary>
  499. [Test]
  500. public void NearlyEquals()
  501. {
  502. Size testSize = new Size(2, 3);
  503. // Check the size directly
  504. Assert.True(testSize.Equals(testSize),
  505. "testSize is not equal testSize");
  506. // by the "object" overload from .NET
  507. Assert.True(testSize.Equals((object)testSize),
  508. "testSize is not equal testSize");
  509. // and the nearly equal check
  510. Assert.True(testSize.Equals(
  511. new Size(2 + MathHelper.Epsilon, 3 - MathHelper.Epsilon)),
  512. "Size.NearlyEquals() chaecks a too small epsilon");
  513. // Finally check the "bad" false cases with unequal values
  514. Assert.False(testSize.Equals(new Size(4, 3)),
  515. new Size(4, 3) + " shouldn't be (nearly) equal to " + testSize);
  516. // and a too big epsilon
  517. Assert.False(testSize.Equals(
  518. new Size(2 + (2 * MathHelper.Epsilon), 3)),
  519. "Size.NearlyEquals() checks a too big epsilon");
  520. }
  521. #endregion
  522. #region ToString
  523. /// <summary>
  524. /// To string
  525. /// </summary>
  526. [Test]
  527. public new void ToString()
  528. {
  529. Assert.Equal("(5, 3.5)", new Size(5, 3.5f).ToString());
  530. }
  531. #endregion
  532. }
  533. }
  534. }