I’ve been wondering why FMath::InterpSinInOut wasn’t working in my use case so I decided to explore some more and ended up writing a quick test program to play around with it to output valid results. Here are my findings:
Original Version:
template< class T >
static FORCEINLINE_DEBUGGABLE T InterpSinInOut(const T& A, const T& B, float Alpha)
{
return (Alpha < 0.5f) ?
InterpSinIn(A, B, Alpha * 2.f) * 0.5f :
InterpSinOut(A, B, Alpha * 2.f - 1.f) * 0.5f + 0.5f;
}
This gives output as to the following with Alpha increasing by 0.1 from 0.0 to 1.0 with different values of A and B:
A=0.0, B=1.0
0.0-1.0 :0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
------------------------------------------------------------------------
SinInOut:0.00, 0.02, 0.10, 0.21, 0.35, 0.50, 0.65, 0.79, 0.90, 0.98, 1.00,
A=0.5, B=1.0
0.5-1.0 :0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
------------------------------------------------------------------------
SinInOut:0.25, 0.26, 0.30, 0.35, 0.42, 0.75, 0.83, 0.90, 0.95, 0.99, 1.00,
A=0.25, B=0.75
.25-.75 :0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
------------------------------------------------------------------------
SinInOut:0.13, 0.14, 0.17, 0.23, 0.30, 0.63, 0.70, 0.77, 0.83, 0.86, 0.88,
As you can plainly see it reports invalid results if A is not 0.0 and/or if B is not 1.0.
I have derived two different fixes for this issue and before I submit a pull request to github for this issue I would like to know which version would be preferred.
Version1:
template< class T >
static T InterpSinInOutMod1(const T& A, const T& B, float Alpha)
{
return
(1.f - Alpha) * (InterpSinIn(A, B, Alpha)) +
Alpha * (InterpSinOut(A, B, Alpha));
}
Results:
A=0.0, B=1.0
0.0-1.0 :0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
------------------------------------------------------------------------
SinInOu1:0.00, 0.03, 0.10, 0.21, 0.35, 0.50, 0.65, 0.79, 0.90, 0.97, 1.00,
A=0.5, B=1.0
0.5-1.0 :0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
------------------------------------------------------------------------
SinInOu1:0.50, 0.51, 0.55, 0.61, 0.67, 0.75, 0.83, 0.89, 0.95, 0.99, 1.00,
A=0.25, B=0.75
.25-.75 :0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
------------------------------------------------------------------------
SinInOu1:0.25, 0.26, 0.30, 0.36, 0.42, 0.50, 0.58, 0.64, 0.70, 0.74, 0.75,
Version 2:
template< class T >
static T InterpSinInOutMod2(const T& A, const T& B, float Alpha)
{
return
0.5f * (InterpSinIn(A, B, Alpha)) +
0.5f * (InterpSinOut(A, B, Alpha));
}
Results:
A=0.0, B=1.0
0.0-1.0 :0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
------------------------------------------------------------------------
SinInOu2:0.00, 0.08, 0.18, 0.28, 0.39, 0.50, 0.61, 0.72, 0.82, 0.92, 1.00,
A=0.5, B=1.0
0.5-1.0 :0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
------------------------------------------------------------------------
SinInOu2:0.50, 0.54, 0.59, 0.64, 0.69, 0.75, 0.81, 0.86, 0.91, 0.96, 1.00,
A=0.25, B=0.75
.25-.75 :0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
------------------------------------------------------------------------
SinInOu2:0.25, 0.29, 0.34, 0.39, 0.44, 0.50, 0.56, 0.61, 0.66, 0.71, 0.75,
I, personally, prefer the first version based on the alpha value passed in rather than the mean calculation in the second version.