|
@@ -17,7 +17,10 @@ public class RepeatNodeStart : Node, IPairNode
|
|
|
public Guid OtherNode { get; set; }
|
|
public Guid OtherNode { get; set; }
|
|
|
private RepeatNodeEnd? endNode;
|
|
private RepeatNodeEnd? endNode;
|
|
|
|
|
|
|
|
- private bool iterationInProgress = false;
|
|
|
|
|
|
|
+
|
|
|
|
|
+ private Guid virtualSessionId;
|
|
|
|
|
+ private Queue<IReadOnlyNode> unrolledQueue;
|
|
|
|
|
+ private List<IReadOnlyNode> clonedNodes = new List<IReadOnlyNode>();
|
|
|
|
|
|
|
|
public RepeatNodeStart()
|
|
public RepeatNodeStart()
|
|
|
{
|
|
{
|
|
@@ -29,11 +32,6 @@ public class RepeatNodeStart : Node, IPairNode
|
|
|
|
|
|
|
|
protected override void OnExecute(RenderContext context)
|
|
protected override void OnExecute(RenderContext context)
|
|
|
{
|
|
{
|
|
|
- if (iterationInProgress)
|
|
|
|
|
- {
|
|
|
|
|
- return;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
endNode = FindEndNode();
|
|
endNode = FindEndNode();
|
|
|
if (endNode == null)
|
|
if (endNode == null)
|
|
|
{
|
|
{
|
|
@@ -46,26 +44,130 @@ public class RepeatNodeStart : Node, IPairNode
|
|
|
var queue = GraphUtils.CalculateExecutionQueue(endNode, true, true,
|
|
var queue = GraphUtils.CalculateExecutionQueue(endNode, true, true,
|
|
|
property => property.Connection?.Node != this);
|
|
property => property.Connection?.Node != this);
|
|
|
|
|
|
|
|
|
|
+ if (iterations == 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ Output.Value = null;
|
|
|
|
|
+ CurrentIteration.Value = 0;
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (iterations > 1)
|
|
|
|
|
+ {
|
|
|
|
|
+ virtualSessionId = Guid.NewGuid();
|
|
|
|
|
+ context.BeginVirtualConnectionScope(virtualSessionId);
|
|
|
|
|
+ ClearLastUnrolledNodes();
|
|
|
|
|
+ queue = UnrollLoop(iterations, queue, context);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
Output.Value = Input.Value;
|
|
Output.Value = Input.Value;
|
|
|
- iterationInProgress = true;
|
|
|
|
|
- for (int i = 0; i < iterations; i++)
|
|
|
|
|
|
|
+ CurrentIteration.Value = 0; // TODO: Increment iteration in unrolled nodes
|
|
|
|
|
+
|
|
|
|
|
+ foreach (var node in queue)
|
|
|
{
|
|
{
|
|
|
- CurrentIteration.Value = i + 1;
|
|
|
|
|
- foreach (var node in queue)
|
|
|
|
|
|
|
+ node.Execute(context);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void ClearLastUnrolledNodes()
|
|
|
|
|
+ {
|
|
|
|
|
+ if (clonedNodes.Count > 0)
|
|
|
|
|
+ {
|
|
|
|
|
+ foreach (var node in clonedNodes)
|
|
|
{
|
|
{
|
|
|
- if (node == this)
|
|
|
|
|
|
|
+ if (node is IDisposable disposable) disposable.Dispose();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ clonedNodes.Clear();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private Queue<IReadOnlyNode> UnrollLoop(int iterations, Queue<IReadOnlyNode> executionQueue, RenderContext context)
|
|
|
|
|
+ {
|
|
|
|
|
+ var connectToNextStart = endNode.Input.Connection;
|
|
|
|
|
+ var connectPreviousTo = Output.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);
|
|
|
|
|
+ connectPreviousTo =
|
|
|
|
|
+ ReplaceConnections(connectToNextStart, connectPreviousTo, mapping, virtualSessionId, context);
|
|
|
|
|
+ connectToNextStart = mapping[connectToNextStart.Node.Id].OutputProperties
|
|
|
|
|
+ .FirstOrDefault(y => y.InternalPropertyName == connectToNextStart.InternalPropertyName);
|
|
|
|
|
+
|
|
|
|
|
+ clonedNodes.AddRange(mapping.Values);
|
|
|
|
|
+ lastQueue = new Queue<IReadOnlyNode>(mapping.Values);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ connectToNextStart.VirtualConnectTo(endNode.Input, virtualSessionId, context);
|
|
|
|
|
+
|
|
|
|
|
+ return GraphUtils.CalculateExecutionQueue(endNode, true, true,
|
|
|
|
|
+ property => property.Connection?.Node != this);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private IReadOnlyCollection<IInputProperty> ReplaceConnections(IOutputProperty? connectToNextStart,
|
|
|
|
|
+ IReadOnlyCollection<IInputProperty> connectPreviousTo, Dictionary<Guid, Node> mapping, Guid virtualConnectionId,
|
|
|
|
|
+ RenderContext context)
|
|
|
|
|
+ {
|
|
|
|
|
+ var connectPreviousToMapped = new List<IInputProperty>();
|
|
|
|
|
+ foreach (var input in connectPreviousTo)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (mapping.TryGetValue(input.Node.Id, out var mappedNode))
|
|
|
|
|
+ {
|
|
|
|
|
+ var mappedInput =
|
|
|
|
|
+ mappedNode.InputProperties.FirstOrDefault(i =>
|
|
|
|
|
+ i.InternalPropertyName == input.InternalPropertyName);
|
|
|
|
|
+ if (mappedInput != null)
|
|
|
{
|
|
{
|
|
|
- continue;
|
|
|
|
|
|
|
+ connectPreviousToMapped.Add(mappedInput);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- node.Execute(context);
|
|
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ foreach (var input in connectPreviousToMapped)
|
|
|
|
|
+ {
|
|
|
|
|
+ connectToNextStart?.VirtualConnectTo(input, virtualConnectionId, context);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
|
|
+ return connectPreviousToMapped;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- Output.Value = endNode.Output.Value;
|
|
|
|
|
|
|
+ private void CloneNodes(Queue<IReadOnlyNode> originalQueue, Dictionary<Guid, Node> mapping)
|
|
|
|
|
+ {
|
|
|
|
|
+ foreach (var node in originalQueue)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (node is not Node n) continue;
|
|
|
|
|
+ var clonedNode = n.Clone();
|
|
|
|
|
+ mapping[node.Id] = clonedNode;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- iterationInProgress = false;
|
|
|
|
|
|
|
+ ConnectRelatedNodes(originalQueue, mapping);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private void ConnectRelatedNodes(Queue<IReadOnlyNode> originalQueue, Dictionary<Guid, Node> mapping)
|
|
|
|
|
+ {
|
|
|
|
|
+ foreach (var node in originalQueue)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (node is not Node n) continue;
|
|
|
|
|
+ var clonedNode = mapping[node.Id];
|
|
|
|
|
+
|
|
|
|
|
+ foreach (var input in n.InputProperties)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (input.Connection != null &&
|
|
|
|
|
+ mapping.TryGetValue(input.Connection.Node.Id, out var connectedClonedNode))
|
|
|
|
|
+ {
|
|
|
|
|
+ var output = connectedClonedNode.OutputProperties.FirstOrDefault(o =>
|
|
|
|
|
+ o.InternalPropertyName == input.Connection.InternalPropertyName);
|
|
|
|
|
+ if (output != null)
|
|
|
|
|
+ {
|
|
|
|
|
+ var inputProp = clonedNode.InputProperties.FirstOrDefault(i =>
|
|
|
|
|
+ i.InternalPropertyName == input.InternalPropertyName);
|
|
|
|
|
+ output.ConnectTo(inputProp);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private RepeatNodeEnd FindEndNode()
|
|
private RepeatNodeEnd FindEndNode()
|