Files
fn_registry/cpp/functions/viz/graph_renderer.md
T
egutierrez c29428a187 feat(viz): graph_types modelo extendido + EntityType/RelationType + flags (issue 0049e)
Extiende el modelo agnostico de graph_types.h para soportar shapes/iconos/
filtros/labels/streaming sin acoplar a backend. Migra el unico consumer
(demos_graph) en el mismo cambio.

- GraphNode v2: type_id + shape_override/color_override/size_override +
  flags (NF_PINNED/VISIBLE/SELECTED/HOVERED) + label_idx + user_data.
- GraphEdge v2: type_id + style_override + flags (EF_DIRECTED/VISIBLE).
- EntityType / RelationType: tablas en GraphData (types, rel_types).
- Helpers de resolucion (resolve_node_color/shape/size, resolve_edge_*)
  y constructores ergonomicos (graph_node, graph_edge, entity_type,
  relation_type) — sentinel-based para herencia automatica del tipo.
- graph_renderer v1.4: lee NF_VISIBLE / EF_VISIBLE, resuelve apariencia
  via override → EntityType → fallback indexado por type_id. Skipea
  aristas con endpoints invisibles. Shapes siguen pintandose como
  circulo (0049f cableara el dispatch real).
- graph_force_layout v1.2: pinned ahora vive en flags & NF_PINNED.
- graph_viewport v1.1: hover/seleccion publican NF_HOVERED/SELECTED en
  el grafo (clear-then-set). Drag usa NF_PINNED. Tooltip muestra Type/
  user_data en lugar de community/value/label.
- demos_graph: 8 EntityType (paleta antigua) + 1 RelationType. type_id
  por cluster. user_data = indice numerico del nodo. Apariencia visual
  identica al pre-cambio.
- test_graph_types.cpp: 12 casos cubriendo helpers, defaults, bitmask
  manipulation y resoluciones override-vs-EntityType. test_graph_edge_
  static actualizado al nuevo modelo (ya no tiene .color directo).
- 4 .md de tipos nuevos (graph_node, graph_edge, entity_type,
  relation_type) + GraphData v2.0 actualizado.

Tests: 31/31 ctest verdes (incluye test_visual golden).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 22:44:40 +02:00

108 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: graph_renderer
kind: function
lang: cpp
domain: viz
version: "1.4.0"
purity: impure
signature: "GraphRenderer* graph_renderer_create(int width, int height, const GraphRendererConfig& config)"
description: "Renderer GPU de grafos con instanced rendering a FBO, compatible con ImGui::Image para visualizacion de grafos grandes"
tags: [graph, renderer, opengl, gpu, instanced, fbo, visualization, frustum-cull, rgba8, vertex-pulling, tbo]
uses_functions: ["gl_loader_cpp_gfx"]
uses_types: ["GraphData_cpp_viz"]
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [imgui]
tested: false
tests: []
test_file_path: ""
file_path: "cpp/functions/viz/graph_renderer.cpp"
framework: imgui
params:
- name: width
desc: "Ancho del framebuffer en pixels"
- name: height
desc: "Alto del framebuffer en pixels"
- name: config
desc: "Configuracion visual: outline width, edge width, edge alpha, color de fondo, fade de aristas por distancia a camara"
output: "Handle opaco al renderer. Usar graph_renderer_draw() para obtener texture ID de OpenGL, pasable directamente a ImGui::Image()"
---
# graph_renderer
Renderer GPU de grafos basado en OpenGL 3.3 core profile con instanced rendering. Renderiza nodos y aristas de un `GraphData` a un FBO interno y retorna el texture ID para integracion directa con `ImGui::Image()`.
## Funciones del API
```cpp
// Ciclo de vida
GraphRenderer* graph_renderer_create(int width, int height, const GraphRendererConfig& config = {});
void graph_renderer_destroy(GraphRenderer* r);
void graph_renderer_resize(GraphRenderer* r, int width, int height);
// Renderizado
unsigned int graph_renderer_draw(GraphRenderer* r, const GraphData& graph,
float cam_x, float cam_y, float cam_zoom);
```
## Ejemplo de uso con ImGui
```cpp
// Inicializacion (una vez)
GraphRenderer* renderer = graph_renderer_create(800, 600);
// En el render loop
ImVec2 panel_size = ImGui::GetContentRegionAvail();
graph_renderer_resize(renderer, (int)panel_size.x, (int)panel_size.y);
unsigned int tex = graph_renderer_draw(renderer, graph_data,
cam_x, cam_y, cam_zoom);
ImGui::Image((ImTextureID)(uintptr_t)tex,
panel_size,
ImVec2(0, 1), ImVec2(1, 0)); // flip Y para OpenGL
// Destruccion
graph_renderer_destroy(renderer);
```
## Notas de implementacion
**Renderizado de nodos:** Instanced rendering con un quad unitario [-0.5, 0.5] expandido por el tamano del nodo. El fragment shader aplica un SDF circular con anti-aliasing via `smoothstep` y un anillo de outline.
**Renderizado de aristas:** `GL_LINES` con datos de posicion y color empaquetados por arista. El ancho se controla con `GraphRendererConfig::edge_width`.
**Transformacion de camara:**
```
tx = -cam_x * zoom + width/2
ty = -cam_y * zoom + height/2
ndc = (screen / viewport) * 2 - 1
```
**Paleta de comunidades:** 10 colores ABGR usados cuando `node.color == 0`, seleccionados por `node.community % 10`.
**Estado GL:** Guarda y restaura `GL_FRAMEBUFFER_BINDING` y `GL_VIEWPORT` para ser compatible con el render loop de ImGui sin efectos secundarios.
**Includes GL:** Usa `gfx/gl_loader.h` (v1.1+). En Linux es no-op (incluye headers con `GL_GLEXT_PROTOTYPES`). En Windows expone los simbolos modernos via `wglGetProcAddress` con macros `#define gl* fn_gl*`. Cualquier app que use `graph_renderer` debe linkear `gl_loader.cpp` y llamar `fn::gfx::gl_loader_init()` una vez tras crear el contexto GL.
## Notas
- **v1.4** (2026-04-29, issue 0049e): adapta el renderer al modelo extendido de `GraphData`. Lee `n.flags & NF_VISIBLE` para skipear nodos invisibles, resuelve color via `n.color_override``EntityType` → fallback indexado por `type_id`. Aristas: skip si `!(EF_VISIBLE)` o si los endpoints no son visibles, color via `RelationType`. Shapes/iconos/dashed-style siguen como circulo solido — el dispatch real llega en 0049f.
- **v1.3** (2026-04-29, issue 0049d): aristas via vertex pulling. API publica intacta.
- El buffer de aristas pasa a ser estatico (`source_idx, target_idx, color, flags` × E, 16 bytes/arista) y solo se reupload cuando el grafo cambia (detectado por `(edges_ptr, edge_count)` — heuristica suficiente mientras `GraphData` no tenga `revision`). Para 100k aristas: 1.6 MB iniciales vs 4.8 MB/frame del esquema anterior — el upload baja a cero en regimen estable.
- Las posiciones de los nodos se suben cada frame a un Texture Buffer Object `RG32F` (`vec2[]`, 8 bytes/nodo). El vertex shader de aristas hace `texelFetch(u_node_pos, idx)` con `idx` derivado de `gl_VertexID & 1` (0=source, 1=target).
- Draw call: `glDrawArraysInstanced(GL_LINES, 0, 2, edge_count)` — 1 instancia por arista, 2 vertices por linea, todos los atributos con `divisor=1`.
- Frustum cull de aristas eliminado en CPU (la GPU clipea fuera de viewport por su cuenta). Se mantiene para nodos.
- `edge_alpha` pasa a uniform en el shader; la pre-multiplicacion en CPU desaparece y permite que el buffer estatico no dependa del config.
- GLSL: 330 core (con `samplerBuffer`/`texelFetch` que estan en 1.40+). `gl_loader` gana `glBufferSubData`, `glVertexAttribIPointer` y `glTexBuffer` (Linux ya los tenia via `GL_GLEXT_PROTOTYPES`).
- **v1.2** (2026-04-29, issue 0049c): tres optimizaciones internas, API publica intacta.
1. **RGBA8 packing**: el buffer de instancia/vertice usa `uint32` por color en lugar de 4 floats. Nodo: 28 → 16 bytes/instance (-43%). Edge: 24 → 12 bytes/vertex (-50%). Los shaders desempaquetan con bit shifts (compatible GL 3.30+, sin necesidad de `unpackUnorm4x8` que es 4.20+). Helpers expuestos en el .h: `pack_rgba8`, `unpack_rgba8`, `modulate_alpha_rgba8` (testeados en `test_graph_pack_rgba8.cpp`).
2. **Capacity-tracked streaming buffers**: el VBO se mantiene orphaned con `glBufferData(NULL, capacity)` y se actualiza con `glBufferSubData` solo los bytes usados. La capacidad crece x2 cuando hace falta (inicial: 4096 nodos / 8192 vertices de aristas) → reallocaciones en O(log N). Staging CPU reutilizado entre frames.
3. **Frustum cull**: nodos y aristas fuera del viewport AABB (con margen 10%) se saltan en CPU antes del upload. Para nodos, solo los visibles entran en el instance buffer (`glDrawArraysInstanced` con `visible_count`). Para aristas, AABB del segmento contra viewport. Pop-in al borde mitigado por el margen.
Resultado esperado: ~20k nodos a 60fps en GPU integrada cuando `cam_zoom` mantiene la mayoria fuera del viewport.
- **v1.1** (2026-04-25): cambia de raw `<GL/glext.h>` a `gfx/gl_loader.h` para que compile en cross-compile MinGW. Sin cambios funcionales — el binario Linux es bit-equivalente.