From 810960e4a6ddc6e2fc1de3314444cb0bad79bbb0 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Fri, 27 Sep 2019 18:49:11 +0200 Subject: [PATCH 01/21] Fixed #190 - OnWillDeleteAsset() conflicts with lighting data regeneration --- Scripts/Editor/NodeEditorAssetModProcessor.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Scripts/Editor/NodeEditorAssetModProcessor.cs b/Scripts/Editor/NodeEditorAssetModProcessor.cs index edaebaa..f4b14a2 100644 --- a/Scripts/Editor/NodeEditorAssetModProcessor.cs +++ b/Scripts/Editor/NodeEditorAssetModProcessor.cs @@ -1,5 +1,6 @@ using UnityEditor; using UnityEngine; +using System.IO; namespace XNodeEditor { /// Deals with modified assets @@ -9,6 +10,9 @@ namespace XNodeEditor { /// This is important to do, because you can't delete null sub assets. /// For another workaround, see: https://gitlab.com/RotaryHeart-UnityShare/subassetmissingscriptdelete private static AssetDeleteResult OnWillDeleteAsset (string path, RemoveAssetOptions options) { + // Skip processing anything without the .cs extension + if (Path.GetExtension(path) != ".cs") return AssetDeleteResult.DidNotDelete; + // Get the object that is requested for deletion UnityEngine.Object obj = AssetDatabase.LoadAssetAtPath (path); From 6a1c1dbb4fef935115d036ce7c999d4053279495 Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Wed, 9 Oct 2019 10:20:11 +0200 Subject: [PATCH 02/21] This line was not present before commit 43bcb54fda4378e46a678c866242954248a63702 (Odin inspector support (#182) and causes the window to repaint every frame but only if you have a custom node editor. --- Scripts/Editor/NodeEditor.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Scripts/Editor/NodeEditor.cs b/Scripts/Editor/NodeEditor.cs index 812d3c0..13440ea 100644 --- a/Scripts/Editor/NodeEditor.cs +++ b/Scripts/Editor/NodeEditor.cs @@ -72,8 +72,6 @@ namespace XNodeEditor { GUIHelper.ClearRepaintRequest(); window.Repaint(); } -#else - window.Repaint(); #endif #if ODIN_INSPECTOR From 1b9a04017b4c0b866e13c3592e8174df682bb95c Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Wed, 9 Oct 2019 10:20:28 +0200 Subject: [PATCH 03/21] this action needs a repaint --- Scripts/Editor/NodeEditorAction.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index cf5929d..74d27a4 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -309,6 +309,7 @@ namespace XNodeEditor { SelectNode(node, true); } } + Repaint(); } break; case EventType.ValidateCommand: From 63b0bf428b04c59ce43ee44b4d6400a77cc9906c Mon Sep 17 00:00:00 2001 From: Igor Vasiak Date: Tue, 15 Oct 2019 12:13:55 -0300 Subject: [PATCH 04/21] 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 From 08af93c2e312855d4b2e18bc7a92d763f7ebfa91 Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Wed, 16 Oct 2019 23:08:07 +0200 Subject: [PATCH 05/21] Added NodeGraphEditor.GetNoodleThickness --- Scripts/Editor/NodeEditorAction.cs | 3 +- Scripts/Editor/NodeEditorGUI.cs | 44 ++++++++++++++---------------- Scripts/Editor/NodeGraphEditor.cs | 9 +++++- 3 files changed, 31 insertions(+), 25 deletions(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index b2549b1..7fdc91e 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -472,6 +472,7 @@ namespace XNodeEditor { public void DrawDraggedConnection() { if (IsDraggingPort) { Gradient gradient = graphEditor.GetNoodleGradient(draggedOutput, null); + float thickness = graphEditor.GetNoodleThickness(draggedOutput, null); Rect fromRect; if (!_portConnectionPoints.TryGetValue(draggedOutput, out fromRect)) return; @@ -483,7 +484,7 @@ namespace XNodeEditor { if (draggedOutputTarget != null) gridPoints.Add(portConnectionPoints[draggedOutputTarget].center); else gridPoints.Add(WindowToGridPosition(Event.current.mousePosition)); - DrawNoodle(gradient, gridPoints); + DrawNoodle(gradient, thickness, gridPoints); Color bgcol = Color.black; Color frcol = gradient.colorKeys[0].color; diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 1d7c931..76dbb8f 100644 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -117,7 +117,7 @@ namespace XNodeEditor { } /// Draw a bezier from output to input in grid coordinates - public void DrawNoodle(Gradient gradient, List gridPoints) { + public void DrawNoodle(Gradient gradient, float width, List gridPoints) { Vector2[] windowPoints = gridPoints.Select(x => GridToWindowPosition(x)).ToArray(); Handles.color = gradient.Evaluate(0f); int length = gridPoints.Count; @@ -150,13 +150,12 @@ namespace XNodeEditor { 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]); + Handles.DrawAAPolyLine(width, points[j], points[j + 1]); } outputTangent = -inputTangent; } @@ -165,14 +164,12 @@ namespace XNodeEditor { for (int i = 0; i < length - 1; i++) { 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); + Handles.DrawAAPolyLine(width, prev_point, lerp); prev_point = lerp; } } @@ -187,15 +184,15 @@ namespace XNodeEditor { start_1.x = midpoint; end_1.x = midpoint; if (i == gridPoints.Count - 2) { - Handles.DrawAAPolyLine(5, windowPoints[i], start_1); + Handles.DrawAAPolyLine(width, windowPoints[i], start_1); Handles.color = gradient.Evaluate(0.5f); - Handles.DrawAAPolyLine(5, start_1, end_1); + Handles.DrawAAPolyLine(width, start_1, end_1); Handles.color = gradient.Evaluate(1f); - Handles.DrawAAPolyLine(5, end_1, windowPoints[i + 1]); + Handles.DrawAAPolyLine(width, 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]); + Handles.DrawAAPolyLine(width, windowPoints[i], start_1); + Handles.DrawAAPolyLine(width, start_1, end_1); + Handles.DrawAAPolyLine(width, end_1, windowPoints[i + 1]); } } else { float midpoint = (windowPoints[i].y + windowPoints[i + 1].y) * 0.5f; @@ -208,21 +205,21 @@ namespace XNodeEditor { start_2.y = midpoint; end_2.y = midpoint; if (i == gridPoints.Count - 2) { - Handles.DrawAAPolyLine(5, windowPoints[i], start_1); + Handles.DrawAAPolyLine(width, windowPoints[i], start_1); Handles.color = gradient.Evaluate(0.25f); - Handles.DrawAAPolyLine(5, start_1, start_2); + Handles.DrawAAPolyLine(width, start_1, start_2); Handles.color = gradient.Evaluate(0.5f); - Handles.DrawAAPolyLine(5, start_2, end_2); + Handles.DrawAAPolyLine(width, start_2, end_2); Handles.color = gradient.Evaluate(0.75f); - Handles.DrawAAPolyLine(5, end_2, end_1); + Handles.DrawAAPolyLine(width, end_2, end_1); Handles.color = gradient.Evaluate(1f); - Handles.DrawAAPolyLine(5, end_1, windowPoints[i + 1]); + Handles.DrawAAPolyLine(width, 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]); + Handles.DrawAAPolyLine(width, windowPoints[i], start_1); + Handles.DrawAAPolyLine(width, start_1, start_2); + Handles.DrawAAPolyLine(width, start_2, end_2); + Handles.DrawAAPolyLine(width, end_2, end_1); + Handles.DrawAAPolyLine(width, end_1, windowPoints[i + 1]); } } } @@ -252,6 +249,7 @@ namespace XNodeEditor { XNode.NodePort input = output.GetConnection(k); Gradient noodleGradient = graphEditor.GetNoodleGradient(output, input); + float noodleThickness = graphEditor.GetNoodleThickness(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. @@ -265,7 +263,7 @@ namespace XNodeEditor { gridPoints.Add(fromRect.center); gridPoints.AddRange(reroutePoints); gridPoints.Add(toRect.center); - DrawNoodle(noodleGradient, gridPoints); + DrawNoodle(noodleGradient, noodleThickness, gridPoints); // Loop through reroute points again and draw the points for (int i = 0; i < reroutePoints.Count; i++) { diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index 93c915f..0829e4d 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -63,7 +63,7 @@ namespace XNodeEditor { menu.AddCustomContextMenuItems(target); } - /// Returned color is used to color noodles + /// Returned gradient is used to color noodles /// 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) { @@ -94,6 +94,13 @@ namespace XNodeEditor { return grad; } + /// Returned float is used for noodle thickness + /// 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 float GetNoodleThickness(XNode.NodePort output, XNode.NodePort input) { + return 5f; + } + /// Returned color is used to color ports public virtual Color GetPortColor(XNode.NodePort port) { return GetTypeColor(port.ValueType); From 421fb5747eba9b8e4ec5c41b2bd7ef397413a50e Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Thu, 17 Oct 2019 00:16:36 +0200 Subject: [PATCH 06/21] Added virtual NodeEditorGraph.GetNoodlePath and GetNoodleStroke for additional customization --- Scripts/Editor/NodeEditorAction.cs | 4 +- Scripts/Editor/NodeEditorGUI.cs | 74 +++++++++++++++---------- Scripts/Editor/NodeEditorPreferences.cs | 13 +++-- Scripts/Editor/NodeGraphEditor.cs | 8 +++ 4 files changed, 66 insertions(+), 33 deletions(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 7fdc91e..baf9400 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -473,6 +473,8 @@ namespace XNodeEditor { if (IsDraggingPort) { Gradient gradient = graphEditor.GetNoodleGradient(draggedOutput, null); float thickness = graphEditor.GetNoodleThickness(draggedOutput, null); + NoodlePath path = graphEditor.GetNoodlePath(draggedOutput, null); + NoodleStroke stroke = graphEditor.GetNoodleStroke(draggedOutput, null); Rect fromRect; if (!_portConnectionPoints.TryGetValue(draggedOutput, out fromRect)) return; @@ -484,7 +486,7 @@ namespace XNodeEditor { if (draggedOutputTarget != null) gridPoints.Add(portConnectionPoints[draggedOutputTarget].center); else gridPoints.Add(WindowToGridPosition(Event.current.mousePosition)); - DrawNoodle(gradient, thickness, gridPoints); + DrawNoodle(gradient, path, stroke, thickness, gridPoints); Color bgcol = Color.black; Color frcol = gradient.colorKeys[0].color; diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 76dbb8f..cae1f15 100644 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -117,12 +117,12 @@ namespace XNodeEditor { } /// Draw a bezier from output to input in grid coordinates - public void DrawNoodle(Gradient gradient, float width, List gridPoints) { + public void DrawNoodle(Gradient gradient, NoodlePath path, NoodleStroke stroke, float thickness, List gridPoints) { Vector2[] windowPoints = gridPoints.Select(x => GridToWindowPosition(x)).ToArray(); Handles.color = gradient.Evaluate(0f); int length = gridPoints.Count; - switch (NodeEditorPreferences.GetSettings().noodleType) { - case NodeEditorPreferences.NoodleType.Curve: + switch (path) { + case NoodlePath.Curvy: Vector2 outputTangent = Vector2.right; for (int i = 0; i < length - 1; i++) { Vector2 inputTangent = Vector2.left; @@ -150,31 +150,47 @@ namespace XNodeEditor { Vector2 tangent_a = point_a + outputTangent * 50 / zoom; Vector2 tangent_b = point_b + inputTangent * 50 / zoom; // Hover effect. - int division = Mathf.RoundToInt(.1f * dist_ab) + 3; + int division = Mathf.RoundToInt(.2f * dist_ab) + 3; Vector3[] points = Handles.MakeBezierPoints(point_a, point_b, tangent_a, tangent_b, division); + int draw = 0; // Coloring and bezier drawing. for (int j = 0; j < points.Length - 1; j++) { + if (stroke == NoodleStroke.Dashed) { + draw++; + if (draw >= 2) draw = -2; + if (draw < 0) continue; + } + if (i == gridPoints.Count - 2) Handles.color = gradient.Evaluate((j + 1f) / points.Length); - Handles.DrawAAPolyLine(width, points[j], points[j + 1]); + Handles.DrawAAPolyLine(thickness, points[j], points[j + 1]); } outputTangent = -inputTangent; } break; - case NodeEditorPreferences.NoodleType.Line: + case NoodlePath.Straight: for (int i = 0; i < length - 1; i++) { Vector2 point_a = windowPoints[i]; Vector2 point_b = windowPoints[i + 1]; // 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(width, prev_point, lerp); + // Approximately one segment per 5 pixels + int segments = (int) Vector2.Distance(point_a, point_b) / 5; + + int draw = 0; + for (int j = 0; j <= segments; j++) { + draw++; + float t = j / (float) segments; + Vector2 lerp = Vector2.Lerp(point_a, point_b, t); + if (draw > 0) { + if (i == gridPoints.Count - 2) Handles.color = gradient.Evaluate(t); + Handles.DrawAAPolyLine(thickness, prev_point, lerp); + } prev_point = lerp; + if (stroke == NoodleStroke.Dashed && draw >= 2) draw = -2; } } break; - case NodeEditorPreferences.NoodleType.Angled: + case NoodlePath.Angled: for (int i = 0; i < length - 1; i++) { if (i == length - 1) continue; // Skip last index if (windowPoints[i].x <= windowPoints[i + 1].x - (50 / zoom)) { @@ -184,15 +200,15 @@ namespace XNodeEditor { start_1.x = midpoint; end_1.x = midpoint; if (i == gridPoints.Count - 2) { - Handles.DrawAAPolyLine(width, windowPoints[i], start_1); + Handles.DrawAAPolyLine(thickness, windowPoints[i], start_1); Handles.color = gradient.Evaluate(0.5f); - Handles.DrawAAPolyLine(width, start_1, end_1); + Handles.DrawAAPolyLine(thickness, start_1, end_1); Handles.color = gradient.Evaluate(1f); - Handles.DrawAAPolyLine(width, end_1, windowPoints[i + 1]); + Handles.DrawAAPolyLine(thickness, end_1, windowPoints[i + 1]); } else { - Handles.DrawAAPolyLine(width, windowPoints[i], start_1); - Handles.DrawAAPolyLine(width, start_1, end_1); - Handles.DrawAAPolyLine(width, end_1, windowPoints[i + 1]); + Handles.DrawAAPolyLine(thickness, windowPoints[i], start_1); + Handles.DrawAAPolyLine(thickness, start_1, end_1); + Handles.DrawAAPolyLine(thickness, end_1, windowPoints[i + 1]); } } else { float midpoint = (windowPoints[i].y + windowPoints[i + 1].y) * 0.5f; @@ -205,21 +221,21 @@ namespace XNodeEditor { start_2.y = midpoint; end_2.y = midpoint; if (i == gridPoints.Count - 2) { - Handles.DrawAAPolyLine(width, windowPoints[i], start_1); + Handles.DrawAAPolyLine(thickness, windowPoints[i], start_1); Handles.color = gradient.Evaluate(0.25f); - Handles.DrawAAPolyLine(width, start_1, start_2); + Handles.DrawAAPolyLine(thickness, start_1, start_2); Handles.color = gradient.Evaluate(0.5f); - Handles.DrawAAPolyLine(width, start_2, end_2); + Handles.DrawAAPolyLine(thickness, start_2, end_2); Handles.color = gradient.Evaluate(0.75f); - Handles.DrawAAPolyLine(width, end_2, end_1); + Handles.DrawAAPolyLine(thickness, end_2, end_1); Handles.color = gradient.Evaluate(1f); - Handles.DrawAAPolyLine(width, end_1, windowPoints[i + 1]); + Handles.DrawAAPolyLine(thickness, end_1, windowPoints[i + 1]); } else { - Handles.DrawAAPolyLine(width, windowPoints[i], start_1); - Handles.DrawAAPolyLine(width, start_1, start_2); - Handles.DrawAAPolyLine(width, start_2, end_2); - Handles.DrawAAPolyLine(width, end_2, end_1); - Handles.DrawAAPolyLine(width, end_1, windowPoints[i + 1]); + Handles.DrawAAPolyLine(thickness, windowPoints[i], start_1); + Handles.DrawAAPolyLine(thickness, start_1, start_2); + Handles.DrawAAPolyLine(thickness, start_2, end_2); + Handles.DrawAAPolyLine(thickness, end_2, end_1); + Handles.DrawAAPolyLine(thickness, end_1, windowPoints[i + 1]); } } } @@ -250,6 +266,8 @@ namespace XNodeEditor { Gradient noodleGradient = graphEditor.GetNoodleGradient(output, input); float noodleThickness = graphEditor.GetNoodleThickness(output, input); + NoodlePath noodlePath = graphEditor.GetNoodlePath(output, input); + NoodleStroke noodleStroke = graphEditor.GetNoodleStroke(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. @@ -263,7 +281,7 @@ namespace XNodeEditor { gridPoints.Add(fromRect.center); gridPoints.AddRange(reroutePoints); gridPoints.Add(toRect.center); - DrawNoodle(noodleGradient, noodleThickness, gridPoints); + DrawNoodle(noodleGradient, noodlePath, noodleStroke, noodleThickness, gridPoints); // Loop through reroute points again and draw the points for (int i = 0; i < reroutePoints.Count; i++) { diff --git a/Scripts/Editor/NodeEditorPreferences.cs b/Scripts/Editor/NodeEditorPreferences.cs index b3026b9..642e672 100644 --- a/Scripts/Editor/NodeEditorPreferences.cs +++ b/Scripts/Editor/NodeEditorPreferences.cs @@ -2,10 +2,13 @@ using System.Collections.Generic; using UnityEditor; using UnityEngine; +using UnityEngine.Serialization; namespace XNodeEditor { + public enum NoodlePath { Curvy, Straight, Angled } + public enum NoodleStroke { Full, Dashed } + public static class NodeEditorPreferences { - public enum NoodleType { Curve, Line, Angled } /// The last editor we checked. This should be the one we modify private static XNodeEditor.NodeGraphEditor lastEditor; @@ -37,7 +40,8 @@ namespace XNodeEditor { public bool portTooltips = true; [SerializeField] private string typeColorsData = ""; [NonSerialized] public Dictionary typeColors = new Dictionary(); - public NoodleType noodleType = NoodleType.Curve; + [FormerlySerializedAs("noodleType")] public NoodlePath noodlePath = NoodlePath.Curvy; + public NoodleStroke noodleStroke = NoodleStroke.Full; private Texture2D _gridTexture; public Texture2D gridTexture { @@ -106,7 +110,7 @@ namespace XNodeEditor { private static void PreferencesGUI() { VerifyLoaded(); Settings settings = NodeEditorPreferences.settings[lastKey]; - + if (GUILayout.Button(new GUIContent("Documentation", "https://github.com/Siccity/xNode/wiki"), GUILayout.Width(100))) Application.OpenURL("https://github.com/Siccity/xNode/wiki"); EditorGUILayout.Space(); @@ -151,7 +155,8 @@ namespace XNodeEditor { //Label EditorGUILayout.LabelField("Node", EditorStyles.boldLabel); settings.highlightColor = EditorGUILayout.ColorField("Selection", settings.highlightColor); - settings.noodleType = (NoodleType) EditorGUILayout.EnumPopup("Noodle type", (Enum) settings.noodleType); + settings.noodlePath = (NoodlePath) EditorGUILayout.EnumPopup("Noodle path", (Enum) settings.noodlePath); + settings.noodleStroke = (NoodleStroke) EditorGUILayout.EnumPopup("Noodle stroke", (Enum) settings.noodleStroke); settings.portTooltips = EditorGUILayout.Toggle("Port Tooltips", settings.portTooltips); settings.dragToCreate = EditorGUILayout.Toggle(new GUIContent("Drag to Create", "Drag a port connection anywhere on the grid to create and connect a node"), settings.dragToCreate); if (GUI.changed) { diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index 0829e4d..bc6e14c 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -101,6 +101,14 @@ namespace XNodeEditor { return 5f; } + public virtual NoodlePath GetNoodlePath(XNode.NodePort output, XNode.NodePort input) { + return NodeEditorPreferences.GetSettings().noodlePath; + } + + public virtual NoodleStroke GetNoodleStroke(XNode.NodePort output, XNode.NodePort input) { + return NodeEditorPreferences.GetSettings().noodleStroke; + } + /// Returned color is used to color ports public virtual Color GetPortColor(XNode.NodePort port) { return GetTypeColor(port.ValueType); From aeaaf180997cc5d4f506df9a2d70921ed4c9644d Mon Sep 17 00:00:00 2001 From: DeepWolf413 Date: Wed, 23 Oct 2019 12:01:20 +0200 Subject: [PATCH 07/21] Added a null check for NodeEditorWindow.current. It returns a new instance of the Settings class if the NodeEditorWindow.current is null. --- Scripts/Editor/NodeEditorPreferences.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Scripts/Editor/NodeEditorPreferences.cs b/Scripts/Editor/NodeEditorPreferences.cs index 642e672..467318a 100644 --- a/Scripts/Editor/NodeEditorPreferences.cs +++ b/Scripts/Editor/NodeEditorPreferences.cs @@ -81,6 +81,8 @@ namespace XNodeEditor { /// Get settings of current active editor public static Settings GetSettings() { + if (XNodeEditor.NodeEditorWindow.current == null) return new Settings(); + if (lastEditor != XNodeEditor.NodeEditorWindow.current.graphEditor) { object[] attribs = XNodeEditor.NodeEditorWindow.current.graphEditor.GetType().GetCustomAttributes(typeof(XNodeEditor.NodeGraphEditor.CustomNodeGraphEditorAttribute), true); if (attribs.Length == 1) { From 455107aabbf5a458fc914b3f32ab6ca87542cc07 Mon Sep 17 00:00:00 2001 From: apkd Date: Sun, 27 Oct 2019 16:30:05 +0100 Subject: [PATCH 08/21] Optimize NodeEditorGUI.DrawNoodle --- Scripts/Editor/NodeEditorGUI.cs | 112 +++++++++++++++++++------------- 1 file changed, 67 insertions(+), 45 deletions(-) diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index cae1f15..b8f55d1 100644 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -15,6 +15,7 @@ namespace XNodeEditor { private int topPadding { get { return isDocked() ? 19 : 22; } } /// Executed after all other window GUI. Useful if Zoom is ruining your day. Automatically resets after being run. public event Action onLateGUI; + private static readonly Vector3[] polyLineTempArray = new Vector3[2]; private void OnGUI() { Event e = Event.current; @@ -115,24 +116,41 @@ namespace XNodeEditor { contextMenu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); } + + static Vector3 CalculateBezierPoint(in Vector3 p0, in Vector3 p1, in Vector3 p2, in Vector3 p3, float t) { + float u = 1 - t; + float tt = t * t, uu = u * u; + float uuu = uu * u, ttt = tt * t; + return (uuu * p0) + (3 * uu * t * p1) + (3 * u * tt * p2) + (ttt * p3); + } + /// Draws a line segment without allocating temporary arrays + static void DrawAAPolyLineNonAlloc(float thickness, in Vector3 p0, in Vector3 p1) { + polyLineTempArray[0] = p0; + polyLineTempArray[1] = p1; + Handles.DrawAAPolyLine(thickness, polyLineTempArray); + } + /// Draw a bezier from output to input in grid coordinates public void DrawNoodle(Gradient gradient, NoodlePath path, NoodleStroke stroke, float thickness, List gridPoints) { - Vector2[] windowPoints = gridPoints.Select(x => GridToWindowPosition(x)).ToArray(); + // convert grid points to window points + for (int i = 0; i < gridPoints.Count; ++i) + gridPoints[i] = GridToWindowPosition(gridPoints[i]); + Handles.color = gradient.Evaluate(0f); int length = gridPoints.Count; switch (path) { case NoodlePath.Curvy: Vector2 outputTangent = Vector2.right; for (int i = 0; i < length - 1; i++) { - Vector2 inputTangent = Vector2.left; + Vector2 inputTangent; // 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]; + Vector2 point_a = gridPoints[i]; + Vector2 point_b = gridPoints[i + 1]; float dist_ab = Vector2.Distance(point_a, point_b); - if (i == 0) outputTangent = Vector2.right * dist_ab * 0.01f * zoom; + if (i == 0) outputTangent = zoom * dist_ab * 0.01f * Vector2.right; if (i < length - 2) { - Vector2 point_c = windowPoints[i + 2]; + Vector2 point_c = gridPoints[i + 2]; Vector2 ab = (point_b - point_a).normalized; Vector2 cb = (point_b - point_c).normalized; Vector2 ac = (point_c - point_a).normalized; @@ -140,37 +158,41 @@ namespace XNodeEditor { 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; + p = tangentLength * Mathf.Sign(side) * new Vector2(-p.y, p.x); inputTangent = p; } else { - inputTangent = Vector2.left * dist_ab * 0.01f * zoom; + inputTangent = zoom * dist_ab * 0.01f * Vector2.left; } // Calculates the tangents for the bezier's curves. - Vector2 tangent_a = point_a + outputTangent * 50 / zoom; - Vector2 tangent_b = point_b + inputTangent * 50 / zoom; + float zoomCoef = 50 / zoom; + Vector2 tangent_a = point_a + outputTangent * zoomCoef; + Vector2 tangent_b = point_b + inputTangent * zoomCoef; // Hover effect. int division = Mathf.RoundToInt(.2f * dist_ab) + 3; - Vector3[] points = Handles.MakeBezierPoints(point_a, point_b, tangent_a, tangent_b, division); - int draw = 0; // Coloring and bezier drawing. - for (int j = 0; j < points.Length - 1; j++) { + int draw = 0; + Vector3 bezierPrevious = point_a; + for (int j = 1; j < division; ++j) + { if (stroke == NoodleStroke.Dashed) { draw++; if (draw >= 2) draw = -2; if (draw < 0) continue; } - - if (i == gridPoints.Count - 2) Handles.color = gradient.Evaluate((j + 1f) / points.Length); - Handles.DrawAAPolyLine(thickness, points[j], points[j + 1]); + if (i == length - 2) + Handles.color = gradient.Evaluate((j + 1f) / division); + Vector3 bezierNext = CalculateBezierPoint(point_a, tangent_a, tangent_b, point_b, j / (float)division); + DrawAAPolyLineNonAlloc(thickness, bezierPrevious, bezierNext); + bezierPrevious = bezierNext; } outputTangent = -inputTangent; } break; case NoodlePath.Straight: for (int i = 0; i < length - 1; i++) { - Vector2 point_a = windowPoints[i]; - Vector2 point_b = windowPoints[i + 1]; + Vector2 point_a = gridPoints[i]; + Vector2 point_b = gridPoints[i + 1]; // Draws the line with the coloring. Vector2 prev_point = point_a; // Approximately one segment per 5 pixels @@ -182,8 +204,8 @@ namespace XNodeEditor { float t = j / (float) segments; Vector2 lerp = Vector2.Lerp(point_a, point_b, t); if (draw > 0) { - if (i == gridPoints.Count - 2) Handles.color = gradient.Evaluate(t); - Handles.DrawAAPolyLine(thickness, prev_point, lerp); + if (i == length - 2) Handles.color = gradient.Evaluate(t); + DrawAAPolyLineNonAlloc(thickness, prev_point, lerp); } prev_point = lerp; if (stroke == NoodleStroke.Dashed && draw >= 2) draw = -2; @@ -193,49 +215,49 @@ namespace XNodeEditor { case NoodlePath.Angled: for (int i = 0; i < length - 1; i++) { if (i == length - 1) continue; // Skip last index - if (windowPoints[i].x <= windowPoints[i + 1].x - (50 / zoom)) { - float midpoint = (windowPoints[i].x + windowPoints[i + 1].x) * 0.5f; - Vector2 start_1 = windowPoints[i]; - Vector2 end_1 = windowPoints[i + 1]; + if (gridPoints[i].x <= gridPoints[i + 1].x - (50 / zoom)) { + float midpoint = (gridPoints[i].x + gridPoints[i + 1].x) * 0.5f; + Vector2 start_1 = gridPoints[i]; + Vector2 end_1 = gridPoints[i + 1]; start_1.x = midpoint; end_1.x = midpoint; - if (i == gridPoints.Count - 2) { - Handles.DrawAAPolyLine(thickness, windowPoints[i], start_1); + if (i == length - 2) { + DrawAAPolyLineNonAlloc(thickness, gridPoints[i], start_1); Handles.color = gradient.Evaluate(0.5f); - Handles.DrawAAPolyLine(thickness, start_1, end_1); + DrawAAPolyLineNonAlloc(thickness, start_1, end_1); Handles.color = gradient.Evaluate(1f); - Handles.DrawAAPolyLine(thickness, end_1, windowPoints[i + 1]); + DrawAAPolyLineNonAlloc(thickness, end_1, gridPoints[i + 1]); } else { - Handles.DrawAAPolyLine(thickness, windowPoints[i], start_1); - Handles.DrawAAPolyLine(thickness, start_1, end_1); - Handles.DrawAAPolyLine(thickness, end_1, windowPoints[i + 1]); + DrawAAPolyLineNonAlloc(thickness, gridPoints[i], start_1); + DrawAAPolyLineNonAlloc(thickness, start_1, end_1); + DrawAAPolyLineNonAlloc(thickness, end_1, gridPoints[i + 1]); } } else { - float midpoint = (windowPoints[i].y + windowPoints[i + 1].y) * 0.5f; - Vector2 start_1 = windowPoints[i]; - Vector2 end_1 = windowPoints[i + 1]; + float midpoint = (gridPoints[i].y + gridPoints[i + 1].y) * 0.5f; + Vector2 start_1 = gridPoints[i]; + Vector2 end_1 = gridPoints[i + 1]; start_1.x += 25 / zoom; end_1.x -= 25 / zoom; Vector2 start_2 = start_1; Vector2 end_2 = end_1; start_2.y = midpoint; end_2.y = midpoint; - if (i == gridPoints.Count - 2) { - Handles.DrawAAPolyLine(thickness, windowPoints[i], start_1); + if (i == length - 2) { + DrawAAPolyLineNonAlloc(thickness, gridPoints[i], start_1); Handles.color = gradient.Evaluate(0.25f); - Handles.DrawAAPolyLine(thickness, start_1, start_2); + DrawAAPolyLineNonAlloc(thickness, start_1, start_2); Handles.color = gradient.Evaluate(0.5f); - Handles.DrawAAPolyLine(thickness, start_2, end_2); + DrawAAPolyLineNonAlloc(thickness, start_2, end_2); Handles.color = gradient.Evaluate(0.75f); - Handles.DrawAAPolyLine(thickness, end_2, end_1); + DrawAAPolyLineNonAlloc(thickness, end_2, end_1); Handles.color = gradient.Evaluate(1f); - Handles.DrawAAPolyLine(thickness, end_1, windowPoints[i + 1]); + DrawAAPolyLineNonAlloc(thickness, end_1, gridPoints[i + 1]); } else { - Handles.DrawAAPolyLine(thickness, windowPoints[i], start_1); - Handles.DrawAAPolyLine(thickness, start_1, start_2); - Handles.DrawAAPolyLine(thickness, start_2, end_2); - Handles.DrawAAPolyLine(thickness, end_2, end_1); - Handles.DrawAAPolyLine(thickness, end_1, windowPoints[i + 1]); + DrawAAPolyLineNonAlloc(thickness, gridPoints[i], start_1); + DrawAAPolyLineNonAlloc(thickness, start_1, start_2); + DrawAAPolyLineNonAlloc(thickness, start_2, end_2); + DrawAAPolyLineNonAlloc(thickness, end_2, end_1); + DrawAAPolyLineNonAlloc(thickness, end_1, gridPoints[i + 1]); } } } From e15076b34fecc178906770b8d207f350a5131f47 Mon Sep 17 00:00:00 2001 From: apkd Date: Sun, 27 Oct 2019 16:37:31 +0100 Subject: [PATCH 09/21] Use Vector2 in bezier calculations --- Scripts/Editor/NodeEditorGUI.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index b8f55d1..9bb6ab7 100644 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -117,7 +117,7 @@ namespace XNodeEditor { if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); } - static Vector3 CalculateBezierPoint(in Vector3 p0, in Vector3 p1, in Vector3 p2, in Vector3 p3, float t) { + static Vector2 CalculateBezierPoint(in Vector2 p0, in Vector2 p1, in Vector2 p2, in Vector2 p3, float t) { float u = 1 - t; float tt = t * t, uu = u * u; float uuu = uu * u, ttt = tt * t; @@ -125,7 +125,7 @@ namespace XNodeEditor { } /// Draws a line segment without allocating temporary arrays - static void DrawAAPolyLineNonAlloc(float thickness, in Vector3 p0, in Vector3 p1) { + static void DrawAAPolyLineNonAlloc(float thickness, in Vector2 p0, in Vector2 p1) { polyLineTempArray[0] = p0; polyLineTempArray[1] = p1; Handles.DrawAAPolyLine(thickness, polyLineTempArray); @@ -172,8 +172,8 @@ namespace XNodeEditor { int division = Mathf.RoundToInt(.2f * dist_ab) + 3; // Coloring and bezier drawing. int draw = 0; - Vector3 bezierPrevious = point_a; - for (int j = 1; j < division; ++j) + Vector2 bezierPrevious = point_a; + for (int j = 1; j <= division; ++j) { if (stroke == NoodleStroke.Dashed) { draw++; @@ -182,7 +182,7 @@ namespace XNodeEditor { } if (i == length - 2) Handles.color = gradient.Evaluate((j + 1f) / division); - Vector3 bezierNext = CalculateBezierPoint(point_a, tangent_a, tangent_b, point_b, j / (float)division); + Vector2 bezierNext = CalculateBezierPoint(point_a, tangent_a, tangent_b, point_b, j / (float)division); DrawAAPolyLineNonAlloc(thickness, bezierPrevious, bezierNext); bezierPrevious = bezierNext; } From 7cd3077ff0b42ab0faf620f70b82e1010e938201 Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Tue, 5 Nov 2019 10:52:04 +0100 Subject: [PATCH 10/21] Duplicating a empty selection makes linq throw exception InvalidOperationException: Sequence contains no elements System.Linq.Enumerable.Aggregate[TSource] (System.Collections.Generic.IEnumerable`1[T] source, System.Func`3[T1,T2,TResult] func) (at :0) --- Scripts/Editor/NodeEditorAction.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index baf9400..67c50a7 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -413,6 +413,7 @@ namespace XNodeEditor { public void DuplicateSelectedNodes() { // Get selected nodes which are part of this graph XNode.Node[] selectedNodes = Selection.objects.Select(x => x as XNode.Node).Where(x => x != null && x.graph == graph).ToArray(); + if (selectedNodes == null || selectedNodes.Length == 0) return; // Get top left node position Vector2 topLeftNode = selectedNodes.Select(x => x.position).Aggregate((x, y) => new Vector2(Mathf.Min(x.x, y.x), Mathf.Min(x.y, y.y))); InsertDuplicateNodes(selectedNodes, topLeftNode + new Vector2(30, 30)); From 2d1a671b71d11cb55339b9c8845c03f54a5efa00 Mon Sep 17 00:00:00 2001 From: Wang Cong Date: Wed, 6 Nov 2019 18:42:22 +0800 Subject: [PATCH 11/21] Undo support (#199) Added undo support for move, create, duplicate and delete node, and connect port --- Scripts/Editor/NodeEditorAction.cs | 1 + Scripts/Editor/NodeGraphEditor.cs | 11 ++++++++++- Scripts/NodePort.cs | 4 ++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Scripts/Editor/NodeEditorAction.cs b/Scripts/Editor/NodeEditorAction.cs index 67c50a7..2581676 100644 --- a/Scripts/Editor/NodeEditorAction.cs +++ b/Scripts/Editor/NodeEditorAction.cs @@ -81,6 +81,7 @@ namespace XNodeEditor { for (int i = 0; i < Selection.objects.Length; i++) { if (Selection.objects[i] is XNode.Node) { XNode.Node node = Selection.objects[i] as XNode.Node; + Undo.RecordObject(node, "Moved Node"); Vector2 initial = node.position; node.position = mousePos + dragOffset[i]; if (gridSnap) { diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index bc6e14c..56f0d69 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -138,7 +138,9 @@ namespace XNodeEditor { /// Create a node and save it in the graph asset public virtual XNode.Node CreateNode(Type type, Vector2 position) { + Undo.RecordObject(target, "Create Node"); XNode.Node node = target.AddNode(type); + Undo.RegisterCreatedObjectUndo(node, "Create Node"); node.position = position; if (node.name == null || node.name.Trim() == "") node.name = NodeEditorUtilities.NodeDefaultName(type); AssetDatabase.AddObjectToAsset(node, target); @@ -149,7 +151,9 @@ namespace XNodeEditor { /// Creates a copy of the original node in the graph public XNode.Node CopyNode(XNode.Node original) { + Undo.RecordObject(target, "Duplicate Node"); XNode.Node node = target.CopyNode(original); + Undo.RegisterCreatedObjectUndo(node, "Duplicate Node"); node.name = original.name; AssetDatabase.AddObjectToAsset(node, target); if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); @@ -158,8 +162,13 @@ namespace XNodeEditor { /// Safely remove a node and all its connections. public virtual void RemoveNode(XNode.Node node) { + Undo.RecordObject(node, "Delete Node"); + Undo.RecordObject(target, "Delete Node"); + foreach (var port in node.Ports) + foreach (var conn in port.GetConnections()) + Undo.RecordObject(conn.node, "Delete Node"); target.RemoveNode(node); - UnityEngine.Object.DestroyImmediate(node, true); + Undo.DestroyObjectImmediate(node); if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); } diff --git a/Scripts/NodePort.cs b/Scripts/NodePort.cs index 1000b23..9db7aee 100644 --- a/Scripts/NodePort.cs +++ b/Scripts/NodePort.cs @@ -199,6 +199,10 @@ namespace XNode { if (port == this) { Debug.LogWarning("Cannot connect port to self."); return; } if (IsConnectedTo(port)) { Debug.LogWarning("Port already connected. "); return; } if (direction == port.direction) { Debug.LogWarning("Cannot connect two " + (direction == IO.Input ? "input" : "output") + " connections"); return; } +#if UNITY_EDITOR + UnityEditor.Undo.RecordObject(node, "Connect Port"); + UnityEditor.Undo.RecordObject(port.node, "Connect Port"); +#endif if (port.connectionType == Node.ConnectionType.Override && port.ConnectionCount != 0) { port.ClearConnections(); } if (connectionType == Node.ConnectionType.Override && ConnectionCount != 0) { ClearConnections(); } connections.Add(new PortConnection(port)); From be0d452d12143bcdf54b176883973c2a6e5a98fc Mon Sep 17 00:00:00 2001 From: Marco Secchi Date: Wed, 6 Nov 2019 11:53:13 +0100 Subject: [PATCH 12/21] Fix wrong override message. (#204) --- Scripts/Editor/NodeGraphEditor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Scripts/Editor/NodeGraphEditor.cs b/Scripts/Editor/NodeGraphEditor.cs index 56f0d69..3a0464c 100644 --- a/Scripts/Editor/NodeGraphEditor.cs +++ b/Scripts/Editor/NodeGraphEditor.cs @@ -133,7 +133,7 @@ namespace XNodeEditor { /// Deal with objects dropped into the graph through DragAndDrop public virtual void OnDropObjects(UnityEngine.Object[] objects) { - Debug.Log("No OnDropItems override defined for " + GetType()); + Debug.Log("No OnDropObjects override defined for " + GetType()); } /// Create a node and save it in the graph asset From 63c3ae2f7d532d253bb9d7515845a83788f37249 Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Wed, 6 Nov 2019 13:12:04 +0100 Subject: [PATCH 13/21] Fixed TypeConstraint.Inherited, and added TypeConstraint.InheritedInverse (#205) * Added TypeConstraint to check inheritance but inverted. * Flipped output inherited type constraints, edited comments --- Scripts/Node.cs | 4 +++- Scripts/NodePort.cs | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Scripts/Node.cs b/Scripts/Node.cs index 27e32c7..a07679a 100644 --- a/Scripts/Node.cs +++ b/Scripts/Node.cs @@ -45,10 +45,12 @@ namespace XNode { public enum TypeConstraint { /// Allow all types of input None, - /// Allow similar and inherited types + /// Allow connections where input value type is assignable from output value type (eg. ScriptableObject --> Object) Inherited, /// Allow only similar types Strict, + /// Allow connections where output value type is assignable from input value type (eg. Object --> ScriptableObject) + InheritedInverse, } #region Obsolete diff --git a/Scripts/NodePort.cs b/Scripts/NodePort.cs index 9db7aee..58a3bd6 100644 --- a/Scripts/NodePort.cs +++ b/Scripts/NodePort.cs @@ -263,9 +263,11 @@ namespace XNode { // Check input type constraints if (input.typeConstraint == XNode.Node.TypeConstraint.Inherited && !input.ValueType.IsAssignableFrom(output.ValueType)) return false; if (input.typeConstraint == XNode.Node.TypeConstraint.Strict && input.ValueType != output.ValueType) return false; + if (input.typeConstraint == XNode.Node.TypeConstraint.InheritedInverse && !output.ValueType.IsAssignableFrom(input.ValueType)) return false; // Check output type constraints - if (output.typeConstraint == XNode.Node.TypeConstraint.Inherited && !output.ValueType.IsAssignableFrom(input.ValueType)) return false; - if (output.typeConstraint == XNode.Node.TypeConstraint.Strict && output.ValueType != input.ValueType) return false; + if (output.typeConstraint == XNode.Node.TypeConstraint.Inherited && !input.ValueType.IsAssignableFrom(output.ValueType)) return false; + if (output.typeConstraint == XNode.Node.TypeConstraint.Strict && input.ValueType != output.ValueType) return false; + if (output.typeConstraint == XNode.Node.TypeConstraint.InheritedInverse && !output.ValueType.IsAssignableFrom(input.ValueType)) return false; // Success return true; } From 984fe730c0c5e0b66f07617261a1fefae53e0843 Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Fri, 8 Nov 2019 09:57:54 +0100 Subject: [PATCH 14/21] Added support for [Header] attribute --- Scripts/Editor/NodeEditorGUILayout.cs | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/Scripts/Editor/NodeEditorGUILayout.cs b/Scripts/Editor/NodeEditorGUILayout.cs index ec93cc1..b6c18e9 100644 --- a/Scripts/Editor/NodeEditorGUILayout.cs +++ b/Scripts/Editor/NodeEditorGUILayout.cs @@ -45,6 +45,11 @@ namespace XNodeEditor { SpaceAttribute spaceAttribute; if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out spaceAttribute)) spacePadding = spaceAttribute.height; + //GUI Values are from https://github.com/Unity-Technologies/UnityCsReference/blob/master/Editor/Mono/ScriptAttributeGUI/Implementations/DecoratorDrawers.cs + float headerPadding = 0; + HeaderAttribute headerAttribute; + if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out headerAttribute)) headerPadding = EditorGUIUtility.singleLineHeight * 1.5f; + // If property is an input, display a regular property field and put a port handle on the left side if (port.direction == XNode.NodePort.IO.Input) { // Get data from [Input] attribute @@ -64,6 +69,14 @@ namespace XNodeEditor { GUILayout.Space(spacePadding); spacePadding = 0; } + if (headerPadding > 0 && useLayoutSpace) + { + Rect position = GUILayoutUtility.GetRect(0, EditorGUIUtility.singleLineHeight * 1.5f); + position.yMin += (EditorGUIUtility.singleLineHeight * 0.5f) + EditorGUIUtility.standardVerticalSpacing; + position = EditorGUI.IndentedRect(position); + GUI.Label(position, headerAttribute.header, EditorStyles.boldLabel); + headerPadding = 0; + } if (dynamicPortList) { Type type = GetType(property); @@ -89,7 +102,7 @@ namespace XNodeEditor { } rect = GUILayoutUtility.GetLastRect(); - rect.position = rect.position - new Vector2(16, -spacePadding); + rect.position = rect.position - new Vector2(16, -(spacePadding + headerPadding)); // If property is an output, display a text label and put a port handle on the right side } else if (port.direction == XNode.NodePort.IO.Output) { // Get data from [Output] attribute @@ -109,6 +122,14 @@ namespace XNodeEditor { GUILayout.Space(spacePadding); spacePadding = 0; } + if (headerPadding > 0 && useLayoutSpace) + { + Rect position = GUILayoutUtility.GetRect(0, EditorGUIUtility.singleLineHeight * 1.5f); + position.yMin += (EditorGUIUtility.singleLineHeight * 0.5f) + EditorGUIUtility.standardVerticalSpacing; + position = EditorGUI.IndentedRect(position); + GUI.Label(position, headerAttribute.header, EditorStyles.boldLabel); + headerPadding = 0; + } if (dynamicPortList) { Type type = GetType(property); @@ -134,7 +155,7 @@ namespace XNodeEditor { } rect = GUILayoutUtility.GetLastRect(); - rect.position = rect.position + new Vector2(rect.width, spacePadding); + rect.position = rect.position + new Vector2(rect.width, spacePadding + headerPadding); } rect.size = new Vector2(16, 16); From 72ff273b5c3342f3f460f932849b6afb6c9d38dc Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Tue, 12 Nov 2019 22:37:29 +0100 Subject: [PATCH 15/21] Rewrite that caches the ordered property attributes so the gui layout drawer can draw them in the correct order. Rewrote how the property attributes are drawn, more compact less messy code. --- Scripts/Editor/NodeEditorGUILayout.cs | 71 +++++++++++++-------------- Scripts/Editor/NodeEditorUtilities.cs | 21 ++++++++ 2 files changed, 56 insertions(+), 36 deletions(-) diff --git a/Scripts/Editor/NodeEditorGUILayout.cs b/Scripts/Editor/NodeEditorGUILayout.cs index b6c18e9..f9333db 100644 --- a/Scripts/Editor/NodeEditorGUILayout.cs +++ b/Scripts/Editor/NodeEditorGUILayout.cs @@ -41,14 +41,7 @@ namespace XNodeEditor { else { Rect rect = new Rect(); - float spacePadding = 0; - SpaceAttribute spaceAttribute; - if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out spaceAttribute)) spacePadding = spaceAttribute.height; - - //GUI Values are from https://github.com/Unity-Technologies/UnityCsReference/blob/master/Editor/Mono/ScriptAttributeGUI/Implementations/DecoratorDrawers.cs - float headerPadding = 0; - HeaderAttribute headerAttribute; - if (NodeEditorUtilities.GetCachedAttrib(port.node.GetType(), property.name, out headerAttribute)) headerPadding = EditorGUIUtility.singleLineHeight * 1.5f; + List propertyAttributes = NodeEditorUtilities.GetCachedPropertyAttribs(port.node.GetType(), property.name); // If property is an input, display a regular property field and put a port handle on the left side if (port.direction == XNode.NodePort.IO.Input) { @@ -61,21 +54,24 @@ namespace XNodeEditor { showBacking = inputAttribute.backingValue; } - //Call GUILayout.Space if Space attribute is set and we are NOT drawing a PropertyField - bool useLayoutSpace = dynamicPortList || + bool usePropertyAttributes = dynamicPortList || showBacking == XNode.Node.ShowBackingValue.Never || (showBacking == XNode.Node.ShowBackingValue.Unconnected && port.IsConnected); - if (spacePadding > 0 && useLayoutSpace) { - GUILayout.Space(spacePadding); - spacePadding = 0; - } - if (headerPadding > 0 && useLayoutSpace) - { - Rect position = GUILayoutUtility.GetRect(0, EditorGUIUtility.singleLineHeight * 1.5f); - position.yMin += (EditorGUIUtility.singleLineHeight * 0.5f) + EditorGUIUtility.standardVerticalSpacing; - position = EditorGUI.IndentedRect(position); - GUI.Label(position, headerAttribute.header, EditorStyles.boldLabel); - headerPadding = 0; + + float spacePadding = 0; + foreach (var attr in propertyAttributes) { + if (attr is SpaceAttribute) { + if (usePropertyAttributes) GUILayout.Space((attr as SpaceAttribute).height); + else spacePadding += (attr as SpaceAttribute).height; + } else if (attr is HeaderAttribute) { + if (usePropertyAttributes) { + //GUI Values are from https://github.com/Unity-Technologies/UnityCsReference/blob/master/Editor/Mono/ScriptAttributeGUI/Implementations/DecoratorDrawers.cs + Rect position = GUILayoutUtility.GetRect(0, (EditorGUIUtility.singleLineHeight * 1.5f) - EditorGUIUtility.standardVerticalSpacing); //Layout adds standardVerticalSpacing after rect so we subtract it. + position.yMin += EditorGUIUtility.singleLineHeight * 0.5f; + position = EditorGUI.IndentedRect(position); + GUI.Label(position, (attr as HeaderAttribute).header, EditorStyles.boldLabel); + } else spacePadding += EditorGUIUtility.singleLineHeight * 1.5f; + } } if (dynamicPortList) { @@ -102,7 +98,7 @@ namespace XNodeEditor { } rect = GUILayoutUtility.GetLastRect(); - rect.position = rect.position - new Vector2(16, -(spacePadding + headerPadding)); + rect.position = rect.position - new Vector2(16, -spacePadding); // If property is an output, display a text label and put a port handle on the right side } else if (port.direction == XNode.NodePort.IO.Output) { // Get data from [Output] attribute @@ -114,21 +110,24 @@ namespace XNodeEditor { showBacking = outputAttribute.backingValue; } - //Call GUILayout.Space if Space attribute is set and we are NOT drawing a PropertyField - bool useLayoutSpace = dynamicPortList || + bool usePropertyAttributes = dynamicPortList || showBacking == XNode.Node.ShowBackingValue.Never || (showBacking == XNode.Node.ShowBackingValue.Unconnected && port.IsConnected); - if (spacePadding > 0 && useLayoutSpace) { - GUILayout.Space(spacePadding); - spacePadding = 0; - } - if (headerPadding > 0 && useLayoutSpace) - { - Rect position = GUILayoutUtility.GetRect(0, EditorGUIUtility.singleLineHeight * 1.5f); - position.yMin += (EditorGUIUtility.singleLineHeight * 0.5f) + EditorGUIUtility.standardVerticalSpacing; - position = EditorGUI.IndentedRect(position); - GUI.Label(position, headerAttribute.header, EditorStyles.boldLabel); - headerPadding = 0; + + float spacePadding = 0; + foreach (var attr in propertyAttributes) { + if (attr is SpaceAttribute) { + if (usePropertyAttributes) GUILayout.Space((attr as SpaceAttribute).height); + else spacePadding += (attr as SpaceAttribute).height; + } else if (attr is HeaderAttribute) { + if (usePropertyAttributes) { + //GUI Values are from https://github.com/Unity-Technologies/UnityCsReference/blob/master/Editor/Mono/ScriptAttributeGUI/Implementations/DecoratorDrawers.cs + Rect position = GUILayoutUtility.GetRect(0, (EditorGUIUtility.singleLineHeight * 1.5f) - EditorGUIUtility.standardVerticalSpacing); //Layout adds standardVerticalSpacing after rect so we subtract it. + position.yMin += EditorGUIUtility.singleLineHeight * 0.5f; + position = EditorGUI.IndentedRect(position); + GUI.Label(position, (attr as HeaderAttribute).header, EditorStyles.boldLabel); + } else spacePadding += EditorGUIUtility.singleLineHeight * 1.5f; + } } if (dynamicPortList) { @@ -155,7 +154,7 @@ namespace XNodeEditor { } rect = GUILayoutUtility.GetLastRect(); - rect.position = rect.position + new Vector2(rect.width, spacePadding + headerPadding); + rect.position = rect.position + new Vector2(rect.width, spacePadding); } rect.size = new Vector2(16, 16); diff --git a/Scripts/Editor/NodeEditorUtilities.cs b/Scripts/Editor/NodeEditorUtilities.cs index bf92ba8..9973145 100644 --- a/Scripts/Editor/NodeEditorUtilities.cs +++ b/Scripts/Editor/NodeEditorUtilities.cs @@ -18,6 +18,9 @@ namespace XNodeEditor { /// Saves Attribute from Type+Field for faster lookup. Resets on recompiles. private static Dictionary>> typeAttributes = new Dictionary>>(); + /// Saves ordered PropertyAttribute from Type+Field for faster lookup. Resets on recompiles. + private static Dictionary>> typeOrderedPropertyAttributes = new Dictionary>>(); + public static bool GetAttrib(Type classType, out T attribOut) where T : Attribute { object[] attribs = classType.GetCustomAttributes(typeof(T), false); return GetAttrib(attribs, out attribOut); @@ -84,6 +87,24 @@ namespace XNodeEditor { return true; } + public static List GetCachedPropertyAttribs(Type classType, string fieldName) { + Dictionary> typeFields; + if (!typeOrderedPropertyAttributes.TryGetValue(classType, out typeFields)) { + typeFields = new Dictionary>(); + typeOrderedPropertyAttributes.Add(classType, typeFields); + } + + List typeAttributes; + if (!typeFields.TryGetValue(fieldName, out typeAttributes)) { + FieldInfo field = classType.GetFieldInfo(fieldName); + object[] attribs = field.GetCustomAttributes(typeof(PropertyAttribute), true); + typeAttributes = attribs.Cast().Reverse().ToList(); //Unity draws them in reverse + typeFields.Add(fieldName, typeAttributes); + } + + return typeAttributes; + } + public static bool IsMac() { #if UNITY_2017_1_OR_NEWER return SystemInfo.operatingSystemFamily == OperatingSystemFamily.MacOSX; From 8b99a34a6347a3e6f4d766c71fd69f51481e8584 Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Tue, 12 Nov 2019 23:05:33 +0100 Subject: [PATCH 16/21] The in parameter was is available in C# 7.2 and later. This breaks compatibility with earlier versions. --- Scripts/Editor/NodeEditorGUI.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 9bb6ab7..5ff178f 100644 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -117,7 +117,7 @@ namespace XNodeEditor { if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); } - static Vector2 CalculateBezierPoint(in Vector2 p0, in Vector2 p1, in Vector2 p2, in Vector2 p3, float t) { + static Vector2 CalculateBezierPoint(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3, float t) { float u = 1 - t; float tt = t * t, uu = u * u; float uuu = uu * u, ttt = tt * t; @@ -125,7 +125,7 @@ namespace XNodeEditor { } /// Draws a line segment without allocating temporary arrays - static void DrawAAPolyLineNonAlloc(float thickness, in Vector2 p0, in Vector2 p1) { + static void DrawAAPolyLineNonAlloc(float thickness, Vector2 p0, Vector2 p1) { polyLineTempArray[0] = p0; polyLineTempArray[1] = p1; Handles.DrawAAPolyLine(thickness, polyLineTempArray); From 92ebd5953999d79b0231a054a6439f076c1509f9 Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Tue, 12 Nov 2019 23:19:51 +0100 Subject: [PATCH 17/21] reuse the list of grid points instead of creating a new one for each node->port --- Scripts/Editor/NodeEditorGUI.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 5ff178f..1feec0e 100644 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -271,6 +271,8 @@ namespace XNodeEditor { List selection = preBoxSelectionReroute != null ? new List(preBoxSelectionReroute) : new List(); hoveredReroute = new RerouteReference(); + List gridPoints = new List(2); + Color col = GUI.color; foreach (XNode.Node node in graph.nodes) { //If a null node is found, return. This can happen if the nodes associated script is deleted. It is currently not possible in Unity to delete a null asset. @@ -299,7 +301,7 @@ namespace XNodeEditor { List reroutePoints = output.GetReroutePoints(k); - List gridPoints = new List(); + gridPoints.Clear(); gridPoints.Add(fromRect.center); gridPoints.AddRange(reroutePoints); gridPoints.Add(toRect.center); From 91eafcc47d08689b560f0f642c3e2e36c8dc85ce Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Wed, 13 Nov 2019 00:10:45 +0100 Subject: [PATCH 18/21] the + and * was creating a lot of new vector2s, went down 80% in call time with this --- Scripts/Editor/NodeEditorGUI.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 1feec0e..30c26b4 100644 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -121,7 +121,10 @@ namespace XNodeEditor { float u = 1 - t; float tt = t * t, uu = u * u; float uuu = uu * u, ttt = tt * t; - return (uuu * p0) + (3 * uu * t * p1) + (3 * u * tt * p2) + (ttt * p3); + return new Vector2( + (uuu * p0.x) + (3 * uu * t * p1.x) + (3 * u * tt * p2.x) + (ttt * p3.x), + (uuu * p0.y) + (3 * uu * t * p1.y) + (3 * u * tt * p2.y) + (ttt * p3.y) + ); } /// Draws a line segment without allocating temporary arrays From 71023e1d58c9aa4ad1ce51aa1c73a9d623f0f5fa Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Wed, 13 Nov 2019 00:14:29 +0100 Subject: [PATCH 19/21] microoptimization, removes implicit case from vec2 to vec3. --- Scripts/Editor/NodeEditorGUI.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 30c26b4..2f605a8 100644 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -129,8 +129,10 @@ namespace XNodeEditor { /// Draws a line segment without allocating temporary arrays static void DrawAAPolyLineNonAlloc(float thickness, Vector2 p0, Vector2 p1) { - polyLineTempArray[0] = p0; - polyLineTempArray[1] = p1; + polyLineTempArray[0].x = p0.x; + polyLineTempArray[0].y = p0.y; + polyLineTempArray[1].x = p1.x; + polyLineTempArray[1].y = p1.y; Handles.DrawAAPolyLine(thickness, polyLineTempArray); } From ec9c5a99de7d148bfc0594a182d50e066c021471 Mon Sep 17 00:00:00 2001 From: Simon Rodriguez Date: Wed, 13 Nov 2019 01:14:09 +0100 Subject: [PATCH 20/21] optimized linq function --- Scripts/Editor/NodeEditorGUI.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 2f605a8..6cb17cb 100644 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -372,6 +372,8 @@ namespace XNodeEditor { //Save guiColor so we can revert it Color guiColor = GUI.color; + List removeEntries = new List(); + if (e.type == EventType.Layout) culledNodes = new List(); for (int n = 0; n < graph.nodes.Count; n++) { // Skip null nodes. The user could be in the process of renaming scripts, so removing them at this point is not advisable. @@ -389,7 +391,10 @@ namespace XNodeEditor { } else if (culledNodes.Contains(node)) continue; if (e.type == EventType.Repaint) { - _portConnectionPoints = _portConnectionPoints.Where(x => x.Key.node != node).ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + removeEntries.Clear(); + foreach (var kvp in _portConnectionPoints) + if (kvp.Key.node == node) removeEntries.Add(kvp.Key); + foreach (var k in removeEntries) _portConnectionPoints.Remove(k); } NodeEditor nodeEditor = NodeEditor.GetEditor(node, this); From 4edcc3f0999bbee52e066f1a06551c3e0a64d35e Mon Sep 17 00:00:00 2001 From: Thor Brigsted Date: Thu, 14 Nov 2019 17:40:09 +0100 Subject: [PATCH 21/21] Fixed formatting, fixed dashed stroke style --- Scripts/Editor/NodeEditorGUI.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Scripts/Editor/NodeEditorGUI.cs b/Scripts/Editor/NodeEditorGUI.cs index 6cb17cb..c41afd0 100644 --- a/Scripts/Editor/NodeEditorGUI.cs +++ b/Scripts/Editor/NodeEditorGUI.cs @@ -116,7 +116,7 @@ namespace XNodeEditor { contextMenu.DropDown(new Rect(Event.current.mousePosition, Vector2.zero)); if (NodeEditorPreferences.GetSettings().autoSave) AssetDatabase.SaveAssets(); } - + static Vector2 CalculateBezierPoint(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3, float t) { float u = 1 - t; float tt = t * t, uu = u * u; @@ -135,7 +135,7 @@ namespace XNodeEditor { polyLineTempArray[1].y = p1.y; Handles.DrawAAPolyLine(thickness, polyLineTempArray); } - + /// Draw a bezier from output to input in grid coordinates public void DrawNoodle(Gradient gradient, NoodlePath path, NoodleStroke stroke, float thickness, List gridPoints) { // convert grid points to window points @@ -178,16 +178,16 @@ namespace XNodeEditor { // Coloring and bezier drawing. int draw = 0; Vector2 bezierPrevious = point_a; - for (int j = 1; j <= division; ++j) - { + for (int j = 1; j <= division; ++j) { if (stroke == NoodleStroke.Dashed) { draw++; if (draw >= 2) draw = -2; if (draw < 0) continue; + if (draw == 0) bezierPrevious = CalculateBezierPoint(point_a, tangent_a, tangent_b, point_b, (j - 1f) / (float) division); } if (i == length - 2) Handles.color = gradient.Evaluate((j + 1f) / division); - Vector2 bezierNext = CalculateBezierPoint(point_a, tangent_a, tangent_b, point_b, j / (float)division); + Vector2 bezierNext = CalculateBezierPoint(point_a, tangent_a, tangent_b, point_b, j / (float) division); DrawAAPolyLineNonAlloc(thickness, bezierPrevious, bezierNext); bezierPrevious = bezierNext; }