/* * 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 3-Dimentional vector of single-precision floating point numbers. /// [Serializable] [StructLayout(LayoutKind.Sequential)] //[TypeConverter(typeof(Vector3Converter))] public struct Vector3 : ICloneable { #region Constructors /// /// Initializes a new instance of the class with the specified coordinates. /// /// The vector's X coordinate. /// The vector's Y coordinate. /// The vector's Z coordinate. public Vector3(float x, float y, float z) { X = x; Y = y; Z = z; } /// /// Initializes a new instance of the class with the specified coordinates. /// /// An array containing the coordinate parameters. public Vector3(float[] coordinates) { Debug.Assert(coordinates != null); Debug.Assert(coordinates.Length >= 3); X = coordinates[0]; Y = coordinates[1]; Z = coordinates[2]; } /// /// Initializes a new instance of the class with the specified coordinates. /// /// An array containing the coordinate parameters. public Vector3(List coordinates) { Debug.Assert(coordinates != null); Debug.Assert(coordinates.Count >= 3); X = coordinates[0]; Y = coordinates[1]; Z = coordinates[2]; } /// /// Initializes a new instance of the class using coordinates from a given instance. /// /// A to get the coordinates from. public Vector3(Vector3 vector) { X = vector.X; Y = vector.Y; Z = vector.Z; } #endregion #region Constants /// /// 4-Dimentional single-precision floating point zero vector. /// public static readonly Vector3 Zero = new Vector3(0.0f, 0.0f, 0.0f); public static readonly Vector3 One = new Vector3(1.0f, 1.0f, 1.0f); public static readonly Vector3 Inf = new Vector3(float.PositiveInfinity, float.PositiveInfinity, float.PositiveInfinity); /// /// 4-Dimentional single-precision floating point X-Axis vector. /// public static readonly Vector3 XAxis = new Vector3(1.0f, 0.0f, 0.0f); /// /// 4-Dimentional single-precision floating point Y-Axis vector. /// public static readonly Vector3 YAxis = new Vector3(0.0f, 1.0f, 0.0f); /// /// 4-Dimentional single-precision floating point Y-Axis vector. /// public static readonly Vector3 ZAxis = new Vector3(0.0f, 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; /// /// Gets or sets the y-coordinate of this vector. /// /// The y-coordinate of this vector. public float Y; /// /// Gets or sets the z-coordinate of this vector. /// /// The z-coordinate of this vector. public float Z; #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 Vector3(this); } /// /// Creates an exact copy of this object. /// /// The object this method creates. public Vector3 Clone() { return new Vector3(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 Vector3 Parse(string value) { Regex r = new Regex(@"\((?.*),(?.*),(?.*)\)", RegexOptions.Singleline); Match m = r.Match(value); if (m.Success) { return new Vector3( float.Parse(m.Result("${x}")), float.Parse(m.Result("${y}")), float.Parse(m.Result("${z}")) ); } 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 Vector3 result) { Regex r = new Regex(@"\((?.*),(?.*),(?.*)\)", RegexOptions.Singleline); Match m = r.Match(value); if (m.Success) { result = new Vector3( float.Parse(m.Result("${x}")), float.Parse(m.Result("${y}")), float.Parse(m.Result("${z}")) ); return true; } result = Vector3.Zero; return false; } #endregion #region Public Static Vector Arithmetics /// /// Adds two vectors. /// /// A instance. /// A instance. /// A new instance containing the sum. public static Vector3 Add(Vector3 left, Vector3 right) { return new Vector3(left.X + right.X, left.Y + right.Y, left.Z + right.Z); } /// /// Adds a vector and a scalar. /// /// A instance. /// A single-precision floating-point number. /// A new instance containing the sum. public static Vector3 Add(Vector3 vector, float scalar) { return new Vector3(vector.X + scalar, vector.Y + scalar, vector.Z + 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(Vector3 left, Vector3 right, ref Vector3 result) { result.X = left.X + right.X; result.Y = left.Y + right.Y; result.Z = left.Z + right.Z; } /// /// 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(Vector3 vector, float scalar, ref Vector3 result) { result.X = vector.X + scalar; result.Y = vector.Y + scalar; result.Z = vector.Z + 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 Vector3 Subtract(Vector3 left, Vector3 right) { return new Vector3(left.X - right.X, left.Y - right.Y, left.Z - right.Z); } /// /// 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 Vector3 Subtract(Vector3 vector, float scalar) { return new Vector3(vector.X - scalar, vector.Y - scalar, vector.Z - 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 Vector3 Subtract(float scalar, Vector3 vector) { return new Vector3(scalar - vector.X, scalar - vector.Y, scalar - vector.Z); } /// /// 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(Vector3 left, Vector3 right, ref Vector3 result) { result.X = left.X - right.X; result.Y = left.Y - right.Y; result.Z = left.Z - right.Z; } /// /// 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(Vector3 vector, float scalar, ref Vector3 result) { result.X = vector.X - scalar; result.Y = vector.Y - scalar; result.Z = vector.Z - 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, Vector3 vector, ref Vector3 result) { result.X = scalar - vector.X; result.Y = scalar - vector.Y; result.Z = scalar - vector.Z; } /// /// Divides a vector by another vector. /// /// A instance. /// A instance. /// A new containing the quotient. /// /// result[i] = left[i] / right[i]. /// public static Vector3 Divide(Vector3 left, Vector3 right) { return new Vector3(left.X / right.X, left.Y / right.Y, left.Z / right.Z); } /// /// Divides a vector by a scalar. /// /// A instance. /// A scalar /// A new containing the quotient. /// /// result[i] = vector[i] / scalar; /// public static Vector3 Divide(Vector3 vector, float scalar) { return new Vector3(vector.X / scalar, vector.Y / scalar, vector.Z / scalar); } /// /// Divides a scalar by a vector. /// /// A instance. /// A scalar /// A new containing the quotient. /// /// result[i] = scalar / vector[i] /// public static Vector3 Divide(float scalar, Vector3 vector) { return new Vector3(scalar / vector.X, scalar / vector.Y, scalar / vector.Z); } /// /// 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(Vector3 left, Vector3 right, ref Vector3 result) { result.X = left.X / right.X; result.Y = left.Y / right.Y; result.Z = left.Z / right.Z; } /// /// 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(Vector3 vector, float scalar, ref Vector3 result) { result.X = vector.X / scalar; result.Y = vector.Y / scalar; result.Z = vector.Z / 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, Vector3 vector, ref Vector3 result) { result.X = scalar / vector.X; result.Y = scalar / vector.Y; result.Z = scalar / vector.Z; } /// /// Multiplies a vector by a scalar. /// /// A instance. /// A single-precision floating-point number. /// A new containing the result. public static Vector3 Multiply(Vector3 vector, float scalar) { return new Vector3(vector.X * scalar, vector.Y * scalar, vector.Z * 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(Vector3 vector, float scalar, ref Vector3 result) { result.X = vector.X * scalar; result.Y = vector.Y * scalar; result.Z = vector.Z * scalar; } /// /// Calculates the dot product of two vectors. /// /// A instance. /// A instance. /// The dot product value. public static float DotProduct(Vector3 left, Vector3 right) { return (left.X * right.X) + (left.Y * right.Y) + (left.Z * right.Z); } /// /// Calculates the cross product of two vectors. /// /// A instance. /// A instance. /// A new containing the cross product result. public static Vector3 CrossProduct(Vector3 left, Vector3 right) { return new Vector3( left.Y * right.Z - left.Z * right.Y, left.Z * right.X - left.X * right.Z, left.X * right.Y - left.Y * right.X); } /// /// Calculates the cross product of two vectors. /// /// A instance. /// A instance. /// A instance to hold the cross product result. public static void CrossProduct(Vector3 left, Vector3 right, ref Vector3 result) { result.X = left.Y * right.Z - left.Z * right.Y; result.Y = left.Z * right.X - left.X * right.Z; result.Z = left.X * right.Y - left.Y * right.X; } /// /// Negates a vector. /// /// A instance. /// A new instance containing the negated values. public static Vector3 Negate(Vector3 vector) { return new Vector3(-vector.X, -vector.Y, -vector.Z); } /// /// 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(Vector3 left, Vector3 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(Vector3 left, Vector3 right, float tolerance) { return ( (System.Math.Abs(left.X - right.X) <= tolerance) && (System.Math.Abs(left.Y - right.Y) <= tolerance) && (System.Math.Abs(left.Z - right.Z) <= 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; Z /= 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 + Z * Z); } /// /// 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 + Z * Z); } /// /// 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); Z = MathFunctions.Clamp(Z, 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); Z = MathFunctions.Clamp(Z, 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() ^ Z.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 Vector3) { Vector3 v = (Vector3)obj; return (X == v.X) && (Y == v.Y) && (Z == v.Z); } return false; } /// /// Returns a string representation of this object. /// /// A string representation of this object. public override string ToString() { return $"({X}, {Y}, {Z})"; } #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 ==(Vector3 left, Vector3 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 !=(Vector3 left, Vector3 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 >(Vector3 left, Vector3 right) { return ( (left.X > right.X) && (left.Y > right.Y) && (left.Z > right.Z)); } /// /// 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 <(Vector3 left, Vector3 right) { return ( (left.X < right.X) && (left.Y < right.Y) && (left.Z < right.Z)); } /// /// 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 >=(Vector3 left, Vector3 right) { return ( (left.X >= right.X) && (left.Y >= right.Y) && (left.Z >= right.Z)); } /// /// 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 <=(Vector3 left, Vector3 right) { return ( (left.X <= right.X) && (left.Y <= right.Y) && (left.Z <= right.Z)); } #endregion #region Unary Operators /// /// Negates the values of the given vector. /// /// A instance. /// A new instance containing the negated values. public static Vector3 operator -(Vector3 vector) { return Vector3.Negate(vector); } #endregion #region Binary Operators /// /// Adds two vectors. /// /// A instance. /// A instance. /// A new instance containing the sum. public static Vector3 operator +(Vector3 left, Vector3 right) { return Vector3.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 Vector3 operator +(Vector3 vector, float scalar) { return Vector3.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 Vector3 operator +(float scalar, Vector3 vector) { return Vector3.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 Vector3 operator -(Vector3 left, Vector3 right) { return Vector3.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 Vector3 operator -(Vector3 vector, float scalar) { return Vector3.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 Vector3 operator -(float scalar, Vector3 vector) { return Vector3.Subtract(scalar, vector); } /// /// Multiplies a vector by a scalar. /// /// A instance. /// A single-precision floating-point number. /// A new containing the result. public static Vector3 operator *(Vector3 vector, float scalar) { return Vector3.Multiply(vector, scalar); } /// /// Multiplies a vector by a scalar. /// /// A instance. /// A single-precision floating-point number. /// A new containing the result. public static Vector3 operator *(float scalar, Vector3 vector) { return Vector3.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 Vector3 operator /(Vector3 vector, float scalar) { return Vector3.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 Vector3 operator /(float scalar, Vector3 vector) { return Vector3.Divide(scalar, vector); } #endregion #region Array Indexing Operator /// /// Indexer ( [x, y, z] ). /// public float this[int index] { get { switch (index) { case 0: return X; case 1: return Y; case 2: return Z; default: throw new IndexOutOfRangeException(); } } set { switch (index) { case 0: X = value; break; case 1: Y = value; break; case 2: Z = value; break; default: throw new IndexOutOfRangeException(); } } } public float this[uint index] { get { switch (index) { case 0: return X; case 1: return Y; case 2: return Z; default: throw new IndexOutOfRangeException(); } } set { switch (index) { case 0: X = value; break; case 1: Y = value; break; case 2: Z = 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[] (Vector3 vector) { float[] array = new float[3]; array[0] = vector.X; array[1] = vector.Y; array[2] = vector.Z; 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(Vector3 vector) { List list = new List(3); list.Add(vector.X); list.Add(vector.Y); list.Add(vector.Z); 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(Vector3 vector) { LinkedList list = new LinkedList(); list.AddLast(vector.X); list.AddLast(vector.Y); list.AddLast(vector.Z); return list; } #endregion public float magnitude() { return (float)Math.Sqrt(X * X + Y * Y + Z * Z); } public float dot(Vector3 rkVector) { return X * rkVector.X + Y * rkVector.Y + Z * rkVector.Z; } public Vector3 cross(Vector3 rkVector) { return new Vector3(Y * rkVector.Z - Z * rkVector.Y, Z * rkVector.X - X * rkVector.Z, X * rkVector.Y - Y * rkVector.X); } public Vector3 Min(Vector3 v) { return new Vector3(Math.Min(v.X, X), Math.Min(v.Y, Y), Math.Min(v.Z, Z)); } public Vector3 Max(Vector3 v) { return new Vector3(Math.Max(v.X, X), Math.Max(v.Y, Y), Math.Max(v.Z, Z)); } public Axis primaryAxis() { Axis a = Axis.X; double nx = Math.Abs(X); double ny = Math.Abs(Y); double nz = Math.Abs(Z); if (nx > ny) { if (nx > nz) a = Axis.X; else a = Axis.Z; } else { if (ny > nz) a = Axis.Y; else a = Axis.Z; } return a; } public enum Axis { X = 0, Y = 1, Z = 2, Detect = -1 }; public Vector3 lerp(Vector3 v, float alpha) { return (this) + (v - this) * alpha; } /// /// /// /// Vector3.Zero if the length is nearly zero, otherwise returns a unit vector public Vector3 directionOrZero() { float mag = magnitude(); if (mag < 0.0000001f) { return Zero; } else if (mag < 1.00001f && mag > 0.99999f) { return this; } else { return this * (1.0f / mag); } } } #region Vector3Converter class /// /// Converts a to and from string representation. /// public class Vector3Converter : 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 Vector3)) { Vector3 v = (Vector3)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 Vector3.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 StandardValuesCollection(new object[4] { Vector3.Zero, Vector3.XAxis, Vector3.YAxis, Vector3.ZAxis }); return svc; } } #endregion }