using System; using System.Collections.Generic; using System.Diagnostics.Contracts; using System.Numerics; using System.Runtime.CompilerServices; namespace MagmaEngine.Math; public static class FixPointVectorExt { public static FixPointVector2 ToFixPointVector2(this Vector2 vector) { return new(vector); } public static FixPointVector2 Average(this IEnumerable source) { return Average(source, f => f); } public static FixPointVector2 Average(this IEnumerable source, Func selector) { using IEnumerator e = source.GetEnumerator(); if (!e.MoveNext()) return FixPointVector2.s_Zero; long sumX = selector(e.Current).m_X.m_Value; long sumY = selector(e.Current).m_Y.m_Value; int count = 1; while (e.MoveNext()) { checked { sumX += selector(e.Current).m_X.m_Value; sumY += selector(e.Current).m_Y.m_Value; } count++; } return new(new FixPoint16 {m_Value = (int)(sumX / count)}, new() {m_Value = (int)(sumY / count)}); } } public struct FixPointVector2 : IEquatable { private enum EParseState { TrimStartWhitespace, ParseX, ParseY, TrimEndWhitespace } /// /// Initializes a new instance of FixPointVector2 /// /// Initial value for the x-component of the vector. /// Initial value for the y-component of the vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public FixPointVector2(int x, int y) { m_X = x; m_Y = y; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public FixPointVector2(FixPoint16 x, FixPoint16 y) { m_X = x; m_Y = y; } public FixPointVector2(float x, float y) { m_X = new(x); m_Y = new(y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public FixPointVector2(IntVector2 coord) { m_X = coord.X; m_Y = coord.Y; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public FixPointVector2(Vector2 coord) { m_X = new(coord.X); m_Y = new(coord.Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator FixPointVector2(IntVector2 coord) { return new(coord); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator FixPointVector2(Vector2 coord) { return new(new(coord.X), new FixPoint16(coord.Y)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static explicit operator Vector2(FixPointVector2 coord) { return new(coord.m_X.ToFloat(), coord.m_Y.ToFloat()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly Vector2 ToVector2() { return new(m_X.ToFloat(), m_Y.ToFloat()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly IntVector2 ToIntVector2() { return new(m_X.ToIntRound(), m_Y.ToIntRound()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly SInt32Vector2 ToInt32Vector() { return new(m_X.m_Value, m_Y.m_Value); } #region -- base overrides --------------------------------------------- [Pure] public override string ToString() { return $"({m_X.ToString()}, {m_Y.ToString()})"; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] public bool Equals(FixPointVector2 other) { return m_X == other.m_X && m_Y == other.m_Y; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] public override bool Equals(object? obj) { if (obj is FixPointVector2 fixPointVector2) return Equals(fixPointVector2); return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] public override int GetHashCode() { return m_X.GetHashCode() + m_Y.GetHashCode(); } #endregion #region -- static properties ------------------------------------------ public static readonly FixPointVector2 s_Zero = new(0, 0); public static readonly FixPointVector2 s_One = new(1, 1); public static readonly FixPointVector2 s_UnitX = new(1, 0); public static readonly FixPointVector2 s_UnitY = new(0, 1); public static readonly FixPointVector2 s_MaxValue = new(FixPoint16.MaxValue, FixPoint16.MaxValue); public static readonly FixPointVector2 s_MinValue = new(FixPoint16.MinValue, FixPoint16.MinValue); #endregion #region -- public properties ----------------------------------------- public FixPoint16 this[int i] { [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] get { switch (i) { case 0: return m_X; case 1: return m_Y; default: throw new ArgumentOutOfRangeException(nameof(i)); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { switch (i) { case 0: m_X = value; break; case 1: m_Y = value; break; default: throw new ArgumentOutOfRangeException(nameof(i)); } } } /// /// Returns a new normalized FixPointVector2 from the current vector. /// public FixPointVector2 Normalized { [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] get { var length = Length(); if (!length.IsZero()) { FixPointVector2 result = new(m_X / length, m_Y / length); return result; } throw new InvalidOperationException("Error: can not normalize vector, the vector length is zero."); } } public FixPointVector2 Halved { [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] get => new(m_X.Halved, m_Y.Halved); } public FixPointVector2 Doubled { [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] get => new(m_X.Doubled, m_Y.Doubled); } #endregion #region -- public methods --------------------------------------------- /// /// Calculates the length of the current vector. /// /// The Length of the current vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] public FixPoint16 Length() { return FixPoint16.Length(m_X, m_Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] public int CompareLength(FixPoint16 length) { unchecked { return ((m_X.m_Value * (long)m_X.m_Value) + (m_Y.m_Value * (long)m_Y.m_Value)).CompareTo(length.m_Value * (long)length.m_Value); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] public long LengthSquaredLong() { unchecked { return (m_X.m_Value * (long)m_X.m_Value) + (m_Y.m_Value * (long)m_Y.m_Value); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] public int CompareLength(FixPointVector2 other) { unchecked { return ((m_X.m_Value * (long)m_X.m_Value) + (m_Y.m_Value * (long)m_Y.m_Value)).CompareTo( (other.m_X.m_Value * (long)other.m_X.m_Value) + (other.m_Y.m_Value * (long)other.m_Y.m_Value)); } } /// /// Normalizes the current vector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Normalize() { unchecked { var length = Length(); if (!length.IsZero()) { m_X = m_X / length; m_Y = m_Y / length; } #if DEBUG else { throw new("Error: can not normalize vector, the vector length is zero."); } #endif } } [MethodImpl(MethodImplOptions.AggressiveInlining)] [Pure] public bool IsZero() { return m_X.IsZero() && m_Y.IsZero(); } /// /// Adds a given scalar value to each component of the current FixPointVector2. /// /// The scalar value [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Add(FixPoint16 value) { unchecked { m_X = m_X + value; m_Y = m_Y + value; } } /// /// Adds a given FixPointVector2 to the current FixPointVector2. /// /// The vector to be added. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Add(FixPointVector2 other) { unchecked { m_X = m_X + other.m_X; m_Y = m_Y + other.m_Y; } } /// /// Subtracts a scalar value from each component of the current FixPointVector2. /// /// The scalar value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Subtract(FixPoint16 value) { unchecked { m_X = m_X - value; m_Y = m_Y - value; } } /// /// Subtracts a given FixPointVector2 from the current FixPointVector2. /// /// The vector to be subtracted. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Subtract(FixPointVector2 other) { unchecked { m_X = m_X - other.m_X; m_Y = m_Y - other.m_Y; } } /// /// Multiplies each component of the current FixPointVector2 by a given scalar value. /// /// The scalar value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Multiply(FixPoint16 scalar) { unchecked { m_X = m_X * scalar; m_Y = m_Y * scalar; } } /// /// Multiplies the current FixPointVector2 by another FixPointVector2. /// /// The source vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Multiply(FixPointVector2 other) { unchecked { m_X = m_X * other.m_X; m_Y = m_Y * other.m_Y; } } /// /// Divides each component of the current FixPointVector2 by a given scalar value. /// /// The scalar divider. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Divide(FixPoint16 divider) { #if DEBUG if (divider.IsZero()) throw new("Error: divider is zero (FixPointVector2.Divide)."); #endif unchecked { m_X = m_X / divider; m_Y = m_Y / divider; } } /// /// Divides the current FixPointVector2 by another FixPointVector2. /// /// The vector divider. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Divide(FixPointVector2 other) { unchecked { if (!other.m_X.IsZero() && !other.m_Y.IsZero()) { m_X = m_X / other.m_X; m_Y = m_Y / other.m_Y; } #if DEBUG else { throw new("Error: divider vector contains zero (FixPointVector2.Divide)."); } #endif } } #endregion #region -- public static methods -------------------------------------- public static FixPointVector2 FromString(ReadOnlySpan vector) { Parse(vector, out var result); return result; } public static bool TryParse(ReadOnlySpan vector, out FixPointVector2 result) { try { Parse(vector, out result); } catch (Exception) { result = s_Zero; return false; } return true; } public static void Parse(ReadOnlySpan vector, out FixPointVector2 result) { var i = 0; var state = EParseState.TrimStartWhitespace; var xStart = -1; var xEnd = -1; var yStart = -1; var yEnd = -1; while (i < vector.Length) { var character = vector[i]; switch (state) { case EParseState.TrimStartWhitespace: { if (char.IsWhiteSpace(character) || character == '(') { ++i; } else { xStart = i; state = EParseState.ParseX; } break; } case EParseState.ParseX: { if (character != ',') { ++i; } else { xEnd = i; ++i; yStart = i; yEnd = i; state = EParseState.ParseY; } break; } case EParseState.ParseY: { if (character != ')') { ++yEnd; ++i; } else { ++i; state = EParseState.TrimEndWhitespace; } break; } case EParseState.TrimEndWhitespace: { if (char.IsWhiteSpace(character)) { ++i; } else { throw new FormatException($"Unexpected character '{character}' after the vector."); } break; } } } if (xStart >= xEnd || yStart >= yEnd || xStart < 0 || xEnd < 0 || yStart < 0 || yEnd < 0) throw new FormatException("Vector components are missing."); var x = vector.Slice(xStart, xEnd - xStart); var y = vector.Slice(yStart, yEnd - yStart); result = new(FixPoint16.FromString(x), FixPoint16.FromString(y)); } /// /// Calculates the length of the given vector. /// /// The Length of the given vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPoint16 Length(FixPointVector2 value) { return value.Length(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int CompareLength(FixPointVector2 value, FixPoint16 fLength) { unchecked { return ((value.m_X.m_Value * (long)value.m_X.m_Value) + (value.m_Y.m_Value * (long)value.m_Y.m_Value)).CompareTo(fLength.m_Value * (long)fLength.m_Value); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int CompareLength(FixPointVector2 value1, FixPointVector2 value2) { unchecked { return ((value1.m_X.m_Value * (long)value1.m_X.m_Value) + (value1.m_Y.m_Value * (long)value1.m_Y.m_Value)).CompareTo( (value2.m_X.m_Value * (long)value2.m_X.m_Value) + (value2.m_Y.m_Value * (long)value2.m_Y.m_Value)); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Equal(FixPointVector2 a, FixPointVector2 b, FixPoint16 threshold) { return FixPoint16.Equal(a.m_X, b.m_X, threshold) && FixPoint16.Equal(a.m_Y, b.m_Y, threshold); } public static FixPoint16 Angle(FixPointVector2 value1, FixPointVector2 value2) { var fpAngle = FixPoint16.Atan2(value2.m_Y, value2.m_X) - FixPoint16.Atan2(value1.m_Y, value1.m_X); if (fpAngle <= FixPoint16.MinusPi) return fpAngle + FixPoint16.TwoPi; if (fpAngle > FixPoint16.Pi) return fpAngle - FixPoint16.TwoPi; return fpAngle; } /// /// Calculates the distance between two vectors. (manhatten/taxi-cab metrix) /// /// The source vector /// The source vector /// Distance between the two vectors. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPoint16 DistanceManhattan(FixPointVector2 left, FixPointVector2 right) { return FixPoint16.Abs(left.m_X - right.m_X) + FixPoint16.Abs(left.m_Y - right.m_Y); } /// /// Calculates the distance between two vectors. /// /// The source vector /// The source vector /// Distance between the two vectors. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPoint16 Distance(FixPointVector2 left, FixPointVector2 right) { var dx = left.m_X - right.m_X; var dy = left.m_Y - right.m_Y; return FixPoint16.Length(dx, dy); } /// /// Calculates the distance between two vectors. /// /// The source vector /// The source vector /// Distance between the two vectors. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long DistanceSquared(FixPointVector2 left, FixPointVector2 right) { var dx = left.m_X - right.m_X; var dy = left.m_Y - right.m_Y; return FixPoint16.LengthSquared(dx, dy); } /// /// Calculates the dot product of two vectors. /// /// The source vector. /// The source vector. /// The dot product of the two vectors. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPoint16 Dot(FixPointVector2 left, FixPointVector2 right) { return (left.m_X * right.m_X) + (left.m_Y * right.m_Y); } /// /// Calculates the dot product of two vectors. /// /// The source vector. /// The source vector. /// The dot product of the two vectors. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static long DotLong(FixPointVector2 left, FixPointVector2 right) { return (left.m_X.m_Value * (long)right.m_X.m_Value) + (left.m_Y.m_Value * (long)right.m_Y.m_Value); } /// /// Calculates the sign of the dot product of two vectors. /// /// The source vector. /// The source vector. /// The sign of the dot product of the two vectors. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int DotSign(FixPointVector2 left, FixPointVector2 right) { unchecked { return ((left.m_X.m_Value * (long)right.m_X.m_Value) + (left.m_Y.m_Value * (long)right.m_Y.m_Value)).CompareTo(0); } } /// /// Calculates the cross product of two vectors. /// /// The source vector. /// The source vector. /// The cross product of the two vectors (this is the magnitude of the 2-dimensional cross product). [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPoint16 Cross(FixPointVector2 left, FixPointVector2 right) { return (left.m_X * right.m_Y) - (left.m_Y * right.m_X); } /// /// Calculates the cross product of two vectors. /// /// The source vector. /// The source vector. /// The cross product of the two vectors (this is the magnitude of the 2-dimensional cross product). [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPoint16Long CrossLong(FixPointVector2 left, FixPointVector2 right) { return new FixPoint16Long(left.m_X) * new FixPoint16Long(right.m_Y) - new FixPoint16Long(left.m_Y) * new FixPoint16Long(right.m_X); } /// /// Calculates the sign of the cross product of two vectors. /// /// The source vector. /// The source vector. /// The sign of the cross product of the two vectors. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int CrossSign(FixPointVector2 left, FixPointVector2 right) { unchecked { return ((left.m_X.m_Value * (long)right.m_Y.m_Value) - (left.m_Y.m_Value * (long)right.m_X.m_Value)).CompareTo(0); } } /// /// Creates a unit vector from the specified vector. /// /// The source vector. /// The created unit vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 Normalize(FixPointVector2 value) { return value.Normalized; } /// /// Returns a vector that contains the lowest value from each matching pair of components. /// /// The source vector. /// The source vector. /// The minimized vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 Min(FixPointVector2 value1, FixPointVector2 value2) { FixPointVector2 result; result.m_X = FixPoint16.Min(value1.m_X, value2.m_X); result.m_Y = FixPoint16.Min(value1.m_Y, value2.m_Y); return result; } /// /// Returns a vector that contains the highest value from each matching pair of components. /// /// The source vector. /// The source vector. /// The maximized vector. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 Max(FixPointVector2 value1, FixPointVector2 value2) { FixPointVector2 result; result.m_X = FixPoint16.Max(value1.m_X, value2.m_X); result.m_Y = FixPoint16.Max(value1.m_Y, value2.m_Y); return result; } /// /// Returns a vector pointing in the opposite direction. /// /// The source vector. /// A new vector pointing in the opposite direction. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 Negate(FixPointVector2 value) { unchecked { FixPointVector2 result; result.m_X = -value.m_X; result.m_Y = -value.m_Y; return result; } } /// /// Adds two vectors /// /// The source vector. /// The source vector. /// A new vector representing the sum of the source vectors. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 Add(FixPointVector2 value1, FixPointVector2 value2) { unchecked { FixPointVector2 result; result.m_X = value1.m_X + value2.m_X; result.m_Y = value1.m_Y + value2.m_Y; return result; } } /// /// Adds a given scalar value to each component of a given vector. /// /// The source vector. /// The scalar value to be added to the vector. /// A new FixPointVector2 representing the sum of the given vector and scalar. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 Add(FixPointVector2 value1, FixPoint16 value2) { unchecked { FixPointVector2 result; result.m_X = value1.m_X + value2; result.m_Y = value1.m_Y + value2; return result; } } /// /// Subtracts a vector from another vector. /// /// The vector to be subtracted from. /// The vector to be subtracted. /// A new vector representing the result of the subtraction. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 Subtract(FixPointVector2 value1, FixPointVector2 value2) { unchecked { FixPointVector2 result; result.m_X = value1.m_X - value2.m_X; result.m_Y = value1.m_Y - value2.m_Y; return result; } } /// /// Subtracts a given scalar value from each component of a given vector. /// /// The vector to be subtracted from. /// The scalar value to subtracted. /// A new FixPointVector2 representing the result of the subtraction. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 Subtract(FixPointVector2 value1, FixPoint16 value2) { unchecked { FixPointVector2 result; result.m_X = value1.m_X - value2; result.m_Y = value1.m_Y - value2; return result; } } /// /// Subtracts each component of a given vector from a given scalar value. /// /// The scalar value to be subtracted from. /// The vector to subtracted. /// A new FixPointVector2 representing the result of the subtraction. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 Subtract(FixPoint16 value1, FixPointVector2 value2) { unchecked { FixPointVector2 result; result.m_X = value1 - value2.m_X; result.m_Y = value1 - value2.m_Y; return result; } } /// /// Multiplies the components of two vectors by each other. /// /// The source vector. /// The source vector. /// A new vector representing the result of the mulitiplication. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 Multiply(FixPointVector2 value1, FixPointVector2 value2) { unchecked { FixPointVector2 result; result.m_X = value1.m_X * value2.m_X; result.m_Y = value1.m_Y * value2.m_Y; return result; } } /// /// Multiplies a vector by a scalar value. /// /// The source vector. /// The scalar value. /// A new vector representing the result of the mulitiplication. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 Multiply(FixPointVector2 value, FixPoint16 scalar) { unchecked { FixPointVector2 result; result.m_X = value.m_X * scalar; result.m_Y = value.m_Y * scalar; return result; } } /// /// Divides the components of a vector by the components of another vector. /// /// The source vector. /// The divisor vector. /// A new vector representing the result of the division. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 Divide(FixPointVector2 value1, FixPointVector2 value2) { unchecked { FixPointVector2 result; result.m_X = value1.m_X / value2.m_X; result.m_Y = value1.m_Y / value2.m_Y; return result; } } /// /// Projects a vector onto another vector. /// /// The projected vector. /// The vector the projected vector is being projected on. /// A new vector representing the result of the division. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 Project(FixPointVector2 projected, FixPointVector2 projectionTarget) { unchecked { FixPointVector2 result; var fpDot = Dot(projected, projectionTarget); var fpProjectionTargetLength = projectionTarget.Length(); var fpScalar = fpDot / (fpProjectionTargetLength * fpProjectionTargetLength); result = projectionTarget * fpScalar; return result; } } /// /// Divides a vector by a scalar value. /// /// The source vector. /// The divider /// A new vector representing the result of the division. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 Divide(FixPointVector2 value, FixPoint16 divider) { unchecked { FixPointVector2 result; result.m_X = value.m_X / divider; result.m_Y = value.m_Y / divider; return result; } } /// /// Creates a new FixPointVector2 with each component being the result of dividing a scalar value by the corresponding /// component of a vector. /// /// The scalar value to be divided. /// The divider vector /// /// A new vector with each component being the result of dividing the scalar value by the corresponding component /// of the vector. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 Divide(FixPoint16 value, FixPointVector2 divider) { unchecked { FixPointVector2 result; result.m_X = value / divider.m_X; result.m_Y = value / divider.m_Y; return result; } } /// /// Performs a linear interpolation between two vectors. /// /// The source vector. /// The source vector. /// /// The value between 0 and 1 indicating the weight of _value2. '0.0' will cause _value1 to be /// returned; '1.0' will cause _value2 to be returned. /// /// The linear interpolation of the two vectors. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 Lerp(FixPointVector2 value1, FixPointVector2 value2, FixPoint16 amount) { unchecked { FixPointVector2 result; result.m_X = value1.m_X + (amount * (value2.m_X - value1.m_X)); result.m_Y = value1.m_Y + (amount * (value2.m_Y - value1.m_Y)); return result; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 Lerp(FixPointVector2 value1, FixPointVector2 value2, FixPointVector2 amount) { unchecked { FixPointVector2 result; result.m_X = value1.m_X + (amount.m_X * (value2.m_X - value1.m_X)); result.m_Y = value1.m_Y + (amount.m_Y * (value2.m_Y - value1.m_Y)); return result; } } public static FixPointVector2 Clamp(FixPointVector2 value, FixPointVector2 min, FixPointVector2 max) { return new(FixPoint16.Clamp(value.m_X, min.m_X, max.m_X), FixPoint16.Clamp(value.m_Y, min.m_Y, max.m_Y)); } public static FixPointVector2 Saturate(FixPointVector2 value) { return Clamp(value, s_Zero, s_One); } public static FixPointVector2 Step(FixPointVector2 value, FixPointVector2 amount) { return new(amount.m_X >= value.m_X ? FixPoint16.One : FixPoint16.Zero, amount.m_Y >= value.m_Y ? FixPoint16.One : FixPoint16.Zero); } /// /// Interpolates between two vectors using a cubic equation. /// /// The source value. /// The source value. /// The weighting value. /// The interpolated value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 SmoothStep(FixPointVector2 value1, FixPointVector2 value2, FixPoint16 amount) { var x = Saturate((new FixPointVector2(amount, amount) - value1) / (value2 - value1)); return x * x * (new FixPointVector2(new FixPoint16(3), new FixPoint16(3)) - x * 2); } /// /// Returns a FixPointVector2 containing the 2D Cartesian coordinates of a point specified in barycentric (areal) /// coordinates relative to a 2D triangle. /// /// A FixPointVector2 containing the 2D Cartesian coordinates of vertex 1 of the triangle. /// A FixPointVector2 containing the 2D Cartesian coordinates of vertex 2 of the triangle. /// A FixPointVector2 containing the 2D Cartesian coordinates of vertex 3 of the triangle. /// /// Barycentric coordinate b2, which expresses the weighting factor toward vertex 2 (specified in /// _value2). /// /// /// Barycentric coordinate b3, which expresses the weighting factor toward vertex 3 (specified in /// _value3). /// /// A new FixPointVector2 containing the 2D Cartesian coordinates of the specified point. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 Barycentric(FixPointVector2 value1, FixPointVector2 value2, FixPointVector2 value3, FixPoint16 amount1, FixPoint16 amount2) { FixPointVector2 result; result.m_X = value1.m_X + (amount1 * (value2.m_X - value1.m_X)) + (amount2 * (value3.m_X - value1.m_X)); result.m_Y = value1.m_Y + (amount1 * (value2.m_Y - value1.m_Y)) + (amount2 * (value3.m_Y - value1.m_Y)); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void ToBarycentric(FixPointVector2 p, FixPointVector2 a, FixPointVector2 b, FixPointVector2 c, out FixPoint16 u, out FixPoint16 v, out FixPoint16 w) { FixPointVector2 v0 = b - a, v1 = c - a, v2 = p - a; var d00 = Dot(v0, v0); var d01 = Dot(v0, v1); var d11 = Dot(v1, v1); var d20 = Dot(v2, v0); var d21 = Dot(v2, v1); var denom = (d00 * d11) - (d01 * d01); v = ((d11 * d20) - (d01 * d21)) / denom; w = ((d00 * d21) - (d01 * d20)) / denom; u = 1 - v - w; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 Fract(FixPointVector2 p) { return new(FixPoint16.Fract(p.m_X), FixPoint16.Fract(p.m_Y)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 FromOrientation(FixPoint16 orientation) { return new(FixPoint16.Cos(orientation), FixPoint16.Sin(orientation)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 FromOrientation(FixPoint16 orientation, FixPoint16 length) { return new(FixPoint16.Cos(orientation) * length, FixPoint16.Sin(orientation) * length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 Rotate(FixPointVector2 value, FixPoint16 angle) { return Rotate(value, FromOrientation(angle)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 InverseRotate(FixPointVector2 value, FixPoint16 angle) { return InverseRotate(value, FromOrientation(angle)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 Rotate(FixPointVector2 value, FixPointVector2 orientationDirection) { return new(value.m_X * orientationDirection.m_X - value.m_Y * orientationDirection.m_Y, value.m_Y * orientationDirection.m_X + value.m_X * orientationDirection.m_Y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 InverseRotate(FixPointVector2 value, FixPointVector2 orientationDirection) { return new(value.m_X * orientationDirection.m_X + value.m_Y * orientationDirection.m_Y, value.m_Y * orientationDirection.m_X - value.m_X * orientationDirection.m_Y); } #endregion #region -- operators -------------------------------------------------- [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(FixPointVector2 left, FixPointVector2 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(FixPointVector2 left, FixPointVector2 right) { return !left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 operator -(FixPointVector2 value) { return Negate(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 operator +(FixPointVector2 left, FixPointVector2 right) { unchecked { FixPointVector2 result; result.m_X = left.m_X + right.m_X; result.m_Y = left.m_Y + right.m_Y; return result; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 operator +(FixPointVector2 left, FixPoint16 right) { return Add(left, right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 operator +(FixPoint16 left, FixPointVector2 right) { return Add(right, left); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 operator -(FixPointVector2 left, FixPointVector2 right) { unchecked { FixPointVector2 result; result.m_X = left.m_X - right.m_X; result.m_Y = left.m_Y - right.m_Y; return result; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 operator -(FixPointVector2 left, FixPoint16 right) { return Subtract(left, right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 operator -(FixPoint16 left, FixPointVector2 right) { return Subtract(left, right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 operator *(FixPointVector2 left, FixPointVector2 right) { return Multiply(left, right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 operator *(FixPointVector2 left, FixPoint16 right) { return new() { m_X = left.m_X * right, m_Y = left.m_Y * right }; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 operator *(FixPoint16 left, FixPointVector2 right) { return Multiply(right, left); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 operator /(FixPointVector2 left, FixPointVector2 right) { return Divide(left, right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 operator /(FixPointVector2 left, FixPoint16 right) { return new() { m_X = left.m_X / right, m_Y = left.m_Y / right }; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static FixPointVector2 operator /(FixPoint16 left, FixPointVector2 right) { return Divide(left, right); } #endregion /// /// The x-component of the vector. /// public FixPoint16 m_X; /// /// The y-component of the vector. /// public FixPoint16 m_Y; }