using System;
namespace Syntriax.Engine.Core;
///
/// Represents a two-dimensional vector.
///
[System.Diagnostics.DebuggerDisplay("{ToString(),nq}, Length: {Magnitude}, LengthSquared: {MagnitudeSquared}, Normalized: {Normalized.ToString(),nq}")]
public readonly struct Vector2D(float x, float y)
{
///
/// The X coordinate of the .
///
public readonly float X = x;
///
/// The Y coordinate of the .
///
public readonly float Y = y;
///
/// The magnitude (length) of the .
///
public float Magnitude => Length(this);
///
/// The squared magnitude (length) of the .
///
public float MagnitudeSquared => LengthSquared(this);
///
/// The normalized form of the (a with the same direction and a magnitude of 1).
///
public Vector2D Normalized => Normalize(this);
///
/// Represents the unit pointing upwards.
///
public readonly static Vector2D Up = new(0f, 1f);
///
/// Represents the unit pointing downwards.
///
public readonly static Vector2D Down = new(0f, -1f);
///
/// Represents the unit pointing leftwards.
///
public readonly static Vector2D Left = new(-1f, 0f);
///
/// Represents the unit pointing rightwards.
///
public readonly static Vector2D Right = new(1f, 0f);
///
/// Represents the zero .
///
public readonly static Vector2D Zero = new(0f, 0f);
///
/// Represents the with both components equal to 1.
///
public readonly static Vector2D One = new(1f, 1f);
public static Vector2D operator -(Vector2D vector) => new(0f - vector.X, 0f - vector.Y);
public static Vector2D operator +(Vector2D left, Vector2D right) => new(left.X + right.X, left.Y + right.Y);
public static Vector2D operator -(Vector2D left, Vector2D right) => new(left.X - right.X, left.Y - right.Y);
public static Vector2D operator *(Vector2D vector, float value) => new(vector.X * value, vector.Y * value);
public static Vector2D operator *(float value, Vector2D vector) => new(vector.X * value, vector.Y * value);
public static Vector2D operator /(Vector2D vector, float value) => new(vector.X / value, vector.Y / value);
public static bool operator ==(Vector2D left, Vector2D right) => left.X == right.X && left.Y == right.Y;
public static bool operator !=(Vector2D left, Vector2D right) => left.X != right.X || left.Y != right.Y;
///
/// Calculates the length of the .
///
/// The .
/// The length of the .
public static float Length(Vector2D vector) => MathF.Sqrt(LengthSquared(vector));
///
/// Calculates the squared length of the .
///
/// The .
/// The squared length of the .
public static float LengthSquared(Vector2D vector) => vector.X * vector.X + vector.Y * vector.Y;
///
/// Calculates the distance between two s.
///
/// The start .
/// The end .
/// The distance between the two s.
public static float Distance(Vector2D from, Vector2D to) => Length(FromTo(from, to));
///
/// Inverts the direction of the .
///
/// The .
/// The inverted .
public static Vector2D Invert(Vector2D vector) => -vector;
///
/// Adds two s.
///
/// The first .
/// The second .
/// The sum of the two s.
public static Vector2D Add(Vector2D left, Vector2D right) => left + right;
///
/// Subtracts one from another.
///
/// The to subtract from.
/// The to subtract.
/// The result of subtracting the second from the first.
public static Vector2D Subtract(Vector2D left, Vector2D right) => left - right;
///
/// Multiplies a by a scalar value.
///
/// The .
/// The scalar value.
/// The result of multiplying the by the scalar value.
public static Vector2D Multiply(Vector2D vector, float value) => vector * value;
///
/// Divides a by a scalar value.
///
/// The .
/// The scalar value.
/// The result of dividing the by the scalar value.
public static Vector2D Divide(Vector2D vector, float value) => vector / value;
///
/// Calculates the absolute value of each component of the vector.
///
/// The .
/// The with each component's absolute value.
public static Vector2D Abs(Vector2D vector) => new(Math.Abs(vector.X), Math.Abs(vector.Y));
///
/// Normalizes the (creates a unit with the same direction).
///
/// The to normalize.
/// The normalized .
public static Vector2D Normalize(Vector2D vector) => vector / Length(vector);
///
/// Reflects a off a surface with the specified normal.
///
/// The incident .
/// The normal of the surface.
/// The reflected .
public static Vector2D Reflect(Vector2D vector, Vector2D normal) => vector - 2f * Dot(vector, normal) * normal;
///
/// Calculates the from one point to another.
///
/// The starting point.
/// The ending point.
/// The from the starting point to the ending point.
public static Vector2D FromTo(Vector2D from, Vector2D to) => to - from;
///
/// Scales a by another component-wise.
///
/// The to scale.
/// The containing the scaling factors for each component.
/// The scaled .
public static Vector2D Scale(Vector2D vector, Vector2D scale) => new(vector.X * scale.X, vector.Y * scale.Y);
///
/// Calculates a perpendicular to the given .
///
/// The input .
/// A perpendicular to the input .
public static Vector2D Perpendicular(Vector2D vector) => new(-vector.Y, vector.X);
///
/// Rotates a by the specified angle (in radians).
///
/// The to rotate.
/// The angle to rotate by, in radians.
/// The rotated .
public static Vector2D Rotate(Vector2D vector, float angleInRadian) => new(MathF.Cos(angleInRadian) * vector.X - MathF.Sin(angleInRadian) * vector.Y, MathF.Sin(angleInRadian) * vector.X + MathF.Cos(angleInRadian) * vector.Y);
///
/// Returns the component-wise minimum of two s.
///
/// The first .
/// The second .
/// The containing the minimum components from both input s.
public static Vector2D Min(Vector2D left, Vector2D right) => new((left.X < right.X) ? left.X : right.X, (left.Y < right.Y) ? left.Y : right.Y);
///
/// Returns the component-wise maximum of two s.
///
/// The first .
/// The second .
/// The containing the maximum components from both input s.
public static Vector2D Max(Vector2D left, Vector2D right) => new((left.X > right.X) ? left.X : right.X, (left.Y > right.Y) ? left.Y : right.Y);
///
/// Clamps each component of a between the corresponding component of two other s.
///
/// The to clamp.
/// The representing the minimum values for each component.
/// The representing the maximum values for each component.
/// A with each component clamped between the corresponding components of the min and max s.
public static Vector2D Clamp(Vector2D vector, Vector2D min, Vector2D max) => new(Math.Clamp(vector.X, min.X, max.X), Math.Clamp(vector.Y, min.Y, max.Y));
///
/// Performs linear interpolation between two s.
///
/// The starting (t = 0).
/// The ending (t = 1).
/// The interpolation parameter.
/// The interpolated .
public static Vector2D Lerp(Vector2D from, Vector2D to, float t) => from + FromTo(from, to) * t;
///
/// Calculates the cross product of two s.
///
/// The first .
/// The second .
/// The cross product of the two s.
public static float Cross(Vector2D left, Vector2D right) => left.X * right.Y - left.Y * right.X;
///
/// Calculates the angle between two s.
///
/// The first .
/// The second .
/// The angle between the two s in radians.
public static float Angle(Vector2D left, Vector2D right) => MathF.Acos(Dot(left, right) / (Length(left) * Length(right)));
///
/// Calculates the dot product of two s.
///
/// The first .
/// The second .
/// The dot product of the two s.
public static float Dot(Vector2D left, Vector2D right) => left.X * right.X + left.Y * right.Y;
///
/// Determines the orientation of three points represented by s.
///
/// The first .
/// The second .
/// The third .
///
/// 0 - Collinear.
/// 1 - Clockwise.
/// 2 - Counterclockwise.
///
public static int Orientation(Vector2D left, Vector2D middle, Vector2D right)
{
Vector2D leftToMiddle = left.FromTo(middle);
Vector2D middleToRight = middle.FromTo(right);
float value = leftToMiddle.Y * middleToRight.X -
leftToMiddle.X * middleToRight.Y;
if (value > 0) return 1;
if (value < 0) return 2;
return 0;
}
///
/// Checks if two s are approximately equal within a specified epsilon range.
///
/// The first .
/// The second .
/// The epsilon range.
/// if the s are approximately equal; otherwise, .
public static bool ApproximatelyEquals(Vector2D left, Vector2D right, float epsilon = float.Epsilon)
=> left.X.ApproximatelyEquals(right.X, epsilon) && left.Y.ApproximatelyEquals(right.Y, epsilon);
///
/// Converts the to its string representation.
///
/// A string representation of the .
public override string ToString() => $"{nameof(Vector2D)}({X}, {Y})";
///
/// Determines whether the specified object is equal to the current .
///
/// The object to compare with the current .
/// if the specified object is equal to the current ; otherwise, .
public override bool Equals(object? obj) => obj is Vector2D objVec && X.Equals(objVec.X) && Y.Equals(objVec.Y);
///
/// Generates a hash code for the .
///
/// A hash code for the .
public override int GetHashCode() => HashCode.Combine(X, Y);
}