Files
fn_registry/cpp/functions/viz/graph_renderer.md
T
egutierrez ac11300335 feat(viz): renderer shapes/iconos/flechas/edge-styles (issue 0049f)
graph_renderer 1.5.0:
- 6 shapes SDF (circle, square, diamond, hex, triangle, rounded square)
  con dispatch en fragment shader y AA via fwidth.
- Atlas opcional de iconos Tabler bakeado por graph_icons; el shader
  compone overlay desde un uniform vec4 u_icon_uvs[256]. Setter publico
  graph_renderer_set_icon_atlas(r, tex, uv_table, count).
- Aristas direccionales: 6 vertices por arista (line + chevron de la
  flecha) en una sola draw call; segmento principal acortado por el
  radio del nodo target.
- Edge styles solid/dashed/dotted via descarte por arc_length en el
  fragment shader; las lineas del chevron son siempre solidas.

graph_icons 1.0.0 (nuevo):
- Atlas RGBA8 512x512 = grid 16x16 (256 iconos max) bakeado con
  stb_truetype desde tabler-icons.ttf.
- API: graph_icons_build/texture/region/uv_table/destroy. icon_id es
  1-based; 0 reservado para "sin icono".
- Hook FN_GRAPH_ICONS_SKIP_GL=1 para tests sin contexto GL.

Demo demos_graph_styles en primitives_gallery: 6 EntityTypes (uno por
shape) con icono Tabler representativo + 3 RelationTypes (knows/uses/
owns) con flechas direccionales y los 3 estilos.

test_graph_icons: 6 casos cubriendo bake, regiones 1-indexed, uv_table
consistente, layout en grid 16x16, validacion de count fuera de rango,
y verificacion de alpha != 0 en las celdas tras bake.

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

8.1 KiB
Raw Blame History

name, kind, lang, domain, version, purity, signature, description, tags, uses_functions, uses_types, returns, returns_optional, error_type, imports, tested, tests, test_file_path, file_path, framework, params, output
name kind lang domain version purity signature description tags uses_functions uses_types returns returns_optional error_type imports tested tests test_file_path file_path framework params output
graph_renderer function cpp viz 1.5.0 impure GraphRenderer* graph_renderer_create(int width, int height, const GraphRendererConfig& config) Renderer GPU de grafos con instanced rendering a FBO, compatible con ImGui::Image para visualizacion de grafos grandes
graph
renderer
opengl
gpu
instanced
fbo
visualization
frustum-cull
rgba8
vertex-pulling
tbo
sdf
icons
arrows
edge-styles
gl_loader_cpp_gfx
GraphData_cpp_viz
EntityType_cpp_viz
RelationType_cpp_viz
false error_go_core
imgui
false
cpp/functions/viz/graph_renderer.cpp imgui
name desc
width Ancho del framebuffer en pixels
name desc
height Alto del framebuffer en pixels
name desc
config Configuracion visual: outline width, edge width, edge alpha, color de fondo, fade de aristas por distancia a camara
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

// 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

// 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.5 (2026-04-29, issue 0049f): renderer extendido con shapes SDF, atlas de iconos, flechas direccionales y estilos de arista. API publica gana graph_renderer_set_icon_atlas(r, tex, uv_table, count). Cambios internos:

    1. 6 shapes SDF en el fragment shader: circle, square, diamond, hex (regular), triangle (equilatero), rounded_square. pick_sdf(shape) despacha por valor (1..6). El AA usa fwidth(d) para mantener calidad a cualquier zoom; el outline se compone con un smoothstep extra del SDF.
    2. Icon atlas opcional: el shader compone un overlay con el icono Tabler bakeado por graph_icons_build. Las UVs (4 floats por icono) viven en un uniform vec4 u_icon_uvs[256] — solo se sube el icon_id (16 bits) por instancia. La textura se bindea al texture unit 1.
    3. Aristas direccionales: cada arista pasa de 2 a 6 vertices (glDrawArraysInstanced(GL_LINES, 0, 6, edges)). Los 4 vertices extra dibujan un chevron (2 lineas) en la cabeza si flags & EF_DIRECTED; el segmento principal se acorta 5 px para que la flecha no se incruste en el target.
    4. Edge styles: style_flags (32 bits) combina flags (low 8) y style (next 8). Fragment shader descarta segun arc_length interpolado: dashed (mod 8 > 4) y dotted (mod 4 > 1). Las lineas del chevron son siempre solidas.
    5. NodeInstance: 16 → 24 bytes (anade shape_icon packeado + 4 bytes pad). EdgeStatic: 16 → 20 bytes (anade style_flags). Bandwidth de aristas sigue subiendo solo en cambios de grafo (vertex pulling intacto).
  • 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_overrideEntityType → 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.