Files
fn_registry/cpp/functions/viz/graph_viewport_selection.cpp
T
egutierrez 7644a50d00 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

46 lines
1.7 KiB
C++

// Helpers de multi-seleccion del viewport (issue 0049i). Logica pura sobre
// GraphData + GraphViewportState — sin ImGui ni OpenGL. Vive en su propio TU
// para que los tests unitarios puedan ejercerla sin pintar nada.
#include "viz/graph_viewport.h"
#include "viz/graph_types.h"
#include <algorithm>
void graph_viewport_clear_selection(GraphData& graph, GraphViewportState& state) {
for (int idx : state.selection) {
if (idx >= 0 && idx < graph.node_count) {
graph.nodes[idx].flags &= ~NF_SELECTED;
}
}
state.selection.clear();
state.selected_node = -1;
}
bool graph_viewport_is_selected(const GraphViewportState& state, int node_idx) {
return std::find(state.selection.begin(), state.selection.end(), node_idx)
!= state.selection.end();
}
void graph_viewport_add_to_selection(GraphData& graph, GraphViewportState& state, int node_idx) {
if (node_idx < 0 || node_idx >= graph.node_count) return;
if (graph_viewport_is_selected(state, node_idx)) return;
state.selection.push_back(node_idx);
graph.nodes[node_idx].flags |= NF_SELECTED;
state.selected_node = node_idx;
}
void graph_viewport_toggle_selection(GraphData& graph, GraphViewportState& state, int node_idx) {
if (node_idx < 0 || node_idx >= graph.node_count) return;
auto it = std::find(state.selection.begin(), state.selection.end(), node_idx);
if (it != state.selection.end()) {
state.selection.erase(it);
graph.nodes[node_idx].flags &= ~NF_SELECTED;
state.selected_node = state.selection.empty() ? -1 : state.selection.back();
} else {
state.selection.push_back(node_idx);
graph.nodes[node_idx].flags |= NF_SELECTED;
state.selected_node = node_idx;
}
}