02b4141cc1
Issue 0049c. Tres optimizaciones internas en graph_renderer.cpp + un
helper puro en graph_force_layout para detectar convergencia. API publica
intacta — solo cambian el layout interno de los buffers, el shader y
los costes por frame.
1. RGBA8 color packing
- El instance buffer de nodos pasa de (x,y,size,r,g,b,a) 28B a
(x,y,size,color_u32) 16B (-43%). Aristas: 24B → 12B/vertex (-50%).
- Shaders desempaquetan con bit shifts (compatible GL 3.30+, no
necesita unpackUnorm4x8 que es 4.20+).
- Helpers expuestos: pack_rgba8 / unpack_rgba8 / modulate_alpha_rgba8
en graph_renderer.h. Los GraphNode.color y la paleta ya tenian el
layout correcto (R en LSB), asi que CPU ahora pasa el uint32 directo
sin convertir a 4 floats por nodo y por frame.
2. Capacity-tracked streaming buffers
- Sustituye el doble glBufferData de antes por:
glBufferData(NULL, capacity, STREAM_DRAW) // orphan + reserva
glBufferSubData(0, used_bytes, data) // solo lo usado
- capacity crece x2 cuando hace falta (inicial 4096 nodos /
8192 vertices de aristas) → reallocaciones en O(log N).
- Staging CPU (NodeInstance* / EdgeVertex*) reusado entre frames con
realloc, no malloc/free per frame.
3. Frustum cull (CPU-side)
- AABB del viewport en world coords con margen 10%.
- Aristas: skip si AABB del segmento no intersecta el viewport.
- Nodos: solo los visibles entran al instance buffer; visible_count
es el N que pasa a glDrawArraysInstanced. Pop-in de borde mitigado
por el margen.
4. graph_force_layout_should_pause(low_frames, min_consecutive)
- Helper puro: el caller mantiene el contador, la funcion solo
decide si parar. Reemplaza la rama inline en demos_graph.cpp.
- Test Catch2 con secuencias artificiales.
Tests: test_graph_pack_rgba8 (16401 asserts, 4 cases — roundtrip exhaustivo
+ alpha modulation + clamp). test_graph_should_pause (3 cases, 14 asserts).
Los 29 tests del cpp/tests/ siguen verdes (incluido test_visual con goldens).
Bump versiones:
- graph_renderer 1.1.0 → 1.2.0
- graph_force_layout 1.0.0 → 1.1.0 (tested: true via should_pause test)
44 lines
2.0 KiB
C
44 lines
2.0 KiB
C
#pragma once
|
|
|
|
struct GraphData; // forward declare
|
|
|
|
struct ForceLayoutConfig {
|
|
float repulsion = 500.0f; // repulsion strength between all nodes
|
|
float attraction = 0.01f; // spring constant for edges
|
|
float damping = 0.85f; // velocity decay per step
|
|
float min_distance = 1.0f; // minimum distance (avoid division by zero)
|
|
float theta = 0.5f; // Barnes-Hut threshold (0 = exact, 1 = fast)
|
|
float gravity = 0.1f; // pull toward center (prevents drift)
|
|
float max_velocity = 50.0f; // cap velocity per axis
|
|
int iterations = 1; // steps per call
|
|
};
|
|
|
|
// Perform one (or more) steps of force-directed layout.
|
|
// Modifies node positions (x, y) and velocities (vx, vy) in-place.
|
|
// Returns the total kinetic energy (sum of |v|^2). When energy < threshold,
|
|
// layout has converged.
|
|
float graph_force_layout_step(GraphData& graph, const ForceLayoutConfig& config = {});
|
|
|
|
// Reset: randomize positions within [-spread, spread], zero velocities.
|
|
void graph_force_layout_reset(GraphData& graph, float spread = 200.0f);
|
|
|
|
// Preset layouts (non-iterative, instant positioning)
|
|
void graph_layout_circular(GraphData& graph, float radius = 100.0f);
|
|
void graph_layout_grid(GraphData& graph, float spacing = 20.0f);
|
|
|
|
// Auto-pause helper. Pure: el caller mantiene `consecutive_low_frames` y se
|
|
// encarga de incrementarlo / ponerlo a cero cada frame.
|
|
//
|
|
// Patron de uso tipico:
|
|
// static int low = 0;
|
|
// float energy = graph_force_layout_step(g, cfg);
|
|
// float per_node = g.node_count > 0 ? energy / g.node_count : 0.0f;
|
|
// if (per_node < threshold) low++; else low = 0;
|
|
// if (graph_force_layout_should_pause(low, min_consecutive)) running = false;
|
|
//
|
|
// Devuelve `true` si la energia ha caido por debajo del umbral durante al
|
|
// menos `min_consecutive` frames consecutivos. La firma toma `low_frames`
|
|
// directamente (en lugar de manejar el contador internamente) para que la
|
|
// funcion sea pura — facil de testear y sin estado oculto.
|
|
bool graph_force_layout_should_pause(int consecutive_low_frames, int min_consecutive);
|