DocumentOperationsModule.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. using System.Collections.Generic;
  2. using System.Collections.Immutable;
  3. using System.IO;
  4. using System.Linq;
  5. using ChunkyImageLib;
  6. using ChunkyImageLib.DataHolders;
  7. using PixiEditor.AvaloniaUI.Helpers.Extensions;
  8. using PixiEditor.AvaloniaUI.Models.Clipboard;
  9. using PixiEditor.AvaloniaUI.Models.DocumentModels.UpdateableChangeExecutors;
  10. using PixiEditor.AvaloniaUI.Models.DocumentPassthroughActions;
  11. using PixiEditor.AvaloniaUI.Models.Handlers;
  12. using PixiEditor.AvaloniaUI.Models.Layers;
  13. using PixiEditor.AvaloniaUI.Models.Position;
  14. using PixiEditor.AvaloniaUI.Models.Tools;
  15. using PixiEditor.ChangeableDocument.Actions.Generated;
  16. using PixiEditor.ChangeableDocument.Actions.Undo;
  17. using PixiEditor.ChangeableDocument.Enums;
  18. using PixiEditor.DrawingApi.Core.Numerics;
  19. using PixiEditor.DrawingApi.Core.Surface.Vector;
  20. using PixiEditor.Extensions.CommonApi.Palettes;
  21. using PixiEditor.Numerics;
  22. namespace PixiEditor.AvaloniaUI.Models.DocumentModels.Public;
  23. #nullable enable
  24. internal class DocumentOperationsModule : IDocumentOperations
  25. {
  26. private IDocument Document { get; }
  27. private DocumentInternalParts Internals { get; }
  28. public DocumentOperationsModule(IDocument document, DocumentInternalParts internals)
  29. {
  30. Document = document;
  31. Internals = internals;
  32. }
  33. /// <summary>
  34. /// Creates a new selection with the size of the document
  35. /// </summary>
  36. public void SelectAll() => Select(new RectI(VecI.Zero, Document.SizeBindable), SelectionMode.Add);
  37. /// <summary>
  38. /// Creates a new selection with the size of the document
  39. /// </summary>
  40. public void Select(RectI rect, SelectionMode mode = SelectionMode.New)
  41. {
  42. if (Internals.ChangeController.IsChangeActive)
  43. return;
  44. Internals.ActionAccumulator.AddFinishedActions(
  45. new SelectRectangle_Action(rect, mode),
  46. new EndSelectRectangle_Action());
  47. }
  48. /// <summary>
  49. /// Clears the current selection
  50. /// </summary>
  51. public void ClearSelection()
  52. {
  53. if (Internals.ChangeController.IsChangeActive)
  54. return;
  55. Internals.ActionAccumulator.AddFinishedActions(new ClearSelection_Action());
  56. }
  57. /// <summary>
  58. /// Deletes selected pixels
  59. /// </summary>
  60. /// <param name="clearSelection">Should the selection be cleared</param>
  61. public void DeleteSelectedPixels(bool clearSelection = false)
  62. {
  63. var member = Document.SelectedStructureMember;
  64. if (Internals.ChangeController.IsChangeActive || member is null)
  65. return;
  66. bool drawOnMask = member is ILayerHandler layer ? layer.ShouldDrawOnMask : true;
  67. if (drawOnMask && !member.HasMaskBindable)
  68. return;
  69. Internals.ActionAccumulator.AddActions(new ClearSelectedArea_Action(member.GuidValue, drawOnMask));
  70. if (clearSelection)
  71. Internals.ActionAccumulator.AddActions(new ClearSelection_Action());
  72. Internals.ActionAccumulator.AddFinishedActions();
  73. }
  74. /// <summary>
  75. /// Sets the opacity of the member with the guid <paramref name="memberGuid"/>
  76. /// </summary>
  77. /// <param name="memberGuid">The Guid of the member</param>
  78. /// <param name="value">A value between 0 and 1</param>
  79. public void SetMemberOpacity(Guid memberGuid, float value)
  80. {
  81. if (Internals.ChangeController.IsChangeActive || value is > 1 or < 0)
  82. return;
  83. Internals.ActionAccumulator.AddFinishedActions(
  84. new StructureMemberOpacity_Action(memberGuid, value),
  85. new EndStructureMemberOpacity_Action());
  86. }
  87. /// <summary>
  88. /// Adds a new viewport or updates a existing one
  89. /// </summary>
  90. public void AddOrUpdateViewport(ViewportInfo info) => Internals.ActionAccumulator.AddActions(new RefreshViewport_PassthroughAction(info));
  91. /// <summary>
  92. /// Deletes the viewport with the <paramref name="viewportGuid"/>
  93. /// </summary>
  94. /// <param name="viewportGuid">The Guid of the viewport to remove</param>
  95. public void RemoveViewport(Guid viewportGuid) => Internals.ActionAccumulator.AddActions(new RemoveViewport_PassthroughAction(viewportGuid));
  96. /// <summary>
  97. /// Delete the whole undo stack
  98. /// </summary>
  99. public void ClearUndo()
  100. {
  101. if (Internals.ChangeController.IsChangeActive)
  102. return;
  103. Internals.ActionAccumulator.AddActions(new DeleteRecordedChanges_Action());
  104. }
  105. /// <summary>
  106. /// Pastes the <paramref name="images"/> as new layers
  107. /// </summary>
  108. /// <param name="images">The images to paste</param>
  109. public void PasteImagesAsLayers(List<DataImage> images)
  110. {
  111. if (Internals.ChangeController.IsChangeActive)
  112. return;
  113. RectI maxSize = new RectI(VecI.Zero, Document.SizeBindable);
  114. foreach (var imageWithName in images)
  115. {
  116. maxSize = maxSize.Union(new RectI(imageWithName.position, imageWithName.image.Size));
  117. }
  118. if (maxSize.Size != Document.SizeBindable)
  119. Internals.ActionAccumulator.AddActions(new ResizeCanvas_Action(maxSize.Size, ResizeAnchor.TopLeft));
  120. foreach (var imageWithName in images)
  121. {
  122. var layerGuid = Internals.StructureHelper.CreateNewStructureMember(StructureMemberType.Layer, Path.GetFileName(imageWithName.name));
  123. DrawImage(imageWithName.image, new ShapeCorners(new RectD(imageWithName.position, imageWithName.image.Size)), layerGuid, true, false, false);
  124. }
  125. Internals.ActionAccumulator.AddFinishedActions();
  126. }
  127. /// <summary>
  128. /// Creates a new structure member of type <paramref name="type"/> with the name <paramref name="name"/>
  129. /// </summary>
  130. /// <param name="type">The type of the member</param>
  131. /// <param name="name">The name of the member</param>
  132. /// <returns>The Guid of the new structure member or null if there is already an active change</returns>
  133. public Guid? CreateStructureMember(StructureMemberType type, string? name = null, bool finish = true)
  134. {
  135. if (Internals.ChangeController.IsChangeActive)
  136. return null;
  137. return Internals.StructureHelper.CreateNewStructureMember(type, name, finish);
  138. }
  139. /// <summary>
  140. /// Duplicates the layer with the <paramref name="guidValue"/>
  141. /// </summary>
  142. /// <param name="guidValue">The Guid of the layer</param>
  143. public void DuplicateLayer(Guid guidValue)
  144. {
  145. if (Internals.ChangeController.IsChangeActive)
  146. return;
  147. Internals.ActionAccumulator.AddFinishedActions(new DuplicateLayer_Action(guidValue));
  148. }
  149. /// <summary>
  150. /// Delete the member with the <paramref name="guidValue"/>
  151. /// </summary>
  152. /// <param name="guidValue">The Guid of the layer</param>
  153. public void DeleteStructureMember(Guid guidValue)
  154. {
  155. if (Internals.ChangeController.IsChangeActive)
  156. return;
  157. Internals.ActionAccumulator.AddFinishedActions(new DeleteStructureMember_Action(guidValue));
  158. }
  159. /// <summary>
  160. /// Deletes all members with the <paramref name="guids"/>
  161. /// </summary>
  162. /// <param name="guids">The Guids of the layers to delete</param>
  163. public void DeleteStructureMembers(IReadOnlyList<Guid> guids)
  164. {
  165. if (Internals.ChangeController.IsChangeActive)
  166. return;
  167. Internals.ActionAccumulator.AddFinishedActions(guids.Select(static guid => new DeleteStructureMember_Action(guid)).ToArray());
  168. }
  169. /// <summary>
  170. /// Resizes the canvas (Does not upscale the content of the image)
  171. /// </summary>
  172. /// <param name="newSize">The size the canvas should be resized to</param>
  173. /// <param name="anchor">Where the existing content should be put</param>
  174. public void ResizeCanvas(VecI newSize, ResizeAnchor anchor)
  175. {
  176. if (Internals.ChangeController.IsChangeActive || newSize.X > 9999 || newSize.Y > 9999 || newSize.X < 1 || newSize.Y < 1)
  177. return;
  178. if (Document.ReferenceLayerHandler.ReferenceBitmap is not null)
  179. {
  180. VecI offset = anchor.FindOffsetFor(Document.SizeBindable, newSize);
  181. ShapeCorners curShape = Document.ReferenceLayerHandler.ReferenceShapeBindable;
  182. ShapeCorners offsetCorners = new ShapeCorners()
  183. {
  184. TopLeft = curShape.TopLeft + offset,
  185. TopRight = curShape.TopRight + offset,
  186. BottomLeft = curShape.BottomLeft + offset,
  187. BottomRight = curShape.BottomRight + offset,
  188. };
  189. Internals.ActionAccumulator.AddActions(new TransformReferenceLayer_Action(offsetCorners), new EndTransformReferenceLayer_Action());
  190. }
  191. Internals.ActionAccumulator.AddFinishedActions(new ResizeCanvas_Action(newSize, anchor));
  192. }
  193. /// <summary>
  194. /// Resizes the image (Upscales the content of the image)
  195. /// </summary>
  196. /// <param name="newSize">The size the image should be resized to</param>
  197. /// <param name="resampling">The resampling method to use</param>
  198. public void ResizeImage(VecI newSize, ResamplingMethod resampling)
  199. {
  200. if (Internals.ChangeController.IsChangeActive || newSize.X > 9999 || newSize.Y > 9999 || newSize.X < 1 || newSize.Y < 1)
  201. return;
  202. if (Document.ReferenceLayerHandler.ReferenceBitmap is not null)
  203. {
  204. VecD scale = ((VecD)newSize).Divide(Document.SizeBindable);
  205. ShapeCorners curShape = Document.ReferenceLayerHandler.ReferenceShapeBindable;
  206. ShapeCorners offsetCorners = new ShapeCorners()
  207. {
  208. TopLeft = curShape.TopLeft.Multiply(scale),
  209. TopRight = curShape.TopRight.Multiply(scale),
  210. BottomLeft = curShape.BottomLeft.Multiply(scale),
  211. BottomRight = curShape.BottomRight.Multiply(scale),
  212. };
  213. Internals.ActionAccumulator.AddActions(new TransformReferenceLayer_Action(offsetCorners), new EndTransformReferenceLayer_Action());
  214. }
  215. Internals.ActionAccumulator.AddFinishedActions(new ResizeImage_Action(newSize, resampling));
  216. }
  217. /// <summary>
  218. /// Replaces all <paramref name="oldColor"/> with <paramref name="newColor"/>
  219. /// </summary>
  220. /// <param name="oldColor">The color to replace</param>
  221. /// <param name="newColor">The new color</param>
  222. public void ReplaceColor(PaletteColor oldColor, PaletteColor newColor)
  223. {
  224. if (Internals.ChangeController.IsChangeActive || oldColor == newColor)
  225. return;
  226. Internals.ActionAccumulator.AddFinishedActions(new ReplaceColor_Action(oldColor.ToColor(), newColor.ToColor()));
  227. ReplaceInPalette(oldColor, newColor);
  228. }
  229. private void ReplaceInPalette(PaletteColor oldColor, PaletteColor newColor)
  230. {
  231. int indexOfOldColor = Document.Palette.IndexOf(oldColor);
  232. if(indexOfOldColor == -1)
  233. return;
  234. Document.Palette.RemoveAt(indexOfOldColor);
  235. Document.Palette.Insert(indexOfOldColor, newColor);
  236. }
  237. /// <summary>
  238. /// Creates a new mask on the <paramref name="member"/>
  239. /// </summary>
  240. public void CreateMask(IStructureMemberHandler member)
  241. {
  242. if (Internals.ChangeController.IsChangeActive)
  243. return;
  244. if (!member.MaskIsVisibleBindable)
  245. Internals.ActionAccumulator.AddActions(new StructureMemberMaskIsVisible_Action(true, member.GuidValue));
  246. Internals.ActionAccumulator.AddFinishedActions(new CreateStructureMemberMask_Action(member.GuidValue));
  247. }
  248. /// <summary>
  249. /// Deletes the mask of the <paramref name="member"/>
  250. /// </summary>
  251. public void DeleteMask(IStructureMemberHandler member)
  252. {
  253. if (Internals.ChangeController.IsChangeActive)
  254. return;
  255. Internals.ActionAccumulator.AddFinishedActions(new DeleteStructureMemberMask_Action(member.GuidValue));
  256. }
  257. /// <summary>
  258. /// Applies the mask to the image
  259. /// </summary>
  260. public void ApplyMask(IStructureMemberHandler member)
  261. {
  262. if (Internals.ChangeController.IsChangeActive)
  263. return;
  264. Internals.ActionAccumulator.AddFinishedActions(new ApplyMask_Action(member.GuidValue), new DeleteStructureMemberMask_Action(member.GuidValue));
  265. }
  266. /// <summary>
  267. /// Sets the selected structure memeber
  268. /// </summary>
  269. /// <param name="memberGuid">The Guid of the member to select</param>
  270. public void SetSelectedMember(Guid memberGuid) => Internals.ActionAccumulator.AddActions(new SetSelectedMember_PassthroughAction(memberGuid));
  271. /// <summary>
  272. /// Adds a member to the soft selection
  273. /// </summary>
  274. /// <param name="memberGuid">The Guid of the member to add</param>
  275. public void AddSoftSelectedMember(Guid memberGuid) => Internals.ActionAccumulator.AddActions(new AddSoftSelectedMember_PassthroughAction(memberGuid));
  276. /// <summary>
  277. /// Removes a member from the soft selection
  278. /// </summary>
  279. /// <param name="memberGuid">The Guid of the member to remove</param>
  280. public void RemoveSoftSelectedMember(Guid memberGuid) => Internals.ActionAccumulator.AddActions(new RemoveSoftSelectedMember_PassthroughAction(memberGuid));
  281. /// <summary>
  282. /// Clears the soft selection
  283. /// </summary>
  284. public void ClearSoftSelectedMembers() => Internals.ActionAccumulator.AddActions(new ClearSoftSelectedMembers_PassthroughAction());
  285. public void AddSelectedKeyFrame(Guid keyFrameGuid) => Internals.ActionAccumulator.AddActions(new AddSelectedKeyFrame_PassthroughAction(keyFrameGuid));
  286. public void RemoveSelectedKeyFrame(Guid keyFrameGuid) => Internals.ActionAccumulator.AddActions(new RemoveSelectedKeyFrame_PassthroughAction(keyFrameGuid));
  287. public void ClearSelectedKeyFrames() => Internals.ActionAccumulator.AddActions(new ClearSelectedKeyFrames_PassthroughAction());
  288. /// <summary>
  289. /// Undo last change
  290. /// </summary>
  291. public void Undo()
  292. {
  293. if (Internals.ChangeController.IsChangeActive)
  294. {
  295. Internals.ChangeController.MidChangeUndoInlet();
  296. return;
  297. }
  298. Internals.ActionAccumulator.AddActions(new Undo_Action());
  299. }
  300. /// <summary>
  301. /// Redo previously undone change
  302. /// </summary>
  303. public void Redo()
  304. {
  305. if (Internals.ChangeController.IsChangeActive)
  306. {
  307. Internals.ChangeController.MidChangeRedoInlet();
  308. return;
  309. }
  310. Internals.ActionAccumulator.AddActions(new Redo_Action());
  311. }
  312. public void NudgeSelectedObject(VecI distance)
  313. {
  314. if (Internals.ChangeController.IsChangeActive)
  315. {
  316. Internals.ChangeController.SelectedObjectNudgedInlet(distance);
  317. }
  318. }
  319. /// <summary>
  320. /// Moves a member next to or inside another structure member
  321. /// </summary>
  322. /// <param name="memberToMove">The member to move</param>
  323. /// <param name="memberToMoveIntoOrNextTo">The target member</param>
  324. /// <param name="placement">Where to place the <paramref name="memberToMove"/></param>
  325. public void MoveStructureMember(Guid memberToMove, Guid memberToMoveIntoOrNextTo, StructureMemberPlacement placement)
  326. {
  327. if (Internals.ChangeController.IsChangeActive || memberToMove == memberToMoveIntoOrNextTo)
  328. return;
  329. Internals.StructureHelper.TryMoveStructureMember(memberToMove, memberToMoveIntoOrNextTo, placement);
  330. }
  331. /// <summary>
  332. /// Merge all structure members with the Guids inside <paramref name="members"/>
  333. /// </summary>
  334. public void MergeStructureMembers(IReadOnlyList<Guid> members)
  335. {
  336. if (Internals.ChangeController.IsChangeActive || members.Count < 2)
  337. return;
  338. var (child, parent) = Document.StructureHelper.FindChildAndParent(members[0]);
  339. if (child is null || parent is null)
  340. return;
  341. int index = parent.Children.IndexOf(child);
  342. Guid newGuid = Guid.NewGuid();
  343. //make a new layer, put combined image onto it, delete layers that were merged
  344. Internals.ActionAccumulator.AddActions(
  345. new CreateStructureMember_Action(parent.GuidValue, newGuid, index, StructureMemberType.Layer),
  346. new StructureMemberName_Action(newGuid, child.NameBindable),
  347. new CombineStructureMembersOnto_Action(members.ToHashSet(), newGuid));
  348. foreach (var member in members)
  349. Internals.ActionAccumulator.AddActions(new DeleteStructureMember_Action(member));
  350. Internals.ActionAccumulator.AddActions(new ChangeBoundary_Action());
  351. }
  352. /// <summary>
  353. /// Starts a image transform and pastes the transformed image on the currently selected layer
  354. /// </summary>
  355. /// <param name="image">The image to paste</param>
  356. /// <param name="startPos">Where the transform should start</param>
  357. public void PasteImageWithTransform(Surface image, VecI startPos)
  358. {
  359. if (Document.SelectedStructureMember is null)
  360. return;
  361. Internals.ChangeController.TryStartExecutor(new PasteImageExecutor(image, startPos));
  362. }
  363. /// <summary>
  364. /// Starts a image transform and pastes the transformed image on the currently selected layer
  365. /// </summary>
  366. /// <param name="image">The image to paste</param>
  367. /// <param name="startPos">Where the transform should start</param>
  368. public void PasteImageWithTransform(Surface image, VecI startPos, Guid memberGuid, bool drawOnMask)
  369. {
  370. Internals.ChangeController.TryStartExecutor(new PasteImageExecutor(image, startPos, memberGuid, drawOnMask));
  371. }
  372. /// <summary>
  373. /// Starts a transform on the selected area
  374. /// </summary>
  375. /// <param name="toolLinked">Is this transform started by a tool</param>
  376. public void TransformSelectedArea(bool toolLinked)
  377. {
  378. if (Document.SelectedStructureMember is null ||
  379. Internals.ChangeController.IsChangeActive && !toolLinked ||
  380. Document.SelectionPathBindable.IsEmpty)
  381. return;
  382. Internals.ChangeController.TryStartExecutor(new TransformSelectedAreaExecutor(toolLinked));
  383. }
  384. /// <summary>
  385. /// Ties stopping the currently executing tool linked executor
  386. /// </summary>
  387. public void TryStopToolLinkedExecutor()
  388. {
  389. if (Internals.ChangeController.GetCurrentExecutorType() == ExecutorType.ToolLinked)
  390. Internals.ChangeController.TryStopActiveExecutor();
  391. }
  392. public void DrawImage(Surface image, ShapeCorners corners, Guid memberGuid, bool ignoreClipSymmetriesEtc, bool drawOnMask) =>
  393. DrawImage(image, corners, memberGuid, ignoreClipSymmetriesEtc, drawOnMask, true);
  394. /// <summary>
  395. /// Draws a image on the member with the <paramref name="memberGuid"/>
  396. /// </summary>
  397. /// <param name="image">The image to draw onto the layer</param>
  398. /// <param name="corners">The shape the image should fit into</param>
  399. /// <param name="memberGuid">The Guid of the member to paste on</param>
  400. /// <param name="ignoreClipSymmetriesEtc">Ignore selection clipping and symmetry (See DrawingChangeHelper.ApplyClipsSymmetriesEtc of UpdateableDocument)</param>
  401. /// <param name="drawOnMask">Draw on the mask or on the image</param>
  402. /// <param name="finish">Is this a finished action</param>
  403. private void DrawImage(Surface image, ShapeCorners corners, Guid memberGuid, bool ignoreClipSymmetriesEtc, bool drawOnMask, bool finish)
  404. {
  405. if (Internals.ChangeController.IsChangeActive)
  406. return;
  407. Internals.ActionAccumulator.AddActions(
  408. new PasteImage_Action(image, corners, memberGuid, ignoreClipSymmetriesEtc, drawOnMask),
  409. new EndPasteImage_Action());
  410. if (finish)
  411. Internals.ActionAccumulator.AddFinishedActions();
  412. }
  413. /// <summary>
  414. /// Resizes the canvas to fit the content
  415. /// </summary>
  416. public void ClipCanvas()
  417. {
  418. if (Internals.ChangeController.IsChangeActive)
  419. return;
  420. Internals.ActionAccumulator.AddFinishedActions(new ClipCanvas_Action());
  421. }
  422. /// <summary>
  423. /// Flips the image on the <paramref name="flipType"/> axis
  424. /// </summary>
  425. public void FlipImage(FlipType flipType) => FlipImage(flipType, null);
  426. /// <summary>
  427. /// Flips the members with the Guids of <paramref name="membersToFlip"/> on the <paramref name="flipType"/> axis
  428. /// </summary>
  429. public void FlipImage(FlipType flipType, List<Guid> membersToFlip)
  430. {
  431. if (Internals.ChangeController.IsChangeActive)
  432. return;
  433. Internals.ActionAccumulator.AddFinishedActions(new FlipImage_Action(flipType, membersToFlip));
  434. }
  435. /// <summary>
  436. /// Rotates the image
  437. /// </summary>
  438. /// <param name="rotation">The degrees to rotate the image by</param>
  439. public void RotateImage(RotationAngle rotation) => RotateImage(rotation, null);
  440. /// <summary>
  441. /// Rotates the members with the Guids of <paramref name="membersToRotate"/>
  442. /// </summary>
  443. /// <param name="rotation">The degrees to rotate the members by</param>
  444. public void RotateImage(RotationAngle rotation, List<Guid> membersToRotate)
  445. {
  446. if (Internals.ChangeController.IsChangeActive)
  447. return;
  448. Internals.ActionAccumulator.AddFinishedActions(new RotateImage_Action(rotation, membersToRotate));
  449. }
  450. /// <summary>
  451. /// Puts the content of the image in the middle of the canvas
  452. /// </summary>
  453. public void CenterContent(IReadOnlyList<Guid> structureMembers)
  454. {
  455. if (Internals.ChangeController.IsChangeActive)
  456. return;
  457. Internals.ActionAccumulator.AddFinishedActions(new CenterContent_Action(structureMembers.ToList()));
  458. }
  459. /// <summary>
  460. /// Imports a reference layer from a Pbgra Int32 array
  461. /// </summary>
  462. /// <param name="imageSize">The size of the image</param>
  463. public void ImportReferenceLayer(ImmutableArray<byte> imageBgra8888Bytes, VecI imageSize)
  464. {
  465. if (Internals.ChangeController.IsChangeActive)
  466. return;
  467. RectD referenceImageRect = new RectD(VecD.Zero, Document.SizeBindable).AspectFit(new RectD(VecD.Zero, imageSize));
  468. ShapeCorners corners = new ShapeCorners(referenceImageRect);
  469. Internals.ActionAccumulator.AddFinishedActions(new SetReferenceLayer_Action(corners, imageBgra8888Bytes, imageSize));
  470. }
  471. /// <summary>
  472. /// Deletes the reference layer
  473. /// </summary>
  474. public void DeleteReferenceLayer()
  475. {
  476. if (Internals.ChangeController.IsChangeActive || Document.ReferenceLayerHandler.ReferenceBitmap is null)
  477. return;
  478. Internals.ActionAccumulator.AddFinishedActions(new DeleteReferenceLayer_Action());
  479. }
  480. /// <summary>
  481. /// Starts a transform on the reference layer
  482. /// </summary>
  483. public void TransformReferenceLayer()
  484. {
  485. if (Document.ReferenceLayerHandler.ReferenceBitmap is null || Internals.ChangeController.IsChangeActive)
  486. return;
  487. Internals.ChangeController.TryStartExecutor(new TransformReferenceLayerExecutor());
  488. }
  489. /// <summary>
  490. /// Resets the reference layer transform
  491. /// </summary>
  492. public void ResetReferenceLayerPosition()
  493. {
  494. if (Document.ReferenceLayerHandler.ReferenceBitmap is null || Internals.ChangeController.IsChangeActive)
  495. return;
  496. VecD size = new(Document.ReferenceLayerHandler.ReferenceBitmap.Size.X, Document.ReferenceLayerHandler.ReferenceBitmap.Size.Y);
  497. RectD referenceImageRect = new RectD(VecD.Zero, Document.SizeBindable).AspectFit(new RectD(VecD.Zero, size));
  498. ShapeCorners corners = new ShapeCorners(referenceImageRect);
  499. Internals.ActionAccumulator.AddFinishedActions(
  500. new TransformReferenceLayer_Action(corners),
  501. new EndTransformReferenceLayer_Action()
  502. );
  503. }
  504. public void SelectionToMask(SelectionMode mode)
  505. {
  506. if (Document.SelectedStructureMember is not { } member || Document.SelectionPathBindable.IsEmpty)
  507. return;
  508. if (!Document.SelectedStructureMember.HasMaskBindable)
  509. {
  510. Internals.ActionAccumulator.AddActions(new CreateStructureMemberMask_Action(member.GuidValue));
  511. }
  512. Internals.ActionAccumulator.AddFinishedActions(new SelectionToMask_Action(member.GuidValue, mode));
  513. }
  514. public void CropToSelection(bool clearSelection = true)
  515. {
  516. var bounds = Document.SelectionPathBindable.TightBounds;
  517. if (Document.SelectionPathBindable.IsEmpty || bounds.Width <= 0 || bounds.Height <= 0)
  518. return;
  519. Internals.ActionAccumulator.AddActions(new Crop_Action((RectI)bounds));
  520. if (clearSelection)
  521. {
  522. Internals.ActionAccumulator.AddFinishedActions(new ClearSelection_Action());
  523. }
  524. else
  525. {
  526. Internals.ActionAccumulator.AddFinishedActions();
  527. }
  528. }
  529. public void InvertSelection()
  530. {
  531. var selection = Document.SelectionPathBindable;
  532. var inverse = new VectorPath();
  533. inverse.AddRect(new RectI(new(0, 0), Document.SizeBindable));
  534. Internals.ActionAccumulator.AddFinishedActions(new SetSelection_Action(inverse.Op(selection, VectorPathOp.Difference)));
  535. }
  536. public void SetActiveFrame(int value)
  537. {
  538. if (Internals.ChangeController.IsChangeActive || value is < 0)
  539. return;
  540. Internals.ActionAccumulator.AddFinishedActions(
  541. new ActiveFrame_Action(value),
  542. new EndActiveFrame_Action());
  543. }
  544. }