using System; using System.Numerics; namespace LinearAlgebra { public class Vector2Of where T : IComparable { public T x; public T y; public Vector2Of(T x, T y) { this.x = x; this.y = y; } } public class Vector2Int : Vector2Of { public Vector2Int(int x, int y) : base(x, y) { } public static Vector2Int operator -(Vector2Int v1, Vector2Int v2) { return new Vector2Int(v1.x - v2.x, v1.y - v2.y); } public float magnitude { get { return (float)Math.Sqrt(this.x * this.x + this.y * this.y); } } public static float Distance(Vector2Int v1, Vector2Int v2) { return (v1 - v2).magnitude; } } public class Vector2Float : Vector2Of { public Vector2Float(float x, float y) : base(x, y) { } public static Vector2Float operator -(Vector2Float v1, Vector2Float v2) { return new Vector2Float(v1.x - v2.x, v1.y - v2.y); } public float magnitude { get { return (float)Math.Sqrt(this.x * this.x + this.y * this.y); } } public static float Distance(Vector2Float v1, Vector2Float v2) { return (v1 - v2).magnitude; } } /// /// 2-dimensional vectors /// public struct Vector2 : IEquatable { /// /// The right axis of the vector /// public float x; // left/right /// /// The upward/forward axis of the vector /// public float y; // forward/backward // directions are to be inline with Vector3 as much as possible... /// /// Create a new 2-dimensional vector /// /// x axis value /// y axis value public Vector2(float x, float y) { this.x = x; this.y = y; } /// /// A vector with zero for all axis /// public static readonly Vector2 zero = new Vector2(0, 0); /// /// A vector with values (1, 1) /// public static readonly Vector2 one = new Vector2(1, 1); /// /// A vector with values (0, 1) /// public static readonly Vector2 up = new Vector2(0, 1); /// /// A vector with values (0, -1) /// public static readonly Vector2 down = new Vector2(0, -1); /// /// A vector with values (0, 1) /// public static readonly Vector2 forward = new Vector2(0, 1); /// /// A vector with values (0, -1) /// public static readonly Vector2 back = new Vector2(0, -1); /// /// A vector3 with values (-1, 0) /// public static readonly Vector2 left = new Vector2(-1, 0); /// /// A vector with values (1, 0) /// public static readonly Vector2 right = new Vector2(1, 0); /// /// The squared length of this vector /// /// The squared length /// The squared length is computationally simpler than the real length. /// Think of Pythagoras A^2 + B^2 = C^2. /// This leaves out the calculation of the squared root of C. public float sqrMagnitude { get { float d = x * x + y * y; return d; } } /// /// The length of this vector /// /// The length of this vector public float magnitude { get { float d = (float)Math.Sqrt(x * x + y * y); return d; } } /// /// Convert the vector to a length of a 1 /// /// The vector with length 1 public Vector2 normalized { get { float l = magnitude; Vector2 v = zero; if (l > Float.epsilon) v = this / l; return v; } } /// /// Add two vectors /// /// The first vector /// The second vector /// The result of adding the two vectors public static Vector2 operator +(Vector2 v1, Vector2 v2) { Vector2 v = new Vector2(v1.x + v2.x, v1.y + v2.y); return v; } /// /// Subtract two vectors /// /// The first vector /// The second vector /// The result of adding the two vectors public static Vector2 operator -(Vector2 v1, Vector2 v2) { Vector2 v = new Vector2(v1.x - v2.x, v1.y - v2.y); return v; } /// /// Negate the vector /// /// The vector to negate /// The negated vector /// This will result in a vector pointing in the opposite direction public static Vector2 operator -(Vector2 v1) { Vector2 v = new Vector2(-v1.x, -v1.y); return v; } /// /// Scale a vector uniformly up /// /// The vector to scale /// The scaling factor /// The scaled vector /// Each component of the vector will be multipled with the same factor. public static Vector2 operator *(Vector2 v1, float f) { Vector2 v = new Vector2(v1.x * f, v1.y * f); return v; } /// /// Scale a vector uniformly up /// /// The scaling factor /// The vector to scale /// The scaled vector /// Each component of the vector will be multipled with the same factor. public static Vector2 operator *(float f, Vector2 v1) { Vector2 v = new Vector2(f * v1.x, f * v1.y); return v; } /// /// Scale a vector uniformly down /// /// The vector to scale /// The scaling factor /// The scaled vector /// Each component of the vector will be devided by the same factor. public static Vector2 operator /(Vector2 v1, float f) { Vector2 v = new Vector2(v1.x / f, v1.y / f); return v; } /// /// Tests if the vector has equal values as the given vector /// /// The vector to compare to /// true if the vector values are equal public bool Equals(Vector2 v1) => x == v1.x && y == v1.y; /// /// Tests if the vector is equal to the given object /// /// The object to compare to /// false when the object is not a Vector2 or does not have equal values public override bool Equals(object obj) { if (!(obj is Vector2 v)) return false; return (x == v.x && y == v.y); } /// /// Tests if the two vectors have equal values /// /// The first vector /// The second vector /// truewhen the vectors have equal values /// Note that this uses a Float equality check which cannot be not exact in all cases. /// In most cases it is better to check if the Vector2.Distance between the vectors is smaller than Float.epsilon /// Or more efficient: (v1 - v2).sqrMagnitude < Float.sqrEpsilon public static bool operator ==(Vector2 v1, Vector2 v2) { return (v1.x == v2.x && v1.y == v2.y); } /// /// Tests if two vectors have different values /// /// The first vector /// The second vector /// truewhen the vectors have different values /// Note that this uses a Float equality check which cannot be not exact in all case. /// In most cases it is better to check if the Vector2.Distance between the vectors is smaller than Float.epsilon. /// Or more efficient: (v1 - v2).sqrMagnitude < Float.sqrEpsilon public static bool operator !=(Vector2 v1, Vector2 v2) { return (v1.x != v2.x || v1.y != v2.y); } /// /// Get an hash code for the vector /// /// The hash code public override int GetHashCode() { return (x, y).GetHashCode(); } /// /// Get the distance between two vectors /// /// The first vector /// The second vector /// The distance between the two vectors public static float Distance(Vector2 v1, Vector2 v2) { float x = v1.x - v2.x; float y = v1.y - v2.y; float d = (float)Math.Sqrt(x * x + y * y); return d; } /// /// The dot product of two vectors /// /// The first vector /// The second vector /// The dot product of the two vectors public static float Dot(Vector2 v1, Vector2 v2) { return v1.x * v2.x + v1.y * v2.y; } /// /// Lerp between two vectors /// /// The from vector /// The to vector /// The interpolation distance [0..1] /// The lerped vector /// The factor f is unclamped. Value 0 matches the *v1* vector, Value 1 /// matches the *v2* vector Value -1 is *v1* vector minus the difference /// between *v1* and *v2* etc. public static Vector2 Lerp(Vector2 v1, Vector2 v2, float f) { Vector2 v = v1 + (v2 - v1) * f; return v; } /// /// Calculate the signed angle between two vectors. /// /// The starting vector /// The ending vector /// The axis to rotate around /// The signed angle in degrees public static float SignedAngle(Vector2 from, Vector2 to) { //float sign = Math.Sign(v1.y * v2.x - v1.x * v2.y); //return Vector2.Angle(v1, v2) * sign; float sqrMagFrom = from.sqrMagnitude; float sqrMagTo = to.sqrMagnitude; if (sqrMagFrom == 0 || sqrMagTo == 0) return 0; //if (!isfinite(sqrMagFrom) || !isfinite(sqrMagTo)) // return nanf(""); float angleFrom = (float)Math.Atan2(from.y, from.x); float angleTo = (float)Math.Atan2(to.y, to.x); return (angleTo - angleFrom) * Angle.Rad2Deg; } /// /// Rotates the vector with the given angle /// /// The vector to rotate /// The angle in degrees /// public static Vector2 Rotate(Vector2 v1, float angle) { float sin = (float)Math.Sin(angle * Angle.Deg2Rad); float cos = (float)Math.Cos(angle * Angle.Deg2Rad); float tx = v1.x; float ty = v1.y; Vector2 v = new Vector2() { x = (cos * tx) - (sin * ty), y = (sin * tx) + (cos * ty) }; return v; } /// /// Map interval of angles between vectors [0..Pi] to interval [0..1] /// /// The first vector /// The second vector /// The resulting factor in interval [0..1] /// Vectors a and b must be normalized public static float ToFactor(Vector2 v1, Vector2 v2) { return (1 - Vector2.Dot(v1, v2)) / 2; } } }