Просмотр исходного кода

First working version of repeat node

Krzysztof Krysiński 10 часов назад
Родитель
Сommit
5117b6ccd5

+ 0 - 2
src/PixiEditor.ChangeableDocument/Changeables/Graph/Context/FuncContext.cs

@@ -15,8 +15,6 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph.Context;
 
 public class FuncContext
 {
-    public static FuncContext NoContext { get; } = new();
-
     public bool HasContext { get; protected set; }
 
     public void ThrowOnMissingContext()

+ 0 - 14
src/PixiEditor.ChangeableDocument/Changeables/Graph/Context/RepeatFuncContext.cs

@@ -1,14 +0,0 @@
-namespace PixiEditor.ChangeableDocument.Changeables.Graph.Context;
-
-public class RepeatFuncContext : FuncContext
-{
-    public int CurrentIteration { get; set; }
-
-    public int TotalIterations { get; }
-
-    public RepeatFuncContext(int totalIterations)
-    {
-        HasContext = true;
-        TotalIterations = totalIterations;
-    }
-}

+ 12 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/GraphUtils.cs

@@ -5,10 +5,12 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph;
 public static class GraphUtils
 {
     public static Queue<IReadOnlyNode> CalculateExecutionQueue(IReadOnlyNode outputNode,
+        bool considerFlowNodes = false,
         Func<IInputProperty, bool>? branchFilter = null)
     {
         var finalQueue = new HashSet<IReadOnlyNode>();
         var queueNodes = new Queue<IReadOnlyNode>();
+        var nodesToExclude = new HashSet<IReadOnlyNode>();
         queueNodes.Enqueue(outputNode);
 
         while (queueNodes.Count > 0)
@@ -20,6 +22,14 @@ public static class GraphUtils
                 continue;
             }
 
+            if (considerFlowNodes && node is IExecutionFlowNode flowNode)
+            {
+                foreach (var handled in flowNode.HandledNodes)
+                {
+                    nodesToExclude.Add(handled);
+                }
+            }
+
             bool canAdd = true;
 
             foreach (var input in node.InputProperties)
@@ -63,6 +73,8 @@ public static class GraphUtils
             }
         }
 
+        finalQueue = new HashSet<IReadOnlyNode>(finalQueue.Except(nodesToExclude));
+
         return new Queue<IReadOnlyNode>(finalQueue);
     }
 }

+ 2 - 2
src/PixiEditor.ChangeableDocument/Changeables/Graph/InputProperty.cs

@@ -42,7 +42,7 @@ public class InputProperty : IInputProperty
             {
                 try
                 {
-                    target = connectionField.DynamicInvoke(FuncContext.NoContext);
+                    target = connectionField.DynamicInvoke(ShaderFuncContext.NoContext);
                 }
                 catch
                 {
@@ -232,7 +232,7 @@ public class InputProperty : IInputProperty
         {
             try
             {
-                var constant = func.DynamicInvoke(FuncContext.NoContext);
+                var constant = func.DynamicInvoke(ShaderFuncContext.NoContext);
                 if (constant is ShaderExpressionVariable shaderExpression)
                 {
                     hash.Add(shaderExpression.GetConstant());

+ 6 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IExecutionFlowNode.cs

@@ -0,0 +1,6 @@
+namespace PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+
+public interface IExecutionFlowNode
+{
+    public HashSet<IReadOnlyNode> HandledNodes { get; }
+}

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Interfaces/IReadOnlyNodeGraph.cs

@@ -12,5 +12,5 @@ public interface IReadOnlyNodeGraph : ICacheable
     public void RemoveNode(IReadOnlyNode node);
     public bool TryTraverse(Action<IReadOnlyNode> action);
     public void Execute(RenderContext context);
-    Queue<IReadOnlyNode> CalculateExecutionQueue(IReadOnlyNode endNode);
+    Queue<IReadOnlyNode> CalculateExecutionQueue(IReadOnlyNode endNode, bool considerFlowNodes = false);
 }

+ 13 - 8
src/PixiEditor.ChangeableDocument/Changeables/Graph/NodeGraph.cs

@@ -7,8 +7,9 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph;
 
 public class NodeGraph : IReadOnlyNodeGraph, IDisposable
 {
-    private ImmutableList<IReadOnlyNode>? cachedExecutionList;
-    
+    private ImmutableList<IReadOnlyNode>? cachedExecutionListWithoutFlowNodes;
+    private ImmutableList<IReadOnlyNode>? cachedExecutionListWithFlowNodes;
+
     private readonly List<Node> _nodes = new();
     public IReadOnlyCollection<Node> Nodes => _nodes;
     public IReadOnlyDictionary<Guid, Node> NodeLookup => nodeLookup;
@@ -57,14 +58,17 @@ public class NodeGraph : IReadOnlyNodeGraph, IDisposable
         return nodeLookup.TryGetValue(guid, out Node? node) && node is T typedNode ? typedNode : null;
     }
 
-    public Queue<IReadOnlyNode> CalculateExecutionQueue(IReadOnlyNode outputNode)
+    public Queue<IReadOnlyNode> CalculateExecutionQueue(IReadOnlyNode outputNode, bool considerFlowNodes = false)
     {
-        return new Queue<IReadOnlyNode>(CalculateExecutionQueueInternal(outputNode));
+        return new Queue<IReadOnlyNode>(CalculateExecutionQueueInternal(outputNode, considerFlowNodes));
     }
     
-    private ImmutableList<IReadOnlyNode> CalculateExecutionQueueInternal(IReadOnlyNode outputNode)
+    private ImmutableList<IReadOnlyNode> CalculateExecutionQueueInternal(IReadOnlyNode outputNode, bool considerFlowNodes = false)
     {
-        return cachedExecutionList ??= GraphUtils.CalculateExecutionQueue(outputNode).ToImmutableList();
+        if (considerFlowNodes)
+            return cachedExecutionListWithFlowNodes ??= GraphUtils.CalculateExecutionQueue(outputNode, considerFlowNodes).ToImmutableList();
+
+        return cachedExecutionListWithoutFlowNodes ??= GraphUtils.CalculateExecutionQueue(outputNode, considerFlowNodes).ToImmutableList();
     }
 
     void IReadOnlyNodeGraph.AddNode(IReadOnlyNode node) => AddNode((Node)node);
@@ -98,7 +102,7 @@ public class NodeGraph : IReadOnlyNodeGraph, IDisposable
         if (OutputNode == null) return;
         if(!CanExecute()) return;
 
-        var queue = CalculateExecutionQueueInternal(OutputNode);
+        var queue = CalculateExecutionQueueInternal(OutputNode, true);
         
         foreach (var node in queue)
         {
@@ -130,7 +134,8 @@ public class NodeGraph : IReadOnlyNodeGraph, IDisposable
     
     private void ResetCache()
     {
-        cachedExecutionList = null;
+        cachedExecutionListWithoutFlowNodes = null;
+        cachedExecutionListWithFlowNodes = null;
     }
 
     public int GetCacheHash()

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FolderNode.cs

@@ -258,7 +258,7 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode, IClipSource
 
         if (Content.Connection != null)
         {
-            var executionQueue = GraphUtils.CalculateExecutionQueue(Content.Connection.Node, FilterInvisibleFolders);
+            var executionQueue = GraphUtils.CalculateExecutionQueue(Content.Connection.Node, false, FilterInvisibleFolders);
             while (executionQueue.Count > 0)
             {
                 IReadOnlyNode node = executionQueue.Dequeue();

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Node.cs

@@ -586,7 +586,7 @@ public abstract class Node : IReadOnlyNode, IDisposable
 
         if (input != null && value is Delegate del)
         {
-            object constant = del.DynamicInvoke(FuncContext.NoContext);
+            object constant = del.DynamicInvoke(ShaderFuncContext.NoContext);
             if (constant is ShaderExpressionVariable expr)
             {
                 return input.FuncFactory(expr.GetConstant());

+ 78 - 4
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Utility/RepeatNodeEnd.cs

@@ -1,18 +1,92 @@
-using PixiEditor.ChangeableDocument.Rendering;
+using Drawie.Backend.Core.Shaders;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Context;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.ChangeableDocument.Rendering;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Utility;
 
 [NodeInfo("RepeatEnd")]
 [PairNode(typeof(RepeatNodeStart), "RepeatZone", false)]
-public class RepeatNodeEnd : Node
+public class RepeatNodeEnd : Node, IPairNode
 {
+    public InputProperty<object> Input { get; }
+    public OutputProperty<object> Output { get; }
+
+    public Guid OtherNode { get; set; }
+
+    private RepeatNodeStart startNode;
+
+    public RepeatNodeEnd()
+    {
+        Input = CreateInput<object>("Input", "INPUT", null);
+        Output = CreateOutput<object>("Output", "OUTPUT", null);
+    }
+
     protected override void OnExecute(RenderContext context)
     {
-        throw new NotImplementedException();
+        if (OtherNode == Guid.Empty || startNode == null)
+        {
+            startNode = FindStartNode();
+            OtherNode = startNode?.Id ?? Guid.Empty;
+            if (OtherNode == Guid.Empty)
+            {
+                return;
+            }
+        }
+
+        int iterations = startNode.Iterations.Value;
+        var queue = GraphUtils.CalculateExecutionQueue(this, false, (input => input.Connection?.Node != startNode));
+
+        for (int i = 0; i < iterations; i++)
+        {
+            startNode.CurrentIteration.Value = i;
+            foreach (var node in queue)
+            {
+                if (node is RepeatNodeStart or RepeatNodeEnd)
+                {
+                    continue;
+                }
+
+                node.Execute(context);
+            }
+
+            startNode.Output.Value = Input.Value;
+        }
+        startNode.CurrentIteration.Value = 0;
+        if (iterations <= 0)
+        {
+            Output.Value = startNode.Input.Value;
+        }
+        else
+        {
+            Output.Value = Input.Value;
+        }
+    }
+
+    private object GetOutput(ShaderFuncContext context)
+    {
+        return null;
     }
 
     public override Node CreateCopy()
     {
-        throw new NotImplementedException();
+        return new RepeatNodeEnd();
+    }
+
+    private RepeatNodeStart FindStartNode()
+    {
+        RepeatNodeStart startNode = null;
+        TraverseBackwards(node =>
+        {
+            if (node is RepeatNodeStart leftNode)
+            {
+                startNode = leftNode;
+                return false;
+            }
+
+            return true;
+        });
+
+        return startNode;
     }
 }

+ 34 - 18
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Utility/RepeatNodeStart.cs

@@ -1,42 +1,58 @@
 using PixiEditor.ChangeableDocument.Changeables.Graph.Context;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Rendering;
 
 namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Utility;
 
 [NodeInfo("RepeatStart")]
 [PairNode(typeof(RepeatNodeEnd), "RepeatZone", true)]
-public class RepeatNodeStart : Node
+public class RepeatNodeStart : Node, IExecutionFlowNode
 {
-    public FuncInputProperty<int, RepeatFuncContext> Iterations { get; }
-    public FuncInputProperty<object, RepeatFuncContext> Input { get; }
-    public FuncOutputProperty<int, RepeatFuncContext> CurrentIteration { get; }
-    public FuncOutputProperty<object, RepeatFuncContext> Output { get; }
+    public InputProperty<int> Iterations { get; }
+    public InputProperty<object> Input { get; }
+    public OutputProperty<int> CurrentIteration { get; }
+    public OutputProperty<object> Output { get; }
 
     public RepeatNodeStart()
     {
-        Iterations = CreateFuncInput<int, RepeatFuncContext>("Iterations", "ITERATIONS", 1);
-        Input = CreateFuncInput<object, RepeatFuncContext>("Input", "INPUT", null);
-        CurrentIteration = CreateFuncOutput<int, RepeatFuncContext>("CurrentIteration", "CURRENT_ITERATION", GetIteration);
-        Output = CreateFuncOutput<object, RepeatFuncContext>("Output", "OUTPUT", GetOutput);
+        Iterations = CreateInput<int>("Iterations", "ITERATIONS", 1);
+        Input = CreateInput<object>("Input", "INPUT", null);
+        CurrentIteration = CreateOutput<int>("CurrentIteration", "CURRENT_ITERATION", 0);
+        Output = CreateOutput<object>("Output", "OUTPUT", null);
     }
 
     protected override void OnExecute(RenderContext context)
     {
-        RepeatFuncContext funcContext = new RepeatFuncContext(1);
+        Output.Value = Input.Value;
+        CurrentIteration.Value = 0;
     }
 
-    private int GetIteration(RepeatFuncContext context)
+    public override Node CreateCopy()
     {
-        return context.CurrentIteration;
+        return new RepeatNodeStart();
     }
 
-    private object GetOutput(RepeatFuncContext context)
-    {
-        return null;
-    }
+    public HashSet<IReadOnlyNode> HandledNodes => CalculateHandledNodes();
 
-    public override Node CreateCopy()
+    private HashSet<IReadOnlyNode> CalculateHandledNodes()
     {
-        return new RepeatNodeStart();
+        HashSet<IReadOnlyNode> handled = new();
+
+        TraverseForwards(node =>
+        {
+            if (node is RepeatNodeEnd)
+            {
+                return false;
+            }
+
+            if (node != this)
+            {
+                handled.Add(node);
+            }
+
+            return true;
+        });
+
+        return handled;
     }
 }

+ 17 - 2
src/PixiEditor.ChangeableDocument/Changes/NodeGraph/ConnectProperties_Change.cs

@@ -200,6 +200,11 @@ internal class ConnectProperties_Change : Change
     {
         if (input.ValueType != output.ValueType)
         {
+            if(IsConnectingObject(output))
+            {
+                return true;
+            }
+
             if (IsCrossExpression(output.Value, input.ValueType))
             {
                 return true;
@@ -233,13 +238,23 @@ internal class ConnectProperties_Change : Change
         return true;
     }
 
+    private static bool IsConnectingObject(OutputProperty output)
+    {
+        if (output.ValueType == typeof(object))
+        {
+            return true;
+        }
+
+        return false;
+    }
+
     private static bool IsConstantToExpression(InputProperty input, object objValue, out object result)
     {
         if (input.Value is Delegate func && func.Method.ReturnType.IsAssignableTo(typeof(ShaderExpressionVariable)))
         {
             try
             {
-                var actualArg = func.DynamicInvoke(FuncContext.NoContext);
+                var actualArg = func.DynamicInvoke(ShaderFuncContext.NoContext);
                 if (actualArg is ShaderExpressionVariable variable)
                 {
                     result = variable.GetConstant();
@@ -263,7 +278,7 @@ internal class ConnectProperties_Change : Change
         {
             try
             {
-                o = func.DynamicInvoke(FuncContext.NoContext);
+                o = func.DynamicInvoke(ShaderFuncContext.NoContext);
                 if (o is ShaderExpressionVariable variable)
                 {
                     o = variable.GetConstant();

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changes/NodeGraph/ConversionTable.cs

@@ -83,7 +83,7 @@ public static class ConversionTable
         {
             try
             {
-                var actualArg = func.DynamicInvoke(FuncContext.NoContext);
+                var actualArg = func.DynamicInvoke(ShaderFuncContext.NoContext);
                 return TryConvert(actualArg, targetType, out result);
             }
             catch

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changes/NodeGraph/GetComputedPropertyValue_Change.cs

@@ -58,7 +58,7 @@ internal class GetComputedPropertyValue_Change : Change
         {
             try
             {
-                value = del.DynamicInvoke(FuncContext.NoContext);
+                value = del.DynamicInvoke(ShaderFuncContext.NoContext);
             }
             catch (Exception e)
             {

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changes/NodeGraph/NodeOperations.cs

@@ -379,7 +379,7 @@ public static class NodeOperations
             object value = input.NonOverridenValue;
             if (value is Delegate del)
             {
-                value = del.DynamicInvoke(FuncContext.NoContext);
+                value = del.DynamicInvoke(ShaderFuncContext.NoContext);
                 if (value is ShaderExpressionVariable expressionVariable)
                 {
                     value = expressionVariable.GetConstant();

+ 1 - 1
src/PixiEditor/Helpers/SerializationUtil.cs

@@ -18,7 +18,7 @@ public static class SerializationUtil
         
         if (value is Delegate del)
         {
-            value = del.DynamicInvoke(FuncContext.NoContext);
+            value = del.DynamicInvoke(ShaderFuncContext.NoContext);
             if (value is ShaderExpressionVariable expressionVariable)
             { 
                 value = expressionVariable.GetConstant();