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
}