// Copyright (c) Xenko contributors. (https://xenko.com) // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. using System; using System.ComponentModel; using System.Globalization; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; namespace math { /// /// Represents a four dimensional mathematical vector with double-precision floats. /// [DataContract( Name = "double4")] [DataStyle(DataStyle.Compact)] [StructLayout(LayoutKind.Sequential, Pack = 4)] public struct Double4 : IEquatable, IFormattable { /// /// The size of the type, in bytes. /// public static readonly int SizeInBytes = Utilities.SizeOf(); /// /// A with all of its components set to zero. /// public static readonly Double4 Zero = new Double4(); /// /// The X unit (1, 0, 0, 0). /// public static readonly Double4 UnitX = new Double4(1.0, 0.0, 0.0, 0.0); /// /// The Y unit (0, 1, 0, 0). /// public static readonly Double4 UnitY = new Double4(0.0, 1.0, 0.0, 0.0); /// /// The Z unit (0, 0, 1, 0). /// public static readonly Double4 UnitZ = new Double4(0.0, 0.0, 1.0, 0.0); /// /// The W unit (0, 0, 0, 1). /// public static readonly Double4 UnitW = new Double4(0.0, 0.0, 0.0, 1.0); /// /// A with all of its components set to one. /// public static readonly Double4 One = new Double4(1.0, 1.0, 1.0, 1.0); /// /// The X component of the vector. /// [DataMember( Order = 0 )] public double X; /// /// The Y component of the vector. /// [DataMember( Order = 1 )] public double Y; /// /// The Z component of the vector. /// [DataMember( Order = 2 )] public double Z; /// /// The W component of the vector. /// [DataMember( Order = 3 )] public double W; /// /// Initializes a new instance of the struct. /// /// The value that will be assigned to all components. public Double4(double value) { X = value; Y = value; Z = value; W = value; } /// /// Initializes a new instance of the struct. /// /// Initial value for the X component of the vector. /// Initial value for the Y component of the vector. /// Initial value for the Z component of the vector. /// Initial value for the W component of the vector. public Double4(double x, double y, double z, double w) { X = x; Y = y; Z = z; W = w; } /// /// Initializes a new instance of the struct. /// /// A vector containing the values with which to initialize the X, Y, and Z components. /// Initial value for the W component of the vector. public Double4(Double3 value, double w) { X = value.X; Y = value.Y; Z = value.Z; W = w; } /// /// Initializes a new instance of the struct. /// /// A vector containing the values with which to initialize the X and Y components. /// Initial value for the Z component of the vector. /// Initial value for the W component of the vector. public Double4(Double2 value, double z, double w) { X = value.X; Y = value.Y; Z = z; W = w; } /// /// Initializes a new instance of the struct. /// /// The values to assign to the X, Y, Z, and W components of the vector. This must be an array with four elements. /// Thrown when is null. /// Thrown when contains more or less than four elements. public Double4(double[] values) { if (values == null) throw new ArgumentNullException("values"); if (values.Length != 4) throw new ArgumentOutOfRangeException("values", "There must be four and only four input values for Double4."); X = values[0]; Y = values[1]; Z = values[2]; W = values[3]; } /// /// Initializes a new instance of the struct. /// /// The Vector4 to construct the Double4 from. public Double4(Vector4 v) { X = v.X; Y = v.Y; Z = v.Z; W = v.W; } /// /// Gets a value indicting whether this instance is normalized. /// public bool IsNormalized { get { return Math.Abs((X * X) + (Y * Y) + (Z * Z) + (W * W) - 1f) < MathUtil.ZeroTolerance; } } /// /// Gets or sets the component at the specified index. /// /// The value of the X, Y, Z, or W component, depending on the index. /// The index of the component to access. Use 0 for the X component, 1 for the Y component, 2 for the Z component, and 3 for the W component. /// The value of the component at the specified index. /// Thrown when the is out of the range [0, 3]. public double this[int index] { get { switch (index) { case 0: return X; case 1: return Y; case 2: return Z; case 3: return W; } throw new ArgumentOutOfRangeException("index", "Indices for Double4 run from 0 to 3, inclusive."); } set { switch (index) { case 0: X = value; break; case 1: Y = value; break; case 2: Z = value; break; case 3: W = value; break; default: throw new ArgumentOutOfRangeException("index", "Indices for Double4 run from 0 to 3, inclusive."); } } } /// /// Calculates the length of the vector. /// /// The length of the vector. /// /// may be preferred when only the relative length is needed /// and speed is of the essence. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public double Length() { return (double)Math.Sqrt((X * X) + (Y * Y) + (Z * Z) + (W * W)); } /// /// Calculates the squared length of the vector. /// /// The squared length of the vector. /// /// This method may be preferred to when only a relative length is needed /// and speed is of the essence. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public double LengthSquared() { return (X * X) + (Y * Y) + (Z * Z) + (W * W); } /// /// Converts the vector into a unit vector. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Normalize() { double length = Length(); if (length > MathUtil.ZeroTolerance) { double inverse = 1.0 / length; X *= inverse; Y *= inverse; Z *= inverse; W *= inverse; } } /// /// Raises the exponent for each components. /// /// The exponent. public void Pow(double exponent) { X = (double)Math.Pow(X, exponent); Y = (double)Math.Pow(Y, exponent); Z = (double)Math.Pow(Z, exponent); W = (double)Math.Pow(W, exponent); } /// /// Creates an array containing the elements of the vector. /// /// A four-element array containing the components of the vector. public double[] ToArray() { return new double[] { X, Y, Z, W }; } /// /// Adds two vectors. /// /// The first vector to add. /// The second vector to add. /// When the method completes, contains the sum of the two vectors. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Add(ref Double4 left, ref Double4 right, out Double4 result) { result = new Double4(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W); } /// /// Adds two vectors. /// /// The first vector to add. /// The second vector to add. /// The sum of the two vectors. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Double4 Add(Double4 left, Double4 right) { return new Double4(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W); } /// /// Subtracts two vectors. /// /// The first vector to subtract. /// The second vector to subtract. /// When the method completes, contains the difference of the two vectors. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Subtract(ref Double4 left, ref Double4 right, out Double4 result) { result = new Double4(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W); } /// /// Subtracts two vectors. /// /// The first vector to subtract. /// The second vector to subtract. /// The difference of the two vectors. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Double4 Subtract(Double4 left, Double4 right) { return new Double4(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W); } /// /// Scales a vector by the given value. /// /// The vector to scale. /// The amount by which to scale the vector. /// When the method completes, contains the scaled vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Multiply(ref Double4 value, double scale, out Double4 result) { result = new Double4(value.X * scale, value.Y * scale, value.Z * scale, value.W * scale); } /// /// Scales a vector by the given value. /// /// The vector to scale. /// The amount by which to scale the vector. /// The scaled vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Double4 Multiply(Double4 value, double scale) { return new Double4(value.X * scale, value.Y * scale, value.Z * scale, value.W * scale); } /// /// Modulates a vector with another by performing component-wise multiplication. /// /// The first vector to modulate. /// The second vector to modulate. /// When the method completes, contains the modulated vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Modulate(ref Double4 left, ref Double4 right, out Double4 result) { result = new Double4(left.X * right.X, left.Y * right.Y, left.Z * right.Z, left.W * right.W); } /// /// Modulates a vector with another by performing component-wise multiplication. /// /// The first vector to modulate. /// The second vector to modulate. /// The modulated vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Double4 Modulate(Double4 left, Double4 right) { return new Double4(left.X * right.X, left.Y * right.Y, left.Z * right.Z, left.W * right.W); } /// /// Scales a vector by the given value. /// /// The vector to scale. /// The amount by which to scale the vector. /// When the method completes, contains the scaled vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Divide(ref Double4 value, double scale, out Double4 result) { result = new Double4(value.X / scale, value.Y / scale, value.Z / scale, value.W / scale); } /// /// Scales a vector by the given value. /// /// The vector to scale. /// The amount by which to scale the vector. /// The scaled vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Double4 Divide(Double4 value, double scale) { return new Double4(value.X / scale, value.Y / scale, value.Z / scale, value.W / scale); } /// /// Demodulates a vector with another by performing component-wise division. /// /// The first vector to demodulate. /// The second vector to demodulate. /// When the method completes, contains the demodulated vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Demodulate(ref Double4 left, ref Double4 right, out Double4 result) { result = new Double4(left.X / right.X, left.Y / right.Y, left.Z / right.Z, left.W / right.W); } /// /// Demodulates a vector with another by performing component-wise division. /// /// The first vector to demodulate. /// The second vector to demodulate. /// The demodulated vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Double4 Demodulate(Double4 left, Double4 right) { return new Double4(left.X / right.X, left.Y / right.Y, left.Z / right.Z, left.W / right.W); } /// /// Reverses the direction of a given vector. /// /// The vector to negate. /// When the method completes, contains a vector facing in the opposite direction. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Negate(ref Double4 value, out Double4 result) { result = new Double4(-value.X, -value.Y, -value.Z, -value.W); } /// /// Reverses the direction of a given vector. /// /// The vector to negate. /// A vector facing in the opposite direction. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Double4 Negate(Double4 value) { return new Double4(-value.X, -value.Y, -value.Z, -value.W); } /// /// Returns a containing the 4D Cartesian coordinates of a point specified in Barycentric coordinates relative to a 4D triangle. /// /// A containing the 4D Cartesian coordinates of vertex 1 of the triangle. /// A containing the 4D Cartesian coordinates of vertex 2 of the triangle. /// A containing the 4D Cartesian coordinates of vertex 3 of the triangle. /// Barycentric coordinate b2, which expresses the weighting factor toward vertex 2 (specified in ). /// Barycentric coordinate b3, which expresses the weighting factor toward vertex 3 (specified in ). /// When the method completes, contains the 4D Cartesian coordinates of the specified point. public static void Barycentric(ref Double4 value1, ref Double4 value2, ref Double4 value3, double amount1, double amount2, out Double4 result) { result = new Double4((value1.X + (amount1 * (value2.X - value1.X))) + (amount2 * (value3.X - value1.X)), (value1.Y + (amount1 * (value2.Y - value1.Y))) + (amount2 * (value3.Y - value1.Y)), (value1.Z + (amount1 * (value2.Z - value1.Z))) + (amount2 * (value3.Z - value1.Z)), (value1.W + (amount1 * (value2.W - value1.W))) + (amount2 * (value3.W - value1.W))); } /// /// Returns a containing the 4D Cartesian coordinates of a point specified in Barycentric coordinates relative to a 4D triangle. /// /// A containing the 4D Cartesian coordinates of vertex 1 of the triangle. /// A containing the 4D Cartesian coordinates of vertex 2 of the triangle. /// A containing the 4D Cartesian coordinates of vertex 3 of the triangle. /// Barycentric coordinate b2, which expresses the weighting factor toward vertex 2 (specified in ). /// Barycentric coordinate b3, which expresses the weighting factor toward vertex 3 (specified in ). /// A new containing the 4D Cartesian coordinates of the specified point. public static Double4 Barycentric(Double4 value1, Double4 value2, Double4 value3, double amount1, double amount2) { Double4 result; Barycentric(ref value1, ref value2, ref value3, amount1, amount2, out result); return result; } /// /// Restricts a value to be within a specified range. /// /// The value to clamp. /// The minimum value. /// The maximum value. /// When the method completes, contains the clamped value. public static void Clamp(ref Double4 value, ref Double4 min, ref Double4 max, out Double4 result) { double x = value.X; x = (x > max.X) ? max.X : x; x = (x < min.X) ? min.X : x; double y = value.Y; y = (y > max.Y) ? max.Y : y; y = (y < min.Y) ? min.Y : y; double z = value.Z; z = (z > max.Z) ? max.Z : z; z = (z < min.Z) ? min.Z : z; double w = value.W; w = (w > max.W) ? max.W : w; w = (w < min.W) ? min.W : w; result = new Double4(x, y, z, w); } /// /// Restricts a value to be within a specified range. /// /// The value to clamp. /// The minimum value. /// The maximum value. /// The clamped value. public static Double4 Clamp(Double4 value, Double4 min, Double4 max) { Double4 result; Clamp(ref value, ref min, ref max, out result); return result; } /// /// Calculates the distance between two vectors. /// /// The first vector. /// The second vector. /// When the method completes, contains the distance between the two vectors. /// /// may be preferred when only the relative distance is needed /// and speed is of the essence. /// public static void Distance(ref Double4 value1, ref Double4 value2, out double result) { double x = value1.X - value2.X; double y = value1.Y - value2.Y; double z = value1.Z - value2.Z; double w = value1.W - value2.W; result = (double)Math.Sqrt((x * x) + (y * y) + (z * z) + (w * w)); } /// /// Calculates the distance between two vectors. /// /// The first vector. /// The second vector. /// The distance between the two vectors. /// /// may be preferred when only the relative distance is needed /// and speed is of the essence. /// public static double Distance(Double4 value1, Double4 value2) { double x = value1.X - value2.X; double y = value1.Y - value2.Y; double z = value1.Z - value2.Z; double w = value1.W - value2.W; return (double)Math.Sqrt((x * x) + (y * y) + (z * z) + (w * w)); } /// /// Calculates the squared distance between two vectors. /// /// The first vector. /// The second vector. /// When the method completes, contains the squared distance between the two vectors. /// Distance squared is the value before taking the square root. /// Distance squared can often be used in place of distance if relative comparisons are being made. /// For example, consider three points A, B, and C. To determine whether B or C is further from A, /// compare the distance between A and B to the distance between A and C. Calculating the two distances /// involves two square roots, which are computationally expensive. However, using distance squared /// provides the same information and avoids calculating two square roots. /// public static void DistanceSquared(ref Double4 value1, ref Double4 value2, out double result) { double x = value1.X - value2.X; double y = value1.Y - value2.Y; double z = value1.Z - value2.Z; double w = value1.W - value2.W; result = (x * x) + (y * y) + (z * z) + (w * w); } /// /// Calculates the squared distance between two vectors. /// /// The first vector. /// The second vector. /// The squared distance between the two vectors. /// Distance squared is the value before taking the square root. /// Distance squared can often be used in place of distance if relative comparisons are being made. /// For example, consider three points A, B, and C. To determine whether B or C is further from A, /// compare the distance between A and B to the distance between A and C. Calculating the two distances /// involves two square roots, which are computationally expensive. However, using distance squared /// provides the same information and avoids calculating two square roots. /// public static double DistanceSquared(Double4 value1, Double4 value2) { double x = value1.X - value2.X; double y = value1.Y - value2.Y; double z = value1.Z - value2.Z; double w = value1.W - value2.W; return (x * x) + (y * y) + (z * z) + (w * w); } /// /// Calculates the dot product of two vectors. /// /// First source vector /// Second source vector. /// When the method completes, contains the dot product of the two vectors. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Dot(ref Double4 left, ref Double4 right, out double result) { result = (left.X * right.X) + (left.Y * right.Y) + (left.Z * right.Z) + (left.W * right.W); } /// /// Calculates the dot product of two vectors. /// /// First source vector. /// Second source vector. /// The dot product of the two vectors. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Dot(Double4 left, Double4 right) { return (left.X * right.X) + (left.Y * right.Y) + (left.Z * right.Z) + (left.W * right.W); } /// /// Converts the vector into a unit vector. /// /// The vector to normalize. /// When the method completes, contains the normalized vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Normalize(ref Double4 value, out Double4 result) { Double4 temp = value; result = temp; result.Normalize(); } /// /// Converts the vector into a unit vector. /// /// The vector to normalize. /// The normalized vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Double4 Normalize(Double4 value) { value.Normalize(); return value; } /// /// Performs a linear interpolation between two vectors. /// /// Start vector. /// End vector. /// Value between 0 and 1 indicating the weight of . /// When the method completes, contains the linear interpolation of the two vectors. /// /// This method performs the linear interpolation based on the following formula. /// start + (end - start) * amount /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. /// public static void Lerp(ref Double4 start, ref Double4 end, double amount, out Double4 result) { result.X = start.X + ((end.X - start.X) * amount); result.Y = start.Y + ((end.Y - start.Y) * amount); result.Z = start.Z + ((end.Z - start.Z) * amount); result.W = start.W + ((end.W - start.W) * amount); } /// /// Performs a linear interpolation between two vectors. /// /// Start vector. /// End vector. /// Value between 0 and 1 indicating the weight of . /// The linear interpolation of the two vectors. /// /// This method performs the linear interpolation based on the following formula. /// start + (end - start) * amount /// Passing a value of 0 will cause to be returned; a value of 1 will cause to be returned. /// public static Double4 Lerp(Double4 start, Double4 end, double amount) { Double4 result; Lerp(ref start, ref end, amount, out result); return result; } /// /// Performs a cubic interpolation between two vectors. /// /// Start vector. /// End vector. /// Value between 0 and 1 indicating the weight of . /// When the method completes, contains the cubic interpolation of the two vectors. public static void SmoothStep(ref Double4 start, ref Double4 end, double amount, out Double4 result) { amount = (amount > 1.0) ? 1.0 : ((amount < 0.0) ? 0.0 : amount); amount = (amount * amount) * (3.0f - (2.0f * amount)); result.X = start.X + ((end.X - start.X) * amount); result.Y = start.Y + ((end.Y - start.Y) * amount); result.Z = start.Z + ((end.Z - start.Z) * amount); result.W = start.W + ((end.W - start.W) * amount); } /// /// Performs a cubic interpolation between two vectors. /// /// Start vector. /// End vector. /// Value between 0 and 1 indicating the weight of . /// The cubic interpolation of the two vectors. public static Double4 SmoothStep(Double4 start, Double4 end, double amount) { Double4 result; SmoothStep(ref start, ref end, amount, out result); return result; } /// /// Performs a Hermite spline interpolation. /// /// First source position vector. /// First source tangent vector. /// Second source position vector. /// Second source tangent vector. /// Weighting factor. /// When the method completes, contains the result of the Hermite spline interpolation. public static void Hermite(ref Double4 value1, ref Double4 tangent1, ref Double4 value2, ref Double4 tangent2, double amount, out Double4 result) { double squared = amount * amount; double cubed = amount * squared; double part1 = ((2.0f * cubed) - (3.0f * squared)) + 1.0; double part2 = (-2.0f * cubed) + (3.0f * squared); double part3 = (cubed - (2.0f * squared)) + amount; double part4 = cubed - squared; result = new Double4((((value1.X * part1) + (value2.X * part2)) + (tangent1.X * part3)) + (tangent2.X * part4), (((value1.Y * part1) + (value2.Y * part2)) + (tangent1.Y * part3)) + (tangent2.Y * part4), (((value1.Z * part1) + (value2.Z * part2)) + (tangent1.Z * part3)) + (tangent2.Z * part4), (((value1.W * part1) + (value2.W * part2)) + (tangent1.W * part3)) + (tangent2.W * part4)); } /// /// Performs a Hermite spline interpolation. /// /// First source position vector. /// First source tangent vector. /// Second source position vector. /// Second source tangent vector. /// Weighting factor. /// The result of the Hermite spline interpolation. public static Double4 Hermite(Double4 value1, Double4 tangent1, Double4 value2, Double4 tangent2, double amount) { Double4 result; Hermite(ref value1, ref tangent1, ref value2, ref tangent2, amount, out result); return result; } /// /// Performs a Catmull-Rom interpolation using the specified positions. /// /// The first position in the interpolation. /// The second position in the interpolation. /// The third position in the interpolation. /// The fourth position in the interpolation. /// Weighting factor. /// When the method completes, contains the result of the Catmull-Rom interpolation. public static void CatmullRom(ref Double4 value1, ref Double4 value2, ref Double4 value3, ref Double4 value4, double amount, out Double4 result) { double squared = amount * amount; double cubed = amount * squared; result.X = 0.5f * ((((2.0f * value2.X) + ((-value1.X + value3.X) * amount)) + (((((2.0f * value1.X) - (5.0f * value2.X)) + (4.0f * value3.X)) - value4.X) * squared)) + ((((-value1.X + (3.0f * value2.X)) - (3.0f * value3.X)) + value4.X) * cubed)); result.Y = 0.5f * ((((2.0f * value2.Y) + ((-value1.Y + value3.Y) * amount)) + (((((2.0f * value1.Y) - (5.0f * value2.Y)) + (4.0f * value3.Y)) - value4.Y) * squared)) + ((((-value1.Y + (3.0f * value2.Y)) - (3.0f * value3.Y)) + value4.Y) * cubed)); result.Z = 0.5f * ((((2.0f * value2.Z) + ((-value1.Z + value3.Z) * amount)) + (((((2.0f * value1.Z) - (5.0f * value2.Z)) + (4.0f * value3.Z)) - value4.Z) * squared)) + ((((-value1.Z + (3.0f * value2.Z)) - (3.0f * value3.Z)) + value4.Z) * cubed)); result.W = 0.5f * ((((2.0f * value2.W) + ((-value1.W + value3.W) * amount)) + (((((2.0f * value1.W) - (5.0f * value2.W)) + (4.0f * value3.W)) - value4.W) * squared)) + ((((-value1.W + (3.0f * value2.W)) - (3.0f * value3.W)) + value4.W) * cubed)); } /// /// Performs a Catmull-Rom interpolation using the specified positions. /// /// The first position in the interpolation. /// The second position in the interpolation. /// The third position in the interpolation. /// The fourth position in the interpolation. /// Weighting factor. /// A vector that is the result of the Catmull-Rom interpolation. public static Double4 CatmullRom(Double4 value1, Double4 value2, Double4 value3, Double4 value4, double amount) { Double4 result; CatmullRom(ref value1, ref value2, ref value3, ref value4, amount, out result); return result; } /// /// Returns a vector containing the smallest components of the specified vectors. /// /// The first source vector. /// The second source vector. /// When the method completes, contains an new vector composed of the largest components of the source vectors. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Max(ref Double4 left, ref Double4 right, out Double4 result) { result.X = (left.X > right.X) ? left.X : right.X; result.Y = (left.Y > right.Y) ? left.Y : right.Y; result.Z = (left.Z > right.Z) ? left.Z : right.Z; result.W = (left.W > right.W) ? left.W : right.W; } /// /// Returns a vector containing the largest components of the specified vectors. /// /// The first source vector. /// The second source vector. /// A vector containing the largest components of the source vectors. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Double4 Max(Double4 left, Double4 right) { Double4 result; Max(ref left, ref right, out result); return result; } /// /// Returns a vector containing the smallest components of the specified vectors. /// /// The first source vector. /// The second source vector. /// When the method completes, contains an new vector composed of the smallest components of the source vectors. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Min(ref Double4 left, ref Double4 right, out Double4 result) { result.X = (left.X < right.X) ? left.X : right.X; result.Y = (left.Y < right.Y) ? left.Y : right.Y; result.Z = (left.Z < right.Z) ? left.Z : right.Z; result.W = (left.W < right.W) ? left.W : right.W; } /// /// Returns a vector containing the smallest components of the specified vectors. /// /// The first source vector. /// The second source vector. /// A vector containing the smallest components of the source vectors. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Double4 Min(Double4 left, Double4 right) { Double4 result; Min(ref left, ref right, out result); return result; } /// /// Orthogonalizes a list of vectors. /// /// The list of orthogonalized vectors. /// The list of vectors to orthogonalize. /// /// Orthogonalization is the process of making all vectors orthogonal to each other. This /// means that any given vector in the list will be orthogonal to any other given vector in the /// list. /// Because this method uses the modified Gram-Schmidt process, the resulting vectors /// tend to be numerically unstable. The numeric stability decreases according to the vectors /// position in the list so that the first vector is the most stable and the last vector is the /// least stable. /// /// Thrown when or is null. /// Thrown when is shorter in length than . public static void Orthogonalize(Double4[] destination, params Double4[] source) { //Uses the modified Gram-Schmidt process. //q1 = m1 //q2 = m2 - ((q1 ⋅ m2) / (q1 ⋅ q1)) * q1 //q3 = m3 - ((q1 ⋅ m3) / (q1 ⋅ q1)) * q1 - ((q2 ⋅ m3) / (q2 ⋅ q2)) * q2 //q4 = m4 - ((q1 ⋅ m4) / (q1 ⋅ q1)) * q1 - ((q2 ⋅ m4) / (q2 ⋅ q2)) * q2 - ((q3 ⋅ m4) / (q3 ⋅ q3)) * q3 //q5 = ... if (source == null) throw new ArgumentNullException("source"); if (destination == null) throw new ArgumentNullException("destination"); if (destination.Length < source.Length) throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); for (int i = 0; i < source.Length; ++i) { Double4 newvector = source[i]; for (int r = 0; r < i; ++r) { newvector -= (Double4.Dot(destination[r], newvector) / Double4.Dot(destination[r], destination[r])) * destination[r]; } destination[i] = newvector; } } /// /// Orthonormalizes a list of vectors. /// /// The list of orthonormalized vectors. /// The list of vectors to orthonormalize. /// /// Orthonormalization is the process of making all vectors orthogonal to each /// other and making all vectors of unit length. This means that any given vector will /// be orthogonal to any other given vector in the list. /// Because this method uses the modified Gram-Schmidt process, the resulting vectors /// tend to be numerically unstable. The numeric stability decreases according to the vectors /// position in the list so that the first vector is the most stable and the last vector is the /// least stable. /// /// Thrown when or is null. /// Thrown when is shorter in length than . public static void Orthonormalize(Double4[] destination, params Double4[] source) { //Uses the modified Gram-Schmidt process. //Because we are making unit vectors, we can optimize the math for orthogonalization //and simplify the projection operation to remove the division. //q1 = m1 / |m1| //q2 = (m2 - (q1 ⋅ m2) * q1) / |m2 - (q1 ⋅ m2) * q1| //q3 = (m3 - (q1 ⋅ m3) * q1 - (q2 ⋅ m3) * q2) / |m3 - (q1 ⋅ m3) * q1 - (q2 ⋅ m3) * q2| //q4 = (m4 - (q1 ⋅ m4) * q1 - (q2 ⋅ m4) * q2 - (q3 ⋅ m4) * q3) / |m4 - (q1 ⋅ m4) * q1 - (q2 ⋅ m4) * q2 - (q3 ⋅ m4) * q3| //q5 = ... if (source == null) throw new ArgumentNullException("source"); if (destination == null) throw new ArgumentNullException("destination"); if (destination.Length < source.Length) throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); for (int i = 0; i < source.Length; ++i) { Double4 newvector = source[i]; for (int r = 0; r < i; ++r) { newvector -= Double4.Dot(destination[r], newvector) * destination[r]; } newvector.Normalize(); destination[i] = newvector; } } /// /// Transforms a 4D vector by the given rotation. /// /// The vector to rotate. /// The rotation to apply. /// When the method completes, contains the transformed . public static void Transform(ref Double4 vector, ref Quaternion rotation, out Double4 result) { double x = rotation.X + rotation.X; double y = rotation.Y + rotation.Y; double z = rotation.Z + rotation.Z; double wx = rotation.W * x; double wy = rotation.W * y; double wz = rotation.W * z; double xx = rotation.X * x; double xy = rotation.X * y; double xz = rotation.X * z; double yy = rotation.Y * y; double yz = rotation.Y * z; double zz = rotation.Z * z; result = new Double4( ((vector.X * ((1.0 - yy) - zz)) + (vector.Y * (xy - wz))) + (vector.Z * (xz + wy)), ((vector.X * (xy + wz)) + (vector.Y * ((1.0 - xx) - zz))) + (vector.Z * (yz - wx)), ((vector.X * (xz - wy)) + (vector.Y * (yz + wx))) + (vector.Z * ((1.0 - xx) - yy)), vector.W); } /// /// Transforms a 4D vector by the given rotation. /// /// The vector to rotate. /// The rotation to apply. /// The transformed . public static Double4 Transform(Double4 vector, Quaternion rotation) { Double4 result; Transform(ref vector, ref rotation, out result); return result; } /// /// Transforms an array of vectors by the given rotation. /// /// The array of vectors to transform. /// The rotation to apply. /// The array for which the transformed vectors are stored. /// This array may be the same array as . /// Thrown when or is null. /// Thrown when is shorter in length than . public static void Transform(Double4[] source, ref Quaternion rotation, Double4[] destination) { if (source == null) throw new ArgumentNullException("source"); if (destination == null) throw new ArgumentNullException("destination"); if (destination.Length < source.Length) throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); double x = rotation.X + rotation.X; double y = rotation.Y + rotation.Y; double z = rotation.Z + rotation.Z; double wx = rotation.W * x; double wy = rotation.W * y; double wz = rotation.W * z; double xx = rotation.X * x; double xy = rotation.X * y; double xz = rotation.X * z; double yy = rotation.Y * y; double yz = rotation.Y * z; double zz = rotation.Z * z; double num1 = ((1.0 - yy) - zz); double num2 = (xy - wz); double num3 = (xz + wy); double num4 = (xy + wz); double num5 = ((1.0 - xx) - zz); double num6 = (yz - wx); double num7 = (xz - wy); double num8 = (yz + wx); double num9 = ((1.0 - xx) - yy); for (int i = 0; i < source.Length; ++i) { destination[i] = new Double4( ((source[i].X * num1) + (source[i].Y * num2)) + (source[i].Z * num3), ((source[i].X * num4) + (source[i].Y * num5)) + (source[i].Z * num6), ((source[i].X * num7) + (source[i].Y * num8)) + (source[i].Z * num9), source[i].W); } } /// /// Transforms a 4D vector by the given . /// /// The source vector. /// The transformation . /// When the method completes, contains the transformed . public static void Transform(ref Double4 vector, ref Matrix transform, out Double4 result) { result = new Double4( (vector.X * transform.M11) + (vector.Y * transform.M21) + (vector.Z * transform.M31) + (vector.W * transform.M41), (vector.X * transform.M12) + (vector.Y * transform.M22) + (vector.Z * transform.M32) + (vector.W * transform.M42), (vector.X * transform.M13) + (vector.Y * transform.M23) + (vector.Z * transform.M33) + (vector.W * transform.M43), (vector.X * transform.M14) + (vector.Y * transform.M24) + (vector.Z * transform.M34) + (vector.W * transform.M44)); } /// /// Transforms a 4D vector by the given . /// /// The source vector. /// The transformation . /// The transformed . public static Double4 Transform(Double4 vector, Matrix transform) { Double4 result; Transform(ref vector, ref transform, out result); return result; } /// /// Transforms an array of 4D vectors by the given . /// /// The array of vectors to transform. /// The transformation . /// The array for which the transformed vectors are stored. /// This array may be the same array as . /// Thrown when or is null. /// Thrown when is shorter in length than . public static void Transform(Double4[] source, ref Matrix transform, Double4[] destination) { if (source == null) throw new ArgumentNullException("source"); if (destination == null) throw new ArgumentNullException("destination"); if (destination.Length < source.Length) throw new ArgumentOutOfRangeException("destination", "The destination array must be of same length or larger length than the source array."); for (int i = 0; i < source.Length; ++i) { Transform(ref source[i], ref transform, out destination[i]); } } /// /// Adds two vectors. /// /// The first vector to add. /// The second vector to add. /// The sum of the two vectors. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Double4 operator +(Double4 left, Double4 right) { return new Double4(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W); } /// /// Assert a vector (return it unchanged). /// /// The vector to assert (unchange). /// The asserted (unchanged) vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Double4 operator +(Double4 value) { return value; } /// /// Subtracts two vectors. /// /// The first vector to subtract. /// The second vector to subtract. /// The difference of the two vectors. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Double4 operator -(Double4 left, Double4 right) { return new Double4(left.X - right.X, left.Y - right.Y, left.Z - right.Z, left.W - right.W); } /// /// Reverses the direction of a given vector. /// /// The vector to negate. /// A vector facing in the opposite direction. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Double4 operator -(Double4 value) { return new Double4(-value.X, -value.Y, -value.Z, -value.W); } /// /// Scales a vector by the given value. /// /// The vector to scale. /// The amount by which to scale the vector. /// The scaled vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Double4 operator *(double scale, Double4 value) { return new Double4(value.X * scale, value.Y * scale, value.Z * scale, value.W * scale); } /// /// Scales a vector by the given value. /// /// The vector to scale. /// The amount by which to scale the vector. /// The scaled vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Double4 operator *(Double4 value, double scale) { return new Double4(value.X * scale, value.Y * scale, value.Z * scale, value.W * scale); } /// /// Modulates a vector with another by performing component-wise multiplication. /// /// The first vector to multiply. /// The second vector to multiply. /// The multiplication of the two vectors. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Double4 operator *(Double4 left, Double4 right) { return new Double4(left.X * right.X, left.Y * right.Y, left.Z * right.Z, left.W * right.W); } /// /// Scales a vector by the given value. /// /// The vector to scale. /// The amount by which to scale the vector. /// The scaled vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Double4 operator /(Double4 value, double scale) { return new Double4(value.X / scale, value.Y / scale, value.Z / scale, value.W / scale); } /// /// Divides a numerator by a vector. /// /// The numerator. /// The value. /// The scaled vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Double4 operator /(double numerator, Double4 value) { return new Double4(numerator / value.X, numerator / value.Y, numerator / value.Z, numerator / value.W); } /// /// Divides a vector by the given vector, component-wise. /// /// The vector to scale. /// The by. /// The scaled vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Double4 operator /(Double4 value, Double4 by) { return new Double4(value.X / by.X, value.Y / by.Y, value.Z / by.Z, value.W / by.W); } /// /// Tests for equality between two objects. /// /// The first value to compare. /// The second value to compare. /// true if has the same value as ; otherwise, false. public static bool operator ==(Double4 left, Double4 right) { return left.Equals(right); } /// /// Tests for inequality between two objects. /// /// The first value to compare. /// The second value to compare. /// true if has a different value than ; otherwise, false. public static bool operator !=(Double4 left, Double4 right) { return !left.Equals(right); } /// /// Performs an explicit conversion from to . /// /// The value. /// The result of the conversion. public static explicit operator Vector4(Double4 value) { return new Vector4((float)value.X, (float)value.Y, (float)value.Z, (float)value.W); } /// /// Performs an implicit conversion from to . /// /// The value. /// The result of the conversion. public static implicit operator Double4(Vector4 value) { return new Double4(value); } /// /// Performs an explicit conversion from to . /// /// The value. /// The result of the conversion. public static explicit operator Half4(Double4 value) { return new Half4((Half)value.X, (Half)value.Y, (Half)value.Z, (Half)value.W); } /// /// Performs an explicit conversion from to . /// /// The value. /// The result of the conversion. public static explicit operator Double4(Half4 value) { return new Double4(value.X, value.Y, value.Z, value.W); } /// /// Performs an explicit conversion from to . /// /// The value. /// The result of the conversion. public static explicit operator Double2(Double4 value) { return new Double2(value.X, value.Y); } /// /// Performs an explicit conversion from to . /// /// The value. /// The result of the conversion. public static explicit operator Double3(Double4 value) { return new Double3(value.X, value.Y, value.Z); } /// /// Returns a that represents this instance. /// /// /// A that represents this instance. /// public override string ToString() { return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1} Z:{2} W:{3}", X, Y, Z, W); } /// /// Returns a that represents this instance. /// /// The format. /// /// A that represents this instance. /// public string ToString(string format) { if (format == null) return ToString(); return string.Format(CultureInfo.CurrentCulture, "X:{0} Y:{1} Z:{2} W:{3}", X.ToString(format, CultureInfo.CurrentCulture), Y.ToString(format, CultureInfo.CurrentCulture), Z.ToString(format, CultureInfo.CurrentCulture), W.ToString(format, CultureInfo.CurrentCulture)); } /// /// Returns a that represents this instance. /// /// The format provider. /// /// A that represents this instance. /// public string ToString(IFormatProvider formatProvider) { return string.Format(formatProvider, "X:{0} Y:{1} Z:{2} W:{3}", X, Y, Z, W); } /// /// Returns a that represents this instance. /// /// The format. /// The format provider. /// /// A that represents this instance. /// public string ToString(string format, IFormatProvider formatProvider) { if (format == null) ToString(formatProvider); return string.Format(formatProvider, "X:{0} Y:{1} Z:{2} W:{3}", X.ToString(format, formatProvider), Y.ToString(format, formatProvider), Z.ToString(format, formatProvider), W.ToString(format, formatProvider)); } /// /// Returns a hash code for this instance. /// /// /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. /// public override int GetHashCode() { return X.GetHashCode() + Y.GetHashCode() + Z.GetHashCode() + W.GetHashCode(); } /// /// Determines whether the specified is equal to this instance. /// /// The to compare with this instance. /// /// true if the specified is equal to this instance; otherwise, false. /// public bool Equals(Double4 other) { return ((double)Math.Abs(other.X - X) < MathUtil.ZeroTolerance && (double)Math.Abs(other.Y - Y) < MathUtil.ZeroTolerance && (double)Math.Abs(other.Z - Z) < MathUtil.ZeroTolerance && (double)Math.Abs(other.W - W) < MathUtil.ZeroTolerance); } /// /// Determines whether the specified is equal to this instance. /// /// The to compare with this instance. /// /// true if the specified is equal to this instance; otherwise, false. /// public override bool Equals(object value) { if (value == null) return false; if (value.GetType() != GetType()) return false; return Equals((Double4)value); } #if WPFInterop /// /// Performs an implicit conversion from to . /// /// The value. /// The result of the conversion. public static implicit operator System.Windows.Media.Media3D.Point4D(Double4 value) { return new System.Windows.Media.Media3D.Point4D(value.X, value.Y, value.Z, value.W); } /// /// Performs an explicit conversion from to . /// /// The value. /// The result of the conversion. public static explicit operator Double4(System.Windows.Media.Media3D.Point4D value) { return new Double4((double)value.X, (double)value.Y, (double)value.Z, (double)value.W); } #endif #if XnaInterop /// /// Performs an implicit conversion from to . /// /// The value. /// The result of the conversion. public static implicit operator Microsoft.Xna.Framework.Vector4(Double4 value) { return new Microsoft.Xna.Framework.Vector4(value.X, value.Y, value.Z, value.W); } /// /// Performs an implicit conversion from to . /// /// The value. /// The result of the conversion. public static implicit operator Double4(Microsoft.Xna.Framework.Vector4 value) { return new Double4(value.X, value.Y, value.Z, value.W); } #endif } }