Browse Source

Merge members wip

flabbet 1 year ago
parent
commit
1c914249c5
27 changed files with 115 additions and 45 deletions
  1. 25 7
      src/PixiEditor.AvaloniaUI/Models/DocumentModels/Public/DocumentOperationsModule.cs
  2. 8 7
      src/PixiEditor.AvaloniaUI/Models/DocumentModels/Public/DocumentStructureModule.cs
  3. 10 8
      src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/LayersViewModel.cs
  4. 4 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CircleNode.cs
  5. 3 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineChannelsNode.cs
  6. 3 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineColorNode.cs
  7. 4 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineVecD.cs
  8. 4 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineVecI.cs
  9. 3 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateChannelsNode.cs
  10. 2 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateColorNode.cs
  11. 2 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateVecDNode.cs
  12. 2 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateVecINode.cs
  13. 3 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/DebugBlendModeNode.cs
  14. 3 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/EmptyImageNode.cs
  15. 2 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/FolderNode.cs
  16. 1 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageLayerNode.cs
  17. 5 3
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageSpaceNode.cs
  18. 3 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/KernelNode.cs
  19. 3 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/MathNode.cs
  20. 3 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/MatrixTransformNode.cs
  21. 2 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/MergeNode.cs
  22. 2 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageLeftNode.cs
  23. 2 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageRightNode.cs
  24. 3 2
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Node.cs
  25. 3 1
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/NoiseNode.cs
  26. 2 0
      src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/OutputNode.cs
  27. 8 8
      src/PixiEditor.ChangeableDocument/Changes/Structure/CreateStructureMember_Change.cs

+ 25 - 7
src/PixiEditor.AvaloniaUI/Models/DocumentModels/Public/DocumentOperationsModule.cs

@@ -381,17 +381,35 @@ internal class DocumentOperationsModule : IDocumentOperations
     {
         if (Internals.ChangeController.IsChangeActive || members.Count < 2)
             return;
-        var (child, parent) = Document.StructureHelper.FindChildAndParent(members[0]);
-        if (child is null || parent is null)
+
+        IStructureMemberHandler? node = Document.StructureHelper.FindNode<IStructureMemberHandler>(members[0]);
+        
+        if (node is null)
+            return;
+
+        INodeHandler? parent = null;
+
+        node.TraverseForwards(node =>
+        {
+            if (!members.Contains(node.Id))
+            {
+                parent = node;
+                return false;
+            }
+            
+            return true;
+        });
+        
+        if (parent is null)
             return;
-        //int index = parent.Children.IndexOf(child);
+        
         Guid newGuid = Guid.NewGuid();
 
         //make a new layer, put combined image onto it, delete layers that were merged
-        /*Internals.ActionAccumulator.AddActions(
-            new CreateStructureMember_Action(parent.Id, newGuid, index, StructureMemberType.Layer),
-            new StructureMemberName_Action(newGuid, child.NameBindable),
-            new CombineStructureMembersOnto_Action(members.ToHashSet(), newGuid, Document.AnimationHandler.ActiveFrameBindable));*/
+        Internals.ActionAccumulator.AddActions(
+            new CreateStructureMember_Action(parent.Id, newGuid, StructureMemberType.Layer),
+            new StructureMemberName_Action(newGuid, node.NameBindable),
+            new CombineStructureMembersOnto_Action(members.ToHashSet(), newGuid, Document.AnimationHandler.ActiveFrameBindable));
         foreach (var member in members)
             Internals.ActionAccumulator.AddActions(new DeleteStructureMember_Action(member));
         Internals.ActionAccumulator.AddActions(new ChangeBoundary_Action());

+ 8 - 7
src/PixiEditor.AvaloniaUI/Models/DocumentModels/Public/DocumentStructureModule.cs

@@ -26,20 +26,21 @@ internal class DocumentStructureModule
         return doc.NodeGraphHandler.AllNodes.FirstOrDefault(x => x.Id == guid && x is T) as T;
     }
 
-    public IStructureMemberHandler? FindFirstWhere(Predicate<IStructureMemberHandler> predicate)
+    public INodeHandler? FindFirstWhere(Predicate<INodeHandler> predicate)
     {
         return FindFirstWhere(predicate, doc.NodeGraphHandler);
     }
 
-    private IStructureMemberHandler? FindFirstWhere(Predicate<IStructureMemberHandler> predicate,
+    private INodeHandler? FindFirstWhere(
+        Predicate<INodeHandler> predicate,
         INodeGraphHandler graphVM)
     {
-        IStructureMemberHandler? result = null;
+        INodeHandler? result = null;
         graphVM.TryTraverse(node =>
         {
-            if (node is IStructureMemberHandler structureMemberNode && predicate(structureMemberNode))
+            if (predicate(node))
             {
-                result = structureMemberNode;
+                result = node;
                 return false;
             }
 
@@ -49,14 +50,14 @@ internal class DocumentStructureModule
         return result;
     }
 
-    public (IStructureMemberHandler?, IFolderHandler?) FindChildAndParent(Guid childGuid)
+    public (IStructureMemberHandler?, INodeHandler?) FindChildAndParent(Guid childGuid)
     {
         List<IStructureMemberHandler>? path = FindPath(childGuid);
         return path.Count switch
         {
             0 => (null, null),
             1 => (path[0], null),
-            >= 2 => (path[0], (IFolderHandler)path[1]),
+            >= 2 => (path[0], path[1]),
             _ => (null, null),
         };
     }

+ 10 - 8
src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/LayersViewModel.cs

@@ -14,6 +14,7 @@ using PixiEditor.AvaloniaUI.Helpers.Extensions;
 using PixiEditor.AvaloniaUI.Models.Commands.Attributes.Commands;
 using PixiEditor.AvaloniaUI.Models.Commands.Attributes.Evaluators;
 using PixiEditor.AvaloniaUI.Models.Dialogs;
+using PixiEditor.AvaloniaUI.Models.Handlers;
 using PixiEditor.AvaloniaUI.Models.IO;
 using PixiEditor.AvaloniaUI.Models.Layers;
 using PixiEditor.AvaloniaUI.ViewModels.Dock;
@@ -315,19 +316,20 @@ internal class LayersViewModel : SubViewModel<ViewModelMain>
 
     public void MergeSelectedWith(bool above)
     {
-        /*var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
+        var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
         var member = doc?.SelectedStructureMember;
         if (doc is null || member is null)
             return;
-        var (child, parent) = doc.StructureHelper.FindChildAndParent(member.Id);
-        if (child is null || parent is null)
-            return;
-        int index = parent.Children.IndexOf(child);
-        if (!above && index == 0)
+       
+        IStructureMemberHandler? nextMergeableMember = doc.StructureHelper.GetAboveMember(member.Id, false);
+        IStructureMemberHandler? previousMergeableMember = doc.StructureHelper.GetBelowMember(member.Id, false); 
+        
+        if (!above && previousMergeableMember is null)
             return;
-        if (above && index == parent.Children.Count - 1)
+        if (above && nextMergeableMember is null)
             return;
-        doc.Operations.MergeStructureMembers(new List<Guid> { member.Id, above ? parent.Children[index + 1].Id : parent.Children[index - 1].GuidValue });*/
+        
+        doc.Operations.MergeStructureMembers(new List<Guid> { member.Id, above ? nextMergeableMember.Id : previousMergeableMember.Id });
     }
 
     [Command.Basic("PixiEditor.Layer.MergeWithAbove", "MERGE_WITH_ABOVE", "MERGE_WITH_ABOVE_DESCRIPTIVE", CanExecute = "PixiEditor.Layer.HasMemberAbove")]

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

@@ -22,9 +22,12 @@ public class CircleNode : Node
         StrokeWidth = CreateInput<int>("StrokeWidth", "STROKE_WIDTH", 1);
         Output = CreateOutput<Surface?>("Output", "OUTPUT", null);
     }
-    
+
+    protected override string NodeUniqueName => "Circle";
+
     protected override Surface? OnExecute(RenderingContext context)
     {
+        /*TODO: ChunkyImage*/
         var radius = Radius.Value / 2;
         var strokeWidth = StrokeWidth.Value;
         var strokeOffset = new VecI(strokeWidth / 2);

+ 3 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineChannelsNode.cs

@@ -43,7 +43,9 @@ public class CombineChannelsNode : Node
         Image = CreateOutput<Surface>(nameof(Image), "IMAGE", null);
         Grayscale = CreateInput(nameof(Grayscale), "GRAYSCALE", false);
     }
-    
+
+    protected override string NodeUniqueName => "CombineChannels";
+
     protected override Surface? OnExecute(RenderingContext context)
     {
         var size = GetSize();

+ 3 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineColorNode.cs

@@ -35,7 +35,9 @@ public class CombineColorNode : Node
 
         return new Color((byte)r, (byte)g, (byte)b, (byte)a);
     }
-    
+
+    protected override string NodeUniqueName => "CombineColor"; 
+
     protected override Surface? OnExecute(RenderingContext context)
     {
         return null;

+ 4 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineVecD.cs

@@ -12,6 +12,8 @@ public class CombineVecD : Node
     public FieldInputProperty<double> X { get; }
     
     public FieldInputProperty<double> Y { get; }
+    
+    
 
     public CombineVecD()
     {
@@ -29,6 +31,8 @@ public class CombineVecD : Node
         return new VecD(r, g);
     }
 
+    protected override string NodeUniqueName => "CombineVecD";
+
     protected override Surface? OnExecute(RenderingContext context)
     {
         return null;

+ 4 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/CombineVecI.cs

@@ -11,6 +11,8 @@ public class CombineVecI : Node
     public FieldInputProperty<int> X { get; }
     
     public FieldInputProperty<int> Y { get; }
+    
+    
 
     public CombineVecI()
     {
@@ -28,6 +30,8 @@ public class CombineVecI : Node
         return new VecI(r, g);
     }
 
+    protected override string NodeUniqueName => "CombineVecI";
+
     protected override Surface? OnExecute(RenderingContext context)
     {
         return null;

+ 3 - 1
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateChannelsNode.cs

@@ -40,7 +40,9 @@ public class SeparateChannelsNode : Node
         Image = CreateInput<Surface>(nameof(Image), "IMAGE", null);
         Grayscale = CreateInput(nameof(Grayscale), "GRAYSCALE", false);
     }
-    
+
+    protected override string NodeUniqueName => "SeparateChannels";
+
     protected override Surface? OnExecute(RenderingContext context)
     {
         var image = Image.Value;

+ 2 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateColorNode.cs

@@ -24,6 +24,8 @@ public class SeparateColorNode : Node
         A = CreateFieldOutput(nameof(A), "A", ctx => Color.Value(ctx).A / 255d);
     }
 
+    protected override string NodeUniqueName => "SeparateColor";
+
     protected override Surface? OnExecute(RenderingContext context)
     {
         return null;

+ 2 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateVecDNode.cs

@@ -18,6 +18,8 @@ public class SeparateVecDNode : Node
         Vector = CreateFieldInput("Vector", "VECTOR", new VecD(0, 0));
     }
 
+    protected override string NodeUniqueName => "SeparateVecD";
+
     protected override Surface? OnExecute(RenderingContext context)
     {
         return null;

+ 2 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/CombineSeparate/SeparateVecINode.cs

@@ -18,6 +18,8 @@ public class SeparateVecINode : Node
         Vector = CreateFieldInput("Vector", "VECTOR", new VecI(0, 0));
     }
 
+    protected override string NodeUniqueName => "SeparateVecI";
+
     protected override Surface? OnExecute(RenderingContext context)
     {
         return null;

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

@@ -28,7 +28,9 @@ public class DebugBlendModeNode : Node
 
         Result = CreateOutput<Surface>(nameof(Result), "Result", null);
     }
-    
+
+    protected override string NodeUniqueName => "DebugBlendMode";
+
     protected override Surface? OnExecute(RenderingContext context)
     {
         if (Dst.Value is not { } dst || Src.Value is not { } src)

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

@@ -23,7 +23,9 @@ public class CreateImageNode : Node
         Size = CreateInput(nameof(Size), "SIZE", new VecI(32, 32));
         Fill = CreateInput(nameof(Fill), "FILL", new Color(0, 0, 0, 255));
     }
-    
+
+    protected override string NodeUniqueName => "EmptyImage";
+
     protected override Surface? OnExecute(RenderingContext context)
     {
         var surface = new Surface(Size.Value);

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

@@ -23,6 +23,8 @@ public class FolderNode : StructureNode, IReadOnlyFolderNode
 
     public override Node CreateCopy() => new FolderNode { MemberName = MemberName };
 
+    protected override string NodeUniqueName => "Folder";
+
     protected override Surface? OnExecute(RenderingContext context)
     {
         if(Background.Value == null && Content.Value == null)

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

@@ -19,6 +19,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         Color = PixiEditor.DrawingApi.Core.ColorsImpl.Colors.Transparent };
     
     // Handled by overriden CacheChanged
+    protected override string NodeUniqueName => "ImageLayer";
     protected override bool AffectedByAnimation => true;
 
     protected override bool AffectedByChunkResolution => true;

+ 5 - 3
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageSpaceNode.cs

@@ -8,16 +8,18 @@ namespace PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 
 public class ImageSpaceNode : Node
 {
-    public FieldOutputProperty<VecD> Position { get; }
+    public FieldOutputProperty<VecD> SpacePosition { get; }
     
     public FieldOutputProperty<VecI> Size { get; }
 
     public ImageSpaceNode()
     {
-        Position = CreateFieldOutput(nameof(Position), "PIXEL_COORDINATE", ctx => ctx.Position);
+        SpacePosition = CreateFieldOutput(nameof(SpacePosition), "PIXEL_COORDINATE", ctx => ctx.Position);
         Size = CreateFieldOutput(nameof(Size), "SIZE", ctx => ctx.Size);
     }
-    
+
+    protected override string NodeUniqueName => "ImageSpace";
+
     protected override Surface? OnExecute(RenderingContext context)
     {
         return null;

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

@@ -34,7 +34,9 @@ public class KernelFilterNode : Node
         Tile = CreateInput(nameof(Tile), "TILE_MODE", TileMode.Clamp);
         OnAlpha = CreateInput(nameof(OnAlpha), "ON_ALPHA", false);
     }
-    
+
+    protected override string NodeUniqueName => "KernelFilter";
+
     protected override Surface? OnExecute(RenderingContext context)
     {
         var input = Image.Value;

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

@@ -49,7 +49,9 @@ public class MathNode : Node
     }
 
     private (double x, double y) GetValues(FieldContext context) => (X.Value(context), Y.Value(context));
-    
+
+    protected override string NodeUniqueName => "Math";
+
     protected override Surface? OnExecute(RenderingContext context)
     {
         return null;

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

@@ -28,7 +28,9 @@ public class MatrixTransformNode : Node
 
         paint = new Paint { ColorFilter = ColorFilter.CreateColorMatrix(previousMatrix) };
     }
-    
+
+    protected override string NodeUniqueName => "MatrixTransform";
+
     protected override Surface? OnExecute(RenderingContext context)
     {
         if (Input.Value == null)

+ 2 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/MergeNode.cs

@@ -29,6 +29,8 @@ public class MergeNode : Node, IBackgroundInput
         return new MergeNode();
     }
 
+    protected override string NodeUniqueName => "Merge";
+
     protected override Surface? OnExecute(RenderingContext context)
     {
         if(Top.Value == null && Bottom.Value == null)

+ 2 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageLeftNode.cs

@@ -44,6 +44,8 @@ public class ModifyImageLeftNode : Node
         pixmap = Image.Value?.PeekPixels();
     }
 
+    protected override string NodeUniqueName => "ModifyImageLeft";
+
     protected override Surface? OnExecute(RenderingContext context)
     {
         return Image.Value;

+ 2 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ModifyImageRightNode.cs

@@ -27,6 +27,8 @@ public class ModifyImageRightNode : Node
         Output = CreateOutput<Surface>(nameof(Output), "OUTPUT", null);
     }
 
+    protected override string NodeUniqueName => "ModifyImageRight";
+
     protected override Surface? OnExecute(RenderingContext renderingContext)
     {
         if (startNode.Image.Value is not { Size: var size })

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

@@ -21,7 +21,9 @@ public abstract class Node : IReadOnlyNode, IDisposable
     public IReadOnlyCollection<OutputProperty> OutputProperties => outputs;
     public Surface? CachedResult { get; private set; }
 
-    public virtual string InternalName { get; }
+    public virtual string InternalName => $"PixiEditor.{NodeUniqueName}";
+    
+    protected abstract string NodeUniqueName { get; }
 
     protected virtual bool AffectedByAnimation { get; }
 
@@ -31,7 +33,6 @@ public abstract class Node : IReadOnlyNode, IDisposable
 
     protected Node()
     {
-        InternalName = $"PixiEditor.{GetType().Name}";
     }
 
     IReadOnlyCollection<IInputProperty> IReadOnlyNode.InputProperties => inputs;

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

@@ -27,7 +27,9 @@ public class NoiseNode : Node
         Scale = CreateInput(nameof(Scale), "SCALE", 0d);
         Seed = CreateInput(nameof(Seed), "SEED", 0d);
     }
-    
+
+    protected override string NodeUniqueName => "Noise";
+
     protected override Surface OnExecute(RenderingContext context)
     {
         if (Math.Abs(previousScale - Scale.Value) > 0.000001 || double.IsNaN(previousScale))

+ 2 - 0
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/OutputNode.cs

@@ -23,6 +23,8 @@ public class OutputNode : Node, IBackgroundInput
         return new OutputNode();
     }
 
+    protected override string NodeUniqueName => "Output";
+
     protected override Surface? OnExecute(RenderingContext context)
     {
         return Input.Value;

+ 8 - 8
src/PixiEditor.ChangeableDocument/Changes/Structure/CreateStructureMember_Change.cs

@@ -13,21 +13,21 @@ internal class CreateStructureMember_Change : Change
 {
     private Guid newMemberGuid;
 
-    private Guid parentFolderGuid;
+    private Guid parentGuid;
     private StructureMemberType type;
 
     [GenerateMakeChangeAction]
-    public CreateStructureMember_Change(Guid parentFolder, Guid newGuid,
+    public CreateStructureMember_Change(Guid parent, Guid newGuid,
         StructureMemberType type)
     {
-        this.parentFolderGuid = parentFolder;
+        this.parentGuid = parent;
         this.type = type;
         newMemberGuid = newGuid;
     }
 
     public override bool InitializeAndValidate(Document target)
     {
-        return target.TryFindNode<Node>(parentFolderGuid, out var targetNode) && targetNode is IBackgroundInput;
+        return target.TryFindNode<Node>(parentGuid, out var targetNode) && targetNode is IBackgroundInput;
     }
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document document, bool firstApply,
@@ -41,7 +41,7 @@ internal class CreateStructureMember_Change : Change
             _ => throw new NotSupportedException(),
         };
 
-        document.TryFindNode<Node>(parentFolderGuid, out var parentNode);
+        document.TryFindNode<Node>(parentGuid, out var parentNode);
 
         List<IChangeInfo> changes = new() { CreateChangeInfo(member) };
 
@@ -70,9 +70,9 @@ internal class CreateStructureMember_Change : Change
     {
         return type switch
         {
-            StructureMemberType.Layer => CreateLayer_ChangeInfo.FromLayer(parentFolderGuid,
+            StructureMemberType.Layer => CreateLayer_ChangeInfo.FromLayer(parentGuid,
                 (LayerNode)member),
-            StructureMemberType.Folder => CreateFolder_ChangeInfo.FromFolder(parentFolderGuid,
+            StructureMemberType.Folder => CreateFolder_ChangeInfo.FromFolder(parentGuid,
                 (FolderNode)member),
             _ => throw new NotSupportedException(),
         };
@@ -80,7 +80,7 @@ internal class CreateStructureMember_Change : Change
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document document)
     {
-        var container = document.FindNodeOrThrow<Node>(parentFolderGuid);
+        var container = document.FindNodeOrThrow<Node>(parentGuid);
         if (container is not IBackgroundInput backgroundInput)
         {
             throw new InvalidOperationException("Parent folder is not a valid container.");