From 9265f23681fe749048f30677a208fd3b8a16ec1a Mon Sep 17 00:00:00 2001 From: stefnotch Date: Thu, 25 Feb 2021 20:22:54 +0100 Subject: [PATCH] Basic reroute nodes --- Source/Editor/Surface/Archetypes/Tools.cs | 126 ++++++++++++++++++ Source/Editor/Surface/Elements/OutputBox.cs | 40 ++++-- Source/Editor/Surface/SurfaceNode.cs | 3 +- Source/Editor/Surface/VisjectSurface.Input.cs | 44 +++++- 4 files changed, 195 insertions(+), 18 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Tools.cs b/Source/Editor/Surface/Archetypes/Tools.cs index 2b63bdfb2..6330b27dd 100644 --- a/Source/Editor/Surface/Archetypes/Tools.cs +++ b/Source/Editor/Surface/Archetypes/Tools.cs @@ -983,6 +983,115 @@ namespace FlaxEditor.Surface.Archetypes } } + private class RerouteNode : SurfaceNode + { + public static readonly Vector2 DefaultSize = new Vector2(16); + + private bool _deleteNode; + + /// + protected override bool ShowTooltip => false; + + /// + public RerouteNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch) + : base(id, context, nodeArch, groupArch) + { + Size = DefaultSize; + Title = string.Empty; + BackgroundColor = Color.Transparent; + } + + /// + public override void OnSurfaceLoaded() + { + base.OnSurfaceLoaded(); + + var inputBox = GetBox(0); + var outputBox = GetBox(1); + inputBox.Location = Vector2.Zero; + outputBox.Location = Vector2.Zero; + + inputBox.Visible = false; + outputBox.Visible = false; + } + + /// + public override void ConnectionTick(Box box) + { + base.ConnectionTick(box); + + var inputBox = GetBox(0); + var outputBox = GetBox(1); + + _deleteNode = !inputBox.HasAnyConnection; + outputBox.Visible = !outputBox.HasAnyConnection; + } + + /*public override void OnDeleted() + { + var inputBox = GetBox(0); + var outputBox = GetBox(1); + + var connectionA = inputBox.HasAnyConnection ? inputBox.Connections[0] : null; // This doesn't work + var connectionB = outputBox.HasAnyConnection ? outputBox.Connections[0] : null; + + base.OnDeleted(); + + if (connectionA != null && connectionB != null) + { + connectionA.CreateConnection(connectionB); // TODO: handle undo + } + }*/ + + public override void Update(float deltaTime) + { + base.Update(deltaTime); + + if (_deleteNode) + { + _deleteNode = false; + Surface.Delete(this); // TODO: handle undo + } + } + + + /// + public override bool CanSelect(ref Vector2 location) + { + return new Rectangle(Location, DefaultSize).Contains(ref location); + } + + /// + protected override void UpdateRectangles() + { + _headerRect = Rectangle.Empty; + _closeButtonRect = Rectangle.Empty; + _footerRect = Rectangle.Empty; + } + + public override void Draw() + { + var style = Surface.Style; + var inputBox = GetBox(0); + var connectionColor = style.Colors.Default; + + if (inputBox.HasAnyConnection) + { + var hints = inputBox.Connections[0].ParentNode.Archetype.ConnectionsHints; + Surface.Style.GetConnectionColor(inputBox.Connections[0].CurrentType, hints, out connectionColor); + } + + //float barHeight = 4; + //Render2D.FillRectangle(new Rectangle(DefaultSize.X / 2, (DefaultSize.Y - barHeight) / 2, barHeight * 4, barHeight), connectionColor); + + SpriteHandle icon = style.Icons.BoxClose; + Render2D.DrawSprite(icon, new Rectangle(Vector2.Zero, DefaultSize), connectionColor); + + base.Draw(); + + } + } + /// /// The nodes for that group. /// @@ -1438,6 +1547,23 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(object), 1), } }, + new NodeArchetype + { + TypeID = 29, + Title = "Reroute", + Create = (id, context, arch, groupArch) => new RerouteNode(id, context, arch, groupArch), + Description = "Reroute a connection.", + Flags = NodeFlags.NoCloseButton | NodeFlags.NoSpawnViaGUI | NodeFlags.AllGraphs, + Size = RerouteNode.DefaultSize, + ConnectionsHints = ConnectionsHint.All, + IndependentBoxes = new int[] { 0 }, + DependentBoxes = new int[] { 1 }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, string.Empty, true, null, 0), + NodeElementArchetype.Factory.Output(0, string.Empty, null, 1), + } + }, }; } } diff --git a/Source/Editor/Surface/Elements/OutputBox.cs b/Source/Editor/Surface/Elements/OutputBox.cs index 852132baf..683c7eebd 100644 --- a/Source/Editor/Surface/Elements/OutputBox.cs +++ b/Source/Editor/Surface/Elements/OutputBox.cs @@ -13,6 +13,11 @@ namespace FlaxEditor.Surface.Elements [HideInEditor] public class OutputBox : Box { + /// + /// Distance for the mouse to be considered above the connection + /// + public float MouseOverConnectionDistance => 100f / Surface.ViewScale; + /// public OutputBox(SurfaceNode parentNode, NodeElementArchetype archetype) : base(parentNode, archetype, archetype.Position + new Vector2(parentNode.Archetype.Size.X, 0)) @@ -44,6 +49,18 @@ namespace FlaxEditor.Surface.Elements */ } + /// + /// Checks if a point intersects a connection + /// + /// The other box. + /// The mouse position + public bool IntersectsConnection(Box targetBox, ref Vector2 mousePosition) + { + var startPos = Parent.PointToParent(Center); + Vector2 endPos = targetBox.Parent.PointToParent(targetBox.Center); + return IntersectsConnection(ref startPos, ref endPos, ref mousePosition, MouseOverConnectionDistance); + } + /// /// Checks if a point intersects a bezier curve /// @@ -53,8 +70,6 @@ namespace FlaxEditor.Surface.Elements /// Distance at which its an intersection public static bool IntersectsConnection(ref Vector2 start, ref Vector2 end, ref Vector2 point, float distance) { - // Maybe I should make this a non-static method and automatically read the Surface.ViewScale? - // Pretty much a point in rectangle check if ((point.X - start.X) * (end.X - point.X) < 0) return false; @@ -82,6 +97,7 @@ namespace FlaxEditor.Surface.Elements float t = i * segmentCountInv; Bezier(ref start, ref control1, ref control2, ref end, t, out p); + // Maybe it would be reasonable to return the point? CollisionsHelper.ClosestPointPointLine(ref point, ref oldp, ref p, out Vector2 result); if (Vector2.DistanceSquared(point, result) <= squaredDistance) { @@ -106,22 +122,21 @@ namespace FlaxEditor.Surface.Elements /// public void DrawConnections(ref Vector2 mousePosition) { + float mouseOverDistance = MouseOverConnectionDistance; // Draw all the connections - var center = Size * 0.5f; - var tmp = PointToParent(ref center); - var startPos = Parent.PointToParent(ref tmp); + var startPos = Parent.PointToParent(Center); var startHighlight = ConnectionsHighlightIntensity; for (int i = 0; i < Connections.Count; i++) { Box targetBox = Connections[i]; - tmp = targetBox.PointToParent(ref center); - Vector2 endPos = targetBox.Parent.PointToParent(ref tmp); + Vector2 endPos = targetBox.Parent.PointToParent(targetBox.Center); var highlight = 1 + Mathf.Max(startHighlight, targetBox.ConnectionsHighlightIntensity); var color = _currentTypeColor * highlight; - if (IntersectsConnection(ref startPos, ref endPos, ref mousePosition, 100f / Surface.ViewScale)) + // TODO: Figure out how to only draw the topmost connection + if (IntersectsConnection(ref startPos, ref endPos, ref mousePosition, mouseOverDistance)) { - highlight += 2; + highlight += 1; } DrawConnection(ref startPos, ref endPos, ref color, highlight); @@ -134,11 +149,8 @@ namespace FlaxEditor.Surface.Elements public void DrawSelectedConnection(Box targetBox) { // Draw all the connections - var center = Size * 0.5f; - var tmp = PointToParent(ref center); - var startPos = Parent.PointToParent(ref tmp); - tmp = targetBox.PointToParent(ref center); - Vector2 endPos = targetBox.Parent.PointToParent(ref tmp); + var startPos = Parent.PointToParent(Center); + Vector2 endPos = targetBox.Parent.PointToParent(targetBox.Center); DrawConnection(ref startPos, ref endPos, ref _currentTypeColor, 2); } diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index d1ea32da7..3c544dbdd 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -125,6 +125,7 @@ namespace FlaxEditor.Surface AutoFocus = false; TooltipText = nodeArch.Description; CullChildren = false; + BackgroundColor = Style.Current.BackgroundNormal; if (Archetype.DefaultValues != null) { @@ -947,7 +948,7 @@ namespace FlaxEditor.Surface // Background var backgroundRect = new Rectangle(Vector2.Zero, Size); - Render2D.FillRectangle(backgroundRect, style.BackgroundNormal); + Render2D.FillRectangle(backgroundRect, BackgroundColor); // Breakpoint hit if (Breakpoint.Hit) diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index 28cff42b8..b28ac340f 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -274,12 +274,24 @@ namespace FlaxEditor.Surface bool handled = base.OnMouseDoubleClick(location, button); if (!handled) CustomMouseDoubleClick?.Invoke(ref location, button, ref handled); - if (handled) + + if (!handled) { - return true; + var mousePos = _rootControl.PointFromParent(ref _mousePos); + if (IntersectsConnection(mousePos, out OutputBox outputBox, out Box connectedBox)) + { + var rerouteNode = Context.SpawnNode(7, 29, mousePos); + + // TODO: Undo action + outputBox.BreakConnection(connectedBox); + outputBox.CreateConnection(rerouteNode.GetBoxes().First(b => !b.IsOutput)); + rerouteNode.GetBoxes().First(b => b.IsOutput).CreateConnection(connectedBox); + + handled = true; + } } - return false; + return handled; } /// @@ -815,5 +827,31 @@ namespace FlaxEditor.Surface yLocation ); } + + private bool IntersectsConnection(Vector2 mousePosition, out OutputBox outputBox, out Box connectedBox) + { + for (int i = 0; i < Nodes.Count; i++) + { + for (int j = 0; j < Nodes[i].Elements.Count; j++) + { + if (Nodes[i].Elements[j] is OutputBox ob) + { + for (int k = 0; k < ob.Connections.Count; k++) + { + if (ob.IntersectsConnection(ob.Connections[k], ref mousePosition)) + { + outputBox = ob; + connectedBox = ob.Connections[k]; + return true; + } + } + } + } + } + + outputBox = null; + connectedBox = null; + return false; + } } }