#region --- License ---
/* Licensed under the MIT/X11 license.
* Copyright (c) 2006-2008 the OpenTK Team.
* This notice may not be removed from any source distribution.
* See license.txt for licensing detailed licensing details.
*
* Contributions by James Talton and Georg Wächter.
*/
#endregion
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)]
internal 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 Perpendicular
///
/// Gets the perpendicular vector.
///
public Vector2d Perpendicular
{
get
{
return new Vector2d(Y, -X);
}
}
#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
}
}