Files
Speckle.DoubleNumerics/Speckle.DoubleNumerics.Tests/Matrix4x4Tests.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

2705 lines
93 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.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.InteropServices;
using Xunit;
namespace Speckle.DoubleNumerics.Tests;
[SuppressMessage("ReSharper", "InconsistentNaming")]
public class Matrix4x4Tests
{
static Matrix4x4 GenerateMatrixNumberFrom1To16()
{
Matrix4x4 a = new();
a.M11 = 1.0;
a.M12 = 2.0;
a.M13 = 3.0;
a.M14 = 4.0;
a.M21 = 5.0;
a.M22 = 6.0;
a.M23 = 7.0;
a.M24 = 8.0;
a.M31 = 9.0;
a.M32 = 10.0;
a.M33 = 11.0;
a.M34 = 12.0;
a.M41 = 13.0;
a.M42 = 14.0;
a.M43 = 15.0;
a.M44 = 16.0;
return a;
}
static Matrix4x4 GenerateTestMatrix()
{
Matrix4x4 m =
Matrix4x4.CreateRotationX(MathHelper.ToRadians(30.0))
* Matrix4x4.CreateRotationY(MathHelper.ToRadians(30.0))
* Matrix4x4.CreateRotationZ(MathHelper.ToRadians(30.0));
m.Translation = new Vector3(111.0, 222.0, 333.0);
return m;
}
// A test for Identity
[Fact]
public void Matrix4x4IdentityTest()
{
Matrix4x4 val = new();
val.M11 = val.M22 = val.M33 = val.M44 = 1.0;
Assert.True(MathHelper.Equal(val, Matrix4x4.Identity), "Matrix4x4.Indentity was not set correctly.");
}
// A test for Determinant
[Fact]
public void Matrix4x4DeterminantTest()
{
Matrix4x4 target =
Matrix4x4.CreateRotationX(MathHelper.ToRadians(30.0))
* Matrix4x4.CreateRotationY(MathHelper.ToRadians(30.0))
* Matrix4x4.CreateRotationZ(MathHelper.ToRadians(30.0));
double val = 1.0;
double det = target.GetDeterminant();
Assert.True(MathHelper.Equal(val, det), "Matrix4x4.Determinant was not set correctly.");
}
// A test for Determinant
// Determinant test |A| = 1 / |A'|
[Fact]
public void Matrix4x4DeterminantTest1()
{
Matrix4x4 a = new();
a.M11 = 5.0;
a.M12 = 2.0;
a.M13 = 8.25;
a.M14 = 1.0;
a.M21 = 12.0;
a.M22 = 6.8;
a.M23 = 2.14;
a.M24 = 9.6;
a.M31 = 6.5;
a.M32 = 1.0;
a.M33 = 3.14;
a.M34 = 2.22;
a.M41 = 0;
a.M42 = 0.86;
a.M43 = 4.0;
a.M44 = 1.0;
Matrix4x4 i;
Assert.True(Matrix4x4.Invert(a, out i));
double detA = a.GetDeterminant();
double detI = i.GetDeterminant();
double t = 1.0 / detI;
// only accurate to 3 precision
Assert.True(Math.Abs(detA - t) < 1e-3, "Matrix4x4.Determinant was not set correctly.");
}
// A test for Invert (Matrix4x4)
[Fact]
public void Matrix4x4InvertTest()
{
Matrix4x4 mtx =
Matrix4x4.CreateRotationX(MathHelper.ToRadians(30.0))
* Matrix4x4.CreateRotationY(MathHelper.ToRadians(30.0))
* Matrix4x4.CreateRotationZ(MathHelper.ToRadians(30.0));
Matrix4x4 expected = new();
expected.M11 = 0.74999994;
expected.M12 = -0.216506317;
expected.M13 = 0.62499994;
expected.M14 = 0.0;
expected.M21 = 0.433012635;
expected.M22 = 0.87499994;
expected.M23 = -0.216506317;
expected.M24 = 0.0;
expected.M31 = -0.49999997;
expected.M32 = 0.433012635;
expected.M33 = 0.74999994;
expected.M34 = 0.0;
expected.M41 = 0.0;
expected.M42 = 0.0;
expected.M43 = 0.0;
expected.M44 = 0.99999994;
Matrix4x4 actual;
Assert.True(Matrix4x4.Invert(mtx, out actual));
Assert.True(MathHelper.Equal(expected, actual), "Matrix4x4.Invert did not return the expected value.");
// Make sure M*M is identity matrix
Matrix4x4 i = mtx * actual;
Assert.True(MathHelper.Equal(i, Matrix4x4.Identity), "Matrix4x4.Invert did not return the expected value.");
}
// A test for Invert (Matrix4x4)
[Fact]
public void Matrix4x4InvertIdentityTest()
{
Matrix4x4 mtx = Matrix4x4.Identity;
Matrix4x4 actual;
Assert.True(Matrix4x4.Invert(mtx, out actual));
Assert.True(MathHelper.Equal(actual, Matrix4x4.Identity));
}
// A test for Invert (Matrix4x4)
[Fact]
public void Matrix4x4InvertTranslationTest()
{
Matrix4x4 mtx = Matrix4x4.CreateTranslation(23, 42, 666);
Matrix4x4 actual;
Assert.True(Matrix4x4.Invert(mtx, out actual));
Matrix4x4 i = mtx * actual;
Assert.True(MathHelper.Equal(i, Matrix4x4.Identity));
}
// A test for Invert (Matrix4x4)
[Fact]
public void Matrix4x4InvertRotationTest()
{
Matrix4x4 mtx = Matrix4x4.CreateFromYawPitchRoll(3, 4, 5);
Matrix4x4 actual;
Assert.True(Matrix4x4.Invert(mtx, out actual));
Matrix4x4 i = mtx * actual;
Assert.True(MathHelper.Equal(i, Matrix4x4.Identity));
}
// A test for Invert (Matrix4x4)
[Fact]
public void Matrix4x4InvertScaleTest()
{
Matrix4x4 mtx = Matrix4x4.CreateScale(23, 42, -666);
Matrix4x4 actual;
Assert.True(Matrix4x4.Invert(mtx, out actual));
Matrix4x4 i = mtx * actual;
Assert.True(MathHelper.Equal(i, Matrix4x4.Identity));
}
// A test for Invert (Matrix4x4)
[Fact]
public void Matrix4x4InvertProjectionTest()
{
Matrix4x4 mtx = Matrix4x4.CreatePerspectiveFieldOfView(1, 1.333, 0.1, 666);
Matrix4x4 actual;
Assert.True(Matrix4x4.Invert(mtx, out actual));
Matrix4x4 i = mtx * actual;
Assert.True(MathHelper.Equal(i, Matrix4x4.Identity));
}
// A test for Invert (Matrix4x4)
[Fact]
public void Matrix4x4InvertAffineTest()
{
Matrix4x4 mtx =
Matrix4x4.CreateFromYawPitchRoll(3, 4, 5)
* Matrix4x4.CreateScale(23, 42, -666)
* Matrix4x4.CreateTranslation(17, 53, 89);
Matrix4x4 actual;
Assert.True(Matrix4x4.Invert(mtx, out actual));
Matrix4x4 i = mtx * actual;
Assert.True(MathHelper.Equal(i, Matrix4x4.Identity));
}
void DecomposeTest(double yaw, double pitch, double roll, Vector3 expectedTranslation, Vector3 expectedScales)
{
Quaternion expectedRotation = Quaternion.CreateFromYawPitchRoll(
MathHelper.ToRadians(yaw),
MathHelper.ToRadians(pitch),
MathHelper.ToRadians(roll)
);
Matrix4x4 m =
Matrix4x4.CreateScale(expectedScales)
* Matrix4x4.CreateFromQuaternion(expectedRotation)
* Matrix4x4.CreateTranslation(expectedTranslation);
Vector3 scales;
Quaternion rotation;
Vector3 translation;
bool actualResult = Matrix4x4.Decompose(m, out scales, out rotation, out translation);
Assert.True(actualResult, "Matrix4x4.Decompose did not return expected value.");
bool scaleIsZeroOrNegative = expectedScales.X <= 0 || expectedScales.Y <= 0 || expectedScales.Z <= 0;
if (scaleIsZeroOrNegative)
{
Assert.True(
MathHelper.Equal(Math.Abs(expectedScales.X), Math.Abs(scales.X)),
"Matrix4x4.Decompose did not return expected value."
);
Assert.True(
MathHelper.Equal(Math.Abs(expectedScales.Y), Math.Abs(scales.Y)),
"Matrix4x4.Decompose did not return expected value."
);
Assert.True(
MathHelper.Equal(Math.Abs(expectedScales.Z), Math.Abs(scales.Z)),
"Matrix4x4.Decompose did not return expected value."
);
}
else
{
Assert.True(
MathHelper.Equal(expectedScales, scales),
$"Matrix4x4.Decompose did not return expected value Expected:{expectedScales} actual:{scales}."
);
Assert.True(
MathHelper.EqualRotation(expectedRotation, rotation),
$"Matrix4x4.Decompose did not return expected value. Expected:{expectedRotation} actual:{rotation}."
);
}
Assert.True(
MathHelper.Equal(expectedTranslation, translation),
$"Matrix4x4.Decompose did not return expected value. Expected:{expectedTranslation} actual:{translation}."
);
}
// Various rotation decompose test.
[Fact]
public void Matrix4x4DecomposeTest01()
{
DecomposeTest(10.0, 20.0, 30.0, new Vector3(10, 20, 30), new Vector3(2, 3, 4));
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)
{
DecomposeTest(yawAngle, pitchAngle, rollAngle, new Vector3(10, 20, 30), new Vector3(2, 3, 4));
}
}
}
}
// Various scaled matrix decompose test.
[Fact]
public void Matrix4x4DecomposeTest02()
{
DecomposeTest(10.0, 20.0, 30.0, new Vector3(10, 20, 30), new Vector3(2, 3, 4));
// Various scales.
DecomposeTest(0, 0, 0, Vector3.Zero, new Vector3(1, 2, 3));
DecomposeTest(0, 0, 0, Vector3.Zero, new Vector3(1, 3, 2));
DecomposeTest(0, 0, 0, Vector3.Zero, new Vector3(2, 1, 3));
DecomposeTest(0, 0, 0, Vector3.Zero, new Vector3(2, 3, 1));
DecomposeTest(0, 0, 0, Vector3.Zero, new Vector3(3, 1, 2));
DecomposeTest(0, 0, 0, Vector3.Zero, new Vector3(3, 2, 1));
DecomposeTest(0, 0, 0, Vector3.Zero, new Vector3(-2, 1, 1));
// Small scales.
DecomposeTest(0, 0, 0, Vector3.Zero, new Vector3(1e-4, 2e-4, 3e-4));
DecomposeTest(0, 0, 0, Vector3.Zero, new Vector3(1e-4, 3e-4, 2e-4));
DecomposeTest(0, 0, 0, Vector3.Zero, new Vector3(2e-4, 1e-4, 3e-4));
DecomposeTest(0, 0, 0, Vector3.Zero, new Vector3(2e-4, 3e-4, 1e-4));
DecomposeTest(0, 0, 0, Vector3.Zero, new Vector3(3e-4, 1e-4, 2e-4));
DecomposeTest(0, 0, 0, Vector3.Zero, new Vector3(3e-4, 2e-4, 1e-4));
// Zero scales.
DecomposeTest(0, 0, 0, new Vector3(10, 20, 30), new Vector3(0, 0, 0));
DecomposeTest(0, 0, 0, new Vector3(10, 20, 30), new Vector3(1, 0, 0));
DecomposeTest(0, 0, 0, new Vector3(10, 20, 30), new Vector3(0, 1, 0));
DecomposeTest(0, 0, 0, new Vector3(10, 20, 30), new Vector3(0, 0, 1));
DecomposeTest(0, 0, 0, new Vector3(10, 20, 30), new Vector3(0, 1, 1));
DecomposeTest(0, 0, 0, new Vector3(10, 20, 30), new Vector3(1, 0, 1));
DecomposeTest(0, 0, 0, new Vector3(10, 20, 30), new Vector3(1, 1, 0));
// Negative scales.
DecomposeTest(0, 0, 0, new Vector3(10, 20, 30), new Vector3(-1, -1, -1));
DecomposeTest(0, 0, 0, new Vector3(10, 20, 30), new Vector3(1, -1, -1));
DecomposeTest(0, 0, 0, new Vector3(10, 20, 30), new Vector3(-1, 1, -1));
DecomposeTest(0, 0, 0, new Vector3(10, 20, 30), new Vector3(-1, -1, 1));
DecomposeTest(0, 0, 0, new Vector3(10, 20, 30), new Vector3(-1, 1, 1));
DecomposeTest(0, 0, 0, new Vector3(10, 20, 30), new Vector3(1, -1, 1));
DecomposeTest(0, 0, 0, new Vector3(10, 20, 30), new Vector3(1, 1, -1));
}
void DecomposeScaleTest(double sx, double sy, double sz)
{
Matrix4x4 m = Matrix4x4.CreateScale(sx, sy, sz);
Vector3 expectedScales = new(sx, sy, sz);
Vector3 scales;
Quaternion rotation;
Vector3 translation;
bool actualResult = Matrix4x4.Decompose(m, out scales, out rotation, out translation);
Assert.True(actualResult, "Matrix4x4.Decompose did not return expected value.");
Assert.True(MathHelper.Equal(expectedScales, scales), "Matrix4x4.Decompose did not return expected value.");
Assert.True(
MathHelper.EqualRotation(Quaternion.Identity, rotation),
"Matrix4x4.Decompose did not return expected value."
);
Assert.True(MathHelper.Equal(Vector3.Zero, translation), "Matrix4x4.Decompose did not return expected value.");
}
// Tiny scale decompose test.
[Fact]
public void Matrix4x4DecomposeTest03()
{
DecomposeScaleTest(1, 2e-4, 3e-4);
DecomposeScaleTest(1, 3e-4, 2e-4);
DecomposeScaleTest(2e-4, 1, 3e-4);
DecomposeScaleTest(2e-4, 3e-4, 1);
DecomposeScaleTest(3e-4, 1, 2e-4);
DecomposeScaleTest(3e-4, 2e-4, 1);
}
[Fact]
public void Matrix4x4DecomposeTest04()
{
Assert.False(
Matrix4x4.Decompose(GenerateMatrixNumberFrom1To16(), out Vector3 _, out Quaternion _, out Vector3 _),
"decompose should have failed."
);
Assert.False(
Matrix4x4.Decompose(new Matrix4x4(Matrix3x2.CreateSkew(1, 2)), out Vector3 _, out Quaternion _, out Vector3 _),
"decompose should have failed."
);
}
// Transform by quaternion test
[Fact]
public void Matrix4x4TransformTest()
{
Matrix4x4 target = GenerateMatrixNumberFrom1To16();
Matrix4x4 m =
Matrix4x4.CreateRotationX(MathHelper.ToRadians(30.0))
* Matrix4x4.CreateRotationY(MathHelper.ToRadians(30.0))
* Matrix4x4.CreateRotationZ(MathHelper.ToRadians(30.0));
Quaternion q = Quaternion.CreateFromRotationMatrix(m);
Matrix4x4 expected = target * m;
Matrix4x4 actual;
actual = Matrix4x4.Transform(target, q);
Assert.True(MathHelper.Equal(expected, actual), "Matrix4x4.Transform did not return the expected value.");
}
// A test for CreateRotationX (double)
[Fact]
public void Matrix4x4CreateRotationXTest()
{
double radians = MathHelper.ToRadians(30.0);
Matrix4x4 expected = new();
expected.M11 = 1.0;
expected.M22 = 0.8660254;
expected.M23 = 0.5;
expected.M32 = -0.5;
expected.M33 = 0.8660254;
expected.M44 = 1.0;
Matrix4x4 actual;
actual = Matrix4x4.CreateRotationX(radians);
Assert.True(MathHelper.Equal(expected, actual), "Matrix4x4.CreateRotationX did not return the expected value.");
}
// A test for CreateRotationX (double)
// CreateRotationX of zero degree
[Fact]
public void Matrix4x4CreateRotationXTest1()
{
double radians = 0;
Matrix4x4 expected = Matrix4x4.Identity;
Matrix4x4 actual = Matrix4x4.CreateRotationX(radians);
Assert.True(MathHelper.Equal(expected, actual), "Matrix4x4.CreateRotationX did not return the expected value.");
}
// A test for CreateRotationX (double, Vector3)
[Fact]
public void Matrix4x4CreateRotationXCenterTest()
{
double radians = MathHelper.ToRadians(30.0);
Vector3 center = new(23, 42, 66);
Matrix4x4 rotateAroundZero = Matrix4x4.CreateRotationX(radians, Vector3.Zero);
Matrix4x4 rotateAroundZeroExpected = Matrix4x4.CreateRotationX(radians);
Assert.True(MathHelper.Equal(rotateAroundZero, rotateAroundZeroExpected));
Matrix4x4 rotateAroundCenter = Matrix4x4.CreateRotationX(radians, center);
Matrix4x4 rotateAroundCenterExpected =
Matrix4x4.CreateTranslation(-center) * Matrix4x4.CreateRotationX(radians) * Matrix4x4.CreateTranslation(center);
Assert.True(MathHelper.Equal(rotateAroundCenter, rotateAroundCenterExpected));
}
// A test for CreateRotationY (double)
[Fact]
public void Matrix4x4CreateRotationYTest()
{
double radians = MathHelper.ToRadians(60.0);
Matrix4x4 expected = new();
expected.M11 = 0.49999997;
expected.M13 = -0.866025448;
expected.M22 = 1.0;
expected.M31 = 0.866025448;
expected.M33 = 0.49999997;
expected.M44 = 1.0;
Matrix4x4 actual;
actual = Matrix4x4.CreateRotationY(radians);
Assert.True(MathHelper.Equal(expected, actual), "Matrix4x4.CreateRotationY did not return the expected value.");
}
// A test for RotationY (double)
// CreateRotationY test for negative angle
[Fact]
public void Matrix4x4CreateRotationYTest1()
{
double radians = MathHelper.ToRadians(-300.0);
Matrix4x4 expected = new();
expected.M11 = 0.49999997;
expected.M13 = -0.866025448;
expected.M22 = 1.0;
expected.M31 = 0.866025448;
expected.M33 = 0.49999997;
expected.M44 = 1.0;
Matrix4x4 actual = Matrix4x4.CreateRotationY(radians);
Assert.True(MathHelper.Equal(expected, actual), "Matrix4x4.CreateRotationY did not return the expected value.");
}
// A test for CreateRotationY (double, Vector3)
[Fact]
public void Matrix4x4CreateRotationYCenterTest()
{
double radians = MathHelper.ToRadians(30.0);
Vector3 center = new(23, 42, 66);
Matrix4x4 rotateAroundZero = Matrix4x4.CreateRotationY(radians, Vector3.Zero);
Matrix4x4 rotateAroundZeroExpected = Matrix4x4.CreateRotationY(radians);
Assert.True(MathHelper.Equal(rotateAroundZero, rotateAroundZeroExpected));
Matrix4x4 rotateAroundCenter = Matrix4x4.CreateRotationY(radians, center);
Matrix4x4 rotateAroundCenterExpected =
Matrix4x4.CreateTranslation(-center) * Matrix4x4.CreateRotationY(radians) * Matrix4x4.CreateTranslation(center);
Assert.True(MathHelper.Equal(rotateAroundCenter, rotateAroundCenterExpected));
}
// A test for CreateFromAxisAngle(Vector3,double)
[Fact]
public void Matrix4x4CreateFromAxisAngleTest()
{
double radians = MathHelper.ToRadians(-30.0);
Matrix4x4 expected = Matrix4x4.CreateRotationX(radians);
Matrix4x4 actual = Matrix4x4.CreateFromAxisAngle(Vector3.UnitX, radians);
Assert.True(MathHelper.Equal(expected, actual));
expected = Matrix4x4.CreateRotationY(radians);
actual = Matrix4x4.CreateFromAxisAngle(Vector3.UnitY, radians);
Assert.True(MathHelper.Equal(expected, actual));
expected = Matrix4x4.CreateRotationZ(radians);
actual = Matrix4x4.CreateFromAxisAngle(Vector3.UnitZ, radians);
Assert.True(MathHelper.Equal(expected, actual));
expected = Matrix4x4.CreateFromQuaternion(Quaternion.CreateFromAxisAngle(Vector3.Normalize(Vector3.One), radians));
actual = Matrix4x4.CreateFromAxisAngle(Vector3.Normalize(Vector3.One), radians);
Assert.True(MathHelper.Equal(expected, actual));
const int rotCount = 16;
for (int i = 0; i < rotCount; ++i)
{
double latitude = (2.0 * MathHelper.Pi) * (i / (double)rotCount);
for (int j = 0; j < rotCount; ++j)
{
double longitude = -MathHelper.PiOver2 + MathHelper.Pi * (j / (double)rotCount);
Matrix4x4 m = Matrix4x4.CreateRotationZ(longitude) * Matrix4x4.CreateRotationY(latitude);
Vector3 axis = new(m.M11, m.M12, m.M13);
for (int k = 0; k < rotCount; ++k)
{
double rot = (2.0 * MathHelper.Pi) * (k / (double)rotCount);
expected = Matrix4x4.CreateFromQuaternion(Quaternion.CreateFromAxisAngle(axis, rot));
actual = Matrix4x4.CreateFromAxisAngle(axis, rot);
Assert.True(MathHelper.Equal(expected, actual));
}
}
}
}
[Fact]
public void Matrix4x4CreateFromYawPitchRollTest1()
{
double yawAngle = MathHelper.ToRadians(30.0);
double pitchAngle = MathHelper.ToRadians(40.0);
double rollAngle = MathHelper.ToRadians(50.0);
Matrix4x4 yaw = Matrix4x4.CreateFromAxisAngle(Vector3.UnitY, yawAngle);
Matrix4x4 pitch = Matrix4x4.CreateFromAxisAngle(Vector3.UnitX, pitchAngle);
Matrix4x4 roll = Matrix4x4.CreateFromAxisAngle(Vector3.UnitZ, rollAngle);
Matrix4x4 expected = roll * pitch * yaw;
Matrix4x4 actual = Matrix4x4.CreateFromYawPitchRoll(yawAngle, pitchAngle, rollAngle);
Assert.True(MathHelper.Equal(expected, actual));
}
// Covers more numeric rigions
[Fact]
public void Matrix4x4CreateFromYawPitchRollTest2()
{
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);
Matrix4x4 yaw = Matrix4x4.CreateFromAxisAngle(Vector3.UnitY, yawRad);
Matrix4x4 pitch = Matrix4x4.CreateFromAxisAngle(Vector3.UnitX, pitchRad);
Matrix4x4 roll = Matrix4x4.CreateFromAxisAngle(Vector3.UnitZ, rollRad);
Matrix4x4 expected = roll * pitch * yaw;
Matrix4x4 actual = Matrix4x4.CreateFromYawPitchRoll(yawRad, pitchRad, rollRad);
Assert.True(MathHelper.Equal(expected, actual), $"Yaw:{yawAngle} Pitch:{pitchAngle} Roll:{rollAngle}");
}
}
}
}
// Simple shadow test.
[Fact]
public void Matrix4x4CreateShadowTest01()
{
Vector3 lightDir = Vector3.UnitY;
Plane plane = new(Vector3.UnitY, 0);
Matrix4x4 expected = Matrix4x4.CreateScale(1, 0, 1);
Matrix4x4 actual = Matrix4x4.CreateShadow(lightDir, plane);
Assert.True(MathHelper.Equal(expected, actual), "Matrix4x4.CreateShadow did not returned expected value.");
}
// Various plane projections.
[Fact]
public void Matrix4x4CreateShadowTest02()
{
// Complex cases.
Plane[] planes = { new(0, 1, 0, 0), new(1, 2, 3, 4), new(5, 6, 7, 8), new(-1, -2, -3, -4), new(-5, -6, -7, -8), };
Vector3[] points =
{
new(1, 2, 3),
new(5, 6, 7),
new(8, 9, 10),
new(-1, -2, -3),
new(-5, -6, -7),
new(-8, -9, -10),
};
foreach (Plane p in planes)
{
Plane plane = Plane.Normalize(p);
// Try various direction of light directions.
var testDirections = new[]
{
new Vector3(-1.0, 1.0, 1.0),
new Vector3(0.0, 1.0, 1.0),
new Vector3(1.0, 1.0, 1.0),
new Vector3(-1.0, 0.0, 1.0),
new Vector3(0.0, 0.0, 1.0),
new Vector3(1.0, 0.0, 1.0),
new Vector3(-1.0, -1.0, 1.0),
new Vector3(0.0, -1.0, 1.0),
new Vector3(1.0, -1.0, 1.0),
new Vector3(-1.0, 1.0, 0.0),
new Vector3(0.0, 1.0, 0.0),
new Vector3(1.0, 1.0, 0.0),
new Vector3(-1.0, 0.0, 0.0),
new Vector3(0.0, 0.0, 0.0),
new Vector3(1.0, 0.0, 0.0),
new Vector3(-1.0, -1.0, 0.0),
new Vector3(0.0, -1.0, 0.0),
new Vector3(1.0, -1.0, 0.0),
new Vector3(-1.0, 1.0, -1.0),
new Vector3(0.0, 1.0, -1.0),
new Vector3(1.0, 1.0, -1.0),
new Vector3(-1.0, 0.0, -1.0),
new Vector3(0.0, 0.0, -1.0),
new Vector3(1.0, 0.0, -1.0),
new Vector3(-1.0, -1.0, -1.0),
new Vector3(0.0, -1.0, -1.0),
new Vector3(1.0, -1.0, -1.0),
};
foreach (Vector3 lightDirInfo in testDirections)
{
if (lightDirInfo.Length() < 0.1)
continue;
Vector3 lightDir = Vector3.Normalize(lightDirInfo);
if (Plane.DotNormal(plane, lightDir) < 0.1)
continue;
Matrix4x4 m = Matrix4x4.CreateShadow(lightDir, plane);
Vector3 pp = -plane.D * plane.Normal; // origin of the plane.
//
foreach (Vector3 point in points)
{
Vector4 v4 = Vector4.Transform(point, m);
Vector3 sp = new Vector3(v4.X, v4.Y, v4.Z) / v4.W;
// Make sure transformed position is on the plane.
Vector3 v = sp - pp;
double d = Vector3.Dot(v, plane.Normal);
Assert.True(MathHelper.Equal(d, 0), "Matrix4x4.CreateShadow did not provide expected value.");
// make sure direction between transformed position and original position are same as light direction.
if (Vector3.Dot(point - pp, plane.Normal) > 0.0001)
{
Vector3 dir = Vector3.Normalize(point - sp);
Assert.True(MathHelper.Equal(dir, lightDir), "Matrix4x4.CreateShadow did not provide expected value.");
}
}
}
}
}
void CreateReflectionTest(Plane plane, Matrix4x4 expected)
{
Matrix4x4 actual = Matrix4x4.CreateReflection(plane);
Assert.True(MathHelper.Equal(actual, expected), "Matrix4x4.CreateReflection did not return expected value.");
}
[Fact]
public void Matrix4x4CreateReflectionTest01()
{
// XY plane.
CreateReflectionTest(new Plane(Vector3.UnitZ, 0), Matrix4x4.CreateScale(1, 1, -1));
// XZ plane.
CreateReflectionTest(new Plane(Vector3.UnitY, 0), Matrix4x4.CreateScale(1, -1, 1));
// YZ plane.
CreateReflectionTest(new Plane(Vector3.UnitX, 0), Matrix4x4.CreateScale(-1, 1, 1));
// Complex cases.
Plane[] planes = { new(0, 1, 0, 0), new(1, 2, 3, 4), new(5, 6, 7, 8), new(-1, -2, -3, -4), new(-5, -6, -7, -8), };
Vector3[] points = { new(1, 2, 3), new(5, 6, 7), new(-1, -2, -3), new(-5, -6, -7), };
foreach (Plane p in planes)
{
Plane plane = Plane.Normalize(p);
Matrix4x4 m = Matrix4x4.CreateReflection(plane);
Vector3 pp = -plane.D * plane.Normal; // Position on the plane.
//
foreach (Vector3 point in points)
{
Vector3 rp = Vector3.Transform(point, m);
// Manually compute reflection point and compare results.
Vector3 v = point - pp;
double d = Vector3.Dot(v, plane.Normal);
Vector3 vp = point - 2.0 * d * plane.Normal;
Assert.True(MathHelper.Equal(rp, vp), "Matrix4x4.Reflection did not provide expected value.");
}
}
}
// A test for CreateRotationZ (double)
[Fact]
public void Matrix4x4CreateRotationZTest()
{
double radians = MathHelper.ToRadians(50.0);
Matrix4x4 expected = new();
expected.M11 = 0.642787635;
expected.M12 = 0.766044438;
expected.M21 = -0.766044438;
expected.M22 = 0.642787635;
expected.M33 = 1.0;
expected.M44 = 1.0;
Matrix4x4 actual;
actual = Matrix4x4.CreateRotationZ(radians);
Assert.True(MathHelper.Equal(expected, actual), "Matrix4x4.CreateRotationZ did not return the expected value.");
}
// A test for CreateRotationZ (double, Vector3)
[Fact]
public void Matrix4x4CreateRotationZCenterTest()
{
double radians = MathHelper.ToRadians(30.0);
Vector3 center = new(23, 42, 66);
Matrix4x4 rotateAroundZero = Matrix4x4.CreateRotationZ(radians, Vector3.Zero);
Matrix4x4 rotateAroundZeroExpected = Matrix4x4.CreateRotationZ(radians);
Assert.True(MathHelper.Equal(rotateAroundZero, rotateAroundZeroExpected));
Matrix4x4 rotateAroundCenter = Matrix4x4.CreateRotationZ(radians, center);
Matrix4x4 rotateAroundCenterExpected =
Matrix4x4.CreateTranslation(-center) * Matrix4x4.CreateRotationZ(radians) * Matrix4x4.CreateTranslation(center);
Assert.True(MathHelper.Equal(rotateAroundCenter, rotateAroundCenterExpected));
}
// A test for CrateLookAt (Vector3, Vector3, Vector3)
[Fact]
public void Matrix4x4CreateLookAtTest()
{
Vector3 cameraPosition = new(10.0, 20.0, 30.0);
Vector3 cameraTarget = new(3.0, 2.0, -4.0);
Vector3 cameraUpVector = new(0.0, 1.0, 0.0);
Matrix4x4 expected = new();
expected.M11 = 0.979457;
expected.M12 = -0.0928267762;
expected.M13 = 0.179017;
expected.M21 = 0.0;
expected.M22 = 0.8877481;
expected.M23 = 0.460329473;
expected.M31 = -0.201652914;
expected.M32 = -0.450872928;
expected.M33 = 0.8695112;
expected.M41 = -3.74498272;
expected.M42 = -3.30050683;
expected.M43 = -37.0820961;
expected.M44 = 1.0;
Matrix4x4 actual = Matrix4x4.CreateLookAt(cameraPosition, cameraTarget, cameraUpVector);
Assert.True(MathHelper.Equal(expected, actual), "Matrix4x4.CreateLookAt did not return the expected value.");
}
// A test for CreateWorld (Vector3, Vector3, Vector3)
[Fact]
public void Matrix4x4CreateWorldTest()
{
Vector3 objectPosition = new(10.0, 20.0, 30.0);
Vector3 objectForwardDirection = new(3.0, 2.0, -4.0);
Vector3 objectUpVector = new(0.0, 1.0, 0.0);
Matrix4x4 expected = new();
expected.M11 = 0.799999952;
expected.M12 = 0;
expected.M13 = 0.599999964;
expected.M14 = 0;
expected.M21 = -0.2228344;
expected.M22 = 0.928476632;
expected.M23 = 0.297112525;
expected.M24 = 0;
expected.M31 = -0.557086;
expected.M32 = -0.371390671;
expected.M33 = 0.742781341;
expected.M34 = 0;
expected.M41 = 10;
expected.M42 = 20;
expected.M43 = 30;
expected.M44 = 1.0;
Matrix4x4 actual = Matrix4x4.CreateWorld(objectPosition, objectForwardDirection, objectUpVector);
Assert.True(MathHelper.Equal(expected, actual), "Matrix4x4.CreateWorld did not return the expected value.");
Assert.Equal(objectPosition, actual.Translation);
Assert.True(Vector3.Dot(Vector3.Normalize(objectUpVector), new Vector3(actual.M21, actual.M22, actual.M23)) > 0);
Assert.True(
Vector3.Dot(Vector3.Normalize(objectForwardDirection), new Vector3(-actual.M31, -actual.M32, -actual.M33)) > 0.999
);
}
// A test for CreateOrtho (double, double, double, double)
[Fact]
public void Matrix4x4CreateOrthoTest()
{
double width = 100.0;
double height = 200.0;
double zNearPlane = 1.5;
double zFarPlane = 1000.0;
Matrix4x4 expected = new();
expected.M11 = 0.02;
expected.M22 = 0.01;
expected.M33 = -0.00100150227;
expected.M43 = -0.00150225335;
expected.M44 = 1.0;
Matrix4x4 actual;
actual = Matrix4x4.CreateOrthographic(width, height, zNearPlane, zFarPlane);
Assert.True(MathHelper.Equal(expected, actual), "Matrix4x4.CreateOrtho did not return the expected value.");
}
// A test for CreateOrthoOffCenter (double, double, double, double, double, double)
[Fact]
public void Matrix4x4CreateOrthoOffCenterTest()
{
double left = 10.0;
double right = 90.0;
double bottom = 20.0;
double top = 180.0;
double zNearPlane = 1.5;
double zFarPlane = 1000.0;
Matrix4x4 expected = new();
expected.M11 = 0.025;
expected.M22 = 0.0125;
expected.M33 = -0.00100150227;
expected.M41 = -1.25;
expected.M42 = -1.25;
expected.M43 = -0.00150225335;
expected.M44 = 1.0;
Matrix4x4 actual;
actual = Matrix4x4.CreateOrthographicOffCenter(left, right, bottom, top, zNearPlane, zFarPlane);
Assert.True(
MathHelper.Equal(expected, actual),
"Matrix4x4.CreateOrthoOffCenter did not return the expected value."
);
}
// A test for CreatePerspective (double, double, double, double)
[Fact]
public void Matrix4x4CreatePerspectiveTest()
{
double width = 100.0;
double height = 200.0;
double zNearPlane = 1.5;
double zFarPlane = 1000.0;
Matrix4x4 expected = new();
expected.M11 = 0.03;
expected.M22 = 0.015;
expected.M33 = -1.00150228;
expected.M34 = -1.0;
expected.M43 = -1.50225341;
Matrix4x4 actual;
actual = Matrix4x4.CreatePerspective(width, height, zNearPlane, zFarPlane);
Assert.True(MathHelper.Equal(expected, actual), "Matrix4x4.CreatePerspective did not return the expected value.");
}
// A test for CreatePerspective (double, double, double, double)
// CreatePerspective test where znear = zfar
[Fact]
public void Matrix4x4CreatePerspectiveTest1() =>
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
double width = 100.0;
double height = 200.0;
double zNearPlane = 0.0;
double zFarPlane = 0.0;
Matrix4x4.CreatePerspective(width, height, zNearPlane, zFarPlane);
});
// A test for CreatePerspective (double, double, double, double)
// CreatePerspective test where near plane is negative value
[Fact]
public void Matrix4x4CreatePerspectiveTest2() =>
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
Matrix4x4.CreatePerspective(10, 10, -10, 10);
});
// A test for CreatePerspective (double, double, double, double)
// CreatePerspective test where far plane is negative value
[Fact]
public void Matrix4x4CreatePerspectiveTest3() =>
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
Matrix4x4.CreatePerspective(10, 10, 10, -10);
});
// A test for CreatePerspective (double, double, double, double)
// CreatePerspective test where near plane is beyond far plane
[Fact]
public void Matrix4x4CreatePerspectiveTest4() =>
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
Matrix4x4.CreatePerspective(10, 10, 10, 1);
});
// A test for CreatePerspectiveFieldOfView (double, double, double, double)
[Fact]
public void Matrix4x4CreatePerspectiveFieldOfViewTest()
{
double fieldOfView = MathHelper.ToRadians(30.0);
double aspectRatio = 1280.0 / 720.0;
double zNearPlane = 1.5;
double zFarPlane = 1000.0;
Matrix4x4 expected = new();
expected.M11 = 2.09927845;
expected.M22 = 3.73205066;
expected.M33 = -1.00150228;
expected.M34 = -1.0;
expected.M43 = -1.50225341;
Matrix4x4 actual;
actual = Matrix4x4.CreatePerspectiveFieldOfView(fieldOfView, aspectRatio, zNearPlane, zFarPlane);
Assert.True(
MathHelper.Equal(expected, actual),
"Matrix4x4.CreatePerspectiveFieldOfView did not return the expected value."
);
}
// A test for CreatePerspectiveFieldOfView (double, double, double, double)
// CreatePerspectiveFieldOfView test where filedOfView is negative value.
[Fact]
public void Matrix4x4CreatePerspectiveFieldOfViewTest1() =>
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
Matrix4x4.CreatePerspectiveFieldOfView(-1, 1, 1, 10);
});
// A test for CreatePerspectiveFieldOfView (double, double, double, double)
// CreatePerspectiveFieldOfView test where filedOfView is more than pi.
[Fact]
public void Matrix4x4CreatePerspectiveFieldOfViewTest2() =>
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
Matrix4x4.CreatePerspectiveFieldOfView(MathHelper.Pi + 0.01, 1, 1, 10);
});
// A test for CreatePerspectiveFieldOfView (double, double, double, double)
// CreatePerspectiveFieldOfView test where nearPlaneDistance is negative value.
[Fact]
public void Matrix4x4CreatePerspectiveFieldOfViewTest3() =>
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
Matrix4x4.CreatePerspectiveFieldOfView(MathHelper.PiOver4, 1, -1, 10);
});
// A test for CreatePerspectiveFieldOfView (double, double, double, double)
// CreatePerspectiveFieldOfView test where farPlaneDistance is negative value.
[Fact]
public void Matrix4x4CreatePerspectiveFieldOfViewTest4() =>
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
Matrix4x4.CreatePerspectiveFieldOfView(MathHelper.PiOver4, 1, 1, -10);
});
// A test for CreatePerspectiveFieldOfView (double, double, double, double)
// CreatePerspectiveFieldOfView test where nearPlaneDistance is larger than farPlaneDistance.
[Fact]
public void Matrix4x4CreatePerspectiveFieldOfViewTest5() =>
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
Matrix4x4.CreatePerspectiveFieldOfView(MathHelper.PiOver4, 1, 10, 1);
});
// A test for CreatePerspectiveOffCenter (double, double, double, double, double, double)
[Fact]
public void Matrix4x4CreatePerspectiveOffCenterTest()
{
double left = 10.0;
double right = 90.0;
double bottom = 20.0;
double top = 180.0;
double zNearPlane = 1.5;
double zFarPlane = 1000.0;
Matrix4x4 expected = new();
expected.M11 = 0.0375;
expected.M22 = 0.01875;
expected.M31 = 1.25;
expected.M32 = 1.25;
expected.M33 = -1.00150228;
expected.M34 = -1.0;
expected.M43 = -1.50225341;
Matrix4x4 actual;
actual = Matrix4x4.CreatePerspectiveOffCenter(left, right, bottom, top, zNearPlane, zFarPlane);
Assert.True(
MathHelper.Equal(expected, actual),
"Matrix4x4.CreatePerspectiveOffCenter did not return the expected value."
);
}
// A test for CreatePerspectiveOffCenter (double, double, double, double, double, double)
// CreatePerspectiveOffCenter test where nearPlaneDistance is negative.
[Fact]
public void Matrix4x4CreatePerspectiveOffCenterTest1() =>
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
double left = 10.0,
right = 90.0,
bottom = 20.0,
top = 180.0;
Matrix4x4.CreatePerspectiveOffCenter(left, right, bottom, top, -1, 10);
});
// A test for CreatePerspectiveOffCenter (double, double, double, double, double, double)
// CreatePerspectiveOffCenter test where farPlaneDistance is negative.
[Fact]
public void Matrix4x4CreatePerspectiveOffCenterTest2() =>
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
double left = 10.0,
right = 90.0,
bottom = 20.0,
top = 180.0;
Matrix4x4.CreatePerspectiveOffCenter(left, right, bottom, top, 1, -10);
});
// A test for CreatePerspectiveOffCenter (double, double, double, double, double, double)
// CreatePerspectiveOffCenter test where test where nearPlaneDistance is larger than farPlaneDistance.
[Fact]
public void Matrix4x4CreatePerspectiveOffCenterTest3() =>
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
double left = 10.0,
right = 90.0,
bottom = 20.0,
top = 180.0;
Matrix4x4.CreatePerspectiveOffCenter(left, right, bottom, top, 10, 1);
});
// A test for Invert (Matrix4x4)
// Non invertible matrix - determinant is zero - singular matrix
[Fact]
public void Matrix4x4InvertTest1()
{
Matrix4x4 a = new();
a.M11 = 1.0;
a.M12 = 2.0;
a.M13 = 3.0;
a.M14 = 4.0;
a.M21 = 5.0;
a.M22 = 6.0;
a.M23 = 7.0;
a.M24 = 8.0;
a.M31 = 9.0;
a.M32 = 10.0;
a.M33 = 11.0;
a.M34 = 12.0;
a.M41 = 13.0;
a.M42 = 14.0;
a.M43 = 15.0;
a.M44 = 16.0;
double detA = a.GetDeterminant();
Assert.True(MathHelper.Equal(detA, 0.0), "Matrix4x4.Invert did not return the expected value.");
Matrix4x4 actual;
Assert.False(Matrix4x4.Invert(a, out actual));
// all the elements in Actual is NaN
Assert.True(
double.IsNaN(actual.M11)
&& double.IsNaN(actual.M12)
&& double.IsNaN(actual.M13)
&& double.IsNaN(actual.M14)
&& double.IsNaN(actual.M21)
&& double.IsNaN(actual.M22)
&& double.IsNaN(actual.M23)
&& double.IsNaN(actual.M24)
&& double.IsNaN(actual.M31)
&& double.IsNaN(actual.M32)
&& double.IsNaN(actual.M33)
&& double.IsNaN(actual.M34)
&& double.IsNaN(actual.M41)
&& double.IsNaN(actual.M42)
&& double.IsNaN(actual.M43)
&& double.IsNaN(actual.M44),
"Matrix4x4.Invert did not return the expected value."
);
}
// A test for Lerp (Matrix4x4, Matrix4x4, double)
[Fact]
public void Matrix4x4LerpTest()
{
Matrix4x4 a = new();
a.M11 = 11.0;
a.M12 = 12.0;
a.M13 = 13.0;
a.M14 = 14.0;
a.M21 = 21.0;
a.M22 = 22.0;
a.M23 = 23.0;
a.M24 = 24.0;
a.M31 = 31.0;
a.M32 = 32.0;
a.M33 = 33.0;
a.M34 = 34.0;
a.M41 = 41.0;
a.M42 = 42.0;
a.M43 = 43.0;
a.M44 = 44.0;
Matrix4x4 b = GenerateMatrixNumberFrom1To16();
double t = 0.5;
Matrix4x4 expected = new();
expected.M11 = a.M11 + (b.M11 - a.M11) * t;
expected.M12 = a.M12 + (b.M12 - a.M12) * t;
expected.M13 = a.M13 + (b.M13 - a.M13) * t;
expected.M14 = a.M14 + (b.M14 - a.M14) * t;
expected.M21 = a.M21 + (b.M21 - a.M21) * t;
expected.M22 = a.M22 + (b.M22 - a.M22) * t;
expected.M23 = a.M23 + (b.M23 - a.M23) * t;
expected.M24 = a.M24 + (b.M24 - a.M24) * t;
expected.M31 = a.M31 + (b.M31 - a.M31) * t;
expected.M32 = a.M32 + (b.M32 - a.M32) * t;
expected.M33 = a.M33 + (b.M33 - a.M33) * t;
expected.M34 = a.M34 + (b.M34 - a.M34) * t;
expected.M41 = a.M41 + (b.M41 - a.M41) * t;
expected.M42 = a.M42 + (b.M42 - a.M42) * t;
expected.M43 = a.M43 + (b.M43 - a.M43) * t;
expected.M44 = a.M44 + (b.M44 - a.M44) * t;
Matrix4x4 actual;
actual = Matrix4x4.Lerp(a, b, t);
Assert.True(MathHelper.Equal(expected, actual), "Matrix4x4.Lerp did not return the expected value.");
}
// A test for operator - (Matrix4x4)
[Fact]
public void Matrix4x4UnaryNegationTest()
{
Matrix4x4 a = GenerateMatrixNumberFrom1To16();
Matrix4x4 expected = new();
expected.M11 = -1.0;
expected.M12 = -2.0;
expected.M13 = -3.0;
expected.M14 = -4.0;
expected.M21 = -5.0;
expected.M22 = -6.0;
expected.M23 = -7.0;
expected.M24 = -8.0;
expected.M31 = -9.0;
expected.M32 = -10.0;
expected.M33 = -11.0;
expected.M34 = -12.0;
expected.M41 = -13.0;
expected.M42 = -14.0;
expected.M43 = -15.0;
expected.M44 = -16.0;
Matrix4x4 actual = -a;
Assert.True(MathHelper.Equal(expected, actual), "Matrix4x4.operator - did not return the expected value.");
}
// A test for operator - (Matrix4x4, Matrix4x4)
[Fact]
public void Matrix4x4SubtractionTest()
{
Matrix4x4 a = GenerateMatrixNumberFrom1To16();
Matrix4x4 b = GenerateMatrixNumberFrom1To16();
Matrix4x4 expected = new();
Matrix4x4 actual = a - b;
Assert.True(MathHelper.Equal(expected, actual), "Matrix4x4.operator - did not return the expected value.");
}
// A test for operator * (Matrix4x4, Matrix4x4)
[Fact]
public void Matrix4x4MultiplyTest1()
{
Matrix4x4 a = GenerateMatrixNumberFrom1To16();
Matrix4x4 b = GenerateMatrixNumberFrom1To16();
Matrix4x4 expected = new();
expected.M11 = a.M11 * b.M11 + a.M12 * b.M21 + a.M13 * b.M31 + a.M14 * b.M41;
expected.M12 = a.M11 * b.M12 + a.M12 * b.M22 + a.M13 * b.M32 + a.M14 * b.M42;
expected.M13 = a.M11 * b.M13 + a.M12 * b.M23 + a.M13 * b.M33 + a.M14 * b.M43;
expected.M14 = a.M11 * b.M14 + a.M12 * b.M24 + a.M13 * b.M34 + a.M14 * b.M44;
expected.M21 = a.M21 * b.M11 + a.M22 * b.M21 + a.M23 * b.M31 + a.M24 * b.M41;
expected.M22 = a.M21 * b.M12 + a.M22 * b.M22 + a.M23 * b.M32 + a.M24 * b.M42;
expected.M23 = a.M21 * b.M13 + a.M22 * b.M23 + a.M23 * b.M33 + a.M24 * b.M43;
expected.M24 = a.M21 * b.M14 + a.M22 * b.M24 + a.M23 * b.M34 + a.M24 * b.M44;
expected.M31 = a.M31 * b.M11 + a.M32 * b.M21 + a.M33 * b.M31 + a.M34 * b.M41;
expected.M32 = a.M31 * b.M12 + a.M32 * b.M22 + a.M33 * b.M32 + a.M34 * b.M42;
expected.M33 = a.M31 * b.M13 + a.M32 * b.M23 + a.M33 * b.M33 + a.M34 * b.M43;
expected.M34 = a.M31 * b.M14 + a.M32 * b.M24 + a.M33 * b.M34 + a.M34 * b.M44;
expected.M41 = a.M41 * b.M11 + a.M42 * b.M21 + a.M43 * b.M31 + a.M44 * b.M41;
expected.M42 = a.M41 * b.M12 + a.M42 * b.M22 + a.M43 * b.M32 + a.M44 * b.M42;
expected.M43 = a.M41 * b.M13 + a.M42 * b.M23 + a.M43 * b.M33 + a.M44 * b.M43;
expected.M44 = a.M41 * b.M14 + a.M42 * b.M24 + a.M43 * b.M34 + a.M44 * b.M44;
Matrix4x4 actual = a * b;
Assert.True(MathHelper.Equal(expected, actual), "Matrix4x4.operator * did not return the expected value.");
}
// A test for operator * (Matrix4x4, Matrix4x4)
// Multiply with identity matrix
[Fact]
public void Matrix4x4MultiplyTest4()
{
Matrix4x4 a = new();
a.M11 = 1.0;
a.M12 = 2.0;
a.M13 = 3.0;
a.M14 = 4.0;
a.M21 = 5.0;
a.M22 = -6.0;
a.M23 = 7.0;
a.M24 = -8.0;
a.M31 = 9.0;
a.M32 = 10.0;
a.M33 = 11.0;
a.M34 = 12.0;
a.M41 = 13.0;
a.M42 = -14.0;
a.M43 = 15.0;
a.M44 = -16.0;
Matrix4x4 b = Matrix4x4.Identity;
Matrix4x4 expected = a;
Matrix4x4 actual = a * b;
Assert.True(MathHelper.Equal(expected, actual), "Matrix4x4.operator * did not return the expected value.");
}
// A test for operator + (Matrix4x4, Matrix4x4)
[Fact]
public void Matrix4x4AdditionTest()
{
Matrix4x4 a = GenerateMatrixNumberFrom1To16();
Matrix4x4 b = GenerateMatrixNumberFrom1To16();
Matrix4x4 expected = new();
expected.M11 = a.M11 + b.M11;
expected.M12 = a.M12 + b.M12;
expected.M13 = a.M13 + b.M13;
expected.M14 = a.M14 + b.M14;
expected.M21 = a.M21 + b.M21;
expected.M22 = a.M22 + b.M22;
expected.M23 = a.M23 + b.M23;
expected.M24 = a.M24 + b.M24;
expected.M31 = a.M31 + b.M31;
expected.M32 = a.M32 + b.M32;
expected.M33 = a.M33 + b.M33;
expected.M34 = a.M34 + b.M34;
expected.M41 = a.M41 + b.M41;
expected.M42 = a.M42 + b.M42;
expected.M43 = a.M43 + b.M43;
expected.M44 = a.M44 + b.M44;
Matrix4x4 actual;
actual = a + b;
Assert.True(MathHelper.Equal(expected, actual), "Matrix4x4.operator + did not return the expected value.");
}
// A test for Transpose (Matrix4x4)
[Fact]
public void Matrix4x4TransposeTest()
{
Matrix4x4 a = GenerateMatrixNumberFrom1To16();
Matrix4x4 expected = new();
expected.M11 = a.M11;
expected.M12 = a.M21;
expected.M13 = a.M31;
expected.M14 = a.M41;
expected.M21 = a.M12;
expected.M22 = a.M22;
expected.M23 = a.M32;
expected.M24 = a.M42;
expected.M31 = a.M13;
expected.M32 = a.M23;
expected.M33 = a.M33;
expected.M34 = a.M43;
expected.M41 = a.M14;
expected.M42 = a.M24;
expected.M43 = a.M34;
expected.M44 = a.M44;
Matrix4x4 actual = Matrix4x4.Transpose(a);
Assert.True(MathHelper.Equal(expected, actual), "Matrix4x4.Transpose did not return the expected value.");
}
// A test for Transpose (Matrix4x4)
// Transpose Identity matrix
[Fact]
public void Matrix4x4TransposeTest1()
{
Matrix4x4 a = Matrix4x4.Identity;
Matrix4x4 expected = Matrix4x4.Identity;
Matrix4x4 actual = Matrix4x4.Transpose(a);
Assert.True(MathHelper.Equal(expected, actual), "Matrix4x4.Transpose did not return the expected value.");
}
// A test for Matrix4x4 (Quaternion)
[Fact]
public void Matrix4x4FromQuaternionTest1()
{
Vector3 axis = Vector3.Normalize(new Vector3(1.0, 2.0, 3.0));
Quaternion q = Quaternion.CreateFromAxisAngle(axis, MathHelper.ToRadians(30.0));
Matrix4x4 expected = new();
expected.M11 = 0.875595033;
expected.M12 = 0.420031041;
expected.M13 = -0.2385524;
expected.M14 = 0.0;
expected.M21 = -0.38175258;
expected.M22 = 0.904303849;
expected.M23 = 0.1910483;
expected.M24 = 0.0;
expected.M31 = 0.295970082;
expected.M32 = -0.07621294;
expected.M33 = 0.952151954;
expected.M34 = 0.0;
expected.M41 = 0.0;
expected.M42 = 0.0;
expected.M43 = 0.0;
expected.M44 = 1.0;
Matrix4x4 target = Matrix4x4.CreateFromQuaternion(q);
Assert.True(
MathHelper.Equal(expected, target),
"Matrix4x4.Matrix4x4(Quaternion) did not return the expected value."
);
}
// A test for FromQuaternion (Matrix4x4)
// Convert X axis rotation matrix
[Fact]
public void Matrix4x4FromQuaternionTest2()
{
for (double angle = 0.0; angle < 720.0; angle += 10.0)
{
Quaternion quat = Quaternion.CreateFromAxisAngle(Vector3.UnitX, angle);
Matrix4x4 expected = Matrix4x4.CreateRotationX(angle);
Matrix4x4 actual = Matrix4x4.CreateFromQuaternion(quat);
Assert.True(
MathHelper.Equal(expected, actual),
$"Quaternion.FromQuaternion did not return the expected value. angle:{angle.ToString()}"
);
// make sure convert back to quaternion is same as we passed quaternion.
Quaternion q2 = Quaternion.CreateFromRotationMatrix(actual);
Assert.True(
MathHelper.EqualRotation(quat, q2),
$"Quaternion.FromQuaternion did not return the expected value. angle:{angle.ToString()}"
);
}
}
// A test for FromQuaternion (Matrix4x4)
// Convert Y axis rotation matrix
[Fact]
public void Matrix4x4FromQuaternionTest3()
{
for (double angle = 0.0; angle < 720.0; angle += 10.0)
{
Quaternion quat = Quaternion.CreateFromAxisAngle(Vector3.UnitY, angle);
Matrix4x4 expected = Matrix4x4.CreateRotationY(angle);
Matrix4x4 actual = Matrix4x4.CreateFromQuaternion(quat);
Assert.True(
MathHelper.Equal(expected, actual),
$"Quaternion.FromQuaternion did not return the expected value. angle:{angle.ToString()}"
);
// make sure convert back to quaternion is same as we passed quaternion.
Quaternion q2 = Quaternion.CreateFromRotationMatrix(actual);
Assert.True(
MathHelper.EqualRotation(quat, q2),
$"Quaternion.FromQuaternion did not return the expected value. angle:{angle.ToString()}"
);
}
}
// A test for FromQuaternion (Matrix4x4)
// Convert Z axis rotation matrix
[Fact]
public void Matrix4x4FromQuaternionTest4()
{
for (double angle = 0.0; angle < 720.0; angle += 10.0)
{
Quaternion quat = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, angle);
Matrix4x4 expected = Matrix4x4.CreateRotationZ(angle);
Matrix4x4 actual = Matrix4x4.CreateFromQuaternion(quat);
Assert.True(
MathHelper.Equal(expected, actual),
$"Quaternion.FromQuaternion did not return the expected value. angle:{angle}"
);
// make sure convert back to quaternion is same as we passed quaternion.
Quaternion q2 = Quaternion.CreateFromRotationMatrix(actual);
Assert.True(
MathHelper.EqualRotation(quat, q2),
$"Quaternion.FromQuaternion did not return the expected value. angle:{angle}"
);
}
}
// A test for FromQuaternion (Matrix4x4)
// Convert XYZ axis rotation matrix
[Fact]
public void Matrix4x4FromQuaternionTest5()
{
for (double angle = 0.0; angle < 720.0; angle += 10.0)
{
Quaternion quat =
Quaternion.CreateFromAxisAngle(Vector3.UnitZ, angle)
* Quaternion.CreateFromAxisAngle(Vector3.UnitY, angle)
* Quaternion.CreateFromAxisAngle(Vector3.UnitX, angle);
Matrix4x4 expected =
Matrix4x4.CreateRotationX(angle) * Matrix4x4.CreateRotationY(angle) * Matrix4x4.CreateRotationZ(angle);
Matrix4x4 actual = Matrix4x4.CreateFromQuaternion(quat);
Assert.True(
MathHelper.Equal(expected, actual),
$"Quaternion.FromQuaternion did not return the expected value. angle:{angle.ToString()}"
);
// make sure convert back to quaternion is same as we passed quaternion.
Quaternion q2 = Quaternion.CreateFromRotationMatrix(actual);
Assert.True(
MathHelper.EqualRotation(quat, q2),
$"Quaternion.FromQuaternion did not return the expected value. angle:{angle.ToString()}"
);
}
}
// A test for ToString ()
[Fact]
public void Matrix4x4ToStringTest()
{
Matrix4x4 a = new();
a.M11 = 11.0;
a.M12 = -12.0;
a.M13 = -13.3;
a.M14 = 14.4;
a.M21 = 21.0;
a.M22 = 22.0;
a.M23 = 23.0;
a.M24 = 24.0;
a.M31 = 31.0;
a.M32 = 32.0;
a.M33 = 33.0;
a.M34 = 34.0;
a.M41 = 41.0;
a.M42 = 42.0;
a.M43 = 43.0;
a.M44 = 44.0;
string expected = String.Format(
CultureInfo.CurrentCulture,
"{{ {{M11:{0} M12:{1} M13:{2} M14:{3}}} {{M21:{4} M22:{5} M23:{6} M24:{7}}} {{M31:{8} M32:{9} M33:{10} M34:{11}}} {{M41:{12} M42:{13} M43:{14} M44:{15}}} }}",
11.0,
-12.0,
-13.3,
14.4,
21.0,
22.0,
23.0,
24.0,
31.0,
32.0,
33.0,
34.0,
41.0,
42.0,
43.0,
44.0
);
string actual = a.ToString();
Assert.Equal(expected, actual);
}
// A test for Add (Matrix4x4, Matrix4x4)
[Fact]
public void Matrix4x4AddTest()
{
Matrix4x4 a = GenerateMatrixNumberFrom1To16();
Matrix4x4 b = GenerateMatrixNumberFrom1To16();
Matrix4x4 expected = new();
expected.M11 = a.M11 + b.M11;
expected.M12 = a.M12 + b.M12;
expected.M13 = a.M13 + b.M13;
expected.M14 = a.M14 + b.M14;
expected.M21 = a.M21 + b.M21;
expected.M22 = a.M22 + b.M22;
expected.M23 = a.M23 + b.M23;
expected.M24 = a.M24 + b.M24;
expected.M31 = a.M31 + b.M31;
expected.M32 = a.M32 + b.M32;
expected.M33 = a.M33 + b.M33;
expected.M34 = a.M34 + b.M34;
expected.M41 = a.M41 + b.M41;
expected.M42 = a.M42 + b.M42;
expected.M43 = a.M43 + b.M43;
expected.M44 = a.M44 + b.M44;
Matrix4x4 actual;
actual = Matrix4x4.Add(a, b);
Assert.Equal(expected, actual);
}
// A test for Equals (object)
[Fact]
public void Matrix4x4EqualsTest()
{
Matrix4x4 a = GenerateMatrixNumberFrom1To16();
Matrix4x4 b = GenerateMatrixNumberFrom1To16();
// 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.M11 = 11.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 Matrix4x4GetHashCodeTest()
{
Matrix4x4 target = GenerateMatrixNumberFrom1To16();
int expected =
target.M11.GetHashCode()
+ target.M12.GetHashCode()
+ target.M13.GetHashCode()
+ target.M14.GetHashCode()
+ target.M21.GetHashCode()
+ target.M22.GetHashCode()
+ target.M23.GetHashCode()
+ target.M24.GetHashCode()
+ target.M31.GetHashCode()
+ target.M32.GetHashCode()
+ target.M33.GetHashCode()
+ target.M34.GetHashCode()
+ target.M41.GetHashCode()
+ target.M42.GetHashCode()
+ target.M43.GetHashCode()
+ target.M44.GetHashCode();
int actual;
actual = target.GetHashCode();
Assert.Equal(expected, actual);
}
// A test for Multiply (Matrix4x4, Matrix4x4)
[Fact]
public void Matrix4x4MultiplyTest3()
{
Matrix4x4 a = GenerateMatrixNumberFrom1To16();
Matrix4x4 b = GenerateMatrixNumberFrom1To16();
Matrix4x4 expected = new();
expected.M11 = a.M11 * b.M11 + a.M12 * b.M21 + a.M13 * b.M31 + a.M14 * b.M41;
expected.M12 = a.M11 * b.M12 + a.M12 * b.M22 + a.M13 * b.M32 + a.M14 * b.M42;
expected.M13 = a.M11 * b.M13 + a.M12 * b.M23 + a.M13 * b.M33 + a.M14 * b.M43;
expected.M14 = a.M11 * b.M14 + a.M12 * b.M24 + a.M13 * b.M34 + a.M14 * b.M44;
expected.M21 = a.M21 * b.M11 + a.M22 * b.M21 + a.M23 * b.M31 + a.M24 * b.M41;
expected.M22 = a.M21 * b.M12 + a.M22 * b.M22 + a.M23 * b.M32 + a.M24 * b.M42;
expected.M23 = a.M21 * b.M13 + a.M22 * b.M23 + a.M23 * b.M33 + a.M24 * b.M43;
expected.M24 = a.M21 * b.M14 + a.M22 * b.M24 + a.M23 * b.M34 + a.M24 * b.M44;
expected.M31 = a.M31 * b.M11 + a.M32 * b.M21 + a.M33 * b.M31 + a.M34 * b.M41;
expected.M32 = a.M31 * b.M12 + a.M32 * b.M22 + a.M33 * b.M32 + a.M34 * b.M42;
expected.M33 = a.M31 * b.M13 + a.M32 * b.M23 + a.M33 * b.M33 + a.M34 * b.M43;
expected.M34 = a.M31 * b.M14 + a.M32 * b.M24 + a.M33 * b.M34 + a.M34 * b.M44;
expected.M41 = a.M41 * b.M11 + a.M42 * b.M21 + a.M43 * b.M31 + a.M44 * b.M41;
expected.M42 = a.M41 * b.M12 + a.M42 * b.M22 + a.M43 * b.M32 + a.M44 * b.M42;
expected.M43 = a.M41 * b.M13 + a.M42 * b.M23 + a.M43 * b.M33 + a.M44 * b.M43;
expected.M44 = a.M41 * b.M14 + a.M42 * b.M24 + a.M43 * b.M34 + a.M44 * b.M44;
Matrix4x4 actual;
actual = Matrix4x4.Multiply(a, b);
Assert.Equal(expected, actual);
}
// A test for Multiply (Matrix4x4, double)
[Fact]
public void Matrix4x4MultiplyTest5()
{
Matrix4x4 a = GenerateMatrixNumberFrom1To16();
Matrix4x4 expected = new(3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48);
Matrix4x4 actual = Matrix4x4.Multiply(a, 3);
Assert.Equal(expected, actual);
}
// A test for Multiply (Matrix4x4, double)
[Fact]
public void Matrix4x4MultiplyTest6()
{
Matrix4x4 a = GenerateMatrixNumberFrom1To16();
Matrix4x4 expected = new(3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36, 39, 42, 45, 48);
Matrix4x4 actual = a * 3;
Assert.Equal(expected, actual);
}
// A test for Negate (Matrix4x4)
[Fact]
public void Matrix4x4NegateTest()
{
Matrix4x4 m = GenerateMatrixNumberFrom1To16();
Matrix4x4 expected = new();
expected.M11 = -1.0;
expected.M12 = -2.0;
expected.M13 = -3.0;
expected.M14 = -4.0;
expected.M21 = -5.0;
expected.M22 = -6.0;
expected.M23 = -7.0;
expected.M24 = -8.0;
expected.M31 = -9.0;
expected.M32 = -10.0;
expected.M33 = -11.0;
expected.M34 = -12.0;
expected.M41 = -13.0;
expected.M42 = -14.0;
expected.M43 = -15.0;
expected.M44 = -16.0;
Matrix4x4 actual;
actual = Matrix4x4.Negate(m);
Assert.Equal(expected, actual);
}
// A test for operator != (Matrix4x4, Matrix4x4)
[Fact]
public void Matrix4x4InequalityTest()
{
Matrix4x4 a = GenerateMatrixNumberFrom1To16();
Matrix4x4 b = GenerateMatrixNumberFrom1To16();
// case 1: compare between same values
bool expected = false;
bool actual = a != b;
Assert.Equal(expected, actual);
// case 2: compare between different values
b.M11 = 11.0;
expected = true;
actual = a != b;
Assert.Equal(expected, actual);
}
// A test for operator == (Matrix4x4, Matrix4x4)
[Fact]
public void Matrix4x4EqualityTest()
{
Matrix4x4 a = GenerateMatrixNumberFrom1To16();
Matrix4x4 b = GenerateMatrixNumberFrom1To16();
// case 1: compare between same values
bool expected = true;
bool actual = a == b;
Assert.Equal(expected, actual);
// case 2: compare between different values
b.M11 = 11.0;
expected = false;
actual = a == b;
Assert.Equal(expected, actual);
}
// A test for Subtract (Matrix4x4, Matrix4x4)
[Fact]
public void Matrix4x4SubtractTest()
{
Matrix4x4 a = GenerateMatrixNumberFrom1To16();
Matrix4x4 b = GenerateMatrixNumberFrom1To16();
Matrix4x4 expected = new();
Matrix4x4 actual;
actual = Matrix4x4.Subtract(a, b);
Assert.Equal(expected, actual);
}
private void CreateBillboardFact(Vector3 placeDirection, Vector3 cameraUpVector, Matrix4x4 expectedRotation)
{
Vector3 cameraPosition = new(3.0, 4.0, 5.0);
Vector3 objectPosition = cameraPosition + placeDirection * 10.0;
Matrix4x4 expected = expectedRotation * Matrix4x4.CreateTranslation(objectPosition);
Matrix4x4 actual = Matrix4x4.CreateBillboard(objectPosition, cameraPosition, cameraUpVector, new Vector3(0, 0, -1));
Assert.True(MathHelper.Equal(expected, actual), "Matrix4x4.CreateBillboard did not return the expected value.");
}
// A test for CreateBillboard (Vector3, Vector3, Vector3, Vector3?)
// Place object at Forward side of camera on XZ-plane
[Fact]
public void Matrix4x4CreateBillboardTest01() =>
// Object placed at Forward of camera. result must be same as 180 degrees rotate along y-axis.
CreateBillboardFact(
new Vector3(0, 0, -1),
new Vector3(0, 1, 0),
Matrix4x4.CreateRotationY(MathHelper.ToRadians(180.0))
);
// A test for CreateBillboard (Vector3, Vector3, Vector3, Vector3?)
// Place object at Backward side of camera on XZ-plane
[Fact]
public void Matrix4x4CreateBillboardTest02() =>
// Object placed at Backward of camera. This result must be same as 0 degrees rotate along y-axis.
CreateBillboardFact(new Vector3(0, 0, 1), new Vector3(0, 1, 0), Matrix4x4.CreateRotationY(MathHelper.ToRadians(0)));
// A test for CreateBillboard (Vector3, Vector3, Vector3, Vector3?)
// Place object at Right side of camera on XZ-plane
[Fact]
public void Matrix4x4CreateBillboardTest03() =>
// Place object at Right side of camera. This result must be same as 90 degrees rotate along y-axis.
CreateBillboardFact(
new Vector3(1, 0, 0),
new Vector3(0, 1, 0),
Matrix4x4.CreateRotationY(MathHelper.ToRadians(90))
);
// A test for CreateBillboard (Vector3, Vector3, Vector3, Vector3?)
// Place object at Left side of camera on XZ-plane
[Fact]
public void Matrix4x4CreateBillboardTest04() =>
// Place object at Left side of camera. This result must be same as -90 degrees rotate along y-axis.
CreateBillboardFact(
new Vector3(-1, 0, 0),
new Vector3(0, 1, 0),
Matrix4x4.CreateRotationY(MathHelper.ToRadians(-90))
);
// A test for CreateBillboard (Vector3, Vector3, Vector3, Vector3?)
// Place object at Up side of camera on XY-plane
[Fact]
public void Matrix4x4CreateBillboardTest05() =>
// Place object at Up side of camera. result must be same as 180 degrees rotate along z-axis after 90 degrees rotate along x-axis.
CreateBillboardFact(
new Vector3(0, 1, 0),
new Vector3(0, 0, 1),
Matrix4x4.CreateRotationX(MathHelper.ToRadians(90.0)) * Matrix4x4.CreateRotationZ(MathHelper.ToRadians(180))
);
// A test for CreateBillboard (Vector3, Vector3, Vector3, Vector3?)
// Place object at Down side of camera on XY-plane
[Fact]
public void Matrix4x4CreateBillboardTest06() =>
// Place object at Down side of camera. result must be same as 0 degrees rotate along z-axis after 90 degrees rotate along x-axis.
CreateBillboardFact(
new Vector3(0, -1, 0),
new Vector3(0, 0, 1),
Matrix4x4.CreateRotationX(MathHelper.ToRadians(90.0)) * Matrix4x4.CreateRotationZ(MathHelper.ToRadians(0))
);
// A test for CreateBillboard (Vector3, Vector3, Vector3, Vector3?)
// Place object at Right side of camera on XY-plane
[Fact]
public void Matrix4x4CreateBillboardTest07() =>
// Place object at Right side of camera. result must be same as 90 degrees rotate along z-axis after 90 degrees rotate along x-axis.
CreateBillboardFact(
new Vector3(1, 0, 0),
new Vector3(0, 0, 1),
Matrix4x4.CreateRotationX(MathHelper.ToRadians(90.0)) * Matrix4x4.CreateRotationZ(MathHelper.ToRadians(90.0))
);
// A test for CreateBillboard (Vector3, Vector3, Vector3, Vector3?)
// Place object at Left side of camera on XY-plane
[Fact]
public void Matrix4x4CreateBillboardTest08() =>
// Place object at Left side of camera. result must be same as -90 degrees rotate along z-axis after 90 degrees rotate along x-axis.
CreateBillboardFact(
new Vector3(-1, 0, 0),
new Vector3(0, 0, 1),
Matrix4x4.CreateRotationX(MathHelper.ToRadians(90.0)) * Matrix4x4.CreateRotationZ(MathHelper.ToRadians(-90.0))
);
// A test for CreateBillboard (Vector3, Vector3, Vector3, Vector3?)
// Place object at Up side of camera on YZ-plane
[Fact]
public void Matrix4x4CreateBillboardTest09() =>
// Place object at Up side of camera. result must be same as -90 degrees rotate along x-axis after 90 degrees rotate along z-axis.
CreateBillboardFact(
new Vector3(0, 1, 0),
new Vector3(-1, 0, 0),
Matrix4x4.CreateRotationZ(MathHelper.ToRadians(90.0)) * Matrix4x4.CreateRotationX(MathHelper.ToRadians(-90.0))
);
// A test for CreateBillboard (Vector3, Vector3, Vector3, Vector3?)
// Place object at Down side of camera on YZ-plane
[Fact]
public void Matrix4x4CreateBillboardTest10() =>
// Place object at Down side of camera. result must be same as 90 degrees rotate along x-axis after 90 degrees rotate along z-axis.
CreateBillboardFact(
new Vector3(0, -1, 0),
new Vector3(-1, 0, 0),
Matrix4x4.CreateRotationZ(MathHelper.ToRadians(90.0)) * Matrix4x4.CreateRotationX(MathHelper.ToRadians(90.0))
);
// A test for CreateBillboard (Vector3, Vector3, Vector3, Vector3?)
// Place object at Forward side of camera on YZ-plane
[Fact]
public void Matrix4x4CreateBillboardTest11() =>
// Place object at Forward side of camera. result must be same as 180 degrees rotate along x-axis after 90 degrees rotate along z-axis.
CreateBillboardFact(
new Vector3(0, 0, -1),
new Vector3(-1, 0, 0),
Matrix4x4.CreateRotationZ(MathHelper.ToRadians(90.0)) * Matrix4x4.CreateRotationX(MathHelper.ToRadians(180.0))
);
// A test for CreateBillboard (Vector3, Vector3, Vector3, Vector3?)
// Place object at Backward side of camera on YZ-plane
[Fact]
public void Matrix4x4CreateBillboardTest12() =>
// Place object at Backward side of camera. result must be same as 0 degrees rotate along x-axis after 90 degrees rotate along z-axis.
CreateBillboardFact(
new Vector3(0, 0, 1),
new Vector3(-1, 0, 0),
Matrix4x4.CreateRotationZ(MathHelper.ToRadians(90.0)) * Matrix4x4.CreateRotationX(MathHelper.ToRadians(0.0))
);
// A test for CreateBillboard (Vector3, Vector3, Vector3, Vector3?)
// Object and camera positions are too close and doesn't pass cameraForwardVector.
[Fact]
public void Matrix4x4CreateBillboardTooCloseTest1()
{
Vector3 objectPosition = new(3.0, 4.0, 5.0);
Vector3 cameraPosition = objectPosition;
Vector3 cameraUpVector = new(0, 1, 0);
// Doesn't pass camera face direction. CreateBillboard uses new Vector3(0, 0, -1) direction. Result must be same as 180 degrees rotate along y-axis.
Matrix4x4 expected =
Matrix4x4.CreateRotationY(MathHelper.ToRadians(180.0)) * Matrix4x4.CreateTranslation(objectPosition);
Matrix4x4 actual = Matrix4x4.CreateBillboard(objectPosition, cameraPosition, cameraUpVector, new Vector3(0, 0, 1));
Assert.True(MathHelper.Equal(expected, actual), "Matrix4x4.CreateBillboard did not return the expected value.");
}
// A test for CreateBillboard (Vector3, Vector3, Vector3, Vector3?)
// Object and camera positions are too close and passed cameraForwardVector.
[Fact]
public void Matrix4x4CreateBillboardTooCloseTest2()
{
Vector3 objectPosition = new(3.0, 4.0, 5.0);
Vector3 cameraPosition = objectPosition;
Vector3 cameraUpVector = new(0, 1, 0);
// Passes Vector3.Right as camera face direction. Result must be same as -90 degrees rotate along y-axis.
Matrix4x4 expected =
Matrix4x4.CreateRotationY(MathHelper.ToRadians(-90.0)) * Matrix4x4.CreateTranslation(objectPosition);
Matrix4x4 actual = Matrix4x4.CreateBillboard(objectPosition, cameraPosition, cameraUpVector, new Vector3(1, 0, 0));
Assert.True(MathHelper.Equal(expected, actual), "Matrix4x4.CreateBillboard did not return the expected value.");
}
private void CreateConstrainedBillboardFact(Vector3 placeDirection, Vector3 rotateAxis, Matrix4x4 expectedRotation)
{
Vector3 cameraPosition = new(3.0, 4.0, 5.0);
Vector3 objectPosition = cameraPosition + placeDirection * 10.0;
Matrix4x4 expected = expectedRotation * Matrix4x4.CreateTranslation(objectPosition);
Matrix4x4 actual = Matrix4x4.CreateConstrainedBillboard(
objectPosition,
cameraPosition,
rotateAxis,
new Vector3(0, 0, -1),
new Vector3(0, 0, -1)
);
Assert.True(
MathHelper.Equal(expected, actual),
"Matrix4x4.CreateConstrainedBillboard did not return the expected value."
);
// When you move camera along rotateAxis, result must be same.
cameraPosition += rotateAxis * 10.0;
actual = Matrix4x4.CreateConstrainedBillboard(
objectPosition,
cameraPosition,
rotateAxis,
new Vector3(0, 0, -1),
new Vector3(0, 0, -1)
);
Assert.True(
MathHelper.Equal(expected, actual),
"Matrix4x4.CreateConstrainedBillboard did not return the expected value."
);
cameraPosition -= rotateAxis * 30.0;
actual = Matrix4x4.CreateConstrainedBillboard(
objectPosition,
cameraPosition,
rotateAxis,
new Vector3(0, 0, -1),
new Vector3(0, 0, -1)
);
Assert.True(
MathHelper.Equal(expected, actual),
"Matrix4x4.CreateConstrainedBillboard did not return the expected value."
);
}
// A test for CreateConstrainedBillboard (Vector3, Vector3, Vector3, Vector3?)
// Place object at Forward side of camera on XZ-plane
[Fact]
public void Matrix4x4CreateConstrainedBillboardTest01() =>
// Object placed at Forward of camera. result must be same as 180 degrees rotate along y-axis.
CreateConstrainedBillboardFact(
new Vector3(0, 0, -1),
new Vector3(0, 1, 0),
Matrix4x4.CreateRotationY(MathHelper.ToRadians(180.0))
);
// A test for CreateConstrainedBillboard (Vector3, Vector3, Vector3, Vector3?)
// Place object at Backward side of camera on XZ-plane
[Fact]
public void Matrix4x4CreateConstrainedBillboardTest02() =>
// Object placed at Backward of camera. This result must be same as 0 degrees rotate along y-axis.
CreateConstrainedBillboardFact(
new Vector3(0, 0, 1),
new Vector3(0, 1, 0),
Matrix4x4.CreateRotationY(MathHelper.ToRadians(0))
);
// A test for CreateConstrainedBillboard (Vector3, Vector3, Vector3, Vector3?)
// Place object at Right side of camera on XZ-plane
[Fact]
public void Matrix4x4CreateConstrainedBillboardTest03() =>
// Place object at Right side of camera. This result must be same as 90 degrees rotate along y-axis.
CreateConstrainedBillboardFact(
new Vector3(1, 0, 0),
new Vector3(0, 1, 0),
Matrix4x4.CreateRotationY(MathHelper.ToRadians(90))
);
// A test for CreateConstrainedBillboard (Vector3, Vector3, Vector3, Vector3?)
// Place object at Left side of camera on XZ-plane
[Fact]
public void Matrix4x4CreateConstrainedBillboardTest04() =>
// Place object at Left side of camera. This result must be same as -90 degrees rotate along y-axis.
CreateConstrainedBillboardFact(
new Vector3(-1, 0, 0),
new Vector3(0, 1, 0),
Matrix4x4.CreateRotationY(MathHelper.ToRadians(-90))
);
// A test for CreateConstrainedBillboard (Vector3, Vector3, Vector3, Vector3?)
// Place object at Up side of camera on XY-plane
[Fact]
public void Matrix4x4CreateConstrainedBillboardTest05() =>
// Place object at Up side of camera. result must be same as 180 degrees rotate along z-axis after 90 degrees rotate along x-axis.
CreateConstrainedBillboardFact(
new Vector3(0, 1, 0),
new Vector3(0, 0, 1),
Matrix4x4.CreateRotationX(MathHelper.ToRadians(90.0)) * Matrix4x4.CreateRotationZ(MathHelper.ToRadians(180))
);
// A test for CreateConstrainedBillboard (Vector3, Vector3, Vector3, Vector3?)
// Place object at Down side of camera on XY-plane
[Fact]
public void Matrix4x4CreateConstrainedBillboardTest06() =>
// Place object at Down side of camera. result must be same as 0 degrees rotate along z-axis after 90 degrees rotate along x-axis.
CreateConstrainedBillboardFact(
new Vector3(0, -1, 0),
new Vector3(0, 0, 1),
Matrix4x4.CreateRotationX(MathHelper.ToRadians(90.0)) * Matrix4x4.CreateRotationZ(MathHelper.ToRadians(0))
);
// A test for CreateConstrainedBillboard (Vector3, Vector3, Vector3, Vector3?)
// Place object at Right side of camera on XY-plane
[Fact]
public void Matrix4x4CreateConstrainedBillboardTest07() =>
// Place object at Right side of camera. result must be same as 90 degrees rotate along z-axis after 90 degrees rotate along x-axis.
CreateConstrainedBillboardFact(
new Vector3(1, 0, 0),
new Vector3(0, 0, 1),
Matrix4x4.CreateRotationX(MathHelper.ToRadians(90.0)) * Matrix4x4.CreateRotationZ(MathHelper.ToRadians(90.0))
);
// A test for CreateConstrainedBillboard (Vector3, Vector3, Vector3, Vector3?)
// Place object at Left side of camera on XY-plane
[Fact]
public void Matrix4x4CreateConstrainedBillboardTest08() =>
// Place object at Left side of camera. result must be same as -90 degrees rotate along z-axis after 90 degrees rotate along x-axis.
CreateConstrainedBillboardFact(
new Vector3(-1, 0, 0),
new Vector3(0, 0, 1),
Matrix4x4.CreateRotationX(MathHelper.ToRadians(90.0)) * Matrix4x4.CreateRotationZ(MathHelper.ToRadians(-90.0))
);
// A test for CreateConstrainedBillboard (Vector3, Vector3, Vector3, Vector3?)
// Place object at Up side of camera on YZ-plane
[Fact]
public void Matrix4x4CreateConstrainedBillboardTest09() =>
// Place object at Up side of camera. result must be same as -90 degrees rotate along x-axis after 90 degrees rotate along z-axis.
CreateConstrainedBillboardFact(
new Vector3(0, 1, 0),
new Vector3(-1, 0, 0),
Matrix4x4.CreateRotationZ(MathHelper.ToRadians(90.0)) * Matrix4x4.CreateRotationX(MathHelper.ToRadians(-90.0))
);
// A test for CreateConstrainedBillboard (Vector3, Vector3, Vector3, Vector3?)
// Place object at Down side of camera on YZ-plane
[Fact]
public void Matrix4x4CreateConstrainedBillboardTest10() =>
// Place object at Down side of camera. result must be same as 90 degrees rotate along x-axis after 90 degrees rotate along z-axis.
CreateConstrainedBillboardFact(
new Vector3(0, -1, 0),
new Vector3(-1, 0, 0),
Matrix4x4.CreateRotationZ(MathHelper.ToRadians(90.0)) * Matrix4x4.CreateRotationX(MathHelper.ToRadians(90.0))
);
// A test for CreateConstrainedBillboard (Vector3, Vector3, Vector3, Vector3?)
// Place object at Forward side of camera on YZ-plane
[Fact]
public void Matrix4x4CreateConstrainedBillboardTest11() =>
// Place object at Forward side of camera. result must be same as 180 degrees rotate along x-axis after 90 degrees rotate along z-axis.
CreateConstrainedBillboardFact(
new Vector3(0, 0, -1),
new Vector3(-1, 0, 0),
Matrix4x4.CreateRotationZ(MathHelper.ToRadians(90.0)) * Matrix4x4.CreateRotationX(MathHelper.ToRadians(180.0))
);
// A test for CreateConstrainedBillboard (Vector3, Vector3, Vector3, Vector3?)
// Place object at Backward side of camera on YZ-plane
[Fact]
public void Matrix4x4CreateConstrainedBillboardTest12() =>
// Place object at Backward side of camera. result must be same as 0 degrees rotate along x-axis after 90 degrees rotate along z-axis.
CreateConstrainedBillboardFact(
new Vector3(0, 0, 1),
new Vector3(-1, 0, 0),
Matrix4x4.CreateRotationZ(MathHelper.ToRadians(90.0)) * Matrix4x4.CreateRotationX(MathHelper.ToRadians(0.0))
);
// A test for CreateConstrainedBillboard (Vector3, Vector3, Vector3, Vector3?)
// Object and camera positions are too close and doesn't pass cameraForwardVector.
[Fact]
public void Matrix4x4CreateConstrainedBillboardTooCloseTest1()
{
Vector3 objectPosition = new(3.0, 4.0, 5.0);
Vector3 cameraPosition = objectPosition;
Vector3 cameraUpVector = new(0, 1, 0);
// Doesn't pass camera face direction. CreateConstrainedBillboard uses new Vector3(0, 0, -1) direction. Result must be same as 180 degrees rotate along y-axis.
Matrix4x4 expected =
Matrix4x4.CreateRotationY(MathHelper.ToRadians(180.0)) * Matrix4x4.CreateTranslation(objectPosition);
Matrix4x4 actual = Matrix4x4.CreateConstrainedBillboard(
objectPosition,
cameraPosition,
cameraUpVector,
new Vector3(0, 0, 1),
new Vector3(0, 0, -1)
);
Assert.True(
MathHelper.Equal(expected, actual),
"Matrix4x4.CreateConstrainedBillboard did not return the expected value."
);
}
// A test for CreateConstrainedBillboard (Vector3, Vector3, Vector3, Vector3?)
// Object and camera positions are too close and passed cameraForwardVector.
[Fact]
public void Matrix4x4CreateConstrainedBillboardTooCloseTest2()
{
Vector3 objectPosition = new(3.0, 4.0, 5.0);
Vector3 cameraPosition = objectPosition;
Vector3 cameraUpVector = new(0, 1, 0);
// Passes Vector3.Right as camera face direction. Result must be same as -90 degrees rotate along y-axis.
Matrix4x4 expected =
Matrix4x4.CreateRotationY(MathHelper.ToRadians(-90.0)) * Matrix4x4.CreateTranslation(objectPosition);
Matrix4x4 actual = Matrix4x4.CreateConstrainedBillboard(
objectPosition,
cameraPosition,
cameraUpVector,
new Vector3(1, 0, 0),
new Vector3(0, 0, -1)
);
Assert.True(
MathHelper.Equal(expected, actual),
"Matrix4x4.CreateConstrainedBillboard did not return the expected value."
);
}
// A test for CreateConstrainedBillboard (Vector3, Vector3, Vector3, Vector3?)
// Angle between rotateAxis and camera to object vector is too small. And use doesn't passed objectForwardVector parameter.
[Fact]
public void Matrix4x4CreateConstrainedBillboardAlongAxisTest1()
{
// Place camera at up side of object.
Vector3 objectPosition = new(3.0, 4.0, 5.0);
Vector3 rotateAxis = new(0, 1, 0);
Vector3 cameraPosition = objectPosition + rotateAxis * 10.0;
// In this case, CreateConstrainedBillboard picks new Vector3(0, 0, -1) as object forward vector.
Matrix4x4 expected =
Matrix4x4.CreateRotationY(MathHelper.ToRadians(180.0)) * Matrix4x4.CreateTranslation(objectPosition);
Matrix4x4 actual = Matrix4x4.CreateConstrainedBillboard(
objectPosition,
cameraPosition,
rotateAxis,
new Vector3(0, 0, -1),
new Vector3(0, 0, -1)
);
Assert.True(
MathHelper.Equal(expected, actual),
"Matrix4x4.CreateConstrainedBillboard did not return the expected value."
);
}
// A test for CreateConstrainedBillboard (Vector3, Vector3, Vector3, Vector3?)
// Angle between rotateAxis and camera to object vector is too small. And user doesn't passed objectForwardVector parameter.
[Fact]
public void Matrix4x4CreateConstrainedBillboardAlongAxisTest2()
{
// Place camera at up side of object.
Vector3 objectPosition = new(3.0, 4.0, 5.0);
Vector3 rotateAxis = new(0, 0, -1);
Vector3 cameraPosition = objectPosition + rotateAxis * 10.0;
// In this case, CreateConstrainedBillboard picks new Vector3(1, 0, 0) as object forward vector.
Matrix4x4 expected =
Matrix4x4.CreateRotationX(MathHelper.ToRadians(-90.0))
* Matrix4x4.CreateRotationZ(MathHelper.ToRadians(-90.0))
* Matrix4x4.CreateTranslation(objectPosition);
Matrix4x4 actual = Matrix4x4.CreateConstrainedBillboard(
objectPosition,
cameraPosition,
rotateAxis,
new Vector3(0, 0, -1),
new Vector3(0, 0, -1)
);
Assert.True(
MathHelper.Equal(expected, actual),
"Matrix4x4.CreateConstrainedBillboard did not return the expected value."
);
}
// A test for CreateConstrainedBillboard (Vector3, Vector3, Vector3, Vector3?)
// Angle between rotateAxis and camera to object vector is too small. And user passed correct objectForwardVector parameter.
[Fact]
public void Matrix4x4CreateConstrainedBillboardAlongAxisTest3()
{
// Place camera at up side of object.
Vector3 objectPosition = new(3.0, 4.0, 5.0);
Vector3 rotateAxis = new(0, 1, 0);
Vector3 cameraPosition = objectPosition + rotateAxis * 10.0;
// User passes correct objectForwardVector.
Matrix4x4 expected =
Matrix4x4.CreateRotationY(MathHelper.ToRadians(180.0)) * Matrix4x4.CreateTranslation(objectPosition);
Matrix4x4 actual = Matrix4x4.CreateConstrainedBillboard(
objectPosition,
cameraPosition,
rotateAxis,
new Vector3(0, 0, -1),
new Vector3(0, 0, -1)
);
Assert.True(
MathHelper.Equal(expected, actual),
"Matrix4x4.CreateConstrainedBillboard did not return the expected value."
);
}
// A test for CreateConstrainedBillboard (Vector3, Vector3, Vector3, Vector3?)
// Angle between rotateAxis and camera to object vector is too small. And user passed incorrect objectForwardVector parameter.
[Fact]
public void Matrix4x4CreateConstrainedBillboardAlongAxisTest4()
{
// Place camera at up side of object.
Vector3 objectPosition = new(3.0, 4.0, 5.0);
Vector3 rotateAxis = new(0, 1, 0);
Vector3 cameraPosition = objectPosition + rotateAxis * 10.0;
// User passes correct objectForwardVector.
Matrix4x4 expected =
Matrix4x4.CreateRotationY(MathHelper.ToRadians(180.0)) * Matrix4x4.CreateTranslation(objectPosition);
Matrix4x4 actual = Matrix4x4.CreateConstrainedBillboard(
objectPosition,
cameraPosition,
rotateAxis,
new Vector3(0, 0, -1),
new Vector3(0, 1, 0)
);
Assert.True(
MathHelper.Equal(expected, actual),
"Matrix4x4.CreateConstrainedBillboard did not return the expected value."
);
}
// A test for CreateConstrainedBillboard (Vector3, Vector3, Vector3, Vector3?)
// Angle between rotateAxis and camera to object vector is too small. And user passed incorrect objectForwardVector parameter.
[Fact]
public void Matrix4x4CreateConstrainedBillboardAlongAxisTest5()
{
// Place camera at up side of object.
Vector3 objectPosition = new(3.0, 4.0, 5.0);
Vector3 rotateAxis = new(0, 0, -1);
Vector3 cameraPosition = objectPosition + rotateAxis * 10.0;
// In this case, CreateConstrainedBillboard picks Vector3.Right as object forward vector.
Matrix4x4 expected =
Matrix4x4.CreateRotationX(MathHelper.ToRadians(-90.0))
* Matrix4x4.CreateRotationZ(MathHelper.ToRadians(-90.0))
* Matrix4x4.CreateTranslation(objectPosition);
Matrix4x4 actual = Matrix4x4.CreateConstrainedBillboard(
objectPosition,
cameraPosition,
rotateAxis,
new Vector3(0, 0, -1),
new Vector3(0, 0, -1)
);
Assert.True(
MathHelper.Equal(expected, actual),
"Matrix4x4.CreateConstrainedBillboard did not return the expected value."
);
}
// A test for CreateScale (Vector3)
[Fact]
public void Matrix4x4CreateScaleTest1()
{
Vector3 scales = new(2.0, 3.0, 4.0);
Matrix4x4 actual = Matrix4x4.CreateScale(scales);
Matrix4x4 expected = new(2.0, 0.0, 0.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 1.0);
Assert.Equal(expected, actual);
}
// A test for CreateScale (Vector3, Vector3)
[Fact]
public void Matrix4x4CreateScaleCenterTest1()
{
Vector3 scale = new(3, 4, 5);
Vector3 center = new(23, 42, 666);
Matrix4x4 scaleAroundZero = Matrix4x4.CreateScale(scale, Vector3.Zero);
Matrix4x4 scaleAroundZeroExpected = Matrix4x4.CreateScale(scale);
Assert.True(MathHelper.Equal(scaleAroundZero, scaleAroundZeroExpected));
Matrix4x4 scaleAroundCenter = Matrix4x4.CreateScale(scale, center);
Matrix4x4 scaleAroundCenterExpected =
Matrix4x4.CreateTranslation(-center) * Matrix4x4.CreateScale(scale) * Matrix4x4.CreateTranslation(center);
Assert.True(MathHelper.Equal(scaleAroundCenter, scaleAroundCenterExpected));
}
// A test for CreateScale (double)
[Fact]
public void Matrix4x4CreateScaleTest2()
{
double scale = 2.0;
Matrix4x4 expected = new(2.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 1.0);
Matrix4x4 actual = Matrix4x4.CreateScale(scale);
Assert.Equal(expected, actual);
}
// A test for CreateScale (double, Vector3)
[Fact]
public void Matrix4x4CreateScaleCenterTest2()
{
double scale = 5;
Vector3 center = new(23, 42, 666);
Matrix4x4 scaleAroundZero = Matrix4x4.CreateScale(scale, Vector3.Zero);
Matrix4x4 scaleAroundZeroExpected = Matrix4x4.CreateScale(scale);
Assert.True(MathHelper.Equal(scaleAroundZero, scaleAroundZeroExpected));
Matrix4x4 scaleAroundCenter = Matrix4x4.CreateScale(scale, center);
Matrix4x4 scaleAroundCenterExpected =
Matrix4x4.CreateTranslation(-center) * Matrix4x4.CreateScale(scale) * Matrix4x4.CreateTranslation(center);
Assert.True(MathHelper.Equal(scaleAroundCenter, scaleAroundCenterExpected));
}
// A test for CreateScale (double, double, double)
[Fact]
public void Matrix4x4CreateScaleTest3()
{
double xScale = 2.0;
double yScale = 3.0;
double zScale = 4.0;
Matrix4x4 expected = new(2.0, 0.0, 0.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 1.0);
Matrix4x4 actual = Matrix4x4.CreateScale(xScale, yScale, zScale);
Assert.Equal(expected, actual);
}
// A test for CreateScale (double, double, double, Vector3)
[Fact]
public void Matrix4x4CreateScaleCenterTest3()
{
Vector3 scale = new(3, 4, 5);
Vector3 center = new(23, 42, 666);
Matrix4x4 scaleAroundZero = Matrix4x4.CreateScale(scale.X, scale.Y, scale.Z, Vector3.Zero);
Matrix4x4 scaleAroundZeroExpected = Matrix4x4.CreateScale(scale.X, scale.Y, scale.Z);
Assert.True(MathHelper.Equal(scaleAroundZero, scaleAroundZeroExpected));
Matrix4x4 scaleAroundCenter = Matrix4x4.CreateScale(scale.X, scale.Y, scale.Z, center);
Matrix4x4 scaleAroundCenterExpected =
Matrix4x4.CreateTranslation(-center)
* Matrix4x4.CreateScale(scale.X, scale.Y, scale.Z)
* Matrix4x4.CreateTranslation(center);
Assert.True(MathHelper.Equal(scaleAroundCenter, scaleAroundCenterExpected));
}
// A test for CreateTranslation (Vector3)
[Fact]
public void Matrix4x4CreateTranslationTest1()
{
Vector3 position = new(2.0, 3.0, 4.0);
Matrix4x4 expected = new(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 2.0, 3.0, 4.0, 1.0);
Matrix4x4 actual = Matrix4x4.CreateTranslation(position);
Assert.Equal(expected, actual);
}
// A test for CreateTranslation (double, double, double)
[Fact]
public void Matrix4x4CreateTranslationTest2()
{
double xPosition = 2.0;
double yPosition = 3.0;
double zPosition = 4.0;
Matrix4x4 expected = new(1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 2.0, 3.0, 4.0, 1.0);
Matrix4x4 actual = Matrix4x4.CreateTranslation(xPosition, yPosition, zPosition);
Assert.Equal(expected, actual);
}
// A test for Translation
[Fact]
public void Matrix4x4TranslationTest()
{
Matrix4x4 a = GenerateTestMatrix();
Matrix4x4 b = a;
// Transformed vector that has same semantics of property must be same.
Vector3 val = new(a.M41, a.M42, a.M43);
Assert.Equal(val, a.Translation);
// Set value and get value must be same.
val = new Vector3(1.0, 2.0, 3.0);
a.Translation = val;
Assert.Equal(val, a.Translation);
// Make sure it only modifies expected value of matrix.
Assert.True(
a.M11 == b.M11
&& a.M12 == b.M12
&& a.M13 == b.M13
&& a.M14 == b.M14
&& a.M21 == b.M21
&& a.M22 == b.M22
&& a.M23 == b.M23
&& a.M24 == b.M24
&& a.M31 == b.M31
&& a.M32 == b.M32
&& a.M33 == b.M33
&& a.M34 == b.M34
&& a.M41 != b.M41
&& a.M42 != b.M42
&& a.M43 != b.M43
&& a.M44 == b.M44
);
}
// A test for Equals (Matrix4x4)
[Fact]
public void Matrix4x4EqualsTest1()
{
Matrix4x4 a = GenerateMatrixNumberFrom1To16();
Matrix4x4 b = GenerateMatrixNumberFrom1To16();
// 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.M11 = 11.0;
expected = false;
actual = a.Equals(b);
Assert.Equal(expected, actual);
}
// A test for IsIdentity
[Fact]
public void Matrix4x4IsIdentityTest()
{
Assert.True(Matrix4x4.Identity.IsIdentity);
Assert.True(new Matrix4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1).IsIdentity);
Assert.False(new Matrix4x4(0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1).IsIdentity);
Assert.False(new Matrix4x4(1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1).IsIdentity);
Assert.False(new Matrix4x4(1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1).IsIdentity);
Assert.False(new Matrix4x4(1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1).IsIdentity);
Assert.False(new Matrix4x4(1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1).IsIdentity);
Assert.False(new Matrix4x4(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1).IsIdentity);
Assert.False(new Matrix4x4(1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1).IsIdentity);
Assert.False(new Matrix4x4(1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1).IsIdentity);
Assert.False(new Matrix4x4(1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1).IsIdentity);
Assert.False(new Matrix4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1).IsIdentity);
Assert.False(new Matrix4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1).IsIdentity);
Assert.False(new Matrix4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1).IsIdentity);
Assert.False(new Matrix4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1).IsIdentity);
Assert.False(new Matrix4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1).IsIdentity);
Assert.False(new Matrix4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1).IsIdentity);
Assert.False(new Matrix4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0).IsIdentity);
}
// A test for Matrix4x4 (Matrix3x2)
[Fact]
public void Matrix4x4From3x2Test()
{
Matrix3x2 source = new(1, 2, 3, 4, 5, 6);
Matrix4x4 result = new(source);
Assert.Equal(source.M11, result.M11);
Assert.Equal(source.M12, result.M12);
Assert.Equal(0, result.M13);
Assert.Equal(0, result.M14);
Assert.Equal(source.M21, result.M21);
Assert.Equal(source.M22, result.M22);
Assert.Equal(0, result.M23);
Assert.Equal(0, result.M24);
Assert.Equal(0, result.M31);
Assert.Equal(0, result.M32);
Assert.Equal(1, result.M33);
Assert.Equal(0, result.M34);
Assert.Equal(source.M31, result.M41);
Assert.Equal(source.M32, result.M42);
Assert.Equal(0, result.M43);
Assert.Equal(1, result.M44);
}
// A test for Matrix4x4 comparison involving NaN values
[Fact]
public void Matrix4x4EqualsNanTest()
{
Matrix4x4 a = new(double.NaN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
Matrix4x4 b = new(0, double.NaN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
Matrix4x4 c = new(0, 0, double.NaN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
Matrix4x4 d = new(0, 0, 0, double.NaN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
Matrix4x4 e = new(0, 0, 0, 0, double.NaN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
Matrix4x4 f = new(0, 0, 0, 0, 0, double.NaN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
Matrix4x4 g = new(0, 0, 0, 0, 0, 0, double.NaN, 0, 0, 0, 0, 0, 0, 0, 0, 0);
Matrix4x4 h = new(0, 0, 0, 0, 0, 0, 0, double.NaN, 0, 0, 0, 0, 0, 0, 0, 0);
Matrix4x4 i = new(0, 0, 0, 0, 0, 0, 0, 0, double.NaN, 0, 0, 0, 0, 0, 0, 0);
Matrix4x4 j = new(0, 0, 0, 0, 0, 0, 0, 0, 0, double.NaN, 0, 0, 0, 0, 0, 0);
Matrix4x4 k = new(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, double.NaN, 0, 0, 0, 0, 0);
Matrix4x4 l = new(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, double.NaN, 0, 0, 0, 0);
Matrix4x4 m = new(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, double.NaN, 0, 0, 0);
Matrix4x4 n = new(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, double.NaN, 0, 0);
Matrix4x4 o = new(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, double.NaN, 0);
Matrix4x4 p = new(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, double.NaN);
Assert.False(a == new Matrix4x4());
Assert.False(b == new Matrix4x4());
Assert.False(c == new Matrix4x4());
Assert.False(d == new Matrix4x4());
Assert.False(e == new Matrix4x4());
Assert.False(f == new Matrix4x4());
Assert.False(g == new Matrix4x4());
Assert.False(h == new Matrix4x4());
Assert.False(i == new Matrix4x4());
Assert.False(j == new Matrix4x4());
Assert.False(k == new Matrix4x4());
Assert.False(l == new Matrix4x4());
Assert.False(m == new Matrix4x4());
Assert.False(n == new Matrix4x4());
Assert.False(o == new Matrix4x4());
Assert.False(p == new Matrix4x4());
Assert.True(a != new Matrix4x4());
Assert.True(b != new Matrix4x4());
Assert.True(c != new Matrix4x4());
Assert.True(d != new Matrix4x4());
Assert.True(e != new Matrix4x4());
Assert.True(f != new Matrix4x4());
Assert.True(g != new Matrix4x4());
Assert.True(h != new Matrix4x4());
Assert.True(i != new Matrix4x4());
Assert.True(j != new Matrix4x4());
Assert.True(k != new Matrix4x4());
Assert.True(l != new Matrix4x4());
Assert.True(m != new Matrix4x4());
Assert.True(n != new Matrix4x4());
Assert.True(o != new Matrix4x4());
Assert.True(p != new Matrix4x4());
Assert.False(a.Equals(new Matrix4x4()));
Assert.False(b.Equals(new Matrix4x4()));
Assert.False(c.Equals(new Matrix4x4()));
Assert.False(d.Equals(new Matrix4x4()));
Assert.False(e.Equals(new Matrix4x4()));
Assert.False(f.Equals(new Matrix4x4()));
Assert.False(g.Equals(new Matrix4x4()));
Assert.False(h.Equals(new Matrix4x4()));
Assert.False(i.Equals(new Matrix4x4()));
Assert.False(j.Equals(new Matrix4x4()));
Assert.False(k.Equals(new Matrix4x4()));
Assert.False(l.Equals(new Matrix4x4()));
Assert.False(m.Equals(new Matrix4x4()));
Assert.False(n.Equals(new Matrix4x4()));
Assert.False(o.Equals(new Matrix4x4()));
Assert.False(p.Equals(new Matrix4x4()));
Assert.False(a.IsIdentity);
Assert.False(b.IsIdentity);
Assert.False(c.IsIdentity);
Assert.False(d.IsIdentity);
Assert.False(e.IsIdentity);
Assert.False(f.IsIdentity);
Assert.False(g.IsIdentity);
Assert.False(h.IsIdentity);
Assert.False(i.IsIdentity);
Assert.False(j.IsIdentity);
Assert.False(k.IsIdentity);
Assert.False(l.IsIdentity);
Assert.False(m.IsIdentity);
Assert.False(n.IsIdentity);
Assert.False(o.IsIdentity);
Assert.False(p.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));
Assert.False(e.Equals(e));
Assert.False(f.Equals(f));
Assert.False(g.Equals(g));
Assert.False(h.Equals(h));
Assert.False(i.Equals(i));
Assert.False(j.Equals(j));
Assert.False(k.Equals(k));
Assert.False(l.Equals(l));
Assert.False(m.Equals(m));
Assert.False(n.Equals(n));
Assert.False(o.Equals(o));
Assert.False(p.Equals(p));
}
// A test to make sure these types are blittable directly into GPU buffer memory layouts
[Fact]
public unsafe void Matrix4x4SizeofTest()
{
Assert.Equal(64 * 2, sizeof(Matrix4x4));
Assert.Equal(128 * 2, sizeof(Matrix4x4_2x));
Assert.Equal(68 * 2, sizeof(Matrix4x4PlusFloat));
Assert.Equal(136 * 2, sizeof(Matrix4x4PlusFloat_2x));
}
[StructLayout(LayoutKind.Sequential)]
struct Matrix4x4_2x
{
private Matrix4x4 _a;
private Matrix4x4 _b;
}
[StructLayout(LayoutKind.Sequential)]
struct Matrix4x4PlusFloat
{
private Matrix4x4 _v;
private double _f;
}
[StructLayout(LayoutKind.Sequential)]
struct Matrix4x4PlusFloat_2x
{
private Matrix4x4PlusFloat _a;
private Matrix4x4PlusFloat _b;
}
// A test to make sure the fields are laid out how we expect
[Fact]
public unsafe void Matrix4x4FieldOffsetTest()
{
Matrix4x4 mat = new();
double* basePtr = &mat.M11; // Take address of first element
Matrix4x4* matPtr = &mat; // Take address of whole matrix
Assert.Equal(new IntPtr(basePtr), new IntPtr(matPtr));
Assert.Equal(new IntPtr(basePtr + 0), new IntPtr(&mat.M11));
Assert.Equal(new IntPtr(basePtr + 1), new IntPtr(&mat.M12));
Assert.Equal(new IntPtr(basePtr + 2), new IntPtr(&mat.M13));
Assert.Equal(new IntPtr(basePtr + 3), new IntPtr(&mat.M14));
Assert.Equal(new IntPtr(basePtr + 4), new IntPtr(&mat.M21));
Assert.Equal(new IntPtr(basePtr + 5), new IntPtr(&mat.M22));
Assert.Equal(new IntPtr(basePtr + 6), new IntPtr(&mat.M23));
Assert.Equal(new IntPtr(basePtr + 7), new IntPtr(&mat.M24));
Assert.Equal(new IntPtr(basePtr + 8), new IntPtr(&mat.M31));
Assert.Equal(new IntPtr(basePtr + 9), new IntPtr(&mat.M32));
Assert.Equal(new IntPtr(basePtr + 10), new IntPtr(&mat.M33));
Assert.Equal(new IntPtr(basePtr + 11), new IntPtr(&mat.M34));
Assert.Equal(new IntPtr(basePtr + 12), new IntPtr(&mat.M41));
Assert.Equal(new IntPtr(basePtr + 13), new IntPtr(&mat.M42));
Assert.Equal(new IntPtr(basePtr + 14), new IntPtr(&mat.M43));
Assert.Equal(new IntPtr(basePtr + 15), new IntPtr(&mat.M44));
}
}