Browse Source

Fixed animation

flabbet 1 year ago
parent
commit
8f03fd8958

+ 2 - 2
src/PixiEditor.AvaloniaUI/Models/Rendering/AffectedAreasGatherer.cs

@@ -255,10 +255,10 @@ internal class AffectedAreasGatherer
     private void AddWholeCanvasToImagePreviews(Guid memberGuid, bool ignoreSelf = false)
     {
         var path = tracker.Document.FindMemberPath(memberGuid);
-        if (path.Count < 2)
+        if (path.Count < 1 || path.Count == 1 && ignoreSelf)
             return;
         // skip root folder
-        for (int i = ignoreSelf ? 1 : 0; i < path.Count - 1; i++)
+        for (int i = ignoreSelf ? 1 : 0; i < path.Count; i++)
         {
             var member = path[i];
             if (!ImagePreviewAreas.ContainsKey(member.Id))

+ 4 - 4
src/PixiEditor.AvaloniaUI/ViewModels/Document/DocumentViewModel.cs

@@ -419,13 +419,13 @@ internal partial class DocumentViewModel : PixiObservableObject, IDocument
                 for (int j = 0; j < sizeInChunks.Y; j++)
                 {
                     // TODO: Implement this
-                    /*var maybeChunk = ChunkRenderer.MergeWholeStructure(new(i, j), ChunkResolution.Full,
-                        Internals.Tracker.Document.StructureRoot, frame);
+                    var maybeChunk = Renderer.RenderChunk(new(i, j), ChunkResolution.Full, frame);
                     if (maybeChunk.IsT1)
                         continue;
                     using Chunk chunk = maybeChunk.AsT0;
-                    finalSurface.DrawingSurface.Canvas.DrawSurface(chunk.Surface.DrawingSurface,
-                        i * ChunkyImage.FullChunkSize, j * ChunkyImage.FullChunkSize);*/
+                    finalSurface.DrawingSurface.Canvas.DrawSurface(
+                        chunk.Surface.DrawingSurface,
+                        i * ChunkyImage.FullChunkSize, j * ChunkyImage.FullChunkSize);
                 }
             }
 

+ 2 - 1
src/PixiEditor.AvaloniaUI/ViewModels/SubViewModels/AnimationsViewModel.cs

@@ -34,7 +34,8 @@ internal class AnimationsViewModel : SubViewModel<ViewModelMain>
         activeDocument.AnimationDataViewModel.CreateRasterKeyFrame(
             activeDocument.SelectedStructureMember.Id,
             newFrame,
-            toCloneFrom);
+            toCloneFrom, 
+            frameToCopyFrom);
         
         activeDocument.Operations.SetActiveFrame(newFrame);
     }

+ 1 - 1
src/PixiEditor.AvaloniaUI/Views/Animations/Timeline.cs

@@ -188,7 +188,7 @@ internal class Timeline : TemplatedControl, INotifyPropertyChanged
 
     public bool DragAllSelectedKeyFrames(int delta)
     {
-        bool canDrag = SelectedKeyFrames.All(x => x.StartFrameBindable + delta >= 0);
+        bool canDrag = SelectedKeyFrames.All(x => x.StartFrameBindable + delta > 0);
         if (!canDrag)
         {
             return false;

+ 2 - 2
src/PixiEditor.AvaloniaUI/Views/Dialogs/ExportFilePopup.axaml.cs

@@ -110,7 +110,7 @@ internal partial class ExportFilePopup : PixiEditorPopup
         set => SetValue(SaveFormatProperty, value);
     }
 
-    public Surface ExportPreview
+    public Surface? ExportPreview
     {
         get => GetValue(ExportPreviewProperty);
         set => SetValue(ExportPreviewProperty, value);
@@ -141,7 +141,7 @@ internal partial class ExportFilePopup : PixiEditorPopup
     public string SizeHint => new LocalizedString("EXPORT_SIZE_HINT", GetBestPercentage());
 
     private DocumentViewModel document;
-    private Image[] videoPreviewFrames = [];
+    private Image[]? videoPreviewFrames = [];
     private DispatcherTimer videoPreviewTimer = new DispatcherTimer();
     private int activeFrame = 0;
     private CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();

+ 3 - 3
src/PixiEditor.ChangeableDocument/Changeables/Animations/AnimationData.cs

@@ -9,7 +9,7 @@ internal class AnimationData : IReadOnlyAnimationData
 
     private List<KeyFrame> keyFrames = new List<KeyFrame>();
     private readonly Document document;
-    
+
     public AnimationData(Document document)
     {
         this.document = document;
@@ -39,7 +39,7 @@ internal class AnimationData : IReadOnlyAnimationData
             {
                 node.RemoveKeyFrame(frame.Id);
             }
-            
+
             parent?.Children.Remove(frame);
         });
     }
@@ -48,7 +48,7 @@ internal class AnimationData : IReadOnlyAnimationData
     {
         return TryFindKeyFrameCallback(id, out keyFrame, null);
     }
-    
+
     private bool TryFindKeyFrameCallback<T>(Guid id, out T? foundKeyFrame,
         Action<KeyFrame, GroupKeyFrame?> onFound = null) where T : IReadOnlyKeyFrame
     {

+ 43 - 0
src/PixiEditor.ChangeableDocument/Changeables/Animations/KeyFrameData.cs

@@ -0,0 +1,43 @@
+namespace PixiEditor.ChangeableDocument.Changeables.Animations;
+
+public abstract class KeyFrameData : IDisposable
+{
+    public int StartFrame { get; set; }
+    public int Duration { get; set; }
+    public Guid KeyFrameGuid { get; }
+
+    public abstract bool RequiresUpdate { get; set; }
+
+    public KeyFrameData(Guid keyFrameGuid, int startFrame, int duration)
+    {
+        KeyFrameGuid = keyFrameGuid;
+        StartFrame = startFrame;
+        Duration = duration;
+    }
+
+    public bool IsInFrame(int frame)
+    {
+        return frame >= StartFrame && frame <= StartFrame + Duration;
+    }
+
+    public abstract void Dispose();
+}
+
+public abstract class KeyFrameData<T> : KeyFrameData
+{
+    public T Data { get; set; }
+
+    public KeyFrameData(Guid keyFrameGuid, T data, int startFrame, int duration) : base(keyFrameGuid, startFrame,
+        duration)
+    {
+        Data = data;
+    }
+
+    public override void Dispose()
+    {
+        if (Data is IDisposable disposable)
+        {
+            disposable.Dispose();
+        } 
+    }
+}

+ 4 - 5
src/PixiEditor.ChangeableDocument/Changeables/Animations/RasterKeyFrame.cs

@@ -9,18 +9,17 @@ internal class RasterKeyFrame : KeyFrame, IReadOnlyRasterKeyFrame
 
     private ImageLayerNode targetNode;
     private ChunkyImage targetImage;
-    
-    public RasterKeyFrame(Guid id, ImageLayerNode node, int startFrame, Document document, ChunkyImage? cloneFrom = null)
+
+    public RasterKeyFrame(Guid id, ImageLayerNode node, int startFrame, Document document, ChunkyImage keyFrameImage)
         : base(node, startFrame)
     {
         Id = id;
         targetNode = node;
-        targetImage = cloneFrom?.CloneFromCommitted() ?? new ChunkyImage(document.Size);
-        node.AddFrame(Id, startFrame, 1, targetImage);
+        targetImage = keyFrameImage;
 
         Document = document;
     }
-    
+
     public override KeyFrame Clone()
     {
         var image = targetImage.CloneFromCommitted();

+ 63 - 115
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/ImageLayerNode.cs

@@ -13,16 +13,17 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
 {
     public InputProperty<bool> LockTransparency { get; }
 
-    private List<ImageFrame> frames = new List<ImageFrame>();
     private VecI size;
 
     private Paint blendPaint = new Paint();
     private Paint maskPaint = new Paint() { BlendMode = DrawingApi.Core.Surface.BlendMode.DstIn };
+    private static readonly Paint clearPaint = new() { BlendMode = DrawingApi.Core.Surface.BlendMode.Src, 
+        Color = PixiEditor.DrawingApi.Core.ColorsImpl.Colors.Transparent };
 
     private Dictionary<ChunkResolution, Surface> workingSurfaces = new Dictionary<ChunkResolution, Surface>();
 
     // Handled by overriden CacheChanged
-    protected override bool AffectedByAnimation => false;
+    protected override bool AffectedByAnimation => true;
 
     protected override bool AffectedByChunkResolution => true;
 
@@ -31,7 +32,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
     public ImageLayerNode(VecI size)
     {
         LockTransparency = CreateInput<bool>("LockTransparency", "LOCK_TRANSPARENCY", false);
-        frames.Add(new ImageFrame(Guid.NewGuid(), 0, 0, new(size)));
+        keyFrames.Add(new ImageFrame(Guid.NewGuid(), 0, 0, new(size)));
         this.size = size;
     }
 
@@ -53,7 +54,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
             return Output.Value;
         }
 
-        var frameImage = GetFrameImage(context.FrameTime).Image;
+        var frameImage = GetFrameImage(context.FrameTime).Data;
 
         blendPaint.Color = new Color(255, 255, 255, (byte)Math.Round(Opacity.Value * 255));
         blendPaint.BlendMode = DrawingApi.Core.Surface.BlendMode.Src;
@@ -84,29 +85,29 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
                 DrawBackground(workingSurface, context);
                 blendPaint.BlendMode = RenderingContext.GetDrawingBlendMode(BlendMode.Value);
             }
-            
+
             DrawLayer(frameImage, context, workingSurface);
             Output.Value = workingSurface;
             return workingSurface;
         }
 
         DrawLayer(frameImage, context, workingSurface);
-        
+
         // shit gets downhill with mask on big canvases, TODO: optimize
         ApplyMaskIfPresent(workingSurface, context);
         ApplyRasterClip(workingSurface, context);
-            
+
         if (Background.Value != null)
         {
             Surface tempSurface = new Surface(workingSurface.Size);
             DrawBackground(tempSurface, context);
             blendPaint.BlendMode = RenderingContext.GetDrawingBlendMode(BlendMode.Value);
             tempSurface.DrawingSurface.Canvas.DrawSurface(workingSurface.DrawingSurface, 0, 0, blendPaint);
-            
+
             Output.Value = tempSurface;
             return tempSurface;
         }
-            
+
         Output.Value = workingSurface;
         return workingSurface;
     }
@@ -116,18 +117,21 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         RectI source = CalculateSourceRect(Background.Value, workingSurface.Size, context);
         RectI target = CalculateDestinationRect(context);
         using var snapshot = Background.Value.DrawingSurface.Snapshot(source);
-        
+
         workingSurface.DrawingSurface.Canvas.DrawImage(snapshot, target.X, target.Y, blendPaint);
     }
 
     private void DrawLayer(ChunkyImage frameImage, RenderingContext context, Surface workingSurface)
     {
-        frameImage.DrawMostUpToDateChunkOn(
-            context.ChunkToUpdate,
-            context.ChunkResolution,
-            workingSurface.DrawingSurface,
-            context.ChunkToUpdate * context.ChunkResolution.PixelSize(),
-            blendPaint);
+        if (!frameImage.DrawMostUpToDateChunkOn(
+                context.ChunkToUpdate,
+                context.ChunkResolution,
+                workingSurface.DrawingSurface,
+                context.ChunkToUpdate * context.ChunkResolution.PixelSize(),
+                blendPaint))
+        {
+            workingSurface.DrawingSurface.Canvas.DrawRect(CalculateDestinationRect(context), clearPaint);
+        }
     }
 
     private bool IsEmptyMask()
@@ -155,9 +159,14 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
 
     private ImageFrame GetFrameImage(KeyFrameTime frame)
     {
-        var imageFrame = frames.FirstOrDefault(x => x.IsInFrame(frame.Frame));
-        var frameImage = imageFrame ?? frames[0];
-        return frameImage;
+        var imageFrame = keyFrames.LastOrDefault(x => x.IsInFrame(frame.Frame));
+        if (imageFrame is not ImageFrame)
+        {
+            return keyFrames[0] as ImageFrame;
+        }
+        
+        var frameImage = imageFrame ?? keyFrames[0];
+        return frameImage as ImageFrame;
     }
 
     private void ApplyMaskIfPresent(Surface surface, RenderingContext context)
@@ -186,7 +195,7 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
 
         return new RectI(x, y, width, height);
     }
-    
+
     private RectI CalculateDestinationRect(RenderingContext context)
     {
         int chunkSize = context.ChunkResolution.PixelSize();
@@ -216,21 +225,10 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         }
     }
 
-    public override void Dispose()
-    {
-        base.Dispose();
-        foreach (var frame in frames)
-        {
-            frame.Image.Dispose();
-        }
-    }
-
     public override Node CreateCopy()
     {
         return new ImageLayerNode(size)
         {
-            frames = frames.Select(x =>
-                new ImageFrame(x.KeyFrameGuid, x.StartFrame, x.Duration, x.Image.CloneFromCommitted())).ToList(),
             MemberName = MemberName,
         };
     }
@@ -251,128 +249,78 @@ public class ImageLayerNode : LayerNode, IReadOnlyImageNode
         set => LockTransparency.NonOverridenValue = value;
     }
 
-    public void SetKeyFrameLength(Guid keyFrameGuid, int startFrame, int duration)
-    {
-        ImageFrame frame = frames.FirstOrDefault(x => x.KeyFrameGuid == keyFrameGuid);
-        if (frame is not null)
-        {
-            frame.StartFrame = startFrame;
-            frame.Duration = duration;
-        }
-    }
-
     public void ForEveryFrame(Action<ChunkyImage> action)
     {
-        foreach (var frame in frames)
+        foreach (var frame in keyFrames)
         {
-            action(frame.Image);
+            if (frame is ImageFrame imageFrame)
+            {
+                action(imageFrame.Data);
+            }
         }
     }
 
     public ChunkyImage GetLayerImageAtFrame(int frame)
     {
-        return frames.FirstOrDefault(x => x.IsInFrame(frame))?.Image ?? frames[0].Image;
+        return GetFrameImage(frame).Data;
     }
 
     public ChunkyImage GetLayerImageByKeyFrameGuid(Guid keyFrameGuid)
     {
-        return frames.FirstOrDefault(x => x.KeyFrameGuid == keyFrameGuid)?.Image ?? frames[0].Image;
-    }
-
-    public void SetLayerImageAtFrame(int frame, ChunkyImage newLayerImage)
-    {
-        ImageFrame existingFrame = frames.FirstOrDefault(x => x.IsInFrame(frame));
-        if (existingFrame is not null)
+        foreach (var keyFrame in keyFrames)
         {
-            existingFrame.Image.Dispose();
-            existingFrame.Image = newLayerImage;
-        }
-    }
-    /*
-          /// <summary>
-        /// Creates a clone of the layer, its image and its mask
-        /// </summary>
-        internal override RasterLayer Clone()
-        {
-            List<ImageFrame> clonedFrames = new();
-            foreach (var frame in frameImages)
+            if (keyFrame.KeyFrameGuid == keyFrameGuid)
             {
-                clonedFrames.Add(new ImageFrame(frame.KeyFrameGuid, frame.StartFrame, frame.Duration,
-                    frame.Image.CloneFromCommitted()));
+                return (keyFrame as ImageFrame).Data;
             }
-
-            return new RasterLayer(clonedFrames)
-            {
-                GuidValue = GuidValue,
-                IsVisible = IsVisible,
-                Name = Name,
-                Opacity = Opacity,
-                Mask = Mask?.CloneFromCommitted(),
-                ClipToMemberBelow = ClipToMemberBelow,
-                MaskIsVisible = MaskIsVisible,
-                BlendMode = BlendMode,
-                LockTransparency = LockTransparency
-            };
-        }
-        public override void RemoveKeyFrame(Guid keyFrameGuid)
-        {
-            // Remove all in case I'm the lucky winner of guid collision
-            frameImages.RemoveAll(x => x.KeyFrameGuid == keyFrameGuid);
         }
 
-        public void SetLayerImageAtFrame(int frame, ChunkyImage newLayerImage)
+        return (keyFrames[0] as ImageFrame).Data;        
+    }
+
+    public void SetLayerImageAtFrame(int frame, ChunkyImage newLayerImage)
+    {
+        var existingFrame = keyFrames.FirstOrDefault(x => x.IsInFrame(frame));
+        if (existingFrame is not null && existingFrame is ImageFrame imgFrame)
         {
-            ImageFrame existingFrame = frameImages.FirstOrDefault(x => x.IsInFrame(frame));
-            if (existingFrame is not null)
-            {
-                existingFrame.Image.Dispose();
-                existingFrame.Image = newLayerImage;
-            }
+            existingFrame.Dispose();
+            imgFrame.Data = newLayerImage;
         }
+    }
 
-
-        public void AddFrame(Guid keyFrameGuid, int startFrame, int duration, ChunkyImage frameImg)
+    public override void Dispose()
+    {
+        base.Dispose();
+        blendPaint.Dispose();
+        maskPaint.Dispose();
+        clearPaint.Dispose();
+        foreach (var surface in workingSurfaces.Values)
         {
-            ImageFrame newFrame = new(keyFrameGuid, startFrame, duration, frameImg);
-            frames.Add(newFrame);
+            surface.Dispose();
         }
-        */
+    }
 }
 
-class ImageFrame
+class ImageFrame : KeyFrameData<ChunkyImage>
 {
-    public int StartFrame { get; set; }
-    public int Duration { get; set; }
-    public ChunkyImage Image { get; set; }
-
     private int lastCommitCounter = 0;
 
-    public bool RequiresUpdate
+    public override bool RequiresUpdate
     {
         get
         {
-            return Image.QueueLength != lastQueueLength || Image.CommitCounter != lastCommitCounter;
+            return Data.QueueLength != lastQueueLength || Data.CommitCounter != lastCommitCounter;
         }
         set
         {
-            lastQueueLength = Image.QueueLength;
-            lastCommitCounter = Image.CommitCounter;
+            lastQueueLength = Data.QueueLength;
+            lastCommitCounter = Data.CommitCounter;
         }
     }
 
-    public Guid KeyFrameGuid { get; set; }
     private int lastQueueLength = 0;
 
-    public ImageFrame(Guid keyFrameGuid, int startFrame, int duration, ChunkyImage image)
-    {
-        StartFrame = startFrame;
-        Duration = duration;
-        Image = image;
-        KeyFrameGuid = keyFrameGuid;
-    }
-
-    public bool IsInFrame(int frame)
+    public ImageFrame(Guid keyFrameGuid, int startFrame, int duration, ChunkyImage image) : base(keyFrameGuid, image, startFrame, duration)
     {
-        return frame >= StartFrame && frame < StartFrame + Duration;
     }
 }

+ 41 - 15
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Node.cs

@@ -13,6 +13,7 @@ public abstract class Node : IReadOnlyNode, IDisposable
 {
     private List<InputProperty> inputs = new();
     private List<OutputProperty> outputs = new();
+    protected List<KeyFrameData> keyFrames = new();
 
     public Guid Id { get; internal set; } = Guid.NewGuid();
 
@@ -40,6 +41,7 @@ public abstract class Node : IReadOnlyNode, IDisposable
     private KeyFrameTime _lastFrameTime = new KeyFrameTime(-1);
     private ChunkResolution? _lastResolution;
     private VecI? _lastChunkPos;
+    private bool _keyFramesDirty;
 
     public Surface? Execute(RenderingContext context)
     {
@@ -69,6 +71,7 @@ public abstract class Node : IReadOnlyNode, IDisposable
     protected virtual bool CacheChanged(RenderingContext context)
     {
         return (!context.FrameTime.Equals(_lastFrameTime) && AffectedByAnimation)
+               || (AffectedByAnimation && _keyFramesDirty)
                || (context.ChunkResolution != _lastResolution && AffectedByChunkResolution)
                || (context.ChunkToUpdate != _lastChunkPos && AffectedByChunkToUpdate)
                || inputs.Any(x => x.CacheChanged);
@@ -84,21 +87,7 @@ public abstract class Node : IReadOnlyNode, IDisposable
         _lastFrameTime = context.FrameTime;
         _lastResolution = context.ChunkResolution;
         _lastChunkPos = context.ChunkToUpdate;
-    }
-
-    public void RemoveKeyFrame(Guid keyFrameGuid)
-    {
-        // TODO: Implement
-    }
-
-    public void SetKeyFrameLength(Guid keyFrameGuid, int startFrame, int duration)
-    {
-        // TODO: Implement
-    }
-
-    public void AddFrame<T>(Guid keyFrameGuid, int startFrame, int duration, T value)
-    {
-        // TODO: Implement
+        _keyFramesDirty = false;
     }
 
     public void TraverseBackwards(Func<IReadOnlyNode, bool> action)
@@ -164,6 +153,34 @@ public abstract class Node : IReadOnlyNode, IDisposable
         }
     }
 
+    public void RemoveKeyFrame(Guid keyFrameId)
+    {
+        keyFrames.RemoveAll(x => x.KeyFrameGuid == keyFrameId);
+        _keyFramesDirty = true;
+    }
+
+    public void SetKeyFrameLength(Guid id, int startFrame, int duration)
+    {
+        KeyFrameData frame = keyFrames.FirstOrDefault(x => x.KeyFrameGuid == id);
+        if (frame is not null)
+        {
+            frame.StartFrame = startFrame;
+            frame.Duration = duration;
+            _keyFramesDirty = true;
+        }
+    }
+
+    public void AddFrame<T>(Guid id, T value) where T : KeyFrameData
+    {
+        if (keyFrames.Any(x => x.KeyFrameGuid == id))
+        {
+            throw new InvalidOperationException("Key frame with this id already exists.");
+        }
+        
+        keyFrames.Add(value);
+        _keyFramesDirty = true;
+    }
+
     protected FieldInputProperty<T> CreateFieldInput<T>(string propName, string displayName, T defaultValue)
     {
         var property = new FieldInputProperty<T>(this, propName, displayName, defaultValue);
@@ -220,6 +237,14 @@ public abstract class Node : IReadOnlyNode, IDisposable
                 disposable.Dispose();
             }
         }
+        
+        if(keyFrames is not null)
+        {
+            foreach (var keyFrame in keyFrames)
+            {
+               keyFrame.Dispose(); 
+            }
+        }
     }
 
     public abstract Node CreateCopy();
@@ -230,6 +255,7 @@ public abstract class Node : IReadOnlyNode, IDisposable
         clone.Id = Guid.NewGuid();
         clone.inputs = new List<InputProperty>();
         clone.outputs = new List<OutputProperty>();
+        clone.keyFrames = new List<KeyFrameData>();
         foreach (var input in inputs)
         {
             var newInput = input.Clone(clone);

+ 5 - 1
src/PixiEditor.ChangeableDocument/Changes/Animation/CreateRasterKeyFrame_Change.cs

@@ -39,8 +39,12 @@ internal class CreateRasterKeyFrame_Change : Change
         
         ImageLayerNode targetNode = target.FindMemberOrThrow<ImageLayerNode>(_targetLayerGuid);
         
+        ChunkyImage img = cloneFromImage?.CloneFromCommitted() ?? new ChunkyImage(target.Size);
+        
         var keyFrame =
-            new RasterKeyFrame(createdKeyFrameId, targetNode, _frame, target, cloneFromImage);
+            new RasterKeyFrame(createdKeyFrameId, targetNode, _frame, target, img);
+        
+        targetNode.AddFrame(createdKeyFrameId, new ImageFrame(createdKeyFrameId, _frame, 1, img));
         target.AnimationData.AddKeyFrame(keyFrame);
         ignoreInUndo = false;
         return new CreateRasterKeyFrame_ChangeInfo(_targetLayerGuid, _frame, createdKeyFrameId, cloneFrom.HasValue);

+ 1 - 1
src/PixiEditor.ChangeableDocument/Changes/Animation/KeyFramesStartPos_UpdateableChange.cs

@@ -14,7 +14,7 @@ internal class KeyFramesStartPos_UpdateableChange : UpdateableChange
     public KeyFramesStartPos_UpdateableChange(List<Guid> keyFramesGuid, int delta)
     {
         KeyFramesGuid = keyFramesGuid.ToArray();
-        Delta = 0;
+        Delta = delta;
     }
 
     [UpdateChangeMethod]