Document.cs 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. using PixiEditor.Helpers;
  2. using PixiEditor.Models.Controllers;
  3. using PixiEditor.Models.Enums;
  4. using PixiEditor.Models.Layers;
  5. using PixiEditor.Models.Layers.Utils;
  6. using PixiEditor.Models.Position;
  7. using PixiEditor.Models.Undo;
  8. using PixiEditor.ViewModels;
  9. using SkiaSharp;
  10. using System;
  11. using System.Buffers;
  12. using System.Collections.Generic;
  13. using System.Collections.ObjectModel;
  14. using System.Diagnostics;
  15. using System.IO;
  16. using System.Linq;
  17. using System.Windows;
  18. namespace PixiEditor.Models.DataHolders
  19. {
  20. [DebuggerDisplay("'{Name, nq}' {width}x{height} {Layers.Count} Layer(s)")]
  21. public partial class Document : NotifyableObject, IDisposable
  22. {
  23. private ViewModelMain xamlAccesibleViewModel = null;
  24. public ViewModelMain XamlAccesibleViewModel // Used to access ViewModelMain, without changing DataContext in XAML
  25. {
  26. get => xamlAccesibleViewModel;
  27. set
  28. {
  29. xamlAccesibleViewModel = value;
  30. RaisePropertyChanged(nameof(XamlAccesibleViewModel));
  31. }
  32. }
  33. public string Name
  34. {
  35. get => (string.IsNullOrEmpty(DocumentFilePath) ? "Untitled" : Path.GetFileName(DocumentFilePath))
  36. + (!ChangesSaved ? " *" : string.Empty);
  37. }
  38. private int width = 1;
  39. public int Width
  40. {
  41. get => width;
  42. private set
  43. {
  44. width = value;
  45. RaisePropertyChanged(nameof(Width));
  46. }
  47. }
  48. private int height = 1;
  49. public int Height
  50. {
  51. get => height;
  52. private set
  53. {
  54. height = value;
  55. RaisePropertyChanged("Height");
  56. }
  57. }
  58. private Selection selection = new Selection(Array.Empty<Coordinates>());
  59. public Selection ActiveSelection
  60. {
  61. get => selection;
  62. set
  63. {
  64. selection = value;
  65. RaisePropertyChanged(nameof(ActiveSelection));
  66. }
  67. }
  68. private double mouseXonCanvas;
  69. public double MouseXOnCanvas // Mouse X coordinate relative to canvas
  70. {
  71. get => mouseXonCanvas;
  72. set
  73. {
  74. mouseXonCanvas = value;
  75. RaisePropertyChanged(nameof(MouseXOnCanvas));
  76. }
  77. }
  78. private double mouseYonCanvas;
  79. public double MouseYOnCanvas // Mouse Y coordinate relative to canvas
  80. {
  81. get => mouseYonCanvas;
  82. set
  83. {
  84. mouseYonCanvas = value;
  85. RaisePropertyChanged(nameof(MouseYOnCanvas));
  86. }
  87. }
  88. public ExecutionTrigger<Size> CenterViewportTrigger { get; } = new();
  89. public ExecutionTrigger<double> ZoomViewportTrigger { get; } = new();
  90. public UndoManager UndoManager { get; set; }
  91. public ObservableCollection<SKColor> Swatches { get; set; } = new ObservableCollection<SKColor>();
  92. public void RaisePropertyChange(string name)
  93. {
  94. RaisePropertyChanged(name);
  95. }
  96. /// <summary>
  97. /// Resizes canvas, so it fits exactly the size of drawn content, without any transparent pixels outside.
  98. /// </summary>
  99. public void ClipCanvas()
  100. {
  101. DoubleCords points = GetEdgePoints(Layers);
  102. int smallestX = points.Coords1.X;
  103. int smallestY = points.Coords1.Y;
  104. int biggestX = points.Coords2.X;
  105. int biggestY = points.Coords2.Y;
  106. if (smallestX == 0 && smallestY == 0 && biggestX == 0 && biggestY == 0)
  107. {
  108. return;
  109. }
  110. int width = biggestX - smallestX;
  111. int height = biggestY - smallestY;
  112. Coordinates moveVector = new Coordinates(-smallestX, -smallestY);
  113. Thickness[] oldOffsets = Layers.Select(x => x.Offset).ToArray();
  114. int oldWidth = Width;
  115. int oldHeight = Height;
  116. MoveOffsets(Layers, moveVector);
  117. object[] reverseArguments = { oldOffsets, oldWidth, oldHeight };
  118. object[] processArguments = { Layers.Select(x => x.Offset).ToArray(), width, height };
  119. ResizeCanvasProcess(processArguments);
  120. UndoManager.AddUndoChange(new Change(
  121. ResizeCanvasProcess,
  122. reverseArguments,
  123. ResizeCanvasProcess,
  124. processArguments,
  125. "Clip canvas"));
  126. }
  127. /// <summary>
  128. /// Centers selected, visible layers inside document.
  129. /// </summary>
  130. public void CenterContent()
  131. {
  132. var layersToCenter = Layers.Where(x => x.IsActive && LayerStructureUtils.GetFinalLayerIsVisible(x, LayerStructure));
  133. if (!layersToCenter.Any())
  134. {
  135. return;
  136. }
  137. DoubleCords points = GetEdgePoints(layersToCenter);
  138. int smallestX = points.Coords1.X;
  139. int smallestY = points.Coords1.Y;
  140. int biggestX = points.Coords2.X;
  141. int biggestY = points.Coords2.Y;
  142. if (smallestX == 0 && smallestY == 0 && biggestX == 0 && biggestY == 0)
  143. {
  144. return;
  145. }
  146. Coordinates contentCenter = CoordinatesCalculator.GetCenterPoint(points.Coords1, points.Coords2);
  147. Coordinates documentCenter = CoordinatesCalculator.GetCenterPoint(
  148. new Coordinates(0, 0),
  149. new Coordinates(Width, Height));
  150. Coordinates moveVector = new Coordinates(documentCenter.X - contentCenter.X, documentCenter.Y - contentCenter.Y);
  151. MoveOffsets(layersToCenter, moveVector);
  152. UndoManager.AddUndoChange(
  153. new Change(
  154. MoveOffsetsProcess,
  155. new object[] { layersToCenter, new Coordinates(-moveVector.X, -moveVector.Y) },
  156. MoveOffsetsProcess,
  157. new object[] { layersToCenter, moveVector },
  158. "Center content"));
  159. }
  160. public void Dispose()
  161. {
  162. DisposeLayerBitmaps();
  163. UndoManager.Dispose();
  164. GC.SuppressFinalize(this);
  165. }
  166. private void SetAsActiveOnClick(object obj)
  167. {
  168. XamlAccesibleViewModel.BitmapManager.MouseController.StopRecordingMouseMovementChanges();
  169. //XamlAccesibleViewModel.BitmapManager.MouseController.StartRecordingMouseMovementChanges(true);
  170. if (XamlAccesibleViewModel.BitmapManager.ActiveDocument != this)
  171. {
  172. XamlAccesibleViewModel.BitmapManager.ActiveDocument = this;
  173. }
  174. }
  175. private void RequestCloseDocument(object parameter)
  176. {
  177. ViewModelMain.Current.DocumentSubViewModel.RequestCloseDocument(this);
  178. }
  179. private int GetOffsetXForAnchor(int srcWidth, int destWidth, AnchorPoint anchor)
  180. {
  181. if (anchor.HasFlag(AnchorPoint.Center))
  182. {
  183. return (destWidth / 2) - (srcWidth / 2);
  184. }
  185. if (anchor.HasFlag(AnchorPoint.Right))
  186. {
  187. return destWidth - srcWidth;
  188. }
  189. return 0;
  190. }
  191. private int GetOffsetYForAnchor(int srcHeight, int destHeight, AnchorPoint anchor)
  192. {
  193. if (anchor.HasFlag(AnchorPoint.Middle))
  194. {
  195. return (destHeight / 2) - (srcHeight / 2);
  196. }
  197. if (anchor.HasFlag(AnchorPoint.Bottom))
  198. {
  199. return destHeight - srcHeight;
  200. }
  201. return 0;
  202. }
  203. private DoubleCords GetEdgePoints(IEnumerable<Layer> layers)
  204. {
  205. if (Layers.Count == 0)
  206. {
  207. throw new ArgumentException("Not enough layers");
  208. }
  209. int smallestX = int.MaxValue;
  210. int smallestY = int.MaxValue;
  211. int biggestX = int.MinValue;
  212. int biggestY = int.MinValue;
  213. foreach (Layer layer in layers)
  214. {
  215. layer.ClipCanvas();
  216. if (layer.OffsetX < smallestX)
  217. {
  218. smallestX = layer.OffsetX;
  219. }
  220. if (layer.OffsetX + layer.Width > biggestX)
  221. {
  222. biggestX = layer.OffsetX + layer.Width;
  223. }
  224. if (layer.OffsetY < smallestY)
  225. {
  226. smallestY = layer.OffsetY;
  227. }
  228. if (layer.OffsetY + layer.Height > biggestY)
  229. {
  230. biggestY = layer.OffsetY + layer.Height;
  231. }
  232. }
  233. return new DoubleCords(
  234. new Coordinates(smallestX, smallestY),
  235. new Coordinates(biggestX, biggestY));
  236. }
  237. }
  238. }