// 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 #include #include 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