4a0750445c
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>
85 lines
3.5 KiB
C++
85 lines
3.5 KiB
C++
#pragma once
|
|
#include "imgui.h"
|
|
#include <vector>
|
|
|
|
struct GraphData;
|
|
struct GraphRenderer;
|
|
struct SpatialHash;
|
|
|
|
// Callbacks opcionales del viewport. Se invocan dentro del frame ImGui — el
|
|
// caller puede abrir popups (`ImGui::OpenPopup`) o cambiar estado de la app.
|
|
// Cualquier puntero NULL se ignora.
|
|
struct GraphViewportCallbacks {
|
|
void (*on_context_menu)(int node_idx, ImVec2 screen_pos, void* user) = nullptr;
|
|
void (*on_double_click)(int node_idx, void* user) = nullptr;
|
|
void* user = nullptr;
|
|
};
|
|
|
|
// Persistent state for graph_viewport widget. Create one per viewport and keep
|
|
// alive across frames.
|
|
struct GraphViewportState {
|
|
// Camera
|
|
float cam_x = 0.0f, cam_y = 0.0f;
|
|
float zoom = 1.0f;
|
|
float zoom_min = 0.01f, zoom_max = 50.0f;
|
|
|
|
// Interaction result (read after calling graph_viewport each frame)
|
|
int hovered_node = -1; // node index under cursor, -1 if none
|
|
int selected_node = -1; // legacy: ultimo nodo clicado, -1 si ninguno
|
|
bool is_dragging = false;
|
|
|
|
// Multi-seleccion (issue 0049i). `selection` mantiene los indices de
|
|
// todos los nodos seleccionados (orden de insercion). El flag NF_SELECTED
|
|
// se mantiene sincronizado con esta lista.
|
|
std::vector<int> selection;
|
|
|
|
// Lasso (issue 0049i). Activado durante Shift+Drag sobre area vacia.
|
|
// Coordenadas en world space.
|
|
bool lasso_active = false;
|
|
ImVec2 lasso_start = ImVec2(0.0f, 0.0f);
|
|
ImVec2 lasso_end = ImVec2(0.0f, 0.0f);
|
|
|
|
// Layout
|
|
bool layout_running = true; // animate force layout each frame
|
|
float layout_energy = 0.0f; // kinetic energy from last step
|
|
|
|
// Internal — managed by graph_viewport / graph_viewport_destroy
|
|
GraphRenderer* renderer = nullptr;
|
|
SpatialHash* spatial = nullptr;
|
|
bool initialized = false;
|
|
|
|
// Widget pixel dimensions tracked for resize detection
|
|
int render_w = 0, render_h = 0;
|
|
|
|
// Node being dragged (-1 = none). Si hay multi-seleccion, todos los
|
|
// seleccionados se mueven solidariamente con el delta del raton.
|
|
int drag_node = -1;
|
|
// Posicion world del raton al iniciar el drag (issue 0049i).
|
|
float drag_anchor_x = 0.0f;
|
|
float drag_anchor_y = 0.0f;
|
|
};
|
|
|
|
// Main viewport widget. Call every ImGui frame.
|
|
// id: unique ImGui widget identifier
|
|
// graph: mutable graph data (node positions updated on drag)
|
|
// state: persistent state (camera, selection, GPU renderer); must outlive frames
|
|
// size: widget size in pixels — ImVec2(0,0) uses all available space
|
|
// cb: callbacks opcionales (right-click, double-click). Default = sin callbacks.
|
|
// Returns true if any user interaction occurred (hover, click, drag, zoom).
|
|
bool graph_viewport(const char* id, GraphData& graph, GraphViewportState& state,
|
|
ImVec2 size = ImVec2(0.0f, 0.0f),
|
|
const GraphViewportCallbacks& cb = GraphViewportCallbacks{});
|
|
|
|
// Release GPU resources. Call once when done with the viewport.
|
|
void graph_viewport_destroy(GraphViewportState& state);
|
|
|
|
// Fit camera to current graph bounds with 10% padding.
|
|
void graph_viewport_fit(GraphData& graph, GraphViewportState& state);
|
|
|
|
// Helpers de multi-seleccion (puros respecto al state). Mantienen
|
|
// NF_SELECTED sincronizado.
|
|
void graph_viewport_clear_selection(GraphData& graph, GraphViewportState& state);
|
|
void graph_viewport_add_to_selection(GraphData& graph, GraphViewportState& state, int node_idx);
|
|
void graph_viewport_toggle_selection(GraphData& graph, GraphViewportState& state, int node_idx);
|
|
bool graph_viewport_is_selected(const GraphViewportState& state, int node_idx);
|