/* * Copyright (C) 2012-2020 CypherCore * Copyright (C) 2003-2004 Eran Kampf eran@ekampf.com http://www.ekampf.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Runtime.InteropServices; using System.Text.RegularExpressions; namespace Framework.GameMath { /// /// Represents 2-Dimentional vector of single-precision floating point numbers. /// [Serializable] [StructLayout(LayoutKind.Sequential)] [TypeConverter(typeof(Vector2FConverter))] public struct Vector2 : ICloneable { #region Private fields private float _x; private float _y; #endregion #region Constructors /// /// Initializes a new instance of the class with the specified coordinates. /// /// The vector's X coordinate. /// The vector's Y coordinate. public Vector2(float x, float y) { _x = x; _y = y; } /// /// Initializes a new instance of the class with the specified coordinates. /// /// An array containing the coordinate parameters. public Vector2(float[] coordinates) { Debug.Assert(coordinates != null); Debug.Assert(coordinates.Length >= 2); _x = coordinates[0]; _y = coordinates[1]; } /// /// Initializes a new instance of the class with the specified coordinates. /// /// An array containing the coordinate parameters. public Vector2(List coordinates) { Debug.Assert(coordinates != null); Debug.Assert(coordinates.Count >= 2); _x = coordinates[0]; _y = coordinates[1]; } /// /// Initializes a new instance of the class using coordinates from a given instance. /// /// A to get the coordinates from. public Vector2(Vector2 vector) { _x = vector.X; _y = vector.Y; } #endregion #region Constants /// /// 4-Dimentional single-precision floating point zero vector. /// public static readonly Vector2 Zero = new(0.0f, 0.0f); /// /// 4-Dimentional single-precision floating point X-Axis vector. /// public static readonly Vector2 XAxis = new(1.0f, 0.0f); /// /// 4-Dimentional single-precision floating point Y-Axis vector. /// public static readonly Vector2 YAxis = new(0.0f, 1.0f); #endregion #region Public properties /// /// Gets or sets the x-coordinate of this vector. /// /// The x-coordinate of this vector. public float X { get { return _x; } set { _x = value; } } /// /// Gets or sets the y-coordinate of this vector. /// /// The y-coordinate of this vector. public float Y { get { return _y; } set { _y = value; } } #endregion #region ICloneable Members /// /// Creates an exact copy of this object. /// /// The object this method creates, cast as an object. object ICloneable.Clone() { return new Vector2(this); } /// /// Creates an exact copy of this object. /// /// The object this method creates. public Vector2 Clone() { return new Vector2(this); } #endregion #region Public Static Parse Methods /// /// Converts the specified string to its equivalent. /// /// A string representation of a . /// A that represents the vector specified by the parameter. public static Vector2 Parse(string value) { Regex r = new(@"\((?.*),(?.*)\)", RegexOptions.Singleline); Match m = r.Match(value); if (m.Success) { return new Vector2( float.Parse(m.Result("${x}")), float.Parse(m.Result("${y}")) ); } else { throw new Exception("Unsuccessful Match."); } } /// /// Converts the specified string to its equivalent. /// A return value indicates whether the conversion succeeded or failed. /// /// A string representation of a . /// /// When this method returns, if the conversion succeeded, /// contains a representing the vector specified by . /// /// if value was converted successfully; otherwise, . public static bool TryParse(string value, out Vector2 result) { Regex r = new(@"\((?.*),(?.*)\)", RegexOptions.Singleline); Match m = r.Match(value); if (m.Success) { result = new Vector2( float.Parse(m.Result("${x}")), float.Parse(m.Result("${y}")) ); return true; } result = Vector2.Zero; return false; } #endregion #region Public Static Vector Arithmetics /// /// Adds two vectors. /// /// A instance. /// A instance. /// A new instance containing the sum. public static Vector2 Add(Vector2 left, Vector2 right) { return new Vector2(left.X + right.X, left.Y + right.Y); } /// /// Adds a vector and a scalar. /// /// A instance. /// A single-precision floating-point number. /// A new instance containing the sum. public static Vector2 Add(Vector2 vector, float scalar) { return new Vector2(vector.X + scalar, vector.Y + scalar); } /// /// Adds two vectors and put the result in the third vector. /// /// A instance. /// A instance /// A instance to hold the result. public static void Add(Vector2 left, Vector2 right, ref Vector2 result) { result.X = left.X + right.X; result.Y = left.Y + right.Y; } /// /// Adds a vector and a scalar and put the result into another vector. /// /// A instance. /// A single-precision floating-point number. /// A instance to hold the result. public static void Add(Vector2 vector, float scalar, ref Vector2 result) { result.X = vector.X + scalar; result.Y = vector.Y + scalar; } /// /// Subtracts a vector from a vector. /// /// A instance. /// A instance. /// A new instance containing the difference. /// /// result[i] = left[i] - right[i]. /// public static Vector2 Subtract(Vector2 left, Vector2 right) { return new Vector2(left.X - right.X, left.Y - right.Y); } /// /// Subtracts a scalar from a vector. /// /// A instance. /// A single-precision floating-point number. /// A new instance containing the difference. /// /// result[i] = vector[i] - scalar /// public static Vector2 Subtract(Vector2 vector, float scalar) { return new Vector2(vector.X - scalar, vector.Y - scalar); } /// /// Subtracts a vector from a scalar. /// /// A instance. /// A single-precision floating-point number. /// A new instance containing the difference. /// /// result[i] = scalar - vector[i] /// public static Vector2 Subtract(float scalar, Vector2 vector) { return new Vector2(scalar - vector.X, scalar - vector.Y); } /// /// Subtracts a vector from a second vector and puts the result into a third vector. /// /// A instance. /// A instance /// A instance to hold the result. /// /// result[i] = left[i] - right[i]. /// public static void Subtract(Vector2 left, Vector2 right, ref Vector2 result) { result.X = left.X - right.X; result.Y = left.Y - right.Y; } /// /// Subtracts a vector from a scalar and put the result into another vector. /// /// A instance. /// A single-precision floating-point number. /// A instance to hold the result. /// /// result[i] = vector[i] - scalar /// public static void Subtract(Vector2 vector, float scalar, ref Vector2 result) { result.X = vector.X - scalar; result.Y = vector.Y - scalar; } /// /// Subtracts a scalar from a vector and put the result into another vector. /// /// A instance. /// A single-precision floating-point number. /// A instance to hold the result. /// /// result[i] = scalar - vector[i] /// public static void Subtract(float scalar, Vector2 vector, ref Vector2 result) { result.X = scalar - vector.X; result.Y = scalar - vector.Y; } /// /// Divides a vector by another vector. /// /// A instance. /// A instance. /// A new containing the quotient. /// /// result[i] = left[i] / right[i]. /// public static Vector2 Divide(Vector2 left, Vector2 right) { return new Vector2(left.X / right.X, left.Y / right.Y); } /// /// Divides a vector by a scalar. /// /// A instance. /// A scalar /// A new containing the quotient. /// /// result[i] = vector[i] / scalar; /// public static Vector2 Divide(Vector2 vector, float scalar) { return new Vector2(vector.X / scalar, vector.Y / scalar); } /// /// Divides a scalar by a vector. /// /// A instance. /// A scalar /// A new containing the quotient. /// /// result[i] = scalar / vector[i] /// public static Vector2 Divide(float scalar, Vector2 vector) { return new Vector2(scalar / vector.X, scalar / vector.Y); } /// /// Divides a vector by another vector. /// /// A instance. /// A instance. /// A instance to hold the result. /// /// result[i] = left[i] / right[i] /// public static void Divide(Vector2 left, Vector2 right, ref Vector2 result) { result.X = left.X / right.X; result.Y = left.Y / right.Y; } /// /// Divides a vector by a scalar. /// /// A instance. /// A scalar /// A instance to hold the result. /// /// result[i] = vector[i] / scalar /// public static void Divide(Vector2 vector, float scalar, ref Vector2 result) { result.X = vector.X / scalar; result.Y = vector.Y / scalar; } /// /// Divides a scalar by a vector. /// /// A instance. /// A scalar /// A instance to hold the result. /// /// result[i] = scalar / vector[i] /// public static void Divide(float scalar, Vector2 vector, ref Vector2 result) { result.X = scalar / vector.X; result.Y = scalar / vector.Y; } /// /// Multiplies a vector by a scalar. /// /// A instance. /// A single-precision floating-point number. /// A new containing the result. public static Vector2 Multiply(Vector2 vector, float scalar) { return new Vector2(vector.X * scalar, vector.Y * scalar); } /// /// Multiplies a vector by a scalar and put the result in another vector. /// /// A instance. /// A single-precision floating-point number. /// A instance to hold the result. public static void Multiply(Vector2 vector, float scalar, ref Vector2 result) { result.X = vector.X * scalar; result.Y = vector.Y * scalar; } /// /// Calculates the dot product of two vectors. /// /// A instance. /// A instance. /// The dot product value. public static float DotProduct(Vector2 left, Vector2 right) { return (left.X * right.X) + (left.Y * right.Y); } /// /// Calculates the Kross product of two vectors. /// /// A instance. /// A instance. /// The Kross product value. /// ///

/// The Kross product is defined as: /// Kross(u,v) = u.X*v.Y - u.Y*v.X. ///

///

/// The operation is related to the cross product in 3D given by (x0, y0, 0) X (x1, y1, 0) = (0, 0, Kross((x0, y0), (x1, y1))). /// The operation has the property that Kross(u, v) = -Kross(v, u). ///

///
public static float KrossProduct(Vector2 left, Vector2 right) { return (left.X * right.Y) - (left.Y * right.X); } /// /// Negates a vector. /// /// A instance. /// A new instance containing the negated values. public static Vector2 Negate(Vector2 vector) { return new Vector2(-vector.X, -vector.Y); } /// /// Tests whether two vectors are approximately equal using default tolerance value. /// /// A instance. /// A instance. /// if the two vectors are approximately equal; otherwise, . public static bool ApproxEqual(Vector2 left, Vector2 right) { return ApproxEqual(left, right, MathFunctions.Epsilon); } /// /// Tests whether two vectors are approximately equal given a tolerance value. /// /// A instance. /// A instance. /// The tolerance value used to test approximate equality. /// if the two vectors are approximately equal; otherwise, . public static bool ApproxEqual(Vector2 left, Vector2 right, float tolerance) { return ( (System.Math.Abs(left.X - right.X) <= tolerance) && (System.Math.Abs(left.Y - right.Y) <= tolerance) ); } #endregion #region Public Methods /// /// Scale the vector so that its length is 1. /// public void Normalize() { float length = GetLength(); if (length == 0) { throw new DivideByZeroException("Trying to normalize a vector with length of zero."); } _x /= length; _y /= length; } /// /// Calculates the length of the vector. /// /// Returns the length of the vector. (Sqrt(X*X + Y*Y)) public float GetLength() { return (float)System.Math.Sqrt(_x * _x + _y * _y); } /// /// Calculates the squared length of the vector. /// /// Returns the squared length of the vector. (X*X + Y*Y) public float GetLengthSquared() { return (_x * _x + _y * _y); } /// /// Clamps vector values to zero using a given tolerance value. /// /// The tolerance to use. /// /// The vector values that are close to zero within the given tolerance are set to zero. /// public void ClampZero(float tolerance) { _x = MathFunctions.Clamp(_x, 0, tolerance); _y = MathFunctions.Clamp(_y, 0, tolerance); } /// /// Clamps vector values to zero using the default tolerance value. /// /// /// The vector values that are close to zero within the given tolerance are set to zero. /// The tolerance value used is /// public void ClampZero() { _x = MathFunctions.Clamp(_x, 0); _y = MathFunctions.Clamp(_y, 0); } #endregion #region System.Object Overrides /// /// Returns the hashcode for this instance. /// /// A 32-bit signed integer hash code. public override int GetHashCode() { return _x.GetHashCode() ^ _y.GetHashCode(); } /// /// Returns a value indicating whether this instance is equal to /// the specified object. /// /// An object to compare to this instance. /// if is a and has the same values as this instance; otherwise, . public override bool Equals(object obj) { if (obj is Vector2) { Vector2 v = (Vector2)obj; return (_x == v.X) && (_y == v.Y); } return false; } /// /// Returns a string representation of this object. /// /// A string representation of this object. public override string ToString() { return $"({_x}, {_y})"; } #endregion #region Comparison Operators /// /// Tests whether two specified vectors are equal. /// /// A instance. /// A instance. /// if the two vectors are equal; otherwise, . public static bool operator ==(Vector2 left, Vector2 right) { return ValueType.Equals(left, right); } /// /// Tests whether two specified vectors are not equal. /// /// A instance. /// A instance. /// if the two vectors are not equal; otherwise, . public static bool operator !=(Vector2 left, Vector2 right) { return !ValueType.Equals(left, right); } /// /// Tests if a vector's components are greater than another vector's components. /// /// A instance. /// A instance. /// if the left-hand vector's components are greater than the right-hand vector's component; otherwise, . public static bool operator >(Vector2 left, Vector2 right) { return ( (left._x > right._x) && (left._y > right._y)); } /// /// Tests if a vector's components are smaller than another vector's components. /// /// A instance. /// A instance. /// if the left-hand vector's components are smaller than the right-hand vector's component; otherwise, . public static bool operator <(Vector2 left, Vector2 right) { return ( (left._x < right._x) && (left._y < right._y)); } /// /// Tests if a vector's components are greater or equal than another vector's components. /// /// A instance. /// A instance. /// if the left-hand vector's components are greater or equal than the right-hand vector's component; otherwise, . public static bool operator >=(Vector2 left, Vector2 right) { return ( (left._x >= right._x) && (left._y >= right._y)); } /// /// Tests if a vector's components are smaller or equal than another vector's components. /// /// A instance. /// A instance. /// if the left-hand vector's components are smaller or equal than the right-hand vector's component; otherwise, . public static bool operator <=(Vector2 left, Vector2 right) { return ( (left._x <= right._x) && (left._y <= right._y)); } #endregion #region Unary Operators /// /// Negates the values of the given vector. /// /// A instance. /// A new instance containing the negated values. public static Vector2 operator -(Vector2 vector) { return Vector2.Negate(vector); } #endregion #region Binary Operators /// /// Adds two vectors. /// /// A instance. /// A instance. /// A new instance containing the sum. public static Vector2 operator +(Vector2 left, Vector2 right) { return Vector2.Add(left, right); } /// /// Adds a vector and a scalar. /// /// A instance. /// A single-precision floating-point number. /// A new instance containing the sum. public static Vector2 operator +(Vector2 vector, float scalar) { return Vector2.Add(vector, scalar); } /// /// Adds a vector and a scalar. /// /// A instance. /// A single-precision floating-point number. /// A new instance containing the sum. public static Vector2 operator +(float scalar, Vector2 vector) { return Vector2.Add(vector, scalar); } /// /// Subtracts a vector from a vector. /// /// A instance. /// A instance. /// A new instance containing the difference. /// /// result[i] = left[i] - right[i]. /// public static Vector2 operator -(Vector2 left, Vector2 right) { return Vector2.Subtract(left, right); } /// /// Subtracts a scalar from a vector. /// /// A instance. /// A single-precision floating-point number. /// A new instance containing the difference. /// /// result[i] = vector[i] - scalar /// public static Vector2 operator -(Vector2 vector, float scalar) { return Vector2.Subtract(vector, scalar); } /// /// Subtracts a vector from a scalar. /// /// A instance. /// A single-precision floating-point number. /// A new instance containing the difference. /// /// result[i] = scalar - vector[i] /// public static Vector2 operator -(float scalar, Vector2 vector) { return Vector2.Subtract(scalar, vector); } /// /// Multiplies a vector by a scalar. /// /// A instance. /// A single-precision floating-point number. /// A new containing the result. public static Vector2 operator *(Vector2 vector, float scalar) { return Vector2.Multiply(vector, scalar); } /// /// Multiplies a vector by a scalar. /// /// A instance. /// A single-precision floating-point number. /// A new containing the result. public static Vector2 operator *(float scalar, Vector2 vector) { return Vector2.Multiply(vector, scalar); } /// /// Divides a vector by a scalar. /// /// A instance. /// A scalar /// A new containing the quotient. /// /// result[i] = vector[i] / scalar; /// public static Vector2 operator /(Vector2 vector, float scalar) { return Vector2.Divide(vector, scalar); } /// /// Divides a scalar by a vector. /// /// A instance. /// A scalar /// A new containing the quotient. /// /// result[i] = scalar / vector[i] /// public static Vector2 operator /(float scalar, Vector2 vector) { return Vector2.Divide(scalar, vector); } #endregion #region Array Indexing Operator /// /// Indexer ( [x, y] ). /// public float this[int index] { get { switch (index) { case 0: return _x; case 1: return _y; default: throw new IndexOutOfRangeException(); } } set { switch (index) { case 0: _x = value; break; case 1: _y = value; break; default: throw new IndexOutOfRangeException(); } } } #endregion #region Conversion Operators /// /// Converts the vector to an array of single-precision floating point values. /// /// A instance. /// An array of single-precision floating point values. public static explicit operator float[] (Vector2 vector) { float[] array = new float[2]; array[0] = vector.X; array[1] = vector.Y; return array; } /// /// Converts the vector to a of single-precision floating point values. /// /// A instance. /// A of single-precision floating point values. public static explicit operator List(Vector2 vector) { List list = new(); list.Add(vector.X); list.Add(vector.Y); return list; } /// /// Converts the vector to a of single-precision floating point values. /// /// A instance. /// A of single-precision floating point values. public static explicit operator LinkedList(Vector2 vector) { LinkedList list = new(); list.AddLast(vector.X); list.AddLast(vector.Y); return list; } #endregion } #region Vector2FConverter class /// /// Converts a to and from string representation. /// public class Vector2FConverter : ExpandableObjectConverter { /// /// Returns whether this converter can convert an object of the given type to the type of this converter, using the specified context. /// /// An that provides a format context. /// A that represents the type you want to convert from. /// true if this converter can perform the conversion; otherwise, false. public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { if (sourceType == typeof(string)) return true; return base.CanConvertFrom(context, sourceType); } /// /// Returns whether this converter can convert the object to the specified type, using the specified context. /// /// An that provides a format context. /// A that represents the type you want to convert to. /// true if this converter can perform the conversion; otherwise, false. public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { if (destinationType == typeof(string)) return true; return base.CanConvertTo(context, destinationType); } /// /// Converts the given value object to the specified type, using the specified context and culture information. /// /// An that provides a format context. /// A object. If a null reference (Nothing in Visual Basic) is passed, the current culture is assumed. /// The to convert. /// The Type to convert the parameter to. /// An that represents the converted value. public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) { if ((destinationType == typeof(string)) && (value is Vector2)) { Vector2 v = (Vector2)value; return v.ToString(); } return base.ConvertTo(context, culture, value, destinationType); } /// /// Converts the given object to the type of this converter, using the specified context and culture information. /// /// An that provides a format context. /// The to use as the current culture. /// The to convert. /// An that represents the converted value. public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { if (value.GetType() == typeof(string)) { return Vector2.Parse((string)value); } return base.ConvertFrom(context, culture, value); } /// /// Returns whether this object supports a standard set of values that can be picked from a list. /// /// An that provides a format context. /// true if should be called to find a common set of values the object supports; otherwise, false. public override bool GetStandardValuesSupported(ITypeDescriptorContext context) { return true; } /// /// Returns a collection of standard values for the data type this type converter is designed for when provided with a format context. /// /// An that provides a format context that can be used to extract additional information about the environment from which this converter is invoked. This parameter or properties of this parameter can be a null reference. /// A that holds a standard set of valid values, or a null reference (Nothing in Visual Basic) if the data type does not support a standard set of values. public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { StandardValuesCollection svc = new(new object[3] { Vector2.Zero, Vector2.XAxis, Vector2.YAxis }); return svc; } } #endregion }