Files
fn_registry/cpp/functions/viz/graph_viewport.md
T
egutierrez 4a0750445c feat(viz): graph_layouts (radial/hierarchical/fixed) + viewport multi-select+lasso (issue 0049i)
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>
2026-04-29 23:42:31 +02:00

6.5 KiB

name, kind, lang, domain, version, purity, signature, description, tags, uses_functions, tested, tests, test_file_path, uses_types, returns, returns_optional, error_type, imports, file_path, framework, props, emits, has_state, params, output
name kind lang domain version purity signature description tags uses_functions tested tests test_file_path uses_types returns returns_optional error_type imports file_path framework props emits has_state params output
graph_viewport component cpp viz 1.2.0 impure bool graph_viewport(const char* id, GraphData& graph, GraphViewportState& state, ImVec2 size) Widget ImGui completo para visualizacion interactiva de grafos con pan, zoom, hover, seleccion y layout en vivo
graph
viewport
imgui
interactive
pan
zoom
dashboard
lasso
multi-select
graph_force_layout_cpp_viz
graph_renderer_cpp_viz
graph_spatial_hash_cpp_core
true
selection add/clear/toggle/is_selected
out-of-range indices ignored
cpp/tests/test_graph_viewport.cpp
GraphData_cpp_viz
false error_go_core
imgui
cpp/functions/viz/graph_viewport.cpp imgui
name type required description
id const char* true Identificador unico del widget ImGui
name type required description
graph GraphData& true Referencia al grafo (lectura de datos, escritura de posiciones al drag)
name type required description
state GraphViewportState& true Estado persistente del viewport (camera, seleccion, renderer). Debe vivir mas que los frames.
name type required description
size ImVec2 false Tamanio del widget en pixeles. ImVec2(0,0) usa todo el espacio disponible.
name type required description
cb GraphViewportCallbacks false Callbacks opcionales para right-click (on_context_menu) y double-click (on_double_click). Se invocan dentro del frame ImGui.
true
name desc
id Identificador unico del widget ImGui. Debe ser estable entre frames.
name desc
graph Grafo a visualizar. Las posiciones de nodos se modifican al arrastrar.
name desc
state Estado persistente: camara (cam_x, cam_y, zoom), nodo seleccionado/hovereado, renderer GPU, spatial hash. Alojado por el caller.
name desc
size Tamanio del widget en pixeles. (0,0) ocupa todo el espacio disponible en la ventana ImGui.
name desc
cb Callbacks opcionales: on_context_menu(idx, screen_pos, user) en right-click; on_double_click(idx, user) en doble click. Se invocan dentro del frame ImGui — el caller puede llamar OpenPopup.
true si hubo alguna interaccion del usuario en el frame actual (hover, click, drag, zoom, teclado, lasso, callbacks)

graph_viewport

Widget ImGui self-contained para visualizar grafos interactivos. Integra rendering GPU, force-directed layout y hit-testing espacial en una sola llamada por frame.

Uso basico

// Declarar estado persistente (fuera del loop de render)
GraphViewportState vp_state;

// En el loop de render (dentro de una ventana ImGui):
if (graph_viewport("mi_grafo", my_graph, vp_state)) {
    // hubo interaccion este frame
    if (vp_state.selected_node >= 0) {
        auto& n = my_graph.nodes[vp_state.selected_node];
        // mostrar panel de detalle de n
    }
}

// Al terminar:
graph_viewport_destroy(vp_state);

Estado de camara

La camara usa coordenadas del espacio del grafo:

  • cam_x, cam_y: centro de la camara en espacio del grafo
  • zoom: pixeles por unidad de grafo

graph_viewport_fit() centra y ajusta el zoom para que el grafo quepa con 10% de padding.

Controles

Accion Control
Pan Boton medio o derecho + arrastrar (sobre area vacia)
Zoom Rueda del raton (hacia el cursor)
Seleccionar nodo (single) Click izquierdo sobre nodo
Toggle nodo en seleccion Ctrl + Click izquierdo
Lasso (multi-seleccion) Shift + Click izquierdo + arrastrar sobre area vacia
Arrastrar seleccion entera Click izquierdo sobre nodo seleccionado + arrastrar
Menu contextual Click derecho sobre nodo (callback on_context_menu)
Activar (double-click) Doble click sobre nodo (callback on_double_click)
Limpiar seleccion Esc, o click en area vacia
Toggle layout Barra espaciadora
Fit camara F

Force layout

El layout se ejecuta automaticamente cada frame mientras state.layout_running == true. Se detiene solo cuando la energia cinetica cae por debajo de 0.01. Se puede pausar/reanudar con la barra espaciadora.

Los nodos arrastrados se marcan como pinned = true durante el drag, impidiendo que el force layout los mueva. Al soltar, pinned vuelve a false.

Tooltip

Al hacer hover sobre un nodo se muestra un tooltip con: label, id numerico, community, degree (aristas conectadas) y value.

Status bar

En la parte inferior del widget aparece: numero de nodos, aristas, zoom actual, energia del layout y recordatorio de atajos de teclado.

Inicializacion lazy

El renderer OpenGL y el spatial hash se crean en el primer frame. La camara se ajusta automaticamente con graph_viewport_fit en la inicializacion.

Notas de version

  • v1.2 (2026-04-29, issue 0049i): multi-seleccion con state.selection, lasso (Shift+Drag sobre area vacia), drag de seleccion entera (todos los nodos seleccionados pinnean y se mueven juntos), Ctrl+click toggle, Esc limpia seleccion. Callbacks opcionales GraphViewportCallbacks para right-click (menu contextual) y double-click. Los nodos arrastrados se quedan pinned al soltar. Tooltip suprimido durante drag/lasso. Helpers expuestos: graph_viewport_clear_selection, graph_viewport_add_to_selection, graph_viewport_toggle_selection, graph_viewport_is_selected. Tests: cpp/tests/test_graph_viewport.cpp.
  • v1.1 (2026-04-29, issue 0049e): adapta el viewport al modelo extendido. Hover/seleccion ahora se publican tambien como flags |= NF_HOVERED / NF_SELECTED en el grafo (clear-then-set) — los state.hovered_node / selected_node siguen siendo la API estable. El drag usa flags |= NF_PINNED en lugar del campo pinned desaparecido. El tooltip muestra Type (nombre del EntityType si esta) y user_data en lugar de community/value/label/id.

Notas de implementacion

  • Usa ImGui::InvisibleButton con flags para los tres botones del raton, capturando input sin dibujar ningun boton visible.
  • La textura del renderer se muestra con UV volteado en Y (ImVec2(0,1) a ImVec2(1,0)) para corregir la convencion de coordenadas de OpenGL vs ImGui.
  • El spatial hash se reconstruye cada frame desde las posiciones actuales de los nodos, garantizando hit-testing correcto despues de drag o layout.
  • El zoom hacia el cursor mantiene el punto del grafo bajo el cursor fijo en pantalla ajustando cam_x/cam_y.