From 63b0bf428b04c59ce43ee44b4d6400a77cc9906c Mon Sep 17 00:00:00 2001 From: Igor Vasiak Date: Tue, 15 Oct 2019 12:13:55 -0300 Subject: [PATCH] Added gradients to connection noodles (#191) Upgrade notice: Override GetNoodleGradient instead of GetNoodleColor --- Scripts/Editor/NodeEditorAction.cs | 11 ++-- Scripts/Editor/NodeEditorGUI.cs | 96 ++++++++++++++++++++++-------- Scripts/Editor/NodeGraphEditor.cs | 32 ++++++++-- 3 files changed, 104 insertions(+), 35 deletions(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 74d27a4..b2549b1 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -274,13 +274,13 @@ namespace XNodeEditor { ShowPortContextMenu(hoveredPort); } else if (IsHoveringNode && IsHoveringTitle(hoveredNode)) { if (!Selection.Contains(hoveredNode)) SelectNode(hoveredNode, false); - autoConnectOutput = null; + autoConnectOutput = null; GenericMenu menu = new GenericMenu(); NodeEditor.GetEditor(hoveredNode, this).AddContextMenuItems(menu); menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); e.Use(); // Fixes copy/paste context menu appearing in Unity 5.6.6f2 - doesn't occur in 2018.3.2f1 Probably needs to be used in other places. } else if (!IsHoveringNode) { - autoConnectOutput = null; + autoConnectOutput = null; GenericMenu menu = new GenericMenu(); graphEditor.AddContextMenuItems(menu); menu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); @@ -471,8 +471,7 @@ namespace XNodeEditor { /// Draw a connection as we are dragging it public void DrawDraggedConnection() { if (IsDraggingPort) { - Color col = NodeEditorPreferences.GetTypeColor(draggedOutput.ValueType); - col.a = draggedOutputTarget != null ? 1.0f : 0.6f; + Gradient gradient = graphEditor.GetNoodleGradient(draggedOutput, null); Rect fromRect; if (!_portConnectionPoints.TryGetValue(draggedOutput, out fromRect)) return; @@ -484,10 +483,10 @@ namespace XNodeEditor { if (draggedOutputTarget != null) gridPoints.Add(portConnectionPoints[draggedOutputTarget].center); else gridPoints.Add(WindowToGridPosition(Event.current.mousePosition)); - DrawNoodle(col, gridPoints); + DrawNoodle(gradient, gridPoints); Color bgcol = Color.black; - Color frcol = col; + Color frcol = gradient.colorKeys[0].color; bgcol.a = 0.6f; frcol.a = 0.6f; diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 37977cf..1d7c931 100644 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -117,39 +117,64 @@ namespace XNodeEditor { } /// Draw a bezier from output to input in grid coordinates - public void DrawNoodle(Color col, List gridPoints) { + public void DrawNoodle(Gradient gradient, List gridPoints) { Vector2[] windowPoints = gridPoints.Select(x => GridToWindowPosition(x)).ToArray(); - Handles.color = col; + Handles.color = gradient.Evaluate(0f); int length = gridPoints.Count; switch (NodeEditorPreferences.GetSettings().noodleType) { case NodeEditorPreferences.NoodleType.Curve: Vector2 outputTangent = Vector2.right; for (int i = 0; i < length - 1; i++) { Vector2 inputTangent = Vector2.left; - - if (i == 0) outputTangent = Vector2.right * Vector2.Distance(windowPoints[i], windowPoints[i + 1]) * 0.01f * zoom; + // Cached most variables that repeat themselves here to avoid so many indexer calls :p + Vector2 point_a = windowPoints[i]; + Vector2 point_b = windowPoints[i + 1]; + float dist_ab = Vector2.Distance(point_a, point_b); + if (i == 0) outputTangent = Vector2.right * dist_ab * 0.01f * zoom; if (i < length - 2) { - Vector2 ab = (windowPoints[i + 1] - windowPoints[i]).normalized; - Vector2 cb = (windowPoints[i + 1] - windowPoints[i + 2]).normalized; - Vector2 ac = (windowPoints[i + 2] - windowPoints[i]).normalized; + Vector2 point_c = windowPoints[i + 2]; + Vector2 ab = (point_b - point_a).normalized; + Vector2 cb = (point_b - point_c).normalized; + Vector2 ac = (point_c - point_a).normalized; Vector2 p = (ab + cb) * 0.5f; - float tangentLength = (Vector2.Distance(windowPoints[i], windowPoints[i + 1]) + Vector2.Distance(windowPoints[i + 1], windowPoints[i + 2])) * 0.005f * zoom; - float side = ((ac.x * (windowPoints[i + 1].y - windowPoints[i].y)) - (ac.y * (windowPoints[i + 1].x - windowPoints[i].x))); + float tangentLength = (dist_ab + Vector2.Distance(point_b, point_c)) * 0.005f * zoom; + float side = ((ac.x * (point_b.y - point_a.y)) - (ac.y * (point_b.x - point_a.x))); p = new Vector2(-p.y, p.x) * Mathf.Sign(side) * tangentLength; inputTangent = p; - } - else { - inputTangent = Vector2.left * Vector2.Distance(windowPoints[i], windowPoints[i + 1]) * 0.01f * zoom; + } else { + inputTangent = Vector2.left * dist_ab * 0.01f * zoom; } - Handles.DrawBezier(windowPoints[i], windowPoints[i + 1], windowPoints[i] + ((outputTangent * 50) / zoom), windowPoints[i + 1] + ((inputTangent * 50) / zoom), col, null, 4); + // Calculates the tangents for the bezier's curves. + Vector2 tangent_a = point_a + outputTangent * 50 / zoom; + Vector2 tangent_b = point_b + inputTangent * 50 / zoom; + // Hover effect. + int bezier_width = 4; + int division = Mathf.RoundToInt(.1f * dist_ab) + 3; + Vector3[] points = Handles.MakeBezierPoints(point_a, point_b, tangent_a, tangent_b, division); + // Coloring and bezier drawing. + for (int j = 0; j < points.Length - 1; j++) { + if (i == gridPoints.Count - 2) Handles.color = gradient.Evaluate((j + 1f) / points.Length); + Handles.DrawAAPolyLine(bezier_width, points[j], points[j + 1]); + } outputTangent = -inputTangent; } break; case NodeEditorPreferences.NoodleType.Line: for (int i = 0; i < length - 1; i++) { - Handles.DrawAAPolyLine(5, windowPoints[i], windowPoints[i + 1]); + Vector2 point_a = windowPoints[i]; + Vector2 point_b = windowPoints[i + 1]; + // Hover effect. + int line_width = 5; + // Draws the line with the coloring. + Vector2 prev_point = point_a; + for (float j = 0; j < 1; j += 10f / Vector2.Distance(point_a, point_b)) { + Vector2 lerp = Vector2.Lerp(point_a, point_b, j); + if (i == gridPoints.Count - 2) Handles.color = gradient.Evaluate(j); + Handles.DrawAAPolyLine(line_width, prev_point, lerp); + prev_point = lerp; + } } break; case NodeEditorPreferences.NoodleType.Angled: @@ -161,9 +186,17 @@ namespace XNodeEditor { Vector2 end_1 = windowPoints[i + 1]; start_1.x = midpoint; end_1.x = midpoint; - Handles.DrawAAPolyLine(5, windowPoints[i], start_1); - Handles.DrawAAPolyLine(5, start_1, end_1); - Handles.DrawAAPolyLine(5, end_1, windowPoints[i + 1]); + if (i == gridPoints.Count - 2) { + Handles.DrawAAPolyLine(5, windowPoints[i], start_1); + Handles.color = gradient.Evaluate(0.5f); + Handles.DrawAAPolyLine(5, start_1, end_1); + Handles.color = gradient.Evaluate(1f); + Handles.DrawAAPolyLine(5, end_1, windowPoints[i + 1]); + } else { + Handles.DrawAAPolyLine(5, windowPoints[i], start_1); + Handles.DrawAAPolyLine(5, start_1, end_1); + Handles.DrawAAPolyLine(5, end_1, windowPoints[i + 1]); + } } else { float midpoint = (windowPoints[i].y + windowPoints[i + 1].y) * 0.5f; Vector2 start_1 = windowPoints[i]; @@ -174,11 +207,23 @@ namespace XNodeEditor { Vector2 end_2 = end_1; start_2.y = midpoint; end_2.y = midpoint; - Handles.DrawAAPolyLine(5, windowPoints[i], start_1); - Handles.DrawAAPolyLine(5, start_1, start_2); - Handles.DrawAAPolyLine(5, start_2, end_2); - Handles.DrawAAPolyLine(5, end_2, end_1); - Handles.DrawAAPolyLine(5, end_1, windowPoints[i + 1]); + if (i == gridPoints.Count - 2) { + Handles.DrawAAPolyLine(5, windowPoints[i], start_1); + Handles.color = gradient.Evaluate(0.25f); + Handles.DrawAAPolyLine(5, start_1, start_2); + Handles.color = gradient.Evaluate(0.5f); + Handles.DrawAAPolyLine(5, start_2, end_2); + Handles.color = gradient.Evaluate(0.75f); + Handles.DrawAAPolyLine(5, end_2, end_1); + Handles.color = gradient.Evaluate(1f); + Handles.DrawAAPolyLine(5, end_1, windowPoints[i + 1]); + } else { + Handles.DrawAAPolyLine(5, windowPoints[i], start_1); + Handles.DrawAAPolyLine(5, start_1, start_2); + Handles.DrawAAPolyLine(5, start_2, end_2); + Handles.DrawAAPolyLine(5, end_2, end_1); + Handles.DrawAAPolyLine(5, end_1, windowPoints[i + 1]); + } } } break; @@ -202,10 +247,11 @@ namespace XNodeEditor { Rect fromRect; if (!_portConnectionPoints.TryGetValue(output, out fromRect)) continue; + Color portColor = graphEditor.GetPortColor(output); for (int k = 0; k < output.ConnectionCount; k++) { XNode.NodePort input = output.GetConnection(k); - Color noodleColor = graphEditor.GetNoodleColor(output, input); + Gradient noodleGradient = graphEditor.GetNoodleGradient(output, input); // Error handling if (input == null) continue; //If a script has been updated and the port doesn't exist, it is removed and null is returned. If this happens, return. @@ -219,7 +265,7 @@ namespace XNodeEditor { gridPoints.Add(fromRect.center); gridPoints.AddRange(reroutePoints); gridPoints.Add(toRect.center); - DrawNoodle(noodleColor, gridPoints); + DrawNoodle(noodleGradient, gridPoints); // Loop through reroute points again and draw the points for (int i = 0; i < reroutePoints.Count; i++) { @@ -235,7 +281,7 @@ namespace XNodeEditor { GUI.DrawTexture(rect, NodeEditorResources.dotOuter); } - GUI.color = noodleColor; + GUI.color = portColor; GUI.DrawTexture(rect, NodeEditorResources.dot); if (rect.Overlaps(selectionBox)) selection.Add(rerouteRef); if (rect.Contains(mousePos)) hoveredReroute = rerouteRef; diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index e993abe..93c915f 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -64,10 +64,34 @@ namespace XNodeEditor { } /// Returned color is used to color noodles - public virtual Color GetNoodleColor(XNode.NodePort output, XNode.NodePort input) { - Color col = GetTypeColor(output.ValueType); - if (window.hoveredPort == output || window.hoveredPort == input) return Color.Lerp(col, Color.white, 0.8f); - return col; + /// The output this noodle comes from. Never null. + /// The output this noodle comes from. Can be null if we are dragging the noodle. + public virtual Gradient GetNoodleGradient(XNode.NodePort output, XNode.NodePort input) { + Gradient grad = new Gradient(); + + // If dragging the noodle, draw solid, slightly transparent + if (input == null) { + Color a = GetTypeColor(output.ValueType); + grad.SetKeys( + new GradientColorKey[] { new GradientColorKey(a, 0f) }, + new GradientAlphaKey[] { new GradientAlphaKey(0.6f, 0f) } + ); + } + // If normal, draw gradient fading from one input color to the other + else { + Color a = GetTypeColor(output.ValueType); + Color b = GetTypeColor(input.ValueType); + // If any port is hovered, tint white + if (window.hoveredPort == output || window.hoveredPort == input) { + a = Color.Lerp(a, Color.white, 0.8f); + b = Color.Lerp(b, Color.white, 0.8f); + } + grad.SetKeys( + new GradientColorKey[] { new GradientColorKey(a, 0f), new GradientColorKey(b, 1f) }, + new GradientAlphaKey[] { new GradientAlphaKey(1f, 0f), new GradientAlphaKey(1f, 1f) } + ); + } + return grad; } /// Returned color is used to color ports