From a41fc51f92aeaf99aa6675bba7ea77a6eed52b1c Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Sat, 28 Jun 2025 15:53:32 +0200 Subject: [PATCH 1/3] add visject node formatting option to straighten node connections Also fixes a bunch of missing trailing "." in doc comments and changes "Node editors" category in the input options to "Node Editors" to match the case of all other categories. --- Source/Editor/Options/InputOptions.cs | 24 ++-- Source/Editor/Surface/NodeAlignmentType.cs | 16 +-- .../Surface/VisjectSurface.ContextMenu.cs | 3 +- .../Surface/VisjectSurface.Formatting.cs | 106 ++++++++++++++---- Source/Editor/Surface/VisjectSurface.cs | 1 + 5 files changed, 107 insertions(+), 43 deletions(-) diff --git a/Source/Editor/Options/InputOptions.cs b/Source/Editor/Options/InputOptions.cs index 871156662..0281d8ce5 100644 --- a/Source/Editor/Options/InputOptions.cs +++ b/Source/Editor/Options/InputOptions.cs @@ -652,42 +652,46 @@ namespace FlaxEditor.Options #endregion - #region Node editors + #region Node Editors [DefaultValue(typeof(InputBinding), "Shift+W")] - [EditorDisplay("Node editors"), EditorOrder(4500)] + [EditorDisplay("Node Editors"), EditorOrder(4500)] public InputBinding NodesAlignTop = new InputBinding(KeyboardKeys.W, KeyboardKeys.Shift); [DefaultValue(typeof(InputBinding), "Shift+A")] - [EditorDisplay("Node editors"), EditorOrder(4510)] + [EditorDisplay("Node Editors"), EditorOrder(4510)] public InputBinding NodesAlignLeft = new InputBinding(KeyboardKeys.A, KeyboardKeys.Shift); [DefaultValue(typeof(InputBinding), "Shift+S")] - [EditorDisplay("Node editors"), EditorOrder(4520)] + [EditorDisplay("Node Editors"), EditorOrder(4520)] public InputBinding NodesAlignBottom = new InputBinding(KeyboardKeys.S, KeyboardKeys.Shift); [DefaultValue(typeof(InputBinding), "Shift+D")] - [EditorDisplay("Node editors"), EditorOrder(4530)] + [EditorDisplay("Node Editors"), EditorOrder(4530)] public InputBinding NodesAlignRight = new InputBinding(KeyboardKeys.D, KeyboardKeys.Shift); [DefaultValue(typeof(InputBinding), "Alt+Shift+W")] - [EditorDisplay("Node editors"), EditorOrder(4540)] + [EditorDisplay("Node Editors"), EditorOrder(4540)] public InputBinding NodesAlignMiddle = new InputBinding(KeyboardKeys.W, KeyboardKeys.Shift, KeyboardKeys.Alt); [DefaultValue(typeof(InputBinding), "Alt+Shift+S")] - [EditorDisplay("Node editors"), EditorOrder(4550)] + [EditorDisplay("Node Editors"), EditorOrder(4550)] public InputBinding NodesAlignCenter = new InputBinding(KeyboardKeys.S, KeyboardKeys.Shift, KeyboardKeys.Alt); [DefaultValue(typeof(InputBinding), "Q")] - [EditorDisplay("Node editors"), EditorOrder(4560)] + [EditorDisplay("Node Editors"), EditorOrder(4560)] public InputBinding NodesAutoFormat = new InputBinding(KeyboardKeys.Q); + [DefaultValue(typeof(InputBinding), "Shift+Q")] + [EditorDisplay("Node Editors"), EditorOrder(4560)] + public InputBinding NodesStraightenConnections = new InputBinding(KeyboardKeys.Q, KeyboardKeys.Shift); + [DefaultValue(typeof(InputBinding), "None")] - [EditorDisplay("Node editors"), EditorOrder(4570)] + [EditorDisplay("Node Editors"), EditorOrder(4570)] public InputBinding NodesDistributeHorizontal = new InputBinding(KeyboardKeys.None); [DefaultValue(typeof(InputBinding), "None")] - [EditorDisplay("Node editors"), EditorOrder(4580)] + [EditorDisplay("Node Editors"), EditorOrder(4580)] public InputBinding NodesDistributeVertical = new InputBinding(KeyboardKeys.None); #endregion diff --git a/Source/Editor/Surface/NodeAlignmentType.cs b/Source/Editor/Surface/NodeAlignmentType.cs index 141235783..07e1d8dc4 100644 --- a/Source/Editor/Surface/NodeAlignmentType.cs +++ b/Source/Editor/Surface/NodeAlignmentType.cs @@ -5,39 +5,39 @@ using FlaxEngine; namespace FlaxEditor.Surface { /// - /// Node Alignment type + /// Node Alignment type. /// [HideInEditor] public enum NodeAlignmentType { /// - /// Align nodes vertically to top, matching top-most node + /// Align nodes vertically to top, matching top-most node. /// Top, /// - /// Align nodes vertically to middle, using average of all nodes + /// Align nodes vertically to middle, using average of all nodes. /// Middle, /// - /// Align nodes vertically to bottom, matching bottom-most node + /// Align nodes vertically to bottom, matching bottom-most node. /// Bottom, /// - /// Align nodes horizontally to left, matching left-most node + /// Align nodes horizontally to left, matching left-most node. /// Left, /// - /// Align nodes horizontally to center, using average of all nodes + /// Align nodes horizontally to center, using average of all nodes. /// Center, /// - /// Align nodes horizontally to right, matching right-most node + /// Align nodes horizontally to right, matching right-most node. /// Right, } -} \ No newline at end of file +} diff --git a/Source/Editor/Surface/VisjectSurface.ContextMenu.cs b/Source/Editor/Surface/VisjectSurface.ContextMenu.cs index 84055aaf0..41020c962 100644 --- a/Source/Editor/Surface/VisjectSurface.ContextMenu.cs +++ b/Source/Editor/Surface/VisjectSurface.ContextMenu.cs @@ -28,7 +28,7 @@ namespace FlaxEditor.Surface /// /// The input type to process. /// Node groups cache that can be used for reusing groups for different nodes. - /// The cache version number. Can be used to reject any cached data after rebuilt. + /// The cache version number. Can be used to reject any cached data after. rebuilt. public delegate void IterateType(ScriptType scriptType, Dictionary, GroupArchetype> cache, int version); internal static readonly List Caches = new List(8); @@ -412,6 +412,7 @@ namespace FlaxEditor.Surface _cmFormatNodesMenu.Enabled = CanEdit && HasNodesSelection; _cmFormatNodesConnectionButton = _cmFormatNodesMenu.ContextMenu.AddButton("Auto format", Editor.Instance.Options.Options.Input.NodesAutoFormat, () => { FormatGraph(SelectedNodes); }); + _cmFormatNodesConnectionButton = _cmFormatNodesMenu.ContextMenu.AddButton("Straighten connections", Editor.Instance.Options.Options.Input.NodesStraightenConnections, () => { StraightenGraphConnections(SelectedNodes); }); _cmFormatNodesMenu.ContextMenu.AddSeparator(); _cmAlignNodesTopButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align top", Editor.Instance.Options.Options.Input.NodesAlignTop, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Top); }); diff --git a/Source/Editor/Surface/VisjectSurface.Formatting.cs b/Source/Editor/Surface/VisjectSurface.Formatting.cs index 2ff48b290..8b557c357 100644 --- a/Source/Editor/Surface/VisjectSurface.Formatting.cs +++ b/Source/Editor/Surface/VisjectSurface.Formatting.cs @@ -1,9 +1,9 @@ +using FlaxEditor.Surface.Elements; +using FlaxEditor.Surface.Undo; +using FlaxEngine; using System; using System.Collections.Generic; using System.Linq; -using FlaxEngine; -using FlaxEditor.Surface.Elements; -using FlaxEditor.Surface.Undo; namespace FlaxEditor.Surface { @@ -14,26 +14,26 @@ namespace FlaxEditor.Surface private class NodeFormattingData { /// - /// Starting from 0 at the main nodes + /// Starting from 0 at the main nodes. /// public int Layer; /// - /// Position in the layer + /// Position in the layer. /// public int Offset; /// - /// How far the subtree needs to be moved additionally + /// How far the subtree needs to be moved additionally. /// public int SubtreeOffset; } /// /// Formats a graph where the nodes can be disjointed. - /// Uses the Sugiyama method + /// Uses the Sugiyama method. /// - /// List of nodes + /// List of nodes. public void FormatGraph(List nodes) { if (nodes.Count <= 1) @@ -78,9 +78,9 @@ namespace FlaxEditor.Surface } /// - /// Formats a graph where all nodes are connected + /// Formats a graph where all nodes are connected. /// - /// List of connected nodes + /// List of connected nodes. protected void FormatConnectedGraph(List nodes) { if (nodes.Count <= 1) @@ -160,11 +160,69 @@ namespace FlaxEditor.Surface } /// - /// Assigns a layer to every node + /// Straightens every connection between nodes in . /// - /// The exta node data - /// The end nodes - /// The number of the maximum layer + /// List of nodes. + public void StraightenGraphConnections(List nodes) + { + if (nodes.Count <= 1) + return; + + List undoActions = new List(); + + // Only process nodes that have any connection + List connectedNodes = nodes.Where(n => n.GetBoxes().Any(b => b.HasAnyConnection)).ToList(); + + if (connectedNodes.Count == 0) + return; + + for (int i = 0; i < connectedNodes.Count - 1; i++) + { + SurfaceNode nodeA = connectedNodes[i]; + List connectedOutputBoxes = nodeA.GetBoxes().Where(b => b.IsOutput && b.HasAnyConnection).ToList(); + + for (int j = 0; j < connectedOutputBoxes.Count; j++) + { + Box boxA = connectedOutputBoxes[j]; + + for (int b = 0; b < boxA.Connections.Count; b++) + { + Box boxB = boxA.Connections[b]; + + // Ensure the other node is selected + if (!connectedNodes.Contains(boxB.ParentNode)) + continue; + + // Node with no outgoing connections reached. Advance to next node in list + if (boxA == null || boxB == null) + continue; + + SurfaceNode nodeB = boxB.ParentNode; + + // Calculate the Y offset needed for nodeB to align boxB's Y to boxA's Y + float boxASurfaceY = boxA.PointToParent(this, Float2.Zero).Y; + float boxBSurfaceY = boxB.PointToParent(this, Float2.Zero).Y; + float deltaY = (boxASurfaceY - boxBSurfaceY) / ViewScale; + Float2 delta = new Float2(0f, deltaY); + + nodeB.Location += delta; + + if (Undo != null) + undoActions.Add(new MoveNodesAction(Context, new[] { nodeB.ID }, delta)); + } + } + } + + Undo?.AddAction(new MultiUndoAction(undoActions, "Straightned ")); + MarkAsEdited(false); + } + + /// + /// Assigns a layer to every node. + /// + /// The exta node data. + /// The end nodes. + /// The number of the maximum layer. private int SetLayers(Dictionary nodeData, List endNodes) { // Longest path layering @@ -201,12 +259,12 @@ namespace FlaxEditor.Surface /// - /// Sets the node offsets + /// Sets the node offsets. /// - /// The exta node data - /// The end nodes - /// The number of the maximum layer - /// The number of the maximum offset + /// The exta node data. + /// The end nodes. + /// The number of the maximum layer. + /// The number of the maximum offset. private int SetOffsets(Dictionary nodeData, List endNodes, int maxLayer) { int maxOffset = 0; @@ -287,10 +345,10 @@ namespace FlaxEditor.Surface /// Align given nodes on a graph using the given alignment type. /// Ignores any potential overlap. /// - /// List of nodes - /// Alignemnt type + /// List of nodes. + /// Alignemnt type. public void AlignNodes(List nodes, NodeAlignmentType alignmentType) - { + { if(nodes.Count <= 1) return; @@ -328,8 +386,8 @@ namespace FlaxEditor.Surface /// /// Distribute the given nodes as equally as possible inside the bounding box, if no fit can be done it will use a default pad of 10 pixels between nodes. /// - /// List of nodes - /// If false will be done horizontally, if true will be done vertically + /// List of nodes. + /// If false will be done horizontally, if true will be done vertically. public void DistributeNodes(List nodes, bool vertically) { if(nodes.Count <= 1) diff --git a/Source/Editor/Surface/VisjectSurface.cs b/Source/Editor/Surface/VisjectSurface.cs index 1201318d8..3bdad7eab 100644 --- a/Source/Editor/Surface/VisjectSurface.cs +++ b/Source/Editor/Surface/VisjectSurface.cs @@ -416,6 +416,7 @@ namespace FlaxEditor.Surface new InputActionsContainer.Binding(options => options.Cut, Cut), new InputActionsContainer.Binding(options => options.Duplicate, Duplicate), new InputActionsContainer.Binding(options => options.NodesAutoFormat, () => { FormatGraph(SelectedNodes); }), + new InputActionsContainer.Binding(options => options.NodesStraightenConnections, () => { StraightenGraphConnections(SelectedNodes); }), new InputActionsContainer.Binding(options => options.NodesAlignTop, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Top); }), new InputActionsContainer.Binding(options => options.NodesAlignMiddle, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Middle); }), new InputActionsContainer.Binding(options => options.NodesAlignBottom, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Bottom); }), From 683a48a6e371f02425581858d7b9abddc5d4465d Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Sat, 28 Jun 2025 16:02:44 +0200 Subject: [PATCH 2/3] add default shortcuts to distribute node options --- Source/Editor/Options/InputOptions.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Options/InputOptions.cs b/Source/Editor/Options/InputOptions.cs index 0281d8ce5..af919c1f3 100644 --- a/Source/Editor/Options/InputOptions.cs +++ b/Source/Editor/Options/InputOptions.cs @@ -686,13 +686,13 @@ namespace FlaxEditor.Options [EditorDisplay("Node Editors"), EditorOrder(4560)] public InputBinding NodesStraightenConnections = new InputBinding(KeyboardKeys.Q, KeyboardKeys.Shift); - [DefaultValue(typeof(InputBinding), "None")] + [DefaultValue(typeof(InputBinding), "Alt+W")] [EditorDisplay("Node Editors"), EditorOrder(4570)] - public InputBinding NodesDistributeHorizontal = new InputBinding(KeyboardKeys.None); + public InputBinding NodesDistributeHorizontal = new InputBinding(KeyboardKeys.W, KeyboardKeys.Alt); - [DefaultValue(typeof(InputBinding), "None")] + [DefaultValue(typeof(InputBinding), "Alt+A")] [EditorDisplay("Node Editors"), EditorOrder(4580)] - public InputBinding NodesDistributeVertical = new InputBinding(KeyboardKeys.None); + public InputBinding NodesDistributeVertical = new InputBinding(KeyboardKeys.A, KeyboardKeys.Alt); #endregion } From d7ab497b0e0a2d57c7e5f2bfae0f0a430784764f Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Sat, 28 Jun 2025 16:22:29 +0200 Subject: [PATCH 3/3] fix adding empty multi action to undo stack --- Source/Editor/Surface/VisjectSurface.Formatting.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Surface/VisjectSurface.Formatting.cs b/Source/Editor/Surface/VisjectSurface.Formatting.cs index 8b557c357..39ac58242 100644 --- a/Source/Editor/Surface/VisjectSurface.Formatting.cs +++ b/Source/Editor/Surface/VisjectSurface.Formatting.cs @@ -213,7 +213,9 @@ namespace FlaxEditor.Surface } } - Undo?.AddAction(new MultiUndoAction(undoActions, "Straightned ")); + if (undoActions.Count > 0) + Undo?.AddAction(new MultiUndoAction(undoActions, "Straightned ")); + MarkAsEdited(false); }