Răsfoiți Sursa

nested stuff

Krzysztof Krysiński 2 luni în urmă
părinte
comite
1c6cb79324

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

@@ -150,6 +150,9 @@ public static class GraphUtils
             }
         }
 
+        if(ignoreOutputFlowNode && outputNode is IExecutionFlowNode)
+            nodesToExclude.Add(outputNode);
+
         finalQueue = new HashSet<IReadOnlyNode>(finalQueue.Except(nodesToExclude));
 
         return new Queue<IReadOnlyNode>(finalQueue);

+ 65 - 46
src/PixiEditor.ChangeableDocument/Changeables/Graph/InputProperty.cs

@@ -16,6 +16,7 @@ public class InputProperty : IInputProperty
     private PropertyValidator? validator;
     private IOutputProperty? connection;
     private Dictionary<Guid, IOutputProperty> virtualConnections = new();
+    private Dictionary<Guid, object> virtualNonOverridenValues = new();
 
     public event Action ConnectionChanged;
     public event Action<object> NonOverridenValueChanged;
@@ -25,6 +26,19 @@ public class InputProperty : IInputProperty
 
     public Guid? ActiveVirtualSession { get; set; } = null;
 
+    public IOutputProperty? Connection
+    {
+        get => ActiveVirtualSession != null ? virtualConnections.GetValueOrDefault(ActiveVirtualSession.Value) : connection;
+        set
+        {
+            if (connection != value)
+            {
+                connection = value;
+                ConnectionChanged?.Invoke();
+            }
+        }
+    }
+
     public object? Value
     {
         get
@@ -35,7 +49,7 @@ public class InputProperty : IInputProperty
 
             if (connection == null)
             {
-                return _internalValue;
+                return NonOverridenValue;
             }
 
             var connectionValue = connection.Value;
@@ -45,51 +59,58 @@ public class InputProperty : IInputProperty
                 return null;
             }
 
-            object target = connectionValue;
-            if (!ValueType.IsAssignableTo(typeof(Delegate)) && connectionValue is Delegate connectionField)
-            {
-                try
-                {
-                    target = connectionField.DynamicInvoke(ShaderFuncContext.NoContext);
-                }
-                catch
-                {
-                    return null;
-                }
-            }
+            return Validator.GetClosestValidValue(CastValue(connectionValue));
+        }
+    }
 
-            if (ValueType.IsAssignableTo(typeof(Delegate)) && connectionValue is not Delegate)
+    private object CastValue(object value)
+    {
+        object target = value;
+        if (!ValueType.IsAssignableTo(typeof(Delegate)) && value is Delegate connectionField)
+        {
+            try
             {
-                return FuncFactory(connectionValue);
+                target = connectionField.DynamicInvoke(ShaderFuncContext.NoContext);
             }
-
-            if (connectionValue.GetType().IsAssignableTo(ValueType))
+            catch
             {
-                return connectionValue;
+                return null;
             }
+        }
 
-            if (connectionValue is Delegate func && ValueType.IsAssignableTo(typeof(Delegate)))
-            {
-                return FuncFactoryDelegate(func);
-            }
+        if (ValueType.IsAssignableTo(typeof(Delegate)) && value is not Delegate)
+        {
+            return FuncFactory(value);
+        }
 
-            if (target is ShaderExpressionVariable shaderExpression)
-            {
-                target = shaderExpression.GetConstant();
-            }
+        if (value.GetType().IsAssignableTo(ValueType))
+        {
+            return value;
+        }
 
-            if (!ConversionTable.TryConvert(target, ValueType, out object result))
-            {
-                return null;
-            }
+        if (value is Delegate func && ValueType.IsAssignableTo(typeof(Delegate)))
+        {
+            return FuncFactoryDelegate(func);
+        }
 
-            return Validator.GetClosestValidValue(result);
+        if (target is ShaderExpressionVariable shaderExpression)
+        {
+            target = shaderExpression.GetConstant();
+        }
+
+        if (!ConversionTable.TryConvert(target, ValueType, out object result))
+        {
+            return null;
         }
+
+        return result;
     }
 
     public object NonOverridenValue
     {
-        get => _internalValue;
+        get => ActiveVirtualSession != null && virtualNonOverridenValues.TryGetValue(ActiveVirtualSession.Value, out var virtualValue)
+            ? virtualValue
+            : _internalValue;
         set
         {
             object evaluatedValue = value;
@@ -215,25 +236,12 @@ public class InputProperty : IInputProperty
         return virtualConnections.GetValueOrDefault(virtualConnectionId);
     }
 
-    public void SetVirtualConnection(OutputProperty outputProperty, Guid virtualConnectionId, RenderContext context)
+    public void SetVirtualConnection(IOutputProperty outputProperty, Guid virtualConnectionId, RenderContext context)
     {
         virtualConnections[virtualConnectionId] = outputProperty;
         context.RecordVirtualConnection(this, virtualConnectionId);
     }
 
-    public IOutputProperty? Connection
-    {
-        get => ActiveVirtualSession != null ? virtualConnections.GetValueOrDefault(ActiveVirtualSession.Value) : connection;
-        set
-        {
-            if (connection != value)
-            {
-                connection = value;
-                ConnectionChanged?.Invoke();
-            }
-        }
-    }
-
     internal InputProperty(Node node, string internalName, string displayName, object defaultValue, Type valueType)
     {
         InternalPropertyName = internalName;
@@ -272,6 +280,17 @@ public class InputProperty : IInputProperty
         hash.Add(Connection?.GetCacheHash() ?? 0);
         return hash.ToHashCode();
     }
+
+    public void SetVirtualNonOverridenValue<T>(T value, Guid virtualConnectionId, RenderContext context)
+    {
+        virtualNonOverridenValues[virtualConnectionId] = CastValue(value);
+        context.RecordVirtualNonOverridenValue(this, virtualConnectionId);
+    }
+
+    public void RemoveVirtualNonOverridenValues(Guid virtualSessionId)
+    {
+        virtualNonOverridenValues.Remove(virtualSessionId);
+    }
 }
 
 public class InputProperty<T> : InputProperty, IInputProperty<T>

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

@@ -22,7 +22,7 @@ public interface INodeProperty<T> : INodeProperty
 public interface IInputProperty : INodeProperty
 {
     public IOutputProperty? GetVirtualConnection(Guid virtualConnectionId);
-    public void SetVirtualConnection(OutputProperty outputProperty, Guid virtualConnectionId, RenderContext context);
+    public void SetVirtualConnection(IOutputProperty outputProperty, Guid virtualConnectionId, RenderContext context);
     public IOutputProperty? Connection { get; set; }
     public object NonOverridenValue { get; set;  }
     public void RemoveVirtualConnection(Guid virtualConnectionId);

+ 15 - 9
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Utility/RepeatNodeEnd.cs

@@ -13,7 +13,18 @@ public class RepeatNodeEnd : Node, IPairNode, IExecutionFlowNode
     public InputProperty<object> Input { get; }
     public OutputProperty<object> Output { get; }
 
-    public Guid OtherNode { get; set; }
+    public Guid OtherNode
+    {
+        get
+        {
+            startNode = FindStartNode();
+            return startNode?.Id ?? Guid.Empty;
+        }
+        set
+        {
+           // no op, the start node is found dynamically
+        }
+    }
 
     private RepeatNodeStart startNode;
 
@@ -27,17 +38,12 @@ public class RepeatNodeEnd : Node, IPairNode, IExecutionFlowNode
 
     protected override void OnExecute(RenderContext context)
     {
-        if (OtherNode == Guid.Empty || startNode == null)
+        if (OtherNode == Guid.Empty)
         {
-            startNode = FindStartNode();
-            OtherNode = startNode?.Id ?? Guid.Empty;
-            if (OtherNode == Guid.Empty)
-            {
-                return;
-            }
+            return;
         }
 
-        if(startNode.Iterations.Value == 0)
+        if (startNode.Iterations.Value == 0)
         {
             Output.Value = DefaultOfType(Input.Value);
             return;

+ 58 - 11
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Utility/RepeatNodeStart.cs

@@ -1,4 +1,5 @@
 using Drawie.Backend.Core.Shaders.Generation;
+using Drawie.Backend.Core.Shaders.Generation.Expressions;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Context;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using PixiEditor.ChangeableDocument.Rendering;
@@ -17,7 +18,7 @@ public class RepeatNodeStart : Node, IPairNode
     public Guid OtherNode { get; set; }
     private RepeatNodeEnd? endNode;
 
-
+    private bool iterationInProgress = false;
     private Guid virtualSessionId;
     private Queue<IReadOnlyNode> unrolledQueue;
     private List<IReadOnlyNode> clonedNodes = new List<IReadOnlyNode>();
@@ -44,28 +45,41 @@ public class RepeatNodeStart : Node, IPairNode
         var queue = GraphUtils.CalculateExecutionQueue(endNode, true, true,
             property => property.Connection?.Node != this);
 
+        if (iterationInProgress)
+        {
+            return;
+        }
+
+        iterationInProgress = true;
+
         if (iterations == 0)
         {
             Output.Value = null;
             CurrentIteration.Value = 0;
+            iterationInProgress = false;
             return;
         }
 
         if (iterations > 1)
         {
+            ClearLastUnrolledNodes();
             virtualSessionId = Guid.NewGuid();
             context.BeginVirtualConnectionScope(virtualSessionId);
-            ClearLastUnrolledNodes();
-            queue = UnrollLoop(iterations, queue, context);
+            var unrollQueue = GraphUtils.CalculateExecutionQueue(endNode, false, true,
+                property => property.Connection?.Node != this);
+            queue = UnrollLoop(iterations, unrollQueue, context);
         }
 
+        CurrentIteration.Value = 1;
         Output.Value = Input.Value;
-        CurrentIteration.Value = 0; // TODO: Increment iteration in unrolled nodes
 
         foreach (var node in queue)
         {
+            context.SetActiveVirtualConnectionScope(virtualSessionId);
             node.Execute(context);
         }
+
+        iterationInProgress = false;
     }
 
     private void ClearLastUnrolledNodes()
@@ -85,17 +99,21 @@ public class RepeatNodeStart : Node, IPairNode
     {
         var connectToNextStart = endNode.Input.Connection;
         var connectPreviousTo = Output.Connections;
+        var originalConnectedToIteration = CurrentIteration.Connections;
 
         Queue<IReadOnlyNode> lastQueue = new Queue<IReadOnlyNode>(executionQueue.Where(x => x != this && x != endNode));
         for (int i = 0; i < iterations - 1; i++)
         {
             var mapping = new Dictionary<Guid, Node>();
-            CloneNodes(lastQueue, mapping);
+            CloneNodes(lastQueue, mapping, virtualSessionId, context);
             connectPreviousTo =
                 ReplaceConnections(connectToNextStart, connectPreviousTo, mapping, virtualSessionId, context);
             connectToNextStart = mapping[connectToNextStart.Node.Id].OutputProperties
                 .FirstOrDefault(y => y.InternalPropertyName == connectToNextStart.InternalPropertyName);
 
+            originalConnectedToIteration =
+                SetIterationConstants(mapping, originalConnectedToIteration, i + 2, virtualSessionId, context);
+
             clonedNodes.AddRange(mapping.Values);
             lastQueue = new Queue<IReadOnlyNode>(mapping.Values);
         }
@@ -106,6 +124,27 @@ public class RepeatNodeStart : Node, IPairNode
             property => property.Connection?.Node != this);
     }
 
+    private List<IInputProperty> SetIterationConstants(Dictionary<Guid, Node> mapping,
+        IReadOnlyCollection<IInputProperty> originalConnectedToIteration, int iteration, Guid virtualConnectionId,
+        RenderContext context)
+    {
+        List<IInputProperty> iterationInputs = new List<IInputProperty>();
+        foreach (var input in originalConnectedToIteration)
+        {
+            if (mapping.TryGetValue(input.Node.Id, out var mappedNode))
+            {
+                var mappedInput =
+                    mappedNode.InputProperties.FirstOrDefault(i =>
+                        i.InternalPropertyName == input.InternalPropertyName);
+
+                mappedInput.SetVirtualNonOverridenValue(iteration, virtualConnectionId, context);
+                iterationInputs.Add(mappedInput);
+            }
+        }
+
+        return iterationInputs;
+    }
+
     private IReadOnlyCollection<IInputProperty> ReplaceConnections(IOutputProperty? connectToNextStart,
         IReadOnlyCollection<IInputProperty> connectPreviousTo, Dictionary<Guid, Node> mapping, Guid virtualConnectionId,
         RenderContext context)
@@ -133,19 +172,21 @@ public class RepeatNodeStart : Node, IPairNode
         return connectPreviousToMapped;
     }
 
-    private void CloneNodes(Queue<IReadOnlyNode> originalQueue, Dictionary<Guid, Node> mapping)
+    private void CloneNodes(Queue<IReadOnlyNode> originalQueue, Dictionary<Guid, Node> mapping, Guid virtualSession, RenderContext context)
     {
         foreach (var node in originalQueue)
         {
             if (node is not Node n) continue;
-            var clonedNode = n.Clone();
+            Node clonedNode;
+            clonedNode = n.Clone();
+
             mapping[node.Id] = clonedNode;
         }
 
-        ConnectRelatedNodes(originalQueue, mapping);
+        ConnectRelatedNodes(originalQueue, mapping, virtualSession, context);
     }
 
-    private void ConnectRelatedNodes(Queue<IReadOnlyNode> originalQueue, Dictionary<Guid, Node> mapping)
+    private void ConnectRelatedNodes(Queue<IReadOnlyNode> originalQueue, Dictionary<Guid, Node> mapping, Guid virtualSession, RenderContext context)
     {
         foreach (var node in originalQueue)
         {
@@ -163,9 +204,15 @@ public class RepeatNodeStart : Node, IPairNode
                     {
                         var inputProp = clonedNode.InputProperties.FirstOrDefault(i =>
                             i.InternalPropertyName == input.InternalPropertyName);
-                        output.ConnectTo(inputProp);
+                        output.ConnectTo(inputProp); // No need for virtual connection as it is a cloned node anyway
                     }
-                }
+                } // TODO: Handle external connections
+                /*else if(input.Connection != null && input.Connection.Node != this) // External property connection, keep it
+                {
+                    var inputProp = clonedNode.InputProperties.FirstOrDefault(i =>
+                        i.InternalPropertyName == input.InternalPropertyName);
+                    input.Connection.VirtualConnectTo(inputProp, virtualSession, context);
+                }*/
             }
         }
     }

+ 6 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/OutputProperty.cs

@@ -17,6 +17,8 @@ public class OutputProperty : IOutputProperty
     public Node Node { get; }
     public Type ValueType { get; }
 
+    public Guid? ActiveVirtualSession { get; set; }
+
     IReadOnlyNode INodeProperty.Node => Node;
 
     public object Value
@@ -28,12 +30,15 @@ public class OutputProperty : IOutputProperty
         }
     }
 
-    public IReadOnlyCollection<IInputProperty> Connections => _connections;
+    public IReadOnlyCollection<IInputProperty> Connections => ActiveVirtualSession != null && _virtualConnections.TryGetValue(ActiveVirtualSession.Value, out var virtualConnections)
+        ? virtualConnections
+        : _connections;
     public IReadOnlyCollection<IInputProperty> GetVirtualConnections(Guid virtualSession) => _virtualConnections[virtualSession];
 
     public event InputConnectedEvent Connected;
     public event InputConnectedEvent Disconnected;
 
+
     internal OutputProperty(Node node, string internalName, string displayName, object defaultValue, Type valueType)
     {
         InternalPropertyName = internalName;

+ 45 - 2
src/PixiEditor.ChangeableDocument/Rendering/RenderContext.cs

@@ -25,6 +25,7 @@ public class RenderContext
     public string? TargetOutput { get; set; }   
 
     private List<Guid> virtualGraphSessions = new List<Guid>();
+
     private Dictionary<Guid, List<InputProperty>> recordedVirtualInputs = new();
     private Dictionary<Guid, List<OutputProperty>> recordedVirtualOutputs = new();
 
@@ -84,6 +85,30 @@ public class RenderContext
         virtualGraphSessions.Add(virtualSessionId);
     }
 
+    public void SetActiveVirtualConnectionScope(Guid? virtualSessionId)
+    {
+        if (virtualSessionId == null || !virtualGraphSessions.Contains(virtualSessionId.Value))
+            return;
+
+        var inputProperties = recordedVirtualInputs.GetValueOrDefault(virtualSessionId.Value);
+        if (inputProperties != null)
+        {
+            foreach (var input in inputProperties)
+            {
+                input.ActiveVirtualSession = virtualSessionId;
+            }
+        }
+
+        var outputProperties = recordedVirtualOutputs.GetValueOrDefault(virtualSessionId.Value);
+        if (outputProperties != null)
+        {
+            foreach (var output in outputProperties)
+            {
+                output.ActiveVirtualSession = virtualSessionId;
+            }
+        }
+    }
+
     public void EndVirtualConnectionScope(Guid virtualSessionId)
     {
         if (!virtualGraphSessions.Contains(virtualSessionId))
@@ -96,6 +121,7 @@ public class RenderContext
             foreach (var input in inputProperty.Value)
             {
                 input.RemoveVirtualConnection(virtualSessionId);
+                input.RemoveVirtualNonOverridenValues(virtualSessionId);
                 input.ActiveVirtualSession = null;
             }
         }
@@ -105,6 +131,7 @@ public class RenderContext
             foreach (var output in outputProperty.Value)
             {
                 output.RemoveAllVirtualConnections(virtualSessionId);
+                output.ActiveVirtualSession = null;
             }
         }
     }
@@ -136,13 +163,29 @@ public class RenderContext
         }
 
         outputs.Add(outputProperty);
+        outputProperty.ActiveVirtualSession = virtualConnectionId;
     }
 
     public void CleanupVirtualConnectionScopes()
     {
-        foreach (var virtualSessionId in virtualGraphSessions.ToArray())
+        while (virtualGraphSessions.Count > 0)
         {
-            EndVirtualConnectionScope(virtualSessionId);
+            EndVirtualConnectionScope(virtualGraphSessions[^1]);
         }
     }
+
+    public void RecordVirtualNonOverridenValue(InputProperty inputProperty, Guid virtualConnectionId)
+    {
+        if (virtualGraphSessions.Count == 0)
+            return;
+
+        if(!recordedVirtualInputs.TryGetValue(virtualConnectionId, out var inputs))
+        {
+            inputs = new List<InputProperty>();
+            recordedVirtualInputs[virtualConnectionId] = inputs;
+        }
+
+        inputProperty.ActiveVirtualSession = virtualConnectionId;
+        inputs.Add(inputProperty);
+    }
 }