Files
Misaki.HighPerformance/Misaki.HighPerformance.Test/UnitTest/Mathematics/TestMathFunctions.cs
Misaki b08662b77d fix(math): correct select logic in quaternion and svd
Fixed conditional selection logic in quaternion and SVD math functions by swapping select argument order for correctness. Fixed LookRotationSafe and normalizesafe to return valid quaternions. Corrected SVD helper functions for proper value swapping and safe reciprocal. Added unit tests for matrix, reflection, projection, refraction, quaternion normalization, LookRotationSafe, and SVD operations. Incremented project version to 1.3.3. Minor formatting and using directive updates.
2026-04-07 22:18:55 +09:00

353 lines
10 KiB
C#

using Misaki.HighPerformance.Mathematics;
using System.Numerics;
namespace Misaki.HighPerformance.Test.UnitTest.Mathematics;
[TestClass]
public class TestMathFunctions
{
[TestMethod]
public void TestTrigonometricFunctions()
{
// Test sin, cos, tan
var angle = math.PI / 4f; // 45 degrees
var sinValue = math.sin(angle);
var cosValue = math.cos(angle);
var tanValue = math.tan(angle);
Assert.AreEqual(math.SQRT2 / 2f, sinValue, 1e-6f);
Assert.AreEqual(math.SQRT2 / 2f, cosValue, 1e-6f);
Assert.AreEqual(1f, tanValue, 1e-6f);
// Test vector versions
var angles = new float2(0f, math.PI / 2f);
var sinValues = math.sin(angles);
var cosValues = math.cos(angles);
Assert.AreEqual(0f, sinValues.x, 1e-6f);
Assert.AreEqual(1f, sinValues.y, 1e-6f);
Assert.AreEqual(1f, cosValues.x, 1e-6f);
Assert.AreEqual(0f, cosValues.y, 1e-6f);
}
[TestMethod]
public void TestHyperbolicFunctions()
{
var value = 1f;
var sinhValue = math.sinh(value);
var coshValue = math.cosh(value);
var tanhValue = math.tanh(value);
// sinh(1) ≈ 1.175, cosh(1) ≈ 1.543, tanh(1) ≈ 0.762
Assert.AreEqual(1.175201f, sinhValue, 1e-3f);
Assert.AreEqual(1.543081f, coshValue, 1e-3f);
Assert.AreEqual(0.761594f, tanhValue, 1e-3f);
}
[TestMethod]
public void TestExponentialAndLogarithmic()
{
// Test exp, log
var value = 2f;
var expValue = math.exp(value);
var logValue = math.log(expValue);
Assert.AreEqual(value, logValue, 1e-6f);
// Test exp2, log2
var exp2Value = math.exp2(value);
var log2Value = math.log2(exp2Value);
Assert.AreEqual(value, log2Value, 1e-6f);
// Test exp10, log10
var exp10Value = math.exp10(value);
var log10Value = math.log10(exp10Value);
Assert.AreEqual(value, log10Value, 1e-6f);
}
[TestMethod]
public void TestPowerFunctions()
{
// Test pow
var baseValue = 2f;
var exponent = 3f;
var powValue = math.pow(baseValue, exponent);
Assert.AreEqual(8f, powValue, 1e-6f);
// Test sqrt
var sqrtValue = math.sqrt(16f);
Assert.AreEqual(4f, sqrtValue, 1e-6f);
// Test rsqrt (reciprocal square root)
var rsqrtValue = math.rsqrt(16f);
Assert.AreEqual(0.25f, rsqrtValue, 1e-6f);
}
[TestMethod]
public void TestMinMaxClamp()
{
// Test min, max
var a = 3f;
var b = 7f;
Assert.AreEqual(3f, math.min(a, b));
Assert.AreEqual(7f, math.max(a, b));
// Test clamp
var value = 5f;
var clamped = math.clamp(value, 2f, 4f);
Assert.AreEqual(4f, clamped, 1e-6f);
var clampedLow = math.clamp(1f, 2f, 4f);
Assert.AreEqual(2f, clampedLow, 1e-6f);
var clampedNormal = math.clamp(3f, 2f, 4f);
Assert.AreEqual(3f, clampedNormal, 1e-6f);
// Test vector versions
var va = new float3(1f, 5f, 9f);
var vb = new float3(3f, 3f, 3f);
var minResult = math.min(va, vb);
var maxResult = math.max(va, vb);
Assert.AreEqual(1f, minResult.x, 1e-6f);
Assert.AreEqual(3f, minResult.y, 1e-6f);
Assert.AreEqual(3f, minResult.z, 1e-6f);
Assert.AreEqual(3f, maxResult.x, 1e-6f);
Assert.AreEqual(5f, maxResult.y, 1e-6f);
Assert.AreEqual(9f, maxResult.z, 1e-6f);
}
[TestMethod]
public void TestRoundingFunctions()
{
var value = 3.7f;
// Test floor, ceil, round, trunc
Assert.AreEqual(3f, math.floor(value), 1e-6f);
Assert.AreEqual(4f, math.ceil(value), 1e-6f);
Assert.AreEqual(4f, math.round(value), 1e-6f);
Assert.AreEqual(3f, math.trunc(value), 1e-6f);
var negativeValue = -3.7f;
Assert.AreEqual(-4f, math.floor(negativeValue), 1e-6f);
Assert.AreEqual(-3f, math.ceil(negativeValue), 1e-6f);
Assert.AreEqual(-4f, math.round(negativeValue), 1e-6f);
Assert.AreEqual(-3f, math.trunc(negativeValue), 1e-6f);
}
[TestMethod]
public void TestAbsAndSign()
{
// Test abs
Assert.AreEqual(5f, math.abs(-5f), 1e-6f);
Assert.AreEqual(5f, math.abs(5f), 1e-6f);
Assert.AreEqual(0f, math.abs(0f), 1e-6f);
// Test sign
Assert.AreEqual(-1f, math.sign(-5f), 1e-6f);
Assert.AreEqual(1f, math.sign(5f), 1e-6f);
Assert.AreEqual(0f, math.sign(0f), 1e-6f);
// Test vector versions
var v = new float3(-2f, 0f, 3f);
var absResult = math.abs(v);
var signResult = math.sign(v);
Assert.AreEqual(2f, absResult.x, 1e-6f);
Assert.AreEqual(0f, absResult.y, 1e-6f);
Assert.AreEqual(3f, absResult.z, 1e-6f);
Assert.AreEqual(-1f, signResult.x, 1e-6f);
Assert.AreEqual(0f, signResult.y, 1e-6f);
Assert.AreEqual(1f, signResult.z, 1e-6f);
}
[TestMethod]
public void TestInterpolation()
{
// Test lerp (linear interpolation)
var a = 0f;
var b = 10f;
var t = 0.3f;
var lerped = math.lerp(a, b, t);
Assert.AreEqual(3f, lerped, 1e-6f);
// Test vector lerp
var va = new float3(0f, 0f, 0f);
var vb = new float3(10f, 20f, 30f);
var vLerped = math.lerp(va, vb, t);
Assert.AreEqual(3f, vLerped.x, 1e-6f);
Assert.AreEqual(6f, vLerped.y, 1e-6f);
Assert.AreEqual(9f, vLerped.z, 1e-6f);
// Test unlerp (inverse lerp)
var unlerpResult = math.unlerp(a, b, 3f);
Assert.AreEqual(0.3f, unlerpResult, 1e-6f);
}
[TestMethod]
public void TestStep()
{
// Test step function
var edge = 5f;
Assert.AreEqual(0f, math.step(edge, 3f), 1e-6f);
Assert.AreEqual(1f, math.step(edge, 5f), 1e-6f);
Assert.AreEqual(1f, math.step(edge, 7f), 1e-6f);
// Test smoothstep
var smoothResult1 = math.smoothstep(0f, 10f, 5f);
Assert.AreEqual(0.5f, smoothResult1, 1e-6f);
var smoothResult2 = math.smoothstep(0f, 10f, 0f);
Assert.AreEqual(0f, smoothResult2, 1e-6f);
var smoothResult3 = math.smoothstep(0f, 10f, 10f);
Assert.AreEqual(1f, smoothResult3, 1e-6f);
}
[TestMethod]
public void TestModAndRemainder()
{
// Test fmod
var fmodResult = math.fmod(7.5f, 3f);
Assert.AreEqual(1.5f, fmodResult, 1e-6f);
// Test remainder functions if available
var a = 7f;
var b = 3f;
var remainder = a % b;
Assert.AreEqual(1f, remainder, 1e-6f);
}
[TestMethod]
public void TestAngleConversions()
{
var degrees = 180f;
var radians = math.radians(degrees);
Assert.AreEqual(math.PI, radians, 1e-6f);
var backToDegrees = math.degrees(radians);
Assert.AreEqual(degrees, backToDegrees, 1e-6f);
}
[TestMethod]
public void TestFloatingPointChecks()
{
// Test isnan
Assert.IsTrue(math.isnan(float.NaN));
Assert.IsFalse(math.isnan(1f));
// Test isinf
Assert.IsTrue(math.isinf(float.PositiveInfinity));
Assert.IsTrue(math.isinf(float.NegativeInfinity));
Assert.IsFalse(math.isinf(1f));
// Test isfinite
Assert.IsFalse(math.isfinite(float.PositiveInfinity));
Assert.IsFalse(math.isfinite(float.NaN));
Assert.IsTrue(math.isfinite(1f));
}
[TestMethod]
public void TestBitOperations()
{
// Test asfloat, asint, asuint
var floatValue = 1.5f;
var intBits = math.asint(floatValue);
var floatBack = math.asfloat(intBits);
Assert.AreEqual(floatValue, floatBack, 1e-6f);
var uintBits = math.asuint(floatValue);
var floatFromUint = math.asfloat(uintBits);
Assert.AreEqual(floatValue, floatFromUint, 1e-6f);
}
[TestMethod]
public void TestSelectFunction()
{
// Test select function
var condition = new bool3(true, false, true);
var a = new float3(1f, 2f, 3f);
var b = new float3(4f, 5f, 6f);
var result = math.select(a, b, condition);
Assert.AreEqual(1f, result.x, 1e-6f); // condition is true, so select a.x
Assert.AreEqual(5f, result.y, 1e-6f); // condition is false, so select b.y
Assert.AreEqual(3f, result.z, 1e-6f); // condition is true, so select a.z
}
[TestMethod]
public void TestMatrixFunctions()
{
// Test determinant and inverse of a 2x2 matrix
var m = new float2x2(new float2(1f, 2f), new float2(3f, 4f));
var det = math.determinant(m);
Assert.AreEqual(-2f, det, 1e-6f);
var inv = math.inverse(m);
var expectedInv = new float2x2(new float2(-2f, 1f), new float2(1.5f, -0.5f));
Assert.AreEqual(expectedInv.c0.x, inv.c0.x, 1e-6f);
Assert.AreEqual(expectedInv.c0.y, inv.c0.y, 1e-6f);
Assert.AreEqual(expectedInv.c1.x, inv.c1.x, 1e-6f);
Assert.AreEqual(expectedInv.c1.y, inv.c1.y, 1e-6f);
}
[TestMethod]
public void TestRflections()
{
// Test reflect function
var incident = new float3(1f, -1f, 0f);
var normal = math.normalize(new float3(0f, 1f, 0f));
var reflected = math.reflect(incident, normal);
// The reflected vector should be (1, 1, 0)
Assert.AreEqual(1f, reflected.x, 1e-6f);
Assert.AreEqual(1f, reflected.y, 1e-6f);
Assert.AreEqual(0f, reflected.z, 1e-6f);
}
[TestMethod]
public void TestProjections()
{
// Test project function
var vector = new float3(1f, 2f, 3f);
var onNormal = math.normalize(new float3(0f, 1f, 0f));
var projected = math.project(vector, onNormal);
// The projection of (1,2,3) onto the Y-axis should be (0,2,0)
Assert.AreEqual(0f, projected.x, 1e-6f);
Assert.AreEqual(2f, projected.y, 1e-6f);
Assert.AreEqual(0f, projected.z, 1e-6f);
}
[TestMethod]
public void TestRefraction()
{
// Test refract function
var incident = math.normalize(new float3(1f, -1f, 0f));
var normal = math.normalize(new float3(0f, 1f, 0f));
var eta = 0.5f; // Refractive index ratio
var refracted = math.refract(incident, normal, eta);
// The refracted vector should be approximately (0.707, -0.707, 0)
Assert.AreEqual(0.3535534f, refracted.x, 1e-6f);
Assert.AreEqual(-0.9354144f, refracted.y, 1e-6f);
Assert.AreEqual(0f, refracted.z, 1e-6f);
}
}