Files
Speckle.DoubleNumerics/Speckle.DoubleNumerics.Tests/QuaternionTests.cs
T
Adam Hathcock acdc486510 Cleanup (#1)
* Initial update for net8 and netstandard2

* delete more and fix test folders

* clean up changes

* format

* more clean up

* allow build script to run

* format again

* fix packing and tests

* do tests always

* fmt again
2024-06-25 15:02:20 +01:00

1066 lines
34 KiB
C#

// 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;
using System.Globalization;
using System.Runtime.InteropServices;
using Xunit;
namespace Speckle.DoubleNumerics.Tests;
public class QuaternionTests
{
// A test for Dot (Quaternion, Quaternion)
[Fact]
public void QuaternionDotTest()
{
Quaternion a = new Quaternion(1.0, 2.0, 3.0, 4.0);
Quaternion b = new Quaternion(5.0, 6.0, 7.0, 8.0);
double expected = 70.0;
double actual;
actual = Quaternion.Dot(a, b);
Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Dot did not return the expected value.");
}
// A test for Length ()
[Fact]
public void QuaternionLengthTest()
{
Vector3 v = new Vector3(1.0, 2.0, 3.0);
double w = 4.0;
Quaternion target = new Quaternion(v, w);
double expected = 5.477226;
double actual;
actual = target.Length();
Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Length did not return the expected value.");
}
// A test for LengthSquared ()
[Fact]
public void QuaternionLengthSquaredTest()
{
Vector3 v = new Vector3(1.0, 2.0, 3.0);
double w = 4.0;
Quaternion target = new Quaternion(v, w);
double expected = 30.0;
double actual;
actual = target.LengthSquared();
Assert.True(MathHelper.Equal(expected, actual), "Quaternion.LengthSquared did not return the expected value.");
}
// A test for Lerp (Quaternion, Quaternion, double)
[Fact]
public void QuaternionLerpTest()
{
Vector3 axis = Vector3.Normalize(new Vector3(1.0, 2.0, 3.0));
Quaternion a = Quaternion.CreateFromAxisAngle(axis, MathHelper.ToRadians(10.0));
Quaternion b = Quaternion.CreateFromAxisAngle(axis, MathHelper.ToRadians(30.0));
double t = 0.5;
Quaternion expected = Quaternion.CreateFromAxisAngle(axis, MathHelper.ToRadians(20.0));
Quaternion actual;
actual = Quaternion.Lerp(a, b, t);
Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Lerp did not return the expected value.");
// 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.");
}
// A test for Lerp (Quaternion, Quaternion, double)
// Lerp test when t = 0
[Fact]
public void QuaternionLerpTest1()
{
Vector3 axis = Vector3.Normalize(new Vector3(1.0, 2.0, 3.0));
Quaternion a = Quaternion.CreateFromAxisAngle(axis, MathHelper.ToRadians(10.0));
Quaternion b = Quaternion.CreateFromAxisAngle(axis, MathHelper.ToRadians(30.0));
double t = 0.0;
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.");
}
// A test for Lerp (Quaternion, Quaternion, double)
// Lerp test when t = 1
[Fact]
public void QuaternionLerpTest2()
{
Vector3 axis = Vector3.Normalize(new Vector3(1.0, 2.0, 3.0));
Quaternion a = Quaternion.CreateFromAxisAngle(axis, MathHelper.ToRadians(10.0));
Quaternion b = Quaternion.CreateFromAxisAngle(axis, MathHelper.ToRadians(30.0));
double t = 1.0;
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.");
}
// A test for Lerp (Quaternion, Quaternion, double)
// Lerp test when the two quaternions are more than 90 degree (dot product <0)
[Fact]
public void QuaternionLerpTest3()
{
Vector3 axis = Vector3.Normalize(new Vector3(1.0, 2.0, 3.0));
Quaternion a = Quaternion.CreateFromAxisAngle(axis, MathHelper.ToRadians(10.0));
Quaternion b = Quaternion.Negate(a);
double t = 1.0;
Quaternion actual = Quaternion.Lerp(a, b, t);
// 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.");
}
// A test for Conjugate(Quaternion)
[Fact]
public void QuaternionConjugateTest1()
{
Quaternion a = new Quaternion(1, 2, 3, 4);
Quaternion expected = new Quaternion(-1, -2, -3, 4);
Quaternion actual;
actual = Quaternion.Conjugate(a);
Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Conjugate did not return the expected value.");
}
// A test for Normalize (Quaternion)
[Fact]
public void QuaternionNormalizeTest()
{
Quaternion a = new Quaternion(1.0, 2.0, 3.0, 4.0);
Quaternion expected = new Quaternion(0.182574168, 0.365148336, 0.5477225, 0.7302967);
Quaternion actual;
actual = Quaternion.Normalize(a);
Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Normalize did not return the expected value.");
}
// A test for Normalize (Quaternion)
// Normalize zero length quaternion
[Fact]
public void QuaternionNormalizeTest1()
{
Quaternion a = new Quaternion(0.0, 0.0, -0.0, 0.0);
Quaternion actual = Quaternion.Normalize(a);
Assert.True(
double.IsNaN(actual.X) && double.IsNaN(actual.Y) && double.IsNaN(actual.Z) && double.IsNaN(actual.W),
"Quaternion.Normalize did not return the expected value."
);
}
// A test for Concatenate(Quaternion, Quaternion)
[Fact]
public void QuaternionConcatenateTest1()
{
Quaternion b = new Quaternion(1.0, 2.0, 3.0, 4.0);
Quaternion a = new Quaternion(5.0, 6.0, 7.0, 8.0);
Quaternion expected = new Quaternion(24.0, 48.0, 48.0, -6.0);
Quaternion actual;
actual = Quaternion.Concatenate(a, b);
Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Concatenate did not return the expected value.");
}
// A test for operator - (Quaternion, Quaternion)
[Fact]
public void QuaternionSubtractionTest()
{
Quaternion a = new Quaternion(1.0, 6.0, 7.0, 4.0);
Quaternion b = new Quaternion(5.0, 2.0, 3.0, 8.0);
Quaternion expected = new Quaternion(-4.0, 4.0, 4.0, -4.0);
Quaternion actual;
actual = a - b;
Assert.True(MathHelper.Equal(expected, actual), "Quaternion.operator - did not return the expected value.");
}
// A test for operator * (Quaternion, double)
[Fact]
public void QuaternionMultiplyTest()
{
Quaternion a = new Quaternion(1.0, 2.0, 3.0, 4.0);
double factor = 0.5;
Quaternion expected = new Quaternion(0.5, 1.0, 1.5, 2.0);
Quaternion actual;
actual = a * factor;
Assert.True(MathHelper.Equal(expected, actual), "Quaternion.operator * did not return the expected value.");
}
// A test for operator * (Quaternion, Quaternion)
[Fact]
public void QuaternionMultiplyTest1()
{
Quaternion a = new Quaternion(1.0, 2.0, 3.0, 4.0);
Quaternion b = new Quaternion(5.0, 6.0, 7.0, 8.0);
Quaternion expected = new Quaternion(24.0, 48.0, 48.0, -6.0);
Quaternion actual;
actual = a * b;
Assert.True(MathHelper.Equal(expected, actual), "Quaternion.operator * did not return the expected value.");
}
// A test for operator / (Quaternion, Quaternion)
[Fact]
public void QuaternionDivisionTest1()
{
Quaternion a = new Quaternion(1.0, 2.0, 3.0, 4.0);
Quaternion b = new Quaternion(5.0, 6.0, 7.0, 8.0);
Quaternion expected = new Quaternion(-0.045977015, -0.09195402, -7.450581E-9, 0.402298868);
Quaternion actual;
actual = a / b;
Assert.True(MathHelper.Equal(expected, actual), "Quaternion.operator / did not return the expected value.");
}
// A test for operator + (Quaternion, Quaternion)
[Fact]
public void QuaternionAdditionTest()
{
Quaternion a = new Quaternion(1.0, 2.0, 3.0, 4.0);
Quaternion b = new Quaternion(5.0, 6.0, 7.0, 8.0);
Quaternion expected = new Quaternion(6.0, 8.0, 10.0, 12.0);
Quaternion actual;
actual = a + b;
Assert.True(MathHelper.Equal(expected, actual), "Quaternion.operator + did not return the expected value.");
}
// A test for Quaternion (double, double, double, double)
[Fact]
public void QuaternionConstructorTest()
{
double x = 1.0;
double y = 2.0;
double z = 3.0;
double w = 4.0;
Quaternion target = new Quaternion(x, y, z, w);
Assert.True(
MathHelper.Equal(target.X, x)
&& MathHelper.Equal(target.Y, y)
&& MathHelper.Equal(target.Z, z)
&& MathHelper.Equal(target.W, w),
"Quaternion.constructor (x,y,z,w) did not return the expected value."
);
}
// A test for Quaternion (Vector3, double)
[Fact]
public void QuaternionConstructorTest1()
{
Vector3 v = new Vector3(1.0, 2.0, 3.0);
double w = 4.0;
Quaternion target = new Quaternion(v, w);
Assert.True(
MathHelper.Equal(target.X, v.X)
&& MathHelper.Equal(target.Y, v.Y)
&& MathHelper.Equal(target.Z, v.Z)
&& MathHelper.Equal(target.W, w),
"Quaternion.constructor (Vector3,w) did not return the expected value."
);
}
// A test for CreateFromAxisAngle (Vector3, double)
[Fact]
public void QuaternionCreateFromAxisAngleTest()
{
Vector3 axis = Vector3.Normalize(new Vector3(1.0, 2.0, 3.0));
double angle = MathHelper.ToRadians(30.0);
Quaternion expected = new Quaternion(0.0691723, 0.1383446, 0.207516879, 0.9659258);
Quaternion actual;
actual = Quaternion.CreateFromAxisAngle(axis, angle);
Assert.True(
MathHelper.Equal(expected, actual),
"Quaternion.CreateFromAxisAngle did not return the expected value."
);
}
// A test for CreateFromAxisAngle (Vector3, double)
// CreateFromAxisAngle of zero vector
[Fact]
public void QuaternionCreateFromAxisAngleTest1()
{
Vector3 axis = new Vector3();
double angle = MathHelper.ToRadians(-30.0);
double cos = Math.Cos(angle / 2.0);
Quaternion actual = Quaternion.CreateFromAxisAngle(axis, angle);
Assert.True(
actual.X == 0.0 && actual.Y == 0.0 && actual.Z == 0.0 && MathHelper.Equal(cos, actual.W),
"Quaternion.CreateFromAxisAngle did not return the expected value."
);
}
// A test for CreateFromAxisAngle (Vector3, double)
// CreateFromAxisAngle of angle = 30 && 750
[Fact]
public void QuaternionCreateFromAxisAngleTest2()
{
Vector3 axis = new Vector3(1, 0, 0);
double angle1 = MathHelper.ToRadians(30.0);
double angle2 = MathHelper.ToRadians(750.0);
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."
);
}
// A test for CreateFromAxisAngle (Vector3, double)
// CreateFromAxisAngle of angle = 30 && 390
[Fact]
public void QuaternionCreateFromAxisAngleTest3()
{
Vector3 axis = new Vector3(1, 0, 0);
double angle1 = MathHelper.ToRadians(30.0);
double angle2 = MathHelper.ToRadians(390.0);
Quaternion actual1 = Quaternion.CreateFromAxisAngle(axis, angle1);
Quaternion actual2 = Quaternion.CreateFromAxisAngle(axis, angle2);
actual1.X = -actual1.X;
actual1.W = -actual1.W;
Assert.True(
MathHelper.Equal(actual1, actual2),
"Quaternion.CreateFromAxisAngle did not return the expected value."
);
}
[Fact]
public void QuaternionCreateFromYawPitchRollTest1()
{
double yawAngle = MathHelper.ToRadians(30.0);
double pitchAngle = MathHelper.ToRadians(40.0);
double rollAngle = MathHelper.ToRadians(50.0);
Quaternion yaw = Quaternion.CreateFromAxisAngle(Vector3.UnitY, yawAngle);
Quaternion pitch = Quaternion.CreateFromAxisAngle(Vector3.UnitX, pitchAngle);
Quaternion roll = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, rollAngle);
Quaternion expected = yaw * pitch * roll;
Quaternion actual = Quaternion.CreateFromYawPitchRoll(yawAngle, pitchAngle, rollAngle);
Assert.True(MathHelper.Equal(expected, actual));
}
// Covers more numeric rigions
[Fact]
public void QuaternionCreateFromYawPitchRollTest2()
{
const double step = 35.0;
for (double yawAngle = -720.0; yawAngle <= 720.0; yawAngle += step)
{
for (double pitchAngle = -720.0; pitchAngle <= 720.0; pitchAngle += step)
{
for (double rollAngle = -720.0; rollAngle <= 720.0; rollAngle += step)
{
double yawRad = MathHelper.ToRadians(yawAngle);
double pitchRad = MathHelper.ToRadians(pitchAngle);
double rollRad = MathHelper.ToRadians(rollAngle);
Quaternion yaw = Quaternion.CreateFromAxisAngle(Vector3.UnitY, yawRad);
Quaternion pitch = Quaternion.CreateFromAxisAngle(Vector3.UnitX, pitchRad);
Quaternion roll = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, rollRad);
Quaternion expected = yaw * pitch * roll;
Quaternion actual = Quaternion.CreateFromYawPitchRoll(yawRad, pitchRad, rollRad);
Assert.True(MathHelper.Equal(expected, actual), $"Yaw:{yawAngle} Pitch:{pitchAngle} Roll:{rollAngle}");
}
}
}
}
// A test for Slerp (Quaternion, Quaternion, double)
[Fact]
public void QuaternionSlerpTest()
{
Vector3 axis = Vector3.Normalize(new Vector3(1.0, 2.0, 3.0));
Quaternion a = Quaternion.CreateFromAxisAngle(axis, MathHelper.ToRadians(10.0));
Quaternion b = Quaternion.CreateFromAxisAngle(axis, MathHelper.ToRadians(30.0));
double t = 0.5;
Quaternion expected = Quaternion.CreateFromAxisAngle(axis, MathHelper.ToRadians(20.0));
Quaternion actual;
actual = Quaternion.Slerp(a, b, t);
Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Slerp did not return the expected value.");
// 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.");
}
// A test for Slerp (Quaternion, Quaternion, double)
// Slerp test where t = 0
[Fact]
public void QuaternionSlerpTest1()
{
Vector3 axis = Vector3.Normalize(new Vector3(1.0, 2.0, 3.0));
Quaternion a = Quaternion.CreateFromAxisAngle(axis, MathHelper.ToRadians(10.0));
Quaternion b = Quaternion.CreateFromAxisAngle(axis, MathHelper.ToRadians(30.0));
double t = 0.0;
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.");
}
// A test for Slerp (Quaternion, Quaternion, double)
// Slerp test where t = 1
[Fact]
public void QuaternionSlerpTest2()
{
Vector3 axis = Vector3.Normalize(new Vector3(1.0, 2.0, 3.0));
Quaternion a = Quaternion.CreateFromAxisAngle(axis, MathHelper.ToRadians(10.0));
Quaternion b = Quaternion.CreateFromAxisAngle(axis, MathHelper.ToRadians(30.0));
double t = 1.0;
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.");
}
// A test for Slerp (Quaternion, Quaternion, double)
// Slerp test where dot product is < 0
[Fact]
public void QuaternionSlerpTest3()
{
Vector3 axis = Vector3.Normalize(new Vector3(1.0, 2.0, 3.0));
Quaternion a = Quaternion.CreateFromAxisAngle(axis, MathHelper.ToRadians(10.0));
Quaternion b = -a;
double t = 1.0;
Quaternion expected = a;
Quaternion actual = Quaternion.Slerp(a, b, t);
// 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.");
}
// A test for Slerp (Quaternion, Quaternion, double)
// Slerp test where the quaternion is flipped
[Fact]
public void QuaternionSlerpTest4()
{
Vector3 axis = Vector3.Normalize(new Vector3(1.0, 2.0, 3.0));
Quaternion a = Quaternion.CreateFromAxisAngle(axis, MathHelper.ToRadians(10.0));
Quaternion b = -Quaternion.CreateFromAxisAngle(axis, MathHelper.ToRadians(30.0));
double t = 0.0;
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.");
}
// A test for operator - (Quaternion)
[Fact]
public void QuaternionUnaryNegationTest()
{
Quaternion a = new Quaternion(1.0, 2.0, 3.0, 4.0);
Quaternion expected = new Quaternion(-1.0, -2.0, -3.0, -4.0);
Quaternion actual;
actual = -a;
Assert.True(MathHelper.Equal(expected, actual), "Quaternion.operator - did not return the expected value.");
}
// A test for Inverse (Quaternion)
[Fact]
public void QuaternionInverseTest()
{
Quaternion a = new Quaternion(5.0, 6.0, 7.0, 8.0);
Quaternion expected = new Quaternion(
-0.028735632183908046,
-0.034482758620689655,
-0.040229885057471264,
0.045977011494252873
);
Quaternion actual;
actual = Quaternion.Inverse(a);
Assert.Equal(expected, actual);
}
// A test for Inverse (Quaternion)
// Invert zero length quaternion
[Fact]
public void QuaternionInverseTest1()
{
Quaternion a = new Quaternion();
Quaternion actual = Quaternion.Inverse(a);
Assert.True(double.IsNaN(actual.X) && double.IsNaN(actual.Y) && double.IsNaN(actual.Z) && double.IsNaN(actual.W));
}
// A test for ToString ()
[Fact]
public void QuaternionToStringTest()
{
Quaternion target = new Quaternion(-1.0, 2.2, 3.3, -4.4);
string expected = string.Format(CultureInfo.CurrentCulture, "{{X:{0} Y:{1} Z:{2} W:{3}}}", -1.0, 2.2, 3.3, -4.4);
string actual = target.ToString();
Assert.Equal(expected, actual);
}
// A test for Add (Quaternion, Quaternion)
[Fact]
public void QuaternionAddTest()
{
Quaternion a = new Quaternion(1.0, 2.0, 3.0, 4.0);
Quaternion b = new Quaternion(5.0, 6.0, 7.0, 8.0);
Quaternion expected = new Quaternion(6.0, 8.0, 10.0, 12.0);
Quaternion actual;
actual = Quaternion.Add(a, b);
Assert.Equal(expected, actual);
}
// A test for Divide (Quaternion, Quaternion)
[Fact]
public void QuaternionDivideTest()
{
Quaternion a = new Quaternion(1.0, 2.0, 3.0, 4.0);
Quaternion b = new Quaternion(5.0, 6.0, 7.0, 8.0);
Quaternion expected = new Quaternion(-0.045977015, -0.09195402, -7.450581E-9, 0.402298868);
Quaternion actual;
actual = Quaternion.Divide(a, b);
Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Divide did not return the expected value.");
}
// A test for Equals (object)
[Fact]
public void QuaternionEqualsTest()
{
Quaternion a = new Quaternion(1.0, 2.0, 3.0, 4.0);
Quaternion b = new Quaternion(1.0, 2.0, 3.0, 4.0);
// case 1: compare between same values
object? obj = b;
bool expected = true;
bool actual = a.Equals(obj);
Assert.Equal(expected, actual);
// case 2: compare between different values
b.X = 10.0;
obj = b;
expected = false;
actual = a.Equals(obj);
Assert.Equal(expected, actual);
// case 3: compare between different types.
obj = new Vector4();
expected = false;
actual = a.Equals(obj);
Assert.Equal(expected, actual);
// case 3: compare against null.
obj = null;
expected = false;
actual = a.Equals(obj);
Assert.Equal(expected, actual);
}
// A test for GetHashCode ()
[Fact]
public void QuaternionGetHashCodeTest()
{
Quaternion a = new Quaternion(1.0, 2.0, 3.0, 4.0);
int expected = a.X.GetHashCode() + a.Y.GetHashCode() + a.Z.GetHashCode() + a.W.GetHashCode();
int actual = a.GetHashCode();
Assert.Equal(expected, actual);
}
// A test for Multiply (Quaternion, double)
[Fact]
public void QuaternionMultiplyTest2()
{
Quaternion a = new Quaternion(1.0, 2.0, 3.0, 4.0);
double factor = 0.5;
Quaternion expected = new Quaternion(0.5, 1.0, 1.5, 2.0);
Quaternion actual;
actual = Quaternion.Multiply(a, factor);
Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Multiply did not return the expected value.");
}
// A test for Multiply (Quaternion, Quaternion)
[Fact]
public void QuaternionMultiplyTest3()
{
Quaternion a = new Quaternion(1.0, 2.0, 3.0, 4.0);
Quaternion b = new Quaternion(5.0, 6.0, 7.0, 8.0);
Quaternion expected = new Quaternion(24.0, 48.0, 48.0, -6.0);
Quaternion actual;
actual = Quaternion.Multiply(a, b);
Assert.True(MathHelper.Equal(expected, actual), "Quaternion.Multiply did not return the expected value.");
}
// A test for Negate (Quaternion)
[Fact]
public void QuaternionNegateTest()
{
Quaternion a = new Quaternion(1.0, 2.0, 3.0, 4.0);
Quaternion expected = new Quaternion(-1.0, -2.0, -3.0, -4.0);
Quaternion actual;
actual = Quaternion.Negate(a);
Assert.Equal(expected, actual);
}
// A test for Subtract (Quaternion, Quaternion)
[Fact]
public void QuaternionSubtractTest()
{
Quaternion a = new Quaternion(1.0, 6.0, 7.0, 4.0);
Quaternion b = new Quaternion(5.0, 2.0, 3.0, 8.0);
Quaternion expected = new Quaternion(-4.0, 4.0, 4.0, -4.0);
Quaternion actual;
actual = Quaternion.Subtract(a, b);
Assert.Equal(expected, actual);
}
// A test for operator != (Quaternion, Quaternion)
[Fact]
public void QuaternionInequalityTest()
{
Quaternion a = new Quaternion(1.0, 2.0, 3.0, 4.0);
Quaternion b = new Quaternion(1.0, 2.0, 3.0, 4.0);
// case 1: compare between same values
bool expected = false;
bool actual = a != b;
Assert.Equal(expected, actual);
// case 2: compare between different values
b.X = 10.0;
expected = true;
actual = a != b;
Assert.Equal(expected, actual);
}
// A test for operator == (Quaternion, Quaternion)
[Fact]
public void QuaternionEqualityTest()
{
Quaternion a = new Quaternion(1.0, 2.0, 3.0, 4.0);
Quaternion b = new Quaternion(1.0, 2.0, 3.0, 4.0);
// case 1: compare between same values
bool expected = true;
bool actual = a == b;
Assert.Equal(expected, actual);
// case 2: compare between different values
b.X = 10.0;
expected = false;
actual = a == b;
Assert.Equal(expected, actual);
}
// A test for CreateFromRotationMatrix (Matrix4x4)
// Convert Identity matrix test
[Fact]
public void QuaternionFromRotationMatrixTest1()
{
Matrix4x4 matrix = Matrix4x4.Identity;
Quaternion expected = new Quaternion(0.0, 0.0, 0.0, 1.0);
Quaternion actual = Quaternion.CreateFromRotationMatrix(matrix);
Assert.True(
MathHelper.Equal(expected, actual),
"Quaternion.CreateFromRotationMatrix did not return the expected value."
);
// 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.");
}
// A test for CreateFromRotationMatrix (Matrix4x4)
// Convert X axis rotation matrix
[Fact]
public void QuaternionFromRotationMatrixTest2()
{
for (double angle = 0.0; angle < 720.0; angle += 10.0)
{
Matrix4x4 matrix = Matrix4x4.CreateRotationX(angle);
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()
)
);
// 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()
)
);
}
}
// A test for CreateFromRotationMatrix (Matrix4x4)
// Convert Y axis rotation matrix
[Fact]
public void QuaternionFromRotationMatrixTest3()
{
for (double angle = 0.0; angle < 720.0; angle += 10.0)
{
Matrix4x4 matrix = Matrix4x4.CreateRotationY(angle);
Quaternion expected = Quaternion.CreateFromAxisAngle(Vector3.UnitY, angle);
Quaternion actual = Quaternion.CreateFromRotationMatrix(matrix);
Assert.True(
MathHelper.EqualRotation(expected, actual),
$"Quaternion.CreateFromRotationMatrix did not return the expected value. angle:{angle.ToString()}"
);
// 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. angle:{angle.ToString()}"
);
}
}
// A test for CreateFromRotationMatrix (Matrix4x4)
// Convert Z axis rotation matrix
[Fact]
public void QuaternionFromRotationMatrixTest4()
{
for (double angle = 0.0; angle < 720.0; angle += 10.0)
{
Matrix4x4 matrix = Matrix4x4.CreateRotationZ(angle);
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()
)
);
// 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()
)
);
}
}
// A test for CreateFromRotationMatrix (Matrix4x4)
// Convert XYZ axis rotation matrix
[Fact]
public void QuaternionFromRotationMatrixTest5()
{
for (double angle = 0.0; angle < 720.0; angle += 10.0)
{
Matrix4x4 matrix =
Matrix4x4.CreateRotationX(angle) * Matrix4x4.CreateRotationY(angle) * Matrix4x4.CreateRotationZ(angle);
Quaternion expected =
Quaternion.CreateFromAxisAngle(Vector3.UnitZ, angle)
* Quaternion.CreateFromAxisAngle(Vector3.UnitY, angle)
* 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()
)
);
// 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()
)
);
}
}
// A test for CreateFromRotationMatrix (Matrix4x4)
// X axis is most large axis case
[Fact]
public void QuaternionFromRotationMatrixWithScaledMatrixTest1()
{
double angle = MathHelper.ToRadians(180.0);
Matrix4x4 matrix = Matrix4x4.CreateRotationY(angle) * Matrix4x4.CreateRotationZ(angle);
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."
);
// 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.");
}
// A test for CreateFromRotationMatrix (Matrix4x4)
// Y axis is most large axis case
[Fact]
public void QuaternionFromRotationMatrixWithScaledMatrixTest2()
{
double angle = MathHelper.ToRadians(180.0);
Matrix4x4 matrix = Matrix4x4.CreateRotationX(angle) * Matrix4x4.CreateRotationZ(angle);
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."
);
// 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.");
}
// A test for CreateFromRotationMatrix (Matrix4x4)
// Z axis is most large axis case
[Fact]
public void QuaternionFromRotationMatrixWithScaledMatrixTest3()
{
double angle = MathHelper.ToRadians(180.0);
Matrix4x4 matrix = Matrix4x4.CreateRotationX(angle) * Matrix4x4.CreateRotationY(angle);
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."
);
// 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.");
}
// A test for Equals (Quaternion)
[Fact]
public void QuaternionEqualsTest1()
{
Quaternion a = new Quaternion(1.0, 2.0, 3.0, 4.0);
Quaternion b = new Quaternion(1.0, 2.0, 3.0, 4.0);
// case 1: compare between same values
bool expected = true;
bool actual = a.Equals(b);
Assert.Equal(expected, actual);
// case 2: compare between different values
b.X = 10.0;
expected = false;
actual = a.Equals(b);
Assert.Equal(expected, actual);
}
// A test for Identity
[Fact]
public void QuaternionIdentityTest()
{
Quaternion val = new Quaternion(0, 0, 0, 1);
Assert.Equal(val, Quaternion.Identity);
}
// A test for IsIdentity
[Fact]
public void QuaternionIsIdentityTest()
{
Assert.True(Quaternion.Identity.IsIdentity);
Assert.True(new Quaternion(0, 0, 0, 1).IsIdentity);
Assert.False(new Quaternion(1, 0, 0, 1).IsIdentity);
Assert.False(new Quaternion(0, 1, 0, 1).IsIdentity);
Assert.False(new Quaternion(0, 0, 1, 1).IsIdentity);
Assert.False(new Quaternion(0, 0, 0, 0).IsIdentity);
}
// A test for Quaternion comparison involving NaN values
[Fact]
public void QuaternionEqualsNanTest()
{
Quaternion a = new Quaternion(double.NaN, 0, 0, 0);
Quaternion b = new Quaternion(0, double.NaN, 0, 0);
Quaternion c = new Quaternion(0, 0, double.NaN, 0);
Quaternion d = new Quaternion(0, 0, 0, double.NaN);
Assert.False(a == new Quaternion(0, 0, 0, 0));
Assert.False(b == new Quaternion(0, 0, 0, 0));
Assert.False(c == new Quaternion(0, 0, 0, 0));
Assert.False(d == new Quaternion(0, 0, 0, 0));
Assert.True(a != new Quaternion(0, 0, 0, 0));
Assert.True(b != new Quaternion(0, 0, 0, 0));
Assert.True(c != new Quaternion(0, 0, 0, 0));
Assert.True(d != new Quaternion(0, 0, 0, 0));
Assert.False(a.Equals(new Quaternion(0, 0, 0, 0)));
Assert.False(b.Equals(new Quaternion(0, 0, 0, 0)));
Assert.False(c.Equals(new Quaternion(0, 0, 0, 0)));
Assert.False(d.Equals(new Quaternion(0, 0, 0, 0)));
Assert.False(a.IsIdentity);
Assert.False(b.IsIdentity);
Assert.False(c.IsIdentity);
Assert.False(d.IsIdentity);
// Counterintuitive result - IEEE rules for NaN comparison are weird!
Assert.False(a.Equals(a));
Assert.False(b.Equals(b));
Assert.False(c.Equals(c));
Assert.False(d.Equals(d));
}
// A test to make sure these types are blittable directly into GPU buffer memory layouts
[Fact]
public unsafe void QuaternionSizeofTest()
{
Assert.Equal(16 * 2, sizeof(Quaternion));
Assert.Equal(32 * 2, sizeof(Quaternion_2x));
Assert.Equal(20 * 2, sizeof(QuaternionPlusFloat));
Assert.Equal(40 * 2, sizeof(QuaternionPlusFloat_2x));
}
[StructLayout(LayoutKind.Sequential)]
struct Quaternion_2x
{
private Quaternion _a;
private Quaternion _b;
}
[StructLayout(LayoutKind.Sequential)]
struct QuaternionPlusFloat
{
private Quaternion _v;
private double _f;
}
[StructLayout(LayoutKind.Sequential)]
struct QuaternionPlusFloat_2x
{
private QuaternionPlusFloat _a;
private QuaternionPlusFloat _b;
}
// A test to make sure the fields are laid out how we expect
[Fact]
public unsafe void QuaternionFieldOffsetTest()
{
Quaternion quat = new Quaternion();
double* basePtr = &quat.X; // Take address of first element
Quaternion* quatPtr = &quat; // Take address of whole Quaternion
Assert.Equal(new IntPtr(basePtr), new IntPtr(quatPtr));
Assert.Equal(new IntPtr(basePtr + 0), new IntPtr(&quat.X));
Assert.Equal(new IntPtr(basePtr + 1), new IntPtr(&quat.Y));
Assert.Equal(new IntPtr(basePtr + 2), new IntPtr(&quat.Z));
Assert.Equal(new IntPtr(basePtr + 3), new IntPtr(&quat.W));
}
}