Files
tech5/source/shared/idlib/math/interpolate.h
T
2026-08-08 19:30:03 -07:00

328 lines
13 KiB
C++

#pragma once
#include "extrapolate.h"
#include <cmath>
enum XUI_INTERPOLATE : int {
XUI_INTERPOLATE_LINEAR = 0,
XUI_INTERPOLATE_NONE = 1,
XUI_INTERPOLATE_EASE = 2
};
class idInterpolateParms {
public:
int accelTimeMs;
int decelTimeMs;
int durationMs;
};
template<class T>
class idInterpolate {
public:
float startTime;
float duration;
T startValue;
T endValue;
mutable float currentTime;
mutable T currentValue;
idInterpolate()
: startTime(0.0f), duration(0.0f), startValue(T()), endValue(T()),
currentTime(-1.0f), currentValue(startValue) {
}
void Init(const float newStartTime, const float newDuration,
const T& newStartValue, const T& newEndValue) {
startTime = newStartTime;
duration = newDuration;
startValue = newStartValue;
endValue = newEndValue;
currentTime = -1.0f;
currentValue = startValue;
}
T GetCurrentValue(const float time) const {
if (time == currentTime) return currentValue;
currentTime = time;
const float delta = time - startTime;
if ((duration >= 0.0f && delta <= 0.0f)
|| (duration < 0.0f && delta >= 0.0f)) {
currentValue = startValue;
} else if ((duration >= 0.0f && delta >= duration)
|| (duration < 0.0f && delta <= duration)) {
currentValue = endValue;
} else {
currentValue = startValue + (endValue - startValue) * (delta / duration);
}
return currentValue;
}
T GetCurrentValueEaseOut(const float time) const {
const float delta = time - startTime;
if (duration <= 0.0f || delta <= 0.0f) return startValue;
if (delta >= duration) return endValue;
const float fraction = std::sin((delta / duration) * 1.57079632679489661923f);
currentTime = time;
currentValue = startValue + (endValue - startValue) * fraction;
return currentValue;
}
bool IsDone(const float time) const {
return duration >= 0.0f ? time >= startTime + duration
: time <= startTime + duration;
}
void SetStartTime(const float value) { startTime = value; currentTime = -1.0f; }
void SetDuration(const float value) { duration = value; currentTime = -1.0f; }
void SetStartValue(const T& value) { startValue = value; currentTime = -1.0f; }
void SetEndValue(const T& value) { endValue = value; currentTime = -1.0f; }
float GetStartTime() const { return startTime; }
float GetEndTime() const { return startTime + duration; }
float GetDuration() const { return duration; }
const T& GetStartValue() const { return startValue; }
const T& GetEndValue() const { return endValue; }
};
template<class T>
class idInterpolateAccelDecelLinear {
public:
float startTime;
float accelTime;
float linearTime;
float decelTime;
T startValue;
T endValue;
mutable idExtrapolate<T> extrapolate;
idInterpolateAccelDecelLinear()
: startTime(0.0f), accelTime(0.0f), linearTime(0.0f), decelTime(0.0f),
startValue(T()), endValue(T()), extrapolate() {
}
void Init(const float newStartTime, float newAccelTime, float newDecelTime,
const float duration, const T& newStartValue, const T& newEndValue) {
startTime = newStartTime;
accelTime = newAccelTime;
decelTime = newDecelTime;
startValue = newStartValue;
endValue = newEndValue;
if (duration <= 0.0f) {
linearTime = 0.0f;
extrapolate.Init(startTime, 0.0f, startValue, T(), T(), EXTRAPOLATION_NONE);
return;
}
if (accelTime + decelTime > duration) {
const float sum = accelTime + decelTime;
accelTime = sum > 0.0f ? accelTime * duration / sum : 0.0f;
decelTime = duration - accelTime;
}
linearTime = duration - accelTime - decelTime;
const float effectiveTime = 0.5f * (accelTime + decelTime) + linearTime;
const T phaseSpeed = (endValue - startValue) * (1000.0f / effectiveTime);
extrapolation_t phase = EXTRAPOLATION_ACCELLINEAR;
float phaseDuration = accelTime;
if (accelTime == 0.0f) {
phase = linearTime == 0.0f
? EXTRAPOLATION_DECELLINEAR : EXTRAPOLATION_LINEAR;
phaseDuration = linearTime == 0.0f ? decelTime : linearTime;
}
extrapolate.Init(startTime, phaseDuration, startValue, T(), phaseSpeed, phase);
}
T GetCurrentValue(const float time) const {
SetPhase(time);
return extrapolate.GetCurrentValue(time);
}
T GetCurrentSpeed(const float time) const {
SetPhase(time);
return extrapolate.GetCurrentSpeed(time);
}
bool IsDone(const float time) const {
return time >= startTime + accelTime + linearTime + decelTime;
}
float GetStartTime() const { return startTime; }
float GetEndTime() const { return startTime + accelTime + linearTime + decelTime; }
float GetDuration() const { return accelTime + linearTime + decelTime; }
void SetStartTime(const float value) { startTime = value; extrapolate.currentTime = -1.0f; }
void SetStartValue(const T& value) { startValue = value; extrapolate.currentTime = -1.0f; }
void SetEndValue(const T& value) { endValue = value; extrapolate.currentTime = -1.0f; }
private:
void SetPhase(const float time) const {
const float elapsed = time - startTime;
const T zero = T();
const T phaseSpeed = extrapolate.speed;
if (elapsed < accelTime) {
if ((static_cast<int>(extrapolate.extrapolationType) & ~EXTRAPOLATION_NOSTOP)
!= EXTRAPOLATION_ACCELLINEAR) {
extrapolate.Init(startTime, accelTime, startValue, zero,
phaseSpeed, EXTRAPOLATION_ACCELLINEAR);
}
} else if (elapsed < accelTime + linearTime) {
if ((static_cast<int>(extrapolate.extrapolationType) & ~EXTRAPOLATION_NOSTOP)
!= EXTRAPOLATION_LINEAR) {
const T phaseStart = startValue
+ phaseSpeed * (accelTime * 0.0005f);
extrapolate.Init(startTime + accelTime, linearTime, phaseStart,
zero, phaseSpeed, EXTRAPOLATION_LINEAR);
}
} else if ((static_cast<int>(extrapolate.extrapolationType)
& ~EXTRAPOLATION_NOSTOP) != EXTRAPOLATION_DECELLINEAR) {
const T phaseStart = endValue
- phaseSpeed * (decelTime * 0.0005f);
extrapolate.Init(startTime + accelTime + linearTime, decelTime,
phaseStart, zero, phaseSpeed, EXTRAPOLATION_DECELLINEAR);
}
}
};
template<class T>
class idInterpolateAccelDecelSine {
public:
float startTime;
float accelTime;
float linearTime;
float decelTime;
T startValue;
T endValue;
mutable idExtrapolate<T> extrapolate;
idInterpolateAccelDecelSine()
: startTime(0.0f), accelTime(0.0f), linearTime(0.0f), decelTime(0.0f),
startValue(T()), endValue(T()), extrapolate() {
}
void Init(const float newStartTime, float newAccelTime, float newDecelTime,
const float duration, const T& newStartValue, const T& newEndValue) {
startTime = newStartTime;
accelTime = newAccelTime;
decelTime = newDecelTime;
startValue = newStartValue;
endValue = newEndValue;
if (duration <= 0.0f) {
linearTime = 0.0f;
extrapolate.Init(startTime, 0.0f, startValue, T(), T(), EXTRAPOLATION_NONE);
return;
}
if (accelTime + decelTime > duration) {
const float sum = accelTime + decelTime;
accelTime = sum > 0.0f ? accelTime * duration / sum : 0.0f;
decelTime = duration - accelTime;
}
linearTime = duration - accelTime - decelTime;
const float effectiveTime = 0.70710678118654752440f
* (accelTime + decelTime) + linearTime;
const T phaseSpeed = (endValue - startValue) * (1000.0f / effectiveTime);
extrapolation_t phase = EXTRAPOLATION_ACCELSINE;
float phaseDuration = accelTime;
if (accelTime == 0.0f) {
phase = linearTime == 0.0f
? EXTRAPOLATION_DECELSINE : EXTRAPOLATION_LINEAR;
phaseDuration = linearTime == 0.0f ? decelTime : linearTime;
}
extrapolate.Init(startTime, phaseDuration, startValue, T(), phaseSpeed, phase);
}
T GetCurrentValue(const float time) const { SetPhase(time); return extrapolate.GetCurrentValue(time); }
T GetCurrentSpeed(const float time) const { SetPhase(time); return extrapolate.GetCurrentSpeed(time); }
bool IsDone(const float time) const { return time >= startTime + accelTime + linearTime + decelTime; }
private:
void SetPhase(const float time) const {
constexpr float SQRT_HALF = 0.70710678118654752440f;
const float elapsed = time - startTime;
const T zero = T();
const T phaseSpeed = extrapolate.speed;
if (elapsed < accelTime) {
if ((static_cast<int>(extrapolate.extrapolationType) & ~EXTRAPOLATION_NOSTOP)
!= EXTRAPOLATION_ACCELSINE) {
extrapolate.Init(startTime, accelTime, startValue, zero,
phaseSpeed, EXTRAPOLATION_ACCELSINE);
}
} else if (elapsed < accelTime + linearTime) {
if ((static_cast<int>(extrapolate.extrapolationType) & ~EXTRAPOLATION_NOSTOP)
!= EXTRAPOLATION_LINEAR) {
const T phaseStart = startValue
+ phaseSpeed * (accelTime * SQRT_HALF * 0.001f);
extrapolate.Init(startTime + accelTime, linearTime, phaseStart,
zero, phaseSpeed, EXTRAPOLATION_LINEAR);
}
} else if ((static_cast<int>(extrapolate.extrapolationType)
& ~EXTRAPOLATION_NOSTOP) != EXTRAPOLATION_DECELSINE) {
const T phaseStart = endValue
- phaseSpeed * (decelTime * SQRT_HALF * 0.001f);
extrapolate.Init(startTime + accelTime + linearTime, decelTime,
phaseStart, zero, phaseSpeed, EXTRAPOLATION_DECELSINE);
}
}
};
template<class T>
class idInterpolateAccelLinearEx {
public:
float startTime;
float duration;
float startSpeed;
float endSpeed;
T startValue;
T endValue;
idExtrapolate<T> extrapolate;
idInterpolateAccelLinearEx()
: startTime(0.0f), duration(0.0f), startSpeed(0.0f), endSpeed(0.0f),
startValue(T()), endValue(T()), extrapolate() {
}
void InitDuration(const float newStartTime, const float newStartSpeed,
const float newDuration, const T& newStartValue, const T& newEndValue) {
startTime = newStartTime;
startSpeed = newStartSpeed;
duration = newDuration;
startValue = newStartValue;
endValue = newEndValue;
endSpeed = duration > 0.0f
? -2.0f * ((duration * 0.001f * startSpeed + startValue - endValue)
/ (duration * 0.001f)) + startSpeed
: startSpeed;
extrapolate.Init(startTime, duration, startValue, T() + startSpeed,
T() + (endSpeed - startSpeed), EXTRAPOLATION_ACCELLINEAR);
}
float InitEndSpeed(const float newStartTime, const float newStartSpeed,
const float newEndSpeed, const T& newStartValue, const T& newEndValue) {
startTime = newStartTime;
startSpeed = newStartSpeed;
endSpeed = newEndSpeed;
startValue = newStartValue;
endValue = newEndValue;
const float denominator = 2.0f * startSpeed + (endSpeed - startSpeed);
duration = denominator != 0.0f
? static_cast<float>((endValue - startValue) / denominator) * 2000.0f
: 0.0f;
extrapolate.Init(startTime, duration, startValue, T() + startSpeed,
T() + (endSpeed - startSpeed), EXTRAPOLATION_ACCELLINEAR);
return duration;
}
T GetCurrentValue(const float time) const {
if (time < startTime + duration) return extrapolate.GetCurrentValue(time);
if (startSpeed == endSpeed) return endValue;
return endValue + (T() + endSpeed) * ((time - startTime - duration) * 0.001f);
}
};
static_assert(sizeof(idInterpolate<float>) == 24,
"Recovered idInterpolate<float> ABI changed");
static_assert(sizeof(idInterpolate<idVec3>) == 48,
"Recovered idInterpolate<idVec3> ABI changed");
static_assert(sizeof(idInterpolate<idQuat>) == 60,
"Recovered idInterpolate<idQuat> ABI changed");
static_assert(sizeof(idInterpolateAccelDecelLinear<float>) == 56,
"Recovered idInterpolateAccelDecelLinear<float> ABI changed");
static_assert(sizeof(idInterpolateAccelDecelLinear<idVec3>) == 104,
"Recovered idInterpolateAccelDecelLinear<idVec3> ABI changed");
static_assert(sizeof(idInterpolateAccelDecelLinear<idQuat>) == 128,
"Recovered idInterpolateAccelDecelLinear<idQuat> ABI changed");
static_assert(sizeof(idInterpolateAccelLinearEx<float>) == 56,
"Recovered idInterpolateAccelLinearEx<float> ABI changed");