e72e526c64
Adds three new demos to the Core section of primitives_gallery: - demo_tween: dropdown of all 16 Ease modes + animated plot showing the curve and a moving marker that traverses t=0..1 in a loop. - demo_bezier_editor: live editor with reset + ease-out / ease-in-out presets, current control points displayed, slider over t showing bezier_eval(curve, t). - demo_timeline: 2 tracks (hue, amp) with mixed eases, live progress bars showing track_value_at(current_time) updating each frame. Wires the three demos into k_demos[] in main.cpp and adds the new sources (plus the three function .cpp files) to CMakeLists.txt.
250 lines
8.6 KiB
C++
250 lines
8.6 KiB
C++
// Demos para los primitivos de animacion (issue 0031):
|
|
// - tween_curves
|
|
// - bezier_editor
|
|
// - timeline
|
|
|
|
#include "demos.h"
|
|
#include "demo.h"
|
|
|
|
#include "core/tween_curves.h"
|
|
#include "core/bezier_editor.h"
|
|
#include "core/timeline.h"
|
|
#include "core/tokens.h"
|
|
|
|
#include <imgui.h>
|
|
|
|
#include <cstdio>
|
|
#include <cmath>
|
|
|
|
namespace gallery {
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// demo_tween — dropdown + plot animado
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void demo_tween() {
|
|
using namespace fn_tokens;
|
|
using fn::tween::Ease;
|
|
|
|
demo_header("tween_curves", "v1.0.0",
|
|
"Funciones de easing (Penner): linear, quad, cubic, expo, elastic, "
|
|
"bounce con variantes in/out/inOut. Header-mostly: el compilador "
|
|
"inlinea cada curva en el sitio de llamada.");
|
|
|
|
section("Selector + plot");
|
|
|
|
static int ease_idx = (int)Ease::OutCubic;
|
|
static float anim_t = 0.0f;
|
|
anim_t += ImGui::GetIO().DeltaTime * 0.5f;
|
|
if (anim_t > 1.5f) anim_t = -0.25f; // hold un poco antes de reiniciar
|
|
|
|
// Build labels
|
|
const char* labels[fn::tween::ease_count];
|
|
for (int i = 0; i < fn::tween::ease_count; i++) {
|
|
labels[i] = fn::tween::name((Ease)i);
|
|
}
|
|
ImGui::SetNextItemWidth(220.0f);
|
|
ImGui::Combo("##tween_ease", &ease_idx, labels, fn::tween::ease_count);
|
|
|
|
Ease ease = (Ease)ease_idx;
|
|
float t_clamped = anim_t;
|
|
if (t_clamped < 0.0f) t_clamped = 0.0f;
|
|
if (t_clamped > 1.0f) t_clamped = 1.0f;
|
|
float v = fn::tween::apply(ease, t_clamped);
|
|
|
|
ImGui::SameLine();
|
|
ImGui::PushStyleColor(ImGuiCol_Text, colors::text_muted);
|
|
ImGui::Text(" t=%.2f f(t)=%.3f", t_clamped, v);
|
|
ImGui::PopStyleColor();
|
|
|
|
// Canvas plot
|
|
ImVec2 canvas_min = ImGui::GetCursorScreenPos();
|
|
ImVec2 canvas_size(360.0f, 220.0f);
|
|
ImVec2 canvas_max = ImVec2(canvas_min.x + canvas_size.x, canvas_min.y + canvas_size.y);
|
|
|
|
ImDrawList* dl = ImGui::GetWindowDrawList();
|
|
dl->AddRectFilled(canvas_min, canvas_max, ImGui::GetColorU32(colors::bg), radius::sm);
|
|
dl->AddRect(canvas_min, canvas_max, ImGui::GetColorU32(colors::border), radius::sm);
|
|
|
|
auto to_px = [&](float tx, float ty) {
|
|
// ty puede salir de [0,1] (elastic/bounce); damos algo de margen vertical.
|
|
return ImVec2(canvas_min.x + tx * canvas_size.x,
|
|
canvas_min.y + (1.0f - ty) * canvas_size.y);
|
|
};
|
|
|
|
// Grid 4x4
|
|
ImU32 grid = ImGui::GetColorU32(colors::border);
|
|
for (int i = 1; i < 4; i++) {
|
|
float fx = canvas_min.x + canvas_size.x * (float)i / 4.0f;
|
|
float fy = canvas_min.y + canvas_size.y * (float)i / 4.0f;
|
|
dl->AddLine(ImVec2(fx, canvas_min.y), ImVec2(fx, canvas_max.y), grid);
|
|
dl->AddLine(ImVec2(canvas_min.x, fy), ImVec2(canvas_max.x, fy), grid);
|
|
}
|
|
|
|
// Diagonal linear
|
|
dl->AddLine(to_px(0.0f, 0.0f), to_px(1.0f, 1.0f),
|
|
ImGui::GetColorU32(colors::text_dim), 1.0f);
|
|
|
|
// Curva
|
|
constexpr int N = 96;
|
|
ImVec2 prev = to_px(0.0f, fn::tween::apply(ease, 0.0f));
|
|
ImU32 col = ImGui::GetColorU32(colors::primary);
|
|
for (int i = 1; i <= N; i++) {
|
|
float x = (float)i / (float)N;
|
|
float y = fn::tween::apply(ease, x);
|
|
ImVec2 cur = to_px(x, y);
|
|
dl->AddLine(prev, cur, col, 2.0f);
|
|
prev = cur;
|
|
}
|
|
|
|
// Marker animado
|
|
ImVec2 m = to_px(t_clamped, v);
|
|
dl->AddCircleFilled(m, 5.0f, ImGui::GetColorU32(colors::primary_light));
|
|
dl->AddCircle(m, 6.0f, ImGui::GetColorU32(colors::text), 0, 1.5f);
|
|
|
|
// Avanzar cursor
|
|
ImGui::Dummy(canvas_size);
|
|
|
|
code_block(
|
|
"#include \"core/tween_curves.h\"\n\n"
|
|
"float k = fn::tween::apply(fn::tween::Ease::OutCubic, t);\n"
|
|
"// o named:\n"
|
|
"float k2 = fn::tween::out_cubic(t);"
|
|
);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// demo_bezier_editor — editor + plot evaluado
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void demo_bezier_editor() {
|
|
using namespace fn_tokens;
|
|
|
|
demo_header("bezier_editor", "v1.0.0",
|
|
"Editor visual de curva Bezier cubica (4 puntos). Para diseñar "
|
|
"easing curves custom. p1/p2 son draggable; p0/p3 fijos en (0,0)/(1,1).");
|
|
|
|
section("Editor");
|
|
|
|
static fn::BezierCurve curve; // identidad por defecto: ease lineal con handles desplazados
|
|
|
|
if (ImGui::Button("Reset##bz_reset")) {
|
|
curve = fn::BezierCurve{};
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Ease-out preset##bz_eo")) {
|
|
curve = {{0,0}, {0.0f, 0.0f}, {0.58f, 1.0f}, {1,1}};
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::Button("Ease-in-out preset##bz_eio")) {
|
|
curve = {{0,0}, {0.42f, 0.0f}, {0.58f, 1.0f}, {1,1}};
|
|
}
|
|
|
|
fn::bezier_editor("##bz_editor", curve, ImVec2(220, 220));
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_Text, colors::text_muted);
|
|
ImGui::Text("p0=(%.2f,%.2f) p1=(%.2f,%.2f) p2=(%.2f,%.2f) p3=(%.2f,%.2f)",
|
|
curve.p0.x, curve.p0.y, curve.p1.x, curve.p1.y,
|
|
curve.p2.x, curve.p2.y, curve.p3.x, curve.p3.y);
|
|
ImGui::PopStyleColor();
|
|
|
|
// Plot evaluation
|
|
section("bezier_eval(curve, t)");
|
|
static float t = 0.0f;
|
|
ImGui::SetNextItemWidth(360.0f);
|
|
ImGui::SliderFloat("t##bz_t", &t, 0.0f, 1.0f, "%.3f");
|
|
float y = fn::bezier_eval(curve, t);
|
|
ImGui::PushStyleColor(ImGuiCol_Text, colors::text_muted);
|
|
ImGui::Text("y(t=%.3f) = %.3f", t, y);
|
|
ImGui::PopStyleColor();
|
|
|
|
code_block(
|
|
"#include \"core/bezier_editor.h\"\n\n"
|
|
"static fn::BezierCurve curve;\n"
|
|
"if (fn::bezier_editor(\"##my\", curve, ImVec2(220, 220))) {\n"
|
|
" // user dragged a control point\n"
|
|
"}\n"
|
|
"float k = fn::bezier_eval(curve, t);"
|
|
);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// demo_timeline — 2 tracks + display
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void demo_timeline() {
|
|
using namespace fn_tokens;
|
|
using fn::tween::Ease;
|
|
|
|
demo_header("timeline", "v1.0.0",
|
|
"Timeline tipo DAW: tracks horizontales con keyframes draggable, "
|
|
"scrub con el ruler, play/pause/loop. track_value_at(t) interpola "
|
|
"aplicando la Ease de cada keyframe destino.");
|
|
|
|
static fn::TimelineState tl;
|
|
static bool inited = false;
|
|
if (!inited) {
|
|
tl.duration = 4.0f;
|
|
tl.playing = true;
|
|
tl.tracks.push_back({"hue", {
|
|
{0.0f, 0.0f, Ease::Linear},
|
|
{2.0f, 1.0f, Ease::OutCubic},
|
|
{4.0f, 0.0f, Ease::InOutCubic},
|
|
}});
|
|
tl.tracks.push_back({"amp", {
|
|
{0.0f, 0.2f, Ease::Linear},
|
|
{3.0f, 1.0f, Ease::OutElastic},
|
|
}});
|
|
inited = true;
|
|
}
|
|
|
|
// Update
|
|
fn::timeline_update(tl, ImGui::GetIO().DeltaTime);
|
|
|
|
// Display values
|
|
section("Live values");
|
|
float hue = fn::track_value_at(tl.tracks[0], tl.current_time);
|
|
float amp = fn::track_value_at(tl.tracks[1], tl.current_time);
|
|
|
|
ImGui::PushStyleColor(ImGuiCol_Text, colors::text);
|
|
ImGui::Text("t = %.3fs", tl.current_time);
|
|
ImGui::PopStyleColor();
|
|
|
|
auto draw_bar = [&](const char* name, float value, float vmin, float vmax) {
|
|
ImGui::PushStyleColor(ImGuiCol_Text, colors::text_muted);
|
|
ImGui::Text("%-4s", name);
|
|
ImGui::PopStyleColor();
|
|
ImGui::SameLine();
|
|
ImVec2 cmin = ImGui::GetCursorScreenPos();
|
|
ImVec2 csize = ImVec2(280.0f, 14.0f);
|
|
ImDrawList* dl = ImGui::GetWindowDrawList();
|
|
dl->AddRectFilled(cmin, ImVec2(cmin.x + csize.x, cmin.y + csize.y),
|
|
ImGui::GetColorU32(colors::surface_active), radius::sm);
|
|
float k = (value - vmin) / (vmax - vmin);
|
|
if (k < 0.0f) k = 0.0f;
|
|
if (k > 1.0f) k = 1.0f;
|
|
dl->AddRectFilled(cmin, ImVec2(cmin.x + csize.x * k, cmin.y + csize.y),
|
|
ImGui::GetColorU32(colors::primary), radius::sm);
|
|
ImGui::Dummy(csize);
|
|
ImGui::SameLine();
|
|
ImGui::Text("%.3f", value);
|
|
};
|
|
draw_bar("hue", hue, 0.0f, 1.0f);
|
|
draw_bar("amp", amp, 0.0f, 1.0f);
|
|
|
|
section("Widget");
|
|
fn::timeline_widget("##gallery_tl", tl, ImVec2(-1, 220));
|
|
|
|
code_block(
|
|
"#include \"core/timeline.h\"\n\n"
|
|
"static fn::TimelineState tl;\n"
|
|
"tl.tracks.push_back({\"hue\", {{0,0}, {2,1, fn::tween::Ease::OutCubic}, {4,0}}});\n"
|
|
"tl.duration = 4.0f; tl.playing = true;\n\n"
|
|
"fn::timeline_update(tl, ImGui::GetIO().DeltaTime);\n"
|
|
"float h = fn::track_value_at(tl.tracks[0], tl.current_time);\n"
|
|
"fn::timeline_widget(\"##tl\", tl);"
|
|
);
|
|
}
|
|
|
|
} // namespace gallery
|