fad4006f60
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6.5 KiB
6.5 KiB
id, title, status, type, domain, scope, priority, depends, blocks, related, created, updated, tags
| id | title | status | type | domain | scope | priority | depends | blocks | related | created | updated | tags |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0049i | `graph_layouts` (radial, hierarchical, fixed) + viewport extendido | completado | feature | multi-app | media | 2026-05-17 | 2026-05-17 |
0049i — graph_layouts (radial, hierarchical, fixed) + viewport extendido
Metadata
| Campo | Valor |
|---|---|
| ID | 0049i |
| Estado | pendiente |
| Prioridad | media |
| Tipo | feature — parte de #0049 |
Dependencias
Bloqueada por: 0049e (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
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
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_PINNEDmientras 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 decircular/griddesdegraph_force_layout.cpp. - 1.2 Implementar
layout_radial: BFS desderoot_node, posicionar cada hop k en un circulo de radiok * 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::AddRectpara 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
GraphViewportCallbacksy wirearon_context_menu(right-click) +on_double_click. - 3.2 Documentar en el
.mdque 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 queNF_PINNEDno se mueve; queradialdistribuye 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_layouts1.0.0 (nuevo),graph_viewport1.x → 1.x+1. - Documentar
params/outputen el.mdpara 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 |