fad4006f60
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
162 lines
6.5 KiB
Markdown
162 lines
6.5 KiB
Markdown
---
|
|
id: "0049i"
|
|
title: "`graph_layouts` (radial, hierarchical, fixed) + viewport extendido"
|
|
status: completado
|
|
type: feature
|
|
domain: []
|
|
scope: multi-app
|
|
priority: media
|
|
depends: []
|
|
blocks: []
|
|
related: []
|
|
created: 2026-05-17
|
|
updated: 2026-05-17
|
|
tags: []
|
|
---
|
|
# 0049i — `graph_layouts` (radial, hierarchical, fixed) + viewport extendido
|
|
|
|
## Metadata
|
|
|
|
| Campo | Valor |
|
|
|-------|-------|
|
|
| **ID** | 0049i |
|
|
| **Estado** | pendiente |
|
|
| **Prioridad** | media |
|
|
| **Tipo** | feature — parte de [#0049](0049-osint-graph-viewer.md) |
|
|
|
|
## Dependencias
|
|
|
|
**Bloqueada por:** [0049e](0049e-graph-types-extended.md) (necesita `flags`).
|
|
|
|
---
|
|
|
|
## Objetivo
|
|
|
|
Consolidar las estrategias de layout estatico en una sola funcion `graph_layouts` (anadiendo `radial`, `hierarchical`, `fixed`), y extender `graph_viewport` con lasso, multi-select acumulativo, drag de seleccion entera y callbacks de menu contextual / double-click.
|
|
|
|
## Contexto
|
|
|
|
Hoy `graph_force_layout.cpp` incluye `graph_layout_circular` y `graph_layout_grid` como helpers. Para OSINT son utiles:
|
|
- **Radial**: arbol con un nodo raiz seleccionado y sus vecinos en circulos concentricos por hop.
|
|
- **Hierarchical** (Sugiyama-style): niveles por tipo o por dependencia (Person → Email → Domain).
|
|
- **Fixed**: no-op, las posiciones las pone el caller.
|
|
|
|
`graph_viewport` ya soporta pan/zoom/click + hit-test. Falta el resto de UX para Maltego.
|
|
|
|
## Arquitectura
|
|
|
|
```
|
|
cpp/functions/viz/
|
|
├── graph_layouts.{h,cpp} # NEW (mueve circular/grid + nuevos)
|
|
├── graph_layouts.md # NEW
|
|
├── graph_viewport.{h,cpp} # MOD: lasso, multi-select, callbacks
|
|
└── graph_viewport.md # MOD: bump
|
|
|
|
cpp/tests/
|
|
├── test_graph_layouts.cpp # NEW
|
|
└── test_graph_viewport.cpp # NEW (smoke)
|
|
```
|
|
|
|
### `graph_layouts` API
|
|
|
|
```cpp
|
|
namespace graph {
|
|
|
|
// Estaticos. Mutan posiciones, respetan NF_PINNED.
|
|
void layout_grid (GraphData&, float spacing);
|
|
void layout_circular (GraphData&, float radius);
|
|
void layout_random (GraphData&, float spread);
|
|
void layout_radial (GraphData&, int root_node, float ring_spacing);
|
|
void layout_hierarchical(GraphData&, int direction); // 0=LR, 1=RL, 2=TB, 3=BT
|
|
void layout_fixed (GraphData&); // no-op
|
|
|
|
} // namespace graph
|
|
```
|
|
|
|
`graph_force_layout.cpp` deja de exportar `_circular`/`_grid` (delegan a `graph_layouts`). Mantener wrappers deprecados un sub-issue maximo, eliminar antes del cierre de 0049.
|
|
|
|
### `graph_viewport` extensiones
|
|
|
|
```cpp
|
|
struct GraphViewportCallbacks {
|
|
void (*on_context_menu)(int node_idx, ImVec2 screen_pos, void* user) = nullptr;
|
|
void (*on_double_click)(int node_idx, void* user) = nullptr;
|
|
void* user = nullptr;
|
|
};
|
|
|
|
struct GraphViewportState {
|
|
// ... existente
|
|
int selected_node; // legacy: ultimo seleccionado
|
|
std::vector<int> selection; // NEW: multi-seleccion
|
|
bool lasso_active;
|
|
ImVec2 lasso_start, lasso_end;
|
|
};
|
|
|
|
// Igual firma que hoy, mas un parametro opcional de callbacks.
|
|
void graph_viewport(const char* id, GraphData&, GraphViewportState&,
|
|
ImVec2 size, const GraphViewportCallbacks& cb = {});
|
|
```
|
|
|
|
Comportamiento:
|
|
- **Click**: limpia seleccion, anade nodo bajo cursor.
|
|
- **Ctrl+Click**: toggle nodo en seleccion.
|
|
- **Shift+Drag (sin nodo bajo cursor)**: lasso. Al soltar, anade los nodos dentro del rect a la seleccion.
|
|
- **Drag con un nodo seleccionado bajo el cursor**: arrastra todos los seleccionados como pinned (set `NF_PINNED` mientras se arrastra; mantener pinned al soltar).
|
|
- **Right-click sobre un nodo**: invoca `on_context_menu(idx, screen_pos, user)` si esta seteado.
|
|
- **Double-click sobre un nodo**: invoca `on_double_click(idx, user)`.
|
|
- **Esc**: limpia seleccion.
|
|
|
|
## Tareas
|
|
|
|
### Fase 1 — `graph_layouts`
|
|
|
|
- [ ] **1.1** Crear `graph_layouts.{h,cpp,md}`. Mover impl de `circular`/`grid` desde `graph_force_layout.cpp`.
|
|
- [ ] **1.2** Implementar `layout_radial`: BFS desde `root_node`, posicionar cada hop k en un circulo de radio `k * ring_spacing`, distribuir uniformemente.
|
|
- [ ] **1.3** Implementar `layout_hierarchical`: BFS levels por longest-path desde nodos sin in-edges; dentro de cada nivel ordenar por minimo cruce (greedy heuristico — no optimo, pero bueno para la UX OSINT).
|
|
- [ ] **1.4** Implementar `layout_fixed`: no-op (recordar que existe la funcion).
|
|
- [ ] **1.5** Todas respetan `NF_PINNED`.
|
|
|
|
### Fase 2 — Viewport multi-select + lasso
|
|
|
|
- [ ] **2.1** En `graph_viewport.cpp`, implementar el comportamiento de la tabla anterior.
|
|
- [ ] **2.2** Lasso: `ImDrawList::AddRect` para feedback visual + AABB hit-test al soltar.
|
|
- [ ] **2.3** Drag de seleccion: pin todos los nodos seleccionados al inicio del drag, aplicar el delta a todos, mantener pinned al soltar.
|
|
|
|
### Fase 3 — Callbacks
|
|
|
|
- [ ] **3.1** Anadir `GraphViewportCallbacks` y wirear `on_context_menu` (right-click) + `on_double_click`.
|
|
- [ ] **3.2** Documentar en el `.md` que el callback se invoca dentro del frame ImGui — el caller puede abrir un popup.
|
|
|
|
### Fase 4 — Tests
|
|
|
|
- [ ] **4.1** `test_graph_layouts`: smoke de cada layout sobre un grafo pequeño; verificar que `NF_PINNED` no se mueve; que `radial` distribuye correctamente.
|
|
- [ ] **4.2** `test_graph_viewport`: setup de un grafo, simular hit-test programatico (no test interactivo, solo helpers puros).
|
|
|
|
### Fase 5 — Demo
|
|
|
|
- [ ] **5.1** Anadir toggle de layout en `demos_graph` (`force | grid | circular | radial | hierarchical | fixed`).
|
|
- [ ] **5.2** Anadir lasso + multi-select visible en el demo (text overlay con count seleccionados).
|
|
|
|
### Fase 6 — Cleanup
|
|
|
|
- [ ] Bump versions: `graph_layouts` 1.0.0 (nuevo), `graph_viewport` 1.x → 1.x+1.
|
|
- [ ] Documentar `params`/`output` en el `.md` para FTS5 search.
|
|
- [ ] `fn index`.
|
|
- [ ] Commit `feat(viz): graph_layouts (radial/hierarchical/fixed) + viewport multi-select+lasso`.
|
|
|
|
## Criterio de done
|
|
|
|
- [ ] Switch entre layouts en el demo es instantaneo.
|
|
- [ ] Lasso visible, multi-seleccion acumulativa funcional.
|
|
- [ ] Drag de N nodos seleccionados los mueve juntos como pinned.
|
|
- [ ] Right-click invoca callback si esta seteado.
|
|
- [ ] Tests verdes.
|
|
|
|
## Riesgos
|
|
|
|
| Riesgo | Mitigacion |
|
|
|---|---|
|
|
| Hierarchical layout se ve mal en grafos densamente cruzados | Aceptable — Sugiyama optimo es un campo entero; el heuristico es para visualizacion OSINT, no publicacion |
|
|
| Multi-select state en GraphViewportState rompe ABI | Es un cambio interno; `selection` es campo nuevo, ok |
|
|
| Drag de seleccion gigante (10k nodos) lagueva | Desactivar fuerzas en pinned ya implica que la GPU no los toca. Drag solo aplica delta — O(N seleccionados) trivial |
|