--- name: graph_layouts kind: function lang: cpp domain: viz version: "1.0.0" purity: pure signature: "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&)" description: "Conjunto de layouts estaticos (no iterativos) para GraphData: grid, circular, random, radial, hierarchical (Sugiyama heuristico), fixed. Todos respetan NF_PINNED." tags: [graph, layout, static, radial, hierarchical, sugiyama, osint] uses_functions: [] uses_types: ["GraphData_cpp_viz"] returns: [] returns_optional: false error_type: "" imports: [] tested: true tests: ["grid centers and respects pin", "circular places on circle", "radial root at center, hop ring", "hierarchical levels by longest path", "fixed is no-op"] test_file_path: "cpp/tests/test_graph_layouts.cpp" file_path: "cpp/functions/viz/graph_layouts.cpp" framework: imgui params: - name: graph desc: "Referencia al grafo (GraphData) cuyos nodos se reposicionan in-place. Mutaciones: x, y, vx=0, vy=0 por nodo no pinned." - name: spacing desc: "(layout_grid) Distancia en world units entre celdas adyacentes de la cuadricula." - name: radius desc: "(layout_circular) Radio del circulo unico en world units." - name: spread desc: "(layout_random) Mitad del lado del cuadrado en el que se distribuyen aleatoriamente las posiciones (rango [-spread, spread])." - name: root_node desc: "(layout_radial) Indice del nodo raiz. BFS desde aqui asigna el hop. Si es invalido, usa 0." - name: ring_spacing desc: "(layout_radial) Distancia radial entre anillos de hops consecutivos." - name: direction desc: "(layout_hierarchical) Orientacion: 0=LR (left->right), 1=RL, 2=TB (top->bottom), 3=BT." - name: layer_spacing desc: "(layout_hierarchical) Distancia entre niveles consecutivos en el eje del layout." - name: node_spacing desc: "(layout_hierarchical) Distancia entre nodos del mismo nivel en el eje transversal." output: "Ninguno (void). Los nodos no pinned se mueven in-place; nodos con NF_PINNED conservan posicion y velocidad." notes: "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 ```cpp 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: 1. Niveles por longest-path BFS desde nodos sin in-edges. 2. Reduccion de cruces greedy: cada nivel `L>0` se reordena por el baricentro de los indices de sus padres en el nivel `L-1`. 3. 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. ```cpp 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_circular` y `graph_layout_grid` siguen en `graph_force_layout.h` y delegan aqui. Codigo nuevo debe usar el namespace `graph::`. - `layout_radial` y `layout_hierarchical` construyen una lista de adyacencia temporal `O(V+E)` por llamada. Para grafos enormes (>>1M aristas) considerar cachear la adyacencia entre llamadas.