#pragma once #include "Easing.h" #include "TimeWarp.h" namespace EasingFunc { // typedef float (*eFunc)(float); // тип easing-функции, как у Вас // Статические данные для обёртки static const TimeWarp* g_warp = nullptr; static eFunc g_baseEasing = nullptr; // Обёртка с модификатором времени (вызывается Tween'ом) inline float _easingWithWarp(float t) { if (!g_warp || !g_baseEasing) return t; float warpedT = g_warp->apply(t); return g_baseEasing(warpedT); } // Установить текущий модификатор времени inline eFunc withTimeWarp(eFunc easing, const TimeWarp& warp) { g_warp = &warp; g_baseEasing = easing; return _easingWithWarp; } }; // namespace EasingFunc class Tween { private: static inline Tween* head = nullptr; static inline Tween* last = nullptr; static inline uint32_t frameRateTimer; Tween* next; public: Tween(uint16_t fps = 30) { setFps(fps); if (Tween::head == nullptr) { Tween::head = this; } if (last != nullptr) { last->next = this; } last = this; } static void tick() { Tween* current = Tween::head; while (current != nullptr) { current->update(); current = current->next; } } /////// private: uint16_t frameTime; float dt; uint32_t oldMillis; float from; float to; float duration; EasingFunc::eFunc easing = nullptr; float progress; bool isPlayingF; bool triggerLastTick = false; // Флаг последнего тика public: float current; void dtTick() { uint32_t loopStartTime = millis(); dt = (loopStartTime - oldMillis) / 1000.0; oldMillis = loopStartTime; } void update() { if (millis() - frameRateTimer > frameTime) { uint32_t now = millis(); dt = (now - oldMillis) / 1000.0; oldMillis = now; if (!isPlayingF && triggerLastTick) { triggerLastTick = false; current = to; return; } if (!isPlayingF || easing == nullptr) return; progress = constrain(progress + dt, 0, duration); float normProgress = constrain(progress / duration, 0, 1); current = Tween::lerp(from, to, easing(normProgress)); if (progress >= duration) { current = to; triggerLastTick = true; isPlayingF = false; } frameRateTimer = millis(); } } float getProgress() { return constrain(progress / duration, 0, 1); } void start(float from, float to, uint16_t duration, EasingFunc::eFunc easing) { current = from; if (from == to) { current = to; isPlayingF = false; triggerLastTick = true; return; } this->from = from; this->to = to; this->duration = duration / 1000.0; this->easing = easing; progress = 0; oldMillis = millis(); isPlayingF = true; triggerLastTick = false; } void resetToStart() { progress = 0; } void stop() { isPlayingF = false; current = to; triggerLastTick = true; } bool isPlaying() const { return isPlayingF || triggerLastTick; // Учитываем последний тик } void setFps(uint16_t fps){ frameTime = 1000 / fps; } float getFrom() const { return from; } float getTo() const { return to; } static float integrateEasing(EasingFunc::eFunc easing, int steps = 100) { float sum = 0; for (int i = 0; i <= steps; ++i) { float t = i / (float)steps; sum += easing(t); } return sum / (steps + 1); // Среднее значение функции } static float lerp(float a, float b, float t) { return a + (b - a) * t; } private: // int clamp(int value, int a, int b) { // if (a > b) { // a ^= b; // b ^= a; // a ^= b; // } // if (value < a) return a; // else if (value > b) return b; // else return value; // } };