# 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

```bash
# 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 `pin`ea 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`:
   ```cpp
   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`:
   ```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.
