Files
fn_registry/cpp/apps/primitives_gallery
egutierrez d3d5af51f2 feat(primitives_gallery): wire gl_texture_load demo
Tras integrar 0025 (que trajo la app entera) y 0026 (que solo añadio
demos_gl_texture.cpp + assets/sample.png + cpp/functions/gfx/gl_texture_load.*),
falta wire-up de la entrada nueva en los archivos compartidos del gallery.

- demos.h: declarar gallery::demo_gl_texture()
- main.cpp: entrada en k_demos[] con id "gl_texture", category "Gfx"
- CMakeLists.txt: añadir demos_gl_texture.cpp, gl_texture_load.cpp,
  stb_image_impl.cpp, e include dir vendor/stb

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 21:05:45 +02:00
..

primitives_gallery

Catalogo visual interactivo de los primitivos UI del registry (cpp/functions/core y cpp/functions/viz). Un solo ejecutable con sidebar izquierdo + panel derecho que renderiza la demo del primitivo seleccionado con todas sus variantes y un snippet de codigo.

Rol

Funcion Como lo cumple
Smoke test visual Abrir la gallery tras un cambio en tokens / componentes; si algo se ve raro, lo cazas en segundos.
Documentacion viva Cada demo muestra el componente trabajando + el snippet exacto. Mas rapido que leer los .md.
Build gate Esta en el CMake principal (cpp/CMakeLists.txt). Si un primitivo rompe API, la gallery no compila => CI rojo.
Sandbox de prototipos Datos sinteticos, sin backend; ideal para iterar un primitivo nuevo sin tocar el dashboard.

Build & run

# Linux
cmake --build cpp/build/linux --target primitives_gallery -j$(nproc)
./cpp/build/linux/apps/primitives_gallery/primitives_gallery

# Windows (cross-compile)
cmake --build cpp/build/windows --target primitives_gallery -j$(nproc)
# binario: cpp/build/windows/apps/primitives_gallery/primitives_gallery.exe

No se conecta a sqlite_api ni a ningun backend. Datos sinteticos generados in-memory.

Demos disponibles

Core

Demo Primitivo Que muestra
button button_cpp_core 4 variantes x 3 sizes
icon_button icon_button_cpp_core Glyphs comunes con tooltip
toolbar toolbar_cpp_core Dos grupos con separador vertical
modal_dialog modal_dialog_cpp_core Boton que abre modal con form
text_input text_input_cpp_core 3 inputs con placeholder
select select_cpp_core Dropdown con y sin (none)
toast + inbox toast_cpp_core (v1.1) 4 botones que disparan toasts + campana con badge
tree_view tree_view_cpp_core Arbol fake de proyectos -> apps
badge badge_cpp_core 6 variantes semanticas
empty_state empty_state_cpp_core Lista vacia con icono + cta
page_header page_header_cpp_core Header con toolbar a la derecha
dashboard_panel dashboard_panel_cpp_core Panel con titulo y borde
kpi_card kpi_card_cpp_viz (v1.2) Grid 1x4 con sparklines y delta

Viz

Demo Primitivo Que muestra
bar_chart bar_chart_cpp_viz (v1.2) Labels que caben + labels rotados 45
pie_chart pie_chart_cpp_viz (v1.1) Pie + donut con tooltip por slice
line_plot line_plot_cpp_viz (v1.1) Serie sintetica sin(t) + ruido
scatter_plot scatter_plot_cpp_viz (v1.1) 120 puntos con correlacion
histogram histogram_cpp_viz (v1.1) 300 muestras gaussianas
sparkline sparkline_cpp_viz Trending up / down / flat
graph_viewport graph_viewport_cpp_viz Ver seccion abajo

Demo graph_viewport (en detalle)

Pipeline completo de visualizacion de grafos con instanced GPU rendering:

  • graph_renderer_cpp_viz (1 draw call para todos los nodos via glDrawArraysInstanced)
  • graph_force_layout_cpp_viz (Barnes-Hut, paso de simulacion por frame)
  • graph_spatial_hash_cpp_core (hit-testing O(1) bajo el cursor)
  • graph_viewport_cpp_viz (widget que orquesta los anteriores con pan/zoom/select)

Controles

Control Rango Efecto
Nodes 100 20 000 Numero de nodos a generar
Clusters 2 16 Numero de comunidades (cada una con su color)
Repulsion 100 20 000 Fuerza repulsiva entre todos los nodos. Mas alto => grafo mas extendido y energia mayor.
Attraction 0.001 0.5 Constante del muelle de las aristas. Mas alto => clusters mas compactos.
Gravity 0.0 0.05 Tiron hacia (0,0). Util para evitar drift cuando subes mucho la repulsion.
Regenerate boton Regenera el grafo con los valores actuales de Nodes/Clusters.
Pause / Resume layout boton Para o reanuda la simulacion force-directed.
Fit view boton Encuadra la camara al bounding box del grafo con 10% de padding.

Los tres sliders de fuerzas se leen cada frame y se inyectan en ForceLayoutConfig, asi que cambiar un valor durante el layout en marcha re-calibra el sistema al instante.

Stats line (sin vibracion)

Una sola linea fija — sin secciones condicionales que cambien la altura del panel:

nodes=N  edges=E  energy=X  fps=F  |  hover=#id cN  sel=#id

hover y sel muestran - cuando no hay nada seleccionado para mantener el ancho/alto estable; antes una fila condicional desplazaba el viewport en cada hover.

Interaccion con el viewport

Gesto Accion
Drag con boton izquierdo en zona vacia Pan de camara
Wheel Zoom (limites 0.01x 50x)
Drag sobre nodo Mueve el nodo (lo pinea durante el drag)
Click sobre nodo Selecciona (s_state.selected_node)
Hover sobre nodo Resaltado + s_state.hovered_node poblado

Datos sinteticos

generate_synthetic_graph(N, K) reparte N nodos en K clusters dispuestos en circulo, con ~3 aristas intra-cluster por nodo y un 5% adicional de aristas inter-cluster. Paleta de 8 colores ABGR. Posiciones iniciales con dispersion gaussiana de 80 px alrededor del centroide del cluster — el force layout las reordena en pocos frames.

Performance esperada

Nodes FPS objetivo (RTX 30xx, viewport 800x460) Notas
1 000 60 (vsync) Caso comun; layout converge < 1 s
5 000 60 Pipeline al limite del CPU para Barnes-Hut
20 000 30 50 El cuello pasa a ser el layout (CPU); GPU render sigue holgado

Si necesitas mas, fija los nodos (pinned = true o Pause layout) y veras 60 fps estables — el bottleneck es la simulacion, no el render.

Anadir un demo nuevo

  1. Anadir el prototipo en demos.h dentro de namespace gallery:
    void demo_my_thing();
    
  2. Implementar el cuerpo en demos_core.cpp o demos_viz.cpp (o un fichero nuevo si la demo es grande, p.ej. demos_graph.cpp).
  3. Registrar la entrada en el array k_demos[] de main.cpp:
    {"my_thing", "my_thing", "Core" /* o "Viz" */, &gallery::demo_my_thing},
    
  4. Si la demo necesita .cpp adicionales del registry, anadirlos a CMakeLists.txt de la gallery.
  5. Recompilar.

Estructura

cpp/apps/primitives_gallery/
  CMakeLists.txt        # target primitives_gallery
  README.md             # este fichero
  main.cpp              # sidebar + router
  demo.{h,cpp}          # helpers (demo_header, section, code_block, ...)
  demos.h               # prototipos void demo_xxx()
  demos_core.cpp        # demos del dominio core
  demos_viz.cpp         # demos del dominio viz (charts simples)
  demos_graph.cpp       # demo de graph_viewport (mas pesada, fichero aparte)

Convenciones para los demos

  • Sin estado real: usar arrays sinteticos (float fake[] = {...}) o generadores deterministas con seed fijo. Datos reproducibles.
  • Sin red: nunca llamar a sqlite_api, HTTP, filesystem. La gallery debe arrancar offline en cualquier maquina.
  • Snippets honestos: el code_block(...) debe mostrar el codigo que produce esa demo, no pseudocodigo.
  • Variantes en grids: si un primitivo tiene N variantes x M tamanos, mostrarlos todos en un BeginTable para comparacion lado-a-lado.
  • Estado static: si la demo es interactiva (sliders, modal, etc.), guardar el estado en static locales — la gallery no destruye demos al cambiar de seccion, asi que el estado persiste hasta cerrar la app.

Iconos en los demos

A partir de la sesion 2026-04-25 los demos usan los macros TI_* de cpp/functions/core/icons_tabler.h (Tabler v3.41.1, 5093 glyphs). La fuente la carga automaticamente fn::run_app via icon_font_cpp_core, y add_imgui_app copia tabler-icons.ttf junto al ejecutable post-build (no hay paso manual).

demo_icon_button y demo_toolbar (en demos_core.cpp) son la referencia visual: muestran el patron button(TI_PLUS " New", V::Primary) y la fila de iconos sueltos. Ver cpp/DESIGN_SYSTEM.md seccion 11 para la regla.

Si añades un demo nuevo y necesitas glyphs, no metas \x.. UTF-8 inline — busca el icono en icons_tabler.h (o en https://tabler.io/icons) y usa el TI_* correspondiente.