Files
tooll3--t3/Core.Tests/CurveInterpolationTests.cs
2026-07-13 13:13:17 +08:00

271 lines
12 KiB
C#

using T3.Core.Animation;
using T3.Core.DataTypes;
using Xunit;
namespace Core.Tests;
public class CurveInterpolationTests
{
[Fact]
public void LinearToLinear_InterpolatesLinearly()
{
var curve = new Curve();
curve.AddOrUpdateV(0.0, new VDefinition
{
Value = 0.0,
InInterpolation = VDefinition.KeyInterpolation.Linear,
OutInterpolation = VDefinition.KeyInterpolation.Linear,
});
curve.AddOrUpdateV(1.0, new VDefinition
{
Value = 1.0,
InInterpolation = VDefinition.KeyInterpolation.Linear,
OutInterpolation = VDefinition.KeyInterpolation.Linear,
});
Assert.Equal(0.0, curve.GetSampledValue(0.0), 4);
Assert.Equal(0.25, curve.GetSampledValue(0.25), 4);
Assert.Equal(0.5, curve.GetSampledValue(0.5), 4);
Assert.Equal(0.75, curve.GetSampledValue(0.75), 4);
Assert.Equal(1.0, curve.GetSampledValue(1.0), 4);
}
[Fact]
public void LinearToLinear_NegativeSlope()
{
var curve = new Curve();
curve.AddOrUpdateV(0.0, new VDefinition
{
Value = 10.0,
InInterpolation = VDefinition.KeyInterpolation.Linear,
OutInterpolation = VDefinition.KeyInterpolation.Linear,
});
curve.AddOrUpdateV(1.0, new VDefinition
{
Value = 0.0,
InInterpolation = VDefinition.KeyInterpolation.Linear,
OutInterpolation = VDefinition.KeyInterpolation.Linear,
});
Assert.Equal(10.0, curve.GetSampledValue(0.0), 4);
Assert.Equal(5.0, curve.GetSampledValue(0.5), 4);
Assert.Equal(0.0, curve.GetSampledValue(1.0), 4);
}
[Fact]
public void CollinearSmoothKeys_FormStraightLine()
{
// Three points on y=x: [0,0] -> [0.5,0.5] -> [1,1]
// With smooth interpolation, collinear points should produce a straight line
var curve = new Curve();
curve.AddOrUpdateV(0.0, new VDefinition
{
Value = 0.0,
InInterpolation = VDefinition.KeyInterpolation.Linear,
OutInterpolation = VDefinition.KeyInterpolation.Linear,
});
curve.AddOrUpdateV(0.5, new VDefinition
{
Value = 0.5,
InInterpolation = VDefinition.KeyInterpolation.Smooth,
OutInterpolation = VDefinition.KeyInterpolation.Smooth,
});
curve.AddOrUpdateV(1.0, new VDefinition
{
Value = 1.0,
InInterpolation = VDefinition.KeyInterpolation.Linear,
OutInterpolation = VDefinition.KeyInterpolation.Linear,
});
// Sample at multiple points — should all lie on y=x
Assert.Equal(0.0, curve.GetSampledValue(0.0), 4);
Assert.Equal(0.125, curve.GetSampledValue(0.125), 4);
Assert.Equal(0.25, curve.GetSampledValue(0.25), 4);
Assert.Equal(0.5, curve.GetSampledValue(0.5), 4);
Assert.Equal(0.75, curve.GetSampledValue(0.75), 4);
Assert.Equal(0.875, curve.GetSampledValue(0.875), 4);
Assert.Equal(1.0, curve.GetSampledValue(1.0), 4);
}
[Fact]
public void ConstantToConstant_HoldsFirstValue()
{
var curve = new Curve();
curve.AddOrUpdateV(0.0, new VDefinition
{
Value = 3.0,
InInterpolation = VDefinition.KeyInterpolation.Constant,
OutInterpolation = VDefinition.KeyInterpolation.Constant,
});
curve.AddOrUpdateV(1.0, new VDefinition
{
Value = 7.0,
InInterpolation = VDefinition.KeyInterpolation.Constant,
OutInterpolation = VDefinition.KeyInterpolation.Constant,
});
// Should hold 3.0 for everything before the second key
Assert.Equal(3.0, curve.GetSampledValue(0.0), 4);
Assert.Equal(3.0, curve.GetSampledValue(0.25), 4);
Assert.Equal(3.0, curve.GetSampledValue(0.5), 4);
Assert.Equal(3.0, curve.GetSampledValue(0.999), 4);
// At exactly the second key, should snap to 7.0
Assert.Equal(7.0, curve.GetSampledValue(1.0), 4);
}
[Fact]
public void ConstantToConstant_MultipleKeys()
{
var curve = new Curve();
curve.AddOrUpdateV(0.0, new VDefinition
{
Value = 1.0,
InInterpolation = VDefinition.KeyInterpolation.Constant,
OutInterpolation = VDefinition.KeyInterpolation.Constant,
});
curve.AddOrUpdateV(1.0, new VDefinition
{
Value = 5.0,
InInterpolation = VDefinition.KeyInterpolation.Constant,
OutInterpolation = VDefinition.KeyInterpolation.Constant,
});
curve.AddOrUpdateV(2.0, new VDefinition
{
Value = 9.0,
InInterpolation = VDefinition.KeyInterpolation.Constant,
OutInterpolation = VDefinition.KeyInterpolation.Constant,
});
Assert.Equal(1.0, curve.GetSampledValue(0.0), 4);
Assert.Equal(1.0, curve.GetSampledValue(0.5), 4);
Assert.Equal(5.0, curve.GetSampledValue(1.0), 4);
Assert.Equal(5.0, curve.GetSampledValue(1.5), 4);
Assert.Equal(9.0, curve.GetSampledValue(2.0), 4);
}
[Fact]
public void BeforeFirstKey_ReturnsFirstValue()
{
var curve = new Curve();
curve.AddOrUpdateV(1.0, new VDefinition
{
Value = 42.0,
InInterpolation = VDefinition.KeyInterpolation.Linear,
OutInterpolation = VDefinition.KeyInterpolation.Linear,
});
Assert.Equal(42.0, curve.GetSampledValue(0.0), 4);
Assert.Equal(42.0, curve.GetSampledValue(-10.0), 4);
}
[Fact]
public void AfterLastKey_ReturnsLastValue()
{
var curve = new Curve();
curve.AddOrUpdateV(0.0, new VDefinition
{
Value = 3.0,
InInterpolation = VDefinition.KeyInterpolation.Linear,
OutInterpolation = VDefinition.KeyInterpolation.Linear,
});
curve.AddOrUpdateV(1.0, new VDefinition
{
Value = 7.0,
InInterpolation = VDefinition.KeyInterpolation.Linear,
OutInterpolation = VDefinition.KeyInterpolation.Linear,
});
Assert.Equal(7.0, curve.GetSampledValue(2.0), 4);
Assert.Equal(7.0, curve.GetSampledValue(100.0), 4);
}
[Fact]
public void SingleKey_ReturnsThatValueEverywhere()
{
var curve = new Curve();
curve.AddOrUpdateV(5.0, new VDefinition
{
Value = 42.0,
InInterpolation = VDefinition.KeyInterpolation.Smooth,
OutInterpolation = VDefinition.KeyInterpolation.Smooth,
});
Assert.Equal(42.0, curve.GetSampledValue(0.0), 4);
Assert.Equal(42.0, curve.GetSampledValue(5.0), 4);
Assert.Equal(42.0, curve.GetSampledValue(10.0), 4);
}
[Fact]
public void EmptyCurve_ReturnsZero()
{
var curve = new Curve();
Assert.Equal(0.0, curve.GetSampledValue(0.0));
Assert.Equal(0.0, curve.GetSampledValue(1.0));
}
[Fact]
public void EqualEndpoints_WithUpwardTangents_ProducesOvershoot()
{
// Two keys at same value with tangents both pointing upward —
// spline interpolation should overshoot above 5.0 (intentional behavior).
var curve = new Curve();
curve.AddOrUpdateV(0.0, new VDefinition
{
Value = 5.0,
InInterpolation = VDefinition.KeyInterpolation.Tangent,
OutInterpolation = VDefinition.KeyInterpolation.Tangent,
InTangentAngle = 0.0,
OutTangentAngle = 0.7854, // ~45 degrees up
});
curve.AddOrUpdateV(1.0, new VDefinition
{
Value = 5.0,
InInterpolation = VDefinition.KeyInterpolation.Tangent,
OutInterpolation = VDefinition.KeyInterpolation.Tangent,
InTangentAngle = Math.PI - 0.7854, // ~135 degrees = coming from above
OutTangentAngle = 0.0,
});
// Endpoints must be exactly 5.0
Assert.Equal(5.0, curve.GetSampledValue(0.0), 4);
Assert.Equal(5.0, curve.GetSampledValue(1.0), 4);
// Midpoint should be above 5.0 due to both tangents pushing upward
var mid = curve.GetSampledValue(0.5);
Assert.True(mid > 5.1, $"Expected overshoot above 5.0, got {mid}");
}
[Fact]
public void MixedInterpolation_ConstantThenLinear()
{
var curve = new Curve();
curve.AddOrUpdateV(0.0, new VDefinition
{
Value = 1.0,
InInterpolation = VDefinition.KeyInterpolation.Constant,
OutInterpolation = VDefinition.KeyInterpolation.Constant,
});
curve.AddOrUpdateV(1.0, new VDefinition
{
Value = 3.0,
InInterpolation = VDefinition.KeyInterpolation.Linear,
OutInterpolation = VDefinition.KeyInterpolation.Linear,
});
curve.AddOrUpdateV(2.0, new VDefinition
{
Value = 5.0,
InInterpolation = VDefinition.KeyInterpolation.Linear,
OutInterpolation = VDefinition.KeyInterpolation.Linear,
});
// First segment: constant hold
Assert.Equal(1.0, curve.GetSampledValue(0.5), 4);
// At key 1: snap to 3.0
Assert.Equal(3.0, curve.GetSampledValue(1.0), 4);
// Second segment: linear 3→5
Assert.Equal(4.0, curve.GetSampledValue(1.5), 4);
Assert.Equal(5.0, curve.GetSampledValue(2.0), 4);
}
}