Files
egutierrez 448765fa15 feat(core): add timeline — DAW-style keyframe widget
Timeline widget with:
- Header: play/pause + reset + duration drag + loop checkbox
- Ruler: 0.5s ticks, scrub via click+drag
- Tracks: horizontal rows with diamond-shaped draggable keyframes
- Playhead: vertical primary_light line + ruler triangle marker

State and types:
- Keyframe { time, value, ease }
- Track { name, vector<Keyframe> }
- TimelineState { tracks, current_time, duration, playing, loop }

Pure functions:
- track_value_at(track, t): interp between keys, ease applied via the
  destination keyframe (Maya/AfterEffects convention)
- timeline_update(state, dt): advance current_time, wrap or saturate

Render with fn_tokens for visual coherence with the rest of the design
system. Keys are sorted by time on every changed frame to keep order
consistent during drag.
2026-04-25 21:50:35 +02:00

68 lines
2.1 KiB
C++

#pragma once
// timeline — widget tipo DAW: tracks horizontales con keyframes interpolados,
// scrub y play/pause. Sirve para animar valores escalares (uniforms shader,
// parametros UI, etc) a lo largo del tiempo.
//
// Estado puro (TimelineState) + funciones puras de interpolacion
// (track_value_at) + render impuro (timeline_widget).
//
// Uso:
// static fn::TimelineState tl;
// tl.tracks.push_back({"hue", {{0,0}, {2,1}, {4,0}}});
// tl.duration = 4.0f;
//
// fn::timeline_update(tl, dt);
// float h = fn::track_value_at(tl.tracks[0], tl.current_time);
// fn::timeline_widget("##tl", tl);
#include "core/tween_curves.h"
#include <imgui.h>
#include <string>
#include <vector>
namespace fn {
struct Keyframe {
float time;
float value;
tween::Ease ease = tween::Ease::Linear;
};
struct Track {
std::string name;
std::vector<Keyframe> keys;
};
struct TimelineState {
std::vector<Track> tracks;
float current_time = 0.0f;
float duration = 5.0f;
bool playing = false;
bool loop = true;
};
// --- Pure -------------------------------------------------------------------
// Interpola el valor de `track` en el tiempo `t`. Asume keys ordenadas por
// time. Si `t` cae antes del primer keyframe devuelve el value del primero;
// si cae despues del ultimo, devuelve el value del ultimo. Entre keyframes
// usa el ease de la SEGUNDA key (la "curva entrante" hasta esa key).
float track_value_at(const Track& track, float t);
// --- Update -----------------------------------------------------------------
// Avanza current_time si playing. Si loop=true hace wrap; si no, satura en
// duration y pone playing=false al llegar.
void timeline_update(TimelineState& s, float dt);
// --- Render -----------------------------------------------------------------
// Widget completo: cabecera con play/pause + tiempo, ruler con scrub, y un
// panel por track con keyframes draggable. Devuelve true si el usuario hizo
// algun cambio (drag de keyframe, scrub, play/pause).
bool timeline_widget(const char* id, TimelineState& s, ImVec2 size = ImVec2(-1, 200));
} // namespace fn