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>
This commit is contained in:
@@ -0,0 +1,187 @@
|
||||
# 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 |
|
||||
Reference in New Issue
Block a user