Browse Source

Merging layers merges animation

flabbet 9 months ago
parent
commit
25d1422a07

+ 4 - 2
src/PixiEditor.ChangeableDocument/Changeables/Animations/KeyFrameData.cs

@@ -57,14 +57,16 @@ public class KeyFrameData : IDisposable, IReadOnlyKeyFrameData
         }
     }
 
-    public KeyFrameData Clone()
+    public KeyFrameData Clone(bool newGuid = false)
     {
         if (Data is not ICloneable && !Data.GetType().IsValueType && Data is not string)
         {
             throw new InvalidOperationException("Data must be ICloneable, ValueType or string to be cloned");
         }
         
-        return new KeyFrameData(KeyFrameGuid, StartFrame, Duration, AffectedElement)
+        Guid newGuidValue = newGuid ? Guid.NewGuid() : KeyFrameGuid;
+        
+        return new KeyFrameData(newGuidValue, StartFrame, Duration, AffectedElement)
         {
             Data = Data is ICloneable cloneable ? cloneable.Clone() : Data,
             IsVisible = IsVisible

+ 124 - 40
src/PixiEditor.ChangeableDocument/Changes/Drawing/CombineStructureMembersOnto_Change.cs

@@ -6,6 +6,8 @@ using Drawie.Backend.Core;
 using Drawie.Backend.Core.Bridge;
 using Drawie.Backend.Core.Numerics;
 using Drawie.Numerics;
+using PixiEditor.ChangeableDocument.Changeables.Animations;
+using PixiEditor.ChangeableDocument.ChangeInfos.Animation;
 
 namespace PixiEditor.ChangeableDocument.Changes.Drawing;
 
@@ -14,23 +16,21 @@ internal class CombineStructureMembersOnto_Change : Change
     private HashSet<Guid> membersToMerge;
 
     private HashSet<Guid> layersToCombine = new();
-    private int frame;
 
-    private Guid targetLayer;
-    private CommittedChunkStorage? originalChunks;
+    private Guid targetLayerGuid;
+    private Dictionary<int, CommittedChunkStorage> originalChunks = new();
     
 
     [GenerateMakeChangeAction]
-    public CombineStructureMembersOnto_Change(HashSet<Guid> membersToMerge, Guid targetLayer, int frame)
+    public CombineStructureMembersOnto_Change(HashSet<Guid> membersToMerge, Guid targetLayer)
     {
         this.membersToMerge = new HashSet<Guid>(membersToMerge);
-        this.targetLayer = targetLayer;
-        this.frame = frame;
+        this.targetLayerGuid = targetLayer;
     }
 
     public override bool InitializeAndValidate(Document target)
     {
-        if (!target.HasMember(targetLayer) || membersToMerge.Count == 0)
+        if (!target.HasMember(targetLayerGuid) || membersToMerge.Count == 0)
             return false;
         foreach (Guid guid in membersToMerge)
         {
@@ -66,14 +66,34 @@ internal class CombineStructureMembersOnto_Change : Change
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target, bool firstApply,
         out bool ignoreInUndo)
     {
+        List<IChangeInfo> changes = new();
+        var targetLayer = target.FindMemberOrThrow<LayerNode>(targetLayerGuid);
+
         // TODO: add merging similar layers (vector -> vector)
-        var toDrawOn = target.FindMemberOrThrow<LayerNode>(targetLayer);
+        int maxFrame = GetMaxFrame(target, targetLayer);
+
+        for (int frame = 0; frame < maxFrame || frame == 0; frame++)
+        {
+            changes.AddRange(ApplyToFrame(target, targetLayer, frame));
+        }
 
+        ignoreInUndo = false;
+
+        return changes;
+    }
+
+    private List<IChangeInfo> ApplyToFrame(Document target, LayerNode targetLayer, int frame)
+    {
         var chunksToCombine = new HashSet<VecI>();
+        List<IChangeInfo> changes = new();
+
         foreach (var guid in layersToCombine)
         {
             var layer = target.FindMemberOrThrow<LayerNode>(guid);
-            if(layer is not IRasterizable or ImageLayerNode)
+
+            AddMissingKeyFrame(targetLayer, frame, layer, changes, target);
+            
+            if (layer is not IRasterizable or ImageLayerNode)
                 continue;
 
             if (layer is ImageLayerNode imageLayerNode)
@@ -83,53 +103,91 @@ internal class CombineStructureMembersOnto_Change : Change
             }
             else
             {
-                AddChunksByTightBounds(layer, chunksToCombine);
+                AddChunksByTightBounds(layer, chunksToCombine, frame);
             }
         }
-        
-        List<IChangeInfo> changes = new();
-        
-        var toDrawOnImage = ((ImageLayerNode)toDrawOn).GetLayerImageAtFrame(frame);
+
+        var toDrawOnImage = ((ImageLayerNode)targetLayer).GetLayerImageAtFrame(frame);
         toDrawOnImage.EnqueueClear();
 
         Texture tempTexture = new Texture(target.Size);
-        
+
         DocumentRenderer renderer = new(target);
 
         AffectedArea affArea = new();
         DrawingBackendApi.Current.RenderingDispatcher.Invoke(() =>
         {
-            RectI? globalClippingRect = new RectI(0, 0, target.Size.X, target.Size.Y);
-            /*foreach (var chunk in chunksToCombine)
+            if (frame == 0)
             {
-                OneOf<Chunk, EmptyChunk> combined =
-                    renderer.RenderLayersChunk(chunk, ChunkResolution.Full, frame, layersToCombine, globalClippingRect);
-                if (combined.IsT0)
+                renderer.RenderLayers(tempTexture.DrawingSurface, layersToCombine, frame, ChunkResolution.Full);
+            }
+            else
+            {
+                HashSet<Guid> layersToRender = new();
+                foreach (var layer in layersToCombine)
                 {
-                    toDrawOnImage.EnqueueDrawImage(chunk * ChunkyImage.FullChunkSize, combined.AsT0.Surface);
-                    combined.AsT0.Dispose();
+                    if (target.FindMember(layer) is LayerNode node)
+                    {
+                        if (node.KeyFrames.Any(x => x.IsInFrame(frame)))
+                        {
+                            layersToRender.Add(layer);
+                        }
+                    }
                 }
-            }*/
-            
-            renderer.RenderLayers(tempTexture.DrawingSurface, layersToCombine, frame, ChunkResolution.Full);
-            
+                
+                renderer.RenderLayers(tempTexture.DrawingSurface, layersToRender, frame, ChunkResolution.Full);
+            }
+
             toDrawOnImage.EnqueueDrawTexture(VecI.Zero, tempTexture);
 
             affArea = toDrawOnImage.FindAffectedArea();
-            originalChunks = new CommittedChunkStorage(toDrawOnImage, affArea.Chunks);
+            originalChunks.Add(frame, new CommittedChunkStorage(toDrawOnImage, affArea.Chunks));
             toDrawOnImage.CommitChanges();
-            
+
             tempTexture.Dispose();
         });
 
+        changes.Add(new LayerImageArea_ChangeInfo(targetLayerGuid, affArea));
+        return changes;
+    }
 
-        ignoreInUndo = false;
+    private void AddMissingKeyFrame(LayerNode targetLayer, int frame, LayerNode layer, List<IChangeInfo> changes,
+        Document target)
+    {
+        bool hasKeyframe = targetLayer.KeyFrames.Any(x => x.IsInFrame(frame));
+        if (hasKeyframe)
+            return;
+
+        if (layer is not ImageLayerNode)
+            return;
+
+        var keyFrameData = layer.KeyFrames.FirstOrDefault(x => x.IsInFrame(frame));
+        if (keyFrameData is null)
+            return;
+
+        var clonedData = keyFrameData.Clone(true);
         
-        changes.Add(new LayerImageArea_ChangeInfo(targetLayer, affArea));
-        return changes;
+        targetLayer.AddFrame(keyFrameData.KeyFrameGuid, clonedData);
+        
+        changes.Add(new CreateRasterKeyFrame_ChangeInfo(targetLayerGuid, frame, clonedData.KeyFrameGuid, true));
+        changes.Add(new KeyFrameLength_ChangeInfo(targetLayerGuid, clonedData.StartFrame, clonedData.Duration));
+
+        target.AnimationData.AddKeyFrame(new RasterKeyFrame(clonedData.KeyFrameGuid, targetLayerGuid, frame, target));
+    }
+
+    private int GetMaxFrame(Document target, LayerNode targetLayer)
+    {
+        int maxFrame = targetLayer.KeyFrames.Max(x => x.StartFrame + x.Duration);
+        foreach (var toMerge in membersToMerge)
+        {
+            var member = target.FindMemberOrThrow<LayerNode>(toMerge);
+            maxFrame = Math.Max(maxFrame, member.KeyFrames.Max(x => x.StartFrame + x.Duration));
+        }
+
+        return maxFrame;
     }
 
-    private void AddChunksByTightBounds(LayerNode layer, HashSet<VecI> chunksToCombine)
+    private void AddChunksByTightBounds(LayerNode layer, HashSet<VecI> chunksToCombine, int frame)
     {
         var tightBounds = layer.GetTightBounds(frame);
         if (tightBounds.HasValue)
@@ -149,19 +207,45 @@ internal class CombineStructureMembersOnto_Change : Change
 
     public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
     {
-        var toDrawOn = target.FindMemberOrThrow<ImageLayerNode>(targetLayer);
-        var affectedArea =
-            DrawingChangeHelper.ApplyStoredChunksDisposeAndSetToNull(toDrawOn.GetLayerImageAtFrame(frame),
-                ref originalChunks);
-        
+        var toDrawOn = target.FindMemberOrThrow<ImageLayerNode>(targetLayerGuid);
+
         List<IChangeInfo> changes = new();
-        changes.Add(new LayerImageArea_ChangeInfo(targetLayer, affectedArea));
 
-        return changes; 
+        int maxFrame = GetMaxFrame(target, toDrawOn);
+
+        for (int frame = 0; frame < maxFrame || frame == 0; frame++)
+        {
+            changes.Add(RevertFrame(toDrawOn, frame));
+        }
+        
+        target.AnimationData.RemoveKeyFrame(targetLayerGuid);
+        originalChunks.Clear();
+        changes.Add(new DeleteKeyFrame_ChangeInfo(targetLayerGuid));
+
+        return changes;
+    }
+
+    private IChangeInfo RevertFrame(ImageLayerNode targetLayer, int frame)
+    {
+        var toDrawOnImage = targetLayer.GetLayerImageAtFrame(frame);
+        toDrawOnImage.EnqueueClear();
+
+        CommittedChunkStorage? storedChunks = originalChunks[frame];
+
+        var affectedArea =
+            DrawingChangeHelper.ApplyStoredChunksDisposeAndSetToNull(
+                targetLayer.GetLayerImageAtFrame(frame),
+                ref storedChunks);
+        
+        toDrawOnImage.CommitChanges();
+        return new LayerImageArea_ChangeInfo(targetLayerGuid, affectedArea);
     }
 
     public override void Dispose()
     {
-        originalChunks?.Dispose();
+        foreach (var originalChunk in originalChunks)
+        {
+            originalChunk.Value.Dispose();
+        }
     }
 }

+ 1 - 2
src/PixiEditor/Models/DocumentModels/Public/DocumentOperationsModule.cs

@@ -518,8 +518,7 @@ internal class DocumentOperationsModule : IDocumentOperations
         Internals.ActionAccumulator.AddActions(
             new CreateStructureMember_Action(parent.Id, newGuid, typeof(ImageLayerNode)),
             new StructureMemberName_Action(newGuid, node.NodeNameBindable),
-            new CombineStructureMembersOnto_Action(members.ToHashSet(), newGuid,
-                Document.AnimationHandler.ActiveFrameBindable));
+            new CombineStructureMembersOnto_Action(members.ToHashSet(), newGuid));
         foreach (var member in members)
             Internals.ActionAccumulator.AddActions(new DeleteStructureMember_Action(member));
         Internals.ActionAccumulator.AddActions(new ChangeBoundary_Action());

+ 1 - 1
src/PixiEditor/Models/Rendering/SceneRenderer.cs

@@ -51,7 +51,7 @@ internal class SceneRenderer
             texture.Dispose();
         }
     }
-
+    
     private void RenderOnionSkin(DrawingSurface target, ChunkResolution resolution)
     {
         var animationData = Document.AnimationData;