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

227 lines
10 KiB
C#

using T3.Core.Animation;
using T3.Core.DataTypes;
using Xunit;
namespace Core.Tests;
public class TensionInterpolationTests
{
[Fact]
public void DefaultTension_BehavesLikeOriginal()
{
// Tension 1.0 (default) should produce the same result as the original interpolation
var curve = new Curve();
curve.AddOrUpdateV(0.0, new VDefinition
{
Value = 0.0,
InInterpolation = VDefinition.KeyInterpolation.Smooth,
OutInterpolation = VDefinition.KeyInterpolation.Smooth,
TensionIn = 1.0f,
TensionOut = 1.0f,
});
curve.AddOrUpdateV(1.0, new VDefinition
{
Value = 1.0,
InInterpolation = VDefinition.KeyInterpolation.Smooth,
OutInterpolation = VDefinition.KeyInterpolation.Smooth,
TensionIn = 1.0f,
TensionOut = 1.0f,
});
// Should match a curve without explicit tension settings
var curveNoTension = new Curve();
curveNoTension.AddOrUpdateV(0.0, new VDefinition
{
Value = 0.0,
InInterpolation = VDefinition.KeyInterpolation.Smooth,
OutInterpolation = VDefinition.KeyInterpolation.Smooth,
});
curveNoTension.AddOrUpdateV(1.0, new VDefinition
{
Value = 1.0,
InInterpolation = VDefinition.KeyInterpolation.Smooth,
OutInterpolation = VDefinition.KeyInterpolation.Smooth,
});
for (double u = 0.0; u <= 1.0; u += 0.1)
{
Assert.Equal(curveNoTension.GetSampledValue(u), curve.GetSampledValue(u), 10);
}
}
[Fact]
public void LowTension_ProducesSnapperCurve()
{
// Low tension (0.3) should produce a snappier transition — closer to linear
var curveSnap = new Curve();
curveSnap.AddOrUpdateV(0.0, new VDefinition
{
Value = 0.0,
InInterpolation = VDefinition.KeyInterpolation.Tangent,
OutInterpolation = VDefinition.KeyInterpolation.Tangent,
OutTangentAngle = Math.Atan(1.0), // 45 degrees, slope=1
TensionOut = 0.3f,
});
curveSnap.AddOrUpdateV(1.0, new VDefinition
{
Value = 1.0,
InInterpolation = VDefinition.KeyInterpolation.Tangent,
OutInterpolation = VDefinition.KeyInterpolation.Tangent,
InTangentAngle = Math.Atan(1.0),
TensionIn = 0.3f,
});
var curveNormal = new Curve();
curveNormal.AddOrUpdateV(0.0, new VDefinition
{
Value = 0.0,
InInterpolation = VDefinition.KeyInterpolation.Tangent,
OutInterpolation = VDefinition.KeyInterpolation.Tangent,
OutTangentAngle = Math.Atan(1.0),
TensionOut = 1.0f,
});
curveNormal.AddOrUpdateV(1.0, new VDefinition
{
Value = 1.0,
InInterpolation = VDefinition.KeyInterpolation.Tangent,
OutInterpolation = VDefinition.KeyInterpolation.Tangent,
InTangentAngle = Math.Atan(1.0),
TensionIn = 1.0f,
});
// At midpoint, low tension should be closer to linear (0.5) than high tension
var snapMid = curveSnap.GetSampledValue(0.5);
var normalMid = curveNormal.GetSampledValue(0.5);
// Low tension should produce a DIFFERENT curve than normal tension
var snapQuarter = curveSnap.GetSampledValue(0.25);
var normalQuarter = curveNormal.GetSampledValue(0.25);
Assert.True(Math.Abs(snapQuarter - normalQuarter) > 0.01,
$"Different tensions should produce different curves: snap={snapQuarter:F4}, normal={normalQuarter:F4}");
}
[Fact]
public void HighTension_ProducesMoreOvershoot()
{
// High tension (2.0) with tangents pointing up should produce overshoot
var curve = new Curve();
curve.AddOrUpdateV(0.0, new VDefinition
{
Value = 0.0,
InInterpolation = VDefinition.KeyInterpolation.Tangent,
OutInterpolation = VDefinition.KeyInterpolation.Tangent,
OutTangentAngle = 0.7854, // 45 degrees up
TensionOut = 2.0f,
});
curve.AddOrUpdateV(1.0, new VDefinition
{
Value = 0.0,
InInterpolation = VDefinition.KeyInterpolation.Tangent,
OutInterpolation = VDefinition.KeyInterpolation.Tangent,
InTangentAngle = Math.PI - 0.7854, // coming from above
TensionIn = 2.0f,
});
// With high tension and upward tangents, midpoint should overshoot significantly above 0
var mid = curve.GetSampledValue(0.5);
Assert.True(mid > 0.5, $"High tension should produce significant overshoot, got {mid}");
}
[Fact]
public void ZeroTension_ApproachesLinear()
{
// Near-zero tension should produce nearly linear interpolation regardless of angle
var curve = new Curve();
curve.AddOrUpdateV(0.0, new VDefinition
{
Value = 0.0,
InInterpolation = VDefinition.KeyInterpolation.Tangent,
OutInterpolation = VDefinition.KeyInterpolation.Tangent,
OutTangentAngle = 1.2, // steep angle
TensionOut = 0.05f,
});
curve.AddOrUpdateV(1.0, new VDefinition
{
Value = 1.0,
InInterpolation = VDefinition.KeyInterpolation.Tangent,
OutInterpolation = VDefinition.KeyInterpolation.Tangent,
InTangentAngle = 1.2,
TensionIn = 0.05f,
});
// With near-zero tension, tangent slopes contribute almost nothing →
// the Hermite basis is dominated by the position terms → approaches a smooth S-curve
// between the endpoints, NOT necessarily linear. Verify the curve stays within bounds.
for (double u = 0.0; u <= 1.0; u += 0.1)
{
var sampled = curve.GetSampledValue(u);
Assert.True(sampled >= -0.1 && sampled <= 1.1,
$"Near-zero tension should stay close to [0,1] range, got {sampled} at u={u}");
}
}
[Fact]
public void TensionSerializationRoundTrip()
{
var original = new VDefinition
{
Value = 1.0,
InInterpolation = VDefinition.KeyInterpolation.Tangent,
OutInterpolation = VDefinition.KeyInterpolation.Tangent,
Weighted = true,
TensionIn = 0.5f,
TensionOut = 2.0f,
};
var sb = new System.Text.StringBuilder();
using var sw = new System.IO.StringWriter(sb);
using var writer = new Newtonsoft.Json.JsonTextWriter(sw);
writer.WriteStartObject();
original.Write(writer);
writer.WriteEndObject();
writer.Flush();
var json = Newtonsoft.Json.Linq.JObject.Parse(sb.ToString());
var restored = new VDefinition();
restored.Read(json);
Assert.Equal(0.5f, restored.TensionIn);
Assert.Equal(2.0f, restored.TensionOut);
}
[Fact]
public void DefaultTension_OmittedFromJson()
{
var vDef = new VDefinition
{
Value = 1.0,
TensionIn = 1.0f,
TensionOut = 1.0f,
};
var sb = new System.Text.StringBuilder();
using var sw = new System.IO.StringWriter(sb);
using var writer = new Newtonsoft.Json.JsonTextWriter(sw);
writer.WriteStartObject();
vDef.Write(writer);
writer.WriteEndObject();
writer.Flush();
var json = Newtonsoft.Json.Linq.JObject.Parse(sb.ToString());
Assert.Null(json["TensionIn"]);
Assert.Null(json["TensionOut"]);
}
[Fact]
public void CloneCopiesTension()
{
var original = new VDefinition { TensionIn = 0.3f, TensionOut = 2.5f };
var clone = original.Clone();
Assert.Equal(0.3f, clone.TensionIn);
Assert.Equal(2.5f, clone.TensionOut);
}
}