--- name: graph_viewport kind: component lang: cpp domain: viz version: "1.0.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] uses_functions: ["graph_renderer_cpp_viz", "graph_force_layout_cpp_viz", "graph_spatial_hash_cpp_core"] 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: - name: id type: "const char*" required: true description: "Identificador unico del widget ImGui" - name: graph type: "GraphData&" required: true description: "Referencia al grafo (lectura de datos, escritura de posiciones al drag)" - name: state type: "GraphViewportState&" required: true description: "Estado persistente del viewport (camera, seleccion, renderer). Debe vivir mas que los frames." - name: size type: "ImVec2" required: false description: "Tamanio del widget en pixeles. ImVec2(0,0) usa todo el espacio disponible." emits: [] has_state: true params: - name: id desc: "Identificador unico del widget ImGui. Debe ser estable entre frames." - name: graph desc: "Grafo a visualizar. Las posiciones de nodos se modifican al arrastrar." - name: state 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)" --- # 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 ```cpp // 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 | | Zoom | Rueda del raton (hacia el cursor) | | Seleccionar nodo | Click izquierdo | | Arrastrar nodo | Click izquierdo sobre nodo | | 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 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`.