# 0049e — Modelo de datos extendido: `GraphNode`/`GraphEdge` + `EntityType`/`RelationType`

## Metadata

| Campo | Valor |
|-------|-------|
| **ID** | 0049e |
| **Estado** | pendiente |
| **Prioridad** | alta |
| **Tipo** | breaking change controlado — parte de [#0049](0049-osint-graph-viewer.md) |

## Dependencias

**Bloqueada por:** [0049d](0049d-graph-edges-vertex-pulling.md) (cambios pegan en mismo `graph_types.h`; mejor encadenarlos).
**Desbloquea:** [0049f](0049f-graph-renderer-symbols.md), [0049g](0049g-graph-source-operations.md), [0049i](0049i-graph-layouts-static.md).

---

## Objetivo

Extender `graph_types.h` con el modelo agnostico necesario para shapes/iconos/filtros/labels/streaming, sin acoplar a ningun backend de datos. Migrar `demos_graph.cpp` (unico consumer actual) en el mismo sub-issue.

## Contexto

El modelo actual es minimo (`x, y, vx, vy, size, color, community`). Para soportar:
- Shapes y iconos per-tipo
- Filtrado por tipo y per-nodo
- Selection / hover / pin
- Mapeo de vuelta a la entidad real del backend
- Aristas con tipo, direccion, estilo

Se necesita `type_id`, `flags`, `*_override`, `label_idx`, `user_data` en nodos; `type_id`, `flags`, `style_override` en aristas; tablas `EntityType[]` / `RelationType[]`.

## Arquitectura

```
cpp/functions/viz/
├── graph_types.h                      # MOD: structs extendidos + enums
├── graph_types.cpp                    # MOD: helpers (graph_node, graph_edge actualizados)
├── graph_types.md                     # MOD: bump
├── graph_renderer.{h,cpp}             # MOD: leer type_id, flags, overrides al pintar
├── graph_force_layout.{h,cpp}         # MOD: respetar flags.pinned
├── graph_viewport.{h,cpp}             # MOD: setear flags.selected/hovered

cpp/apps/primitives_gallery/
└── demos_graph.cpp                    # MOD: migrar al modelo nuevo

cpp/tests/
└── test_graph_types.cpp               # NEW
```

### Modelo final

```cpp
// graph_types.h
namespace graph {

enum NodeFlags : uint8_t {
    NF_NONE     = 0,
    NF_PINNED   = 1 << 0,
    NF_VISIBLE  = 1 << 1,
    NF_SELECTED = 1 << 2,
    NF_HOVERED  = 1 << 3,
};

enum EdgeFlags : uint8_t {
    EF_NONE     = 0,
    EF_DIRECTED = 1 << 0,
    EF_VISIBLE  = 1 << 1,
};

enum Shape : uint8_t {
    SHAPE_CIRCLE = 0, SHAPE_SQUARE, SHAPE_DIAMOND, SHAPE_HEX,
    SHAPE_TRIANGLE, SHAPE_ROUNDED_SQUARE,
};

enum EdgeStyle : uint8_t {
    EDGE_SOLID = 0, EDGE_DASHED, EDGE_DOTTED,
};

struct GraphNode {
    float    x, y, vx, vy;
    uint16_t type_id;
    uint8_t  shape_override;     // 0 = use type, otherwise SHAPE_*
    uint8_t  flags;              // NF_* mask, default NF_VISIBLE
    uint32_t color_override;     // 0 = use type, !=0 = RGBA8
    float    size_override;      // 0 = use type
    uint32_t label_idx;          // index into consumer's string pool
    uint64_t user_data;          // opaque, app uses to map back to its DB
};

struct GraphEdge {
    uint32_t source, target;
    uint16_t type_id;
    uint8_t  style_override;     // 0 = use type, otherwise EDGE_*
    uint8_t  flags;              // EF_* mask
    float    weight;
    uint32_t label_idx;
};

struct EntityType {
    uint32_t color;              // RGBA8
    uint8_t  shape;              // SHAPE_*
    uint16_t icon_id;            // 0 = no icon
    float    default_size;
    const char* name;            // for UI/debug, not for render
};

struct RelationType {
    uint32_t color;
    uint8_t  style;              // EDGE_*
    float    width;
    const char* name;
};

struct GraphData {
    GraphNode*    nodes;       int node_count;     int node_capacity;
    GraphEdge*    edges;       int edge_count;     int edge_capacity;
    EntityType*   types;       int type_count;
    RelationType* rel_types;   int rel_type_count;
    // bounds, etc. (existentes)
};

} // namespace graph
```

## Tareas

### Fase 1 — Tipos

- [ ] **1.1** Reescribir `graph_types.h` con el modelo de arriba.
- [ ] **1.2** Helpers `graph_node(...)` / `graph_edge(...)` para construccion ergonomica con defaults.
- [ ] **1.3** Crear `types/viz/graph_node.md`, `graph_edge.md`, `entity_type.md`, `relation_type.md` con frontmatter completo (algebraic: product). Si ya existen, bump version.

### Fase 2 — Renderer

- [ ] **2.1** En `graph_renderer.cpp`, resolver visual final del nodo:
  ```cpp
  uint32_t color = n.color_override ? n.color_override : graph.types[n.type_id].color;
  uint8_t  shape = n.shape_override ? n.shape_override : graph.types[n.type_id].shape;
  float    size  = n.size_override  > 0 ? n.size_override : graph.types[n.type_id].default_size;
  if (!(n.flags & NF_VISIBLE)) skip;
  ```
- [ ] **2.2** Idem para aristas: estilo y color del `RelationType`. Skip si `!(EF_VISIBLE)` o si los dos endpoints no son visibles.
- [ ] **2.3** Mientras tanto, todavia NO renderizamos shapes (eso es 0049f) — todos como circulo. Pero el dispatch ya esta cableado.

### Fase 3 — Force layout

- [ ] **3.1** En `graph_force_layout.cpp`, sustituir `n.pinned` por `(n.flags & NF_PINNED)`.

### Fase 4 — Viewport

- [ ] **4.1** En `graph_viewport.cpp`, setear `flags |= NF_HOVERED` y `flags |= NF_SELECTED` en lugar de los campos actuales.
- [ ] **4.2** Limpiar el flag al cambiar el target (clear-then-set).

### Fase 5 — Migrar `demos_graph`

- [ ] **5.1** Crear un `EntityType` y un `RelationType` por defecto (paleta antigua → 8 entity types, 1 relation type).
- [ ] **5.2** Asignar `type_id` por cluster como antes.
- [ ] **5.3** Setear `flags = NF_VISIBLE` en cada nodo creado y `EF_VISIBLE` en cada arista.
- [ ] **5.4** Ejecutar y verificar visual identico al pre-cambio.

### Fase 6 — Tests

- [ ] **6.1** `cpp/tests/test_graph_types.cpp`: helpers, defaults, flag manipulation, lookup color/shape con/sin override.
- [ ] **6.2** Visual golden de `demos_graph` regenerado si pixel-diff > tolerancia (no deberia).

### Fase 7 — Cleanup

- [ ] Bump version `graph_types`, `graph_renderer`, `graph_force_layout`, `graph_viewport`.
- [ ] `fn index`.
- [ ] Commit `feat(viz): graph_types modelo extendido + EntityType/RelationType + flags`.

## Criterio de done

- [ ] `demos_graph` visualmente identico al pre-cambio.
- [ ] Tests Catch2 verdes.
- [ ] Visual golden ok.
- [ ] `fn show graph_node_cpp_viz` (y los otros tipos) reflejan el nuevo schema.

## Riesgos

| Riesgo | Mitigacion |
|---|---|
| Tamaño del struct sube | `GraphNode` ~40 bytes y `GraphEdge` ~24 bytes — aceptable para 100k entidades (~4 MB) |
| Migrar `demos_graph` rompe golden | Regenerar golden si la diff es solo cosmetica; investigar si es semantica |
| Otros consumers ocultos | Solo `demos_graph` consume hoy — verificado con grep antes de mergear |
