using System.Runtime.CompilerServices; namespace RobotNet10.Shared.Numbers; /// /// Custom 2D vector struct that supports JSON serialization. /// Replacement for System.Numerics.Vector2 which doesn't serialize properly. /// public struct Vector2 : IEquatable { /// /// X component /// public double X { get; set; } /// /// Y component /// public double Y { get; set; } /// /// Creates a new Vector2 /// public Vector2(double x, double y) { X = x; Y = y; } /// /// Creates a Vector2 with both components set to the same value /// public Vector2(double value) { X = Y = value; } #region Static Properties /// /// Returns a Vector2 with both components set to zero /// public static Vector2 Zero => new(0, 0); /// /// Returns a Vector2 with both components set to one /// public static Vector2 One => new(1, 1); /// /// Returns the unit vector for the X axis (1, 0) /// public static Vector2 UnitX => new(1, 0); /// /// Returns the unit vector for the Y axis (0, 1) /// public static Vector2 UnitY => new(0, 1); #endregion #region Properties /// /// Returns the length (magnitude) of the vector /// public readonly double Length() { return Math.Sqrt(X * X + Y * Y); } /// /// Returns the squared length of the vector (faster than Length) /// public readonly double LengthSquared() { return X * X + Y * Y; } #endregion #region Methods /// /// Returns a normalized copy of this vector (unit length) /// public readonly Vector2 Normalize() { double length = Length(); if (length < double.Epsilon) return Zero; double invLength = 1.0 / length; return new Vector2(X * invLength, Y * invLength); } /// /// Normalizes this vector in place /// public void NormalizeInPlace() { double length = Length(); if (length < double.Epsilon) { X = Y = 0; return; } double invLength = 1.0 / length; X *= invLength; Y *= invLength; } #endregion #region Static Methods /// /// Calculates the dot product of two vectors /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Dot(Vector2 left, Vector2 right) { return left.X * right.X + left.Y * right.Y; } /// /// Returns a normalized copy of a vector (static version) /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 Normalize(Vector2 value) { return value.Normalize(); } /// /// Returns the distance between two vectors /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Distance(Vector2 value1, Vector2 value2) { double dx = value1.X - value2.X; double dy = value1.Y - value2.Y; return Math.Sqrt(dx * dx + dy * dy); } /// /// Returns the squared distance between two vectors (faster than Distance) /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double DistanceSquared(Vector2 value1, Vector2 value2) { double dx = value1.X - value2.X; double dy = value1.Y - value2.Y; return dx * dx + dy * dy; } /// /// Performs linear interpolation between two vectors /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 Lerp(Vector2 value1, Vector2 value2, double amount) { return new Vector2( value1.X + (value2.X - value1.X) * amount, value1.Y + (value2.Y - value1.Y) * amount ); } /// /// Returns a vector with the minimum components of two vectors /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 Min(Vector2 value1, Vector2 value2) { return new Vector2( Math.Min(value1.X, value2.X), Math.Min(value1.Y, value2.Y) ); } /// /// Returns a vector with the maximum components of two vectors /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 Max(Vector2 value1, Vector2 value2) { return new Vector2( Math.Max(value1.X, value2.X), Math.Max(value1.Y, value2.Y) ); } /// /// Returns a vector whose components are the absolute values of the input vector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 Abs(Vector2 value) { return new Vector2( Math.Abs(value.X), Math.Abs(value.Y) ); } /// /// Clamps a vector to the specified minimum and maximum values /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 Clamp(Vector2 value, Vector2 min, Vector2 max) { return new Vector2( Math.Clamp(value.X, min.X, max.X), Math.Clamp(value.Y, min.Y, max.Y) ); } /// /// Reflects a vector off a surface with the specified normal /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 Reflect(Vector2 vector, Vector2 normal) { double dot = Dot(vector, normal); return vector - 2.0 * dot * normal; } /// /// Transforms a Vector2 by a Matrix3x2 /// [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 ); } #endregion #region Operators /// /// Adds two vectors /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 operator +(Vector2 left, Vector2 right) { return new Vector2(left.X + right.X, left.Y + right.Y); } /// /// Subtracts two vectors /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 operator -(Vector2 left, Vector2 right) { return new Vector2(left.X - right.X, left.Y - right.Y); } /// /// Negates a vector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 operator -(Vector2 value) { return new Vector2(-value.X, -value.Y); } /// /// Multiplies two vectors component-wise /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 operator *(Vector2 left, Vector2 right) { return new Vector2(left.X * right.X, left.Y * right.Y); } /// /// Multiplies a vector by a scalar /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 operator *(Vector2 left, double right) { return new Vector2(left.X * right, left.Y * right); } /// /// Multiplies a scalar by a vector /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 operator *(double left, Vector2 right) { return new Vector2(left * right.X, left * right.Y); } /// /// Divides two vectors component-wise /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 operator /(Vector2 left, Vector2 right) { return new Vector2(left.X / right.X, left.Y / right.Y); } /// /// Divides a vector by a scalar /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector2 operator /(Vector2 left, double right) { double invRight = 1.0 / right; return new Vector2(left.X * invRight, left.Y * invRight); } /// /// Checks if two vectors are equal /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Vector2 left, Vector2 right) { return left.X == right.X && left.Y == right.Y; } /// /// Checks if two vectors are not equal /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Vector2 left, Vector2 right) { return left.X != right.X || left.Y != right.Y; } #endregion #region Equality /// /// Checks if this vector equals another vector /// public readonly bool Equals(Vector2 other) { return X == other.X && Y == other.Y; } /// /// Checks if this vector equals an object /// public override readonly bool Equals(object? obj) { return obj is Vector2 vector && Equals(vector); } /// /// Gets the hash code for this vector /// public override readonly int GetHashCode() { return HashCode.Combine(X, Y); } #endregion #region String /// /// Returns a string representation of this vector /// public override readonly string ToString() { return $"<{X}, {Y}>"; } /// /// Returns a formatted string representation of this vector /// public readonly string ToString(string format) { return $"<{X.ToString(format)}, {Y.ToString(format)}>"; } #endregion }