using System; using System.Runtime.CompilerServices; using Microsoft.Xna.Framework; namespace MonoGame.Extended { public static class Vector2Extensions { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 SetX(this Vector2 vector2, float x) => new Vector2(x, vector2.Y); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 SetY(this Vector2 vector2, float y) => new Vector2(vector2.X, y); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 Translate(this Vector2 vector2, float x, float y) => new Vector2(vector2.X + x, vector2.Y + y); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static SizeF ToSize(this Vector2 value) => new SizeF(value.X, value.Y); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static SizeF ToAbsoluteSize(this Vector2 value) { var x = Math.Abs(value.X); var y = Math.Abs(value.Y); return new SizeF(x, y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 Round(this Vector2 value, int digits, MidpointRounding mode) { var x = (float)Math.Round(value.X, digits, mode); var y = (float)Math.Round(value.Y, digits, mode); return new Vector2(x, y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 Round(this Vector2 value, int digits) { var x = (float)Math.Round(value.X, digits); var y = (float)Math.Round(value.Y, digits); return new Vector2(x, y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 Round(this Vector2 value) { var x = (float)Math.Round(value.X); var y = (float)Math.Round(value.Y); return new Vector2(x, y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool EqualsWithTolerence(this Vector2 value, Vector2 otherValue, float tolerance = 0.00001f) { return Math.Abs(value.X - otherValue.X) <= tolerance && (Math.Abs(value.Y - otherValue.Y) <= tolerance); } #if FNA || KNI [MethodImpl(MethodImplOptions.AggressiveInlining)] #else [Obsolete("Use native Vector2.Rotate provided by MonoGame instead. This will be removed in a future release.", false)] #endif public static Vector2 Rotate(this Vector2 value, float radians) { var cos = (float) Math.Cos(radians); var sin = (float) Math.Sin(radians); return new Vector2(value.X*cos - value.Y*sin, value.X*sin + value.Y*cos); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 NormalizedCopy(this Vector2 value) { var newVector2 = new Vector2(value.X, value.Y); newVector2.Normalize(); return newVector2; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 PerpendicularClockwise(this Vector2 value) => new Vector2(value.Y, -value.X); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 PerpendicularCounterClockwise(this Vector2 value) => new Vector2(-value.Y, value.X); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 Truncate(this Vector2 value, float maxLength) { if (value.LengthSquared() > maxLength*maxLength) return value.NormalizedCopy()*maxLength; return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsNaN(this Vector2 value) => float.IsNaN(value.X) || float.IsNaN(value.Y); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float ToAngle(this Vector2 value) => (float) Math.Atan2(value.X, -value.Y); /// /// Calculates the dot product of two vectors. If the two vectors are unit vectors, the dot product returns a floating /// point value between -1 and 1 that can be used to determine some properties of the angle between two vectors. For /// example, it can show whether the vectors are orthogonal, parallel, or have an acute or obtuse angle between them. /// /// The first vector. /// The second vector. /// The dot product of the two vectors. /// /// The dot product is also known as the inner product. /// /// For any two vectors, the dot product is defined as: (vector1.X * vector2.X) + (vector1.Y * vector2.Y). /// The result of this calculation, plus or minus some margin to account for floating point error, is equal to: /// Length(vector1) * Length(vector2) * System.Math.Cos(theta), where theta is the angle between the /// two vectors. /// /// /// If and are unit vectors, the length of each /// vector will be equal to 1. So, when and are unit /// vectors, the dot product is simply equal to the cosine of the angle between the two vectors. For example, both /// cos values in the following calcuations would be equal in value: /// vector1.Normalize(); vector2.Normalize(); var cos = vector1.Dot(vector2), /// var cos = System.Math.Cos(theta), where theta is angle in radians betwen the two vectors. /// /// /// If and are unit vectors, without knowing the value of /// theta or using a potentially processor-intensive trigonometric function, the value of the dot product /// can tell us the /// following things: /// /// /// /// If vector1.Dot(vector2) > 0, the angle between the two vectors /// is less than 90 degrees. /// /// /// /// /// If vector1.Dot(vector2) < 0, the angle between the two vectors /// is more than 90 degrees. /// /// /// /// /// If vector1.Dot(vector2) == 0, the angle between the two vectors /// is 90 degrees; that is, the vectors are othogonal. /// /// /// /// /// If vector1.Dot(vector2) == 1, the angle between the two vectors /// is 0 degrees; that is, the vectors point in the same direction and are parallel. /// /// /// /// /// If vector1.Dot(vector2) == -1, the angle between the two vectors /// is 180 degrees; that is, the vectors point in opposite directions and are parallel. /// /// /// /// /// /// Because of floating point error, two orthogonal vectors may not return a dot product that is exactly zero. It /// might be zero plus some amount of floating point error. In your code, you will want to determine what amount of /// error is acceptable in your calculation, and take that into account when you do your comparisons. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Dot(this Vector2 vector1, Vector2 vector2) { return vector1.X*vector2.X + vector1.Y*vector2.Y; } /// /// Calculates the scalar projection of one vector onto another. The scalar projection returns the length of the /// orthogonal projection of the first vector onto a straight line parallel to the second vector, with a negative value /// if the projection has an opposite direction with respect to the second vector. /// /// The first vector. /// The second vector. /// The scalar projection of onto . /// /// /// The scalar projection is also known as the scalar resolute of the first vector in the direction of the second /// vector. /// /// /// For any two vectors, the scalar projection is defined as: vector1.Dot(vector2) / Length(vector2). The /// result of this calculation, plus or minus some margin to account for floating point error, is equal to: /// Length(vector1) * System.Math.Cos(theta), where theta is the angle in radians between /// and . /// /// /// The value of the scalar projection can tell us the following things: /// /// /// /// If vector1.ScalarProjectOnto(vector2) >= 0, the angle between /// and is between 0 degrees (exclusive) and 90 degrees (inclusive). /// /// /// /// /// If vector1.ScalarProjectOnto(vector2) < 0, the angle between /// and is between 90 degrees (exclusive) and 180 degrees (inclusive). /// /// /// /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float ScalarProjectOnto(this Vector2 vector1, Vector2 vector2) { var dotNumerator = vector1.X*vector2.X + vector1.Y*vector2.Y; var lengthSquaredDenominator = vector2.X*vector2.X + vector2.Y*vector2.Y; return dotNumerator/(float) Math.Sqrt(lengthSquaredDenominator); } /// /// Calculates the vector projection of one vector onto another. The vector projection returns the orthogonal /// projection of the first vector onto a straight line parallel to the second vector. /// /// The first vector. /// The second vector. /// The vector projection of onto . /// /// /// The vector projection is also known as the vector component or vector resolute of the first vector in the /// direction of the second vector. /// /// /// For any two vectors, the vector projection is defined as: /// ( vector1.Dot(vector2) / Length(vector2)^2 ) * vector2. /// The /// result of this calculation, plus or minus some margin to account for floating point error, is equal to: /// ( Length(vector1) * System.Math.Cos(theta) ) * vector2 / Length(vector2), where theta is the /// angle in radians between and . /// /// /// This function is easier to compute than since it does not use a square root. /// When the vector projection and the scalar projection is required, consider using this function; the scalar /// projection can be obtained by taking the length of the projection vector. /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 ProjectOnto(this Vector2 vector1, Vector2 vector2) { var dotNumerator = vector1.X*vector2.X + vector1.Y*vector2.Y; var lengthSquaredDenominator = vector2.X*vector2.X + vector2.Y*vector2.Y; return dotNumerator/lengthSquaredDenominator*vector2; } #if FNA // MomoGame compatibility layer /// /// Gets a Point representation for this Vector2. /// /// A Point representation for this Vector2. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Point ToPoint(this Vector2 value) { return new Point((int)value.X, (int)value.Y); } /// /// Gets a Vector2 representation for this Point. /// /// A Vector2 representation for this Point. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 ToVector2(this Point value) { return new Vector2(value.X, value.Y); } #endif } }