Files
fn_registry/cpp/functions/viz/graph_force_layout.md
T
egutierrez 958189227d chore(registry): notes en huerfanas usadas por framework/apps
Auditoria del issue 0044: anota en notes: el contexto de consumo de
huerfanos que no pueden registrarse en uses_functions porque sus
consumidores no son funciones del registry:
- consumido por cpp/framework/app_base.cpp (framework no indexado)
- consumido por cpp/apps/{shaders_lab,chart_demo,text_editor_smoke}/main.cpp
- scaffolding/demo en primitives_gallery

31 huerfanas anotadas. Las que quedan en uses_functions=[] tras esto
son hojas legitimas (no llaman a nada) o realmente sin uso (lista
DEAD reportada en el issue 0044).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 23:40:51 +02:00

3.4 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_force_layout function cpp viz 1.0.0 pure float graph_force_layout_step(GraphData& graph, const ForceLayoutConfig& config) Layout force-directed con aproximacion Barnes-Hut para grafos grandes, ejecuta un paso de simulacion por llamada
graph
layout
force-directed
barnes-hut
physics
gpu
GraphData_cpp_viz
false
false
cpp/functions/viz/graph_force_layout.cpp imgui
name desc
graph Referencia al grafo (GraphData) cuyos nodos se actualizan in-place. Modifica x, y, vx, vy de cada nodo no pinned.
name desc
config Parametros de la simulacion: repulsion (fuerza coulombiana), attraction (spring constant), damping (decay de velocidad), theta (precision Barnes-Hut 0=exacto/1=rapido), gravity (atraccion al centro), max_velocity, iterations.
Energia cinetica total (suma de |v|^2). Cuando cae por debajo de un umbral elegido por el caller, el layout ha convergido y se puede dejar de llamar. scaffolding/demo en primitives_gallery

graph_force_layout

Implementa el algoritmo de layout force-directed clasico (Fruchterman-Reingold / Eades) con aproximacion Barnes-Hut O(n log n) para escalar a grafos de miles de nodos.

Algoritmo

Cada llamada a graph_force_layout_step ejecuta config.iterations pasos. Un paso:

  1. Construccion del quadtree (Barnes-Hut): se calcula el bounding box de las posiciones actuales, se construye un quadtree flat en quad_pool (sin allocaciones por nodo). Cada celda acumula centro de masa y masa total.
  2. Repulsion: para cada nodo se recorre el quadtree. Si el cociente cell_size / distance < theta, la celda se trata como una sola masa puntual (multipolo de orden 0). Si no, se desciende a los hijos. Con theta=0 es O(n²) exacto; con theta=0.5 es O(n log n).
  3. Atraccion: para cada arista (s, t), fuerza de Hooke F = k * dist * weight en la direccion del arco.
  4. Gravedad: fuerza proporcional a la distancia al origen, evita que el grafo derive fuera de pantalla.
  5. Integracion: v = v * damping + F, pos += v, con clamping de velocidad.
  6. Nodos con pinned = true no se mueven en ningun paso.

Funciones auxiliares

// Randomizar posiciones para empezar la simulacion
graph_force_layout_reset(graph, 200.0f);

// Layout circular instantaneo (sin iteracion)
graph_layout_circular(graph, 150.0f);

// Layout en grid instantaneo
graph_layout_grid(graph, 25.0f);

Ejemplo de uso tipico (loop ImGui)

static ForceLayoutConfig cfg;
static bool running = true;

if (running) {
    float energy = graph_force_layout_step(my_graph, cfg);
    if (energy < 0.01f) running = false; // convergido
}

Notas de implementacion

  • El quadtree usa un pool estatico de 1 << 20 (~1M) celdas. Para grafos de >500K nodos se recomienda reducir MAX_QUAD_NODES o aumentarlo segun memoria disponible.
  • La pila de traversal en quad_force es tambien estatica (static int stack[]); no es thread-safe si se llama desde multiples hilos simultaneamente.
  • graph_force_layout_reset usa rand(). Para reproducibilidad llama srand(seed) antes.
  • Los buffers de fuerza (fx_buf, fy_buf) se realocan una sola vez cuando el conteo de nodos supera la capacidad previa; en el uso normal (tamano fijo) no hay allocaciones por frame.