diff --git a/cpp/apps/primitives_gallery/CMakeLists.txt b/cpp/apps/primitives_gallery/CMakeLists.txt index 6812bf9f..ef41c929 100644 --- a/cpp/apps/primitives_gallery/CMakeLists.txt +++ b/cpp/apps/primitives_gallery/CMakeLists.txt @@ -8,6 +8,11 @@ add_imgui_app(primitives_gallery demos_text_editor.cpp demos_gl_texture.cpp demos_extras.cpp + # animation primitives (issue 0031) + demos_animation.cpp + ${CMAKE_SOURCE_DIR}/functions/core/tween_curves.cpp + ${CMAKE_SOURCE_DIR}/functions/core/bezier_editor.cpp + ${CMAKE_SOURCE_DIR}/functions/core/timeline.cpp # text_editor + file_watcher (issue 0025) ${CMAKE_SOURCE_DIR}/functions/core/text_editor.cpp ${CMAKE_SOURCE_DIR}/functions/core/file_watcher.cpp diff --git a/cpp/apps/primitives_gallery/demos.h b/cpp/apps/primitives_gallery/demos.h index b7f22c32..4d7a4e82 100644 --- a/cpp/apps/primitives_gallery/demos.h +++ b/cpp/apps/primitives_gallery/demos.h @@ -21,6 +21,9 @@ void demo_dashboard_panel(); void demo_text_editor(); // wave 1, issue 0025 void demo_file_watcher(); // wave 1, issue 0025 void demo_process_runner(); +void demo_tween(); // issue 0031 +void demo_bezier_editor(); // issue 0031 +void demo_timeline(); // issue 0031 // --- Viz --- void demo_bar_chart(); diff --git a/cpp/apps/primitives_gallery/demos_animation.cpp b/cpp/apps/primitives_gallery/demos_animation.cpp new file mode 100644 index 00000000..877324a0 --- /dev/null +++ b/cpp/apps/primitives_gallery/demos_animation.cpp @@ -0,0 +1,249 @@ +// 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 diff --git a/cpp/apps/primitives_gallery/main.cpp b/cpp/apps/primitives_gallery/main.cpp index 269bfe6b..d99f7399 100644 --- a/cpp/apps/primitives_gallery/main.cpp +++ b/cpp/apps/primitives_gallery/main.cpp @@ -49,6 +49,9 @@ static const DemoEntry k_demos[] = { {"text_editor", "text_editor", "Core", &gallery::demo_text_editor}, // wave 1 {"file_watcher", "file_watcher", "Core", &gallery::demo_file_watcher}, // wave 1 {"process_runner", "process_runner", "Core", &gallery::demo_process_runner}, + {"tween", "tween_curves", "Core", &gallery::demo_tween}, + {"bezier_editor", "bezier_editor", "Core", &gallery::demo_bezier_editor}, + {"timeline", "timeline", "Core", &gallery::demo_timeline}, // Viz {"bar_chart", "bar_chart", "Viz", &gallery::demo_bar_chart}, {"pie_chart", "pie_chart", "Viz", &gallery::demo_pie_chart},