Browse Source

Merge pull request #2761 from JaggerJo/add_common_math_operators_to_vector

Add common math operators to Vector
Steven Kirk 6 years ago
parent
commit
ebfff21f90
2 changed files with 252 additions and 20 deletions
  1. 140 20
      src/Avalonia.Visuals/Vector.cs
  2. 112 0
      tests/Avalonia.Visuals.UnitTests/VectorTests.cs

+ 140 - 20
src/Avalonia.Visuals/Vector.cs

@@ -65,9 +65,7 @@ namespace Avalonia
         /// <param name="b">Second vector</param>
         /// <returns>The dot product</returns>
         public static double operator *(Vector a, Vector b)
-        {
-            return a.X * b.X + a.Y * b.Y;
-        }
+            => Dot(a, b);
 
         /// <summary>
         /// Scales a vector.
@@ -76,9 +74,7 @@ namespace Avalonia
         /// <param name="scale">The scaling factor.</param>
         /// <returns>The scaled vector.</returns>
         public static Vector operator *(Vector vector, double scale)
-        {
-            return new Vector(vector._x * scale, vector._y * scale);
-        }
+            => Multiply(vector, scale);
 
         /// <summary>
         /// Scales a vector.
@@ -87,14 +83,17 @@ namespace Avalonia
         /// <param name="scale">The divisor.</param>
         /// <returns>The scaled vector.</returns>
         public static Vector operator /(Vector vector, double scale)
-        {
-            return new Vector(vector._x / scale, vector._y / scale);
-        }
+            => Divide(vector, scale);
 
         /// <summary>
         /// Length of the vector
         /// </summary>
-        public double Length => Math.Sqrt(X * X + Y * Y);
+        public double Length => Math.Sqrt(SquaredLength);
+
+        /// <summary>
+        /// Squared Length of the vector
+        /// </summary>
+        public double SquaredLength => _x * _x + _y * _y;
 
         /// <summary>
         /// Negates a vector.
@@ -102,9 +101,7 @@ namespace Avalonia
         /// <param name="a">The vector.</param>
         /// <returns>The negated vector.</returns>
         public static Vector operator -(Vector a)
-        {
-            return new Vector(-a._x, -a._y);
-        }
+            => Negate(a);
 
         /// <summary>
         /// Adds two vectors.
@@ -113,9 +110,7 @@ namespace Avalonia
         /// <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)
-        {
-            return new Vector(a._x + b._x, a._y + b._y);
-        }
+            => Add(a, b);
 
         /// <summary>
         /// Subtracts two vectors.
@@ -124,9 +119,7 @@ namespace Avalonia
         /// <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)
-        {
-            return new Vector(a._x - b._x, a._y - b._y);
-        }
+            => Subtract(a, b);
 
         /// <summary>
         /// 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);
         }
+
+        /// <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>
+        /// Returnes the vector (0.0, 0.0)
+        /// </summary>
+        public static Vector Zero
+            => new Vector(0, 0);
+
+        /// <summary>
+        /// Returnes the vector (1.0, 1.0)
+        /// </summary>
+        public static Vector One
+            => new Vector(1, 1);
+
+        /// <summary>
+        /// Returnes the vector (1.0, 0.0)
+        /// </summary>
+        public static Vector UnitX
+            => new Vector(1, 0);
+
+        /// <summary>
+        /// Returnes the vector (0.0, 1.0)
+        /// </summary>
+        public static Vector UnitY
+            => new Vector(0, 1);
     }
 }

+ 112 - 0
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));
+        }
+    }
+}