Phase 1 — graph_layouts:
- New module cpp/functions/viz/graph_layouts.{h,cpp,md} v1.0.0
- layout_grid, layout_circular, layout_random (migrated from graph_force_layout.cpp)
- layout_radial: BFS rings from root, hop k -> circle of radius k*ring_spacing
- layout_hierarchical: Sugiyama-style heuristic (longest-path levels + barycenter ordering)
- layout_fixed: no-op
- All respect NF_PINNED. graph_layout_circular/grid kept as deprecated wrappers.
Phase 2-3 — graph_viewport v1.2.0:
- Multi-selection via state.selection (vector<int>); NF_SELECTED kept in sync
- Lasso: Shift+Drag on empty area; AABB hit-test on release
- Drag of N-selection: all selected pinned + moved by mouse delta
- Ctrl+click toggle, Esc clears selection
- Right-click on node -> on_context_menu callback
- Double-click on node -> on_double_click callback
- Helpers exposed: graph_viewport_clear/add_to/toggle/is_selected (own TU for tests)
Phase 4 — tests:
- test_graph_layouts: 12 cases / 364 assertions covering geometry, pin, edges
- test_graph_viewport: 5 cases for selection helpers (pure logic, no GL)
Phase 5 — demo (primitives_gallery):
- Layout combo (force/grid/circular/radial/hierarchical/fixed) + Apply button
- Right-click popup with Pin/Unpin/Add-to-selection
- Status overlay shows [N selected] when selection non-empty
- Updated golden images
Issue moved to dev/issues/completed/.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5.5 KiB
name, kind, lang, domain, version, purity, signature, description, tags, uses_functions, uses_types, returns, returns_optional, error_type, imports, tested, tests, test_file_path, file_path, framework, params, output, notes
| name | kind | lang | domain | version | purity | signature | description | tags | uses_functions | uses_types | returns | returns_optional | error_type | imports | tested | tests | test_file_path | file_path | framework | params | output | notes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| graph_layouts | function | cpp | viz | 1.0.0 | pure | void graph::layout_grid(GraphData&, float spacing); void graph::layout_circular(GraphData&, float radius); void graph::layout_random(GraphData&, float spread); void graph::layout_radial(GraphData&, int root_node, float ring_spacing); void graph::layout_hierarchical(GraphData&, int direction, float layer_spacing, float node_spacing); void graph::layout_fixed(GraphData&) | Conjunto de layouts estaticos (no iterativos) para GraphData: grid, circular, random, radial, hierarchical (Sugiyama heuristico), fixed. Todos respetan NF_PINNED. |
|
|
false | true |
|
cpp/tests/test_graph_layouts.cpp | cpp/functions/viz/graph_layouts.cpp | imgui |
|
Ninguno (void). Los nodos no pinned se mueven in-place; nodos con NF_PINNED conservan posicion y velocidad. | Issue 0049i. layout_circular/layout_grid migrados desde graph_force_layout.cpp; los wrappers deprecados graph_layout_circular/graph_layout_grid siguen funcionando como compat hasta cierre de 0049. |
graph_layouts
Funciones de layout estatico (instantaneo, sin iterar). Todas escriben las
posiciones de los nodos no pinned y cero las velocidades; los nodos con
NF_PINNED no se tocan. Util para inicializar un grafo antes del force layout
o para ofrecer alternativas de visualizacion en TUIs OSINT.
API
namespace graph {
void layout_grid (GraphData&, float spacing = 20.0f);
void layout_circular (GraphData&, float radius = 100.0f);
void layout_random (GraphData&, float spread = 200.0f);
void layout_radial (GraphData&, int root_node = 0,
float ring_spacing = 80.0f);
void layout_hierarchical(GraphData&, int direction = 0,
float layer_spacing = 120.0f,
float node_spacing = 60.0f);
void layout_fixed (GraphData&); // no-op
}
Algoritmos
layout_grid
Cuadricula uniforme. cols = ceil(sqrt(N)), rows = ceil(N/cols). Centro del
grafo en (0,0).
layout_circular
Circulo unico. Cada nodo en el angulo 2*pi*i/N.
layout_random
Posiciones uniformes en [-spread, spread]^2. Usa rand() — llamar
srand(seed) antes para reproducibilidad.
layout_radial
BFS no dirigido desde root_node. Cada hop k se coloca en un circulo de
radio k * ring_spacing. Nodos del mismo hop se distribuyen uniformemente
en su circulo. Las componentes desconectadas van a un anillo extra al final.
Util para vistas de "vecinos a N saltos" tipicas en OSINT.
layout_hierarchical
Sugiyama-style heuristico:
- Niveles por longest-path BFS desde nodos sin in-edges.
- Reduccion de cruces greedy: cada nivel
L>0se reordena por el baricentro de los indices de sus padres en el nivelL-1. - Posiciones: el nivel define el eje principal (X o Y segun
direction), los nodos dentro del nivel se centran en el eje transversal.
No es Sugiyama optimo (eso es un problema NP-hard). Es suficientemente bueno
para grafos OSINT pequenios/medianos: jerarquias Person -> Email -> Domain,
arboles de dependencia, etc.
layout_fixed
No-op. Se mantiene en el conjunto para que un caller pueda escribir un switch que cubra todos los modos sin un caso especial.
Composicion con graph_force_layout
Tipico: layout estatico inicial -> force-directed para refinar.
graph::layout_circular(g, 200.0f); // arranque uniforme
ForceLayoutConfig cfg;
for (int i = 0; i < 300; ++i) {
graph_force_layout_step(g, cfg);
}
Para grafos jerarquicos donde no se quiere que el force layout deshaga la
estructura: pinnar nodos clave (NF_PINNED) tras layout_hierarchical y
dejar que el force solo refine los del medio.
Notas
- Wrappers deprecados:
graph_layout_circularygraph_layout_gridsiguen engraph_force_layout.hy delegan aqui. Codigo nuevo debe usar el namespacegraph::. layout_radialylayout_hierarchicalconstruyen una lista de adyacencia temporalO(V+E)por llamada. Para grafos enormes (>>1M aristas) considerar cachear la adyacencia entre llamadas.