using System; using System.Diagnostics; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text.RegularExpressions; namespace OpenTK.Math { /// A 2-dimensional vector using double-precision floating point numbers. [Serializable] [StructLayout(LayoutKind.Sequential)] public struct Vector2d : IEquatable, IComparer, IComparable { #region Fields & Access /// The X coordinate of the vector. public double X; /// The Y coordinate of the vector. public double Y; /// The coordinate at the index of the vector. public double this[int index] { get { switch (index) { case 0: return X; case 1: return Y; } throw new IndexOutOfRangeException(); } set { switch (index) { case 0: X = value; return; case 1: Y = value; return; } throw new IndexOutOfRangeException(); } } /// Converts the vector into an array of double-precision floating point numbers. /// The vector being converted. /// An array of double-precision floating point numbers representing the vector coordinates. public static explicit operator double[](Vector2d vector) { return new double[2] { vector.X, vector.Y }; } /// Converts the vector into left double-precision floating point number pointer. /// The vector being converted. /// A double-precision floating point number pointer to the vector coordinates. //unsafe public static explicit operator double*(Vector2d vector) //{ // return &vector.X; //} /// Converts the vector into an IntPtr. /// The vector being converted. /// An IntPtr to the vector coordinates. //public static explicit operator IntPtr(Vector2d vector) //{ // unsafe // { // return (IntPtr)(&vector.X); // } //} #endregion #region Constructors /// Constructs left vector with the given coordinates. /// The X coordinate. /// The Y coordinate. public Vector2d(double x, double y) { this.X = x; this.Y = y; } /// Constructs left vector with the same coordinates as the given vector. /// The vector whose coordinates to copy. public Vector2d(ref Vector2d vector) { this.X = vector.X; this.Y = vector.Y; } /// Constructs left vector from the given array of double-precision floating point numbers. /// The array of doubles for the coordinates of the vector. public Vector2d(double[] coordinateArray) { if (coordinateArray == null || coordinateArray.GetLength(0) < 2) throw new Exception("Invalid parameter."); this.X = coordinateArray[0]; this.Y = coordinateArray[1]; } #endregion #region Equality /// Indicates whether the current vector is equal to another vector. /// An vector to compare with this vector. /// true if the current vector is equal to the vector parameter; otherwise, false. [CLSCompliant(false)] public bool Equals(Vector2d vector) { return X == vector.X && Y == vector.Y; } /// Indicates whether the current vector is equal to another vector. /// An vector to compare with this vector. /// true if the current vector is equal to the vector parameter; otherwise, false. public bool Equals(ref Vector2d vector) { return X == vector.X && Y == vector.Y; } /// Indicates whether two vectors are approximately equal to each other. /// The first vector. /// The second vector. /// true if the vectors are approximately equal; otherwise, false. public static bool Equals(ref Vector2d left, ref Vector2d right) { return left.X == right.X && left.Y == right.Y; } /// Indicates whether the current vector is equal to another vector. /// An vector to compare with this vector. /// true if the current vector is equal to the vector parameter; otherwise, false. public bool EqualsApprox(ref Vector2d vector, double tolerance) { return System.Math.Abs(X - vector.X) <= tolerance && System.Math.Abs(Y - vector.Y) <= tolerance; } /// Indicates whether two vectors are approximately equal to each other within left given tolerance. /// The first vector. /// The second vector. /// The tolerance for the approximation. /// true if the vectors are approximately equal; otherwise, false. public static bool EqualsApprox(ref Vector2d left, ref Vector2d right, double tolerance) { return System.Math.Abs(left.X - right.X) <= tolerance && System.Math.Abs(left.Y - right.Y) <= tolerance; } #endregion #region IComparer /// Compares two vectors and returns left value indicating whether one is less than, equal to, or greater than the other. public int Compare(Vector2d left, Vector2d right) { if (left.X != right.X) { if (left.X < right.X) return -1; else return 1; } else if (left.Y != right.Y) { if (left.Y < right.Y) return -1; else return 1; } return 0; } #endregion #region IComparable /// Compares the vector with another vector and returns left value indicating whether it is less than, equal to, or greater than the other vector. public int CompareTo(Vector2d vector) { return Compare(this, vector); } #endregion #region Length /// Gets the length of the vector. public double Length { get { double lengthSquared = LengthSquared; if (lengthSquared == 1) { return 1; } else { return System.Math.Sqrt(lengthSquared); } } } /// Gets the squared length of the vector. public double LengthSquared { get { return X * X + Y * Y; } } /// Gets the approimate length of the vector. public double LengthApprox { get { return 1.0d / Functions.InverseSqrtFast(X * X + Y * Y); } } #endregion #region Distance /// Gets the distance from this vector to the given vector. /// The vector to which to find the distance. /// The distance from this vector to the given vector. public double DistanceTo(ref Vector2d vector) { double deltaX = vector.X - X; double deltaY = vector.Y - Y; return System.Math.Sqrt(deltaX * deltaX + deltaY * deltaY); } /// Gets the squared distance from this vector to the given vector. /// The vector to which to find the squared distance. /// The squared distance from this vector to the given vector. public double DistanceSquaredTo(ref Vector2d vector) { double deltaX = vector.X - X; double deltaY = vector.Y - Y; return deltaX * deltaX + deltaY * deltaY; } /// Gets the approximate distance from this vector to the given vector. /// The vector to which to find the approximate distance. /// The approximate distance from this vector to the given vector. public double DistanceApproxTo(ref Vector2d vector) { double deltaX = vector.X - X; double deltaY = vector.Y - Y; return 1.0d / Functions.InverseSqrtFast(deltaX * deltaX + deltaY * deltaY); } #endregion #region Normalize /// Normalize this vector. public void Normalize() { double lengthSquared = LengthSquared; if (lengthSquared != 1 && lengthSquared != 0) { double length = System.Math.Sqrt(lengthSquared); X = X / length; Y = Y / length; } } /// Get the normalized version of this vector. /// The resulting normalized vector. public void Normalize(out Vector2d result) { double lengthSquared = LengthSquared; if (lengthSquared == 1) { result.X = X; result.Y = Y; } else if (lengthSquared == 0) { result.X = 0; result.Y = 0; } else { double length = System.Math.Sqrt(lengthSquared); result.X = X / length; result.Y = Y / length; } } public static void Normalize(ref Vector2d vector, out Vector2d result) { double lengthSquared = vector.LengthSquared; if (lengthSquared == 1) { result.X = vector.X; result.Y = vector.Y; } else if (lengthSquared == 0) { result.X = 0; result.Y = 0; } else { double length = System.Math.Sqrt(lengthSquared); result.X = vector.X / length; result.Y = vector.Y / length; } } public void NormalizeApprox() { double inverseSquare = Functions.InverseSqrtFast(X * X + Y * Y); X = X * inverseSquare; Y = Y * inverseSquare; } /// Gets left approximately normalized vector of the vector. public void NormalizedApprox(out Vector2d result) { double inverseSquare = Functions.InverseSqrtFast(X * X + Y * Y); result.X = X * inverseSquare; result.Y = Y * inverseSquare; } public static void NormalizeApprox(ref Vector2d vector, out Vector2d result) { double inverseSquare = Functions.InverseSqrtFast(vector.X * vector.X + vector.Y * vector.Y); result.X = vector.X * inverseSquare; result.Y = vector.Y * inverseSquare; } #endregion /// Gets the dot product of two vectors. /// The first vector. /// The second vector. /// The dot product of two vectors. public static double DotProduct(ref Vector2d left, ref Vector2d right) { return left.X * right.X + left.Y * right.Y; } #region Abs public void Abs() { X = System.Math.Abs(X); Y = System.Math.Abs(Y); } public void Abs(out Vector2d result) { result.X = System.Math.Abs(X); result.Y = System.Math.Abs(Y); } public static void Abs(ref Vector2d vector, out Vector2d result) { result.X = System.Math.Abs(vector.X); result.Y = System.Math.Abs(vector.Y); } #endregion #region Inverse public void Inverse() { X = -X; Y = -Y; } public void Inverse(out Vector2d result) { result.X = -X; result.Y = -Y; } public static void Inverse(ref Vector2d vector, out Vector2d result) { result.X = -vector.X; result.Y = -vector.Y; } #endregion #region Arithmatic public void Add(ref Vector2d vector) { X = X + vector.X; Y = Y + vector.Y; } public void Add(ref Vector2d vector, out Vector2d result) { result.X = X + vector.X; result.Y = Y + vector.Y; } public static void Add(ref Vector2d left, ref Vector2d right, out Vector2d result) { result.X = left.X + right.X; result.Y = left.Y + right.Y; } public void Add(double x, double y) { X = X + x; Y = Y + y; } public void Add(double x, double y, out Vector2d result) { result.X = X + x; result.Y = Y + y; } public static void Add(ref Vector2d vector, double x, double y, out Vector2d result) { result.X = vector.X + x; result.Y = vector.Y + y; } public void Subtract(ref Vector2d vector) { X = X - vector.X; Y = Y - vector.Y; } public void Subtract(ref Vector2d vector, out Vector2d result) { result.X = X - vector.X; result.Y = Y - vector.Y; } public static void Subtract(ref Vector2d left, ref Vector2d right, out Vector2d result) { result.X = left.X - right.X; result.Y = left.Y - right.Y; } public void Subtract(double x, double y) { X = X - x; Y = Y - y; } public void Subtract(double x, double y, out Vector2d result) { result.X = X - x; result.Y = Y - y; } public static void Subtract(ref Vector2d vector, double x, double y, out Vector2d result) { result.X = vector.X - x; result.Y = vector.Y - y; } public void Multiply(double scalar) { X = X * scalar; Y = Y * scalar; } public void Multiply(double scalar, out Vector2d result) { result.X = X * scalar; result.Y = Y * scalar; } public static void Multiply(ref Vector2d vector, double scalar, out Vector2d result) { result.X = vector.X * scalar; result.Y = vector.Y * scalar; } public void Multiply(ref Vector2d vector) { X = X * vector.X; Y = Y * vector.Y; } public void Multiply(ref Vector2d vector, out Vector2d result) { result.X = X * vector.X; result.Y = Y * vector.Y; } public static void Multiply(ref Vector2d left, ref Vector2d right, out Vector2d result) { result.X = left.X * right.X; result.Y = left.Y * right.Y; } public void Multiply(double x, double y) { X = X * x; Y = Y * y; } public void Multiply(double x, double y, out Vector2d result) { result.X = X * x; result.Y = Y * y; } public static void Multiply(ref Vector2d vector, double x, double y, out Vector2d result) { result.X = vector.X * x; result.Y = vector.Y * y; } public void Divide(double scalar) { X = X / scalar; Y = Y / scalar; } public void Divide(double scalar, out Vector2d result) { result.X = X / scalar; result.Y = Y / scalar; } public static void Divide(ref Vector2d vector, double scalar, out Vector2d result) { result.X = vector.X / scalar; result.Y = vector.Y / scalar; } public void Divide(ref Vector2d vector) { X = X / vector.X; Y = Y / vector.Y; } public void Divide(ref Vector2d vector, out Vector2d result) { result.X = X / vector.X; result.Y = Y / vector.Y; } public static void Divide(ref Vector2d left, ref Vector2d right, out Vector2d result) { result.X = left.X / right.X; result.Y = left.Y / right.Y; } public void Divide(double x, double y) { X = X / x; Y = Y / y; } public void Divide(double x, double y, out Vector2d result) { result.X = X / x; result.Y = Y / y; } public static void Divide(ref Vector2d vector, double x, double y, out Vector2d result) { result.X = vector.X / x; result.Y = vector.Y / y; } #endregion #region Transformations public void Transform(ref Matrix4d matrix) { double x = matrix.R0C0 * X + matrix.R0C1 * Y; Y = matrix.R1C0 * X + matrix.R1C1 * Y; X = x; } public void Transform(ref Matrix4d matrix, out Vector2d result) { result.X = matrix.R0C0 * X + matrix.R0C1 * Y; result.Y = matrix.R1C0 * X + matrix.R1C1 * Y; } public static void Transform(ref Vector2d vector, ref Matrix4d matrix, out Vector2d result) { result.X = matrix.R0C0 * vector.X + matrix.R0C1 * vector.Y; result.Y = matrix.R1C0 * vector.X + matrix.R1C1 * vector.Y; } public void Translate(ref Vector2d vector) { X = X + vector.X; Y = Y + vector.Y; } public void Translate(ref Vector2d vector, out Vector2d result) { result.X = X + vector.X; result.Y = Y + vector.Y; } public static void Translate(ref Vector2d left, ref Vector2d right, out Vector2d result) { result.X = left.X + right.X; result.Y = left.Y + right.Y; } public void Translate(double x, double y) { X = X + x; Y = Y + y; } public void Translate(double x, double y, out Vector2d result) { result.X = X + x; result.Y = Y + y; } public static void Translate(ref Vector2d vector, double x, double y, out Vector2d result) { result.X = vector.X + x; result.Y = vector.Y + y; } public void Scale(ref Vector2d vector) { X = X * vector.X; Y = Y * vector.Y; } public void Scale(ref Vector2d vector, out Vector2d result) { result.X = X * vector.X; result.Y = Y * vector.Y; } public static void Scale(ref Vector2d left, ref Vector2d right, out Vector2d result) { result.X = left.X * right.X; result.Y = left.Y * right.Y; } public void Scale(double x, double y) { X = X * x; Y = Y * y; } public void Scale(double x, double y, out Vector2d result) { result.X = X * x; result.Y = Y * y; } public static void Scale(ref Vector2d vector, double x, double y, out Vector2d result) { result.X = vector.X * x; result.Y = vector.Y * y; } public void Rotate(double angle) { double angleRadians = Functions.DTOR * angle; double sin = (double)System.Math.Sin(angleRadians); double cos = (double)System.Math.Cos(angleRadians); double x = cos * X + sin * Y; Y = cos * Y - sin * X; X = x; } public void Rotate(double angle, out Vector2d result) { double angleRadians = Functions.DTOR * angle; double sin = (double)System.Math.Sin(angleRadians); double cos = (double)System.Math.Cos(angleRadians); result.X = cos * X + sin * Y; result.Y = cos * Y - sin * X; } public static void Rotate(ref Vector2d vector, double angle, out Vector2d result) { double angleRadians = Functions.DTOR * angle; double sin = (double)System.Math.Sin(angleRadians); double cos = (double)System.Math.Cos(angleRadians); result.X = cos * vector.X + sin * vector.Y; result.Y = cos * vector.Y - sin * vector.X; } #endregion #region Min & Max public void Min(ref Vector2d vector) { double lengthSquared = X * X + Y * Y; double vectorLengthSquared = vector.X * vector.X + vector.Y * vector.Y; if (vectorLengthSquared < lengthSquared) { X = vector.X; Y = vector.Y; } } public void Min(ref Vector2d vector, out Vector2d result) { double lengthSquared = X * X + Y * Y; double vectorLengthSquared = vector.X * vector.X + vector.Y * vector.Y; if (vectorLengthSquared < lengthSquared) { result.X = vector.X; result.Y = vector.Y; } else { result.X = X; result.Y = Y; } } public static void Min(ref Vector2d left, ref Vector2d right, out Vector2d result) { double leftLengthSquared = left.X * left.X + left.Y * left.Y; double rightLengthSquared = right.X * right.X + right.Y * right.Y; if (rightLengthSquared < leftLengthSquared) { result.X = right.X; result.Y = right.Y; } else { result.X = left.X; result.Y = left.Y; } } public void Max(ref Vector2d vector) { double lengthSquared = X * X + Y * Y; double vectorLengthSquared = vector.X * vector.X + vector.Y * vector.Y; if (vectorLengthSquared > lengthSquared) { X = vector.X; Y = vector.Y; } } public void Max(ref Vector2d vector, out Vector2d result) { double lengthSquared = X * X + Y * Y; double vectorLengthSquared = vector.X * vector.X + vector.Y * vector.Y; if (vectorLengthSquared > lengthSquared) { result.X = vector.X; result.Y = vector.Y; } else { result.X = X; result.Y = Y; } } public static void Max(ref Vector2d left, ref Vector2d right, out Vector2d result) { double leftLengthSquared = left.X * left.X + left.Y * left.Y; double rightLengthSquared = right.X * right.X + right.Y * right.Y; if (rightLengthSquared > leftLengthSquared) { result.X = right.X; result.Y = right.Y; } else { result.X = left.X; result.Y = left.Y; } } public void CoordinateMin(ref Vector2d vector) { X = System.Math.Min(X, vector.X); Y = System.Math.Min(Y, vector.Y); } public void CoordinateMin(ref Vector2d vector, out Vector2d result) { result.X = System.Math.Min(X, vector.X); result.Y = System.Math.Min(Y, vector.Y); } public static void CoordinateMin(ref Vector2d left, ref Vector2d right, out Vector2d result) { result.X = System.Math.Min(left.X, right.X); result.Y = System.Math.Min(left.Y, right.Y); } public void CoordinateMax(ref Vector2d vector) { X = System.Math.Max(X, vector.X); Y = System.Math.Max(Y, vector.Y); } public void CoordinateMax(ref Vector2d vector, out Vector2d result) { result.X = System.Math.Max(X, vector.X); result.Y = System.Math.Max(Y, vector.Y); } public static void CoordinateMax(ref Vector2d left, ref Vector2d right, out Vector2d result) { result.X = System.Math.Max(left.X, right.X); result.Y = System.Math.Max(left.Y, right.Y); } public void Clamp(ref Vector2d min, ref Vector2d max) { X = System.Math.Max(System.Math.Min(X, min.X), max.X); Y = System.Math.Max(System.Math.Min(Y, min.Y), max.Y); } public void Clamp(ref Vector2d min, ref Vector2d max, out Vector2d result) { result.X = System.Math.Max(System.Math.Min(X, min.X), max.X); result.Y = System.Math.Max(System.Math.Min(Y, min.Y), max.Y); } public static void Clamp(ref Vector2d vector, ref Vector2d min, ref Vector2d max, out Vector2d result) { result.X = System.Math.Max(System.Math.Min(vector.X, min.X), max.X); result.Y = System.Math.Max(System.Math.Min(vector.Y, min.Y), max.Y); } #endregion #region Interpolation public void Lerp(ref Vector2d end, double blend) { X = X + (end.X - X) * blend; Y = Y + (end.Y - Y) * blend; } public void Lerp(ref Vector2d end, double blend, out Vector2d result) { result.X = X + (end.X - X) * blend; result.Y = Y + (end.Y - Y) * blend; } public static void Lerp(ref Vector2d start, ref Vector2d end, double blend, out Vector2d result) { result.X = start.X + (end.X - start.X) * blend; result.Y = start.Y + (end.Y - start.Y) * blend; } public void BaryCentric(ref Vector2d endU, ref Vector2d endV, double u, double v) { X = X + (endU.X - X) * u + (endV.X - X) * v; Y = Y + (endU.Y - Y) * u + (endV.Y - Y) * v; } public void BaryCentric(ref Vector2d endU, ref Vector2d endV, double u, double v, out Vector2d result) { result.X = X + (endU.X - X) * u + (endV.X - X) * v; result.Y = Y + (endU.Y - Y) * u + (endV.Y - Y) * v; } public static void BaryCentric(ref Vector2d start, ref Vector2d endU, ref Vector2d endV, double u, double v, out Vector2d result) { result.X = start.X + (endU.X - start.X) * u + (endV.X - start.X) * v; result.Y = start.Y + (endU.Y - start.Y) * u + (endV.Y - start.Y) * v; } #endregion #region String and Parse /// Returns the fully qualified type name of this instance. /// A System.String containing left fully qualified type name. public override string ToString() { return String.Format("{0} {1} {2}", X, Y); } /// Parse left string to convert it to left vector. /// The string to parse. /// The vector represented by the string. public static void Parse(string str, out Vector2d result) { Match match = new Regex(@"(?.*) (?.*)", RegexOptions.None).Match(str); if (!match.Success) throw new Exception("Parse failed!"); result.X = double.Parse(match.Result("${x}")); result.Y = double.Parse(match.Result("${y}")); } #endregion #region HashCode /// Returns the hash code for this instance. /// A 32-bit signed integer that is the hash code for this instance. public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode(); } #endregion #region Constants /// A vector representing left zero vector. public static readonly Vector2d Zero = new Vector2d(0, 0); /// A vector with all coordinates set to one. public static readonly Vector2d One = new Vector2d(1, 1); #endregion } }