|  | @@ -0,0 +1,407 @@
 | 
	
		
			
				|  |  | +using ChunkyImageLib.DataHolders;
 | 
	
		
			
				|  |  | +using Drawie.Backend.Core.Shaders.Generation.Expressions;
 | 
	
		
			
				|  |  | +using Drawie.Backend.Core.Surfaces;
 | 
	
		
			
				|  |  | +using Drawie.Backend.Core.Surfaces.ImageData;
 | 
	
		
			
				|  |  | +using Drawie.Numerics;
 | 
	
		
			
				|  |  | +using PixiEditor.ChangeableDocument.Changeables.Animations;
 | 
	
		
			
				|  |  | +using PixiEditor.ChangeableDocument.Changeables.Graph;
 | 
	
		
			
				|  |  | +using PixiEditor.ChangeableDocument.Changeables.Graph.Context;
 | 
	
		
			
				|  |  | +using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 | 
	
		
			
				|  |  | +using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.CombineSeparate;
 | 
	
		
			
				|  |  | +using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Utility;
 | 
	
		
			
				|  |  | +using PixiEditor.ChangeableDocument.Rendering;
 | 
	
		
			
				|  |  | +using PixiEditor.Tests;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +namespace PixiEditor.Backend.Tests;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +public class RepeatNodeTests : PixiEditorTest
 | 
	
		
			
				|  |  | +{
 | 
	
		
			
				|  |  | +    [Fact]
 | 
	
		
			
				|  |  | +    public void TestThatRepeatNodeProperlyDuplicatesNodes()
 | 
	
		
			
				|  |  | +    {
 | 
	
		
			
				|  |  | +        RepeatNodeStart start = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd end = new();
 | 
	
		
			
				|  |  | +        MathNode mathNode = new();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        mathNode.X.NonOverridenValue = context => new Float1("") { ConstantValue = 1 };
 | 
	
		
			
				|  |  | +        start.Output.ConnectTo(mathNode.Y);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        mathNode.Result.ConnectTo(end.Input);
 | 
	
		
			
				|  |  | +        start.Iterations.NonOverridenValue = 3;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        RenderContext context = new(null, new KeyFrameTime(), ChunkResolution.Full, VecI.Zero, VecI.Zero,
 | 
	
		
			
				|  |  | +            ColorSpace.CreateSrgb(), SamplingOptions.Bilinear);
 | 
	
		
			
				|  |  | +        var executionQueue = GraphUtils.CalculateExecutionQueue(end, true);
 | 
	
		
			
				|  |  | +        foreach (var node in executionQueue)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            node.Execute(context);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        Assert.True(end.Output.Value is Delegate);
 | 
	
		
			
				|  |  | +        var func = (Delegate)end.Output.Value!;
 | 
	
		
			
				|  |  | +        var result = func.DynamicInvoke(ShaderFuncContext.NoContext);
 | 
	
		
			
				|  |  | +        Assert.Equal(3, ((Float1)result!).ConstantValue);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    [Fact]
 | 
	
		
			
				|  |  | +    public void TestThatNestedRepeatNodesProperlyExecutes()
 | 
	
		
			
				|  |  | +    {
 | 
	
		
			
				|  |  | +        RepeatNodeStart startOuter = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd endOuter = new();
 | 
	
		
			
				|  |  | +        RepeatNodeStart startInner = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd endInner = new();
 | 
	
		
			
				|  |  | +        MathNode mathNode = new();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        mathNode.X.NonOverridenValue = context => new Float1("") { ConstantValue = 1 };
 | 
	
		
			
				|  |  | +        startInner.Output.ConnectTo(mathNode.Y);
 | 
	
		
			
				|  |  | +        mathNode.Result.ConnectTo(endInner.Input);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        startOuter.Output.ConnectTo(startInner.Input);
 | 
	
		
			
				|  |  | +        endInner.Output.ConnectTo(endOuter.Input);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        startOuter.Iterations.NonOverridenValue = 2;
 | 
	
		
			
				|  |  | +        startInner.Iterations.NonOverridenValue = 7;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        RenderContext context = new(null, new KeyFrameTime(), ChunkResolution.Full, VecI.Zero, VecI.Zero,
 | 
	
		
			
				|  |  | +            ColorSpace.CreateSrgb(), SamplingOptions.Bilinear);
 | 
	
		
			
				|  |  | +        var executionQueue = GraphUtils.CalculateExecutionQueue(endOuter, true);
 | 
	
		
			
				|  |  | +        foreach (var node in executionQueue)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            node.Execute(context);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        Assert.True(endOuter.Output.Value is Delegate);
 | 
	
		
			
				|  |  | +        var func = (Delegate)endOuter.Output.Value!;
 | 
	
		
			
				|  |  | +        var result = func.DynamicInvoke(ShaderFuncContext.NoContext);
 | 
	
		
			
				|  |  | +        Assert.Equal(14, ((Float1)result!).ConstantValue);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    [Fact]
 | 
	
		
			
				|  |  | +    public void TestThatNodePairingWorksForMultipleNestingLevels()
 | 
	
		
			
				|  |  | +    {
 | 
	
		
			
				|  |  | +        RepeatNodeStart start1 = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd end1 = new();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        RepeatNodeStart start2 = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd end2 = new();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        start1.Output.ConnectTo(start2.Input);
 | 
	
		
			
				|  |  | +        start2.Output.ConnectTo(end2.Input);
 | 
	
		
			
				|  |  | +        end2.Output.ConnectTo(end1.Input);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        var emptyContext = new RenderContext(null, new KeyFrameTime(), ChunkResolution.Full, VecI.Zero, VecI.Zero,
 | 
	
		
			
				|  |  | +            ColorSpace.CreateSrgb(), SamplingOptions.Bilinear);
 | 
	
		
			
				|  |  | +        start1.Execute(emptyContext);
 | 
	
		
			
				|  |  | +        start2.Execute(emptyContext);
 | 
	
		
			
				|  |  | +        end2.Execute(emptyContext);
 | 
	
		
			
				|  |  | +        end1.Execute(emptyContext);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        Assert.Equal(start1.OtherNode, end1.Id);
 | 
	
		
			
				|  |  | +        Assert.Equal(end1.OtherNode, start1.Id);
 | 
	
		
			
				|  |  | +        Assert.Equal(start2.OtherNode, end2.Id);
 | 
	
		
			
				|  |  | +        Assert.Equal(end2.OtherNode, start2.Id);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    [Fact]
 | 
	
		
			
				|  |  | +    public void CalculateHandledNodesProperlyReportsMathNodeOnly()
 | 
	
		
			
				|  |  | +    {
 | 
	
		
			
				|  |  | +        RepeatNodeStart startOuter = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd endOuter = new();
 | 
	
		
			
				|  |  | +        RepeatNodeStart startInner = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd endInner = new();
 | 
	
		
			
				|  |  | +        MathNode mathNode = new();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        mathNode.X.NonOverridenValue = context => new Float1("") { ConstantValue = 1 };
 | 
	
		
			
				|  |  | +        startInner.Output.ConnectTo(mathNode.Y);
 | 
	
		
			
				|  |  | +        mathNode.Result.ConnectTo(endInner.Input);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        startOuter.Output.ConnectTo(startInner.Input);
 | 
	
		
			
				|  |  | +        endInner.Output.ConnectTo(endOuter.Input);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        Assert.Single(endInner.HandledNodes);
 | 
	
		
			
				|  |  | +        Assert.Contains(mathNode, endOuter.HandledNodes);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        Assert.Equal(3, endOuter.HandledNodes.Count); // mathNode, startInner, endInner
 | 
	
		
			
				|  |  | +        Assert.Contains(startInner, endOuter.HandledNodes);
 | 
	
		
			
				|  |  | +        Assert.Contains(endInner, endOuter.HandledNodes);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    [Fact]
 | 
	
		
			
				|  |  | +    public void FindStartNodeProperlyFindsNodeInNestedZone()
 | 
	
		
			
				|  |  | +    {
 | 
	
		
			
				|  |  | +        RepeatNodeStart startOuter = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd endOuter = new();
 | 
	
		
			
				|  |  | +        RepeatNodeStart startInner = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd endInner = new();
 | 
	
		
			
				|  |  | +        MathNode mathNode = new();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        startOuter.Output.ConnectTo(startInner.Input);
 | 
	
		
			
				|  |  | +        startInner.Output.ConnectTo(mathNode.Y);
 | 
	
		
			
				|  |  | +        mathNode.Result.ConnectTo(endInner.Input);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        endInner.Output.ConnectTo(endOuter.Input);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        Assert.Equal(startOuter, endOuter.FindStartNode(out _));
 | 
	
		
			
				|  |  | +        Assert.Equal(startInner, endInner.FindStartNode(out _));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    [Fact]
 | 
	
		
			
				|  |  | +    public void FindStartNodeProperlyFindsNodeInNestedZoneWithOuterZoneReference()
 | 
	
		
			
				|  |  | +    {
 | 
	
		
			
				|  |  | +        RepeatNodeStart startOuter = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd endOuter = new();
 | 
	
		
			
				|  |  | +        RepeatNodeStart startInner = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd endInner = new();
 | 
	
		
			
				|  |  | +        MathNode mathNode = new();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        startInner.Output.ConnectTo(mathNode.Y);
 | 
	
		
			
				|  |  | +        mathNode.Result.ConnectTo(endInner.Input);
 | 
	
		
			
				|  |  | +        startOuter.CurrentIteration.ConnectTo(mathNode.X);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        startOuter.Output.ConnectTo(startInner.Input);
 | 
	
		
			
				|  |  | +        endInner.Output.ConnectTo(endOuter.Input);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        Assert.Equal(startOuter, endOuter.FindStartNode(out _));
 | 
	
		
			
				|  |  | +        Assert.Equal(startInner, endInner.FindStartNode(out _));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    [Fact]
 | 
	
		
			
				|  |  | +    public void FindStartNodeProperlyFindsNodeInNestedZoneWithOuterZoneReference2()
 | 
	
		
			
				|  |  | +    {
 | 
	
		
			
				|  |  | +        RepeatNodeStart startOuter = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd endOuter = new();
 | 
	
		
			
				|  |  | +        RepeatNodeStart startInner = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd endInner = new();
 | 
	
		
			
				|  |  | +        MathNode mathNode = new();
 | 
	
		
			
				|  |  | +        MathNode mathNode2 = new();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        startInner.Output.ConnectTo(mathNode.Y);
 | 
	
		
			
				|  |  | +        startInner.CurrentIteration.ConnectTo(mathNode2.X);
 | 
	
		
			
				|  |  | +        startOuter.CurrentIteration.ConnectTo(mathNode.Y);
 | 
	
		
			
				|  |  | +        mathNode.Result.ConnectTo(mathNode2.Y);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        mathNode2.Result.ConnectTo(endInner.Input);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        startOuter.Output.ConnectTo(startInner.Input);
 | 
	
		
			
				|  |  | +        endInner.Output.ConnectTo(endOuter.Input);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        Assert.Equal(startOuter, endOuter.FindStartNode(out _));
 | 
	
		
			
				|  |  | +        Assert.Equal(startInner, endInner.FindStartNode(out _));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    [Fact]
 | 
	
		
			
				|  |  | +    public void TestThatNestedRepeatReferencingOuterActiveIterationCalculatesHandledNodesProperly2()
 | 
	
		
			
				|  |  | +    {
 | 
	
		
			
				|  |  | +        RepeatNodeStart startOuter = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd endOuter = new();
 | 
	
		
			
				|  |  | +        RepeatNodeStart startInner = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd endInner = new();
 | 
	
		
			
				|  |  | +        MathNode mathNode = new();
 | 
	
		
			
				|  |  | +        MathNode mathNode2 = new();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        startInner.Output.ConnectTo(mathNode.Y);
 | 
	
		
			
				|  |  | +        startInner.CurrentIteration.ConnectTo(mathNode2.X);
 | 
	
		
			
				|  |  | +        startOuter.CurrentIteration.ConnectTo(mathNode.Y);
 | 
	
		
			
				|  |  | +        mathNode.Result.ConnectTo(mathNode2.Y);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        mathNode2.Result.ConnectTo(endInner.Input);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        startOuter.Output.ConnectTo(startInner.Input);
 | 
	
		
			
				|  |  | +        endInner.Output.ConnectTo(endOuter.Input);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        Assert.Equal(2, endInner.HandledNodes.Count);
 | 
	
		
			
				|  |  | +        Assert.Contains(mathNode, endOuter.HandledNodes);
 | 
	
		
			
				|  |  | +        Assert.Contains(mathNode2, endOuter.HandledNodes);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        Assert.Equal(4, endOuter.HandledNodes.Count);
 | 
	
		
			
				|  |  | +        Assert.Contains(startInner, endOuter.HandledNodes);
 | 
	
		
			
				|  |  | +        Assert.Contains(endInner, endOuter.HandledNodes);
 | 
	
		
			
				|  |  | +        Assert.Contains(mathNode, endOuter.HandledNodes);
 | 
	
		
			
				|  |  | +        Assert.Contains(mathNode2, endOuter.HandledNodes);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    [Fact]
 | 
	
		
			
				|  |  | +    public void TestThatNestedRepeatReferencingOuterActiveIterationCalculatesHandledNodesProperly()
 | 
	
		
			
				|  |  | +    {
 | 
	
		
			
				|  |  | +        RepeatNodeStart startOuter = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd endOuter = new();
 | 
	
		
			
				|  |  | +        RepeatNodeStart startInner = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd endInner = new();
 | 
	
		
			
				|  |  | +        MathNode mathNode = new();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        startOuter.Output.ConnectTo(startInner.Input);
 | 
	
		
			
				|  |  | +        startOuter.CurrentIteration.ConnectTo(mathNode.X);
 | 
	
		
			
				|  |  | +        startInner.Output.ConnectTo(mathNode.Y);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        mathNode.Result.ConnectTo(endInner.Input);
 | 
	
		
			
				|  |  | +        endInner.Output.ConnectTo(endOuter.Input);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        Assert.Single(endInner.HandledNodes);
 | 
	
		
			
				|  |  | +        Assert.Contains(mathNode, endOuter.HandledNodes);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        Assert.Equal(3, endOuter.HandledNodes.Count); // mathNode, startInner, endInner
 | 
	
		
			
				|  |  | +        Assert.Contains(startInner, endOuter.HandledNodes);
 | 
	
		
			
				|  |  | +        Assert.Contains(endInner, endOuter.HandledNodes);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    [Fact]
 | 
	
		
			
				|  |  | +    public void TestThatStartNodesAreCalculatedProperlyForNestedRepeats()
 | 
	
		
			
				|  |  | +    {
 | 
	
		
			
				|  |  | +        RepeatNodeStart startOuter = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd endOuter = new();
 | 
	
		
			
				|  |  | +        RepeatNodeStart startInner = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd endInner = new();
 | 
	
		
			
				|  |  | +        MathNode mathNodeToConnectToOuter = new();
 | 
	
		
			
				|  |  | +        MathNode mathNodeToConnectToInner = new();
 | 
	
		
			
				|  |  | +        MathNode mathNodeToCombineBoth = new();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        mathNodeToConnectToOuter.X.NonOverridenValue = context => new Float1("") { ConstantValue = 1 };
 | 
	
		
			
				|  |  | +        mathNodeToConnectToInner.X.NonOverridenValue = context => new Float1("") { ConstantValue = 1 };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        startOuter.Output.ConnectTo(startInner.Input);
 | 
	
		
			
				|  |  | +        startOuter.CurrentIteration.ConnectTo(mathNodeToConnectToOuter.Y);
 | 
	
		
			
				|  |  | +        startInner.CurrentIteration.ConnectTo(mathNodeToConnectToInner.Y);
 | 
	
		
			
				|  |  | +        mathNodeToConnectToOuter.Result.ConnectTo(mathNodeToCombineBoth.X);
 | 
	
		
			
				|  |  | +        mathNodeToConnectToInner.Result.ConnectTo(mathNodeToCombineBoth.Y);
 | 
	
		
			
				|  |  | +        mathNodeToCombineBoth.Result.ConnectTo(endInner.Input);
 | 
	
		
			
				|  |  | +        endInner.Output.ConnectTo(endOuter.Input);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        Assert.Equal(startOuter, endOuter.FindStartNode(out _));
 | 
	
		
			
				|  |  | +        Assert.Equal(startInner, endInner.FindStartNode(out _));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    [Fact]
 | 
	
		
			
				|  |  | +    public void TestThatStartNodesAreCalculatedProperlyForUnrolledRepeatNodes()
 | 
	
		
			
				|  |  | +    {
 | 
	
		
			
				|  |  | +        RepeatNodeStart startOuter = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd endOuter = new();
 | 
	
		
			
				|  |  | +        RepeatNodeStart startInner = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd endInner = new();
 | 
	
		
			
				|  |  | +        MathNode mathNodeToConnectToOuter = new();
 | 
	
		
			
				|  |  | +        MathNode mathNodeToConnectToInner = new();
 | 
	
		
			
				|  |  | +        MathNode mathNodeToCombineBoth = new();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        mathNodeToConnectToOuter.X.NonOverridenValue = context => new Float1("") { ConstantValue = 2 };
 | 
	
		
			
				|  |  | +        mathNodeToConnectToInner.X.NonOverridenValue = context => new Float1("") { ConstantValue = 2 };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        startOuter.Output.ConnectTo(startInner.Input);
 | 
	
		
			
				|  |  | +        startOuter.CurrentIteration.ConnectTo(mathNodeToConnectToOuter.Y);
 | 
	
		
			
				|  |  | +        startInner.CurrentIteration.ConnectTo(mathNodeToConnectToInner.Y);
 | 
	
		
			
				|  |  | +        mathNodeToConnectToOuter.Result.ConnectTo(mathNodeToCombineBoth.X);
 | 
	
		
			
				|  |  | +        mathNodeToConnectToInner.Result.ConnectTo(mathNodeToCombineBoth.Y);
 | 
	
		
			
				|  |  | +        mathNodeToCombineBoth.Result.ConnectTo(endInner.Input);
 | 
	
		
			
				|  |  | +        endInner.Output.ConnectTo(endOuter.Input);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        var emptyContext = new RenderContext(null, new KeyFrameTime(), ChunkResolution.Full, VecI.Zero, VecI.Zero,
 | 
	
		
			
				|  |  | +            ColorSpace.CreateSrgb(), SamplingOptions.Bilinear);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        var unrollQueue = endOuter.HandledNodes;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        var queue = startOuter.UnrollLoop(2, unrollQueue, emptyContext, emptyContext.ContextVirtualSession);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        var startNode = queue.OfType<RepeatNodeStart>().First();
 | 
	
		
			
				|  |  | +        var endNode = queue.OfType<RepeatNodeEnd>().First();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        foreach (var node in queue)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            emptyContext.SetActiveVirtualConnectionScope(emptyContext.ContextVirtualSession);
 | 
	
		
			
				|  |  | +            node.Execute(emptyContext);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        Assert.Equal(startNode.OtherNode, endNode.Id);
 | 
	
		
			
				|  |  | +        Assert.Equal(endNode.OtherNode, startNode.Id);
 | 
	
		
			
				|  |  | +        Assert.Equal(startNode.Id, endNode.OtherNode);
 | 
	
		
			
				|  |  | +        Assert.Equal(endNode.Id, startNode.OtherNode);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        var queueInner = GraphUtils.CalculateExecutionQueue(endInner, false, true,
 | 
	
		
			
				|  |  | +            property => property.Connection?.Node != startInner);
 | 
	
		
			
				|  |  | +        var startNodeOuter = queueInner.OfType<RepeatNodeStart>().First();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        Assert.Equal(endOuter.Id, startNodeOuter.OtherNode);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    [Fact]
 | 
	
		
			
				|  |  | +    public void TestThatHandledNodesAreCalculatedProperlyForNestedRepeats()
 | 
	
		
			
				|  |  | +    {
 | 
	
		
			
				|  |  | +        RepeatNodeStart startOuter = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd endOuter = new();
 | 
	
		
			
				|  |  | +        RepeatNodeStart startInner = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd endInner = new();
 | 
	
		
			
				|  |  | +        MathNode mathNodeToConnectToOuter = new();
 | 
	
		
			
				|  |  | +        MathNode mathNodeToConnectToInner = new();
 | 
	
		
			
				|  |  | +        MathNode mathNodeToCombineBoth = new();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        mathNodeToConnectToOuter.X.NonOverridenValue = context => new Float1("") { ConstantValue = 1 };
 | 
	
		
			
				|  |  | +        mathNodeToConnectToInner.X.NonOverridenValue = context => new Float1("") { ConstantValue = 1 };
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        startOuter.Output.ConnectTo(startInner.Input);
 | 
	
		
			
				|  |  | +        startOuter.CurrentIteration.ConnectTo(mathNodeToConnectToOuter.Y);
 | 
	
		
			
				|  |  | +        startInner.CurrentIteration.ConnectTo(mathNodeToConnectToInner.Y);
 | 
	
		
			
				|  |  | +        mathNodeToConnectToOuter.Result.ConnectTo(mathNodeToCombineBoth.X);
 | 
	
		
			
				|  |  | +        mathNodeToConnectToInner.Result.ConnectTo(mathNodeToCombineBoth.Y);
 | 
	
		
			
				|  |  | +        mathNodeToCombineBoth.Result.ConnectTo(endInner.Input);
 | 
	
		
			
				|  |  | +        endInner.Output.ConnectTo(endOuter.Input);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        Assert.Equal(2, endInner.HandledNodes.Count);
 | 
	
		
			
				|  |  | +        Assert.Contains(mathNodeToConnectToInner, endInner.HandledNodes);
 | 
	
		
			
				|  |  | +        Assert.Contains(mathNodeToCombineBoth, endInner.HandledNodes);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        Assert.Equal(5, endOuter.HandledNodes.Count);
 | 
	
		
			
				|  |  | +        Assert.Contains(startInner, endOuter.HandledNodes);
 | 
	
		
			
				|  |  | +        Assert.Contains(endInner, endOuter.HandledNodes);
 | 
	
		
			
				|  |  | +        Assert.Contains(mathNodeToConnectToOuter, endOuter.HandledNodes);
 | 
	
		
			
				|  |  | +        Assert.Contains(mathNodeToConnectToInner, endOuter.HandledNodes);
 | 
	
		
			
				|  |  | +        Assert.Contains(mathNodeToCombineBoth, endOuter.HandledNodes);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    [Fact]
 | 
	
		
			
				|  |  | +    public void TestThatNestedRepeatReferencingOuterActiveIterationCalculatesValueProperly()
 | 
	
		
			
				|  |  | +    {
 | 
	
		
			
				|  |  | +        RepeatNodeStart startOuter = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd endOuter = new();
 | 
	
		
			
				|  |  | +        RepeatNodeStart startInner = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd endInner = new();
 | 
	
		
			
				|  |  | +        MathNode mathNode = new();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        startOuter.Output.ConnectTo(startInner.Input);
 | 
	
		
			
				|  |  | +        startOuter.CurrentIteration.ConnectTo(mathNode.X);
 | 
	
		
			
				|  |  | +        startInner.Output.ConnectTo(mathNode.Y);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        mathNode.Result.ConnectTo(endInner.Input);
 | 
	
		
			
				|  |  | +        endInner.Output.ConnectTo(endOuter.Input);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        startOuter.Iterations.NonOverridenValue = 2;
 | 
	
		
			
				|  |  | +        startInner.Iterations.NonOverridenValue = 10;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        RenderContext context = new(null, new KeyFrameTime(), ChunkResolution.Full, VecI.Zero, VecI.Zero,
 | 
	
		
			
				|  |  | +            ColorSpace.CreateSrgb(), SamplingOptions.Bilinear);
 | 
	
		
			
				|  |  | +        var executionQueue = GraphUtils.CalculateExecutionQueue(endOuter, true);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        Assert.Equal(startOuter.Id, executionQueue.ElementAt(0).Id);
 | 
	
		
			
				|  |  | +        Assert.Equal(endOuter.Id, executionQueue.ElementAt(1).Id);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        foreach (var node in executionQueue)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            node.Execute(context);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        Assert.True(endOuter.Output.Value is Delegate);
 | 
	
		
			
				|  |  | +        var func = (Delegate)endOuter.Output.Value!;
 | 
	
		
			
				|  |  | +        var result = func.DynamicInvoke(ShaderFuncContext.NoContext);
 | 
	
		
			
				|  |  | +        Assert.Equal(30, ((Float1)result!).ConstantValue);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    [Fact]
 | 
	
		
			
				|  |  | +    public void TestThatNestedCurrentIterationUnrollsProperly()
 | 
	
		
			
				|  |  | +    {
 | 
	
		
			
				|  |  | +        RepeatNodeStart startOuter = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd endOuter = new();
 | 
	
		
			
				|  |  | +        RepeatNodeStart startInner = new();
 | 
	
		
			
				|  |  | +        RepeatNodeEnd endInner = new();
 | 
	
		
			
				|  |  | +        MathNode toConnectToOuter = new();
 | 
	
		
			
				|  |  | +        MathNode toConnectToInner = new();
 | 
	
		
			
				|  |  | +        CombineVecDNode toCombineBoth = new();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +}
 |