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>
This commit is contained in:
@@ -3,20 +3,20 @@ name: graph_viewport
|
||||
kind: component
|
||||
lang: cpp
|
||||
domain: viz
|
||||
version: "1.1.0"
|
||||
version: "1.2.0"
|
||||
purity: impure
|
||||
signature: "bool graph_viewport(const char* id, GraphData& graph, GraphViewportState& state, ImVec2 size)"
|
||||
description: "Widget ImGui completo para visualizacion interactiva de grafos con pan, zoom, hover, seleccion y layout en vivo"
|
||||
tags: [graph, viewport, imgui, interactive, pan, zoom, dashboard]
|
||||
tags: [graph, viewport, imgui, interactive, pan, zoom, dashboard, lasso, multi-select]
|
||||
uses_functions: ["graph_force_layout_cpp_viz", "graph_renderer_cpp_viz", "graph_spatial_hash_cpp_core"]
|
||||
tested: true
|
||||
tests: ["selection add/clear/toggle/is_selected", "out-of-range indices ignored"]
|
||||
test_file_path: "cpp/tests/test_graph_viewport.cpp"
|
||||
uses_types: ["GraphData_cpp_viz"]
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: [imgui]
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "cpp/functions/viz/graph_viewport.cpp"
|
||||
framework: imgui
|
||||
props:
|
||||
@@ -36,6 +36,10 @@ props:
|
||||
type: "ImVec2"
|
||||
required: false
|
||||
description: "Tamanio del widget en pixeles. ImVec2(0,0) usa todo el espacio disponible."
|
||||
- name: cb
|
||||
type: "GraphViewportCallbacks"
|
||||
required: false
|
||||
description: "Callbacks opcionales para right-click (on_context_menu) y double-click (on_double_click). Se invocan dentro del frame ImGui."
|
||||
emits: []
|
||||
has_state: true
|
||||
params:
|
||||
@@ -47,7 +51,9 @@ params:
|
||||
desc: "Estado persistente: camara (cam_x, cam_y, zoom), nodo seleccionado/hovereado, renderer GPU, spatial hash. Alojado por el caller."
|
||||
- name: size
|
||||
desc: "Tamanio del widget en pixeles. (0,0) ocupa todo el espacio disponible en la ventana ImGui."
|
||||
output: "true si hubo alguna interaccion del usuario en el frame actual (hover, click, drag, zoom, teclado)"
|
||||
- name: cb
|
||||
desc: "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."
|
||||
output: "true si hubo alguna interaccion del usuario en el frame actual (hover, click, drag, zoom, teclado, lasso, callbacks)"
|
||||
---
|
||||
|
||||
# graph_viewport
|
||||
@@ -86,10 +92,15 @@ La camara usa coordenadas del espacio del grafo:
|
||||
|
||||
| Accion | Control |
|
||||
|--------|---------|
|
||||
| Pan | Boton medio o derecho + arrastrar |
|
||||
| Pan | Boton medio o derecho + arrastrar (sobre area vacia) |
|
||||
| Zoom | Rueda del raton (hacia el cursor) |
|
||||
| Seleccionar nodo | Click izquierdo |
|
||||
| Arrastrar nodo | Click izquierdo sobre nodo |
|
||||
| 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 |
|
||||
|
||||
@@ -113,6 +124,7 @@ El renderer OpenGL y el spatial hash se crean en el primer frame. La camara se a
|
||||
|
||||
## 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
|
||||
|
||||
Reference in New Issue
Block a user