feat(registry): index cpp/apps/* + e2e test infrastructure
registry/indexer.go ahora escanea <lang>/apps/*/app.md ademas de apps/ y projects/*/apps/. cpp/apps/chart_demo y cpp/apps/shaders_lab pasan a estar en registry.db con sus manifests. Infraestructura de tests e2e (opt-in con -DFN_BUILD_TESTS=ON): - vendor de Dear ImGui Test Engine (personal/open-source license). - chart_demo_tests target con tests/chart_demo_tests.cpp. - /e2e-cpp slash command para crear y ejecutar tests e2e. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,3 +6,17 @@ add_imgui_app(chart_demo
|
||||
${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.
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "viz/bar_chart.h"
|
||||
#include "viz/heatmap.h"
|
||||
#include "core/app_menubar.h"
|
||||
#include "core/logger.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
@@ -23,6 +24,7 @@ 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]);
|
||||
@@ -37,9 +39,10 @@ static void init_data() {
|
||||
heat_data[i] = sinf(r * 0.5f) * cosf(c * 0.5f);
|
||||
}
|
||||
data_initialized = true;
|
||||
fn_log::log_debug("init_data: ok");
|
||||
}
|
||||
|
||||
static void render() {
|
||||
void render() {
|
||||
init_data();
|
||||
|
||||
// MainMenuBar (solo Settings — chart_demo no tiene paneles toggleables)
|
||||
@@ -76,6 +79,7 @@ static void render() {
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
#ifndef FN_TEST_BUILD
|
||||
int main() {
|
||||
return fn::run_app({
|
||||
.title = "fn_registry — Chart Demo",
|
||||
@@ -83,6 +87,9 @@ int main() {
|
||||
.height = 900,
|
||||
.about = {.name = "chart demo",
|
||||
.version = "0.2.0",
|
||||
.description = "Demo de primitivos viz: line, scatter, bar, heatmap. AppConfig estandar + multi-viewport."}
|
||||
.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);
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
---
|
||||
name: shaders_lab
|
||||
lang: cpp
|
||||
domain: gfx
|
||||
description: "Live GLSL shader playground con DAG pipeline. Editor de codigo con compilacion en caliente, panel DAG con paleta de generadores/filtros/output, dos canvas (Code y DAG), parseo de uniforms anotados (// @slider, @color, @xy) que se convierten en controles, persistencia de generators en shaders_lab.db, y guardado/carga de layouts ImGui."
|
||||
tags: [imgui, opengl, glsl, shaders, dag, live-coding, playground, sqlite]
|
||||
uses_functions:
|
||||
# gfx
|
||||
- gl_loader_cpp_gfx
|
||||
- gl_shader_cpp_gfx
|
||||
- gl_framebuffer_cpp_gfx
|
||||
- fullscreen_quad_cpp_gfx
|
||||
- shader_canvas_cpp_gfx
|
||||
- uniform_parser_cpp_gfx
|
||||
- uniform_panel_cpp_gfx
|
||||
- dag_catalog_cpp_gfx
|
||||
- dag_compile_cpp_gfx
|
||||
- dag_uniforms_cpp_gfx
|
||||
- dag_panel_cpp_gfx
|
||||
- dag_node_editor_cpp_gfx
|
||||
- dag_palette_cpp_gfx
|
||||
- dag_node_previews_cpp_gfx
|
||||
- shaderlab_db_cpp_gfx
|
||||
- code_to_generator_cpp_gfx
|
||||
# core (modal Save-as-generator)
|
||||
- modal_dialog_cpp_core
|
||||
- text_input_cpp_core
|
||||
- button_cpp_core
|
||||
uses_types:
|
||||
- dag_types_cpp_gfx
|
||||
framework: "imgui"
|
||||
entry_point: "main.cpp"
|
||||
dir_path: "cpp/apps/shaders_lab"
|
||||
repo_url: ""
|
||||
---
|
||||
|
||||
## Arquitectura
|
||||
|
||||
App ImGui de live-coding GLSL con dos modos en paralelo:
|
||||
|
||||
1. **Code panel** — editor de fragment shader libre. Las anotaciones en
|
||||
uniforms (`// @slider`, `// @color`, `// @xy`, `// @toggle`) se parsean y
|
||||
convierten en controles del panel **Controls** que escriben en un
|
||||
`UniformStore` aplicado al programa cada frame.
|
||||
2. **DAG panel** — pipeline node-based con catalogo de generadores
|
||||
(plasma, voronoi, etc.) y filtros (blur, threshold, etc.) que se
|
||||
compilan a un fragment shader unificado y se renderizan en **Canvas DAG**.
|
||||
|
||||
Al guardar un Code shader como "generator" se traduce a un `DagNodeDef` y se
|
||||
persiste en `shaders_lab.db` (tabla via `shaderlab_db`), apareciendo en la
|
||||
paleta del DAG junto a los builtins.
|
||||
|
||||
## Capas
|
||||
|
||||
| Archivo | Responsabilidad |
|
||||
|---|---|
|
||||
| `main.cpp` | UI shell, paneles, modal save-as, layouts, AppConfig |
|
||||
| `compiler.cpp` | `compile_code()`, `compile_dag()`, `mark_code_dirty()` con debounce 250ms |
|
||||
|
||||
`main.cpp` mantiene estado global de sesion (g_source, g_pipeline, g_descs,
|
||||
g_store, g_layouts...) — ImGui retained-mode obliga a que persista entre
|
||||
frames. Toda la logica pura de compilacion vive en `compiler.cpp` y en las
|
||||
funciones `dag_compile`, `code_to_generator`, `uniform_parser` del registry.
|
||||
|
||||
## Persistencia
|
||||
|
||||
- **`shaders_lab.db`** (junto al .exe) — tabla de generators de usuario via
|
||||
`shaderlab_db_*`, ademas de `imgui_layouts` (creada por `layout_storage`).
|
||||
- `imgui.ini` y `app_settings.ini` — gestionados por `fn::run_app` en
|
||||
`<exe_dir>/local_files/`.
|
||||
|
||||
## Paneles
|
||||
|
||||
| Panel | Atajo | Que muestra |
|
||||
|---|---|---|
|
||||
| Code | Ctrl+1 | Editor del fragment shader + boton "Save as generator" |
|
||||
| DAG Pipeline | Ctrl+2 | Node editor con la pipeline |
|
||||
| Canvas Code | Ctrl+3 | Render del Code shader |
|
||||
| Canvas DAG | Ctrl+4 | Render del shader compilado del DAG |
|
||||
| Controls | Ctrl+5 | Sliders/color pickers de uniforms anotados |
|
||||
| Functions | Ctrl+6 | Paleta del DAG (generators + filters + output) |
|
||||
| Generated GLSL | Ctrl+7 | GLSL final del DAG con uniforms baked como const array |
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
# Linux
|
||||
cd cpp && cmake -B build/linux -S . && cmake --build build/linux --target shaders_lab
|
||||
|
||||
# Windows (cross-compile)
|
||||
cd cpp && cmake -B build/windows -S . -DCMAKE_TOOLCHAIN_FILE=toolchains/mingw-w64.cmake \
|
||||
&& cmake --build build/windows --target shaders_lab
|
||||
```
|
||||
|
||||
## Decisiones
|
||||
|
||||
- `init_gl_loader = true` (via `fn::run_app` por default cuando se enlaza
|
||||
con OpenGL) — `shader_canvas`, `gl_shader`, `gl_framebuffer` llaman gl*.
|
||||
- `viewports = true` — los Canvas se pueden arrastrar fuera del main.
|
||||
- DAG default: arranca con un nodo "plasma" + "output" si la paleta los
|
||||
encuentra; persiste el INI con `layout_storage`.
|
||||
- El boton "Save as generator" valida snake_case, evita colisionar con
|
||||
builtins, traduce con `code_to_generator`, persiste con `shaderlab_db_save_generator`,
|
||||
y registra el nodo nuevo en el catalogo en vivo (`dag_register_node`).
|
||||
Reference in New Issue
Block a user