Files
fn_registry/dev/issues/completed/0049f-graph-renderer-symbols.md
T
egutierrez c967c2edfd 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

6.3 KiB
Raw Blame History

0049f — Renderer extendido: shapes SDF, icon atlas, flechas, edge styles

Metadata

Campo Valor
ID 0049f
Estado pendiente
Prioridad alta
Tipo feature — parte de #0049

Dependencias

Bloqueada por: 0049e (necesita shape, icon_id, flags.directed, style).


Objetivo

Extender el fragment shader del renderer con una libreria de SDF (circulo, cuadrado, diamante, hex, triangulo, rounded square), un atlas de iconos Tabler renderizado a textura, flechas direccionales en aristas, y estilos solid/dashed/dotted. Una sola draw call para todos los nodos, una para todas las aristas.

Contexto

Maltego/OSINT requiere distinguir entidades de un vistazo: Person ≠ Email ≠ Domain. Color solo no escala — necesitamos forma + icono. El stack ya tiene Tabler como font (cpp/functions/core/icons_tabler.h); aqui lo bakeamos a una textura para que cada nodo pueda llevar un icono dentro.

Arquitectura

cpp/functions/viz/
├── graph_renderer.{h,cpp}            # MOD: shaders extendidos + uniform sampler de iconos
├── graph_renderer.md                  # MOD: bump version
├── graph_icons.{h,cpp}               # NEW: builder del atlas
├── graph_icons.md                     # NEW
└── (fragment shader incluye sdf_*.glsl inline)

cpp/tests/
└── test_graph_icons.cpp              # NEW

API graph_icons

struct IconAtlas;

struct IconRegion {
    uint16_t id;          // = posicion en el array al construir
    float u0, v0, u1, v1; // UVs en la textura
};

// Construye una textura 512x512 RGBA con `count` iconos Tabler renderizados a 32px.
// Codepoints son los valores `TI_*` del header.
IconAtlas* graph_icons_build(const uint16_t* codepoints, int count, int icon_px = 32);
unsigned int graph_icons_texture(const IconAtlas*);   // GL texture id
const IconRegion* graph_icons_region(const IconAtlas*, uint16_t icon_id);
void graph_icons_destroy(IconAtlas*);

Shaders extendidos

Vertex shader de nodos ya pasa type_id, shape, icon_id por instance. Fragment shader compone:

float sdf_circle (vec2 uv) { return length(uv - 0.5) - 0.5; }
float sdf_square (vec2 uv) { vec2 d = abs(uv - 0.5) - 0.5; return max(d.x, d.y); }
float sdf_diamond(vec2 uv) { vec2 d = abs(uv - 0.5); return d.x + d.y - 0.5; }
float sdf_hex    (vec2 uv) { ... }
float sdf_triangle(vec2 uv){ ... }
float sdf_rrect  (vec2 uv) { vec2 d = abs(uv - 0.5) - 0.5 + r; return length(max(d,0.0)) - r; }

float pick_sdf(uint shape, vec2 uv) {
    switch (shape) {
        case 0u: return sdf_circle(uv);
        case 1u: return sdf_square(uv);
        case 2u: return sdf_diamond(uv);
        ...
    }
}

void main() {
    float d = pick_sdf(v_shape, v_uv);
    float aa = fwidth(d);
    float a  = 1.0 - smoothstep(0.0, aa, d);
    if (a < 0.001) discard;
    vec3 col = v_color.rgb;
    if (v_icon_id != 0u) {
        // UV del icono dentro del atlas: (uv - 0.5) * scale + region_center
        vec2 atlas_uv = mix(vec2(v_icon_u0, v_icon_v0), vec2(v_icon_u1, v_icon_v1), v_uv);
        vec4 ic = texture(u_icon_atlas, atlas_uv);
        col = mix(col, vec3(1.0), ic.a * 0.85);
    }
    frag_color = vec4(col, a * v_color.a);
}

Aristas direccionales con flecha

Cada arista pasa de 2 vertices (line) a 4 vertices: 2 para el segmento + 2 para el triangulo de la flecha (solo si flags & EF_DIRECTED). Indices 0-1 = linea, 2-3 = triangulo apuntando al target. Vertex shader calcula la flecha en world coords usando direccion target-source y tamaño constante en pixels.

Edge styles

Fragment shader de aristas recibe arc_length (interpolado linealmente entre source y target en pixels). Para style=DASHED: if (mod(arc_length, 8.0) > 4.0) discard;. Para DOTTED: similar con periodo y duty diferentes.

Tareas

Fase 1 — graph_icons

  • 1.1 Crear graph_icons.{h,cpp,md}. Implementar _build usando stb_truetype (o ImGui font baker) para rasterizar codepoints Tabler a una bitmap 512×512.
  • 1.2 Layout simple: grid 16×16 a 32px por celda → 256 iconos por atlas.
  • 1.3 Subir como GL texture RGBA8 con linear filtering.
  • 1.4 Tests: build de 10 iconos conocidos; verificar que la textura tiene contenido en las regiones esperadas.

Fase 2 — Shaders SDF

  • 2.1 Implementar las 6 funciones SDF en GLSL.
  • 2.2 pick_sdf con switch por shape_id.
  • 2.3 Pasar shape, icon_id, icon_u0/v0/u1/v1 por instance. Layout actualizado.
  • 2.4 Compose icon overlay en fragment.

Fase 3 — Aristas direccionales + estilos

  • 3.1 Cambiar glDrawArrays(GL_LINES, ...) por geometry expansion en CPU/shader: 4 vertices por arista, los 2 ultimos solo se usan si EF_DIRECTED.
  • 3.2 Vertex shader calcula posicion de la flecha (10 px constante en pantalla).
  • 3.3 Fragment shader recibe arc_length y descarta segun style.

Fase 4 — Demo

  • 4.1 Crear cpp/apps/primitives_gallery/demos_graph_styles.cpp: grafo pequeño (~30 nodos) con 6 EntityTypes (uno por shape), 3 RelationTypes (solid/dashed/dotted), aristas direccionales mezcladas. Iconos Tabler representativos: TI_USER, TI_MAIL, TI_GLOBE, TI_PHONE, TI_BUILDING, TI_DATABASE.
  • 4.2 Anadirlo a demos.h y al menu de la galeria.
  • 4.3 Visual golden generado.

Fase 5 — Cleanup

  • Bump version graph_renderer 1.2.0 → 1.3.0; graph_icons 1.0.0.
  • fn index.
  • Commit feat(viz): renderer shapes/iconos/flechas/edge-styles.

Criterio de done

  • demos_graph_styles muestra todas las shapes + iconos + flechas + estilos visualmente correctos.
  • Sigue siendo 1 draw call por nodos y 1 por aristas.
  • Test golden estable.
  • Tests test_graph_icons verdes.

Riesgos

Riesgo Mitigacion
switch en GLSL ramifica → menos eficiente Acceptable a estas escalas; se puede unfold luego con #define por tipo si hace falta
Atlas baker mete artifacts en bordes Padding 2px entre celdas
Flechas ocupan area visible del nodo target Acortar el segmento de linea por el radio del nodo target en vertex shader
Codepoints Tabler con caracteres compuestos Usar solo los basicos del header icons_tabler.h (ya validados)