diff --git a/AssetDependencyGraph/AssetDependencyGraph-Editor.asmdef b/AssetDependencyGraph/AssetDependencyGraph-Editor.asmdef index 69a8ee7..f9eefb0 100644 --- a/AssetDependencyGraph/AssetDependencyGraph-Editor.asmdef +++ b/AssetDependencyGraph/AssetDependencyGraph-Editor.asmdef @@ -1,14 +1,17 @@ { "name": "AssetDependencyGraph-Editor", "references": [], - "optionalUnityReferences": [], "includePlatforms": [ "Editor" ], "excludePlatforms": [], "allowUnsafeCode": false, - "overrideReferences": false, - "precompiledReferences": [], - "autoReferenced": true, - "defineConstraints": [] + "overrideReferences": true, + "precompiledReferences": [ + "AutomaticGraphLayout.dll" + ], + "autoReferenced": false, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false } \ No newline at end of file diff --git a/AssetDependencyGraph/Editor/AssetDependencyGraph.cs b/AssetDependencyGraph/Editor/AssetDependencyGraph.cs index fa55c4d..26a249b 100644 --- a/AssetDependencyGraph/Editor/AssetDependencyGraph.cs +++ b/AssetDependencyGraph/Editor/AssetDependencyGraph.cs @@ -3,6 +3,7 @@ #if UNITY_2019_1_OR_NEWER using UnityEditor.Experimental.GraphView; using UnityEngine.UIElements; +using UnityEditor.UIElements; #else using UnityEditor.Experimental.UIElements.GraphView; using UnityEngine.Experimental.UIElements; @@ -40,6 +41,7 @@ public class AssetDependencyGraph : EditorWindow private const float kNodeWidth = 250.0f; private GraphView m_GraphView; + private bool m_UseAutoLayout = true; private readonly List m_AssetElements = new List(); private readonly Dictionary m_GUIDNodeLookup = new Dictionary(); @@ -88,6 +90,13 @@ public void OnEnable() text = "Clear" }); + var toggle = new ToolbarToggle() { + text = "Auto Layout", + value = m_UseAutoLayout + }; + toggle.RegisterValueChangedCallback(x => { m_UseAutoLayout = x.newValue; }); + toolbar.Add(toggle); + #if !UNITY_2019_1_OR_NEWER rootVisualElement = this.GetRootVisualContainer(); #endif @@ -330,8 +339,10 @@ private void ClearGraph() m_GUIDNodeLookup.Clear(); } - private void UpdateDependencyNodePlacement(GeometryChangedEvent e) - { + private void UpdateDependencyNodePlacement(GeometryChangedEvent e) { + var element = e.target as VisualElement; + element.UnregisterCallback(UpdateDependencyNodePlacement); + // The current y offset in per depth var depthYOffset = new Dictionary(); @@ -364,5 +375,20 @@ private void UpdateDependencyNodePlacement(GeometryChangedEvent e) } m_DependenciesForPlacement.Clear(); + + if(m_UseAutoLayout) { + // run graph improvements on top + var layouter = new GraphViewLayouter(); + layouter.Adjust(m_GraphView); + } + + element.RegisterCallback(FrameOnce); + } + + private void FrameOnce(GeometryChangedEvent e) { + if (e.target is VisualElement element) + element.UnregisterCallback(FrameOnce); + + m_GraphView.FrameAll(); } } diff --git a/AssetDependencyGraph/Editor/AutomaticGraphLayout.dll b/AssetDependencyGraph/Editor/AutomaticGraphLayout.dll new file mode 100644 index 0000000..34cd6a0 Binary files /dev/null and b/AssetDependencyGraph/Editor/AutomaticGraphLayout.dll differ diff --git a/AssetDependencyGraph/Editor/AutomaticGraphLayout.dll.meta b/AssetDependencyGraph/Editor/AutomaticGraphLayout.dll.meta new file mode 100644 index 0000000..8b83c10 --- /dev/null +++ b/AssetDependencyGraph/Editor/AutomaticGraphLayout.dll.meta @@ -0,0 +1,33 @@ +fileFormatVersion: 2 +guid: c0915053d62bcd544b1a916fe5e18cd8 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 1 + settings: + DefaultValueInitialized: true + - first: + Windows Store Apps: WindowsStoreApps + second: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/AssetDependencyGraph/Editor/GraphViewLayouter.cs b/AssetDependencyGraph/Editor/GraphViewLayouter.cs new file mode 100644 index 0000000..2351ef3 --- /dev/null +++ b/AssetDependencyGraph/Editor/GraphViewLayouter.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using Microsoft.Msagl.Core.Geometry; +using Microsoft.Msagl.Core.Geometry.Curves; +using Microsoft.Msagl.Core.Layout; +using Microsoft.Msagl.Core.Routing; +using Microsoft.Msagl.Layout.Layered; + +public class GraphViewLayouter +{ + // https://www.microsoft.com/en-us/research/project/microsoft-automatic-graph-layout/#code-samples + public void Adjust(UnityEditor.Experimental.GraphView.GraphView graphView) + { + var elements = graphView.graphElements.ToList(); + var nodes = elements.Where(x => x is UnityEditor.Experimental.GraphView.Node) + .Cast(); + var edges = elements.Where(x => x is UnityEditor.Experimental.GraphView.Edge) + .Cast(); + + GeometryGraph graph = new GeometryGraph(); + + Dictionary geomNodes = + new Dictionary(); + + foreach (var node in nodes) { + var nodeRect = node.GetPosition(); + Node geometryNode = new Node(CurveFactory.CreateRectangle(nodeRect.width, nodeRect.height, new Point()), node); + graph.Nodes.Add(geometryNode); + geomNodes.Add(node, geometryNode); + } + + foreach (var e in edges) { + // Exact edge end positions could be found with e.output.GetGlobalCenter() + Edge geometryEdge = new Edge(geomNodes[e.output.node], geomNodes[e.input.node]); + graph.Edges.Add(geometryEdge); + } + + // settings found to look good (enough separation, not too much, clean layout) by experimentation + var settings = new SugiyamaLayoutSettings { + Transformation = PlaneTransformation.Rotation(Math.PI / 2), + EdgeRoutingSettings = {EdgeRoutingMode = EdgeRoutingMode.StraightLine}, + // AspectRatio = 9.0 / 16.0, // allows adjusting aspect + LayerSeparation = 200, + GridSizeByX = 300, + GridSizeByY = 300, + SnapToGridByY = SnapToGridByY.Bottom + }; + var layout = new LayeredLayout(graph, settings); + layout.Run(); + + foreach (var n in graph.Nodes) { + var unityNode = (UnityEditor.Experimental.GraphView.Node) n.UserData; + unityNode.SetPosition(new Rect((float) n.Center.X, (float) n.Center.Y, 0, 0)); + } + } +} \ No newline at end of file diff --git a/AssetDependencyGraph/Editor/GraphViewLayouter.cs.meta b/AssetDependencyGraph/Editor/GraphViewLayouter.cs.meta new file mode 100644 index 0000000..87558a0 --- /dev/null +++ b/AssetDependencyGraph/Editor/GraphViewLayouter.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d985edf951165c84d9ae2ab0820a5b18 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: