Skip to content

Commit daa1363

Browse files
committed
feat: auto calculate node tree layout
1 parent 8a9a1c9 commit daa1363

6 files changed

Lines changed: 159 additions & 19 deletions

File tree

Editor/Scripts/GraphView/PlayableGraphView.cs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
using System.Collections.Generic;
2-
using GBG.PlayableGraphMonitor.Editor.Node;
1+
using GBG.PlayableGraphMonitor.Editor.Node;
2+
using System.Collections.Generic;
3+
using UnityEditor;
34
using UnityEditor.Experimental.GraphView;
45
using UnityEditor.Playables;
56
using UnityEngine;
@@ -42,7 +43,7 @@ public void SetPlayableGraph(PlayableGraph playableGraph, bool forceRepaint)
4243

4344
PopulateView();
4445

45-
// FrameAll();
46+
EditorApplication.delayCall += () => FrameAll();
4647
}
4748

4849

@@ -54,6 +55,8 @@ private void ClearView()
5455
}
5556

5657
_playableOutputNodes.Clear();
58+
59+
GraphViewNode.LayoutInfo.Reset();
5760
}
5861

5962
private void PopulateView()
@@ -63,6 +66,7 @@ private void PopulateView()
6366
return;
6467
}
6568

69+
// create nodes
6670
for (int i = 0; i < _playableGraph.GetOutputCount(); i++)
6771
{
6872
var playableOutput = _playableGraph.GetOutput(i);
@@ -72,7 +76,6 @@ private void PopulateView()
7276
{
7377
title = $"{playableOutputTypeName} ({playableOutputEditorName})"
7478
};
75-
playableOutputNode.SetPosition(new Rect(200, 200, 0, 0));
7679
playableOutputNode.AddToContainer(this);
7780

7881
_playableOutputNodes.Add(playableOutputNode);
@@ -82,6 +85,17 @@ private void PopulateView()
8285
{
8386
_playableOutputNodes[i].CreateAndConnectInputNodes();
8487
}
88+
89+
// calculate node layout
90+
var origin = Vector2.zero;
91+
for (int i = 0; i < _playableOutputNodes.Count; i++)
92+
{
93+
var outputNode = _playableOutputNodes[i];
94+
var hierarchySize = outputNode.GetHierarchySize();
95+
outputNode.CalculateLayout(hierarchySize, origin);
96+
97+
origin.y += hierarchySize.y + NodeLayoutInfo.VerticalSpace;
98+
}
8599
}
86100

87101

Editor/Scripts/Node/GraphViewNode.cs

Lines changed: 84 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
using System.Collections.Generic;
2-
using GBG.PlayableGraphMonitor.Editor.GraphView;
32
using UnityEditor.Experimental.GraphView;
4-
using UnityEngine.Assertions;
3+
using UnityEngine;
54
using UGraphView = UnityEditor.Experimental.GraphView.GraphView;
65
using UNode = UnityEditor.Experimental.GraphView.Node;
76

87
namespace GBG.PlayableGraphMonitor.Editor.Node
98
{
109
public abstract class GraphViewNode : UNode
1110
{
11+
internal static NodeLayoutInfo LayoutInfo;
12+
1213
public int Depth { get; }
1314

1415
public IReadOnlyList<Port> InputPorts => InternalInputPorts;
@@ -19,14 +20,90 @@ public abstract class GraphViewNode : UNode
1920

2021
protected List<Port> InternalOutputPorts { get; } = new List<Port>();
2122

22-
protected UGraphView Container { get; set; }
23+
protected UGraphView Container { get; set; }
2324

2425
// public IReadOnlyList<Edge> InputEdges => InternalInputEdges;
2526

2627
protected List<Edge> InternalInputEdges { get; } = new List<Edge>();
2728

28-
protected List<GraphViewNode> ChildNodes { get; } = new List<GraphViewNode>();
29+
protected GraphViewNode Parent { get; private set; }
30+
31+
protected new List<GraphViewNode> Children { get; } = new List<GraphViewNode>();
32+
33+
34+
private Vector2? _hierarchySize;
35+
36+
37+
public Vector2 GetNodeSize()
38+
{
39+
return NodeLayoutInfo.StandardNodeSize;
40+
//return worldBound.size;
41+
}
42+
43+
public Vector2 GetHierarchySize()
44+
{
45+
if (_hierarchySize != null)
46+
{
47+
return _hierarchySize.Value;
48+
}
49+
50+
51+
if (Children.Count == 0)
52+
{
53+
_hierarchySize = GetNodeSize();
54+
return _hierarchySize.Value;
55+
}
56+
57+
if (Children.Count == 1)
58+
{
59+
var selfSize = GetNodeSize();
60+
var childHierarchySize = Children[0].GetHierarchySize();
61+
_hierarchySize = new Vector2
62+
{
63+
x = selfSize.x + childHierarchySize.x + NodeLayoutInfo.HorizontalSpace,
64+
y = selfSize.y + childHierarchySize.y + NodeLayoutInfo.VerticalSpace
65+
};
66+
67+
return _hierarchySize.Value;
68+
}
69+
70+
var hierarchySize = GetNodeSize() + new Vector2(NodeLayoutInfo.HorizontalSpace, 0);
71+
for (int i = 0; i < Children.Count; i++)
72+
{
73+
var childSize = Children[i].GetHierarchySize();
74+
hierarchySize.x += childSize.x;
75+
hierarchySize.y = Mathf.Max(hierarchySize.y, childSize.y);
76+
}
2977

78+
hierarchySize.y += (Children.Count - 1) * NodeLayoutInfo.VerticalSpace;
79+
_hierarchySize = hierarchySize;
80+
81+
return _hierarchySize.Value;
82+
}
83+
84+
public void CalculateLayout(Vector2 treeSize, Vector2 origin)
85+
{
86+
var hierarchySize = GetHierarchySize();
87+
var nodePos = CalculateSubTreeRootPosition(treeSize, hierarchySize, origin);
88+
SetPosition(new Rect(nodePos, Vector2.zero));
89+
90+
origin.x -= GetNodeSize().x - NodeLayoutInfo.HorizontalSpace;
91+
for (int i = 0; i < Children.Count; i++)
92+
{
93+
var childNode = Children[i];
94+
var childHierarchySize = childNode.GetHierarchySize();
95+
childNode.CalculateLayout(treeSize, origin);
96+
97+
origin.y += childHierarchySize.y + NodeLayoutInfo.VerticalSpace;
98+
}
99+
}
100+
101+
public static Vector2 CalculateSubTreeRootPosition(Vector2 treeSize, Vector2 subTreeSize, Vector2 subTreeOrigin)
102+
{
103+
var subTreePos = subTreeOrigin;
104+
subTreePos.y += subTreeSize.y / 2;
105+
return subTreePos;
106+
}
30107

31108
public virtual void AddToContainer(UGraphView container)
32109
{
@@ -48,12 +125,12 @@ public virtual void RemoveFromContainer()
48125
InternalInputEdges.Clear();
49126

50127
// children
51-
foreach (var childNode in ChildNodes)
128+
foreach (var childNode in Children)
52129
{
53130
childNode.RemoveFromContainer();
54131
}
55132

56-
ChildNodes.Clear();
133+
Children.Clear();
57134

58135
Container = null;
59136
}
@@ -71,4 +148,4 @@ protected Port InstantiatePort<TPort>(Direction direction)
71148
return InstantiatePort(Orientation.Horizontal, direction, Port.Capacity.Single, typeof(TPort));
72149
}
73150
}
74-
}
151+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using UnityEngine;
2+
3+
namespace GBG.PlayableGraphMonitor.Editor.Node
4+
{
5+
public struct NodeLayoutInfo
6+
{
7+
public const int HorizontalSpace = 80;
8+
9+
public const int VerticalSpace = 80;
10+
11+
public static readonly Vector2 StandardNodeSize = new Vector2(300, 150);
12+
13+
public int TreeWidth;
14+
15+
public Vector2 Origin;
16+
17+
18+
public void Reset()
19+
{
20+
TreeWidth = 0;
21+
22+
Origin = Vector2.zero;
23+
}
24+
}
25+
}

Editor/Scripts/Node/NodeLayoutInfo.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Editor/Scripts/Node/PlayableNode.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ public override void CreateAndConnectInputNodes()
3434
{
3535
title = inputPlayableTypeName
3636
};
37-
inputPlayableNode.SetPosition(new Rect(-400 * inputPlayableDepth, 200, 0, 0));
37+
//inputPlayableNode.SetPosition(new Rect(-400 * inputPlayableDepth, 200, 0, 0));
3838
inputPlayableNode.AddToContainer(Container);
39-
ChildNodes.Add(inputPlayableNode);
39+
Children.Add(inputPlayableNode);
4040

4141
var inputPlayableNodeOutputPort = inputPlayableNode.OutputPorts[0];
4242
var selfInputPort = InternalInputPorts[i];
@@ -45,9 +45,14 @@ public override void CreateAndConnectInputNodes()
4545
InternalInputEdges.Add(edge);
4646
}
4747

48-
for (int i = 0; i < ChildNodes.Count; i++)
48+
if (Children.Count > 1)
4949
{
50-
ChildNodes[i].CreateAndConnectInputNodes();
50+
LayoutInfo.TreeWidth += Children.Count - 1;
51+
}
52+
53+
for (int i = 0; i < Children.Count; i++)
54+
{
55+
Children[i].CreateAndConnectInputNodes();
5156
}
5257
}
5358

Editor/Scripts/Node/PlayableOutputNode.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ public PlayableOutputNode(int depth, PlayableOutput playableOutput)
1616
CreatePorts();
1717
RefreshExpandedState();
1818
RefreshPorts();
19+
20+
// root node occupies one width unit
21+
LayoutInfo.TreeWidth += 1;
1922
}
2023

2124
public override void CreateAndConnectInputNodes()
@@ -32,9 +35,9 @@ public override void CreateAndConnectInputNodes()
3235
{
3336
title = sourcePlayableTypeName
3437
};
35-
sourcePlayableNode.SetPosition(new Rect(-400 * sourcePlayableNodeDepth, 200, 0, 0));
38+
//sourcePlayableNode.SetPosition(new Rect(-400 * sourcePlayableNodeDepth, 200, 0, 0));
3639
sourcePlayableNode.AddToContainer(Container);
37-
ChildNodes.Add(sourcePlayableNode);
40+
Children.Add(sourcePlayableNode);
3841

3942
var sourcePlayableOutputPortIndex = PlayableOutput.GetSourceOutputPort();
4043
var sourcePlayableOutputPort = sourcePlayableNode.OutputPorts[sourcePlayableOutputPortIndex];
@@ -43,9 +46,14 @@ public override void CreateAndConnectInputNodes()
4346
Container.AddElement(edge);
4447
InternalInputEdges.Add(edge);
4548

46-
for (int i = 0; i < ChildNodes.Count; i++)
49+
if (Children.Count > 1)
50+
{
51+
LayoutInfo.TreeWidth += Children.Count - 1;
52+
}
53+
54+
for (int i = 0; i < Children.Count; i++)
4755
{
48-
ChildNodes[i].CreateAndConnectInputNodes();
56+
Children[i].CreateAndConnectInputNodes();
4957
}
5058
}
5159

0 commit comments

Comments
 (0)