// 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 three single precision floating point values and provides hardware accelerated methods. /// public partial struct Vector3 : IEquatable, IFormattable { #region Public Static Properties /// /// Returns the vector (0,0,0). /// public static Vector3 Zero { get { return new Vector3(); } } /// /// Returns the vector (1,1,1). /// public static Vector3 One { get { return new Vector3(1.0f, 1.0f, 1.0f); } } /// /// Returns the vector (1,0,0). /// public static Vector3 UnitX { get { return new Vector3(1.0f, 0.0f, 0.0f); } } /// /// Returns the vector (0,1,0). /// public static Vector3 UnitY { get { return new Vector3(0.0f, 1.0f, 0.0f); } } /// /// Returns the vector (0,0,1). /// public static Vector3 UnitZ { get { return new Vector3(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()); return hash; } /// /// Returns a boolean indicating whether the given Object is equal to this Vector3 instance. /// /// The Object to compare against. /// True if the Object is equal to this Vector3; False otherwise. [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object obj) { if (!(obj is Vector3)) return false; return Equals((Vector3)obj); } /// /// Returns a String representing this Vector3 instance. /// /// The string representation. public override string ToString() { return ToString("G", CultureInfo.CurrentCulture); } /// /// Returns a String representing this Vector3 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 Vector3 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(((IFormattable)this.X).ToString(format, formatProvider)); sb.Append(separator); sb.Append(' '); sb.Append(((IFormattable)this.Y).ToString(format, formatProvider)); sb.Append(separator); sb.Append(' '); sb.Append(((IFormattable)this.Z).ToString(format, formatProvider)); sb.Append('>'); return sb.ToString(); } /// /// Returns the length of the vector. /// /// The vector's length. [MethodImpl(MethodImplOptions.AggressiveInlining)] public float Length() { if (Vector.IsHardwareAccelerated) { float ls = Vector3.Dot(this, this); return (float)System.Math.Sqrt(ls); } else { float ls = X * X + Y * Y + Z * Z; return (float)System.Math.Sqrt(ls); } } /// /// Returns the length of the vector squared. This operation is cheaper than Length(). /// /// The vector's length squared. [MethodImpl(MethodImplOptions.AggressiveInlining)] public float LengthSquared() { if (Vector.IsHardwareAccelerated) { return Vector3.Dot(this, this); } else { return X * X + Y * Y + Z * Z; } } #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(Vector3 value1, Vector3 value2) { if (Vector.IsHardwareAccelerated) { Vector3 difference = value1 - value2; float ls = Vector3.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 ls = dx * dx + dy * dy + dz * dz; return (float)System.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(Vector3 value1, Vector3 value2) { if (Vector.IsHardwareAccelerated) { Vector3 difference = value1 - value2; return Vector3.Dot(difference, difference); } else { float dx = value1.X - value2.X; float dy = value1.Y - value2.Y; float dz = value1.Z - value2.Z; return dx * dx + dy * dy + dz * dz; } } /// /// 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 Vector3 Normalize(Vector3 value) { if (Vector.IsHardwareAccelerated) { float length = value.Length(); return value / length; } else { float ls = value.X * value.X + value.Y * value.Y + value.Z * value.Z; float length = (float)System.Math.Sqrt(ls); return new Vector3(value.X / length, value.Y / length, value.Z / length); } } /// /// Computes the cross product of two vectors. /// /// The first vector. /// The second vector. /// The cross product. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Cross(Vector3 vector1, Vector3 vector2) { return new Vector3( vector1.Y * vector2.Z - vector1.Z * vector2.Y, vector1.Z * vector2.X - vector1.X * vector2.Z, vector1.X * vector2.Y - vector1.Y * vector2.X); } /// /// Returns the reflection of a vector off a surface that has the specified normal. /// /// The source vector. /// The normal of the surface being reflected off. /// The reflected vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Reflect(Vector3 vector, Vector3 normal) { if (Vector.IsHardwareAccelerated) { float dot = Vector3.Dot(vector, normal); Vector3 temp = normal * dot * 2f; return vector - temp; } else { float dot = vector.X * normal.X + vector.Y * normal.Y + vector.Z * normal.Z; float tempX = normal.X * dot * 2f; float tempY = normal.Y * dot * 2f; float tempZ = normal.Z * dot * 2f; return new Vector3(vector.X - tempX, vector.Y - tempY, vector.Z - tempZ); } } /// /// 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 Vector3 Clamp(Vector3 value1, Vector3 min, Vector3 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; return new Vector3(x, y, z); } /// /// 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 Vector3 Lerp(Vector3 value1, Vector3 value2, float amount) { if (Vector.IsHardwareAccelerated) { Vector3 firstInfluence = value1 * (1f - amount); Vector3 secondInfluence = value2 * amount; return firstInfluence + secondInfluence; } else { return new Vector3( value1.X + (value2.X - value1.X) * amount, value1.Y + (value2.Y - value1.Y) * amount, value1.Z + (value2.Z - value1.Z) * amount); } } /// /// Transforms a vector by the given matrix. /// /// The source vector. /// The transformation matrix. /// The transformed vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Transform(Vector3 position, Matrix4x4 matrix) { return new Vector3( 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); } /// /// Transforms a vector normal by the given matrix. /// /// The source vector. /// The transformation matrix. /// The transformed vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 TransformNormal(Vector3 normal, Matrix4x4 matrix) { return new Vector3( normal.X * matrix.M11 + normal.Y * matrix.M21 + normal.Z * matrix.M31, normal.X * matrix.M12 + normal.Y * matrix.M22 + normal.Z * matrix.M32, normal.X * matrix.M13 + normal.Y * matrix.M23 + normal.Z * matrix.M33); } /// /// 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 Vector3 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 Vector3( 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)); } #endregion Public Static Methods #region Public operator methods // All these methods should be inlined 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 Vector3 Add(Vector3 left, Vector3 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 Vector3 Subtract(Vector3 left, Vector3 right) { return left - right; } /// /// Multiplies two vectors together. /// /// The first source vector. /// The second source vector. /// The product vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Multiply(Vector3 left, Vector3 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 Vector3 Multiply(Vector3 left, Single right) { return left * right; } /// /// Multiplies a vector by the given scalar. /// /// The scalar value. /// The source vector. /// The scaled vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Multiply(Single left, Vector3 right) { return 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 Vector3 Divide(Vector3 left, Vector3 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 Vector3 Divide(Vector3 left, Single divisor) { return left / divisor; } /// /// Negates a given vector. /// /// The source vector. /// The negated vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector3 Negate(Vector3 value) { return -value; } #endregion Public operator methods } }