13#ifndef MALENA_TWEENER_H
14#define MALENA_TWEENER_H
30 inline float lerp(
float a,
float b,
float t) {
return a + (b - a) * t; }
34 return { a.
x + (b.
x - a.
x) * t, a.
y + (b.
y - a.
y) * t };
39 auto ch = [t](std::uint8_t x, std::uint8_t y) -> std::uint8_t {
40 const float v =
static_cast<float>(x) + (
static_cast<float>(y) - x) * t;
41 return static_cast<std::uint8_t
>(std::clamp(v, 0.f, 255.f) + 0.5f);
43 return { ch(a.
r, b.
r), ch(a.
g, b.
g), ch(a.
b, b.
b), ch(a.
a, b.
a) };
72 : _from(from), _to(to), _value(from),
77 Tweener&
delay(
float seconds) { _delay = std::max(0.f, seconds);
return *
this; }
81 Tweener&
onUpdate(std::function<
void(
const T&)> cb) { _onUpdate = std::move(cb);
return *
this; }
82 Tweener&
onComplete(std::function<
void()> cb) { _onComplete = std::move(cb);
return *
this; }
89 if (dt < 0.f) dt = 0.f;
95 if (_delay > 0.f) { emit(_from);
return; }
101 if (_duration <= 0.f)
110 const float eased = _easing(phase());
114 if (!_loop && !_pingPong && _elapsed >= _duration) finish();
115 else if (_pingPong && !_loop && _elapsed >= 2.f * _duration) finish();
130 [[nodiscard]]
bool done()
const {
return _done; }
131 [[nodiscard]]
const T&
value()
const {
return _value; }
135 [[nodiscard]]
float phase()
const
137 if (_duration <= 0.f)
return 1.f;
138 float p = _elapsed / _duration;
141 float c = std::fmod(p, 2.f);
142 if (!_loop) c = std::min(p, 2.f);
143 return c <= 1.f ? c : 2.f - c;
145 if (_loop)
return std::fmod(p, 1.f);
146 return std::clamp(p, 0.f, 1.f);
149 void emit(
const T& v)
const {
if (_onUpdate) _onUpdate(v); }
154 if (_onComplete) _onComplete();
157 T _from{}, _to{}, _value{};
158 float _duration = 0.f;
160 float _elapsed = 0.f;
163 bool _pingPong =
false;
165 std::function<void(
const T&)> _onUpdate;
166 std::function<void()> _onComplete;
void update(float dt)
Advance by dt seconds. Safe to keep calling after completion (no-op).
Tweener & onUpdate(std::function< void(const T &)> cb)
Tweener & onComplete(std::function< void()> cb)
void complete()
Jump to the end immediately, firing onUpdate(end) + onComplete once.
Tweener & delay(float seconds)
Tweener & pingPong(bool on=true)
void stop()
Stop where it is — no onComplete. (Used by cancel paths.).
Tweener & easing(Easing::Fn fn)
Tweener(T from, T to, float duration, Easing::Fn easing=Easing::Linear)
Tweener & loop(bool on=true)
float lerp(float a, float b, float t)
Vector2< float > Vector2f
Easing curves for animation, as pure functions of normalized time.
static float Linear(float t)