Repository navigation
Expand file tree
/
Copy pathLayoutEngine.h
More file actions
132 lines (107 loc) · 5.88 KB
/
Copy pathLayoutEngine.h
File metadata and controls
132 lines (107 loc) · 5.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#pragma once
#include "DataModel.h"
#include <map>
#include <unordered_map>
#include <set>
// Forward declare ImGui types (avoid full include in header)
struct ImVec2;
struct ImRect;
////////////////////////////////////////////////////////////////////////////////
// LayoutEngine - Computes node positions (separation between data and view)
// Can be swapped with different layout algorithms (grid, force-directed, etc.)
////////////////////////////////////////////////////////////////////////////////
class LayoutEngine {
public:
std::map<int, ImVec2> nodePositions_;
std::map<int, ImVec2> nodeSizes_;
// Expand/collapse state (view state, not data)
std::set<int> expandedNodes_; // Set of node IDs that are currently expanded
// Expansion order: most-recently-expanded parent is at the BACK. Used as z-order for
// intersecting subgraph boundaries (topmost = last expanded). Kept in sync with expandedNodes_.
std::vector<int> expandOrder_;
// Z-order rank of an expanded parent (higher = more recently expanded = on top). -1 if not expanded.
int GetExpandOrder(int nodeId) const;
// Edge rendering mode (controls curve type through dummy nodes)
// 0=BezierDirect, 1=PolylineDummy, 2=CatmullRomDummy (default), 3=BezierChain
int edgeRenderMode_ = 2; // Default to Catmull-Rom splines
// Dummy nodes for long edges (internal to layout algorithm)
struct DummyNode {
int id; // Unique ID for this dummy (negative to avoid collision with real nodes)
int originalSrcId; // Real source node of the original edge
int originalDstId; // Real target node of the original edge
int rank; // Layer this dummy sits in
int prevDummyId; // Previous dummy in chain (-1 if connects to real source)
int nextDummyId; // Next dummy in chain (-1 if connects to real target)
};
std::vector<DummyNode> dummyNodes_;
std::unordered_map<int, DummyNode> dummyNodeById_; // Fast O(1) lookup by dummy ID
int nextDummyId_ = -1; // Counter for generating unique dummy IDs (negative)
// Grid layout parameters (matching current CreateNodesGrid)
float nodeWidth_ = 150.0f;
float nodeHeight_ = 200.0f;
float horizontalSpacing_ = 50.0f;
float verticalSpacing_ = 50.0f;
int nodesPerRow_ = 100;
public:
LayoutEngine() = default;
~LayoutEngine() = default;
// Grid layout - simple positioning in rows and columns
void ComputeGridLayout(const GraphData::DataModel& model);
// Layered layout - hierarchical layout following edge direction
// Based on simplified Sugiyama algorithm (topological sort + crossing reduction)
void ComputeLayoutLayered(const GraphData::DataModel& model);
// Hierarchy management (expand/collapse state + active node calculation)
void ToggleExpand(int nodeId);
void ExpandNode(int nodeId);
void CollapseNode(int nodeId);
bool IsExpanded(int nodeId) const;
std::vector<const GraphData::Node*> GetActiveNodes(const GraphData::DataModel& model) const;
bool IsNodeActive(int nodeId, const GraphData::DataModel& model) const;
// Position queries
ImVec2 GetNodePosition(int nodeId) const;
ImVec2 GetNodeSize(int nodeId) const;
bool HasNodePosition(int nodeId) const;
// Dirty region detection (for expand/collapse optimization. Defintely more room for optmization)
ImRect GetNodeBounds(int nodeId) const;
std::vector<ImRect> GetChangedRegions(const std::map<int, ImVec2>& oldPositions,
const std::vector<GraphData::Connection>& connections) const;
// Utility
ImRect GetWorldBounds() const;
void Clear();
// Dummy node helpers
std::vector<ImVec2> GetEdgeRoutePoints(int srcId, int dstId) const;
bool IsDummyNode(int nodeId) const { return nodeId < 0; }
// Edge rendering mode control
void SetEdgeRenderMode(int mode) { edgeRenderMode_ = mode; }
int GetEdgeRenderMode() const { return edgeRenderMode_; }
// Configuration (for grid layout)
void SetGridParameters(float nodeW, float nodeH, float hSpacing, float vSpacing);
void SetGridNodesPerRow(int perRow) { nodesPerRow_ = perRow; }
// World size ComputeGridLayout() will produce for nodeCount grid nodes (no model/layout needed).
ImVec2 GridWorldSizeForNodeCount(int nodeCount) const;
// Largest grid node count whose laid-out world fits within maxTiles tiles of tileSize pixels.
// Inverts ComputeGridLayout()'s geometry against a known tile budget (see .cpp for the derivation).
int MaxGridNodesForTileBudget(int maxTiles, int tileSize) const;
// A directed edge between two active node ids (after any substitution the caller did).
struct LayoutEdge { int src; int dst; };
// Result of a Sugiyama layout pass: node id -> position / size. Dummy nodes created for
// long edges appear under their negative ids.
struct LayoutResult {
std::map<int, ImVec2> positions;
std::map<int, ImVec2> sizes;
};
// Lay out ONLY the children of `parentId` (a mini-Sugiyama on the subgraph), placed at
// `origin`. Uses just the internal edges among the children; does NOT touch nodePositions_
// or the global layout. Used by the SPH expand animation to get the children's target
// shape (their fixed homes) without a full relayout. Returns positions/sizes for children
// (+ any dummy waypoints among them).
LayoutResult LayoutSubgraph(const GraphData::DataModel& model, int parentId, ImVec2 origin);
private:
// Shared Sugiyama coordinate assignment (Phases 1-4) used by both full-graph layout
// and per-subgraph expand layout. Builds its own adjacency from nodes + edges, runs the
// algorithm, and returns positions/sizes offset by `origin`.
LayoutResult LayoutNodesInternal(
const std::vector<const GraphData::Node*>& nodes,
const std::vector<LayoutEdge>& edges,
ImVec2 origin);
};