Files
fn_registry/cpp/tests/test_graph_viewport.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

105 lines
3.3 KiB
C++

// Smoke tests for graph_viewport (issue 0049i).
// El widget completo necesita un contexto OpenGL + ImGui — eso vive en el
// demo y en el test visual. Aqui solo cubrimos los helpers de seleccion
// (logica pura sobre GraphData / GraphViewportState) que no dependen de GL
// ni de ImGui frame state.
#define CATCH_CONFIG_MAIN
#include "catch_amalgamated.hpp"
#include "viz/graph_viewport.h"
#include "viz/graph_types.h"
#include <vector>
namespace {
struct TG {
std::vector<GraphNode> nodes;
GraphData data{};
void make(int N) {
nodes.clear();
for (int i = 0; i < N; ++i) nodes.push_back(graph_node());
data.nodes = nodes.data();
data.node_count = (int)nodes.size();
data.node_capacity = (int)nodes.capacity();
data.edges = nullptr; data.edge_count = 0; data.edge_capacity = 0;
data.types = nullptr; data.type_count = 0;
data.rel_types = nullptr; data.rel_type_count = 0;
}
};
} // namespace
TEST_CASE("selection: add sets NF_SELECTED and selected_node", "[viewport][selection]") {
TG g; g.make(5);
GraphViewportState st;
graph_viewport_add_to_selection(g.data, st, 2);
REQUIRE(st.selection.size() == 1);
REQUIRE(st.selection[0] == 2);
REQUIRE(st.selected_node == 2);
REQUIRE((g.nodes[2].flags & NF_SELECTED) != 0);
// Anadir el mismo no duplica
graph_viewport_add_to_selection(g.data, st, 2);
REQUIRE(st.selection.size() == 1);
graph_viewport_add_to_selection(g.data, st, 4);
REQUIRE(st.selection.size() == 2);
REQUIRE(st.selected_node == 4);
}
TEST_CASE("selection: clear unsets all NF_SELECTED", "[viewport][selection]") {
TG g; g.make(3);
GraphViewportState st;
graph_viewport_add_to_selection(g.data, st, 0);
graph_viewport_add_to_selection(g.data, st, 1);
graph_viewport_add_to_selection(g.data, st, 2);
graph_viewport_clear_selection(g.data, st);
REQUIRE(st.selection.empty());
REQUIRE(st.selected_node == -1);
for (int i = 0; i < 3; ++i) {
REQUIRE((g.nodes[i].flags & NF_SELECTED) == 0);
}
}
TEST_CASE("selection: toggle adds then removes", "[viewport][selection]") {
TG g; g.make(3);
GraphViewportState st;
graph_viewport_toggle_selection(g.data, st, 1);
REQUIRE(graph_viewport_is_selected(st, 1));
REQUIRE((g.nodes[1].flags & NF_SELECTED) != 0);
graph_viewport_toggle_selection(g.data, st, 1);
REQUIRE_FALSE(graph_viewport_is_selected(st, 1));
REQUIRE((g.nodes[1].flags & NF_SELECTED) == 0);
REQUIRE(st.selected_node == -1);
}
TEST_CASE("selection: out-of-range indices are ignored", "[viewport][selection]") {
TG g; g.make(2);
GraphViewportState st;
graph_viewport_add_to_selection(g.data, st, -1);
graph_viewport_add_to_selection(g.data, st, 5);
graph_viewport_toggle_selection(g.data, st, -3);
REQUIRE(st.selection.empty());
REQUIRE(st.selected_node == -1);
}
TEST_CASE("selection: is_selected reports membership", "[viewport][selection]") {
TG g; g.make(4);
GraphViewportState st;
graph_viewport_add_to_selection(g.data, st, 0);
graph_viewport_add_to_selection(g.data, st, 3);
REQUIRE(graph_viewport_is_selected(st, 0));
REQUIRE_FALSE(graph_viewport_is_selected(st, 1));
REQUIRE_FALSE(graph_viewport_is_selected(st, 2));
REQUIRE(graph_viewport_is_selected(st, 3));
}