// Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.Globalization; using System.Runtime.CompilerServices; using System.Text; namespace System.Numerics { /// /// A structure encapsulating four single precision floating point values and provides hardware accelerated methods. /// public partial struct Vector4 : IEquatable, IFormattable { #region Public Static Properties /// /// Returns the vector (0,0,0,0). /// public static Vector4 Zero { get { return new Vector4(); } } /// /// Returns the vector (1,1,1,1). /// public static Vector4 One { get { return new Vector4(1.0f, 1.0f, 1.0f, 1.0f); } } /// /// Returns the vector (1,0,0,0). /// public static Vector4 UnitX { get { return new Vector4(1.0f, 0.0f, 0.0f, 0.0f); } } /// /// Returns the vector (0,1,0,0). /// public static Vector4 UnitY { get { return new Vector4(0.0f, 1.0f, 0.0f, 0.0f); } } /// /// Returns the vector (0,0,1,0). /// public static Vector4 UnitZ { get { return new Vector4(0.0f, 0.0f, 1.0f, 0.0f); } } /// /// Returns the vector (0,0,0,1). /// public static Vector4 UnitW { get { return new Vector4(0.0f, 0.0f, 0.0f, 1.0f); } } #endregion Public Static Properties #region Public instance methods /// /// Returns the hash code for this instance. /// /// The hash code. public override int GetHashCode() { int hash = this.X.GetHashCode(); hash = HashCodeHelper.CombineHashCodes(hash, this.Y.GetHashCode()); hash = HashCodeHelper.CombineHashCodes(hash, this.Z.GetHashCode()); hash = HashCodeHelper.CombineHashCodes(hash, this.W.GetHashCode()); return hash; } /// /// Returns a boolean indicating whether the given Object is equal to this Vector4 instance. /// /// The Object to compare against. /// True if the Object is equal to this Vector4; False otherwise. [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object obj) { if (!(obj is Vector4)) return false; return Equals((Vector4)obj); } /// /// Returns a String representing this Vector4 instance. /// /// The string representation. public override string ToString() { return ToString("G", CultureInfo.CurrentCulture); } /// /// Returns a String representing this Vector4 instance, using the specified format to format individual elements. /// /// The format of individual elements. /// The string representation. public string ToString(string format) { return ToString(format, CultureInfo.CurrentCulture); } /// /// Returns a String representing this Vector4 instance, using the specified format to format individual elements /// and the given IFormatProvider. /// /// The format of individual elements. /// The format provider to use when formatting elements. /// The string representation. public string ToString(string format, IFormatProvider formatProvider) { StringBuilder sb = new StringBuilder(); string separator = NumberFormatInfo.GetInstance(formatProvider).NumberGroupSeparator; sb.Append('<'); sb.Append(this.X.ToString(format, formatProvider)); sb.Append(separator); sb.Append(' '); sb.Append(this.Y.ToString(format, formatProvider)); sb.Append(separator); sb.Append(' '); sb.Append(this.Z.ToString(format, formatProvider)); sb.Append(separator); sb.Append(' '); sb.Append(this.W.ToString(format, formatProvider)); sb.Append('>'); return sb.ToString(); } /// /// Returns the length of the vector. This operation is cheaper than Length(). /// /// The vector's length. [MethodImpl(MethodImplOptions.AggressiveInlining)] public float Length() { if (Vector.IsHardwareAccelerated) { float ls = Vector4.Dot(this, this); return (float)System.Math.Sqrt(ls); } else { float ls = X * X + Y * Y + Z * Z + W * W; return (float)Math.Sqrt((double)ls); } } /// /// Returns the length of the vector squared. /// /// The vector's length squared. [MethodImpl(MethodImplOptions.AggressiveInlining)] public float LengthSquared() { if (Vector.IsHardwareAccelerated) { return Vector4.Dot(this, this); } else { return X * X + Y * Y + Z * Z + W * W; } } #endregion Public Instance Methods #region Public Static Methods /// /// Returns the Euclidean distance between the two given points. /// /// The first point. /// The second point. /// The distance. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Distance(Vector4 value1, Vector4 value2) { if (Vector.IsHardwareAccelerated) { Vector4 difference = value1 - value2; float ls = Vector4.Dot(difference, difference); return (float)System.Math.Sqrt(ls); } else { float dx = value1.X - value2.X; float dy = value1.Y - value2.Y; float dz = value1.Z - value2.Z; float dw = value1.W - value2.W; float ls = dx * dx + dy * dy + dz * dz + dw * dw; return (float)Math.Sqrt((double)ls); } } /// /// Returns the Euclidean distance squared between the two given points. /// /// The first point. /// The second point. /// The distance squared. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float DistanceSquared(Vector4 value1, Vector4 value2) { if (Vector.IsHardwareAccelerated) { Vector4 difference = value1 - value2; return Vector4.Dot(difference, difference); } else { float dx = value1.X - value2.X; float dy = value1.Y - value2.Y; float dz = value1.Z - value2.Z; float dw = value1.W - value2.W; return dx * dx + dy * dy + dz * dz + dw * dw; } } /// /// Returns a vector with the same direction as the given vector, but with a length of 1. /// /// The vector to normalize. /// The normalized vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Normalize(Vector4 vector) { if (Vector.IsHardwareAccelerated) { float length = vector.Length(); return vector / length; } else { float ls = vector.X * vector.X + vector.Y * vector.Y + vector.Z * vector.Z + vector.W * vector.W; float invNorm = 1.0f / (float)Math.Sqrt((double)ls); return new Vector4( vector.X * invNorm, vector.Y * invNorm, vector.Z * invNorm, vector.W * invNorm); } } /// /// Restricts a vector between a min and max value. /// /// The source vector. /// The minimum value. /// The maximum value. /// The restricted vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Clamp(Vector4 value1, Vector4 min, Vector4 max) { // This compare order is very important!!! // We must follow HLSL behavior in the case user specified min value is bigger than max value. float x = value1.X; x = (x > max.X) ? max.X : x; x = (x < min.X) ? min.X : x; float y = value1.Y; y = (y > max.Y) ? max.Y : y; y = (y < min.Y) ? min.Y : y; float z = value1.Z; z = (z > max.Z) ? max.Z : z; z = (z < min.Z) ? min.Z : z; float w = value1.W; w = (w > max.W) ? max.W : w; w = (w < min.W) ? min.W : w; return new Vector4(x, y, z, w); } /// /// Linearly interpolates between two vectors based on the given weighting. /// /// The first source vector. /// The second source vector. /// Value between 0 and 1 indicating the weight of the second source vector. /// The interpolated vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Lerp(Vector4 value1, Vector4 value2, float amount) { return new Vector4( value1.X + (value2.X - value1.X) * amount, value1.Y + (value2.Y - value1.Y) * amount, value1.Z + (value2.Z - value1.Z) * amount, value1.W + (value2.W - value1.W) * amount); } /// /// Transforms a vector by the given matrix. /// /// The source vector. /// The transformation matrix. /// The transformed vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Transform(Vector2 position, Matrix4x4 matrix) { return new Vector4( position.X * matrix.M11 + position.Y * matrix.M21 + matrix.M41, position.X * matrix.M12 + position.Y * matrix.M22 + matrix.M42, position.X * matrix.M13 + position.Y * matrix.M23 + matrix.M43, position.X * matrix.M14 + position.Y * matrix.M24 + matrix.M44); } /// /// Transforms a vector by the given matrix. /// /// The source vector. /// The transformation matrix. /// The transformed vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Transform(Vector3 position, Matrix4x4 matrix) { return new Vector4( position.X * matrix.M11 + position.Y * matrix.M21 + position.Z * matrix.M31 + matrix.M41, position.X * matrix.M12 + position.Y * matrix.M22 + position.Z * matrix.M32 + matrix.M42, position.X * matrix.M13 + position.Y * matrix.M23 + position.Z * matrix.M33 + matrix.M43, position.X * matrix.M14 + position.Y * matrix.M24 + position.Z * matrix.M34 + matrix.M44); } /// /// Transforms a vector by the given matrix. /// /// The source vector. /// The transformation matrix. /// The transformed vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Transform(Vector4 vector, Matrix4x4 matrix) { return new Vector4( vector.X * matrix.M11 + vector.Y * matrix.M21 + vector.Z * matrix.M31 + vector.W * matrix.M41, vector.X * matrix.M12 + vector.Y * matrix.M22 + vector.Z * matrix.M32 + vector.W * matrix.M42, vector.X * matrix.M13 + vector.Y * matrix.M23 + vector.Z * matrix.M33 + vector.W * matrix.M43, vector.X * matrix.M14 + vector.Y * matrix.M24 + vector.Z * matrix.M34 + vector.W * matrix.M44); } /// /// Transforms a vector by the given Quaternion rotation value. /// /// The source vector to be rotated. /// The rotation to apply. /// The transformed vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Transform(Vector2 value, Quaternion rotation) { float x2 = rotation.X + rotation.X; float y2 = rotation.Y + rotation.Y; float z2 = rotation.Z + rotation.Z; float wx2 = rotation.W * x2; float wy2 = rotation.W * y2; float wz2 = rotation.W * z2; float xx2 = rotation.X * x2; float xy2 = rotation.X * y2; float xz2 = rotation.X * z2; float yy2 = rotation.Y * y2; float yz2 = rotation.Y * z2; float zz2 = rotation.Z * z2; return new Vector4( value.X * (1.0f - yy2 - zz2) + value.Y * (xy2 - wz2), value.X * (xy2 + wz2) + value.Y * (1.0f - xx2 - zz2), value.X * (xz2 - wy2) + value.Y * (yz2 + wx2), 1.0f); } /// /// Transforms a vector by the given Quaternion rotation value. /// /// The source vector to be rotated. /// The rotation to apply. /// The transformed vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Transform(Vector3 value, Quaternion rotation) { float x2 = rotation.X + rotation.X; float y2 = rotation.Y + rotation.Y; float z2 = rotation.Z + rotation.Z; float wx2 = rotation.W * x2; float wy2 = rotation.W * y2; float wz2 = rotation.W * z2; float xx2 = rotation.X * x2; float xy2 = rotation.X * y2; float xz2 = rotation.X * z2; float yy2 = rotation.Y * y2; float yz2 = rotation.Y * z2; float zz2 = rotation.Z * z2; return new Vector4( value.X * (1.0f - yy2 - zz2) + value.Y * (xy2 - wz2) + value.Z * (xz2 + wy2), value.X * (xy2 + wz2) + value.Y * (1.0f - xx2 - zz2) + value.Z * (yz2 - wx2), value.X * (xz2 - wy2) + value.Y * (yz2 + wx2) + value.Z * (1.0f - xx2 - yy2), 1.0f); } /// /// Transforms a vector by the given Quaternion rotation value. /// /// The source vector to be rotated. /// The rotation to apply. /// The transformed vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Transform(Vector4 value, Quaternion rotation) { float x2 = rotation.X + rotation.X; float y2 = rotation.Y + rotation.Y; float z2 = rotation.Z + rotation.Z; float wx2 = rotation.W * x2; float wy2 = rotation.W * y2; float wz2 = rotation.W * z2; float xx2 = rotation.X * x2; float xy2 = rotation.X * y2; float xz2 = rotation.X * z2; float yy2 = rotation.Y * y2; float yz2 = rotation.Y * z2; float zz2 = rotation.Z * z2; return new Vector4( value.X * (1.0f - yy2 - zz2) + value.Y * (xy2 - wz2) + value.Z * (xz2 + wy2), value.X * (xy2 + wz2) + value.Y * (1.0f - xx2 - zz2) + value.Z * (yz2 - wx2), value.X * (xz2 - wy2) + value.Y * (yz2 + wx2) + value.Z * (1.0f - xx2 - yy2), value.W); } #endregion Public Static Methods #region Public operator methods // All these methods should be inlines as they are implemented // over JIT intrinsics /// /// Adds two vectors together. /// /// The first source vector. /// The second source vector. /// The summed vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Add(Vector4 left, Vector4 right) { return left + right; } /// /// Subtracts the second vector from the first. /// /// The first source vector. /// The second source vector. /// The difference vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Subtract(Vector4 left, Vector4 right) { return left - right; } /// /// Multiplies two vectors together. /// /// The first source vector. /// The second source vector. /// The product vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Multiply(Vector4 left, Vector4 right) { return left * right; } /// /// Multiplies a vector by the given scalar. /// /// The source vector. /// The scalar value. /// The scaled vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Multiply(Vector4 left, Single right) { return left * new Vector4(right, right, right, right); } /// /// Multiplies a vector by the given scalar. /// /// The scalar value. /// The source vector. /// The scaled vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Multiply(Single left, Vector4 right) { return new Vector4(left, left, left, left) * right; } /// /// Divides the first vector by the second. /// /// The first source vector. /// The second source vector. /// The vector resulting from the division. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Divide(Vector4 left, Vector4 right) { return left / right; } /// /// Divides the vector by the given scalar. /// /// The source vector. /// The scalar value. /// The result of the division. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Divide(Vector4 left, Single divisor) { return left / divisor; } /// /// Negates a given vector. /// /// The source vector. /// The negated vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector4 Negate(Vector4 value) { return -value; } #endregion Public operator methods } }