using System; using System.Numerics; using System.Runtime.CompilerServices; namespace Robust.Shared.Maths; public static class Vector2Helpers { public static readonly Vector2 Infinity = new(float.PositiveInfinity, float.PositiveInfinity); public static readonly Vector2 NaN = new(float.NaN, float.NaN); /// /// Half of a unit vector. /// public static readonly Vector2 Half = new(0.5f, 0.5f); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 InterpolateCubic(Vector2 preA, Vector2 a, Vector2 b, Vector2 postB, float t) { return a + (b - preA + (preA * 2.0f - a * 5.0f + b * 4.0f - postB + ((a - b) * 3.0f + postB - preA) * t) * t) * t * 0.5f; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool EqualsApprox(this Vector2 vec, Vector2 otherVec) { return MathHelper.CloseTo(vec.X, otherVec.X) && MathHelper.CloseTo(vec.Y, otherVec.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool EqualsApprox(this Vector2 vec, Vector2 otherVec, double tolerance) { return MathHelper.CloseTo(vec.X, otherVec.X, tolerance) && MathHelper.CloseTo(vec.Y, otherVec.Y, tolerance); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 Normalized(this Vector2 vec) { var length = vec.Length(); return new Vector2(vec.X / length, vec.Y / length); } /// /// Normalizes this vector if its length > 0, otherwise sets it to 0. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Normalize(ref this Vector2 vec) { var length = vec.Length(); if (length < float.Epsilon) { vec = Vector2.Zero; return 0f; } var invLength = 1f / length; vec.X *= invLength; vec.Y *= invLength; return length; } /// /// Compares the lengths of two vectors. /// /// /// Avoids square root computations by using squared lengths. /// /// /// a positive value if is longer than , /// a negative value if is longer than , /// or 0 if and have equal lengths. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float CompareLength(Vector2 a, Vector2 b) { return a.LengthSquared() - b.LengthSquared(); } /// /// Compares the length of a vector with a scalar. /// /// /// Avoids a square root computation by using squared length. /// /// /// a positive value if is longer than , /// a negative value if is shorter than , /// or 0 if has a length equal to . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float CompareLength(Vector2 vec, float scalar) { return vec.LengthSquared() - (scalar * scalar); } /// /// Compares the length of this vector with a scalar. /// /// /// Avoids a square root computation by using squared length. /// /// /// a positive value if this vector is longer than , /// a negative value if this vector is shorter than , /// or 0 if this vector has a length equal to . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float CompareLengthTo(this Vector2 vec, float scalar) { return CompareLength(vec, scalar); } /// /// Compares the length of this vector with another. /// /// /// Avoids square root computations by using squared lengths. /// /// /// a positive value if this vector is longer than , /// a negative value if this vector is shorter than , /// or 0 if this vector and have equal lengths. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float CompareLengthTo(this Vector2 thisVec, Vector2 otherVec) { return CompareLength(thisVec, otherVec); } /// /// Is the length of this vector greater than ? /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsLongerThan(this Vector2 vec, float scalar) { return CompareLength(vec, scalar) > 0; } /// /// Is the length of this vector greater than the length of ? /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsLongerThan(this Vector2 thisVec, Vector2 otherVec) { return CompareLength(thisVec, otherVec) > 0; } /// /// Is the length of this vector greater than or equal to ? /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsLongerThanOrEqualTo(this Vector2 vec, float scalar) { return CompareLength(vec, scalar) >= 0; } /// /// Is the length of this vector greater than or equal to the length of ? /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsLongerThanOrEqualTo(this Vector2 thisVec, Vector2 otherVec) { return CompareLength(thisVec, otherVec) >= 0; } /// /// Is the length of this vector less than ? /// /// /// /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsShorterThan(this Vector2 vec, float scalar) { return CompareLength(vec, scalar) < 0; } /// /// Is the length of this vector less than the length of ? /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsShorterThan(this Vector2 thisVec, Vector2 otherVec) { return CompareLength(thisVec, otherVec) < 0; } /// /// Is the length of this vector less than or equal to ? /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsShorterThanOrEqualTo(this Vector2 vec, float scalar) { return CompareLength(vec, scalar) <= 0; } /// /// Is the length of this vector less than or equal to the length of ? /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsShorterThanOrEqualTo(this Vector2 thisVec, Vector2 otherVec) { return CompareLength(thisVec, otherVec) <= 0; } /// /// Returns true if this vector's length is equal to . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool LengthEquals(this Vector2 thisVec, float scalar) { return CompareLength(thisVec, scalar) == 0; } /// /// Is this vector the same length as ? /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsEqualLengthTo(this Vector2 thisVec, Vector2 otherVec) { return CompareLength(thisVec, otherVec) == 0; } /// /// Compares the length of a vector with a scalar. /// /// /// Avoids a square root computation by using squared length. /// /// /// a positive value if is shorter than , /// a negative value if is longer than , /// or 0 if has a length equal to . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float CompareLength(float scalar, Vector2 vec) { return (scalar * scalar) - vec.LengthSquared(); } /// /// Is the length of this vector zero? /// public static bool IsLengthZero(this Vector2 vec) { return vec.LengthSquared() == 0; } /// /// Perform the cross product on a scalar and a vector. In 2D this produces /// a vector. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 Cross(float s, in Vector2 a) { return new(-s * a.Y, s * a.X); } /// /// Perform the cross product on a scalar and a vector. In 2D this produces /// a vector. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 Cross(in Vector2 a, in float s) { return new(s * a.Y, -s * a.X); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float Cross(Vector2 a, Vector2 b) { return a.X * b.Y - a.Y * b.X; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2i Floored(this Vector2 vec) { return new Vector2i((int) MathF.Floor(vec.X), (int) MathF.Floor(vec.Y)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2i Ceiled(this Vector2 vec) { return new Vector2i((int) MathF.Ceiling(vec.X), (int) MathF.Ceiling(vec.Y)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 Rounded(this Vector2 vec) { return new Vector2(MathF.Round(vec.X), MathF.Round(vec.Y)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Deconstruct(this Vector2 vec, out float x, out float y) { x = vec.X; y = vec.Y; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool EqualsApproxPercent(this Vector2 vec, Vector2 other, double tolerance = 0.0001) { return MathHelper.CloseToPercent(vec.X, other.X, tolerance) && MathHelper.CloseToPercent(vec.Y, other.Y, tolerance); } }