#pragma once #include "creeper-qt/utility/animation/math.hh" #include "creeper-qt/utility/animation/state/accessor.hh" #include namespace creeper { template struct LinearState : public NormalAccessor { using ValueT = T; using Clock = std::chrono::steady_clock; using TimePoint = Clock::time_point; T value = animate::zero(); T target = animate::zero(); struct { double speed = 1.0; double epsilon = 1e-2; } config; struct { TimePoint last_timestamp; } details; auto set_target(T new_target) noexcept -> void { target = new_target; const auto current_time = Clock::now(); using namespace std::chrono_literals; const auto threshold = 16ms; const auto elapsed_time = current_time - details.last_timestamp; if (elapsed_time > threshold) { details.last_timestamp = current_time; } } auto update() noexcept -> bool { const auto now = Clock::now(); const auto duration = now - details.last_timestamp; const auto dt = std::chrono::duration(duration).count(); if (dt <= 0.0) { details.last_timestamp = now; return animate::magnitude(target - value) > config.epsilon; } const auto delta = target - value; const auto dist = animate::magnitude(delta); if (dist <= config.epsilon) { value = target; details.last_timestamp = now; return false; } const auto direction = animate::normalize(delta); const auto step = config.speed * dt; if (step >= dist) { value = target; } else { value += direction * step; } details.last_timestamp = now; return true; } }; }