csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
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
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); }
|
|
}
|
|
}
|
|
}
|
|
|