/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
- using System;
- using System.ComponentModel;
- using System.Diagnostics;
- using System.IO;
- using System.Runtime.InteropServices;
- using Delta.Utilities.Helpers;
- using NUnit.Framework;
-
- namespace Delta.Utilities.Datatypes
- {
- /// <summary>
- /// Size struct, has just Width and Height as floats and is used to manage
- /// sizes, most importantly for our quadratic screen space and the
- /// Rectangle struct.
- /// </summary>
- [Serializable]
- [StructLayout(LayoutKind.Explicit)]
- [DebuggerDisplay("Size=({Width}, {Height})")]
- [Description("Expand to edit this Size")]
- [TypeConverter(typeof(ExpandableObjectConverter))]
- public struct Size : ISaveLoadBinary, IEquatable<Size>
- {
- #region Constants
- /// <summary>
- /// Zero size, both Width and Height are 0.
- /// </summary>
- /// <returns>Size</returns>
- public static readonly Size Zero = new Size(0.0f);
-
- /// <summary>
- /// One size, has both Width and Height set to 1.
- /// </summary>
- public static readonly Size One = new Size(1.0f);
-
- /// <summary>
- /// Half size, has both Width and Height set to 0.5.
- /// </summary>
- public static readonly Size Half = new Size(0.5f);
-
- /// <summary>
- /// Quarter size, has both Width and Height set to 0.25.
- /// </summary>
- public static readonly Size Quarter = new Size(0.25f);
- #endregion
-
- #region Max (Static)
- /// <summary>
- /// Returns the max. dimesions based on both values.
- /// e.g. Max((2,5) (4,4)) -> (4,5)
- /// </summary>
- /// <param name="value1">value1</param>
- /// <param name="value2">value2</param>
- /// <returns>the max. dimesions based on both values</returns>
- public static Size Max(Size value1, Size value2)
- {
- return new Size(MathHelper.Max(value1.Width, value2.Width),
- MathHelper.Max(value1.Height, value2.Height));
- }
- #endregion
-
- #region FromString (Static)
- /// <summary>
- /// Convert a string to a Size, works the same way as for Point.FromString
- /// </summary>
- /// <param name="pointString">The string in the correct format
- /// (with or without brackets, comma or space seperated).</param>
- /// <returns>
- /// Size from the given string or Zero if parsing failed.
- /// </returns>
- public static Size FromString(string pointString)
- {
- return (Size)Point.FromString(pointString);
- }
- #endregion
-
- #region Width (Public)
- /// <summary>
- /// Width
- /// </summary>
- [FieldOffset(0)]
- public float Width;
- #endregion
-
- #region Height (Public)
- /// <summary>
- /// Height
- /// </summary>
- [FieldOffset(4)]
- public float Height;
- #endregion
-
- #region WidthProperty (Public)
- /// <summary>
- /// Property-wrapper for using the X field in the editor.
- /// </summary>
- [Browsable(true)]
- [DisplayName("Width")]
- public float WidthProperty
- {
- get
- {
- return Width;
- }
- set
- {
- Width = value;
- }
- }
- #endregion
-
- #region HeightProperty (Public)
- /// <summary>
- /// Property-wrapper for using the Y field in the editor
- /// </summary>
- [Browsable(true)]
- [DisplayName("Height")]
- public float HeightProperty
- {
- get
- {
- return Height;
- }
- set
- {
- Height = value;
- }
- }
- #endregion
-
- #region WidthHalf (Public)
- /// <summary>
- /// The half width of the size.
- /// </summary>
- [Browsable(false)]
- public float WidthHalf
- {
- get
- {
- return Width * 0.5f;
- }
- }
- #endregion
-
- #region HeightHalf (Public)
- /// <summary>
- /// The half height of the size.
- /// </summary>
- [Browsable(false)]
- public float HeightHalf
- {
- get
- {
- return Height * 0.5f;
- }
- }
- #endregion
-
- #region IsZero (Public)
- /// <summary>
- /// Returns "true" if the size hasn't any width AND height.
- /// </summary>
- [Browsable(false)]
- public bool IsZero
- {
- get
- {
- return Width == 0.0f && Height == 0.0f;
- }
- }
- #endregion
-
- #region Constructors
- /// <summary>
- /// Create size
- /// </summary>
- /// <param name="setDimension">setDimension</param>
- public Size(float setDimension)
- : this(setDimension, setDimension)
- {
- }
-
- /// <summary>
- /// Create size
- /// </summary>
- /// <param name="setHeight">setHeight</param>
- /// <param name="setWidth">setWidht</param>
- public Size(float setWidth, float setHeight)
- : this()
- {
- Width = setWidth;
- Height = setHeight;
- }
-
- /// <summary>
- /// Create size
- /// </summary>
- /// <param name="dataReader">Data reader</param>
- public Size(BinaryReader dataReader)
- : this()
- {
- Load(dataReader);
- }
- #endregion
-
- #region IEquatable<Size> Members
- /// <summary>
- /// Check if another size has almost the same values (using
- /// MathHelper.Epsilon).
- /// </summary>
- /// <param name="other">Other size to compare to</param>
- /// <returns>True if the other size has nearly the same values</returns>
- public bool Equals(Size other)
- {
- return
- // Allow a difference of the Epsilon (in both directions)
- // for the width value range
- Width - MathHelper.Epsilon <= other.Width &&
- Width + MathHelper.Epsilon >= other.Width &&
- // and height value range
- Height - MathHelper.Epsilon <= other.Height &&
- Height + MathHelper.Epsilon >= other.Height;
- }
- #endregion
-
- #region ISaveLoadBinary Members
- /// <summary>
- /// Load size from binary stream (8 bytes, 2 floats)
- /// </summary>
- /// <param name="reader">reader</param>
- public void Load(BinaryReader reader)
- {
- Width = reader.ReadSingle();
- Height = reader.ReadSingle();
- }
-
- /// <summary>
- /// Save size to binary stream (8 bytes, 2 floats)
- /// </summary>
- /// <param name="writer">writer</param>
- public void Save(BinaryWriter writer)
- {
- writer.Write(Width);
- writer.Write(Height);
- }
- #endregion
-
- #region op_Equality (Operator)
- /// <summary>
- /// Operator for equality
- /// </summary>
- /// <param name="value1">Size 1</param>
- /// <param name="value2">Size 2</param>
- /// <returns>True if both sizes are the same</returns>
- public static bool operator ==(Size value1, Size value2)
- {
- return value1.Width == value2.Width &&
- value1.Height == value2.Height;
- }
- #endregion
-
- #region op_Inequality (Operator)
- /// <summary>
- /// Operator for inequality
- /// </summary>
- /// <param name="value1">Size 1</param>
- /// <param name="value2">Size 2</param>
- /// <returns>True if both sizes are not the same</returns>
- public static bool operator !=(Size value1, Size value2)
- {
- return value1.Width != value2.Width ||
- value1.Height != value2.Height;
- }
- #endregion
-
- #region op_Addition (Operator)
- /// <summary>
- /// Operator for addition
- /// </summary>
- /// <param name="value1">Size 1</param>
- /// <param name="value2">Size 2</param>
- /// <returns>Added size from both sizes</returns>
- public static Size operator +(Size value1, Size value2)
- {
- return new Size(value1.Width + value2.Width,
- value1.Height + value2.Height);
- }
- #endregion
-
- #region op_Subtraction (Operator)
- /// <summary>
- /// Operator for subtraction
- /// </summary>
- /// <param name="value1">Size 1</param>
- /// <param name="value2">Size 2</param>
- /// <returns>Subtracted size from both sizes</returns>
- public static Size operator -(Size value1, Size value2)
- {
- return new Size(value1.Width - value2.Width,
- value1.Height - value2.Height);
- }
- #endregion
-
- #region op_Multiply (Operator)
- /// <summary>
- /// Operator for multiplication points
- /// </summary>
- /// <param name="value1">Size 1</param>
- /// <param name="value2">Size 2</param>
- /// <returns>
- /// Size with both Width multiplied and both Heights multiplied.
- /// </returns>
- public static Size operator *(Size value1, Size value2)
- {
- return new Size(value1.Width * value2.Width,
- value1.Height * value2.Height);
- }
-
- /// <summary>
- /// Operator for multiplying a Size with a scale factor.
- /// </summary>
- /// <param name="value">Size value</param>
- /// <param name="scaleFactor">Scale Factor</param>
- /// <returns>
- /// Size with both Width and Height multiplied by scaleFactor
- /// </returns>
- public static Size operator *(Size value, float scaleFactor)
- {
- return new Size(value.Width * scaleFactor, value.Height * scaleFactor);
- }
-
- /// <summary>
- /// Operator for multiplying a Size with a scale factor.
- /// </summary>
- /// <param name="scaleFactor">Scale Factor</param>
- /// <param name="value">Size value</param>
- /// <returns>
- /// Size with both Width and Height multiplied by scaleFactor
- /// </returns>
- public static Size operator *(float scaleFactor, Size value)
- {
- return new Size(value.Width * scaleFactor, value.Height * scaleFactor);
- }
- #endregion
-
- #region op_Division (Operator)
- /// <summary>
- /// Operator to divide a Size with a float.
- /// </summary>
- /// <param name="value">Value</param>
- /// <param name="divisor">Divisor</param>
- /// <returns>value.Width / divisor, value.Height / divisor</returns>
- public static Size operator /(Size value, float divisor)
- {
- return new Size(value.Width / divisor, value.Height / divisor);
- }
-
- /// <summary>
- /// Operator to divide a float with a Size.
- /// </summary>
- /// <param name="divisor">Divisor</param>
- /// <param name="value">Value</param>
- /// <returns>value / divisor.Width, value / divisor.Height</returns>
- public static Size operator /(float value, Size divisor)
- {
- return new Size(value / divisor.Width, value / divisor.Height);
- }
-
- /// <summary>
- /// Operator to divide a Size with another Size.
- /// </summary>
- /// <param name="divisor">Divisor</param>
- /// <param name="value">Value</param>
- /// <returns>value.Width / divisor.Width, value.Height / divisor.Height
- /// </returns>
- public static Size operator /(Size value, Size divisor)
- {
- return new Size(value.Width / divisor.Width,
- value.Height / divisor.Height);
- }
- #endregion
-
- #region op_Explicit (Operator)
- /// <summary>
- /// Operator to implicit convert Point to Size.
- /// </summary>
- /// <param name="anyPoint">Any point value</param>
- /// <returns>Point converted to size</returns>
- public static explicit operator Size(Point anyPoint)
- {
- return new Size(anyPoint.X, anyPoint.Y);
- }
- #endregion
-
- #region Equals (Public)
- /// <summary>
- /// Check in another object is a Size and equal to this size.
- /// </summary>
- /// <param name="obj">Object to compare to</param>
- /// <returns>True if both sizes are the same</returns>
- public override bool Equals(object obj)
- {
- return (obj is Size)
- ? Equals((Size)obj)
- : base.Equals(obj);
- }
- #endregion
-
- #region GetHashCode (Public)
- /// <summary>
- /// Get hash code
- /// </summary>
- /// <returns>Hash code from Width and Height</returns>
- public override int GetHashCode()
- {
- return Width.GetHashCode() + Height.GetHashCode();
- }
- #endregion
-
- #region Round (Public)
- /// <summary>
- /// Round the width and height values to the nearest integer value.
- /// </summary>
- /// <returns>Size with rounded width and height values.</returns>
- public Size Round()
- {
- return new Size(MathHelper.Round(Width), MathHelper.Round(Height));
- }
- #endregion
-
- #region ToString (Public)
- /// <summary>
- /// To string
- /// </summary>
- /// <returns>string</returns>
- public override string ToString()
- {
- return "(" + Width.ToInvariantString() + ", " +
- Height.ToInvariantString() + ")";
- }
- #endregion
-
- #region ToCommaString (Public)
- /// <summary>
- /// Returns the vector as a string that can be used in a Setting files,
- /// which is just using the x, y format (and works fine with FromString).
- /// </summary>
- /// <returns>vector</returns>
- public string ToCommaString()
- {
- return Width.ToInvariantString() + ", " +
- Height.ToInvariantString();
- }
- #endregion
-
- /// <summary>
- /// Tests
- /// </summary>
- internal class SizeTests
- {
- #region SizeOf
- /// <summary>
- /// Checks if the size of Point is exactly 8 bytes (2 floats: X and Y)
- /// </summary>
- [Test]
- public void SizeOf()
- {
- // Size consists of 2 floats: Width and Height
- Assert.Equal(2 * 4, Marshal.SizeOf(typeof(Size)));
- }
- #endregion
-
- #region SizeTest
- /// <summary>
- /// Size test
- /// </summary>
- [Test]
- public void SizeTest()
- {
- Size Size1 = new Size(2, 2);
- Size Size2 = new Size(1, 1);
-
- Assert.Equal(Size2.Width, Size1.WidthHalf);
- Assert.Equal(Size2.Height, Size1.HeightHalf);
- }
- #endregion
-
- #region Equality
- /// <summary>
- /// Equality
- /// </summary>
- [Test]
- public void Equality()
- {
- Assert.Equal(new Size(10, 10), new Size(10));
- Assert.NotEqual(new Size(10, 5), new Size(10f, 10f));
- }
- #endregion
-
- #region Addition
- /// <summary>
- /// Addition
- /// </summary>
- [Test]
- public void Addition()
- {
- Assert.Equal(new Size(9, 8.8f), new Size(5, 3) + new Size(4, 5.8f));
- Assert.Equal(new Size(-1, -2.8f),
- new Size(-5, 3) + new Size(4, -5.8f));
- }
- #endregion
-
- #region Substraction
- /// <summary>
- /// Substraction
- /// </summary>
- [Test]
- public void Substraction()
- {
- Assert.Equal(new Size(1, -2.8f), new Size(5, 3) - new Size(4, 5.8f));
- Assert.Equal(new Size(-9, 8.8f), new Size(-5, 3) - new Size(4, -5.8f));
- }
- #endregion
-
- #region Multiplication
- /// <summary>
- /// Multiplication
- /// </summary>
- [Test]
- public void Multiplication()
- {
- // with a scale factor
- Assert.Equal(new Size(10, 20), new Size(2, 4) * 5);
- Assert.Equal(new Size(-1, -2), new Size(2, 4) * -0.5f);
- Assert.Equal(new Size(0.5f, 1), 0.25f * new Size(2, 4));
- }
- #endregion
-
- #region NearlyEquals
- /// <summary>
- /// Nearly equals
- /// </summary>
- [Test]
- public void NearlyEquals()
- {
- Size testSize = new Size(2, 3);
-
- // Check the size directly
- Assert.True(testSize.Equals(testSize),
- "testSize is not equal testSize");
- // by the "object" overload from .NET
- Assert.True(testSize.Equals((object)testSize),
- "testSize is not equal testSize");
-
- // and the nearly equal check
- Assert.True(testSize.Equals(
- new Size(2 + MathHelper.Epsilon, 3 - MathHelper.Epsilon)),
- "Size.NearlyEquals() chaecks a too small epsilon");
-
- // Finally check the "bad" false cases with unequal values
- Assert.False(testSize.Equals(new Size(4, 3)),
- new Size(4, 3) + " shouldn't be (nearly) equal to " + testSize);
- // and a too big epsilon
- Assert.False(testSize.Equals(
- new Size(2 + (2 * MathHelper.Epsilon), 3)),
- "Size.NearlyEquals() checks a too big epsilon");
- }
- #endregion
-
- #region ToString
- /// <summary>
- /// To string
- /// </summary>
- [Test]
- public new void ToString()
- {
- Assert.Equal("(5, 3.5)", new Size(5, 3.5f).ToString());
- }
- #endregion
- }
- }
- }