using System; using System.Globalization; #if !BUILDTASK using Avalonia.Animation.Animators; #endif using Avalonia.Utilities; #nullable enable namespace Avalonia { /// /// Defines a vector. /// #if !BUILDTASK public #endif readonly struct Vector : IEquatable { static Vector() { #if !BUILDTASK Animation.Animation.RegisterAnimator(prop => typeof(Vector).IsAssignableFrom(prop.PropertyType)); #endif } /// /// The X component. /// private readonly double _x; /// /// The Y component. /// private readonly double _y; /// /// Initializes a new instance of the structure. /// /// The X component. /// The Y component. public Vector(double x, double y) { _x = x; _y = y; } /// /// Gets the X component. /// public double X => _x; /// /// Gets the Y component. /// public double Y => _y; /// /// Converts the to a . /// /// The vector. public static explicit operator Point(Vector a) { return new Point(a._x, a._y); } /// /// Calculates the dot product of two vectors. /// /// First vector. /// Second vector. /// The dot product. public static double operator *(Vector a, Vector b) => Dot(a, b); /// /// Scales a vector. /// /// The vector. /// The scaling factor. /// The scaled vector. public static Vector operator *(Vector vector, double scale) => Multiply(vector, scale); /// /// Scales a vector. /// /// The vector. /// The scaling factor. /// The scaled vector. public static Vector operator *(double scale, Vector vector) => Multiply(vector, scale); /// /// Scales a vector. /// /// The vector. /// The divisor. /// The scaled vector. public static Vector operator /(Vector vector, double scale) => Divide(vector, scale); /// /// Parses a string. /// /// The string. /// The . public static Vector Parse(string s) { using (var tokenizer = new StringTokenizer(s, CultureInfo.InvariantCulture, exceptionMessage: "Invalid Vector.")) { return new Vector( tokenizer.ReadDouble(), tokenizer.ReadDouble() ); } } /// /// Length of the vector. /// public double Length => Math.Sqrt(SquaredLength); /// /// Squared Length of the vector. /// public double SquaredLength => _x * _x + _y * _y; /// /// Negates a vector. /// /// The vector. /// The negated vector. public static Vector operator -(Vector a) => Negate(a); /// /// Adds two vectors. /// /// The first vector. /// The second vector. /// A vector that is the result of the addition. public static Vector operator +(Vector a, Vector b) => Add(a, b); /// /// Subtracts two vectors. /// /// The first vector. /// The second vector. /// A vector that is the result of the subtraction. public static Vector operator -(Vector a, Vector b) => Subtract(a, b); /// /// Check if two vectors are equal (bitwise). /// /// /// public bool Equals(Vector other) { // ReSharper disable CompareOfFloatsByEqualityOperator return _x == other._x && _y == other._y; // ReSharper restore CompareOfFloatsByEqualityOperator } /// /// Check if two vectors are nearly equal (numerically). /// /// The other vector. /// True if vectors are nearly equal. public bool NearlyEquals(Vector other) { return MathUtilities.AreClose(_x, other._x) && MathUtilities.AreClose(_y, other._y); } public override bool Equals(object? obj) => obj is Vector other && Equals(other); public override int GetHashCode() { unchecked { return (_x.GetHashCode() * 397) ^ _y.GetHashCode(); } } public static bool operator ==(Vector left, Vector right) { return left.Equals(right); } public static bool operator !=(Vector left, Vector right) { return !left.Equals(right); } /// /// Returns the string representation of the vector. /// /// The string representation of the vector. public override string ToString() { return string.Format(CultureInfo.InvariantCulture, "{0}, {1}", _x, _y); } /// /// Returns a new vector with the specified X component. /// /// The X component. /// The new vector. public Vector WithX(double x) { return new Vector(x, _y); } /// /// Returns a new vector with the specified Y component. /// /// The Y component. /// The new vector. public Vector WithY(double y) { return new Vector(_x, y); } /// /// Returns a normalized version of this vector. /// /// The normalized vector. public Vector Normalize() => Normalize(this); /// /// Returns a negated version of this vector. /// /// The negated vector. public Vector Negate() => Negate(this); /// /// Returns the dot product of two vectors. /// /// The first vector. /// The second vector. /// The dot product. public static double Dot(Vector a, Vector b) => a._x * b._x + a._y * b._y; /// /// Returns the cross product of two vectors. /// /// The first vector. /// The second vector. /// The cross product. public static double Cross(Vector a, Vector b) => a._x * b._y - a._y * b._x; /// /// Normalizes the given vector. /// /// The vector /// The normalized vector. public static Vector Normalize(Vector vector) => Divide(vector, vector.Length); /// /// Divides the first vector by the second. /// /// The first vector. /// The second vector. /// The scaled vector. public static Vector Divide(Vector a, Vector b) => new Vector(a._x / b._x, a._y / b._y); /// /// Divides the vector by the given scalar. /// /// The vector /// The scalar value /// The scaled vector. public static Vector Divide(Vector vector, double scalar) => new Vector(vector._x / scalar, vector._y / scalar); /// /// Multiplies the first vector by the second. /// /// The first vector. /// The second vector. /// The scaled vector. public static Vector Multiply(Vector a, Vector b) => new Vector(a._x * b._x, a._y * b._y); /// /// Multiplies the vector by the given scalar. /// /// The vector /// The scalar value /// The scaled vector. public static Vector Multiply(Vector vector, double scalar) => new Vector(vector._x * scalar, vector._y * scalar); /// /// Adds the second to the first vector /// /// The first vector. /// The second vector. /// The summed vector. public static Vector Add(Vector a, Vector b) => new Vector(a._x + b._x, a._y + b._y); /// /// Subtracts the second from the first vector /// /// The first vector. /// The second vector. /// The difference vector. public static Vector Subtract(Vector a, Vector b) => new Vector(a._x - b._x, a._y - b._y); /// /// Negates the vector /// /// The vector to negate. /// The scaled vector. public static Vector Negate(Vector vector) => new Vector(-vector._x, -vector._y); /// /// Returns the vector (0.0, 0.0). /// public static Vector Zero => new Vector(0, 0); /// /// Returns the vector (1.0, 1.0). /// public static Vector One => new Vector(1, 1); /// /// Returns the vector (1.0, 0.0). /// public static Vector UnitX => new Vector(1, 0); /// /// Returns the vector (0.0, 1.0). /// public static Vector UnitY => new Vector(0, 1); /// /// Deconstructs the vector into its X and Y components. /// /// The X component. /// The Y component. public void Deconstruct(out double x, out double y) { x = this._x; y = this._y; } /// /// Gets a value indicating whether the X and Y components are zero. /// public bool IsDefault { get { return (_x == 0) && (_y == 0); } } } }