/Utilities/Datatypes/Point.cs
C# | 1261 lines | 684 code | 103 blank | 474 comment | 16 complexity | ba0c09e8e31902208f30d2c918054ebc 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>
- /// Point class, can also be used as a Vector2 (because we got floats here).
- /// </summary>
- [Serializable]
- [StructLayout(LayoutKind.Explicit)]
- [DebuggerDisplay("Point=({X}, {Y})")]
- [Description("Expand to edit this Point")]
- [TypeConverter(typeof(ExpandableObjectConverter))]
- public struct Point : ISaveLoadBinary, IEquatable<Point>
- {
- #region Constants
- /// <summary>
- /// Represents the size in bytes of each Point (2 * 4 = 8 bytes).
- /// </summary>
- public const int DataSize = 2 * 4;
-
- /// <summary>
- /// Invalid point, usually used to indicate invalid cached points.
- /// </summary>
- public static readonly Point Invalid = new Point(-1, -1);
-
- /// <summary>
- /// Returns the zero point (0, 0).
- /// </summary>
- public static readonly Point Zero = new Point(0);
-
- /// <summary>
- /// Returns the point (0.5, 0.5).
- /// </summary>
- public static readonly Point Half = new Point(0.5f);
-
- /// <summary>
- /// Returns the point (1, 1).
- /// </summary>
- public static readonly Point One = new Point(1);
-
- /// <summary>
- /// Unit x vector, returns the point (1, 0)
- /// </summary>
- public static readonly Point UnitX = new Point(1, 0);
-
- /// <summary>
- /// Unit y vector, returns the point (0, 1)
- /// </summary>
- public static readonly Point UnitY = new Point(0, 1);
- #endregion
-
- #region Normalize (Static)
- /// <summary>
- /// Normalize
- /// </summary>
- /// <param name="anyPoint">anyPoint</param>
- /// <returns>Normalized point</returns>
- public static Point Normalize(Point anyPoint)
- {
- float lengthSquared = anyPoint.LengthSquared;
- if (lengthSquared == 0)
- {
- // and return the original unchanged value
- return anyPoint;
- } // if
- float invLength = 1f / MathHelper.Sqrt(lengthSquared);
- return new Point(anyPoint.X * invLength, anyPoint.Y * invLength);
- }
- #endregion
-
- #region Dot (Static)
- /// <summary>
- /// Return a scale as result of the dot product from two points
- /// </summary>
- /// <param name="value1">Point 1</param>
- /// <param name="value2">Point 2</param>
- /// <returns>Result of the dot product</returns>
- public static float Dot(Point value1, Point value2)
- {
- return value1.X * value2.X + value1.Y * value2.Y;
- }
-
- /// <summary>
- /// Return a scale as result of the do product from two points
- /// </summary>
- /// <param name="value1">Point 1</param>
- /// <param name="value2">Point 2</param>
- /// <param name="result">Result of the dot product</param>
- public static void Dot(ref Point value1, ref Point value2,
- out float result)
- {
- result = value1.X * value2.X + value1.Y * value2.Y;
- }
- #endregion
-
- #region Distance (Static)
- /// <summary>
- /// Calculate the distance from two points resulting in scale value
- /// </summary>
- /// <param name="value1">Point 1</param>
- /// <param name="value2">Point 2</param>
- /// <returns>Distance between points</returns>
- public static float Distance(Point value1, Point value2)
- {
- float result;
- DistanceSquared(ref value1, ref value2, out result);
- return MathHelper.Sqrt(result);
- }
-
- /// <summary>
- /// Calculate the distance from two points resulting in scale value
- /// </summary>
- /// <param name="value1">Point 1</param>
- /// <param name="value2">Point 2</param>
- /// <param name="result">Distance between points</param>
- public static void Distance(ref Point value1, ref Point value2,
- out float result)
- {
- DistanceSquared(ref value1, ref value2, out result);
- result = MathHelper.Sqrt(result);
- }
- #endregion
-
- #region DistanceSquared (Static)
- /// <summary>
- /// Calculate the squared distance from two points. This is faster than
- /// using Distance because we don't need to take the square root.
- /// </summary>
- /// <param name="value1">Point 1</param>
- /// <param name="value2">Point 2</param>
- /// <returns>Squared distance between points</returns>
- public static float DistanceSquared(Point value1, Point value2)
- {
- float result;
- DistanceSquared(ref value1, ref value2, out result);
- return result;
- }
-
- /// <summary>
- /// Calculate the squared distance from two points resulting in scale value
- /// </summary>
- /// <param name="value1">Point 1</param>
- /// <param name="value2">Point 2</param>
- /// <param name="result">Squared distance between points</param>
- public static void DistanceSquared(ref Point value1, ref Point value2,
- out float result)
- {
- result =
- (value1.X - value2.X) * (value1.X - value2.X) +
- (value1.Y - value2.Y) * (value1.Y - value2.Y);
- }
- #endregion
-
- #region Transform (Static)
- /// <summary>
- /// Transform point by multiplying it with a matrix.
- /// Note: This is slower than using the ref version of Transform.
- /// </summary>
- /// <param name="matrix">Matrix for the transformation</param>
- /// <param name="position">Position to transform</param>
- /// <returns>Transformed point resulting from matrix*position</returns>
- public static Point Transform(Point position, Matrix matrix)
- {
- Transform(ref position, ref matrix, out position);
- return position;
- }
-
- /// <summary>
- /// Transform point by multiplying it with a matrix
- /// </summary>
- /// <param name="matrix">Matrix for the transformation</param>
- /// <param name="position">Position to transform</param>
- /// <param name="result">Transformed point resulting from matrix*position
- /// </param>
- public static void Transform(ref Point position, ref Matrix matrix,
- out Point result)
- {
- result = new Point(
- position.X * matrix.M11 + position.Y * matrix.M21 + matrix.M41,
- position.X * matrix.M12 + position.Y * matrix.M22 + matrix.M42);
- }
- #endregion
-
- #region TransformNormal (Static)
- /// <summary>
- /// Transform normal by multiplying it with a matrix.
- /// Note: This is slower than using the ref version of Transform.
- /// </summary>
- /// <param name="normal">Position to transform</param>
- /// <param name="matrix">Matrix for the transformation</param>
- /// <returns>Transformed normal</returns>
- public static Point TransformNormal(Point normal, Matrix matrix)
- {
- TransformNormal(ref normal, ref matrix, out normal);
- return normal;
- }
-
- /// <summary>
- /// Transform normal by multiplying it with a matrix
- /// </summary>
- /// <param name="normal">Position to transform</param>
- /// <param name="matrix">Matrix for the transformation</param>
- /// <param name="result">Transformed normal</param>
- public static void TransformNormal(ref Point normal, ref Matrix matrix,
- out Point result)
- {
- result = new Point(
- (normal.X * matrix.M11) + (normal.Y * matrix.M21),
- (normal.X * matrix.M12) + (normal.Y * matrix.M22));
- }
- #endregion
-
- #region ComputeNormal (Static)
- /// <summary>
- /// Computes the normal of the given 2D vector. For more information see:
- /// http://www.sciface.com/education/data/web/Beschreibung-von-Geraden.html
- /// http://www.matheboard.de/archive/23062/thread.html
- /// </summary>
- /// <param name="vector2D">2D Vector</param>
- /// <returns>Normal, which is just (-Y, X)</returns>
- public static Point ComputeNormal(Point vector2D)
- {
- return new Point(-vector2D.Y, vector2D.X);
- }
- #endregion
-
- #region Lerp (Static)
- /// <summary>
- /// Performs a linear interpolation between two points.
- /// </summary>
- /// <param name="amount">Amount to lerp</param>
- /// <param name="value1">Point 1</param>
- /// <param name="value2">Point 2</param>
- /// <returns>Interpolated point between both points</returns>
- public static Point Lerp(Point value1, Point value2, float amount)
- {
- return new Point(
- MathHelper.Lerp(value1.X, value2.X, amount),
- MathHelper.Lerp(value1.Y, value2.Y, amount));
- }
-
- /// <summary>
- /// Performs a linear interpolation between two points.
- /// </summary>
- /// <param name="amount">Amount to lerp</param>
- /// <param name="value1">Point 1</param>
- /// <param name="value2">Point 2</param>
- /// <param name="result">Interpolated point between both points</param>
- public static void Lerp(ref Point value1, ref Point value2, float amount,
- out Point result)
- {
- result = new Point(
- MathHelper.Lerp(value1.X, value2.X, amount),
- MathHelper.Lerp(value1.Y, value2.Y, amount));
- }
- #endregion
-
- #region Min (Static)
- /// <summary>
- /// Returns the minimum of both points (X and Y are handled seperately).
- /// -> A(2,4), B(4,1) => Min(2,1)
- /// </summary>
- /// <param name="value1">Point 1</param>
- /// <param name="value2">Point 2</param>
- /// <returns>Minimum value of X and Y</returns>
- public static Point Min(Point value1, Point value2)
- {
- return new Point(MathHelper.Min(value1.X, value2.X),
- MathHelper.Min(value1.Y, value2.Y));
- }
- #endregion
-
- #region Max (Static)
- /// <summary>
- /// Returns the maximum of both points (X and Y are handled seperately).
- /// -> A(2,4), B(4,1) => Max(4,4)
- /// </summary>
- /// <param name="value1">Point 1</param>
- /// <param name="value2">Point 2</param>
- /// <returns>Maximum value of X and Y</returns>
- public static Point Max(Point value1, Point value2)
- {
- return new Point(MathHelper.Max(value1.X, value2.X),
- MathHelper.Max(value1.Y, value2.Y));
- }
- #endregion
-
- #region Clamp (Static)
- /// <summary>
- /// Clamp
- /// </summary>
- /// <param name="value">Value</param>
- /// <param name="min">Minimum</param>
- /// <param name="max">Maximum</param>
- public static Point Clamp(Point value, Point min, Point max)
- {
- return new Point(
- MathHelper.Clamp(value.X, min.X, max.X),
- MathHelper.Clamp(value.Y, min.Y, max.Y));
- }
- #endregion
-
- #region FromString (Static)
- /// <summary>
- /// Convert a string to a Point. The expected format is (x.x, y.y), but
- /// it works fine with ToColladaString or ToCommaString strings too :)
- /// </summary>
- /// <param name="pointString">The string in the correct format
- /// (with or without brackets, comma or space seperated).</param>
- /// <returns>
- /// Point from the given string or Zero if parsing failed.
- /// </returns>
- public static Point FromString(string pointString)
- {
- // Remove the brackets and split the string up into seperate values
- pointString = pointString.Replace("(", "");
- pointString = pointString.Replace(")", "");
- pointString = pointString.Replace("{", "");
- pointString = pointString.Replace("}", "");
- string[] pointStrings = pointString.Split(new[]
- {
- ',', ' '
- },
- StringSplitOptions.RemoveEmptyEntries);
-
- // Then check if the length is 2 for 2 values and return the new point.
- // If the length is not 2 than return Point.Zero.
- if (pointStrings.Length == 2)
- {
- return new Point(
- pointStrings[0].FromInvariantString(0.0f),
- pointStrings[1].FromInvariantString(0.0f));
- }
-
- return Zero;
- }
- #endregion
-
- #region IsPointInside (Static)
- /// <summary>
- /// Is the given point inside the rectangle?
- /// </summary>
- /// <param name="point">Point to check</param>
- /// <param name="rect">Rectangle to check against</param>
- /// <returns>True if the point is inside the rectangle.</returns>
- public static bool IsPointInside(ref Point point, ref Rectangle rect)
- {
- return point.X >= rect.Left &&
- point.X <= rect.Right &&
- point.Y >= rect.Top &&
- point.Y <= rect.Bottom;
- }
- #endregion
-
- #region IsCircleInside (Static)
- /// <summary>
- /// Check if a circle is inside a rectangle or not.
- /// </summary>
- /// <param name="centerPoint">The center point of the circle.</param>
- /// <param name="radius">The radius of the cirlce.</param>
- /// <param name="rect">The rectangle to check if the circle lays in.</param>
- /// <returns>
- /// True if the circle is in the rectangle, otherwise False.
- /// </returns>
- public static bool IsCircleInside(ref Point centerPoint, float radius,
- ref Rectangle rect)
- {
- return centerPoint.X + radius >= rect.Left &&
- centerPoint.X - radius <= rect.Right &&
- centerPoint.Y + radius >= rect.Top &&
- centerPoint.Y - radius <= rect.Bottom;
- }
- #endregion
-
- #region GetRandomPoint (Static)
- /// <summary>
- /// Get random point with x and y between 0 and 1.
- /// </summary>
- /// <returns>Random point with x and y between 0 and 1</returns>
- public static Point GetRandomPoint()
- {
- return GetRandomPoint(0, 1);
- }
-
- /// <summary>
- /// Get random point with x and y between minValue and maxValue.
- /// </summary>
- /// <param name="maxValue">Maximum value</param>
- /// <param name="minValue">Mininum value</param>
- /// <returns>
- /// Random point with x and y between minValue and maxValue.
- /// </returns>
- public static Point GetRandomPoint(float minValue, float maxValue)
- {
- return new Point(
- RandomHelper.RandomFloat(minValue, maxValue),
- RandomHelper.RandomFloat(minValue, maxValue));
- }
- #endregion
-
- #region GetUnitCirclePosition (Static)
- /// <summary>
- /// Returns the represented position by the given degree on the unit circle
- /// (-> degree of: 0/360 = top, 90 = right, 180 = bottom, 270 = left)
- /// </summary>
- /// <param name="degreeValue">Degree value</param>
- /// <returns>Unit vector pointing in the given direction.</returns>
- public static Point GetUnitCirclePosition(float degreeValue)
- {
- return new Point(MathHelper.Sin(degreeValue),
- -MathHelper.Cos(degreeValue));
- }
- #endregion
-
- #region GetRotation (Static)
- /// <summary>
- /// This will convert the given unit-circle position in an unit-circle
- /// rotation (-> top = 0/360, right = 90, bottom = 180, left = 270 degree).
- /// If the given position isn't normalized yet, then just set the second
- /// parameter to 'true'.
- /// <para />
- /// Note: When unitCirclePosition is Point.Zero, 0 is returned.
- /// </summary>
- /// <param name="unitCirclePosition">Unit circle position</param>
- /// <param name="isNormalizationNeeded">Is normalization needed</param>
- /// <returns>The unit circle rotation (top=0, right=90, etc.)</returns>
- public static float GetRotation(Point unitCirclePosition,
- bool isNormalizationNeeded)
- {
- #region Validation
- if (unitCirclePosition == Zero)
- {
- return 0;
- }
- #endregion
-
- if (isNormalizationNeeded)
- {
- unitCirclePosition = Normalize(unitCirclePosition);
- }
-
- // Will return a value between 0 until 180 and -179 until -1
- float rawAngle = MathHelper.Atan(unitCirclePosition.X,
- -unitCirclePosition.Y);
- // so we have to convert from 0 until 180 and 181 until 359
- return (rawAngle < 0.0f)
- ? 360.0f + rawAngle
- : rawAngle;
- }
-
- /// <summary>
- /// This will convert the given unit-circle position in an unit-circle
- /// rotation (-> top = 0/360, right = 90, bottom = 180, left = 270 degree).
- /// The given position will be normalized automatically, if that isn't
- /// wished or needed, then just use the other overload with 'false' at the
- /// second parameter.
- /// </summary>
- /// <param name="unitCirclePosition">Unit circle position</param>
- /// <returns>Point</returns>
- public static float GetRotation(Point unitCirclePosition)
- {
- return GetRotation(unitCirclePosition, true);
- }
- #endregion
-
- #region AngleBetweenPoints (Static)
- /// <summary>
- /// Angle between points in degrees, pretty much the same as
- /// AngleBetweenVectors, just in 2D:
- /// http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm
- /// RadiansToDegrees(atan2(a.y,a.x) - atan2(b.y,b.x)) would only give
- /// you 0-180 degrees, but we want full 0-360 degrees with Acos :)
- /// <para />
- /// Note: If one of the points is zero the method we will return 0.0f.
- /// </summary>
- /// <param name="a">First vector.</param>
- /// <param name="b">Second vector.</param>
- /// <returns>Angle between the two vectors in the range [0, 360]</returns>
- public static float AngleBetweenPoints(Point a, Point b)
- {
- #region Validation
- // Having a single zero vector in this method will cause the calculation
- // to return 90 degrees which is not right. So we simply return 0f.
- if (a == Zero ||
- b == Zero)
- {
- return 0f;
- }
- #endregion
-
- // We need to normalize the vectors so we get the cos from 0 to 1
- // the cos is the dot product of the vectors a and b
- float cos = Dot(Normalize(a), Normalize(b));
- cos = MathHelper.Clamp(cos, -1.0f, 1.0f);
-
- // Note: Special way for 2D vector handling (instead of Vector.Cross)
- float cross = MathHelper.Atan(a.Y, a.X) - MathHelper.Atan(b.Y, b.X);
- return cross < 0.0f
- ? // cross products directory is upwards
- 360 - MathHelper.Acos(cos)
- : // else
- MathHelper.Acos(cos);
- }
- #endregion
-
- #region Framework Union Defines (Public)
- #endregion
-
- #region X (Public)
- /// <summary>
- /// X coordinate.
- /// </summary>
- [FieldOffset(0)]
- public float X;
- #endregion
-
- #region Y (Public)
- /// <summary>
- /// Y coordinate.
- /// </summary>
- [FieldOffset(4)]
- public float Y;
- #endregion
-
- #region XProperty (Public)
- /// <summary>
- /// Property-wrapper for using the X field in the editor.
- /// </summary>
- [Browsable(true)]
- [DisplayName("X")]
- public float XProperty
- {
- get
- {
- return X;
- }
- set
- {
- X = value;
- }
- }
- #endregion
-
- #region YProperty (Public)
- /// <summary>
- /// Property-wrapper for using the Y field in the editor
- /// </summary>
- [Browsable(true)]
- [DisplayName("Y")]
- public float YProperty
- {
- get
- {
- return Y;
- }
- set
- {
- Y = value;
- }
- }
- #endregion
-
- #region Length (Public)
- /// <summary>
- /// Length
- /// </summary>
- [Browsable(false)]
- public float Length
- {
- get
- {
- return MathHelper.Sqrt(X * X + Y * Y);
- }
- }
- #endregion
-
- #region LengthSquared (Public)
- /// <summary>
- /// Length squared
- /// </summary>
- [Browsable(false)]
- public float LengthSquared
- {
- get
- {
- return X * X + Y * Y;
- }
- }
- #endregion
-
- #region Constructors
- /// <summary>
- /// Creates a 2D point
- /// </summary>
- /// <param name="setValue">setValue</param>
- public Point(float setValue)
- : this(setValue, setValue)
- {
- }
-
- /// <summary>
- /// Creates a 2D point
- /// </summary>
- /// <param name="setX">setX</param>
- /// <param name="setY">setY</param>
- public Point(float setX, float setY)
- : this()
- {
- X = setX;
- Y = setY;
- }
-
- /// <summary>
- /// Creates a 2D point from a Vector (ignores Z)
- /// </summary>
- /// <param name="fromVector">fromVector</param>
- public Point(Vector fromVector)
- : this()
- {
- X = fromVector.X;
- Y = fromVector.Y;
- }
-
- /// <summary>
- /// Create point
- /// </summary>
- /// <param name="reader">reader</param>
- public Point(BinaryReader reader)
- : this()
- {
- Load(reader);
- }
- #endregion
-
- #region IEquatable<Point> Members
- /// <summary>
- /// Equals check will check if the other point is nearly equals (using
- /// the MathHelper.Epsilon value, which is close to zero).
- /// </summary>
- /// <param name="other">Other point to check against</param>
- /// <returns>
- /// True if both points are almost equal, false if they are apart.
- /// </returns>
- public bool Equals(Point other)
- {
- return
- // Allow a difference of the Epsilon (in both directions)
- // for the X value range
- X - MathHelper.Epsilon <= other.X &&
- X + MathHelper.Epsilon >= other.X &&
- // and Y value range
- Y - MathHelper.Epsilon <= other.Y &&
- Y + MathHelper.Epsilon >= other.Y;
- }
- #endregion
-
- #region ISaveLoadBinary Members
- /// <summary>
- /// Load the point values from a stream.
- /// </summary>
- /// <param name="reader">The stream that will be used.</param>
- public void Load(BinaryReader reader)
- {
- X = reader.ReadSingle();
- Y = reader.ReadSingle();
- }
-
- /// <summary>
- /// Saves the point to a stream.
- /// </summary>
- /// <param name="writer">The stream that will be used.</param>
- public void Save(BinaryWriter writer)
- {
- writer.Write(X);
- writer.Write(Y);
- }
- #endregion
-
- #region op_UnaryNegation (Operator)
- /// <summary>
- /// Operator for negation
- /// </summary>
- /// <param name="value">Value to subtract</param>
- /// <returns>
- /// The returned point is the negative version of the input point.
- /// </returns>
- public static Point operator -(Point value)
- {
- return new Point(-value.X, -value.Y);
- }
- #endregion
-
- #region op_Equality (Operator)
- /// <summary>
- /// Operator for equality
- /// </summary>
- /// <param name="value1">value1</param>
- /// <param name="value2">value2</param>
- /// <returns>Returns True if the points are equal, otherwise False.
- /// </returns>
- public static bool operator ==(Point value1, Point value2)
- {
- return value1.X == value2.X && value1.Y == value2.Y;
- }
- #endregion
-
- #region op_Inequality (Operator)
- /// <summary>
- /// Operator for inequality
- /// </summary>
- /// <param name="value1">value1</param>
- /// <param name="value2">value2</param>
- /// <returns>Returns True if the points are unequal, otherwise False.
- /// </returns>
- public static bool operator !=(Point value1, Point value2)
- {
- return value1.X != value2.X || value1.Y != value2.Y;
- }
- #endregion
-
- #region op_Addition (Operator)
- /// <summary>
- /// Operator for addition
- /// </summary>
- /// <param name="value1">value1</param>
- /// <param name="value2">value2</param>
- /// <returns>Returns a point as the result of the addition.</returns>
- public static Point operator +(Point value1, Point value2)
- {
- return new Point(value1.X + value2.X, value1.Y + value2.Y);
- }
- #endregion
-
- #region op_Subtraction (Operator)
- /// <summary>
- /// Operator for subtraction
- /// </summary>
- /// <param name="value1">Point 1</param>
- /// <param name="value2">Point 2</param>
- /// <returns>Result of the subtraction</returns>
- public static Point operator -(Point value1, Point value2)
- {
- return new Point(value1.X - value2.X, value1.Y - value2.Y);
- }
- #endregion
-
- #region op_Multiply (Operator)
- /// <summary>
- /// Operator for multiplication points
- /// </summary>
- /// <param name="value1">Point 1</param>
- /// <param name="value2">Point 2</param>
- /// <returns>Result of the multiplication</returns>
- public static Point operator *(Point value1, Point value2)
- {
- return new Point(value1.X * value2.X, value1.Y * value2.Y);
- }
-
- /// <summary>
- /// Operator for multiplication a point with a float as scaling
- /// </summary>
- /// <param name="scaleFactor">scaleFactor</param>
- /// <param name="value">value</param>
- /// <returns>Result of the multiplication</returns>
- public static Point operator *(Point value, float scaleFactor)
- {
- return new Point(value.X * scaleFactor, value.Y * scaleFactor);
- }
-
- /// <summary>
- /// Operator for multiplication a float as scaling with a point
- /// </summary>
- /// <param name="scaleFactor">scaleFactor</param>
- /// <param name="value">value</param>
- /// <returns>Result of the multiplication</returns>
- public static Point operator *(float scaleFactor, Point value)
- {
- return new Point(value.X * scaleFactor, value.Y * scaleFactor);
- }
- #endregion
-
- #region op_Division (Operator)
- /// <summary>
- /// Operator to divide a point with a float.
- /// </summary>
- /// <param name="value">Value</param>
- /// <param name="divisor">Divisor</param>
- /// <returns>Result of the division</returns>
- public static Point operator /(Point value, float divisor)
- {
- return new Point(value.X / divisor, value.Y / divisor);
- }
-
- /// <summary>
- /// Operator to divide a point with a float.
- /// </summary>
- /// <param name="divisor">Divisor</param>
- /// <param name="value">Value</param>
- /// <returns>Result of the division</returns>
- public static Point operator /(float divisor, Point value)
- {
- return new Point(divisor / value.X, divisor / value.Y);
- }
- #endregion
-
- #region op_Implicit (Operator)
- /// <summary>
- /// Operator to implicit convert Size to Point.
- /// </summary>
- /// <param name="anySize">Any size</param>
- /// <returns>
- /// Point created from the size (x from width, y from height).
- /// </returns>
- public static implicit operator Point(Size anySize)
- {
- return new Point(anySize.Width, anySize.Height);
- }
- #endregion
-
- #region CloneX (Public)
- /// <summary>
- /// Returns a new Point with the same X value as before and a new Y
- /// changed by the given offset (parameter) value.
- /// </summary>
- /// <param name="setOffsetOfY">The offset to compute the new Y value
- /// for the cloned point.</param>
- /// <returns>New cloned Point instance.</returns>
- public Point CloneX(float setOffsetOfY)
- {
- return new Point(X, Y + setOffsetOfY);
- }
- #endregion
-
- #region CloneY (Public)
- /// <summary>
- /// Returns a new Point with the same Y value as before and a new X
- /// changed by the given offset (parameter) value.
- /// </summary>
- /// <param name="setOffsetOfX">The offset to compute the new X value for
- /// the cloned point.</param>
- /// <returns>New cloned Point instance.</returns>
- public Point CloneY(float setOffsetOfX)
- {
- return new Point(X + setOffsetOfX, Y);
- }
- #endregion
-
- #region Equals (Public)
- /// <summary>
- /// Check if another object is a point and equals to this point.
- /// </summary>
- /// <param name="obj">Object to compare with</param>
- /// <returns>
- /// True if the object is a Point and equal to this point.
- /// </returns>
- public override bool Equals(object obj)
- {
- return (obj is Point)
- ? Equals((Point)obj)
- : base.Equals(obj);
- }
- #endregion
-
- #region NearlyEquals (Public)
- /// <summary>
- /// NearlyEquals
- /// </summary>
- /// <param name="other">Other point we wan't check equality with.</param>
- /// <param name="epsilon">A very small value defining the range in which
- /// the two points can differ to still be nearly equal enough.</param>
- /// <returns>Value indicating the equality of two vectors</returns>
- public bool NearlyEquals(Point other, float epsilon)
- {
- return
- // Allow a difference of the Epsilon (in both directions)
- // for the X value range
- X - epsilon <= other.X &&
- X + epsilon >= other.X &&
- // and Y value range
- Y - epsilon <= other.Y &&
- Y + epsilon >= other.Y;
- }
- #endregion
-
- #region GetHashCode (Public)
- /// <summary>
- /// Get hash code
- /// </summary>
- /// <returns>Hash code from X and Y values</returns>
- public override int GetHashCode()
- {
- return X.GetHashCode() ^ Y.GetHashCode();
- }
- #endregion
-
- #region Resize (Public)
- /// <summary>
- /// Resize this point to given length and returns the new Point
- /// </summary>
- /// <param name="length">length</param>
- /// <returns>Resized point</returns>
- public Point Resize(float length)
- {
- return Normalize(this) * length;
- }
- #endregion
-
- #region Rotate (Public)
- /// <summary>
- /// Rotates this instance around the origin, returns the
- /// </summary>
- /// <param name="angle">Angle to rotate in degree</param>
- /// <returns>Rotated point with given direction from angle</returns>
- public Point Rotate(float angle)
- {
- float sine = MathHelper.Sin(-angle);
- float cosine = MathHelper.Cos(-angle);
- float newX = X * cosine + Y * sine;
- Y = -X * sine + Y * cosine;
- X = newX;
- return this;
- }
- #endregion
-
- #region ToString (Public)
- /// <summary>
- /// To string
- /// </summary>
- /// <returns>String with braces, e.g. "(1.0, 2.3)"</returns>
- public override string ToString()
- {
- return ToString("(", ")");
- }
-
- /// <summary>
- /// To string
- /// </summary>
- /// <returns>String with custom braces, e.g. "(1.0, 2.3)"</returns>
- public string ToString(string openBrace, string closeBrace)
- {
- return openBrace + X.ToInvariantString("0.000") +
- ", " + Y.ToInvariantString("0.000") + closeBrace;
- }
- #endregion
-
- /// <summary>
- /// Tests
- /// </summary>
- internal class PointTests
- {
- #region SizeOf
- /// <summary>
- /// Checks if the size of Point is exactly 8 bytes (2 floats: X and Y)
- /// </summary>
- [Test]
- public void SizeOf()
- {
- // Points are 2 floats: X and Y
- Assert.Equal(2 * 4, Marshal.SizeOf(typeof(Point)));
- }
- #endregion
-
- #region DistanceSquared
- /// <summary>
- /// DistanceSquared
- /// </summary>
- [Test]
- public void DistanceSquared()
- {
- Assert.Equal(4, Point.DistanceSquared(new Point(2, 0), Zero));
- }
- #endregion
-
- #region Distance
- /// <summary>
- /// Distance
- /// </summary>
- [Test]
- public void Distance()
- {
- Assert.Equal(2, Point.Distance(new Point(2, 0), Zero));
- }
- #endregion
-
- #region Lerp
- /// <summary>
- /// Lerp
- /// </summary>
- [Test]
- public void Lerp()
- {
- Assert.Equal(Zero, Point.Lerp(new Point(2, 2), Zero, 1));
- }
- #endregion
-
- #region TransformNormal
- /// <summary>
- /// TransformNormal
- /// </summary>
- [Test]
- public void TransformNormal()
- {
- Assert.Equal(One, Point.TransformNormal(One,
- Matrix.Identity));
- }
- #endregion
-
- #region Transform
- /// <summary>
- /// Transform
- /// </summary>
- [Test]
- public void Transform()
- {
- Assert.Equal(One, Point.Transform(One, Matrix.Identity));
- }
- #endregion
-
- #region Dot
- /// <summary>
- /// Dot
- /// </summary>
- [Test]
- public void Dot()
- {
- Assert.Equal(2, Point.Dot(One, One));
- }
- #endregion
-
- #region Length
- /// <summary>
- /// Length
- /// </summary>
- [Test]
- public void Length()
- {
- Assert.Equal(5, new Point(0, 5).Length);
- Assert.Equal(1, new Point(-1, 0).Length);
- Assert.Equal(5, new Point(3, 4).Length);
- }
- #endregion
-
- #region LengthSquared
- /// <summary>
- /// Length squared
- /// </summary>
- [Test]
- public void LengthSquared()
- {
- Assert.Equal(25, new Point(0, 5).LengthSquared);
- Assert.Equal(1, new Point(-1, 0).LengthSquared);
- Assert.Equal(25, new Point(3, 4).LengthSquared);
- }
- #endregion
-
- #region Negation
- /// <summary>
- /// Negation
- /// </summary>
- [Test]
- public void Negation()
- {
- Assert.Equal(new Point(-3, 1), -(new Point(3, -1)));
- }
- #endregion
-
- #region Equality
- /// <summary>
- /// Equality
- /// </summary>
- [Test]
- public void Equality()
- {
- Assert.Equal(new Point(10, 10), new Point(10));
- Assert.NotEqual(new Point(10, 5), new Point(10f, 10f));
- }
- #endregion
-
- #region Addition
- /// <summary>
- /// Addition
- /// </summary>
- [Test]
- public void Addition()
- {
- Assert.Equal(new Point(9, 8.8f), new Point(5, 3) + new Point(4, 5.8f));
- Assert.Equal(new Point(-1, -2.8f),
- new Point(-5, 3) + new Point(4, -5.8f));
- }
- #endregion
-
- #region Substraction
- /// <summary>
- /// Substraction
- /// </summary>
- [Test]
- public void Substraction()
- {
- Assert.Equal(new Point(1, -2.8f), new Point(5, 3) - new Point(4, 5.8f));
- Assert.Equal(new Point(-9, 8.8f),
- new Point(-5, 3) - new Point(4, -5.8f));
- }
- #endregion
-
- #region Multiplication
- /// <summary>
- /// Multiplication
- /// </summary>
- [Test]
- public void Multiplication()
- {
- // with Points
- Assert.Equal(new Point(2, 2), new Point(2, 4) * new Point(1, 0.5f));
- Assert.Equal(new Point(-2, -4), new Point(2, 4) * new Point(-1));
-
- // with a scale factor
- Assert.Equal(new Point(10, 20), new Point(2, 4) * 5);
- Assert.Equal(new Point(-1, -2), new Point(2, 4) * -0.5f);
- Assert.Equal(new Point(0.5f, 1), 0.25f * new Point(2, 4));
- }
- #endregion
-
- #region Division
- /// <summary>
- /// Division
- /// </summary>
- [Test]
- public void Division()
- {
- Assert.Equal(new Point(2, 5), new Point(10, 25) / 5.0f);
- Assert.Equal(new Point(5, 6), new Point(10, 12) / 2);
-
- Assert.Equal(new Point(0.1f, 0.04f), 1.0f / new Point(10, 25));
- }
- #endregion
-
- #region NearlyEquals
- /// <summary>
- /// Nearly equals
- /// </summary>
- [Test]
- public void NearlyEquals()
- {
- Point testPoint = new Point(2, 3);
-
- // Check the point directly
- Assert.True(testPoint.Equals(testPoint), "TODO");
- // by the "object" overload from .NET
- Assert.True(testPoint.Equals((object)testPoint), "TODO");
-
- // and the nearly equal check
- Assert.True(testPoint.Equals(
- new Point(2 + MathHelper.Epsilon, 3 - MathHelper.Epsilon)), "TODO");
-
- // Finally check the "bad" false cases with unequal values
- Assert.False(testPoint.Equals(new Point(4, 3)), "TODO");
- // and a too big epsilon
- Assert.False(testPoint.Equals(
- new Point(2 + (2 * MathHelper.Epsilon), 3)), "TODO");
- }
- #endregion
-
- #region Normalize
- /// <summary>
- /// Normalize
- /// </summary>
- [Test]
- public void Normalize()
- {
- Assert.Equal(UnitX, Point.Normalize(new Point(13, 0)));
- Assert.Equal(-UnitY, Point.Normalize(new Point(0, -7)));
- }
- #endregion
-
- #region Min
- /// <summary>
- /// Minimum
- /// </summary>
- [Test]
- public void Min()
- {
- Assert.Equal(new Point(2, 1),
- Point.Min(new Point(2, 4), new Point(4, 1)));
- }
- #endregion
-
- #region Max
- /// <summary>
- /// Maximum
- /// </summary>
- [Test]
- public void Max()
- {
- Assert.Equal(new Point(4, 4),
- Point.Max(new Point(2, 4), new Point(4, 1)));
- }
- #endregion
-
- #region Rotate
- /// <summary>
- /// Rotate
- /// </summary>
- [Test]
- public void Rotate()
- {
- Point p = new Point(10, 20);
- p.Rotate(90);
- Assert.Equal(new Point(-20, 10), p);
- }
- #endregion
-
- #region GetRotation
- /// <summary>
- /// Get rotation
- /// </summary>
- [Test]
- public void GetRotation()
- {
- Assert.NearlyEqual(Point.GetRotation(new Point(0, -1), false), 0);
- Assert.NearlyEqual(Point.GetRotation(new Point(1, 0), false), 90);
- Assert.NearlyEqual(Point.GetRotation(new Point(0, 1), false), 180);
- Assert.NearlyEqual(Point.GetRotation(new Point(-1, 0), false), 270);
-
- Assert.NearlyEqual(Point.GetRotation(new Point(0, -4)), 0);
- Assert.NearlyEqual(Point.GetRotation(new Point(2, 0)), 90);
- Assert.NearlyEqual(Point.GetRotation(new Point(0, 8)), 180);
- Assert.NearlyEqual(Point.GetRotation(new Point(-5, 0)), 270);
- }
- #endregion
-
- #region ToString
- /// <summary>
- /// To string
- /// </summary>
- [Test]
- public new void ToString()
- {
- Assert.Equal("(5.000, 3.500)", new Point(5, 3.5f).ToString());
- Assert.Equal("(0.000, 0.000)", new Point().ToString());
- Assert.Equal("5.000, 3.500", new Point(5, 3.5f).ToString("", ""));
- Assert.Equal("{5.000, 3.700}", new Point(5, 3.7f).ToString("{", "}"));
-
- // And the other way around
- Assert.Equal(new Point(5, 3.5f), FromString("(5, 3.5)"));
- Assert.Equal(new Point(), FromString("(0, 0)"));
- Assert.Equal(new Point(5, 3.5f), FromString("5 3.5"));
- Assert.Equal(new Point(), FromString("0 0"));
- Assert.Equal(new Point(5, 3.5f), FromString("5, 3.5"));
- Assert.Equal(new Point(5, 3.7f), FromString("{5, 3.7}"));
- }
- #endregion
- }
- }
- }