diff --git a/src/System.Numerics.Vectors/src/System.Numerics.Vectors.csproj b/src/System.Numerics.Vectors/src/System.Numerics.Vectors.csproj index 47a3bf9c341a..16e8924b81e4 100644 --- a/src/System.Numerics.Vectors/src/System.Numerics.Vectors.csproj +++ b/src/System.Numerics.Vectors/src/System.Numerics.Vectors.csproj @@ -66,12 +66,21 @@ System\MathF.netstandard.cs + + + + + + + + + + - diff --git a/src/System.Numerics.Vectors/src/System/Numerics/Quaternion.netcoreapp.cs b/src/System.Numerics.Vectors/src/System/Numerics/Quaternion.netcoreapp.cs new file mode 100644 index 000000000000..ca4213945fee --- /dev/null +++ b/src/System.Numerics.Vectors/src/System/Numerics/Quaternion.netcoreapp.cs @@ -0,0 +1,669 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Globalization; +using Internal.Runtime.CompilerServices; + +namespace System.Numerics +{ + /// + /// A structure encapsulating a four-dimensional vector (x,y,z,w), + /// which is used to efficiently rotate an object about the (x,y,z) vector by the angle theta, where w = cos(theta/2). + /// + public struct Quaternion : IEquatable + { + /// + /// Specifies the X-value of the vector component of the Quaternion. + /// + public float X; + /// + /// Specifies the Y-value of the vector component of the Quaternion. + /// + public float Y; + /// + /// Specifies the Z-value of the vector component of the Quaternion. + /// + public float Z; + /// + /// Specifies the rotation component of the Quaternion. + /// + public float W; + + /// + /// Returns a Quaternion representing no rotation. + /// + public static Quaternion Identity + { + get { return new Quaternion(0, 0, 0, 1); } + } + + /// + /// Returns whether the Quaternion is the identity Quaternion. + /// + public bool IsIdentity + { + get { return X == 0f && Y == 0f && Z == 0f && W == 1f; } + } + + /// + /// Constructs a Quaternion from the given components. + /// + /// The X component of the Quaternion. + /// The Y component of the Quaternion. + /// The Z component of the Quaternion. + /// The W component of the Quaternion. + public Quaternion(float x, float y, float z, float w) + { + this.X = x; + this.Y = y; + this.Z = z; + this.W = w; + } + + /// + /// Constructs a Quaternion from the given vector and rotation parts. + /// + /// The vector part of the Quaternion. + /// The rotation part of the Quaternion. + public Quaternion(Vector3 vectorPart, float scalarPart) + { + X = vectorPart.X; + Y = vectorPart.Y; + Z = vectorPart.Z; + W = scalarPart; + } + + /// + /// Calculates the length of the Quaternion. + /// + /// The computed length of the Quaternion. + public float Length() + { + Vector4 q = Unsafe.As(ref this); + return q.Length(); + } + + /// + /// Calculates the length squared of the Quaternion. This operation is cheaper than Length(). + /// + /// The length squared of the Quaternion. + public float LengthSquared() + { + Vector4 q = Unsafe.As(ref this); + return q.LengthSquared(); + } + + /// + /// Divides each component of the Quaternion by the length of the Quaternion. + /// + /// The source Quaternion. + /// The normalized Quaternion. + public static Quaternion Normalize(Quaternion value) + { + Vector4 q = Unsafe.As(ref value); + q = Vector4.Normalize(q); + return Unsafe.As(ref q); + } + + /// + /// Creates the conjugate of a specified Quaternion. + /// + /// The Quaternion of which to return the conjugate. + /// A new Quaternion that is the conjugate of the specified one. + public static Quaternion Conjugate(Quaternion value) + { + Vector4 q = -Unsafe.As(ref value); + q.W = -q.W; + return Unsafe.As(ref q); + } + + /// + /// Returns the inverse of a Quaternion. + /// + /// The source Quaternion. + /// The inverted Quaternion. + public static Quaternion Inverse(Quaternion value) + { + // -1 ( a -v ) + // q = ( ------------- ------------- ) + // ( a^2 + |v|^2 , a^2 + |v|^2 ) + + Vector4 q = Unsafe.As(ref value); + + float ls = Vector4.Dot(q, q); + float invNorm = -1.0f / ls; + + q *= invNorm; + q.W = -q.W; + + return Unsafe.As(ref q); + } + + /// + /// Creates a Quaternion from a normalized vector axis and an angle to rotate about the vector. + /// + /// The unit vector to rotate around. + /// This vector must be normalized before calling this function or the resulting Quaternion will be incorrect. + /// The angle, in radians, to rotate around the vector. + /// The created Quaternion. + public static Quaternion CreateFromAxisAngle(Vector3 axis, float angle) + { + float halfAngle = angle * 0.5f; + float s = MathF.Sin(halfAngle); + float c = MathF.Cos(halfAngle); + + Vector4 q = new Vector4(axis, 0) * s; + + q.W = c; + + return Unsafe.As(ref q); + } + + /// + /// Creates a new Quaternion from the given yaw, pitch, and roll, in radians. + /// + /// The yaw angle, in radians, around the Y-axis. + /// The pitch angle, in radians, around the X-axis. + /// The roll angle, in radians, around the Z-axis. + /// + public static Quaternion CreateFromYawPitchRoll(float yaw, float pitch, float roll) + { + // Roll first, about axis the object is facing, then + // pitch upward, then yaw to face into the new heading + float sr, cr, sp, cp, sy, cy; + + float halfRoll = roll * 0.5f; + sr = MathF.Sin(halfRoll); + cr = MathF.Cos(halfRoll); + + float halfPitch = pitch * 0.5f; + sp = MathF.Sin(halfPitch); + cp = MathF.Cos(halfPitch); + + float halfYaw = yaw * 0.5f; + sy = MathF.Sin(halfYaw); + cy = MathF.Cos(halfYaw); + + Vector4 result = + new Vector4( + cy * sp * cr, + sy * cp * cr, + cy * cp * sr, + cy * cp * cr) + + new Vector4( + sy * cp * sr, + -cy * sp * sr, + -sy * sp * cr, + sy * sp * sr); + + return Unsafe.As(ref result); + } + + /// + /// Creates a Quaternion from the given rotation matrix. + /// + /// The rotation matrix. + /// The created Quaternion. + public static Quaternion CreateFromRotationMatrix(Matrix4x4 matrix) + { + float trace = matrix.M11 + matrix.M22 + matrix.M33; + + Quaternion q = new Quaternion(); + + if (trace > 0.0f) + { + float s = MathF.Sqrt(trace + 1.0f); + q.W = s * 0.5f; + s = 0.5f / s; + q.X = (matrix.M23 - matrix.M32) * s; + q.Y = (matrix.M31 - matrix.M13) * s; + q.Z = (matrix.M12 - matrix.M21) * s; + } + else + { + if (matrix.M11 >= matrix.M22 && matrix.M11 >= matrix.M33) + { + float s = MathF.Sqrt(1.0f + matrix.M11 - matrix.M22 - matrix.M33); + float invS = 0.5f / s; + q.X = 0.5f * s; + q.Y = (matrix.M12 + matrix.M21) * invS; + q.Z = (matrix.M13 + matrix.M31) * invS; + q.W = (matrix.M23 - matrix.M32) * invS; + } + else if (matrix.M22 > matrix.M33) + { + float s = MathF.Sqrt(1.0f + matrix.M22 - matrix.M11 - matrix.M33); + float invS = 0.5f / s; + q.X = (matrix.M21 + matrix.M12) * invS; + q.Y = 0.5f * s; + q.Z = (matrix.M32 + matrix.M23) * invS; + q.W = (matrix.M31 - matrix.M13) * invS; + } + else + { + float s = MathF.Sqrt(1.0f + matrix.M33 - matrix.M11 - matrix.M22); + float invS = 0.5f / s; + q.X = (matrix.M31 + matrix.M13) * invS; + q.Y = (matrix.M32 + matrix.M23) * invS; + q.Z = 0.5f * s; + q.W = (matrix.M12 - matrix.M21) * invS; + } + } + + return q; + } + + /// + /// Calculates the dot product of two Quaternions. + /// + /// The first source Quaternion. + /// The second source Quaternion. + /// The dot product of the Quaternions. + public static float Dot(Quaternion quaternion1, Quaternion quaternion2) + { + return Vector4.Dot( + Unsafe.As(ref quaternion1), + Unsafe.As(ref quaternion2)); + } + + /// + /// Interpolates between two quaternions, using spherical linear interpolation. + /// + /// The first source Quaternion. + /// The second source Quaternion. + /// The relative weight of the second source Quaternion in the interpolation. + /// The interpolated Quaternion. + public static Quaternion Slerp(Quaternion quaternion1, Quaternion quaternion2, float amount) + { + const float epsilon = 1e-6f; + + float t = amount; + + Vector4 q1 = Unsafe.As(ref quaternion1); + Vector4 q2 = Unsafe.As(ref quaternion2); + + float cosOmega = Vector4.Dot(q1, q2); + + bool flip = false; + + if (cosOmega < 0.0f) + { + flip = true; + cosOmega = -cosOmega; + } + + float s1, s2; + + if (cosOmega > (1.0f - epsilon)) + { + // Too close, do straight linear interpolation. + s1 = 1.0f - t; + s2 = (flip) ? -t : t; + } + else + { + float omega = MathF.Acos(cosOmega); + float invSinOmega = 1 / MathF.Sin(omega); + + s1 = MathF.Sin((1.0f - t) * omega) * invSinOmega; + s2 = (flip) + ? -MathF.Sin(t * omega) * invSinOmega + : MathF.Sin(t * omega) * invSinOmega; + } + + Vector4 result = q1 * s1 + q2 * s2; + + return Unsafe.As(ref result); + } + + /// + /// Linearly interpolates between two quaternions. + /// + /// The first source Quaternion. + /// The second source Quaternion. + /// The relative weight of the second source Quaternion in the interpolation. + /// The interpolated Quaternion. + public static Quaternion Lerp(Quaternion quaternion1, Quaternion quaternion2, float amount) + { + float t = amount; + float t1 = 1.0f - t; + + Vector4 q1 = Unsafe.As(ref quaternion1); + Vector4 q2 = Unsafe.As(ref quaternion2); + + float dot = Vector4.Dot(q1, q2); + + q1 *= t1; + if (dot >= 0.0f) + { + q2 *= t; + } + else + { + q2 *= -t; + } + + Vector4 result = Vector4.Normalize(q1 + q2); + + return Unsafe.As(ref result); + } + + /// + /// Concatenates two Quaternions; the result represents the value1 rotation followed by the value2 rotation. + /// + /// The first Quaternion rotation in the series. + /// The second Quaternion rotation in the series. + /// A new Quaternion representing the concatenation of the value1 rotation followed by the value2 rotation. + public static Quaternion Concatenate(Quaternion value1, Quaternion value2) + { + float q1w = value1.W; + float q2w = value2.W; + + Vector3 vec1 = Unsafe.As(ref value1); + Vector3 vec2 = Unsafe.As(ref value2); + + float dot = Vector3.Dot(vec1, vec2); + + Vector3 vectorPart = Vector3.Cross(vec2, vec1) + vec1 * q2w + vec2 * q1w; + float scalarPart = q1w * q2w - dot; + + return new Quaternion(vectorPart, scalarPart); + } + + /// + /// Flips the sign of each component of the quaternion. + /// + /// The source Quaternion. + /// The negated Quaternion. + public static Quaternion Negate(Quaternion value) + { + Vector4 q = Unsafe.As(ref value); + Vector4 result = -q; + + return Unsafe.As(ref result); + } + + /// + /// Adds two Quaternions element-by-element. + /// + /// The first source Quaternion. + /// The second source Quaternion. + /// The result of adding the Quaternions. + public static Quaternion Add(Quaternion value1, Quaternion value2) + { + Vector4 q1 = Unsafe.As(ref value1); + Vector4 q2 = Unsafe.As(ref value2); + + Vector4 result = q1 + q2; + + return Unsafe.As(ref result); + } + + /// + /// Subtracts one Quaternion from another. + /// + /// The first source Quaternion. + /// The second Quaternion, to be subtracted from the first. + /// The result of the subtraction. + public static Quaternion Subtract(Quaternion value1, Quaternion value2) + { + Vector4 q1 = Unsafe.As(ref value1); + Vector4 q2 = Unsafe.As(ref value2); + + Vector4 result = q1 - q2; + + return Unsafe.As(ref result); + } + + /// + /// Multiplies two Quaternions together. + /// + /// The Quaternion on the left side of the multiplication. + /// The Quaternion on the right side of the multiplication. + /// The result of the multiplication. + public static Quaternion Multiply(Quaternion value1, Quaternion value2) + { + float q1w = value1.W; + float q2w = value2.W; + + Vector3 vec1 = Unsafe.As(ref value1); + Vector3 vec2 = Unsafe.As(ref value2); + + float dot = Vector3.Dot(vec1, vec2); + + Vector3 vectorPart = Vector3.Cross(vec1, vec2) + vec1 * q2w + vec2 * q1w; + float scalarPart = q1w * q2w - dot; + + return new Quaternion(vectorPart, scalarPart); + } + + /// + /// Multiplies a Quaternion by a scalar value. + /// + /// The source Quaternion. + /// The scalar value. + /// The result of the multiplication. + public static Quaternion Multiply(Quaternion value1, float value2) + { + Vector4 q1 = Unsafe.As(ref value1); + + Vector4 result = q1 * value2; + + return Unsafe.As(ref result); + } + + /// + /// Divides a Quaternion by another Quaternion. + /// + /// The source Quaternion. + /// The divisor. + /// The result of the division. + public static Quaternion Divide(Quaternion value1, Quaternion value2) + { + Vector4 q2 = Unsafe.As(ref value2); + + // Inverse part. + float ls = Vector4.Dot(q2, q2); + float invNorm = -1.0f / ls; + Vector4 q = q2 * invNorm; + float q2w = -q.W; + + // Multiply part. + float q1w = value1.W; + Vector3 vec1 = Unsafe.As(ref value1); + Vector3 vec2 = Unsafe.As(ref q); + + float dot = Vector3.Dot(vec1, vec2); + + Vector3 vectorPart = Vector3.Cross(vec1, vec2) + vec1 * q2w + vec2 * q1w; + float scalarPart = q1w * q2w - dot; + + return new Quaternion(vectorPart, scalarPart); + } + + /// + /// Flips the sign of each component of the quaternion. + /// + /// The source Quaternion. + /// The negated Quaternion. + public static Quaternion operator -(Quaternion value) + { + Vector4 q = Unsafe.As(ref value); + Vector4 result = -q; + + return Unsafe.As(ref result); + } + + /// + /// Adds two Quaternions element-by-element. + /// + /// The first source Quaternion. + /// The second source Quaternion. + /// The result of adding the Quaternions. + public static Quaternion operator +(Quaternion value1, Quaternion value2) + { + Vector4 q1 = Unsafe.As(ref value1); + Vector4 q2 = Unsafe.As(ref value2); + + Vector4 result = q1 + q2; + + return Unsafe.As(ref result); + } + + /// + /// Subtracts one Quaternion from another. + /// + /// The first source Quaternion. + /// The second Quaternion, to be subtracted from the first. + /// The result of the subtraction. + public static Quaternion operator -(Quaternion value1, Quaternion value2) + { + Vector4 q1 = Unsafe.As(ref value1); + Vector4 q2 = Unsafe.As(ref value2); + + Vector4 result = q1 - q2; + + return Unsafe.As(ref result); + } + + /// + /// Multiplies two Quaternions together. + /// + /// The Quaternion on the left side of the multiplication. + /// The Quaternion on the right side of the multiplication. + /// The result of the multiplication. + public static Quaternion operator *(Quaternion value1, Quaternion value2) + { + float q1w = value1.W; + float q2w = value2.W; + + Vector3 vec1 = Unsafe.As(ref value1); + Vector3 vec2 = Unsafe.As(ref value2); + + float dot = Vector3.Dot(vec1, vec2); + + Vector3 vectorPart = Vector3.Cross(vec1, vec2) + vec1 * q2w + vec2 * q1w; + float scalarPart = q1w * q2w - dot; + + return new Quaternion(vectorPart, scalarPart); + } + + /// + /// Multiplies a Quaternion by a scalar value. + /// + /// The source Quaternion. + /// The scalar value. + /// The result of the multiplication. + public static Quaternion operator *(Quaternion value1, float value2) + { + Vector4 q1 = Unsafe.As(ref value1); + + Vector4 result = q1 * value2; + + return Unsafe.As(ref result); + } + + /// + /// Divides a Quaternion by another Quaternion. + /// + /// The source Quaternion. + /// The divisor. + /// The result of the division. + public static Quaternion operator /(Quaternion value1, Quaternion value2) + { + Vector4 q2 = Unsafe.As(ref value2); + + // Inverse part. + float ls = Vector4.Dot(q2, q2); + float invNorm = -1.0f / ls; + Vector4 q = q2 * invNorm; + float q2w = -q.W; + + // Multiply part. + float q1w = value1.W; + Vector3 vec1 = Unsafe.As(ref value1); + Vector3 vec2 = Unsafe.As(ref q); + + float dot = Vector3.Dot(vec1, vec2); + + Vector3 vectorPart = Vector3.Cross(vec1, vec2) + vec1 * q2w + vec2 * q1w; + float scalarPart = q1w * q2w - dot; + + return new Quaternion(vectorPart, scalarPart); + } + + /// + /// Returns a boolean indicating whether the two given Quaternions are equal. + /// + /// The first Quaternion to compare. + /// The second Quaternion to compare. + /// True if the Quaternions are equal; False otherwise. + public static bool operator ==(Quaternion value1, Quaternion value2) + { + Vector4 q1 = Unsafe.As(ref value1); + Vector4 q2 = Unsafe.As(ref value2); + return q1 == q2; + } + + /// + /// Returns a boolean indicating whether the two given Quaternions are not equal. + /// + /// The first Quaternion to compare. + /// The second Quaternion to compare. + /// True if the Quaternions are not equal; False if they are equal. + public static bool operator !=(Quaternion value1, Quaternion value2) + { + Vector4 q1 = Unsafe.As(ref value1); + Vector4 q2 = Unsafe.As(ref value2); + return q1 != q2; + } + + /// + /// Returns a boolean indicating whether the given Quaternion is equal to this Quaternion instance. + /// + /// The Quaternion to compare this instance to. + /// True if the other Quaternion is equal to this instance; False otherwise. + public bool Equals(Quaternion other) + { + Vector4 q1 = Unsafe.As(ref this); + Vector4 q2 = Unsafe.As(ref other); + return q1 == q2; + } + + /// + /// Returns a boolean indicating whether the given Object is equal to this Quaternion instance. + /// + /// The Object to compare against. + /// True if the Object is equal to this Quaternion; False otherwise. + public override bool Equals(object obj) + { + if (obj is Quaternion) + { + return Equals((Quaternion)obj); + } + + return false; + } + + /// + /// Returns a String representing this Quaternion instance. + /// + /// The string representation. + public override string ToString() + { + CultureInfo ci = CultureInfo.CurrentCulture; + + return String.Format(ci, "{{X:{0} Y:{1} Z:{2} W:{3}}}", X.ToString(ci), Y.ToString(ci), Z.ToString(ci), W.ToString(ci)); + } + + /// + /// Returns the hash code for this instance. + /// + /// The hash code. + public override int GetHashCode() + { + return unchecked(X.GetHashCode() + Y.GetHashCode() + Z.GetHashCode() + W.GetHashCode()); + } + } +} diff --git a/src/System.Numerics.Vectors/tests/QuaternionTests.cs b/src/System.Numerics.Vectors/tests/QuaternionTests.cs index 6bb5c32e72d3..759f62a18928 100644 --- a/src/System.Numerics.Vectors/tests/QuaternionTests.cs +++ b/src/System.Numerics.Vectors/tests/QuaternionTests.cs @@ -21,7 +21,7 @@ public void QuaternionDotTest() float actual; actual = Quaternion.Dot(a, b); - Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Dot did not return the expected value."); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.Dot did not return the expected value: expected {expected} actual {actual}"); } // A test for Length () @@ -39,7 +39,7 @@ public void QuaternionLengthTest() actual = target.Length(); - Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Length did not return the expected value."); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.Length did not return the expected value: expected {expected} actual {actual}"); } // A test for LengthSquared () @@ -56,7 +56,7 @@ public void QuaternionLengthSquaredTest() actual = target.LengthSquared(); - Assert.True(MathHelper.Equal(expected, actual), "Quaternion.LengthSquared did not return the expected value."); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.LengthSquared did not return the expected value: expected {expected} actual {actual}"); } // A test for Lerp (Quaternion, Quaternion, float) @@ -73,12 +73,12 @@ public void QuaternionLerpTest() Quaternion actual; actual = Quaternion.Lerp(a, b, t); - Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Lerp did not return the expected value."); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.Lerp did not return the expected value: expected {expected} actual {actual}"); // Case a and b are same. expected = a; actual = Quaternion.Lerp(a, a, t); - Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Lerp did not return the expected value."); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.Lerp did not return the expected value: expected {expected} actual {actual}"); } // A test for Lerp (Quaternion, Quaternion, float) @@ -94,7 +94,7 @@ public void QuaternionLerpTest1() Quaternion expected = new Quaternion(a.X, a.Y, a.Z, a.W); Quaternion actual = Quaternion.Lerp(a, b, t); - Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Lerp did not return the expected value."); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.Lerp did not return the expected value: expected {expected} actual {actual}"); } // A test for Lerp (Quaternion, Quaternion, float) @@ -110,7 +110,7 @@ public void QuaternionLerpTest2() Quaternion expected = new Quaternion(b.X, b.Y, b.Z, b.W); Quaternion actual = Quaternion.Lerp(a, b, t); - Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Lerp did not return the expected value."); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.Lerp did not return the expected value: expected {expected} actual {actual}"); } // A test for Lerp (Quaternion, Quaternion, float) @@ -128,7 +128,7 @@ public void QuaternionLerpTest3() // Note that in quaternion world, Q == -Q. In the case of quaternions dot product is zero, // one of the quaternion will be flipped to compute the shortest distance. When t = 1, we // expect the result to be the same as quaternion b but flipped. - Assert.True(actual == a, "Quaternion.Lerp did not return the expected value."); + Assert.True(actual == a, $"Quaternion.Lerp did not return the expected value: expected {a} actual {actual}"); } // A test for Conjugate(Quaternion) @@ -141,7 +141,7 @@ public void QuaternionConjugateTest1() Quaternion actual; actual = Quaternion.Conjugate(a); - Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Conjugate did not return the expected value."); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.Conjugate did not return the expected value: expected {expected} actual {actual}"); } // A test for Normalize (Quaternion) @@ -154,7 +154,7 @@ public void QuaternionNormalizeTest() Quaternion actual; actual = Quaternion.Normalize(a); - Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Normalize did not return the expected value."); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.Normalize did not return the expected value: expected {expected} actual {actual}"); } // A test for Normalize (Quaternion) @@ -166,7 +166,7 @@ public void QuaternionNormalizeTest1() Quaternion actual = Quaternion.Normalize(a); Assert.True(float.IsNaN(actual.X) && float.IsNaN(actual.Y) && float.IsNaN(actual.Z) && float.IsNaN(actual.W) - , "Quaternion.Normalize did not return the expected value."); + , $"Quaternion.Normalize did not return the expected value: expected {new Quaternion(float.NaN, float.NaN, float.NaN, float.NaN)} actual {actual}"); } // A test for Concatenate(Quaternion, Quaternion) @@ -180,7 +180,7 @@ public void QuaternionConcatenateTest1() Quaternion actual; actual = Quaternion.Concatenate(a, b); - Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Concatenate did not return the expected value."); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.Concatenate did not return the expected value: expected {expected} actual {actual}"); } // A test for operator - (Quaternion, Quaternion) @@ -195,7 +195,7 @@ public void QuaternionSubtractionTest() actual = a - b; - Assert.True(MathHelper.Equal(expected, actual), "Quaternion.operator - did not return the expected value."); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.operator - did not return the expected value: expected {expected} actual {actual}"); } // A test for operator * (Quaternion, float) @@ -210,7 +210,7 @@ public void QuaternionMultiplyTest() actual = a * factor; - Assert.True(MathHelper.Equal(expected, actual), "Quaternion.operator * did not return the expected value."); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.operator * did not return the expected value: expected {expected} actual {actual}"); } // A test for operator * (Quaternion, Quaternion) @@ -225,7 +225,7 @@ public void QuaternionMultiplyTest1() actual = a * b; - Assert.True(MathHelper.Equal(expected, actual), "Quaternion.operator * did not return the expected value."); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.operator * did not return the expected value: expected {expected} actual {actual}"); } // A test for operator / (Quaternion, Quaternion) @@ -240,7 +240,7 @@ public void QuaternionDivisionTest1() actual = a / b; - Assert.True(MathHelper.Equal(expected, actual), "Quaternion.operator / did not return the expected value."); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.operator / did not return the expected value: expected {expected} actual {actual}"); } // A test for operator + (Quaternion, Quaternion) @@ -255,7 +255,7 @@ public void QuaternionAdditionTest() actual = a + b; - Assert.True(MathHelper.Equal(expected, actual), "Quaternion.operator + did not return the expected value."); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.operator + did not return the expected value: expected {expected} actual {actual}"); } // A test for Quaternion (float, float, float, float) @@ -296,7 +296,7 @@ public void QuaternionCreateFromAxisAngleTest() Quaternion actual; actual = Quaternion.CreateFromAxisAngle(axis, angle); - Assert.True(MathHelper.Equal(expected, actual), "Quaternion.CreateFromAxisAngle did not return the expected value."); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.CreateFromAxisAngle did not return the expected value: expected {expected} actual {actual}"); } // A test for CreateFromAxisAngle (Vector3f, float) @@ -325,7 +325,7 @@ public void QuaternionCreateFromAxisAngleTest2() Quaternion actual1 = Quaternion.CreateFromAxisAngle(axis, angle1); Quaternion actual2 = Quaternion.CreateFromAxisAngle(axis, angle2); - Assert.True(MathHelper.Equal(actual1, actual2), "Quaternion.CreateFromAxisAngle did not return the expected value."); + Assert.True(MathHelper.Equal(actual1, actual2), $"Quaternion.CreateFromAxisAngle did not return the expected value: actual1 {actual1} actual2 {actual2}"); } // A test for CreateFromAxisAngle (Vector3f, float) @@ -342,7 +342,7 @@ public void QuaternionCreateFromAxisAngleTest3() actual1.X = -actual1.X; actual1.W = -actual1.W; - Assert.True(MathHelper.Equal(actual1, actual2), "Quaternion.CreateFromAxisAngle did not return the expected value."); + Assert.True(MathHelper.Equal(actual1, actual2), $"Quaternion.CreateFromAxisAngle did not return the expected value: actual1 {actual1} actual2 {actual2}"); } [Fact] @@ -358,7 +358,7 @@ public void QuaternionCreateFromYawPitchRollTest1() Quaternion expected = yaw * pitch * roll; Quaternion actual = Quaternion.CreateFromYawPitchRoll(yawAngle, pitchAngle, rollAngle); - Assert.True(MathHelper.Equal(expected, actual)); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.QuaternionCreateFromYawPitchRollTest1 did not return the expected value: expected {expected} actual {actual}"); } // Covers more numeric rigions @@ -383,7 +383,7 @@ public void QuaternionCreateFromYawPitchRollTest2() Quaternion expected = yaw * pitch * roll; Quaternion actual = Quaternion.CreateFromYawPitchRoll(yawRad, pitchRad, rollRad); - Assert.True(MathHelper.Equal(expected, actual), String.Format("Yaw:{0} Pitch:{1} Roll:{2}", yawAngle, pitchAngle, rollAngle)); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.QuaternionCreateFromYawPitchRollTest2 Yaw:{yawAngle} Pitch:{pitchAngle} Roll:{rollAngle} did not return the expected value: expected {expected} actual {actual}"); } } } @@ -403,12 +403,12 @@ public void QuaternionSlerpTest() Quaternion actual; actual = Quaternion.Slerp(a, b, t); - Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Slerp did not return the expected value."); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.Slerp did not return the expected value: expected {expected} actual {actual}"); // Case a and b are same. expected = a; actual = Quaternion.Slerp(a, a, t); - Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Slerp did not return the expected value."); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.Slerp did not return the expected value: expected {expected} actual {actual}"); } // A test for Slerp (Quaternion, Quaternion, float) @@ -424,7 +424,7 @@ public void QuaternionSlerpTest1() Quaternion expected = new Quaternion(a.X, a.Y, a.Z, a.W); Quaternion actual = Quaternion.Slerp(a, b, t); - Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Slerp did not return the expected value."); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.Slerp did not return the expected value: expected {expected} actual {actual}"); } // A test for Slerp (Quaternion, Quaternion, float) @@ -440,7 +440,7 @@ public void QuaternionSlerpTest2() Quaternion expected = new Quaternion(b.X, b.Y, b.Z, b.W); Quaternion actual = Quaternion.Slerp(a, b, t); - Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Slerp did not return the expected value."); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.Slerp did not return the expected value: expected {expected} actual {actual}"); } // A test for Slerp (Quaternion, Quaternion, float) @@ -459,7 +459,7 @@ public void QuaternionSlerpTest3() // Note that in quaternion world, Q == -Q. In the case of quaternions dot product is zero, // one of the quaternion will be flipped to compute the shortest distance. When t = 1, we // expect the result to be the same as quaternion b but flipped. - Assert.True(actual == expected, "Quaternion.Slerp did not return the expected value."); + Assert.True(actual == expected, $"Quaternion.Slerp did not return the expected value: expected {expected} actual {actual}"); } // A test for Slerp (Quaternion, Quaternion, float) @@ -475,7 +475,7 @@ public void QuaternionSlerpTest4() Quaternion expected = new Quaternion(a.X, a.Y, a.Z, a.W); Quaternion actual = Quaternion.Slerp(a, b, t); - Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Slerp did not return the expected value."); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.Slerp did not return the expected value: expected {expected} actual {actual}"); } // A test for operator - (Quaternion) @@ -489,7 +489,7 @@ public void QuaternionUnaryNegationTest() actual = -a; - Assert.True(MathHelper.Equal(expected, actual), "Quaternion.operator - did not return the expected value."); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.operator - did not return the expected value: expected {expected} actual {actual}"); } // A test for Inverse (Quaternion) @@ -514,7 +514,7 @@ public void QuaternionInverseTest1() Quaternion actual = Quaternion.Inverse(a); Assert.True(float.IsNaN(actual.X) && float.IsNaN(actual.Y) && float.IsNaN(actual.Z) && float.IsNaN(actual.W) - ); + , $"Quaternion.Inverse - did not return the expected value: expected {new Quaternion(float.NaN, float.NaN, float.NaN, float.NaN)} actual {actual}"); } // A test for ToString () @@ -556,7 +556,7 @@ public void QuaternionDivideTest() Quaternion actual; actual = Quaternion.Divide(a, b); - Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Divide did not return the expected value."); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.Divide did not return the expected value: expected {expected} actual {actual}"); } // A test for Equals (object) @@ -615,7 +615,7 @@ public void QuaternionMultiplyTest2() Quaternion actual; actual = Quaternion.Multiply(a, factor); - Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Multiply did not return the expected value."); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.Multiply did not return the expected value: expected {expected} actual {actual}"); } // A test for Multiply (Quaternion, Quaternion) @@ -629,7 +629,7 @@ public void QuaternionMultiplyTest3() Quaternion actual; actual = Quaternion.Multiply(a, b); - Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Multiply did not return the expected value."); + Assert.True(MathHelper.Equal(expected, actual), $"Quaternion.Multiply did not return the expected value: expected {expected} actual {actual}"); } // A test for Negate (Quaternion) @@ -706,11 +706,13 @@ public void QuaternionFromRotationMatrixTest1() Quaternion expected = new Quaternion(0.0f, 0.0f, 0.0f, 1.0f); Quaternion actual = Quaternion.CreateFromRotationMatrix(matrix); - Assert.True(MathHelper.Equal(expected, actual), "Quaternion.CreateFromRotationMatrix did not return the expected value."); + Assert.True(MathHelper.Equal(expected, actual), + $"Quaternion.CreateFromRotationMatrix did not return the expected value: expected {expected} actual {actual}"); // make sure convert back to matrix is same as we passed matrix. Matrix4x4 m2 = Matrix4x4.CreateFromQuaternion(actual); - Assert.True(MathHelper.Equal(matrix, m2), "Quaternion.CreateFromRotationMatrix did not return the expected value."); + Assert.True(MathHelper.Equal(matrix, m2), + $"Quaternion.CreateFromQuaternion did not return the expected value: matrix {matrix} m2 {m2}"); } // A test for CreateFromRotationMatrix (Matrix4x4) @@ -725,14 +727,12 @@ public void QuaternionFromRotationMatrixTest2() Quaternion expected = Quaternion.CreateFromAxisAngle(Vector3.UnitX, angle); Quaternion actual = Quaternion.CreateFromRotationMatrix(matrix); Assert.True(MathHelper.EqualRotation(expected, actual), - string.Format("Quaternion.CreateFromRotationMatrix did not return the expected value. angle:{0} expected:{1} actual:{2}", - angle.ToString(), expected.ToString(), actual.ToString())); + $"Quaternion.CreateFromRotationMatrix angle:{angle} did not return the expected value: expected {expected} actual {actual}"); // make sure convert back to matrix is same as we passed matrix. Matrix4x4 m2 = Matrix4x4.CreateFromQuaternion(actual); Assert.True(MathHelper.Equal(matrix, m2), - string.Format("Quaternion.CreateFromRotationMatrix did not return the expected value. angle:{0} expected:{1} actual:{2}", - angle.ToString(), matrix.ToString(), m2.ToString())); + $"Quaternion.CreateFromQuaternion angle:{angle} did not return the expected value: matrix {matrix} m2 {m2}"); } } @@ -748,14 +748,12 @@ public void QuaternionFromRotationMatrixTest3() Quaternion expected = Quaternion.CreateFromAxisAngle(Vector3.UnitY, angle); Quaternion actual = Quaternion.CreateFromRotationMatrix(matrix); Assert.True(MathHelper.EqualRotation(expected, actual), - string.Format("Quaternion.CreateFromRotationMatrix did not return the expected value. angle:{0}", - angle.ToString())); + $"Quaternion.CreateFromRotationMatrix angle:{angle} did not return the expected value: expected {expected} actual {actual}"); // make sure convert back to matrix is same as we passed matrix. Matrix4x4 m2 = Matrix4x4.CreateFromQuaternion(actual); Assert.True(MathHelper.Equal(matrix, m2), - string.Format("Quaternion.CreateFromRotationMatrix did not return the expected value. angle:{0}", - angle.ToString())); + $"Quaternion.CreateFromQuaternion angle:{angle} did not return the expected value: matrix {matrix} m2 {m2}"); } } @@ -771,14 +769,12 @@ public void QuaternionFromRotationMatrixTest4() Quaternion expected = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, angle); Quaternion actual = Quaternion.CreateFromRotationMatrix(matrix); Assert.True(MathHelper.EqualRotation(expected, actual), - string.Format("Quaternion.CreateFromRotationMatrix did not return the expected value. angle:{0} expected:{1} actual:{2}", - angle.ToString(), expected.ToString(), actual.ToString())); + $"Quaternion.CreateFromRotationMatrix angle:{angle} did not return the expected value: expected {expected} actual {actual}"); // make sure convert back to matrix is same as we passed matrix. Matrix4x4 m2 = Matrix4x4.CreateFromQuaternion(actual); Assert.True(MathHelper.Equal(matrix, m2), - string.Format("Quaternion.CreateFromRotationMatrix did not return the expected value. angle:{0} expected:{1} actual:{2}", - angle.ToString(), matrix.ToString(), m2.ToString())); + $"Quaternion.CreateFromQuaternion angle:{angle} did not return the expected value: matrix {matrix} m2 {m2}"); } } @@ -798,14 +794,12 @@ public void QuaternionFromRotationMatrixTest5() Quaternion actual = Quaternion.CreateFromRotationMatrix(matrix); Assert.True(MathHelper.EqualRotation(expected, actual), - string.Format("Quaternion.CreateFromRotationMatrix did not return the expected value. angle:{0} expected:{1} actual:{2}", - angle.ToString(), expected.ToString(), actual.ToString())); + $"Quaternion.CreateFromRotationMatrix angle:{angle} did not return the expected value: expected {expected} actual {actual}"); // make sure convert back to matrix is same as we passed matrix. Matrix4x4 m2 = Matrix4x4.CreateFromQuaternion(actual); Assert.True(MathHelper.Equal(matrix, m2), - string.Format("Quaternion.CreateFromRotationMatrix did not return the expected value. angle:{0} expected:{1} actual:{2}", - angle.ToString(), matrix.ToString(), m2.ToString())); + $"Quaternion.CreateFromQuaternion angle:{angle} did not return the expected value: matrix {matrix} m2 {m2}"); } } @@ -819,11 +813,13 @@ public void QuaternionFromRotationMatrixWithScaledMatrixTest1() Quaternion expected = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, angle) * Quaternion.CreateFromAxisAngle(Vector3.UnitY, angle); Quaternion actual = Quaternion.CreateFromRotationMatrix(matrix); - Assert.True(MathHelper.EqualRotation(expected, actual), "Quaternion.CreateFromRotationMatrix did not return the expected value."); + Assert.True(MathHelper.EqualRotation(expected, actual), + $"Quaternion.CreateFromRotationMatrix did not return the expected value: expected {expected} actual {actual}"); // make sure convert back to matrix is same as we passed matrix. Matrix4x4 m2 = Matrix4x4.CreateFromQuaternion(actual); - Assert.True(MathHelper.Equal(matrix, m2), "Quaternion.CreateFromRotationMatrix did not return the expected value."); + Assert.True(MathHelper.Equal(matrix, m2), + $"Quaternion.CreateFromQuaternion did not return the expected value: matrix {matrix} m2 {m2}"); } // A test for CreateFromRotationMatrix (Matrix4x4) @@ -836,11 +832,13 @@ public void QuaternionFromRotationMatrixWithScaledMatrixTest2() Quaternion expected = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, angle) * Quaternion.CreateFromAxisAngle(Vector3.UnitX, angle); Quaternion actual = Quaternion.CreateFromRotationMatrix(matrix); - Assert.True(MathHelper.EqualRotation(expected, actual), "Quaternion.CreateFromRotationMatrix did not return the expected value."); + Assert.True(MathHelper.EqualRotation(expected, actual), + $"Quaternion.CreateFromRotationMatrix did not return the expected value: expected {expected} actual {actual}"); // make sure convert back to matrix is same as we passed matrix. Matrix4x4 m2 = Matrix4x4.CreateFromQuaternion(actual); - Assert.True(MathHelper.Equal(matrix, m2), "Quaternion.CreateFromRotationMatrix did not return the expected value."); + Assert.True(MathHelper.Equal(matrix, m2), + $"Quaternion.CreateFromQuaternion did not return the expected value: matrix {matrix} m2 {m2}"); } // A test for CreateFromRotationMatrix (Matrix4x4) @@ -853,11 +851,13 @@ public void QuaternionFromRotationMatrixWithScaledMatrixTest3() Quaternion expected = Quaternion.CreateFromAxisAngle(Vector3.UnitY, angle) * Quaternion.CreateFromAxisAngle(Vector3.UnitX, angle); Quaternion actual = Quaternion.CreateFromRotationMatrix(matrix); - Assert.True(MathHelper.EqualRotation(expected, actual), "Quaternion.CreateFromRotationMatrix did not return the expected value."); + Assert.True(MathHelper.EqualRotation(expected, actual), + $"Quaternion.CreateFromRotationMatrix did not return the expected value: expected {expected} actual {actual}"); // make sure convert back to matrix is same as we passed matrix. Matrix4x4 m2 = Matrix4x4.CreateFromQuaternion(actual); - Assert.True(MathHelper.Equal(matrix, m2), "Quaternion.CreateFromRotationMatrix did not return the expected value."); + Assert.True(MathHelper.Equal(matrix, m2), + $"Quaternion.CreateFromQuaternion did not return the expected value: matrix {matrix} m2 {m2}"); } // A test for Equals (Quaternion)