|
// 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
{
/// <summary>
/// A structure encapsulating two single precision floating point values and provides hardware accelerated methods.
/// </summary>
public partial struct Vector2 : IEquatable<Vector2>, IFormattable
{
#region Public Static Properties
/// <summary>
/// Returns the vector (0,0).
/// </summary>
public static Vector2 Zero { get { return new Vector2(); } }
/// <summary>
/// Returns the vector (1,1).
/// </summary>
public static Vector2 One { get { return new Vector2(1.0f, 1.0f); } }
/// <summary>
/// Returns the vector (1,0).
/// </summary>
public static Vector2 UnitX { get { return new Vector2(1.0f, 0.0f); } }
/// <summary>
/// Returns the vector (0,1).
/// </summary>
public static Vector2 UnitY { get { return new Vector2(0.0f, 1.0f); } }
#endregion Public Static Properties
#region Public instance methods
/// <summary>
/// Returns the hash code for this instance.
/// </summary>
/// <returns>The hash code.</returns>
public override int GetHashCode()
{
int hash = this.X.GetHashCode();
hash = HashCodeHelper.CombineHashCodes(hash, this.Y.GetHashCode());
return hash;
}
/// <summary>
/// Returns a boolean indicating whether the given Object is equal to this Vector2 instance.
/// </summary>
/// <param name="obj">The Object to compare against.</param>
/// <returns>True if the Object is equal to this Vector2; False otherwise.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool Equals(object obj)
{
if (!(obj is Vector2))
return false;
return Equals((Vector2)obj);
}
/// <summary>
/// Returns a String representing this Vector2 instance.
/// </summary>
/// <returns>The string representation.</returns>
public override string ToString()
{
return ToString("G", CultureInfo.CurrentCulture);
}
/// <summary>
/// Returns a String representing this Vector2 instance, using the specified format to format individual elements.
/// </summary>
/// <param name="format">The format of individual elements.</param>
/// <returns>The string representation.</returns>
public string ToString(string format)
{
return ToString(format, CultureInfo.CurrentCulture);
}
/// <summary>
/// Returns a String representing this Vector2 instance, using the specified format to format individual elements
/// and the given IFormatProvider.
/// </summary>
/// <param name="format">The format of individual elements.</param>
/// <param name="formatProvider">The format provider to use when formatting elements.</param>
/// <returns>The string representation.</returns>
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('>');
return sb.ToString();
}
/// <summary>
/// Returns the length of the vector.
/// </summary>
/// <returns>The vector's length.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float Length()
{
if (Vector.IsHardwareAccelerated)
{
float ls = Vector2.Dot(this, this);
return (float)Math.Sqrt(ls);
}
else
{
float ls = X * X + Y * Y;
return (float)Math.Sqrt((double)ls);
}
}
/// <summary>
/// Returns the length of the vector squared. This operation is cheaper than Length().
/// </summary>
/// <returns>The vector's length squared.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float LengthSquared()
{
if (Vector.IsHardwareAccelerated)
{
return Vector2.Dot(this, this);
}
else
{
return X * X + Y * Y;
}
}
#endregion Public Instance Methods
#region Public Static Methods
/// <summary>
/// Returns the Euclidean distance between the two given points.
/// </summary>
/// <param name="value1">The first point.</param>
/// <param name="value2">The second point.</param>
/// <returns>The distance.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Distance(Vector2 value1, Vector2 value2)
{
if (Vector.IsHardwareAccelerated)
{
Vector2 difference = value1 - value2;
float ls = Vector2.Dot(difference, difference);
return (float)System.Math.Sqrt(ls);
}
else
{
float dx = value1.X - value2.X;
float dy = value1.Y - value2.Y;
float ls = dx * dx + dy * dy;
return (float)Math.Sqrt((double)ls);
}
}
/// <summary>
/// Returns the Euclidean distance squared between the two given points.
/// </summary>
/// <param name="value1">The first point.</param>
/// <param name="value2">The second point.</param>
/// <returns>The distance squared.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float DistanceSquared(Vector2 value1, Vector2 value2)
{
if (Vector.IsHardwareAccelerated)
{
Vector2 difference = value1 - value2;
return Vector2.Dot(difference, difference);
}
else
{
float dx = value1.X - value2.X;
float dy = value1.Y - value2.Y;
return dx * dx + dy * dy;
}
}
/// <summary>
/// Returns a vector with the same direction as the given vector, but with a length of 1.
/// </summary>
/// <param name="value">The vector to normalize.</param>
/// <returns>The normalized vector.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2 Normalize(Vector2 value)
{
if (Vector.IsHardwareAccelerated)
{
float length = value.Length();
return value / length;
}
else
{
float ls = value.X * value.X + value.Y * value.Y;
float invNorm = 1.0f / (float)Math.Sqrt((double)ls);
return new Vector2(
value.X * invNorm,
value.Y * invNorm);
}
}
/// <summary>
/// Returns the reflection of a vector off a surface that has the specified normal.
/// </summary>
/// <param name="vector">The source vector.</param>
/// <param name="normal">The normal of the surface being reflected off.</param>
/// <returns>The reflected vector.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2 Reflect(Vector2 vector, Vector2 normal)
{
if (Vector.IsHardwareAccelerated)
{
float dot = Vector2.Dot(vector, normal);
return vector - (2 * dot * normal);
}
else
{
float dot = vector.X * normal.X + vector.Y * normal.Y;
return new Vector2(
vector.X - 2.0f * dot * normal.X,
vector.Y - 2.0f * dot * normal.Y);
}
}
/// <summary>
/// Restricts a vector between a min and max value.
/// </summary>
/// <param name="value1">The source vector.</param>
/// <param name="min">The minimum value.</param>
/// <param name="max">The maximum value.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2 Clamp(Vector2 value1, Vector2 min, Vector2 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;
return new Vector2(x, y);
}
/// <summary>
/// Linearly interpolates between two vectors based on the given weighting.
/// </summary>
/// <param name="value1">The first source vector.</param>
/// <param name="value2">The second source vector.</param>
/// <param name="amount">Value between 0 and 1 indicating the weight of the second source vector.</param>
/// <returns>The interpolated vector.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2 Lerp(Vector2 value1, Vector2 value2, float amount)
{
return new Vector2(
value1.X + (value2.X - value1.X) * amount,
value1.Y + (value2.Y - value1.Y) * amount);
}
/// <summary>
/// Transforms a vector by the given matrix.
/// </summary>
/// <param name="position">The source vector.</param>
/// <param name="matrix">The transformation matrix.</param>
/// <returns>The transformed vector.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2 Transform(Vector2 position, Matrix3x2 matrix)
{
return new Vector2(
position.X * matrix.M11 + position.Y * matrix.M21 + matrix.M31,
position.X * matrix.M12 + position.Y * matrix.M22 + matrix.M32);
}
/// <summary>
/// Transforms a vector by the given matrix.
/// </summary>
/// <param name="position">The source vector.</param>
/// <param name="matrix">The transformation matrix.</param>
/// <returns>The transformed vector.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2 Transform(Vector2 position, Matrix4x4 matrix)
{
return new Vector2(
position.X * matrix.M11 + position.Y * matrix.M21 + matrix.M41,
position.X * matrix.M12 + position.Y * matrix.M22 + matrix.M42);
}
/// <summary>
/// Transforms a vector normal by the given matrix.
/// </summary>
/// <param name="normal">The source vector.</param>
/// <param name="matrix">The transformation matrix.</param>
/// <returns>The transformed vector.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2 TransformNormal(Vector2 normal, Matrix3x2 matrix)
{
return new Vector2(
normal.X * matrix.M11 + normal.Y * matrix.M21,
normal.X * matrix.M12 + normal.Y * matrix.M22);
}
/// <summary>
/// Transforms a vector normal by the given matrix.
/// </summary>
/// <param name="normal">The source vector.</param>
/// <param name="matrix">The transformation matrix.</param>
/// <returns>The transformed vector.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2 TransformNormal(Vector2 normal, Matrix4x4 matrix)
{
return new Vector2(
normal.X * matrix.M11 + normal.Y * matrix.M21,
normal.X * matrix.M12 + normal.Y * matrix.M22);
}
/// <summary>
/// Transforms a vector by the given Quaternion rotation value.
/// </summary>
/// <param name="value">The source vector to be rotated.</param>
/// <param name="rotation">The rotation to apply.</param>
/// <returns>The transformed vector.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2 Transform(Vector2 value, Quaternion rotation)
{
float x2 = rotation.X + rotation.X;
float y2 = rotation.Y + rotation.Y;
float z2 = rotation.Z + rotation.Z;
float wz2 = rotation.W * z2;
float xx2 = rotation.X * x2;
float xy2 = rotation.X * y2;
float yy2 = rotation.Y * y2;
float zz2 = rotation.Z * z2;
return new Vector2(
value.X * (1.0f - yy2 - zz2) + value.Y * (xy2 - wz2),
value.X * (xy2 + wz2) + value.Y * (1.0f - xx2 - zz2));
}
#endregion Public Static Methods
#region Public operator methods
// all the below methods should be inlined as they are
// implemented over JIT intrinsics
/// <summary>
/// Adds two vectors together.
/// </summary>
/// <param name="left">The first source vector.</param>
/// <param name="right">The second source vector.</param>
/// <returns>The summed vector.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2 Add(Vector2 left, Vector2 right)
{
return left + right;
}
/// <summary>
/// Subtracts the second vector from the first.
/// </summary>
/// <param name="left">The first source vector.</param>
/// <param name="right">The second source vector.</param>
/// <returns>The difference vector.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2 Subtract(Vector2 left, Vector2 right)
{
return left - right;
}
/// <summary>
/// Multiplies two vectors together.
/// </summary>
/// <param name="left">The first source vector.</param>
/// <param name="right">The second source vector.</param>
/// <returns>The product vector.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2 Multiply(Vector2 left, Vector2 right)
{
return left * right;
}
/// <summary>
/// Multiplies a vector by the given scalar.
/// </summary>
/// <param name="left">The source vector.</param>
/// <param name="right">The scalar value.</param>
/// <returns>The scaled vector.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2 Multiply(Vector2 left, Single right)
{
return left * right;
}
/// <summary>
/// Multiplies a vector by the given scalar.
/// </summary>
/// <param name="left">The scalar value.</param>
/// <param name="right">The source vector.</param>
/// <returns>The scaled vector.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2 Multiply(Single left, Vector2 right)
{
return left * right;
}
/// <summary>
/// Divides the first vector by the second.
/// </summary>
/// <param name="left">The first source vector.</param>
/// <param name="right">The second source vector.</param>
/// <returns>The vector resulting from the division.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2 Divide(Vector2 left, Vector2 right)
{
return left / right;
}
/// <summary>
/// Divides the vector by the given scalar.
/// </summary>
/// <param name="left">The source vector.</param>
/// <param name="divisor">The scalar value.</param>
/// <returns>The result of the division.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2 Divide(Vector2 left, Single divisor)
{
return left / divisor;
}
/// <summary>
/// Negates a given vector.
/// </summary>
/// <param name="value">The source vector.</param>
/// <returns>The negated vector.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Vector2 Negate(Vector2 value)
{
return -value;
}
#endregion Public operator methods
}
}
|