chore: sync from fn-registry agent
This commit is contained in:
@@ -0,0 +1,22 @@
|
|||||||
|
add_imgui_app(chart_demo
|
||||||
|
main.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/functions/viz/line_plot.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/functions/viz/scatter_plot.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/functions/viz/bar_chart.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/functions/viz/heatmap.cpp
|
||||||
|
# fps_overlay vive en fn_framework
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- E2E tests (opt-in via -DFN_BUILD_TESTS=ON) ---
|
||||||
|
if(FN_BUILD_TESTS)
|
||||||
|
add_imgui_app(chart_demo_tests
|
||||||
|
main.cpp
|
||||||
|
tests/chart_demo_tests.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/functions/viz/line_plot.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/functions/viz/scatter_plot.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/functions/viz/bar_chart.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/functions/viz/heatmap.cpp
|
||||||
|
)
|
||||||
|
# Excludes int main() from main.cpp so the test target provides its own.
|
||||||
|
target_compile_definitions(chart_demo_tests PRIVATE FN_TEST_BUILD)
|
||||||
|
endif()
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
---
|
||||||
|
name: chart_demo
|
||||||
|
lang: cpp
|
||||||
|
domain: viz
|
||||||
|
description: "Demo ImGui de primitivos viz del registry: line_plot, scatter_plot, bar_chart, heatmap. Cada chart en su propia tab del TabBar. Usado como showcase y como build gate de las funciones viz/."
|
||||||
|
tags: [imgui, demo, charts, viz, showcase]
|
||||||
|
uses_functions:
|
||||||
|
- line_plot_cpp_viz
|
||||||
|
- scatter_plot_cpp_viz
|
||||||
|
- bar_chart_cpp_viz
|
||||||
|
- heatmap_cpp_viz
|
||||||
|
# logger, app_menubar viven en fn_framework — no se listan aqui
|
||||||
|
uses_types: []
|
||||||
|
framework: "imgui"
|
||||||
|
entry_point: "main.cpp"
|
||||||
|
dir_path: "cpp/apps/chart_demo"
|
||||||
|
repo_url: ""
|
||||||
|
---
|
||||||
|
|
||||||
|
## Que hace
|
||||||
|
|
||||||
|
App de una sola ventana con cuatro tabs (Line / Scatter / Bar / Heatmap) que
|
||||||
|
renderiza datos sinteticos para mostrar el aspecto y la API de los primitivos
|
||||||
|
viz del registry. Sirve como:
|
||||||
|
|
||||||
|
- **Showcase visual** de las funciones viz existentes — al añadir una nueva
|
||||||
|
primitiva, anadir su tab aqui es la forma natural de probar el binding.
|
||||||
|
- **Build gate**: si una de las funciones rompe API, esta app deja de
|
||||||
|
compilar y lo cazamos sin tener que tocar `registry_dashboard` o
|
||||||
|
`graph_explorer`.
|
||||||
|
|
||||||
|
## Estructura
|
||||||
|
|
||||||
|
`main.cpp` (~93 lineas):
|
||||||
|
|
||||||
|
- `init_data()` — genera arrays sinteticos una vez (estado modulo).
|
||||||
|
- `render()` — DockSpaceOverViewport + TabBar con 4 tabs, cada una invoca
|
||||||
|
un primitivo del registry.
|
||||||
|
- `main()` → `fn::run_app(...)` con AppConfig estandar (titulo, tamaño,
|
||||||
|
about, log).
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Linux
|
||||||
|
cd cpp && cmake -B build/linux -S . && cmake --build build/linux --target chart_demo
|
||||||
|
|
||||||
|
# Windows (cross-compile)
|
||||||
|
cd cpp && cmake -B build/windows -S . -DCMAKE_TOOLCHAIN_FILE=toolchains/mingw-w64.cmake \
|
||||||
|
&& cmake --build build/windows --target chart_demo
|
||||||
|
```
|
||||||
|
|
||||||
|
## Decisiones
|
||||||
|
|
||||||
|
- `viewports = true` (default de `fn::run_app`): las ventanas se pueden
|
||||||
|
arrastrar fuera del main window.
|
||||||
|
- `init_gl_loader = false`: solo usa ImGui/ImPlot, sin gl* directo.
|
||||||
|
- Sin persistencia propia (no abre BD).
|
||||||
|
- `log: file_path = "chart_demo.log"` con nivel Debug — el `init_data`
|
||||||
|
emite info+debug para verificar que el logger funciona.
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
#include "app_base.h"
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "implot.h"
|
||||||
|
|
||||||
|
#include "viz/line_plot.h"
|
||||||
|
#include "viz/scatter_plot.h"
|
||||||
|
#include "viz/bar_chart.h"
|
||||||
|
#include "viz/heatmap.h"
|
||||||
|
#include "core/app_menubar.h"
|
||||||
|
#include "core/logger.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// Generate sample data
|
||||||
|
static constexpr int N = 500;
|
||||||
|
static float xs[N], ys_sin[N], ys_cos[N];
|
||||||
|
static float scatter_x[200], scatter_y[200];
|
||||||
|
static const char* bar_labels[] = {"Go", "Python", "Bash", "TypeScript", "C++"};
|
||||||
|
static float bar_values[] = {201.0f, 202.0f, 38.0f, 80.0f, 5.0f};
|
||||||
|
static float heat_data[10 * 10];
|
||||||
|
|
||||||
|
static bool data_initialized = false;
|
||||||
|
|
||||||
|
static void init_data() {
|
||||||
|
if (data_initialized) return;
|
||||||
|
fn_log::log_info("init_data: generando %d puntos sin/cos, 200 scatter, 10x10 heatmap", N);
|
||||||
|
for (int i = 0; i < N; i++) {
|
||||||
|
xs[i] = static_cast<float>(i) * 0.02f;
|
||||||
|
ys_sin[i] = sinf(xs[i]);
|
||||||
|
ys_cos[i] = cosf(xs[i]);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 200; i++) {
|
||||||
|
scatter_x[i] = static_cast<float>(rand()) / RAND_MAX * 10.0f;
|
||||||
|
scatter_y[i] = scatter_x[i] * 0.5f + (static_cast<float>(rand()) / RAND_MAX - 0.5f) * 3.0f;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
int r = i / 10, c = i % 10;
|
||||||
|
heat_data[i] = sinf(r * 0.5f) * cosf(c * 0.5f);
|
||||||
|
}
|
||||||
|
data_initialized = true;
|
||||||
|
fn_log::log_debug("init_data: ok");
|
||||||
|
}
|
||||||
|
|
||||||
|
void render() {
|
||||||
|
init_data();
|
||||||
|
|
||||||
|
// MainMenuBar (solo Settings — chart_demo no tiene paneles toggleables)
|
||||||
|
fn_ui::app_menubar(nullptr, 0, nullptr);
|
||||||
|
|
||||||
|
// Full-window dockspace
|
||||||
|
ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport());
|
||||||
|
|
||||||
|
if (ImGui::Begin("fn_registry — Chart Demo")) {
|
||||||
|
if (ImGui::BeginTabBar("##charts")) {
|
||||||
|
if (ImGui::BeginTabItem("Line Plot")) {
|
||||||
|
ImGui::Text("sin(x) — %d points", N);
|
||||||
|
line_plot("Sine Wave", xs, ys_sin, N);
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
if (ImGui::BeginTabItem("Scatter Plot")) {
|
||||||
|
ImGui::Text("y = 0.5x + noise — 200 points");
|
||||||
|
scatter_plot("Scatter Data", scatter_x, scatter_y, 200);
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
if (ImGui::BeginTabItem("Bar Chart")) {
|
||||||
|
ImGui::Text("Functions per language in fn_registry");
|
||||||
|
bar_chart("Registry Languages", bar_labels, bar_values, 5);
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
if (ImGui::BeginTabItem("Heatmap")) {
|
||||||
|
ImGui::Text("sin(r) * cos(c) — 10x10 matrix");
|
||||||
|
heatmap("Correlation Matrix", heat_data, 10, 10, -1.0f, 1.0f);
|
||||||
|
ImGui::EndTabItem();
|
||||||
|
}
|
||||||
|
ImGui::EndTabBar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef FN_TEST_BUILD
|
||||||
|
int main() {
|
||||||
|
return fn::run_app({
|
||||||
|
.title = "fn_registry — Chart Demo",
|
||||||
|
.width = 1400,
|
||||||
|
.height = 900,
|
||||||
|
.about = {.name = "chart demo",
|
||||||
|
.version = "0.2.0",
|
||||||
|
.description = "Demo de primitivos viz: line, scatter, bar, heatmap. AppConfig estandar + multi-viewport."},
|
||||||
|
.log = {.file_path = "chart_demo.log",
|
||||||
|
.level = static_cast<int>(fn_log::Level::Debug)}
|
||||||
|
}, render);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
// E2E tests for chart_demo — Dear ImGui Test Engine.
|
||||||
|
// Built only when -DFN_BUILD_TESTS=ON. The same main.cpp from chart_demo is
|
||||||
|
// compiled here with FN_TEST_BUILD defined so its int main() is excluded and
|
||||||
|
// only render() is reused.
|
||||||
|
|
||||||
|
#include "app_base.h"
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "imgui_te_engine.h"
|
||||||
|
#include "imgui_te_context.h"
|
||||||
|
|
||||||
|
void render(); // defined in chart_demo/main.cpp
|
||||||
|
|
||||||
|
static void register_tests(ImGuiTestEngine* e) {
|
||||||
|
ImGuiTest* t = nullptr;
|
||||||
|
|
||||||
|
// Smoke test: the main window appears and is non-empty.
|
||||||
|
t = IM_REGISTER_TEST(e, "chart_demo", "smoke_window_visible");
|
||||||
|
t->TestFunc = [](ImGuiTestContext* ctx) {
|
||||||
|
ctx->SetRef("fn_registry \xe2\x80\x94 Chart Demo"); // em-dash
|
||||||
|
IM_CHECK(ctx->WindowInfo("").ID != 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Cycle through all four tabs. Test engine fails the test if any tab item
|
||||||
|
// is not found or cannot be activated — that is our implicit assertion.
|
||||||
|
t = IM_REGISTER_TEST(e, "chart_demo", "tabs_cycle_all");
|
||||||
|
t->TestFunc = [](ImGuiTestContext* ctx) {
|
||||||
|
ctx->SetRef("fn_registry \xe2\x80\x94 Chart Demo");
|
||||||
|
ctx->ItemClick("##charts/Line Plot");
|
||||||
|
ctx->ItemClick("##charts/Scatter Plot");
|
||||||
|
ctx->ItemClick("##charts/Bar Chart");
|
||||||
|
ctx->ItemClick("##charts/Heatmap");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
fn::AppConfig cfg{};
|
||||||
|
cfg.title = "chart_demo_tests";
|
||||||
|
cfg.width = 1280;
|
||||||
|
cfg.height = 800;
|
||||||
|
return fn::run_app_test(cfg, render, register_tests);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user