A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

372 lines
12 KiB

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