AnimationDataViewModel.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. using System.Collections.ObjectModel;
  2. using System.Collections.Specialized;
  3. using CommunityToolkit.Mvvm.ComponentModel;
  4. using PixiEditor.AnimationRenderer.Core;
  5. using PixiEditor.ChangeableDocument.Actions.Generated;
  6. using PixiEditor.ChangeableDocument.Changeables.Animations;
  7. using PixiEditor.ChangeableDocument.Changeables.Interfaces;
  8. using PixiEditor.Models.DocumentModels;
  9. using PixiEditor.Models.DocumentPassthroughActions;
  10. using PixiEditor.Models.Handlers;
  11. namespace PixiEditor.ViewModels.Document;
  12. internal class AnimationDataViewModel : ObservableObject, IAnimationHandler
  13. {
  14. private int _activeFrameBindable = 1;
  15. private int frameRateBindable = 60;
  16. private int onionFrames = 1;
  17. private double onionOpacity = 50;
  18. public DocumentViewModel Document { get; }
  19. protected DocumentInternalParts Internals { get; }
  20. public IReadOnlyCollection<ICelHandler> KeyFrames => keyFrames;
  21. public IReadOnlyCollection<ICelHandler> AllCels => allCels;
  22. public event Action<int, int> ActiveFrameChanged;
  23. private KeyFrameCollection keyFrames = new KeyFrameCollection();
  24. private List<ICelHandler> allCels = new List<ICelHandler>();
  25. private bool onionSkinningEnabled;
  26. private bool isPlayingBindable;
  27. private int? cachedFirstFrame;
  28. private int? cachedLastFrame;
  29. public int ActiveFrameBindable
  30. {
  31. get => _activeFrameBindable;
  32. set
  33. {
  34. if (Document.BlockingUpdateableChangeActive)
  35. return;
  36. Internals.ActionAccumulator.AddActions(new SetActiveFrame_PassthroughAction(value));
  37. }
  38. }
  39. public IAnimationRenderer Renderer { get; set; }
  40. public int FrameRateBindable
  41. {
  42. get => frameRateBindable;
  43. set
  44. {
  45. if (Document.BlockingUpdateableChangeActive)
  46. return;
  47. Internals.ActionAccumulator.AddFinishedActions(new SetFrameRate_Action(value));
  48. }
  49. }
  50. public bool OnionSkinningEnabledBindable
  51. {
  52. get => onionSkinningEnabled;
  53. set
  54. {
  55. if (Document.BlockingUpdateableChangeActive)
  56. return;
  57. Internals.ActionAccumulator.AddFinishedActions(new ToggleOnionSkinning_PassthroughAction(value));
  58. }
  59. }
  60. public int OnionFramesBindable
  61. {
  62. get => onionFrames;
  63. set
  64. {
  65. if (Document.BlockingUpdateableChangeActive)
  66. return;
  67. Internals.ActionAccumulator.AddFinishedActions(new SetOnionSettings_Action(value, OnionOpacityBindable));
  68. }
  69. }
  70. public double OnionOpacityBindable
  71. {
  72. get => onionOpacity;
  73. set
  74. {
  75. if (Document.BlockingUpdateableChangeActive)
  76. return;
  77. Internals.ActionAccumulator.AddFinishedActions(new SetOnionSettings_Action(OnionFramesBindable, value));
  78. }
  79. }
  80. public bool IsPlayingBindable
  81. {
  82. get => isPlayingBindable;
  83. set
  84. {
  85. if (Document.BlockingUpdateableChangeActive)
  86. return;
  87. Internals.ActionAccumulator.AddFinishedActions(new SetPlayingState_PassthroughAction(value));
  88. }
  89. }
  90. public int FirstFrame => cachedFirstFrame ??= keyFrames.Count > 0 ? keyFrames.Min(x => x.StartFrameBindable) : 0;
  91. public int LastFrame => cachedLastFrame ??= keyFrames.Count > 0
  92. ? keyFrames.Max(x => x.StartFrameBindable + x.DurationBindable)
  93. : DefaultEndFrame;
  94. public int FramesCount => LastFrame - FirstFrame;
  95. private double ActiveNormalizedTime => (double)(ActiveFrameBindable - FirstFrame) / FramesCount;
  96. private int DefaultEndFrame => FrameRateBindable; // 1 second
  97. public AnimationDataViewModel(DocumentViewModel document, DocumentInternalParts internals)
  98. {
  99. Document = document;
  100. Internals = internals;
  101. Document.LayersChanged += (sender, args) => SortByLayers();
  102. }
  103. public KeyFrameTime ActiveFrameTime => new KeyFrameTime(ActiveFrameBindable, ActiveNormalizedTime);
  104. public void CreateCel(Guid targetLayerGuid, int frame, Guid? toCloneFrom = null,
  105. int? frameToCopyFrom = null)
  106. {
  107. if (!Document.BlockingUpdateableChangeActive)
  108. {
  109. Internals.ActionAccumulator.AddFinishedActions(new CreateCel_Action(targetLayerGuid,
  110. Guid.NewGuid(), Math.Max(1, frame),
  111. frameToCopyFrom ?? -1, toCloneFrom ?? Guid.Empty));
  112. }
  113. }
  114. public void DeleteCels(List<Guid> keyFrameIds)
  115. {
  116. if (!Document.BlockingUpdateableChangeActive)
  117. {
  118. for (var i = 0; i < keyFrameIds.Count; i++)
  119. {
  120. var id = keyFrameIds[i];
  121. if (i == keyFrameIds.Count - 1)
  122. {
  123. Internals.ActionAccumulator.AddFinishedActions(new DeleteKeyFrame_Action(id));
  124. }
  125. else
  126. {
  127. Internals.ActionAccumulator.AddActions(new DeleteKeyFrame_Action(id));
  128. }
  129. }
  130. }
  131. }
  132. public void ChangeKeyFramesStartPos(Guid[] infoIds, int infoDelta)
  133. {
  134. if (!Document.BlockingUpdateableChangeActive)
  135. {
  136. Internals.ActionAccumulator.AddActions(new KeyFramesStartPos_Action(infoIds.ToList(), infoDelta));
  137. }
  138. }
  139. public void ToggleOnionSkinning(bool value)
  140. {
  141. if (!Document.BlockingUpdateableChangeActive)
  142. {
  143. Internals.ActionAccumulator.AddFinishedActions(new ToggleOnionSkinning_PassthroughAction(value));
  144. }
  145. }
  146. public void EndKeyFramesStartPos()
  147. {
  148. if (!Document.BlockingUpdateableChangeActive)
  149. {
  150. Internals.ActionAccumulator.AddFinishedActions(new EndKeyFramesStartPos_Action());
  151. }
  152. }
  153. public void SetFrameRate(int newFrameRate)
  154. {
  155. frameRateBindable = newFrameRate;
  156. OnPropertyChanged(nameof(FrameRateBindable));
  157. OnPropertyChanged(nameof(DefaultEndFrame));
  158. OnPropertyChanged(nameof(LastFrame));
  159. OnPropertyChanged(nameof(FramesCount));
  160. }
  161. public void SetActiveFrame(int newFrame)
  162. {
  163. int previousFrame = _activeFrameBindable;
  164. _activeFrameBindable = newFrame;
  165. ActiveFrameChanged?.Invoke(previousFrame, newFrame);
  166. OnPropertyChanged(nameof(ActiveFrameBindable));
  167. }
  168. public void SetPlayingState(bool value)
  169. {
  170. isPlayingBindable = value;
  171. OnPropertyChanged(nameof(IsPlayingBindable));
  172. }
  173. public void SetOnionSkinning(bool value)
  174. {
  175. onionSkinningEnabled = value;
  176. OnPropertyChanged(nameof(OnionSkinningEnabledBindable));
  177. }
  178. public void SetOnionFrames(int frames, double opacity)
  179. {
  180. onionFrames = frames;
  181. onionOpacity = opacity;
  182. OnPropertyChanged(nameof(OnionFramesBindable));
  183. OnPropertyChanged(nameof(OnionOpacityBindable));
  184. }
  185. public void SetFrameLength(Guid keyFrameId, int newStartFrame, int newDuration)
  186. {
  187. if (TryFindCels(keyFrameId, out CelViewModel keyFrame))
  188. {
  189. cachedFirstFrame = null;
  190. cachedLastFrame = null;
  191. keyFrame.SetStartFrame(newStartFrame);
  192. keyFrame.SetDuration(newDuration);
  193. keyFrames.NotifyCollectionChanged();
  194. OnPropertyChanged(nameof(FirstFrame));
  195. OnPropertyChanged(nameof(LastFrame));
  196. OnPropertyChanged(nameof(FramesCount));
  197. }
  198. }
  199. public void SetKeyFrameVisibility(Guid keyFrameId, bool isVisible)
  200. {
  201. if (TryFindCels(keyFrameId, out CelViewModel keyFrame))
  202. {
  203. keyFrame.SetVisibility(isVisible);
  204. keyFrames.NotifyCollectionChanged();
  205. }
  206. }
  207. public void AddKeyFrame(ICelHandler iCel)
  208. {
  209. Guid id = iCel.LayerGuid;
  210. if (TryFindCels(id, out CelGroupViewModel foundGroup))
  211. {
  212. foundGroup.Children.Add((CelViewModel)iCel);
  213. }
  214. else
  215. {
  216. var group =
  217. new CelGroupViewModel(iCel.StartFrameBindable, iCel.DurationBindable, id, id, Document,
  218. Internals);
  219. group.Children.Add((CelViewModel)iCel);
  220. keyFrames.Add(group);
  221. }
  222. keyFrames.NotifyCollectionChanged(NotifyCollectionChangedAction.Add, (CelViewModel)iCel);
  223. if (!allCels.Contains(iCel))
  224. {
  225. allCels.Add(iCel);
  226. }
  227. SortByLayers();
  228. cachedFirstFrame = null;
  229. cachedLastFrame = null;
  230. OnPropertyChanged(nameof(FirstFrame));
  231. OnPropertyChanged(nameof(LastFrame));
  232. OnPropertyChanged(nameof(FramesCount));
  233. }
  234. public void RemoveKeyFrame(Guid keyFrameId)
  235. {
  236. TryFindCels<CelViewModel>(keyFrameId, out _, (frame, parent) =>
  237. {
  238. if (frame is not CelGroupViewModel group)
  239. {
  240. parent.Children.Remove(frame);
  241. keyFrames.NotifyCollectionChanged(NotifyCollectionChangedAction.Remove, (CelViewModel)frame);
  242. if (parent.Children.Count == 0)
  243. {
  244. keyFrames.Remove(parent as CelGroupViewModel);
  245. }
  246. }
  247. else
  248. {
  249. keyFrames.Remove(group);
  250. }
  251. });
  252. allCels.RemoveAll(x => x.Id == keyFrameId);
  253. cachedFirstFrame = null;
  254. cachedLastFrame = null;
  255. OnPropertyChanged(nameof(FirstFrame));
  256. OnPropertyChanged(nameof(LastFrame));
  257. OnPropertyChanged(nameof(FramesCount));
  258. }
  259. public void AddSelectedKeyFrame(Guid keyFrameId)
  260. {
  261. if (TryFindCels(keyFrameId, out CelViewModel keyFrame))
  262. {
  263. keyFrame.IsSelected = true;
  264. }
  265. }
  266. public void RemoveSelectedKeyFrame(Guid keyFrameId)
  267. {
  268. if (TryFindCels(keyFrameId, out CelViewModel keyFrame))
  269. {
  270. keyFrame.IsSelected = false;
  271. }
  272. }
  273. public void ClearSelectedKeyFrames()
  274. {
  275. var selectedFrames = keyFrames.SelectChildrenBy<CelViewModel>(x => x.IsSelected);
  276. foreach (var frame in selectedFrames)
  277. {
  278. frame.IsSelected = false;
  279. }
  280. }
  281. public void RemoveKeyFrames(List<Guid> keyFrameIds)
  282. {
  283. List<CelViewModel> framesToRemove = new List<CelViewModel>();
  284. foreach (var keyFrame in keyFrameIds)
  285. {
  286. TryFindCels<CelViewModel>(keyFrame, out _, (frame, parent) =>
  287. {
  288. parent.Children.Remove(frame);
  289. framesToRemove.Add((CelViewModel)frame);
  290. });
  291. allCels.RemoveAll(x => x.Id == keyFrame);
  292. }
  293. keyFrames.NotifyCollectionChanged(NotifyCollectionChangedAction.Remove, framesToRemove);
  294. }
  295. public bool FindKeyFrame<T>(Guid guid, out T keyFrameHandler) where T : ICelHandler
  296. {
  297. return TryFindCels<T>(keyFrames, null, guid, out keyFrameHandler, null);
  298. }
  299. // TODO: Use the same structure functions as layers
  300. public bool TryFindCels<T>(Guid id, out T? foundKeyFrame,
  301. Action<ICelHandler, ICelGroupHandler?> onFound = null) where T : ICelHandler
  302. {
  303. return TryFindCels(keyFrames, null, id, out foundKeyFrame, onFound);
  304. }
  305. private bool TryFindCels<T>(IReadOnlyCollection<ICelHandler> root, ICelGroupHandler parent, Guid id,
  306. out T? result,
  307. Action<ICelHandler, ICelGroupHandler?> onFound) where T : ICelHandler
  308. {
  309. for (var i = 0; i < root.Count; i++)
  310. {
  311. var frame = root.ElementAt(i);
  312. if (frame is T targetFrame && targetFrame.Id.Equals(id))
  313. {
  314. result = targetFrame;
  315. onFound?.Invoke(frame, parent);
  316. return true;
  317. }
  318. if (frame is ICelGroupHandler { Children.Count: > 0 } group)
  319. {
  320. bool found = TryFindCels(group.Children, group, id, out result, onFound);
  321. if (found)
  322. {
  323. return true;
  324. }
  325. }
  326. }
  327. result = default;
  328. return false;
  329. }
  330. public void SortByLayers()
  331. {
  332. var allLayers = Document.StructureHelper.GetAllLayers();
  333. var unsortedKeyFrames = keyFrames.ToList();
  334. var layerKeyFrames = new List<CelGroupViewModel>();
  335. foreach (var layer in allLayers)
  336. {
  337. var group = unsortedKeyFrames.FirstOrDefault(x =>
  338. x is CelGroupViewModel group && group.LayerGuid == layer.Id) as CelGroupViewModel;
  339. if (group != null)
  340. {
  341. layerKeyFrames.Insert(0, group);
  342. }
  343. }
  344. foreach (var remaining in unsortedKeyFrames)
  345. {
  346. if (remaining is CelGroupViewModel group && !layerKeyFrames.Contains(group))
  347. {
  348. layerKeyFrames.Add(group);
  349. }
  350. }
  351. this.keyFrames = new KeyFrameCollection(layerKeyFrames);
  352. OnPropertyChanged(nameof(KeyFrames));
  353. }
  354. }