Merge branch 'xxSeys1-VjStraightenConnections'
This commit is contained in:
@@ -652,43 +652,47 @@ namespace FlaxEditor.Options
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Node editors
|
#region Node Editors
|
||||||
|
|
||||||
[DefaultValue(typeof(InputBinding), "Shift+W")]
|
[DefaultValue(typeof(InputBinding), "Shift+W")]
|
||||||
[EditorDisplay("Node editors"), EditorOrder(4500)]
|
[EditorDisplay("Node Editors"), EditorOrder(4500)]
|
||||||
public InputBinding NodesAlignTop = new InputBinding(KeyboardKeys.W, KeyboardKeys.Shift);
|
public InputBinding NodesAlignTop = new InputBinding(KeyboardKeys.W, KeyboardKeys.Shift);
|
||||||
|
|
||||||
[DefaultValue(typeof(InputBinding), "Shift+A")]
|
[DefaultValue(typeof(InputBinding), "Shift+A")]
|
||||||
[EditorDisplay("Node editors"), EditorOrder(4510)]
|
[EditorDisplay("Node Editors"), EditorOrder(4510)]
|
||||||
public InputBinding NodesAlignLeft = new InputBinding(KeyboardKeys.A, KeyboardKeys.Shift);
|
public InputBinding NodesAlignLeft = new InputBinding(KeyboardKeys.A, KeyboardKeys.Shift);
|
||||||
|
|
||||||
[DefaultValue(typeof(InputBinding), "Shift+S")]
|
[DefaultValue(typeof(InputBinding), "Shift+S")]
|
||||||
[EditorDisplay("Node editors"), EditorOrder(4520)]
|
[EditorDisplay("Node Editors"), EditorOrder(4520)]
|
||||||
public InputBinding NodesAlignBottom = new InputBinding(KeyboardKeys.S, KeyboardKeys.Shift);
|
public InputBinding NodesAlignBottom = new InputBinding(KeyboardKeys.S, KeyboardKeys.Shift);
|
||||||
|
|
||||||
[DefaultValue(typeof(InputBinding), "Shift+D")]
|
[DefaultValue(typeof(InputBinding), "Shift+D")]
|
||||||
[EditorDisplay("Node editors"), EditorOrder(4530)]
|
[EditorDisplay("Node Editors"), EditorOrder(4530)]
|
||||||
public InputBinding NodesAlignRight = new InputBinding(KeyboardKeys.D, KeyboardKeys.Shift);
|
public InputBinding NodesAlignRight = new InputBinding(KeyboardKeys.D, KeyboardKeys.Shift);
|
||||||
|
|
||||||
[DefaultValue(typeof(InputBinding), "Alt+Shift+W")]
|
[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);
|
public InputBinding NodesAlignMiddle = new InputBinding(KeyboardKeys.W, KeyboardKeys.Shift, KeyboardKeys.Alt);
|
||||||
|
|
||||||
[DefaultValue(typeof(InputBinding), "Alt+Shift+S")]
|
[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);
|
public InputBinding NodesAlignCenter = new InputBinding(KeyboardKeys.S, KeyboardKeys.Shift, KeyboardKeys.Alt);
|
||||||
|
|
||||||
[DefaultValue(typeof(InputBinding), "Q")]
|
[DefaultValue(typeof(InputBinding), "Q")]
|
||||||
[EditorDisplay("Node editors"), EditorOrder(4560)]
|
[EditorDisplay("Node Editors"), EditorOrder(4560)]
|
||||||
public InputBinding NodesAutoFormat = new InputBinding(KeyboardKeys.Q);
|
public InputBinding NodesAutoFormat = new InputBinding(KeyboardKeys.Q);
|
||||||
|
|
||||||
[DefaultValue(typeof(InputBinding), "None")]
|
[DefaultValue(typeof(InputBinding), "Shift+Q")]
|
||||||
[EditorDisplay("Node editors"), EditorOrder(4570)]
|
[EditorDisplay("Node Editors"), EditorOrder(4560)]
|
||||||
public InputBinding NodesDistributeHorizontal = new InputBinding(KeyboardKeys.None);
|
public InputBinding NodesStraightenConnections = new InputBinding(KeyboardKeys.Q, KeyboardKeys.Shift);
|
||||||
|
|
||||||
[DefaultValue(typeof(InputBinding), "None")]
|
[DefaultValue(typeof(InputBinding), "Alt+W")]
|
||||||
[EditorDisplay("Node editors"), EditorOrder(4580)]
|
[EditorDisplay("Node Editors"), EditorOrder(4570)]
|
||||||
public InputBinding NodesDistributeVertical = new InputBinding(KeyboardKeys.None);
|
public InputBinding NodesDistributeHorizontal = new InputBinding(KeyboardKeys.W, KeyboardKeys.Alt);
|
||||||
|
|
||||||
|
[DefaultValue(typeof(InputBinding), "Alt+A")]
|
||||||
|
[EditorDisplay("Node Editors"), EditorOrder(4580)]
|
||||||
|
public InputBinding NodesDistributeVertical = new InputBinding(KeyboardKeys.A, KeyboardKeys.Alt);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,39 +5,39 @@ using FlaxEngine;
|
|||||||
namespace FlaxEditor.Surface
|
namespace FlaxEditor.Surface
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Node Alignment type
|
/// Node Alignment type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HideInEditor]
|
[HideInEditor]
|
||||||
public enum NodeAlignmentType
|
public enum NodeAlignmentType
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Align nodes vertically to top, matching top-most node
|
/// Align nodes vertically to top, matching top-most node.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Top,
|
Top,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Align nodes vertically to middle, using average of all nodes
|
/// Align nodes vertically to middle, using average of all nodes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Middle,
|
Middle,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Align nodes vertically to bottom, matching bottom-most node
|
/// Align nodes vertically to bottom, matching bottom-most node.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Bottom,
|
Bottom,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Align nodes horizontally to left, matching left-most node
|
/// Align nodes horizontally to left, matching left-most node.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Left,
|
Left,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Align nodes horizontally to center, using average of all nodes
|
/// Align nodes horizontally to center, using average of all nodes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Center,
|
Center,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Align nodes horizontally to right, matching right-most node
|
/// Align nodes horizontally to right, matching right-most node.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Right,
|
Right,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ namespace FlaxEditor.Surface
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="scriptType">The input type to process.</param>
|
/// <param name="scriptType">The input type to process.</param>
|
||||||
/// <param name="cache">Node groups cache that can be used for reusing groups for different nodes.</param>
|
/// <param name="cache">Node groups cache that can be used for reusing groups for different nodes.</param>
|
||||||
/// <param name="version">The cache version number. Can be used to reject any cached data after <see cref="NodesCache"/> rebuilt.</param>
|
/// <param name="version">The cache version number. Can be used to reject any cached data after.<see cref="NodesCache"/> rebuilt.</param>
|
||||||
public delegate void IterateType(ScriptType scriptType, Dictionary<KeyValuePair<string, ushort>, GroupArchetype> cache, int version);
|
public delegate void IterateType(ScriptType scriptType, Dictionary<KeyValuePair<string, ushort>, GroupArchetype> cache, int version);
|
||||||
|
|
||||||
internal static readonly List<NodesCache> Caches = new List<NodesCache>(8);
|
internal static readonly List<NodesCache> Caches = new List<NodesCache>(8);
|
||||||
@@ -412,6 +412,7 @@ namespace FlaxEditor.Surface
|
|||||||
_cmFormatNodesMenu.Enabled = CanEdit && HasNodesSelection;
|
_cmFormatNodesMenu.Enabled = CanEdit && HasNodesSelection;
|
||||||
|
|
||||||
_cmFormatNodesConnectionButton = _cmFormatNodesMenu.ContextMenu.AddButton("Auto format", Editor.Instance.Options.Options.Input.NodesAutoFormat, () => { FormatGraph(SelectedNodes); });
|
_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();
|
_cmFormatNodesMenu.ContextMenu.AddSeparator();
|
||||||
_cmAlignNodesTopButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align top", Editor.Instance.Options.Options.Input.NodesAlignTop, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Top); });
|
_cmAlignNodesTopButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align top", Editor.Instance.Options.Options.Input.NodesAlignTop, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Top); });
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
|
using FlaxEditor.Surface.Elements;
|
||||||
|
using FlaxEditor.Surface.Undo;
|
||||||
|
using FlaxEngine;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FlaxEngine;
|
|
||||||
using FlaxEditor.Surface.Elements;
|
|
||||||
using FlaxEditor.Surface.Undo;
|
|
||||||
|
|
||||||
namespace FlaxEditor.Surface
|
namespace FlaxEditor.Surface
|
||||||
{
|
{
|
||||||
@@ -14,26 +14,26 @@ namespace FlaxEditor.Surface
|
|||||||
private class NodeFormattingData
|
private class NodeFormattingData
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Starting from 0 at the main nodes
|
/// Starting from 0 at the main nodes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Layer;
|
public int Layer;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Position in the layer
|
/// Position in the layer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Offset;
|
public int Offset;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How far the subtree needs to be moved additionally
|
/// How far the subtree needs to be moved additionally.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int SubtreeOffset;
|
public int SubtreeOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Formats a graph where the nodes can be disjointed.
|
/// Formats a graph where the nodes can be disjointed.
|
||||||
/// Uses the Sugiyama method
|
/// Uses the Sugiyama method.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="nodes">List of nodes</param>
|
/// <param name="nodes">List of nodes.</param>
|
||||||
public void FormatGraph(List<SurfaceNode> nodes)
|
public void FormatGraph(List<SurfaceNode> nodes)
|
||||||
{
|
{
|
||||||
if (nodes.Count <= 1)
|
if (nodes.Count <= 1)
|
||||||
@@ -78,9 +78,9 @@ namespace FlaxEditor.Surface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Formats a graph where all nodes are connected
|
/// Formats a graph where all nodes are connected.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="nodes">List of connected nodes</param>
|
/// <param name="nodes">List of connected nodes.</param>
|
||||||
protected void FormatConnectedGraph(List<SurfaceNode> nodes)
|
protected void FormatConnectedGraph(List<SurfaceNode> nodes)
|
||||||
{
|
{
|
||||||
if (nodes.Count <= 1)
|
if (nodes.Count <= 1)
|
||||||
@@ -160,11 +160,71 @@ namespace FlaxEditor.Surface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Assigns a layer to every node
|
/// Straightens every connection between nodes in <paramref name="nodes"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="nodeData">The exta node data</param>
|
/// <param name="nodes">List of nodes.</param>
|
||||||
/// <param name="endNodes">The end nodes</param>
|
public void StraightenGraphConnections(List<SurfaceNode> nodes)
|
||||||
/// <returns>The number of the maximum layer</returns>
|
{
|
||||||
|
if (nodes.Count <= 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
List<MoveNodesAction> undoActions = new List<MoveNodesAction>();
|
||||||
|
|
||||||
|
// Only process nodes that have any connection
|
||||||
|
List<SurfaceNode> 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<Box> 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (undoActions.Count > 0)
|
||||||
|
Undo?.AddAction(new MultiUndoAction(undoActions, "Straightned "));
|
||||||
|
|
||||||
|
MarkAsEdited(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Assigns a layer to every node.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nodeData">The exta node data.</param>
|
||||||
|
/// <param name="endNodes">The end nodes.</param>
|
||||||
|
/// <returns>The number of the maximum layer.</returns>
|
||||||
private int SetLayers(Dictionary<SurfaceNode, NodeFormattingData> nodeData, List<SurfaceNode> endNodes)
|
private int SetLayers(Dictionary<SurfaceNode, NodeFormattingData> nodeData, List<SurfaceNode> endNodes)
|
||||||
{
|
{
|
||||||
// Longest path layering
|
// Longest path layering
|
||||||
@@ -201,12 +261,12 @@ namespace FlaxEditor.Surface
|
|||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the node offsets
|
/// Sets the node offsets.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="nodeData">The exta node data</param>
|
/// <param name="nodeData">The exta node data.</param>
|
||||||
/// <param name="endNodes">The end nodes</param>
|
/// <param name="endNodes">The end nodes.</param>
|
||||||
/// <param name="maxLayer">The number of the maximum layer</param>
|
/// <param name="maxLayer">The number of the maximum layer.</param>
|
||||||
/// <returns>The number of the maximum offset</returns>
|
/// <returns>The number of the maximum offset.</returns>
|
||||||
private int SetOffsets(Dictionary<SurfaceNode, NodeFormattingData> nodeData, List<SurfaceNode> endNodes, int maxLayer)
|
private int SetOffsets(Dictionary<SurfaceNode, NodeFormattingData> nodeData, List<SurfaceNode> endNodes, int maxLayer)
|
||||||
{
|
{
|
||||||
int maxOffset = 0;
|
int maxOffset = 0;
|
||||||
@@ -287,10 +347,10 @@ namespace FlaxEditor.Surface
|
|||||||
/// Align given nodes on a graph using the given alignment type.
|
/// Align given nodes on a graph using the given alignment type.
|
||||||
/// Ignores any potential overlap.
|
/// Ignores any potential overlap.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="nodes">List of nodes</param>
|
/// <param name="nodes">List of nodes.</param>
|
||||||
/// <param name="alignmentType">Alignemnt type</param>
|
/// <param name="alignmentType">Alignemnt type.</param>
|
||||||
public void AlignNodes(List<SurfaceNode> nodes, NodeAlignmentType alignmentType)
|
public void AlignNodes(List<SurfaceNode> nodes, NodeAlignmentType alignmentType)
|
||||||
{
|
{
|
||||||
if(nodes.Count <= 1)
|
if(nodes.Count <= 1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -328,8 +388,8 @@ namespace FlaxEditor.Surface
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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.
|
/// 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="nodes">List of nodes</param>
|
/// <param name="nodes">List of nodes.</param>
|
||||||
/// <param name="vertically">If false will be done horizontally, if true will be done vertically</param>
|
/// <param name="vertically">If false will be done horizontally, if true will be done vertically.</param>
|
||||||
public void DistributeNodes(List<SurfaceNode> nodes, bool vertically)
|
public void DistributeNodes(List<SurfaceNode> nodes, bool vertically)
|
||||||
{
|
{
|
||||||
if(nodes.Count <= 1)
|
if(nodes.Count <= 1)
|
||||||
|
|||||||
@@ -416,6 +416,7 @@ namespace FlaxEditor.Surface
|
|||||||
new InputActionsContainer.Binding(options => options.Cut, Cut),
|
new InputActionsContainer.Binding(options => options.Cut, Cut),
|
||||||
new InputActionsContainer.Binding(options => options.Duplicate, Duplicate),
|
new InputActionsContainer.Binding(options => options.Duplicate, Duplicate),
|
||||||
new InputActionsContainer.Binding(options => options.NodesAutoFormat, () => { FormatGraph(SelectedNodes); }),
|
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.NodesAlignTop, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Top); }),
|
||||||
new InputActionsContainer.Binding(options => options.NodesAlignMiddle, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Middle); }),
|
new InputActionsContainer.Binding(options => options.NodesAlignMiddle, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Middle); }),
|
||||||
new InputActionsContainer.Binding(options => options.NodesAlignBottom, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Bottom); }),
|
new InputActionsContainer.Binding(options => options.NodesAlignBottom, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Bottom); }),
|
||||||
|
|||||||
Reference in New Issue
Block a user