123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 |
- using System.Collections.ObjectModel;
- using System.Collections.Specialized;
- using CommunityToolkit.Mvvm.ComponentModel;
- using PixiEditor.AnimationRenderer.Core;
- using PixiEditor.ChangeableDocument.Actions.Generated;
- using PixiEditor.ChangeableDocument.Changeables.Animations;
- using PixiEditor.ChangeableDocument.Changeables.Interfaces;
- using PixiEditor.Models.DocumentModels;
- using PixiEditor.Models.DocumentPassthroughActions;
- using PixiEditor.Models.Handlers;
- namespace PixiEditor.ViewModels.Document;
- internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
- {
- private int _activeFrameBindable = 1;
- private int frameRateBindable = 60;
- private int onionFrames = 1;
- private double onionOpacity = 50;
- public DocumentViewModel Document { get; }
- protected DocumentInternalParts Internals { get; }
- public IReadOnlyCollection<ICelHandler> KeyFrames => keyFrames;
- public IReadOnlyCollection<ICelHandler> AllCels => allCels;
- public event Action<int, int> ActiveFrameChanged;
- private KeyFrameCollection keyFrames = new KeyFrameCollection();
- private List<ICelHandler> allCels = new List<ICelHandler>();
- private bool onionSkinningEnabled;
- private bool isPlayingBindable;
- private int? cachedFirstFrame;
- private int? cachedLastFrame;
- public int ActiveFrameBindable
- {
- get => _activeFrameBindable;
- set
- {
- if (Document.BlockingUpdateableChangeActive)
- return;
- Internals.ActionAccumulator.AddActions(new SetActiveFrame_PassthroughAction(value));
- }
- }
- public IAnimationRenderer Renderer { get; set; }
- public int FrameRateBindable
- {
- get => frameRateBindable;
- set
- {
- if (Document.BlockingUpdateableChangeActive)
- return;
- Internals.ActionAccumulator.AddFinishedActions(new SetFrameRate_Action(value));
- }
- }
- public bool OnionSkinningEnabledBindable
- {
- get => onionSkinningEnabled;
- set
- {
- if (Document.BlockingUpdateableChangeActive)
- return;
- Internals.ActionAccumulator.AddFinishedActions(new ToggleOnionSkinning_PassthroughAction(value));
- }
- }
- public int OnionFramesBindable
- {
- get => onionFrames;
- set
- {
- if (Document.BlockingUpdateableChangeActive)
- return;
- Internals.ActionAccumulator.AddFinishedActions(new SetOnionSettings_Action(value, OnionOpacityBindable));
- }
- }
- public double OnionOpacityBindable
- {
- get => onionOpacity;
- set
- {
- if (Document.BlockingUpdateableChangeActive)
- return;
- Internals.ActionAccumulator.AddFinishedActions(new SetOnionSettings_Action(OnionFramesBindable, value));
- }
- }
- public bool IsPlayingBindable
- {
- get => isPlayingBindable;
- set
- {
- if (Document.BlockingUpdateableChangeActive)
- return;
- Internals.ActionAccumulator.AddFinishedActions(new SetPlayingState_PassthroughAction(value));
- }
- }
- public int FirstFrame => cachedFirstFrame ??= keyFrames.Count > 0 ? keyFrames.Min(x => x.StartFrameBindable) : 0;
- public int LastFrame => cachedLastFrame ??= keyFrames.Count > 0
- ? keyFrames.Max(x => x.StartFrameBindable + x.DurationBindable)
- : DefaultEndFrame;
- public int FramesCount => LastFrame - FirstFrame;
- private double ActiveNormalizedTime => (double)(ActiveFrameBindable - FirstFrame) / FramesCount;
- private int DefaultEndFrame => FrameRateBindable; // 1 second
- public AnimationDataViewModel(DocumentViewModel document, DocumentInternalParts internals)
- {
- Document = document;
- Internals = internals;
- Document.LayersChanged += (sender, args) => SortByLayers();
- }
- public KeyFrameTime ActiveFrameTime => new KeyFrameTime(ActiveFrameBindable, ActiveNormalizedTime);
- public void CreateCel(Guid targetLayerGuid, int frame, Guid? toCloneFrom = null,
- int? frameToCopyFrom = null)
- {
- if (!Document.BlockingUpdateableChangeActive)
- {
- Internals.ActionAccumulator.AddFinishedActions(new CreateCel_Action(targetLayerGuid,
- Guid.NewGuid(), Math.Max(1, frame),
- frameToCopyFrom ?? -1, toCloneFrom ?? Guid.Empty));
- }
- }
- public void DeleteCels(List<Guid> keyFrameIds)
- {
- if (!Document.BlockingUpdateableChangeActive)
- {
- for (var i = 0; i < keyFrameIds.Count; i++)
- {
- var id = keyFrameIds[i];
- if (i == keyFrameIds.Count - 1)
- {
- Internals.ActionAccumulator.AddFinishedActions(new DeleteKeyFrame_Action(id));
- }
- else
- {
- Internals.ActionAccumulator.AddActions(new DeleteKeyFrame_Action(id));
- }
- }
- }
- }
- public void ChangeKeyFramesStartPos(Guid[] infoIds, int infoDelta)
- {
- if (!Document.BlockingUpdateableChangeActive)
- {
- Internals.ActionAccumulator.AddActions(new KeyFramesStartPos_Action(infoIds.ToList(), infoDelta));
- }
- }
- public void ToggleOnionSkinning(bool value)
- {
- if (!Document.BlockingUpdateableChangeActive)
- {
- Internals.ActionAccumulator.AddFinishedActions(new ToggleOnionSkinning_PassthroughAction(value));
- }
- }
- public void EndKeyFramesStartPos()
- {
- if (!Document.BlockingUpdateableChangeActive)
- {
- Internals.ActionAccumulator.AddFinishedActions(new EndKeyFramesStartPos_Action());
- }
- }
- public void SetFrameRate(int newFrameRate)
- {
- frameRateBindable = newFrameRate;
- OnPropertyChanged(nameof(FrameRateBindable));
- OnPropertyChanged(nameof(DefaultEndFrame));
- OnPropertyChanged(nameof(LastFrame));
- OnPropertyChanged(nameof(FramesCount));
- }
- public void SetActiveFrame(int newFrame)
- {
- int previousFrame = _activeFrameBindable;
- _activeFrameBindable = newFrame;
- ActiveFrameChanged?.Invoke(previousFrame, newFrame);
- OnPropertyChanged(nameof(ActiveFrameBindable));
- }
- public void SetPlayingState(bool value)
- {
- isPlayingBindable = value;
- OnPropertyChanged(nameof(IsPlayingBindable));
- }
- public void SetOnionSkinning(bool value)
- {
- onionSkinningEnabled = value;
- OnPropertyChanged(nameof(OnionSkinningEnabledBindable));
- }
- public void SetOnionFrames(int frames, double opacity)
- {
- onionFrames = frames;
- onionOpacity = opacity;
- OnPropertyChanged(nameof(OnionFramesBindable));
- OnPropertyChanged(nameof(OnionOpacityBindable));
- }
- public void SetFrameLength(Guid keyFrameId, int newStartFrame, int newDuration)
- {
- if (TryFindCels(keyFrameId, out CelViewModel keyFrame))
- {
- cachedFirstFrame = null;
- cachedLastFrame = null;
-
- keyFrame.SetStartFrame(newStartFrame);
- keyFrame.SetDuration(newDuration);
- keyFrames.NotifyCollectionChanged();
- OnPropertyChanged(nameof(FirstFrame));
- OnPropertyChanged(nameof(LastFrame));
- OnPropertyChanged(nameof(FramesCount));
- }
- }
- public void SetKeyFrameVisibility(Guid keyFrameId, bool isVisible)
- {
- if (TryFindCels(keyFrameId, out CelViewModel keyFrame))
- {
- keyFrame.SetVisibility(isVisible);
- keyFrames.NotifyCollectionChanged();
- }
- }
- public void AddKeyFrame(ICelHandler iCel)
- {
- Guid id = iCel.LayerGuid;
- if (TryFindCels(id, out CelGroupViewModel foundGroup))
- {
- foundGroup.Children.Add((CelViewModel)iCel);
- }
- else
- {
- var group =
- new CelGroupViewModel(iCel.StartFrameBindable, iCel.DurationBindable, id, id, Document,
- Internals);
- group.Children.Add((CelViewModel)iCel);
- keyFrames.Add(group);
- }
- keyFrames.NotifyCollectionChanged(NotifyCollectionChangedAction.Add, (CelViewModel)iCel);
- if (!allCels.Contains(iCel))
- {
- allCels.Add(iCel);
- }
- SortByLayers();
-
- cachedFirstFrame = null;
- cachedLastFrame = null;
-
- OnPropertyChanged(nameof(FirstFrame));
- OnPropertyChanged(nameof(LastFrame));
- OnPropertyChanged(nameof(FramesCount));
- }
- public void RemoveKeyFrame(Guid keyFrameId)
- {
- TryFindCels<CelViewModel>(keyFrameId, out _, (frame, parent) =>
- {
- if (frame is not CelGroupViewModel group)
- {
- parent.Children.Remove(frame);
- keyFrames.NotifyCollectionChanged(NotifyCollectionChangedAction.Remove, (CelViewModel)frame);
- if (parent.Children.Count == 0)
- {
- keyFrames.Remove(parent as CelGroupViewModel);
- }
- }
- else
- {
- keyFrames.Remove(group);
- }
- });
- allCels.RemoveAll(x => x.Id == keyFrameId);
-
- cachedFirstFrame = null;
- cachedLastFrame = null;
- OnPropertyChanged(nameof(FirstFrame));
- OnPropertyChanged(nameof(LastFrame));
- OnPropertyChanged(nameof(FramesCount));
- }
- public void AddSelectedKeyFrame(Guid keyFrameId)
- {
- if (TryFindCels(keyFrameId, out CelViewModel keyFrame))
- {
- keyFrame.IsSelected = true;
- }
- }
- public void RemoveSelectedKeyFrame(Guid keyFrameId)
- {
- if (TryFindCels(keyFrameId, out CelViewModel keyFrame))
- {
- keyFrame.IsSelected = false;
- }
- }
- public void ClearSelectedKeyFrames()
- {
- var selectedFrames = keyFrames.SelectChildrenBy<CelViewModel>(x => x.IsSelected);
- foreach (var frame in selectedFrames)
- {
- frame.IsSelected = false;
- }
- }
- public void RemoveKeyFrames(List<Guid> keyFrameIds)
- {
- List<CelViewModel> framesToRemove = new List<CelViewModel>();
- foreach (var keyFrame in keyFrameIds)
- {
- TryFindCels<CelViewModel>(keyFrame, out _, (frame, parent) =>
- {
- parent.Children.Remove(frame);
- framesToRemove.Add((CelViewModel)frame);
- });
- allCels.RemoveAll(x => x.Id == keyFrame);
- }
- keyFrames.NotifyCollectionChanged(NotifyCollectionChangedAction.Remove, framesToRemove);
- }
- public bool FindKeyFrame<T>(Guid guid, out T keyFrameHandler) where T : ICelHandler
- {
- return TryFindCels<T>(keyFrames, null, guid, out keyFrameHandler, null);
- }
- // TODO: Use the same structure functions as layers
- public bool TryFindCels<T>(Guid id, out T? foundKeyFrame,
- Action<ICelHandler, ICelGroupHandler?> onFound = null) where T : ICelHandler
- {
- return TryFindCels(keyFrames, null, id, out foundKeyFrame, onFound);
- }
- private bool TryFindCels<T>(IReadOnlyCollection<ICelHandler> root, ICelGroupHandler parent, Guid id,
- out T? result,
- Action<ICelHandler, ICelGroupHandler?> onFound) where T : ICelHandler
- {
- for (var i = 0; i < root.Count; i++)
- {
- var frame = root.ElementAt(i);
- if (frame is T targetFrame && targetFrame.Id.Equals(id))
- {
- result = targetFrame;
- onFound?.Invoke(frame, parent);
- return true;
- }
- if (frame is ICelGroupHandler { Children.Count: > 0 } group)
- {
- bool found = TryFindCels(group.Children, group, id, out result, onFound);
- if (found)
- {
- return true;
- }
- }
- }
- result = default;
- return false;
- }
- public void SortByLayers()
- {
- var allLayers = Document.StructureHelper.GetAllLayers();
- var unsortedKeyFrames = keyFrames.ToList();
- var layerKeyFrames = new List<CelGroupViewModel>();
- foreach (var layer in allLayers)
- {
- var group = unsortedKeyFrames.FirstOrDefault(x =>
- x is CelGroupViewModel group && group.LayerGuid == layer.Id) as CelGroupViewModel;
- if (group != null)
- {
- layerKeyFrames.Insert(0, group);
- }
- }
- foreach (var remaining in unsortedKeyFrames)
- {
- if (remaining is CelGroupViewModel group && !layerKeyFrames.Contains(group))
- {
- layerKeyFrames.Add(group);
- }
- }
- this.keyFrames = new KeyFrameCollection(layerKeyFrames);
- OnPropertyChanged(nameof(KeyFrames));
- }
- }
|