diff --git a/src/Avalonia.Visuals/Vector.cs b/src/Avalonia.Visuals/Vector.cs
index 2f1690184d..11bda8b00e 100644
--- a/src/Avalonia.Visuals/Vector.cs
+++ b/src/Avalonia.Visuals/Vector.cs
@@ -65,9 +65,7 @@ namespace Avalonia
/// Second vector
/// The dot product
public static double operator *(Vector a, Vector b)
- {
- return a.X * b.X + a.Y * b.Y;
- }
+ => Dot(a, b);
///
/// Scales a vector.
@@ -76,9 +74,7 @@ namespace Avalonia
/// The scaling factor.
/// The scaled vector.
public static Vector operator *(Vector vector, double scale)
- {
- return new Vector(vector._x * scale, vector._y * scale);
- }
+ => Multiply(vector, scale);
///
/// Scales a vector.
@@ -87,14 +83,17 @@ namespace Avalonia
/// The divisor.
/// The scaled vector.
public static Vector operator /(Vector vector, double scale)
- {
- return new Vector(vector._x / scale, vector._y / scale);
- }
+ => Divide(vector, scale);
///
/// Length of the vector
///
- public double Length => Math.Sqrt(X * X + Y * Y);
+ public double Length => Math.Sqrt(SquaredLength);
+
+ ///
+ /// Squared Length of the vector
+ ///
+ public double SquaredLength => _x * _x + _y * _y;
///
/// Negates a vector.
@@ -102,9 +101,7 @@ namespace Avalonia
/// The vector.
/// The negated vector.
public static Vector operator -(Vector a)
- {
- return new Vector(-a._x, -a._y);
- }
+ => Negate(a);
///
/// Adds two vectors.
@@ -113,9 +110,7 @@ namespace Avalonia
/// The second vector.
/// A vector that is the result of the addition.
public static Vector operator +(Vector a, Vector b)
- {
- return new Vector(a._x + b._x, a._y + b._y);
- }
+ => Add(a, b);
///
/// Subtracts two vectors.
@@ -124,9 +119,7 @@ namespace Avalonia
/// The second vector.
/// A vector that is the result of the subtraction.
public static Vector operator -(Vector a, Vector b)
- {
- return new Vector(a._x - b._x, a._y - b._y);
- }
+ => Subtract(a, b);
///
/// Check if two vectors are equal (bitwise).
@@ -155,7 +148,8 @@ namespace Avalonia
public override bool Equals(object obj)
{
- if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(null, obj))
+ return false;
return obj is Vector vector && Equals(vector);
}
@@ -206,5 +200,131 @@ namespace Avalonia
{
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);
+
+ ///
+ /// Returnes the vector (0.0, 0.0)
+ ///
+ public static Vector Zero
+ => new Vector(0, 0);
+
+ ///
+ /// Returnes the vector (1.0, 1.0)
+ ///
+ public static Vector One
+ => new Vector(1, 1);
+
+ ///
+ /// Returnes the vector (1.0, 0.0)
+ ///
+ public static Vector UnitX
+ => new Vector(1, 0);
+
+ ///
+ /// Returnes the vector (0.0, 1.0)
+ ///
+ public static Vector UnitY
+ => new Vector(0, 1);
}
}
diff --git a/tests/Avalonia.Visuals.UnitTests/VectorTests.cs b/tests/Avalonia.Visuals.UnitTests/VectorTests.cs
new file mode 100644
index 0000000000..1bcc165aef
--- /dev/null
+++ b/tests/Avalonia.Visuals.UnitTests/VectorTests.cs
@@ -0,0 +1,112 @@
+// Copyright (c) The Avalonia Project. All rights reserved.
+// Licensed under the MIT license. See licence.md file in the project root for full license information.
+
+using Xunit;
+using Avalonia;
+using System;
+
+namespace Avalonia.Visuals.UnitTests
+{
+ public class VectorTests
+ {
+ [Fact]
+ public void Length_Should_Return_Correct_Length_Of_Vector()
+ {
+ var vector = new Vector(2, 4);
+ var length = Math.Sqrt(2 * 2 + 4 * 4);
+
+ Assert.Equal(length, vector.Length);
+ }
+
+ [Fact]
+ public void Length_Squared_Should_Return_Correct_Length_Of_Vector()
+ {
+ var vectorA = new Vector(2, 4);
+ var squaredLengthA = 2 * 2 + 4 * 4;
+
+ Assert.Equal(squaredLengthA, vectorA.SquaredLength);
+ }
+
+ [Fact]
+ public void Normalize_Should_Return_Normalized_Vector()
+ {
+ // the length of a normalized vector must be 1
+
+ var vectorA = new Vector(13, 84);
+ var vectorB = new Vector(-34, 345);
+ var vectorC = new Vector(-34, -84);
+
+ Assert.Equal(1.0, vectorA.Normalize().Length);
+ Assert.Equal(1.0, vectorB.Normalize().Length);
+ Assert.Equal(1.0, vectorC.Normalize().Length);
+ }
+
+ [Fact]
+ public void Negate_Should_Return_Negated_Vector()
+ {
+ var vector = new Vector(2, 4);
+ var negated = new Vector(-2, -4);
+
+ Assert.Equal(negated, vector.Negate());
+ }
+
+ [Fact]
+ public void Dot_Should_Return_Correct_Value()
+ {
+ var a = new Vector(-6, 8.0);
+ var b = new Vector(5, 12.0);
+
+ Assert.Equal(66.0, Vector.Dot(a, b));
+ }
+
+ [Fact]
+ public void Cross_Should_Return_Correct_Value()
+ {
+ var a = new Vector(-6, 8.0);
+ var b = new Vector(5, 12.0);
+
+ Assert.Equal(-112.0, Vector.Cross(a, b));
+ }
+
+ [Fact]
+ public void Divied_By_Vector_Should_Return_Correct_Value()
+ {
+ var a = new Vector(10, 2);
+ var b = new Vector(5, 2);
+
+ var expected = new Vector(2, 1);
+
+ Assert.Equal(expected, Vector.Divide(a, b));
+ }
+
+ [Fact]
+ public void Divied_Should_Return_Correct_Value()
+ {
+ var vector = new Vector(10, 2);
+ var expected = new Vector(5, 1);
+
+ Assert.Equal(expected, Vector.Divide(vector, 2));
+ }
+
+ [Fact]
+ public void Multiply_By_Vector_Should_Return_Correct_Value()
+ {
+ var a = new Vector(10, 2);
+ var b = new Vector(2, 2);
+
+ var expected = new Vector(20, 4);
+
+ Assert.Equal(expected, Vector.Multiply(a, b));
+ }
+
+ [Fact]
+ public void Multiply_Should_Return_Correct_Value()
+ {
+ var vector = new Vector(10, 2);
+
+ var expected = new Vector(20, 4);
+
+ Assert.Equal(expected, Vector.Multiply(vector, 2));
+ }
+ }
+}