Browse Source

File scoped namespaces

Equbuxu 3 years ago
parent
commit
b7a253f428
100 changed files with 3865 additions and 3966 deletions
  1. 43 44
      src/ChunkyImageLib/Chunk.cs
  2. 39 40
      src/ChunkyImageLib/ChunkPool.cs
  3. 501 502
      src/ChunkyImageLib/ChunkyImage.cs
  4. 33 34
      src/ChunkyImageLib/CommittedChunkStorage.cs
  5. 8 9
      src/ChunkyImageLib/DataHolders/ChunkResolution.cs
  6. 21 22
      src/ChunkyImageLib/DataHolders/ChunkResolutionEx.cs
  7. 17 18
      src/ChunkyImageLib/DataHolders/RotatedShapeData.cs
  8. 17 18
      src/ChunkyImageLib/DataHolders/ShapeData.cs
  9. 146 147
      src/ChunkyImageLib/DataHolders/Vector2d.cs
  10. 99 100
      src/ChunkyImageLib/DataHolders/Vector2i.cs
  11. 7 8
      src/ChunkyImageLib/IReadOnlyChunkyImage.cs
  12. 4 5
      src/ChunkyImageLib/Operations/ClearOperation.cs
  13. 24 25
      src/ChunkyImageLib/Operations/ClearRegionOperation.cs
  14. 6 7
      src/ChunkyImageLib/Operations/IDrawOperation.cs
  15. 3 4
      src/ChunkyImageLib/Operations/IOperation.cs
  16. 34 35
      src/ChunkyImageLib/Operations/ImageOperation.cs
  17. 254 255
      src/ChunkyImageLib/Operations/OperationHelper.cs
  18. 8 9
      src/ChunkyImageLib/Operations/RasterClipOperation.cs
  19. 89 90
      src/ChunkyImageLib/Operations/RectangleOperation.cs
  20. 7 8
      src/ChunkyImageLib/Operations/ResizeOperation.cs
  21. 82 83
      src/ChunkyImageLib/Surface.cs
  22. 28 29
      src/ChunkyImageLibTest/ClearRegionOperationTests.cs
  23. 28 29
      src/ChunkyImageLibTest/OperationHelperTests.cs
  24. 97 98
      src/ChunkyImageLibTest/RectangleOperationTests.cs
  25. 6 7
      src/ChunkyImageLibVis/App.xaml.cs
  26. 152 153
      src/ChunkyImageLibVis/MainWindow.xaml.cs
  27. 12 13
      src/PixiEditor.ChangeableDocument/Actions/Drawing/CombineStructureMembersOnto_Action.cs
  28. 18 19
      src/PixiEditor.ChangeableDocument/Actions/Drawing/Rectangle/DrawRectangle_Action.cs
  29. 5 6
      src/PixiEditor.ChangeableDocument/Actions/Drawing/Rectangle/EndDrawRectangle_Action.cs
  30. 5 6
      src/PixiEditor.ChangeableDocument/Actions/Drawing/Selection/ClearSelection_Action.cs
  31. 5 6
      src/PixiEditor.ChangeableDocument/Actions/Drawing/Selection/EndSelectRectangle_Action.cs
  32. 16 17
      src/PixiEditor.ChangeableDocument/Actions/Drawing/Selection/SelectRectangle_Action.cs
  33. 3 4
      src/PixiEditor.ChangeableDocument/Actions/IAction.cs
  34. 4 5
      src/PixiEditor.ChangeableDocument/Actions/IEndChangeAction.cs
  35. 4 5
      src/PixiEditor.ChangeableDocument/Actions/IMakeChangeAction.cs
  36. 5 6
      src/PixiEditor.ChangeableDocument/Actions/IStartOrUpdateChangeAction.cs
  37. 11 12
      src/PixiEditor.ChangeableDocument/Actions/Properties/CreateStructureMemberMask_Action.cs
  38. 11 12
      src/PixiEditor.ChangeableDocument/Actions/Properties/DeleteStructureMemberMask_Action.cs
  39. 4 5
      src/PixiEditor.ChangeableDocument/Actions/Properties/EndOpacityChange_Action.cs
  40. 16 17
      src/PixiEditor.ChangeableDocument/Actions/Properties/OpacityChange_Action.cs
  41. 10 11
      src/PixiEditor.ChangeableDocument/Actions/Root/ResizeCanvas_Action.cs
  42. 3 4
      src/PixiEditor.ChangeableDocument/Actions/Undo/ChangeBoundary_Action.cs
  43. 3 4
      src/PixiEditor.ChangeableDocument/Actions/Undo/DeleteRecordedChanges_Action.cs
  44. 4 5
      src/PixiEditor.ChangeableDocument/ChangeInfos/CreateStructureMember_ChangeInfo.cs
  45. 4 5
      src/PixiEditor.ChangeableDocument/ChangeInfos/DeleteStructureMember_ChangeInfo.cs
  46. 3 4
      src/PixiEditor.ChangeableDocument/ChangeInfos/IChangeInfo.cs
  47. 5 6
      src/PixiEditor.ChangeableDocument/ChangeInfos/LayerImageChunks_ChangeInfo.cs
  48. 5 6
      src/PixiEditor.ChangeableDocument/ChangeInfos/MaskChunks_ChangeInfo.cs
  49. 4 5
      src/PixiEditor.ChangeableDocument/ChangeInfos/MoveStructureMember_ChangeInfo.cs
  50. 4 5
      src/PixiEditor.ChangeableDocument/ChangeInfos/Selection_ChangeInfo.cs
  51. 3 4
      src/PixiEditor.ChangeableDocument/ChangeInfos/Size_ChangeInfo.cs
  52. 4 5
      src/PixiEditor.ChangeableDocument/ChangeInfos/StructureMemberIsVisible_ChangeInfo.cs
  53. 4 5
      src/PixiEditor.ChangeableDocument/ChangeInfos/StructureMemberMask_ChangeInfo.cs
  54. 4 5
      src/PixiEditor.ChangeableDocument/ChangeInfos/StructureMemberName_ChangeInfo.cs
  55. 4 5
      src/PixiEditor.ChangeableDocument/ChangeInfos/StructureMemberOpacity_ChangeInfo.cs
  56. 50 51
      src/PixiEditor.ChangeableDocument/Changeables/Document.cs
  57. 26 27
      src/PixiEditor.ChangeableDocument/Changeables/Folder.cs
  58. 4 6
      src/PixiEditor.ChangeableDocument/Changeables/IChangeable.cs
  59. 10 11
      src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyDocument.cs
  60. 4 5
      src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyFolder.cs
  61. 4 5
      src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyLayer.cs
  62. 5 6
      src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlySelection.cs
  63. 9 10
      src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyStructureMember.cs
  64. 27 28
      src/PixiEditor.ChangeableDocument/Changeables/Layer.cs
  65. 11 12
      src/PixiEditor.ChangeableDocument/Changeables/Selection.cs
  66. 12 13
      src/PixiEditor.ChangeableDocument/Changeables/StructureMember.cs
  67. 9 10
      src/PixiEditor.ChangeableDocument/Changes/Change.cs
  68. 39 40
      src/PixiEditor.ChangeableDocument/Changes/Drawing/ClearSelection_Change.cs
  69. 68 69
      src/PixiEditor.ChangeableDocument/Changes/Drawing/CombineStructureMembersOnto_Change.cs
  70. 90 91
      src/PixiEditor.ChangeableDocument/Changes/Drawing/DrawRectangle_UpdateableChange.cs
  71. 50 51
      src/PixiEditor.ChangeableDocument/Changes/Drawing/SelectRectangle_UpdateableChange.cs
  72. 25 26
      src/PixiEditor.ChangeableDocument/Changes/Properties/CreateStructureMemberMask_Change.cs
  73. 43 44
      src/PixiEditor.ChangeableDocument/Changes/Properties/DeleteStructureMemberMask_Change.cs
  74. 28 29
      src/PixiEditor.ChangeableDocument/Changes/Properties/StructureMemberIsVisible_Change.cs
  75. 37 38
      src/PixiEditor.ChangeableDocument/Changes/Properties/StructureMemberName_Change.cs
  76. 45 46
      src/PixiEditor.ChangeableDocument/Changes/Properties/StructureMemberOpacity_UpdateableChange.cs
  77. 80 81
      src/PixiEditor.ChangeableDocument/Changes/Root/ResizeCanvas_Change.cs
  78. 35 36
      src/PixiEditor.ChangeableDocument/Changes/Structure/CreateStructureMember_Change.cs
  79. 40 41
      src/PixiEditor.ChangeableDocument/Changes/Structure/DeleteStructureMember_Change.cs
  80. 44 45
      src/PixiEditor.ChangeableDocument/Changes/Structure/MoveStructureMember_Change.cs
  81. 4 5
      src/PixiEditor.ChangeableDocument/Changes/UpdateableChange.cs
  82. 201 202
      src/PixiEditor.ChangeableDocument/DocumentChangeTracker.cs
  83. 5 6
      src/PixiEditor.ChangeableDocument/Enums/StructureMemberType.cs
  84. 75 76
      src/PixiEditor.ChangeableDocument/Rendering/ChunkRenderer.cs
  85. 6 7
      src/PixiEditor.Zoombox/IDragOperation.cs
  86. 23 24
      src/PixiEditor.Zoombox/MoveDragOperation.cs
  87. 41 42
      src/PixiEditor.Zoombox/RotateDragOperation.cs
  88. 13 14
      src/PixiEditor.Zoombox/ViewportRoutedEventArgs.cs
  89. 36 37
      src/PixiEditor.Zoombox/ZoomDragOperation.cs
  90. 302 303
      src/PixiEditor.Zoombox/Zoombox.xaml.cs
  91. 7 8
      src/PixiEditor.Zoombox/ZoomboxMode.cs
  92. 6 7
      src/PixiEditorPrototype/App.xaml.cs
  93. 83 84
      src/PixiEditorPrototype/Behaviors/SliderUpdateBehavior.cs
  94. 10 11
      src/PixiEditorPrototype/Converters/BoolToVisibilityConverter.cs
  95. 17 18
      src/PixiEditorPrototype/Converters/IndexToChunkResolutionConverter.cs
  96. 72 73
      src/PixiEditorPrototype/Models/ActionAccumulator.cs
  97. 14 15
      src/PixiEditorPrototype/Models/DocumentHelpers.cs
  98. 4 5
      src/PixiEditorPrototype/Models/DocumentState.cs
  99. 84 85
      src/PixiEditorPrototype/Models/DocumentStructureHelper.cs
  100. 131 132
      src/PixiEditorPrototype/Models/DocumentUpdater.cs

+ 43 - 44
src/ChunkyImageLib/Chunk.cs

@@ -1,51 +1,50 @@
 using ChunkyImageLib.DataHolders;
 using SkiaSharp;
 
-namespace ChunkyImageLib
+namespace ChunkyImageLib;
+
+public class Chunk : IDisposable
 {
-    public class Chunk : IDisposable
+    private static volatile int chunkCounter = 0;
+    /// <summary>
+    /// The number of chunks that haven't yet been returned (includes garbage collected chunks).
+    /// Used in tests to make sure that all chunks are disposed.
+    /// </summary>
+    public static int ChunkCounter => chunkCounter;
+
+
+    private bool returned = false;
+    public Surface Surface { get; }
+    public Vector2i PixelSize { get; }
+    public ChunkResolution Resolution { get; }
+    private Chunk(ChunkResolution resolution)
+    {
+        int size = resolution.PixelSize();
+
+        Resolution = resolution;
+        PixelSize = new(size, size);
+        Surface = new Surface(PixelSize);
+    }
+
+    public static Chunk Create(ChunkResolution resolution = ChunkResolution.Full)
+    {
+        var chunk = ChunkPool.Instance.Get(resolution) ?? new Chunk(resolution);
+        chunk.returned = false;
+        Interlocked.Increment(ref chunkCounter);
+        return chunk;
+    }
+
+    public void DrawOnSurface(SKSurface surface, Vector2i pos, SKPaint? paint = null)
+    {
+        surface.Canvas.DrawSurface(Surface.SkiaSurface, pos.X, pos.Y, paint);
+    }
+
+    public void Dispose()
     {
-        private static volatile int chunkCounter = 0;
-        /// <summary>
-        /// The number of chunks that haven't yet been returned (includes garbage collected chunks).
-        /// Used in tests to make sure that all chunks are disposed.
-        /// </summary>
-        public static int ChunkCounter => chunkCounter;
-
-
-        private bool returned = false;
-        public Surface Surface { get; }
-        public Vector2i PixelSize { get; }
-        public ChunkResolution Resolution { get; }
-        private Chunk(ChunkResolution resolution)
-        {
-            int size = resolution.PixelSize();
-
-            Resolution = resolution;
-            PixelSize = new(size, size);
-            Surface = new Surface(PixelSize);
-        }
-
-        public static Chunk Create(ChunkResolution resolution = ChunkResolution.Full)
-        {
-            var chunk = ChunkPool.Instance.Get(resolution) ?? new Chunk(resolution);
-            chunk.returned = false;
-            Interlocked.Increment(ref chunkCounter);
-            return chunk;
-        }
-
-        public void DrawOnSurface(SKSurface surface, Vector2i pos, SKPaint? paint = null)
-        {
-            surface.Canvas.DrawSurface(Surface.SkiaSurface, pos.X, pos.Y, paint);
-        }
-
-        public void Dispose()
-        {
-            if (returned)
-                return;
-            returned = true;
-            Interlocked.Decrement(ref chunkCounter);
-            ChunkPool.Instance.Push(this);
-        }
+        if (returned)
+            return;
+        returned = true;
+        Interlocked.Decrement(ref chunkCounter);
+        ChunkPool.Instance.Push(this);
     }
 }

+ 39 - 40
src/ChunkyImageLib/ChunkPool.cs

@@ -1,57 +1,56 @@
 using ChunkyImageLib.DataHolders;
 using System.Collections.Concurrent;
 
-namespace ChunkyImageLib
+namespace ChunkyImageLib;
+
+internal class ChunkPool
 {
-    internal class ChunkPool
-    {
-        //must be divisible by 8
-        public const int FullChunkSize = 256;
+    //must be divisible by 8
+    public const int FullChunkSize = 256;
 
-        private static object lockObj = new();
-        private static ChunkPool? instance;
-        public static ChunkPool Instance
+    private static object lockObj = new();
+    private static ChunkPool? instance;
+    public static ChunkPool Instance
+    {
+        get
         {
-            get
+            if (instance is null)
             {
-                if (instance is null)
+                lock (lockObj)
                 {
-                    lock (lockObj)
-                    {
-                        if (instance is null)
-                            instance = new ChunkPool();
-                    }
+                    if (instance is null)
+                        instance = new ChunkPool();
                 }
-                return instance;
             }
+            return instance;
         }
+    }
 
-        private readonly ConcurrentBag<Chunk> fullChunks = new();
-        private readonly ConcurrentBag<Chunk> halfChunks = new();
-        private readonly ConcurrentBag<Chunk> quarterChunks = new();
-        private readonly ConcurrentBag<Chunk> eighthChunks = new();
-        internal Chunk? Get(ChunkResolution resolution) => GetBag(resolution).TryTake(out Chunk? item) ? item : null;
+    private readonly ConcurrentBag<Chunk> fullChunks = new();
+    private readonly ConcurrentBag<Chunk> halfChunks = new();
+    private readonly ConcurrentBag<Chunk> quarterChunks = new();
+    private readonly ConcurrentBag<Chunk> eighthChunks = new();
+    internal Chunk? Get(ChunkResolution resolution) => GetBag(resolution).TryTake(out Chunk? item) ? item : null;
 
-        private ConcurrentBag<Chunk> GetBag(ChunkResolution resolution)
+    private ConcurrentBag<Chunk> GetBag(ChunkResolution resolution)
+    {
+        return resolution switch
         {
-            return resolution switch
-            {
-                ChunkResolution.Full => fullChunks,
-                ChunkResolution.Half => halfChunks,
-                ChunkResolution.Quarter => quarterChunks,
-                ChunkResolution.Eighth => eighthChunks,
-                _ => fullChunks
-            };
-        }
+            ChunkResolution.Full => fullChunks,
+            ChunkResolution.Half => halfChunks,
+            ChunkResolution.Quarter => quarterChunks,
+            ChunkResolution.Eighth => eighthChunks,
+            _ => fullChunks
+        };
+    }
 
-        internal void Push(Chunk chunk)
-        {
-            var chunks = GetBag(chunk.Resolution);
-            //a race condition can cause the count to go above 200, but likely not by much
-            if (chunks.Count < 200)
-                chunks.Add(chunk);
-            else
-                chunk.Surface.Dispose();
-        }
+    internal void Push(Chunk chunk)
+    {
+        var chunks = GetBag(chunk.Resolution);
+        //a race condition can cause the count to go above 200, but likely not by much
+        if (chunks.Count < 200)
+            chunks.Add(chunk);
+        else
+            chunk.Surface.Dispose();
     }
 }

+ 501 - 502
src/ChunkyImageLib/ChunkyImage.cs

@@ -4,657 +4,656 @@ using ChunkyImageLib.Operations;
 using SkiaSharp;
 
 [assembly: InternalsVisibleTo("ChunkyImageLibTest")]
-namespace ChunkyImageLib
+namespace ChunkyImageLib;
+
+/// <summary>
+/// This class is thread-safe only for reading! Only the functions from IReadOnlyChunkyImage can be called from any thread.
+/// ChunkyImage can be in two general states: 
+/// 1. a state with all chunks committed and no queued operations
+///     - latestChunks and latestChunksData are empty
+///     - queuedOperations are empty
+///     - committedChunks[ChunkResolution.Full] contains the current versions of all stored chunks
+///     - committedChunks[*any other resolution*] may contain the current low res versions of some of the chunks (or all of them, or none)
+///     - LatestSize == CommittedSize == current image size (px)
+/// 2. and a state with some queued operations
+///     - queuedOperations contains all requested operations (drawing, raster clips, clear, etc.)
+///     - committedChunks[ChunkResolution.Full] contains the last versions before any operations of all stored chunks
+///     - committedChunks[*any other resolution*] may contain the last low res versions before any operations of some of the chunks (or all of them, or none)
+///     - latestChunks stores chunks with some (or none, or all) queued operations applied
+///     - latestChunksData stores the data for some or all of the latest chunks (not necessarily synced with latestChunks).
+///         The data includes how many operations from the queue have already been applied to the chunk, as well as chunk deleted state (the clear operation deletes chunks)
+///     - LatestSize contains the new size if any resize operations were requested, otherwise the commited size
+/// You can check the current state via queuedOperations.Count == 0
+/// </summary>
+public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
 {
-    /// <summary>
-    /// This class is thread-safe only for reading! Only the functions from IReadOnlyChunkyImage can be called from any thread.
-    /// ChunkyImage can be in two general states: 
-    /// 1. a state with all chunks committed and no queued operations
-    ///     - latestChunks and latestChunksData are empty
-    ///     - queuedOperations are empty
-    ///     - committedChunks[ChunkResolution.Full] contains the current versions of all stored chunks
-    ///     - committedChunks[*any other resolution*] may contain the current low res versions of some of the chunks (or all of them, or none)
-    ///     - LatestSize == CommittedSize == current image size (px)
-    /// 2. and a state with some queued operations
-    ///     - queuedOperations contains all requested operations (drawing, raster clips, clear, etc.)
-    ///     - committedChunks[ChunkResolution.Full] contains the last versions before any operations of all stored chunks
-    ///     - committedChunks[*any other resolution*] may contain the last low res versions before any operations of some of the chunks (or all of them, or none)
-    ///     - latestChunks stores chunks with some (or none, or all) queued operations applied
-    ///     - latestChunksData stores the data for some or all of the latest chunks (not necessarily synced with latestChunks).
-    ///         The data includes how many operations from the queue have already been applied to the chunk, as well as chunk deleted state (the clear operation deletes chunks)
-    ///     - LatestSize contains the new size if any resize operations were requested, otherwise the commited size
-    /// You can check the current state via queuedOperations.Count == 0
-    /// </summary>
-    public class ChunkyImage : IReadOnlyChunkyImage, IDisposable
+    private struct LatestChunkData
     {
-        private struct LatestChunkData
+        public LatestChunkData()
         {
-            public LatestChunkData()
-            {
-                QueueProgress = 0;
-                IsDeleted = false;
-            }
-
-            public int QueueProgress { get; set; }
-            public bool IsDeleted { get; set; }
+            QueueProgress = 0;
+            IsDeleted = false;
         }
-        private bool disposed = false;
-        private object lockObject = new();
-        private int commitCounter = 0;
 
-        public static int ChunkSize => ChunkPool.FullChunkSize;
-        private static SKPaint ClippingPaint { get; } = new SKPaint() { BlendMode = SKBlendMode.DstIn };
-        private static SKPaint ReplacingPaint { get; } = new SKPaint() { BlendMode = SKBlendMode.Src };
+        public int QueueProgress { get; set; }
+        public bool IsDeleted { get; set; }
+    }
+    private bool disposed = false;
+    private object lockObject = new();
+    private int commitCounter = 0;
 
-        private Dictionary<ChunkResolution, Chunk> tempRasterClipChunks;
+    public static int ChunkSize => ChunkPool.FullChunkSize;
+    private static SKPaint ClippingPaint { get; } = new SKPaint() { BlendMode = SKBlendMode.DstIn };
+    private static SKPaint ReplacingPaint { get; } = new SKPaint() { BlendMode = SKBlendMode.Src };
 
-        public Vector2i CommittedSize { get; private set; }
-        public Vector2i LatestSize { get; private set; }
+    private Dictionary<ChunkResolution, Chunk> tempRasterClipChunks;
 
-        private List<(IOperation operation, HashSet<Vector2i> affectedChunks)> queuedOperations = new();
+    public Vector2i CommittedSize { get; private set; }
+    public Vector2i LatestSize { get; private set; }
 
-        private Dictionary<ChunkResolution, Dictionary<Vector2i, Chunk>> committedChunks;
-        private Dictionary<ChunkResolution, Dictionary<Vector2i, Chunk>> latestChunks;
-        private Dictionary<ChunkResolution, Dictionary<Vector2i, LatestChunkData>> latestChunksData = new();
+    private List<(IOperation operation, HashSet<Vector2i> affectedChunks)> queuedOperations = new();
 
-        public ChunkyImage(Vector2i size)
-        {
-            CommittedSize = size;
-            LatestSize = size;
-            tempRasterClipChunks = new Dictionary<ChunkResolution, Chunk>()
-            {
-                [ChunkResolution.Full] = Chunk.Create(ChunkResolution.Full),
-                [ChunkResolution.Half] = Chunk.Create(ChunkResolution.Half),
-                [ChunkResolution.Quarter] = Chunk.Create(ChunkResolution.Quarter),
-                [ChunkResolution.Eighth] = Chunk.Create(ChunkResolution.Eighth),
-            };
-            committedChunks = new()
-            {
-                [ChunkResolution.Full] = new(),
-                [ChunkResolution.Half] = new(),
-                [ChunkResolution.Quarter] = new(),
-                [ChunkResolution.Eighth] = new(),
-            };
-            latestChunks = new()
-            {
-                [ChunkResolution.Full] = new(),
-                [ChunkResolution.Half] = new(),
-                [ChunkResolution.Quarter] = new(),
-                [ChunkResolution.Eighth] = new(),
-            };
-            latestChunksData = new()
-            {
-                [ChunkResolution.Full] = new(),
-                [ChunkResolution.Half] = new(),
-                [ChunkResolution.Quarter] = new(),
-                [ChunkResolution.Eighth] = new(),
-            };
-        }
+    private Dictionary<ChunkResolution, Dictionary<Vector2i, Chunk>> committedChunks;
+    private Dictionary<ChunkResolution, Dictionary<Vector2i, Chunk>> latestChunks;
+    private Dictionary<ChunkResolution, Dictionary<Vector2i, LatestChunkData>> latestChunksData = new();
 
-        public ChunkyImage CloneFromLatest()
+    public ChunkyImage(Vector2i size)
+    {
+        CommittedSize = size;
+        LatestSize = size;
+        tempRasterClipChunks = new Dictionary<ChunkResolution, Chunk>()
+        {
+            [ChunkResolution.Full] = Chunk.Create(ChunkResolution.Full),
+            [ChunkResolution.Half] = Chunk.Create(ChunkResolution.Half),
+            [ChunkResolution.Quarter] = Chunk.Create(ChunkResolution.Quarter),
+            [ChunkResolution.Eighth] = Chunk.Create(ChunkResolution.Eighth),
+        };
+        committedChunks = new()
+        {
+            [ChunkResolution.Full] = new(),
+            [ChunkResolution.Half] = new(),
+            [ChunkResolution.Quarter] = new(),
+            [ChunkResolution.Eighth] = new(),
+        };
+        latestChunks = new()
+        {
+            [ChunkResolution.Full] = new(),
+            [ChunkResolution.Half] = new(),
+            [ChunkResolution.Quarter] = new(),
+            [ChunkResolution.Eighth] = new(),
+        };
+        latestChunksData = new()
+        {
+            [ChunkResolution.Full] = new(),
+            [ChunkResolution.Half] = new(),
+            [ChunkResolution.Quarter] = new(),
+            [ChunkResolution.Eighth] = new(),
+        };
+    }
+
+    public ChunkyImage CloneFromLatest()
+    {
+        lock (lockObject)
         {
-            lock (lockObject)
+            ChunkyImage output = new(LatestSize);
+            var chunks = FindAllChunks();
+            foreach (var chunk in chunks)
             {
-                ChunkyImage output = new(LatestSize);
-                var chunks = FindAllChunks();
-                foreach (var chunk in chunks)
-                {
-                    var image = (Chunk?)GetLatestChunk(chunk, ChunkResolution.Full);
-                    if (image is not null)
-                        output.DrawImage(chunk * ChunkSize, image.Surface);
-                }
-                output.CommitChanges();
-                return output;
+                var image = (Chunk?)GetLatestChunk(chunk, ChunkResolution.Full);
+                if (image is not null)
+                    output.DrawImage(chunk * ChunkSize, image.Surface);
             }
+            output.CommitChanges();
+            return output;
         }
+    }
 
-        public bool DrawLatestChunkOn(Vector2i chunkPos, ChunkResolution resolution, SKSurface surface, Vector2i pos, SKPaint? paint = null)
+    public bool DrawLatestChunkOn(Vector2i chunkPos, ChunkResolution resolution, SKSurface surface, Vector2i pos, SKPaint? paint = null)
+    {
+        lock (lockObject)
         {
-            lock (lockObject)
-            {
-                var chunk = GetLatestChunk(chunkPos, resolution);
-                if (chunk is null)
-                    return false;
-                chunk.DrawOnSurface(surface, pos, paint);
-                return true;
-            }
+            var chunk = GetLatestChunk(chunkPos, resolution);
+            if (chunk is null)
+                return false;
+            chunk.DrawOnSurface(surface, pos, paint);
+            return true;
         }
+    }
 
-        public bool LatestChunkExists(Vector2i chunkPos, ChunkResolution resolution)
+    public bool LatestChunkExists(Vector2i chunkPos, ChunkResolution resolution)
+    {
+        lock (lockObject)
         {
-            lock (lockObject)
-            {
-                return GetLatestChunk(chunkPos, resolution) is not null;
-            }
+            return GetLatestChunk(chunkPos, resolution) is not null;
         }
+    }
 
-        internal bool DrawCommittedChunkOn(Vector2i chunkPos, ChunkResolution resolution, SKSurface surface, Vector2i pos, SKPaint? paint = null)
+    internal bool DrawCommittedChunkOn(Vector2i chunkPos, ChunkResolution resolution, SKSurface surface, Vector2i pos, SKPaint? paint = null)
+    {
+        lock (lockObject)
         {
-            lock (lockObject)
-            {
-                var chunk = GetCommittedChunk(chunkPos, resolution);
-                if (chunk is null)
-                    return false;
-                chunk.DrawOnSurface(surface, pos, paint);
-                return true;
-            }
+            var chunk = GetCommittedChunk(chunkPos, resolution);
+            if (chunk is null)
+                return false;
+            chunk.DrawOnSurface(surface, pos, paint);
+            return true;
         }
+    }
 
-        internal bool CommitedChunkExists(Vector2i chunkPos, ChunkResolution resolution)
+    internal bool CommitedChunkExists(Vector2i chunkPos, ChunkResolution resolution)
+    {
+        lock (lockObject)
         {
-            lock (lockObject)
-            {
-                return GetCommittedChunk(chunkPos, resolution) is not null;
-            }
+            return GetCommittedChunk(chunkPos, resolution) is not null;
         }
+    }
 
-        /// <summary>
-        /// Returns the latest version of the chunk, with uncommitted changes applied if they exist
-        /// </summary>
-        private Chunk? GetLatestChunk(Vector2i pos, ChunkResolution resolution)
+    /// <summary>
+    /// Returns the latest version of the chunk, with uncommitted changes applied if they exist
+    /// </summary>
+    private Chunk? GetLatestChunk(Vector2i pos, ChunkResolution resolution)
+    {
+        //no queued operations
+        if (queuedOperations.Count == 0)
         {
-            //no queued operations
-            if (queuedOperations.Count == 0)
-            {
-                var sameResChunk = MaybeGetCommittedChunk(pos, resolution);
-                if (sameResChunk is not null)
-                    return sameResChunk;
+            var sameResChunk = MaybeGetCommittedChunk(pos, resolution);
+            if (sameResChunk is not null)
+                return sameResChunk;
 
-                var fullResChunk = MaybeGetCommittedChunk(pos, ChunkResolution.Full);
-                if (fullResChunk is not null)
-                    return GetOrCreateCommittedChunk(pos, resolution);
+            var fullResChunk = MaybeGetCommittedChunk(pos, ChunkResolution.Full);
+            if (fullResChunk is not null)
+                return GetOrCreateCommittedChunk(pos, resolution);
 
-                return null;
-            }
+            return null;
+        }
 
-            // there are queued operations, target chunk is affected
-            MaybeCreateAndProcessQueueForChunk(pos, resolution);
-            var maybeNewlyProcessedChunk = MaybeGetLatestChunk(pos, resolution);
-            if (maybeNewlyProcessedChunk is not null)
-                return maybeNewlyProcessedChunk;
+        // there are queued operations, target chunk is affected
+        MaybeCreateAndProcessQueueForChunk(pos, resolution);
+        var maybeNewlyProcessedChunk = MaybeGetLatestChunk(pos, resolution);
+        if (maybeNewlyProcessedChunk is not null)
+            return maybeNewlyProcessedChunk;
 
-            // there are queued operations, target chunk is unaffected
-            var maybeSameResCommitedChunk = MaybeGetCommittedChunk(pos, resolution);
-            if (maybeSameResCommitedChunk is not null)
-                return maybeSameResCommitedChunk;
+        // there are queued operations, target chunk is unaffected
+        var maybeSameResCommitedChunk = MaybeGetCommittedChunk(pos, resolution);
+        if (maybeSameResCommitedChunk is not null)
+            return maybeSameResCommitedChunk;
 
-            var maybeFullResCommitedChunk = MaybeGetCommittedChunk(pos, ChunkResolution.Full);
-            if (maybeFullResCommitedChunk is not null)
-                return GetOrCreateCommittedChunk(pos, resolution);
+        var maybeFullResCommitedChunk = MaybeGetCommittedChunk(pos, ChunkResolution.Full);
+        if (maybeFullResCommitedChunk is not null)
+            return GetOrCreateCommittedChunk(pos, resolution);
 
-            return null;
-        }
+        return null;
+    }
 
-        /// <summary>
-        /// Returns the committed version of the chunk ignoring any uncommitted changes
-        /// </summary>
-        private Chunk? GetCommittedChunk(Vector2i pos, ChunkResolution resolution)
-        {
-            var maybeSameRes = MaybeGetCommittedChunk(pos, resolution);
-            if (maybeSameRes is not null)
-                return maybeSameRes;
+    /// <summary>
+    /// Returns the committed version of the chunk ignoring any uncommitted changes
+    /// </summary>
+    private Chunk? GetCommittedChunk(Vector2i pos, ChunkResolution resolution)
+    {
+        var maybeSameRes = MaybeGetCommittedChunk(pos, resolution);
+        if (maybeSameRes is not null)
+            return maybeSameRes;
 
-            var maybeFullRes = MaybeGetCommittedChunk(pos, ChunkResolution.Full);
-            if (maybeFullRes is not null)
-                return GetOrCreateCommittedChunk(pos, resolution);
+        var maybeFullRes = MaybeGetCommittedChunk(pos, ChunkResolution.Full);
+        if (maybeFullRes is not null)
+            return GetOrCreateCommittedChunk(pos, resolution);
 
-            return null;
-        }
+        return null;
+    }
 
-        private Chunk? MaybeGetLatestChunk(Vector2i pos, ChunkResolution resolution) => latestChunks[resolution].TryGetValue(pos, out Chunk? value) ? value : null;
-        private Chunk? MaybeGetCommittedChunk(Vector2i pos, ChunkResolution resolution) => committedChunks[resolution].TryGetValue(pos, out Chunk? value) ? value : null;
+    private Chunk? MaybeGetLatestChunk(Vector2i pos, ChunkResolution resolution) => latestChunks[resolution].TryGetValue(pos, out Chunk? value) ? value : null;
+    private Chunk? MaybeGetCommittedChunk(Vector2i pos, ChunkResolution resolution) => committedChunks[resolution].TryGetValue(pos, out Chunk? value) ? value : null;
 
-        public void DrawRectangle(ShapeData rect)
+    public void DrawRectangle(ShapeData rect)
+    {
+        lock (lockObject)
         {
-            lock (lockObject)
-            {
-                RectangleOperation operation = new(rect);
-                EnqueueOperation(operation);
-            }
+            RectangleOperation operation = new(rect);
+            EnqueueOperation(operation);
         }
+    }
 
-        public void DrawImage(Vector2i pos, Surface image)
+    public void DrawImage(Vector2i pos, Surface image)
+    {
+        lock (lockObject)
         {
-            lock (lockObject)
-            {
-                ImageOperation operation = new(pos, image);
-                EnqueueOperation(operation);
-            }
+            ImageOperation operation = new(pos, image);
+            EnqueueOperation(operation);
         }
+    }
 
-        public void ClearRegion(Vector2i pos, Vector2i size)
+    public void ClearRegion(Vector2i pos, Vector2i size)
+    {
+        lock (lockObject)
         {
-            lock (lockObject)
-            {
-                ClearRegionOperation operation = new(pos, size);
-                EnqueueOperation(operation);
-            }
+            ClearRegionOperation operation = new(pos, size);
+            EnqueueOperation(operation);
         }
+    }
 
-        public void Clear()
+    public void Clear()
+    {
+        lock (lockObject)
         {
-            lock (lockObject)
-            {
-                ClearOperation operation = new();
-                EnqueueOperation(operation, FindAllChunks());
-            }
+            ClearOperation operation = new();
+            EnqueueOperation(operation, FindAllChunks());
         }
+    }
 
-        public void ApplyRasterClip(ChunkyImage clippingMask)
+    public void ApplyRasterClip(ChunkyImage clippingMask)
+    {
+        lock (lockObject)
         {
-            lock (lockObject)
-            {
-                RasterClipOperation operation = new(clippingMask);
-                EnqueueOperation(operation, new());
-            }
+            RasterClipOperation operation = new(clippingMask);
+            EnqueueOperation(operation, new());
         }
+    }
 
-        public void Resize(Vector2i newSize)
+    public void Resize(Vector2i newSize)
+    {
+        lock (lockObject)
         {
-            lock (lockObject)
-            {
-                ResizeOperation operation = new(newSize);
-                LatestSize = newSize;
-                EnqueueOperation(operation, FindAllChunksOutsideBounds(newSize));
-            }
+            ResizeOperation operation = new(newSize);
+            LatestSize = newSize;
+            EnqueueOperation(operation, FindAllChunksOutsideBounds(newSize));
         }
+    }
 
-        private void EnqueueOperation(IDrawOperation operation)
-        {
-            var chunks = operation.FindAffectedChunks();
-            chunks.RemoveWhere(pos => IsOutsideBounds(pos, LatestSize));
-            if (operation.IgnoreEmptyChunks)
-                chunks.IntersectWith(FindAllChunks());
-            EnqueueOperation(operation, chunks);
-        }
-        private void EnqueueOperation(IOperation operation, HashSet<Vector2i> chunks)
-        {
-            queuedOperations.Add((operation, chunks));
-        }
+    private void EnqueueOperation(IDrawOperation operation)
+    {
+        var chunks = operation.FindAffectedChunks();
+        chunks.RemoveWhere(pos => IsOutsideBounds(pos, LatestSize));
+        if (operation.IgnoreEmptyChunks)
+            chunks.IntersectWith(FindAllChunks());
+        EnqueueOperation(operation, chunks);
+    }
+    private void EnqueueOperation(IOperation operation, HashSet<Vector2i> chunks)
+    {
+        queuedOperations.Add((operation, chunks));
+    }
 
-        public void CancelChanges()
+    public void CancelChanges()
+    {
+        lock (lockObject)
         {
-            lock (lockObject)
-            {
-                //clear queued operations
-                foreach (var operation in queuedOperations)
-                    operation.Item1.Dispose();
-                queuedOperations.Clear();
+            //clear queued operations
+            foreach (var operation in queuedOperations)
+                operation.Item1.Dispose();
+            queuedOperations.Clear();
 
-                //clear latest chunks
-                foreach (var (_, chunksOfRes) in latestChunks)
-                {
-                    foreach (var (_, chunk) in chunksOfRes)
-                    {
-                        chunk.Dispose();
-                    }
-                }
-                LatestSize = CommittedSize;
-                foreach (var (res, chunks) in latestChunks)
+            //clear latest chunks
+            foreach (var (_, chunksOfRes) in latestChunks)
+            {
+                foreach (var (_, chunk) in chunksOfRes)
                 {
-                    chunks.Clear();
-                    latestChunksData[res].Clear();
+                    chunk.Dispose();
                 }
             }
+            LatestSize = CommittedSize;
+            foreach (var (res, chunks) in latestChunks)
+            {
+                chunks.Clear();
+                latestChunksData[res].Clear();
+            }
         }
+    }
 
-        public void CommitChanges()
+    public void CommitChanges()
+    {
+        lock (lockObject)
         {
-            lock (lockObject)
+            var affectedChunks = FindAffectedChunks();
+            foreach (var chunk in affectedChunks)
             {
-                var affectedChunks = FindAffectedChunks();
-                foreach (var chunk in affectedChunks)
-                {
-                    MaybeCreateAndProcessQueueForChunk(chunk, ChunkResolution.Full);
-                }
-                foreach (var (operation, _) in queuedOperations)
-                {
-                    operation.Dispose();
-                }
-                CommitLatestChunks();
-                CommittedSize = LatestSize;
-                queuedOperations.Clear();
-
-                commitCounter++;
-                if (commitCounter % 30 == 0)
-                    FindAndDeleteEmptyCommittedChunks();
+                MaybeCreateAndProcessQueueForChunk(chunk, ChunkResolution.Full);
+            }
+            foreach (var (operation, _) in queuedOperations)
+            {
+                operation.Dispose();
             }
+            CommitLatestChunks();
+            CommittedSize = LatestSize;
+            queuedOperations.Clear();
+
+            commitCounter++;
+            if (commitCounter % 30 == 0)
+                FindAndDeleteEmptyCommittedChunks();
         }
+    }
 
-        private void CommitLatestChunks()
+    private void CommitLatestChunks()
+    {
+        // move fully processed latest chunks to committed
+        foreach (var (resolution, chunks) in latestChunks)
         {
-            // move fully processed latest chunks to committed
-            foreach (var (resolution, chunks) in latestChunks)
+            foreach (var (pos, chunk) in chunks)
             {
-                foreach (var (pos, chunk) in chunks)
+                if (committedChunks[resolution].ContainsKey(pos))
                 {
-                    if (committedChunks[resolution].ContainsKey(pos))
-                    {
-                        var oldChunk = committedChunks[resolution][pos];
-                        committedChunks[resolution].Remove(pos);
-                        oldChunk.Dispose();
-                    }
+                    var oldChunk = committedChunks[resolution][pos];
+                    committedChunks[resolution].Remove(pos);
+                    oldChunk.Dispose();
+                }
 
-                    LatestChunkData data = latestChunksData[resolution][pos];
-                    if (data.QueueProgress != queuedOperations.Count)
+                LatestChunkData data = latestChunksData[resolution][pos];
+                if (data.QueueProgress != queuedOperations.Count)
+                {
+                    if (resolution == ChunkResolution.Full)
                     {
-                        if (resolution == ChunkResolution.Full)
-                        {
-                            throw new InvalidOperationException("Trying to commit a full res chunk that wasn't fully processed");
-                        }
-                        else
-                        {
-                            chunk.Dispose();
-                            continue;
-                        }
+                        throw new InvalidOperationException("Trying to commit a full res chunk that wasn't fully processed");
                     }
-
-                    if (!data.IsDeleted)
-                        committedChunks[resolution].Add(pos, chunk);
                     else
+                    {
                         chunk.Dispose();
+                        continue;
+                    }
                 }
+
+                if (!data.IsDeleted)
+                    committedChunks[resolution].Add(pos, chunk);
+                else
+                    chunk.Dispose();
             }
+        }
 
-            // delete committed low res chunks that weren't updated
-            foreach (var (pos, chunk) in latestChunks[ChunkResolution.Full])
+        // delete committed low res chunks that weren't updated
+        foreach (var (pos, chunk) in latestChunks[ChunkResolution.Full])
+        {
+            foreach (var (resolution, _) in latestChunks)
             {
-                foreach (var (resolution, _) in latestChunks)
+                if (resolution == ChunkResolution.Full)
+                    continue;
+                if (!latestChunksData[resolution].TryGetValue(pos, out var halfChunk) || halfChunk.QueueProgress != queuedOperations.Count)
                 {
-                    if (resolution == ChunkResolution.Full)
-                        continue;
-                    if (!latestChunksData[resolution].TryGetValue(pos, out var halfChunk) || halfChunk.QueueProgress != queuedOperations.Count)
+                    if (committedChunks[resolution].TryGetValue(pos, out var commitedLowResChunk))
                     {
-                        if (committedChunks[resolution].TryGetValue(pos, out var commitedLowResChunk))
-                        {
-                            committedChunks[resolution].Remove(pos);
-                            commitedLowResChunk.Dispose();
-                        }
+                        committedChunks[resolution].Remove(pos);
+                        commitedLowResChunk.Dispose();
                     }
                 }
             }
+        }
 
-            // clear latest chunks
-            foreach (var (resolution, chunks) in latestChunks)
-            {
-                chunks.Clear();
-                latestChunksData[resolution].Clear();
-            }
+        // clear latest chunks
+        foreach (var (resolution, chunks) in latestChunks)
+        {
+            chunks.Clear();
+            latestChunksData[resolution].Clear();
         }
+    }
 
-        /// <summary>
-        /// Returns all chunks that have something in them, including latest (uncommitted) ones
-        /// </summary>
-        public HashSet<Vector2i> FindAllChunks()
+    /// <summary>
+    /// Returns all chunks that have something in them, including latest (uncommitted) ones
+    /// </summary>
+    public HashSet<Vector2i> FindAllChunks()
+    {
+        lock (lockObject)
         {
-            lock (lockObject)
+            var allChunks = committedChunks[ChunkResolution.Full].Select(chunk => chunk.Key).ToHashSet();
+            foreach (var (operation, opChunks) in queuedOperations)
             {
-                var allChunks = committedChunks[ChunkResolution.Full].Select(chunk => chunk.Key).ToHashSet();
-                foreach (var (operation, opChunks) in queuedOperations)
-                {
-                    allChunks.UnionWith(opChunks);
-                }
-                return allChunks;
+                allChunks.UnionWith(opChunks);
             }
+            return allChunks;
         }
+    }
 
-        /// <summary>
-        /// Returns chunks affected by operations that haven't been committed yet
-        /// </summary>
-        public HashSet<Vector2i> FindAffectedChunks()
+    /// <summary>
+    /// Returns chunks affected by operations that haven't been committed yet
+    /// </summary>
+    public HashSet<Vector2i> FindAffectedChunks()
+    {
+        lock (lockObject)
         {
-            lock (lockObject)
+            var chunks = new HashSet<Vector2i>();
+            foreach (var (_, opChunks) in queuedOperations)
             {
-                var chunks = new HashSet<Vector2i>();
-                foreach (var (_, opChunks) in queuedOperations)
-                {
-                    chunks.UnionWith(opChunks);
-                }
-                return chunks;
+                chunks.UnionWith(opChunks);
             }
+            return chunks;
         }
+    }
 
-        private void MaybeCreateAndProcessQueueForChunk(Vector2i chunkPos, ChunkResolution resolution)
-        {
-            if (!latestChunksData[resolution].TryGetValue(chunkPos, out LatestChunkData chunkData))
-                chunkData = new() { QueueProgress = 0, IsDeleted = !committedChunks[ChunkResolution.Full].ContainsKey(chunkPos) };
-            if (chunkData.QueueProgress == queuedOperations.Count)
-                return;
+    private void MaybeCreateAndProcessQueueForChunk(Vector2i chunkPos, ChunkResolution resolution)
+    {
+        if (!latestChunksData[resolution].TryGetValue(chunkPos, out LatestChunkData chunkData))
+            chunkData = new() { QueueProgress = 0, IsDeleted = !committedChunks[ChunkResolution.Full].ContainsKey(chunkPos) };
+        if (chunkData.QueueProgress == queuedOperations.Count)
+            return;
 
-            Chunk? targetChunk = null;
-            List<ChunkyImage> activeClips = new();
-            bool isFullyMaskedOut = false;
-            bool somethingWasApplied = false;
-            for (int i = 0; i < queuedOperations.Count; i++)
+        Chunk? targetChunk = null;
+        List<ChunkyImage> activeClips = new();
+        bool isFullyMaskedOut = false;
+        bool somethingWasApplied = false;
+        for (int i = 0; i < queuedOperations.Count; i++)
+        {
+            var (operation, operChunks) = queuedOperations[i];
+            if (operation is RasterClipOperation clipOperation)
             {
-                var (operation, operChunks) = queuedOperations[i];
-                if (operation is RasterClipOperation clipOperation)
-                {
-                    if (clipOperation.ClippingMask.CommitedChunkExists(chunkPos, resolution))
-                        activeClips.Add(clipOperation.ClippingMask);
-                    else
-                        isFullyMaskedOut = true;
-                }
-
-                if (!operChunks.Contains(chunkPos))
-                    continue;
-                if (!somethingWasApplied)
-                {
-                    somethingWasApplied = true;
-                    targetChunk = GetOrCreateLatestChunk(chunkPos, resolution);
-                }
-
-                if (chunkData.QueueProgress <= i)
-                    chunkData.IsDeleted = ApplyOperationToChunk(operation, activeClips, isFullyMaskedOut, targetChunk!, chunkPos, resolution, chunkData);
+                if (clipOperation.ClippingMask.CommitedChunkExists(chunkPos, resolution))
+                    activeClips.Add(clipOperation.ClippingMask);
+                else
+                    isFullyMaskedOut = true;
             }
 
-            if (somethingWasApplied)
+            if (!operChunks.Contains(chunkPos))
+                continue;
+            if (!somethingWasApplied)
             {
-                chunkData.QueueProgress = queuedOperations.Count;
-                latestChunksData[resolution][chunkPos] = chunkData;
+                somethingWasApplied = true;
+                targetChunk = GetOrCreateLatestChunk(chunkPos, resolution);
             }
+
+            if (chunkData.QueueProgress <= i)
+                chunkData.IsDeleted = ApplyOperationToChunk(operation, activeClips, isFullyMaskedOut, targetChunk!, chunkPos, resolution, chunkData);
         }
 
-        private bool ApplyOperationToChunk(
-            IOperation operation,
-            List<ChunkyImage> activeClips,
-            bool isFullyMaskedOut,
-            Chunk targetChunk,
-            Vector2i chunkPos,
-            ChunkResolution resolution,
-            LatestChunkData chunkData)
+        if (somethingWasApplied)
         {
-            if (operation is ClearOperation)
-                return true;
+            chunkData.QueueProgress = queuedOperations.Count;
+            latestChunksData[resolution][chunkPos] = chunkData;
+        }
+    }
 
-            if (operation is IDrawOperation chunkOperation)
-            {
-                if (isFullyMaskedOut)
-                    return chunkData.IsDeleted;
+    private bool ApplyOperationToChunk(
+        IOperation operation,
+        List<ChunkyImage> activeClips,
+        bool isFullyMaskedOut,
+        Chunk targetChunk,
+        Vector2i chunkPos,
+        ChunkResolution resolution,
+        LatestChunkData chunkData)
+    {
+        if (operation is ClearOperation)
+            return true;
 
-                if (chunkData.IsDeleted)
-                    targetChunk.Surface.SkiaSurface.Canvas.Clear();
-                if (activeClips.Count == 0)
-                {
-                    chunkOperation.DrawOnChunk(targetChunk, chunkPos);
-                    return false;
-                }
+        if (operation is IDrawOperation chunkOperation)
+        {
+            if (isFullyMaskedOut)
+                return chunkData.IsDeleted;
 
-                var tempChunk = tempRasterClipChunks[targetChunk.Resolution];
-                tempChunk.Surface.SkiaSurface.Canvas.Clear();
-                chunkOperation.DrawOnChunk(tempChunk, chunkPos);
-                foreach (var mask in activeClips)
-                {
-                    mask.DrawCommittedChunkOn(chunkPos, resolution, tempChunk.Surface.SkiaSurface, new(0, 0), ClippingPaint);
-                }
-                tempChunk.DrawOnSurface(targetChunk.Surface.SkiaSurface, new(0, 0));
+            if (chunkData.IsDeleted)
+                targetChunk.Surface.SkiaSurface.Canvas.Clear();
+            if (activeClips.Count == 0)
+            {
+                chunkOperation.DrawOnChunk(targetChunk, chunkPos);
                 return false;
             }
 
-            if (operation is ResizeOperation resizeOperation)
+            var tempChunk = tempRasterClipChunks[targetChunk.Resolution];
+            tempChunk.Surface.SkiaSurface.Canvas.Clear();
+            chunkOperation.DrawOnChunk(tempChunk, chunkPos);
+            foreach (var mask in activeClips)
             {
-                return IsOutsideBounds(chunkPos, resizeOperation.Size);
+                mask.DrawCommittedChunkOn(chunkPos, resolution, tempChunk.Surface.SkiaSurface, new(0, 0), ClippingPaint);
             }
-            return chunkData.IsDeleted;
+            tempChunk.DrawOnSurface(targetChunk.Surface.SkiaSurface, new(0, 0));
+            return false;
         }
 
-        /// <summary>
-        /// Note: this function modifies the internal state, it is not thread safe! (same as all the other functions that change the image in some way)
-        /// </summary>
-        public bool CheckIfCommittedIsEmpty()
+        if (operation is ResizeOperation resizeOperation)
         {
-            lock (lockObject)
-            {
-                FindAndDeleteEmptyCommittedChunks();
-                return committedChunks[ChunkResolution.Full].Count == 0;
-            }
+            return IsOutsideBounds(chunkPos, resizeOperation.Size);
         }
+        return chunkData.IsDeleted;
+    }
 
-        private HashSet<Vector2i> FindAllChunksOutsideBounds(Vector2i size)
+    /// <summary>
+    /// Note: this function modifies the internal state, it is not thread safe! (same as all the other functions that change the image in some way)
+    /// </summary>
+    public bool CheckIfCommittedIsEmpty()
+    {
+        lock (lockObject)
         {
-            var chunks = FindAllChunks();
-            chunks.RemoveWhere(pos => !IsOutsideBounds(pos, size));
-            return chunks;
+            FindAndDeleteEmptyCommittedChunks();
+            return committedChunks[ChunkResolution.Full].Count == 0;
         }
+    }
 
-        private static bool IsOutsideBounds(Vector2i chunkPos, Vector2i imageSize)
-        {
-            return chunkPos.X < 0 || chunkPos.Y < 0 || chunkPos.X * ChunkSize >= imageSize.X || chunkPos.Y * ChunkSize >= imageSize.Y;
-        }
+    private HashSet<Vector2i> FindAllChunksOutsideBounds(Vector2i size)
+    {
+        var chunks = FindAllChunks();
+        chunks.RemoveWhere(pos => !IsOutsideBounds(pos, size));
+        return chunks;
+    }
+
+    private static bool IsOutsideBounds(Vector2i chunkPos, Vector2i imageSize)
+    {
+        return chunkPos.X < 0 || chunkPos.Y < 0 || chunkPos.X * ChunkSize >= imageSize.X || chunkPos.Y * ChunkSize >= imageSize.Y;
+    }
 
-        private void FindAndDeleteEmptyCommittedChunks()
+    private void FindAndDeleteEmptyCommittedChunks()
+    {
+        if (queuedOperations.Count != 0)
+            throw new InvalidOperationException("This method cannot be used while any operations are queued");
+        HashSet<Vector2i> toRemove = new();
+        foreach (var (pos, chunk) in committedChunks[ChunkResolution.Full])
         {
-            if (queuedOperations.Count != 0)
-                throw new InvalidOperationException("This method cannot be used while any operations are queued");
-            HashSet<Vector2i> toRemove = new();
-            foreach (var (pos, chunk) in committedChunks[ChunkResolution.Full])
-            {
-                if (chunk.Surface.IsFullyTransparent())
-                {
-                    toRemove.Add(pos);
-                    chunk.Dispose();
-                }
-            }
-            foreach (var pos in toRemove)
+            if (chunk.Surface.IsFullyTransparent())
             {
-                committedChunks[ChunkResolution.Full].Remove(pos);
-                committedChunks[ChunkResolution.Half].Remove(pos);
-                committedChunks[ChunkResolution.Quarter].Remove(pos);
-                committedChunks[ChunkResolution.Eighth].Remove(pos);
+                toRemove.Add(pos);
+                chunk.Dispose();
             }
         }
-
-        private Chunk GetOrCreateCommittedChunk(Vector2i chunkPos, ChunkResolution resolution)
+        foreach (var pos in toRemove)
         {
-            // commited chunk of the same resolution exists
-            Chunk? targetChunk = MaybeGetCommittedChunk(chunkPos, resolution);
-            if (targetChunk is not null)
-                return targetChunk;
-
-            // for full res chunks: nothing exists, create brand new chunk
-            if (resolution == ChunkResolution.Full)
-            {
-                var newChunk = Chunk.Create(resolution);
-                committedChunks[resolution][chunkPos] = newChunk;
-                return newChunk;
-            }
+            committedChunks[ChunkResolution.Full].Remove(pos);
+            committedChunks[ChunkResolution.Half].Remove(pos);
+            committedChunks[ChunkResolution.Quarter].Remove(pos);
+            committedChunks[ChunkResolution.Eighth].Remove(pos);
+        }
+    }
 
-            // for low res chunks: full res version exists
-            Chunk? existingFullResChunk = MaybeGetCommittedChunk(chunkPos, ChunkResolution.Full);
-            if (resolution != ChunkResolution.Full && existingFullResChunk is not null)
-            {
-                var newChunk = Chunk.Create(resolution);
-                newChunk.Surface.SkiaSurface.Canvas.Save();
-                newChunk.Surface.SkiaSurface.Canvas.Scale((float)resolution.Multiplier());
-
-                newChunk.Surface.SkiaSurface.Canvas.DrawSurface(existingFullResChunk!.Surface.SkiaSurface, 0, 0, ReplacingPaint);
-                newChunk.Surface.SkiaSurface.Canvas.Restore();
-                committedChunks[resolution][chunkPos] = newChunk;
-                return newChunk;
-            }
+    private Chunk GetOrCreateCommittedChunk(Vector2i chunkPos, ChunkResolution resolution)
+    {
+        // commited chunk of the same resolution exists
+        Chunk? targetChunk = MaybeGetCommittedChunk(chunkPos, resolution);
+        if (targetChunk is not null)
+            return targetChunk;
 
-            // for low res chunks: full res version doesn't exist
-            {
-                GetOrCreateCommittedChunk(chunkPos, ChunkResolution.Full);
-                var newChunk = Chunk.Create(resolution);
-                committedChunks[resolution][chunkPos] = newChunk;
-                return newChunk;
-            }
+        // for full res chunks: nothing exists, create brand new chunk
+        if (resolution == ChunkResolution.Full)
+        {
+            var newChunk = Chunk.Create(resolution);
+            committedChunks[resolution][chunkPos] = newChunk;
+            return newChunk;
         }
 
-        private Chunk GetOrCreateLatestChunk(Vector2i chunkPos, ChunkResolution resolution)
+        // for low res chunks: full res version exists
+        Chunk? existingFullResChunk = MaybeGetCommittedChunk(chunkPos, ChunkResolution.Full);
+        if (resolution != ChunkResolution.Full && existingFullResChunk is not null)
         {
-            // latest chunk exists
-            Chunk? targetChunk;
-            targetChunk = MaybeGetLatestChunk(chunkPos, resolution);
-            if (targetChunk is not null)
-                return targetChunk;
+            var newChunk = Chunk.Create(resolution);
+            newChunk.Surface.SkiaSurface.Canvas.Save();
+            newChunk.Surface.SkiaSurface.Canvas.Scale((float)resolution.Multiplier());
 
-            // committed chunk of the same resolution exists
-            var maybeCommittedAnyRes = MaybeGetCommittedChunk(chunkPos, resolution);
-            if (maybeCommittedAnyRes is not null)
-            {
-                Chunk newChunk = Chunk.Create(resolution);
-                maybeCommittedAnyRes.Surface.CopyTo(newChunk.Surface);
-                latestChunks[resolution][chunkPos] = newChunk;
-                return newChunk;
-            }
-
-            // committed chunk of full resolution exists
-            var maybeCommittedFullRes = MaybeGetCommittedChunk(chunkPos, ChunkResolution.Full);
-            if (maybeCommittedFullRes is not null)
-            {
-                //create low res committed chunk
-                var committedChunkLowRes = GetOrCreateCommittedChunk(chunkPos, resolution);
-                //create latest based on it
-                Chunk newChunk = Chunk.Create(resolution);
-                committedChunkLowRes.Surface.CopyTo(newChunk.Surface);
-                latestChunks[resolution][chunkPos] = newChunk;
-                return newChunk;
-            }
+            newChunk.Surface.SkiaSurface.Canvas.DrawSurface(existingFullResChunk!.Surface.SkiaSurface, 0, 0, ReplacingPaint);
+            newChunk.Surface.SkiaSurface.Canvas.Restore();
+            committedChunks[resolution][chunkPos] = newChunk;
+            return newChunk;
+        }
 
-            // no previous chunks exist
-            var newLatestChunk = Chunk.Create(resolution);
-            newLatestChunk.Surface.SkiaSurface.Canvas.Clear();
-            latestChunks[resolution][chunkPos] = newLatestChunk;
-            return newLatestChunk;
+        // for low res chunks: full res version doesn't exist
+        {
+            GetOrCreateCommittedChunk(chunkPos, ChunkResolution.Full);
+            var newChunk = Chunk.Create(resolution);
+            committedChunks[resolution][chunkPos] = newChunk;
+            return newChunk;
         }
+    }
 
-        public void Dispose()
+    private Chunk GetOrCreateLatestChunk(Vector2i chunkPos, ChunkResolution resolution)
+    {
+        // latest chunk exists
+        Chunk? targetChunk;
+        targetChunk = MaybeGetLatestChunk(chunkPos, resolution);
+        if (targetChunk is not null)
+            return targetChunk;
+
+        // committed chunk of the same resolution exists
+        var maybeCommittedAnyRes = MaybeGetCommittedChunk(chunkPos, resolution);
+        if (maybeCommittedAnyRes is not null)
+        {
+            Chunk newChunk = Chunk.Create(resolution);
+            maybeCommittedAnyRes.Surface.CopyTo(newChunk.Surface);
+            latestChunks[resolution][chunkPos] = newChunk;
+            return newChunk;
+        }
+
+        // committed chunk of full resolution exists
+        var maybeCommittedFullRes = MaybeGetCommittedChunk(chunkPos, ChunkResolution.Full);
+        if (maybeCommittedFullRes is not null)
+        {
+            //create low res committed chunk
+            var committedChunkLowRes = GetOrCreateCommittedChunk(chunkPos, resolution);
+            //create latest based on it
+            Chunk newChunk = Chunk.Create(resolution);
+            committedChunkLowRes.Surface.CopyTo(newChunk.Surface);
+            latestChunks[resolution][chunkPos] = newChunk;
+            return newChunk;
+        }
+
+        // no previous chunks exist
+        var newLatestChunk = Chunk.Create(resolution);
+        newLatestChunk.Surface.SkiaSurface.Canvas.Clear();
+        latestChunks[resolution][chunkPos] = newLatestChunk;
+        return newLatestChunk;
+    }
+
+    public void Dispose()
+    {
+        lock (lockObject)
         {
-            lock (lockObject)
-            {
-                if (disposed)
-                    return;
-                CancelChanges();
-                DisposeAll();
-                GC.SuppressFinalize(this);
-            }
+            if (disposed)
+                return;
+            CancelChanges();
+            DisposeAll();
+            GC.SuppressFinalize(this);
         }
+    }
 
-        private void DisposeAll()
+    private void DisposeAll()
+    {
+        foreach (var (_, chunk) in tempRasterClipChunks)
+            chunk.Dispose();
+        foreach (var (_, chunks) in committedChunks)
         {
-            foreach (var (_, chunk) in tempRasterClipChunks)
-                chunk.Dispose();
-            foreach (var (_, chunks) in committedChunks)
+            foreach (var (_, chunk) in chunks)
             {
-                foreach (var (_, chunk) in chunks)
-                {
-                    chunk.Dispose();
-                }
-            }
-            foreach (var (_, chunks) in latestChunks)
-            {
-                foreach (var (_, chunk) in chunks)
-                {
-                    chunk.Dispose();
-                }
+                chunk.Dispose();
             }
-            disposed = true;
         }
-        ~ChunkyImage()
+        foreach (var (_, chunks) in latestChunks)
         {
-            DisposeAll();
+            foreach (var (_, chunk) in chunks)
+            {
+                chunk.Dispose();
+            }
         }
+        disposed = true;
+    }
+    ~ChunkyImage()
+    {
+        DisposeAll();
     }
 }

+ 33 - 34
src/ChunkyImageLib/CommittedChunkStorage.cs

@@ -1,51 +1,50 @@
 using ChunkyImageLib.DataHolders;
 using SkiaSharp;
 
-namespace ChunkyImageLib
+namespace ChunkyImageLib;
+
+public class CommittedChunkStorage : IDisposable
 {
-    public class CommittedChunkStorage : IDisposable
-    {
-        private bool disposed = false;
-        private List<(Vector2i, Chunk?)> savedChunks = new();
-        private static SKPaint ReplacingPaint { get; } = new SKPaint() { BlendMode = SKBlendMode.Src };
+    private bool disposed = false;
+    private List<(Vector2i, Chunk?)> savedChunks = new();
+    private static SKPaint ReplacingPaint { get; } = new SKPaint() { BlendMode = SKBlendMode.Src };
 
-        public CommittedChunkStorage(ChunkyImage image, HashSet<Vector2i> committedChunksToSave)
+    public CommittedChunkStorage(ChunkyImage image, HashSet<Vector2i> committedChunksToSave)
+    {
+        foreach (var chunkPos in committedChunksToSave)
         {
-            foreach (var chunkPos in committedChunksToSave)
+            Chunk copy = Chunk.Create();
+            if (!image.DrawCommittedChunkOn(chunkPos, ChunkResolution.Full, copy.Surface.SkiaSurface, new(0, 0), ReplacingPaint))
             {
-                Chunk copy = Chunk.Create();
-                if (!image.DrawCommittedChunkOn(chunkPos, ChunkResolution.Full, copy.Surface.SkiaSurface, new(0, 0), ReplacingPaint))
-                {
-                    copy.Dispose();
-                    savedChunks.Add((chunkPos, null));
-                    continue;
-                }
-                savedChunks.Add((chunkPos, copy));
+                copy.Dispose();
+                savedChunks.Add((chunkPos, null));
+                continue;
             }
+            savedChunks.Add((chunkPos, copy));
         }
+    }
 
-        public void ApplyChunksToImage(ChunkyImage image)
+    public void ApplyChunksToImage(ChunkyImage image)
+    {
+        if (disposed)
+            throw new ObjectDisposedException(nameof(CommittedChunkStorage));
+        foreach (var (pos, chunk) in savedChunks)
         {
-            if (disposed)
-                throw new ObjectDisposedException(nameof(CommittedChunkStorage));
-            foreach (var (pos, chunk) in savedChunks)
-            {
-                if (chunk is null)
-                    image.ClearRegion(pos * ChunkPool.FullChunkSize, new(ChunkPool.FullChunkSize, ChunkPool.FullChunkSize));
-                else
-                    image.DrawImage(pos * ChunkPool.FullChunkSize, chunk.Surface);
-            }
+            if (chunk is null)
+                image.ClearRegion(pos * ChunkPool.FullChunkSize, new(ChunkPool.FullChunkSize, ChunkPool.FullChunkSize));
+            else
+                image.DrawImage(pos * ChunkPool.FullChunkSize, chunk.Surface);
         }
+    }
 
-        public void Dispose()
+    public void Dispose()
+    {
+        if (disposed)
+            return;
+        foreach (var (_, chunk) in savedChunks)
         {
-            if (disposed)
-                return;
-            foreach (var (_, chunk) in savedChunks)
-            {
-                if (chunk is not null)
-                    chunk.Dispose();
-            }
+            if (chunk is not null)
+                chunk.Dispose();
         }
     }
 }

+ 8 - 9
src/ChunkyImageLib/DataHolders/ChunkResolution.cs

@@ -1,11 +1,10 @@
-namespace ChunkyImageLib.DataHolders
+namespace ChunkyImageLib.DataHolders;
+
+[Flags]
+public enum ChunkResolution
 {
-    [Flags]
-    public enum ChunkResolution
-    {
-        Full = 1,
-        Half = 2,
-        Quarter = 4,
-        Eighth = 8
-    }
+    Full = 1,
+    Half = 2,
+    Quarter = 4,
+    Eighth = 8
 }

+ 21 - 22
src/ChunkyImageLib/DataHolders/ChunkResolutionEx.cs

@@ -1,29 +1,28 @@
-namespace ChunkyImageLib.DataHolders
+namespace ChunkyImageLib.DataHolders;
+
+public static class ChunkResolutionEx
 {
-    public static class ChunkResolutionEx
+    public static double Multiplier(this ChunkResolution resolution)
     {
-        public static double Multiplier(this ChunkResolution resolution)
+        return resolution switch
         {
-            return resolution switch
-            {
-                ChunkResolution.Full => 1.0,
-                ChunkResolution.Half => 1.0 / 2,
-                ChunkResolution.Quarter => 1.0 / 4,
-                ChunkResolution.Eighth => 1.0 / 8,
-                _ => 1,
-            };
-        }
+            ChunkResolution.Full => 1.0,
+            ChunkResolution.Half => 1.0 / 2,
+            ChunkResolution.Quarter => 1.0 / 4,
+            ChunkResolution.Eighth => 1.0 / 8,
+            _ => 1,
+        };
+    }
 
-        public static int PixelSize(this ChunkResolution resolution)
+    public static int PixelSize(this ChunkResolution resolution)
+    {
+        return resolution switch
         {
-            return resolution switch
-            {
-                ChunkResolution.Full => ChunkPool.FullChunkSize,
-                ChunkResolution.Half => ChunkPool.FullChunkSize / 2,
-                ChunkResolution.Quarter => ChunkPool.FullChunkSize / 4,
-                ChunkResolution.Eighth => ChunkPool.FullChunkSize / 8,
-                _ => ChunkPool.FullChunkSize
-            };
-        }
+            ChunkResolution.Full => ChunkPool.FullChunkSize,
+            ChunkResolution.Half => ChunkPool.FullChunkSize / 2,
+            ChunkResolution.Quarter => ChunkPool.FullChunkSize / 4,
+            ChunkResolution.Eighth => ChunkPool.FullChunkSize / 8,
+            _ => ChunkPool.FullChunkSize
+        };
     }
 }

+ 17 - 18
src/ChunkyImageLib/DataHolders/RotatedShapeData.cs

@@ -1,24 +1,23 @@
 using SkiaSharp;
 
-namespace ChunkyImageLib.DataHolders
+namespace ChunkyImageLib.DataHolders;
+
+public record struct RotatedShapeData
 {
-    public record struct RotatedShapeData
+    public RotatedShapeData(Vector2d center, Vector2d size, float angle, int strokeWidth, SKColor strokeColor, SKColor fillColor)
     {
-        public RotatedShapeData(Vector2d center, Vector2d size, float angle, int strokeWidth, SKColor strokeColor, SKColor fillColor)
-        {
-            Center = center;
-            Size = size;
-            Angle = angle;
-            StrokeColor = strokeColor;
-            FillColor = fillColor;
-            StrokeWidth = strokeWidth;
-        }
-
-        public Vector2d Center { get; }
-        public float Angle { get; }
-        public Vector2d Size { get; }
-        public SKColor StrokeColor { get; }
-        public SKColor FillColor { get; }
-        public int StrokeWidth { get; }
+        Center = center;
+        Size = size;
+        Angle = angle;
+        StrokeColor = strokeColor;
+        FillColor = fillColor;
+        StrokeWidth = strokeWidth;
     }
+
+    public Vector2d Center { get; }
+    public float Angle { get; }
+    public Vector2d Size { get; }
+    public SKColor StrokeColor { get; }
+    public SKColor FillColor { get; }
+    public int StrokeWidth { get; }
 }

+ 17 - 18
src/ChunkyImageLib/DataHolders/ShapeData.cs

@@ -1,24 +1,23 @@
 using SkiaSharp;
 
-namespace ChunkyImageLib.DataHolders
+namespace ChunkyImageLib.DataHolders;
+
+public record struct ShapeData
 {
-    public record struct ShapeData
+    public ShapeData(Vector2i pos, Vector2i size, int strokeWidth, SKColor strokeColor, SKColor fillColor)
     {
-        public ShapeData(Vector2i pos, Vector2i size, int strokeWidth, SKColor strokeColor, SKColor fillColor)
-        {
-            Pos = pos;
-            MaxPos = new(pos.X + size.X - 1, pos.Y + size.Y - 1);
-            Size = size;
-            StrokeColor = strokeColor;
-            FillColor = fillColor;
-            StrokeWidth = strokeWidth;
-        }
-
-        public Vector2i Pos { get; }
-        public Vector2i MaxPos { get; }
-        public Vector2i Size { get; }
-        public SKColor StrokeColor { get; }
-        public SKColor FillColor { get; }
-        public int StrokeWidth { get; }
+        Pos = pos;
+        MaxPos = new(pos.X + size.X - 1, pos.Y + size.Y - 1);
+        Size = size;
+        StrokeColor = strokeColor;
+        FillColor = fillColor;
+        StrokeWidth = strokeWidth;
     }
+
+    public Vector2i Pos { get; }
+    public Vector2i MaxPos { get; }
+    public Vector2i Size { get; }
+    public SKColor StrokeColor { get; }
+    public SKColor FillColor { get; }
+    public int StrokeWidth { get; }
 }

+ 146 - 147
src/ChunkyImageLib/DataHolders/Vector2d.cs

@@ -1,164 +1,163 @@
 using SkiaSharp;
 
-namespace ChunkyImageLib.DataHolders
+namespace ChunkyImageLib.DataHolders;
+
+public struct Vector2d
 {
-    public struct Vector2d
-    {
-        public double X { set; get; }
-        public double Y { set; get; }
+    public double X { set; get; }
+    public double Y { set; get; }
 
-        public double TaxicabLength => Math.Abs(X) + Math.Abs(Y);
-        public double Length => Math.Sqrt(LengthSquared);
-        public double LengthSquared => X * X + Y * Y;
-        public double Angle => Y < 0 ? -AngleTo(new Vector2d(1, 0)) : AngleTo(new Vector2d(1, 0));
+    public double TaxicabLength => Math.Abs(X) + Math.Abs(Y);
+    public double Length => Math.Sqrt(LengthSquared);
+    public double LengthSquared => X * X + Y * Y;
+    public double Angle => Y < 0 ? -AngleTo(new Vector2d(1, 0)) : AngleTo(new Vector2d(1, 0));
 
-        public Vector2d(double x, double y)
-        {
-            X = x;
-            Y = y;
-        }
-        public static Vector2d FromAngleAndLength(double angle, double length)
-        {
-            return new Vector2d(Math.Cos(angle) * length, Math.Sin(angle) * length);
-        }
+    public Vector2d(double x, double y)
+    {
+        X = x;
+        Y = y;
+    }
+    public static Vector2d FromAngleAndLength(double angle, double length)
+    {
+        return new Vector2d(Math.Cos(angle) * length, Math.Sin(angle) * length);
+    }
 
-        public Vector2d Rotate(double angle)
-        {
-            Vector2d result = new Vector2d();
-            result.X = X * Math.Cos(angle) - Y * Math.Sin(angle);
-            result.Y = X * Math.Sin(angle) + Y * Math.Cos(angle);
-            return result;
-        }
-        public double DistanceToLineSegment(Vector2d pos1, Vector2d pos2)
-        {
-            Vector2d segment = pos2 - pos1;
-            if ((this - pos1).AngleTo(segment) > Math.PI / 2)
-                return (this - pos1).Length;
-            if ((this - pos2).AngleTo(-segment) > Math.PI / 2)
-                return (this - pos2).Length;
-            return DistanceToLine(pos1, pos2);
-        }
-        public double DistanceToLine(Vector2d pos1, Vector2d pos2)
-        {
-            double a = (pos1 - pos2).Length;
-            double b = (this - pos1).Length;
-            double c = (this - pos2).Length;
+    public Vector2d Rotate(double angle)
+    {
+        Vector2d result = new Vector2d();
+        result.X = X * Math.Cos(angle) - Y * Math.Sin(angle);
+        result.Y = X * Math.Sin(angle) + Y * Math.Cos(angle);
+        return result;
+    }
+    public double DistanceToLineSegment(Vector2d pos1, Vector2d pos2)
+    {
+        Vector2d segment = pos2 - pos1;
+        if ((this - pos1).AngleTo(segment) > Math.PI / 2)
+            return (this - pos1).Length;
+        if ((this - pos2).AngleTo(-segment) > Math.PI / 2)
+            return (this - pos2).Length;
+        return DistanceToLine(pos1, pos2);
+    }
+    public double DistanceToLine(Vector2d pos1, Vector2d pos2)
+    {
+        double a = (pos1 - pos2).Length;
+        double b = (this - pos1).Length;
+        double c = (this - pos2).Length;
 
-            double p = (a + b + c) / 2;
-            double triangleArea = Math.Sqrt(p * (p - a) * (p - b) * (p - c));
+        double p = (a + b + c) / 2;
+        double triangleArea = Math.Sqrt(p * (p - a) * (p - b) * (p - c));
 
-            return triangleArea / a * 2;
-        }
-        public double AngleTo(Vector2d other)
-        {
-            return Math.Acos((this * other) / Length / other.Length);
-        }
+        return triangleArea / a * 2;
+    }
+    public double AngleTo(Vector2d other)
+    {
+        return Math.Acos((this * other) / Length / other.Length);
+    }
 
-        public double CCWAngleTo(Vector2d other)
-        {
-            var rot = other.Rotate(-Angle);
-            return rot.Angle;
-        }
+    public double CCWAngleTo(Vector2d other)
+    {
+        var rot = other.Rotate(-Angle);
+        return rot.Angle;
+    }
 
-        public Vector2d Normalize()
-        {
-            return new Vector2d(X / Length, Y / Length);
-        }
-        public Vector2d Abs()
-        {
-            return new Vector2d(Math.Abs(X), Math.Abs(Y));
-        }
-        public Vector2d Signs()
-        {
-            return new Vector2d(X >= 0 ? 1 : -1, Y >= 0 ? 1 : -1);
-        }
-        public Vector2d Multiply(Vector2d other)
-        {
-            return new Vector2d(X * other.X, Y * other.Y);
-        }
-        public Vector2d Divide(Vector2d other)
-        {
-            return new Vector2d(X / other.X, Y / other.Y);
-        }
-        public static Vector2d operator +(Vector2d a, Vector2d b)
-        {
-            return new Vector2d(a.X + b.X, a.Y + b.Y);
-        }
-        public static Vector2d operator -(Vector2d a, Vector2d b)
-        {
-            return new Vector2d(a.X - b.X, a.Y - b.Y);
-        }
-        public static Vector2d operator -(Vector2d a)
-        {
-            return new Vector2d(-a.X, -a.Y);
-        }
-        public static Vector2d operator *(double b, Vector2d a)
-        {
-            return new Vector2d(a.X * b, a.Y * b);
-        }
-        public static double operator *(Vector2d a, Vector2d b)
-        {
-            return a.X * b.X + a.Y * b.Y;
-        }
-        public static Vector2d operator *(Vector2d a, double b)
-        {
-            return new Vector2d(a.X * b, a.Y * b);
-        }
-        public static Vector2d operator /(Vector2d a, double b)
-        {
-            return new Vector2d(a.X / b, a.Y / b);
-        }
-        public static bool operator ==(Vector2d a, Vector2d b)
-        {
-            return a.X == b.X && a.Y == b.Y;
-        }
-        public static bool operator !=(Vector2d a, Vector2d b)
-        {
-            return !(a.X == b.X && a.Y == b.Y);
-        }
+    public Vector2d Normalize()
+    {
+        return new Vector2d(X / Length, Y / Length);
+    }
+    public Vector2d Abs()
+    {
+        return new Vector2d(Math.Abs(X), Math.Abs(Y));
+    }
+    public Vector2d Signs()
+    {
+        return new Vector2d(X >= 0 ? 1 : -1, Y >= 0 ? 1 : -1);
+    }
+    public Vector2d Multiply(Vector2d other)
+    {
+        return new Vector2d(X * other.X, Y * other.Y);
+    }
+    public Vector2d Divide(Vector2d other)
+    {
+        return new Vector2d(X / other.X, Y / other.Y);
+    }
+    public static Vector2d operator +(Vector2d a, Vector2d b)
+    {
+        return new Vector2d(a.X + b.X, a.Y + b.Y);
+    }
+    public static Vector2d operator -(Vector2d a, Vector2d b)
+    {
+        return new Vector2d(a.X - b.X, a.Y - b.Y);
+    }
+    public static Vector2d operator -(Vector2d a)
+    {
+        return new Vector2d(-a.X, -a.Y);
+    }
+    public static Vector2d operator *(double b, Vector2d a)
+    {
+        return new Vector2d(a.X * b, a.Y * b);
+    }
+    public static double operator *(Vector2d a, Vector2d b)
+    {
+        return a.X * b.X + a.Y * b.Y;
+    }
+    public static Vector2d operator *(Vector2d a, double b)
+    {
+        return new Vector2d(a.X * b, a.Y * b);
+    }
+    public static Vector2d operator /(Vector2d a, double b)
+    {
+        return new Vector2d(a.X / b, a.Y / b);
+    }
+    public static bool operator ==(Vector2d a, Vector2d b)
+    {
+        return a.X == b.X && a.Y == b.Y;
+    }
+    public static bool operator !=(Vector2d a, Vector2d b)
+    {
+        return !(a.X == b.X && a.Y == b.Y);
+    }
 
-        public static explicit operator Vector2i(Vector2d vec)
-        {
-            return new Vector2i((int)vec.X, (int)vec.Y);
-        }
-        public static explicit operator SKPointI(Vector2d vec)
-        {
-            return new SKPointI((int)vec.X, (int)vec.Y);
-        }
-        public static explicit operator SKPoint(Vector2d vec)
-        {
-            return new SKPoint((float)vec.X, (float)vec.Y);
-        }
-        public static explicit operator SKSizeI(Vector2d vec)
-        {
-            return new SKSizeI((int)vec.X, (int)vec.Y);
-        }
-        public static explicit operator SKSize(Vector2d vec)
-        {
-            return new SKSize((float)vec.X, (float)vec.Y);
-        }
+    public static explicit operator Vector2i(Vector2d vec)
+    {
+        return new Vector2i((int)vec.X, (int)vec.Y);
+    }
+    public static explicit operator SKPointI(Vector2d vec)
+    {
+        return new SKPointI((int)vec.X, (int)vec.Y);
+    }
+    public static explicit operator SKPoint(Vector2d vec)
+    {
+        return new SKPoint((float)vec.X, (float)vec.Y);
+    }
+    public static explicit operator SKSizeI(Vector2d vec)
+    {
+        return new SKSizeI((int)vec.X, (int)vec.Y);
+    }
+    public static explicit operator SKSize(Vector2d vec)
+    {
+        return new SKSize((float)vec.X, (float)vec.Y);
+    }
 
-        public bool IsNaNOrInfinity()
-        {
-            return double.IsNaN(X) || double.IsNaN(Y) || double.IsInfinity(X) || double.IsInfinity(Y);
-        }
+    public bool IsNaNOrInfinity()
+    {
+        return double.IsNaN(X) || double.IsNaN(Y) || double.IsInfinity(X) || double.IsInfinity(Y);
+    }
 
-        public override string ToString()
-        {
-            return $"({X}; {Y})";
-        }
+    public override string ToString()
+    {
+        return $"({X}; {Y})";
+    }
 
-        public override bool Equals(object? obj)
-        {
-            var item = obj as Vector2d?;
-            if (item is null)
-                return false;
-            return this == item;
-        }
+    public override bool Equals(object? obj)
+    {
+        var item = obj as Vector2d?;
+        if (item is null)
+            return false;
+        return this == item;
+    }
 
-        public override int GetHashCode()
-        {
-            return HashCode.Combine(X, Y);
-        }
+    public override int GetHashCode()
+    {
+        return HashCode.Combine(X, Y);
     }
 }

+ 99 - 100
src/ChunkyImageLib/DataHolders/Vector2i.cs

@@ -1,112 +1,111 @@
 using SkiaSharp;
 
-namespace ChunkyImageLib.DataHolders
+namespace ChunkyImageLib.DataHolders;
+
+public struct Vector2i
 {
-    public struct Vector2i
-    {
-        public int X { set; get; }
-        public int Y { set; get; }
+    public int X { set; get; }
+    public int Y { set; get; }
 
-        public int TaxicabLength => Math.Abs(X) + Math.Abs(Y);
-        public double Length => Math.Sqrt(LengthSquared);
-        public int LengthSquared => X * X + Y * Y;
+    public int TaxicabLength => Math.Abs(X) + Math.Abs(Y);
+    public double Length => Math.Sqrt(LengthSquared);
+    public int LengthSquared => X * X + Y * Y;
 
-        public Vector2i(int x, int y)
-        {
-            X = x;
-            Y = y;
-        }
+    public Vector2i(int x, int y)
+    {
+        X = x;
+        Y = y;
+    }
 
-        public Vector2i Signs()
-        {
-            return new Vector2i(X >= 0 ? 1 : -1, Y >= 0 ? 1 : -1);
-        }
-        public Vector2i Multiply(Vector2i other)
-        {
-            return new Vector2i(X * other.X, Y * other.Y);
-        }
-        public static Vector2i operator +(Vector2i a, Vector2i b)
-        {
-            return new Vector2i(a.X + b.X, a.Y + b.Y);
-        }
-        public static Vector2i operator -(Vector2i a, Vector2i b)
-        {
-            return new Vector2i(a.X - b.X, a.Y - b.Y);
-        }
-        public static Vector2i operator -(Vector2i a)
-        {
-            return new Vector2i(-a.X, -a.Y);
-        }
-        public static Vector2i operator *(int b, Vector2i a)
-        {
-            return new Vector2i(a.X * b, a.Y * b);
-        }
-        public static int operator *(Vector2i a, Vector2i b)
-        {
-            return a.X * b.X + a.Y * b.Y;
-        }
-        public static Vector2i operator *(Vector2i a, int b)
-        {
-            return new Vector2i(a.X * b, a.Y * b);
-        }
-        public static Vector2d operator *(Vector2i a, double b)
-        {
-            return new Vector2d(a.X * b, a.Y * b);
-        }
-        public static Vector2i operator /(Vector2i a, int b)
-        {
-            return new Vector2i(a.X / b, a.Y / b);
-        }
-        public static Vector2d operator /(Vector2i a, double b)
-        {
-            return new Vector2d(a.X / b, a.Y / b);
-        }
-        public static bool operator ==(Vector2i a, Vector2i b)
-        {
-            return a.X == b.X && a.Y == b.Y;
-        }
-        public static bool operator !=(Vector2i a, Vector2i b)
-        {
-            return !(a.X == b.X && a.Y == b.Y);
-        }
+    public Vector2i Signs()
+    {
+        return new Vector2i(X >= 0 ? 1 : -1, Y >= 0 ? 1 : -1);
+    }
+    public Vector2i Multiply(Vector2i other)
+    {
+        return new Vector2i(X * other.X, Y * other.Y);
+    }
+    public static Vector2i operator +(Vector2i a, Vector2i b)
+    {
+        return new Vector2i(a.X + b.X, a.Y + b.Y);
+    }
+    public static Vector2i operator -(Vector2i a, Vector2i b)
+    {
+        return new Vector2i(a.X - b.X, a.Y - b.Y);
+    }
+    public static Vector2i operator -(Vector2i a)
+    {
+        return new Vector2i(-a.X, -a.Y);
+    }
+    public static Vector2i operator *(int b, Vector2i a)
+    {
+        return new Vector2i(a.X * b, a.Y * b);
+    }
+    public static int operator *(Vector2i a, Vector2i b)
+    {
+        return a.X * b.X + a.Y * b.Y;
+    }
+    public static Vector2i operator *(Vector2i a, int b)
+    {
+        return new Vector2i(a.X * b, a.Y * b);
+    }
+    public static Vector2d operator *(Vector2i a, double b)
+    {
+        return new Vector2d(a.X * b, a.Y * b);
+    }
+    public static Vector2i operator /(Vector2i a, int b)
+    {
+        return new Vector2i(a.X / b, a.Y / b);
+    }
+    public static Vector2d operator /(Vector2i a, double b)
+    {
+        return new Vector2d(a.X / b, a.Y / b);
+    }
+    public static bool operator ==(Vector2i a, Vector2i b)
+    {
+        return a.X == b.X && a.Y == b.Y;
+    }
+    public static bool operator !=(Vector2i a, Vector2i b)
+    {
+        return !(a.X == b.X && a.Y == b.Y);
+    }
 
-        public static implicit operator Vector2d(Vector2i vec)
-        {
-            return new Vector2d(vec.X, vec.Y);
-        }
-        public static implicit operator SKPointI(Vector2i vec)
-        {
-            return new SKPointI(vec.X, vec.Y);
-        }
-        public static implicit operator SKPoint(Vector2i vec)
-        {
-            return new SKPoint(vec.X, vec.Y);
-        }
-        public static implicit operator SKSizeI(Vector2i vec)
-        {
-            return new SKSizeI(vec.X, vec.Y);
-        }
-        public static implicit operator SKSize(Vector2i vec)
-        {
-            return new SKSize(vec.X, vec.Y);
-        }
+    public static implicit operator Vector2d(Vector2i vec)
+    {
+        return new Vector2d(vec.X, vec.Y);
+    }
+    public static implicit operator SKPointI(Vector2i vec)
+    {
+        return new SKPointI(vec.X, vec.Y);
+    }
+    public static implicit operator SKPoint(Vector2i vec)
+    {
+        return new SKPoint(vec.X, vec.Y);
+    }
+    public static implicit operator SKSizeI(Vector2i vec)
+    {
+        return new SKSizeI(vec.X, vec.Y);
+    }
+    public static implicit operator SKSize(Vector2i vec)
+    {
+        return new SKSize(vec.X, vec.Y);
+    }
 
-        public override string ToString()
-        {
-            return $"({X}; {Y})";
-        }
+    public override string ToString()
+    {
+        return $"({X}; {Y})";
+    }
 
-        public override bool Equals(object? obj)
-        {
-            var item = obj as Vector2i?;
-            if (item is null)
-                return false;
-            return this == item;
-        }
+    public override bool Equals(object? obj)
+    {
+        var item = obj as Vector2i?;
+        if (item is null)
+            return false;
+        return this == item;
+    }
 
-        public override int GetHashCode()
-        {
-            return HashCode.Combine(X, Y);
-        }
+    public override int GetHashCode()
+    {
+        return HashCode.Combine(X, Y);
     }
 }

+ 7 - 8
src/ChunkyImageLib/IReadOnlyChunkyImage.cs

@@ -1,13 +1,12 @@
 using ChunkyImageLib.DataHolders;
 using SkiaSharp;
 
-namespace ChunkyImageLib
+namespace ChunkyImageLib;
+
+public interface IReadOnlyChunkyImage
 {
-    public interface IReadOnlyChunkyImage
-    {
-        bool DrawLatestChunkOn(Vector2i chunkPos, ChunkResolution resolution, SKSurface surface, Vector2i pos, SKPaint? paint = null);
-        bool LatestChunkExists(Vector2i chunkPos, ChunkResolution resolution);
-        HashSet<Vector2i> FindAffectedChunks();
-        HashSet<Vector2i> FindAllChunks();
-    }
+    bool DrawLatestChunkOn(Vector2i chunkPos, ChunkResolution resolution, SKSurface surface, Vector2i pos, SKPaint? paint = null);
+    bool LatestChunkExists(Vector2i chunkPos, ChunkResolution resolution);
+    HashSet<Vector2i> FindAffectedChunks();
+    HashSet<Vector2i> FindAllChunks();
 }

+ 4 - 5
src/ChunkyImageLib/Operations/ClearOperation.cs

@@ -1,7 +1,6 @@
-namespace ChunkyImageLib.Operations
+namespace ChunkyImageLib.Operations;
+
+internal record class ClearOperation : IOperation
 {
-    internal record class ClearOperation : IOperation
-    {
-        public void Dispose() { }
-    }
+    public void Dispose() { }
 }

+ 24 - 25
src/ChunkyImageLib/Operations/ClearRegionOperation.cs

@@ -1,36 +1,35 @@
 using ChunkyImageLib.DataHolders;
 using SkiaSharp;
 
-namespace ChunkyImageLib.Operations
+namespace ChunkyImageLib.Operations;
+
+internal class ClearRegionOperation : IDrawOperation
 {
-    internal class ClearRegionOperation : IDrawOperation
-    {
-        Vector2i pos;
-        Vector2i size;
+    Vector2i pos;
+    Vector2i size;
 
-        public bool IgnoreEmptyChunks => true;
+    public bool IgnoreEmptyChunks => true;
 
-        public ClearRegionOperation(Vector2i pos, Vector2i size)
-        {
-            this.pos = pos;
-            this.size = size;
-        }
+    public ClearRegionOperation(Vector2i pos, Vector2i size)
+    {
+        this.pos = pos;
+        this.size = size;
+    }
 
-        public void DrawOnChunk(Chunk chunk, Vector2i chunkPos)
-        {
-            Vector2i convPos = OperationHelper.ConvertForResolution(pos, chunk.Resolution);
-            Vector2i convSize = OperationHelper.ConvertForResolution(size, chunk.Resolution);
+    public void DrawOnChunk(Chunk chunk, Vector2i chunkPos)
+    {
+        Vector2i convPos = OperationHelper.ConvertForResolution(pos, chunk.Resolution);
+        Vector2i convSize = OperationHelper.ConvertForResolution(size, chunk.Resolution);
 
-            chunk.Surface.SkiaSurface.Canvas.Save();
-            chunk.Surface.SkiaSurface.Canvas.ClipRect(SKRect.Create(convPos - chunkPos.Multiply(chunk.PixelSize), convSize));
-            chunk.Surface.SkiaSurface.Canvas.Clear();
-            chunk.Surface.SkiaSurface.Canvas.Restore();
-        }
+        chunk.Surface.SkiaSurface.Canvas.Save();
+        chunk.Surface.SkiaSurface.Canvas.ClipRect(SKRect.Create(convPos - chunkPos.Multiply(chunk.PixelSize), convSize));
+        chunk.Surface.SkiaSurface.Canvas.Clear();
+        chunk.Surface.SkiaSurface.Canvas.Restore();
+    }
 
-        public HashSet<Vector2i> FindAffectedChunks()
-        {
-            return OperationHelper.FindChunksFullyInsideRectangle(pos, size);
-        }
-        public void Dispose() { }
+    public HashSet<Vector2i> FindAffectedChunks()
+    {
+        return OperationHelper.FindChunksFullyInsideRectangle(pos, size);
     }
+    public void Dispose() { }
 }

+ 6 - 7
src/ChunkyImageLib/Operations/IDrawOperation.cs

@@ -1,11 +1,10 @@
 using ChunkyImageLib.DataHolders;
 
-namespace ChunkyImageLib.Operations
+namespace ChunkyImageLib.Operations;
+
+internal interface IDrawOperation : IOperation
 {
-    internal interface IDrawOperation : IOperation
-    {
-        bool IgnoreEmptyChunks { get; }
-        void DrawOnChunk(Chunk chunk, Vector2i chunkPos);
-        HashSet<Vector2i> FindAffectedChunks();
-    }
+    bool IgnoreEmptyChunks { get; }
+    void DrawOnChunk(Chunk chunk, Vector2i chunkPos);
+    HashSet<Vector2i> FindAffectedChunks();
 }

+ 3 - 4
src/ChunkyImageLib/Operations/IOperation.cs

@@ -1,6 +1,5 @@
-namespace ChunkyImageLib.Operations
+namespace ChunkyImageLib.Operations;
+
+internal interface IOperation : IDisposable
 {
-    internal interface IOperation : IDisposable
-    {
-    }
 }

+ 34 - 35
src/ChunkyImageLib/Operations/ImageOperation.cs

@@ -1,53 +1,52 @@
 using ChunkyImageLib.DataHolders;
 using SkiaSharp;
 
-namespace ChunkyImageLib.Operations
+namespace ChunkyImageLib.Operations;
+
+internal record class ImageOperation : IDrawOperation
 {
-    internal record class ImageOperation : IDrawOperation
-    {
-        private Vector2i pos;
-        private Surface toPaint;
-        private static SKPaint ReplacingPaint = new() { BlendMode = SKBlendMode.Src };
+    private Vector2i pos;
+    private Surface toPaint;
+    private static SKPaint ReplacingPaint = new() { BlendMode = SKBlendMode.Src };
 
-        public bool IgnoreEmptyChunks => false;
+    public bool IgnoreEmptyChunks => false;
 
-        public ImageOperation(Vector2i pos, Surface image)
-        {
-            this.pos = pos;
-            toPaint = new Surface(image);
-        }
+    public ImageOperation(Vector2i pos, Surface image)
+    {
+        this.pos = pos;
+        toPaint = new Surface(image);
+    }
 
-        public void DrawOnChunk(Chunk chunk, Vector2i chunkPos)
+    public void DrawOnChunk(Chunk chunk, Vector2i chunkPos)
+    {
+        if (chunk.Resolution == ChunkResolution.Full)
         {
-            if (chunk.Resolution == ChunkResolution.Full)
-            {
-                chunk.Surface.SkiaSurface.Canvas.DrawSurface(toPaint.SkiaSurface, pos - chunkPos * ChunkPool.FullChunkSize, ReplacingPaint);
-                return;
-            }
-            chunk.Surface.SkiaSurface.Canvas.Save();
-            chunk.Surface.SkiaSurface.Canvas.Scale((float)chunk.Resolution.Multiplier());
             chunk.Surface.SkiaSurface.Canvas.DrawSurface(toPaint.SkiaSurface, pos - chunkPos * ChunkPool.FullChunkSize, ReplacingPaint);
-            chunk.Surface.SkiaSurface.Canvas.Restore();
+            return;
         }
+        chunk.Surface.SkiaSurface.Canvas.Save();
+        chunk.Surface.SkiaSurface.Canvas.Scale((float)chunk.Resolution.Multiplier());
+        chunk.Surface.SkiaSurface.Canvas.DrawSurface(toPaint.SkiaSurface, pos - chunkPos * ChunkPool.FullChunkSize, ReplacingPaint);
+        chunk.Surface.SkiaSurface.Canvas.Restore();
+    }
 
-        public HashSet<Vector2i> FindAffectedChunks()
+    public HashSet<Vector2i> FindAffectedChunks()
+    {
+        Vector2i start = OperationHelper.GetChunkPos(pos, ChunkPool.FullChunkSize);
+        Vector2i end = OperationHelper.GetChunkPos(new(pos.X + toPaint.Size.X - 1, pos.Y + toPaint.Size.Y - 1), ChunkPool.FullChunkSize);
+        HashSet<Vector2i> output = new();
+        for (int cx = start.X; cx <= end.X; cx++)
         {
-            Vector2i start = OperationHelper.GetChunkPos(pos, ChunkPool.FullChunkSize);
-            Vector2i end = OperationHelper.GetChunkPos(new(pos.X + toPaint.Size.X - 1, pos.Y + toPaint.Size.Y - 1), ChunkPool.FullChunkSize);
-            HashSet<Vector2i> output = new();
-            for (int cx = start.X; cx <= end.X; cx++)
+            for (int cy = start.Y; cy <= end.Y; cy++)
             {
-                for (int cy = start.Y; cy <= end.Y; cy++)
-                {
-                    output.Add(new(cx, cy));
-                }
+                output.Add(new(cx, cy));
             }
-            return output;
         }
+        return output;
+    }
 
-        public void Dispose()
-        {
-            toPaint.Dispose();
-        }
+    public void Dispose()
+    {
+        toPaint.Dispose();
     }
 }

+ 254 - 255
src/ChunkyImageLib/Operations/OperationHelper.cs

@@ -1,313 +1,312 @@
 using ChunkyImageLib.DataHolders;
 
-namespace ChunkyImageLib.Operations
+namespace ChunkyImageLib.Operations;
+
+public static class OperationHelper
 {
-    public static class OperationHelper
+    public static Vector2i ConvertForResolution(Vector2i pixelPos, ChunkResolution resolution)
+    {
+        var mult = resolution.Multiplier();
+        return new((int)Math.Round(pixelPos.X * mult), (int)Math.Round(pixelPos.Y * mult));
+    }
+
+    public static Vector2i GetChunkPos(Vector2i pixelPos, int chunkSize)
     {
-        public static Vector2i ConvertForResolution(Vector2i pixelPos, ChunkResolution resolution)
+        return new Vector2i()
         {
-            var mult = resolution.Multiplier();
-            return new((int)Math.Round(pixelPos.X * mult), (int)Math.Round(pixelPos.Y * mult));
-        }
+            X = (int)MathF.Floor(pixelPos.X / (float)chunkSize),
+            Y = (int)MathF.Floor(pixelPos.Y / (float)chunkSize)
+        };
+    }
+
+    /// <summary>
+    /// Finds chunks that at least partially lie inside of a rectangle
+    /// </summary>
+    public static HashSet<Vector2i> FindChunksTouchingRectangle(Vector2d center, Vector2d size, double angle, int chunkSize)
+    {
+        if (size.X == 0 || size.Y == 0)
+            return new HashSet<Vector2i>();
+        // draw a line on the outside of each side
+        var corners = FindRectangleCorners(center, size, angle);
+        List<Vector2i>[] lines = new List<Vector2i>[] {
+            FindChunksAlongLine(corners.Item2, corners.Item1, chunkSize),
+            FindChunksAlongLine(corners.Item3, corners.Item2, chunkSize),
+            FindChunksAlongLine(corners.Item4, corners.Item3, chunkSize),
+            FindChunksAlongLine(corners.Item1, corners.Item4, chunkSize)
+        };
+
+        if (lines[0].Count == 0 || lines[1].Count == 0 || lines[2].Count == 0 || lines[3].Count == 0)
+            return new HashSet<Vector2i>();
 
-        public static Vector2i GetChunkPos(Vector2i pixelPos, int chunkSize)
+        //find min and max X for each Y in lines
+        var ySel = (Vector2i vec) => vec.Y;
+        int minY = Math.Min(lines[0].Min(ySel), lines[2].Min(ySel));
+        int maxY = Math.Max(lines[0].Max(ySel), lines[2].Max(ySel));
+
+        int[] minXValues = new int[maxY - minY + 1];
+        int[] maxXValues = new int[maxY - minY + 1];
+        for (int i = 0; i < minXValues.Length; i++)
         {
-            return new Vector2i()
-            {
-                X = (int)MathF.Floor(pixelPos.X / (float)chunkSize),
-                Y = (int)MathF.Floor(pixelPos.Y / (float)chunkSize)
-            };
+            minXValues[i] = int.MaxValue;
+            maxXValues[i] = int.MinValue;
         }
 
-        /// <summary>
-        /// Finds chunks that at least partially lie inside of a rectangle
-        /// </summary>
-        public static HashSet<Vector2i> FindChunksTouchingRectangle(Vector2d center, Vector2d size, double angle, int chunkSize)
+        for (int i = 0; i < lines.Length; i++)
         {
-            if (size.X == 0 || size.Y == 0)
-                return new HashSet<Vector2i>();
-            // draw a line on the outside of each side
-            var corners = FindRectangleCorners(center, size, angle);
-            List<Vector2i>[] lines = new List<Vector2i>[] {
-                FindChunksAlongLine(corners.Item2, corners.Item1, chunkSize),
-                FindChunksAlongLine(corners.Item3, corners.Item2, chunkSize),
-                FindChunksAlongLine(corners.Item4, corners.Item3, chunkSize),
-                FindChunksAlongLine(corners.Item1, corners.Item4, chunkSize)
-            };
+            UpdateMinXValues(lines[i], minXValues, minY);
+            UpdateMaxXValues(lines[i], maxXValues, minY);
+        }
 
-            if (lines[0].Count == 0 || lines[1].Count == 0 || lines[2].Count == 0 || lines[3].Count == 0)
-                return new HashSet<Vector2i>();
+        //draw a line from min X to max X for each Y
+        HashSet<Vector2i> output = new();
+        for (int i = 0; i < minXValues.Length; i++)
+        {
+            int minX = minXValues[i];
+            int maxX = maxXValues[i];
+            for (int x = minX; x <= maxX; x++)
+                output.Add(new(x, i + minY));
+        }
 
-            //find min and max X for each Y in lines
-            var ySel = (Vector2i vec) => vec.Y;
-            int minY = Math.Min(lines[0].Min(ySel), lines[2].Min(ySel));
-            int maxY = Math.Max(lines[0].Max(ySel), lines[2].Max(ySel));
+        return output;
+    }
 
-            int[] minXValues = new int[maxY - minY + 1];
-            int[] maxXValues = new int[maxY - minY + 1];
-            for (int i = 0; i < minXValues.Length; i++)
+    public static HashSet<Vector2i> FindChunksFullyInsideRectangle(Vector2i pos, Vector2i size)
+    {
+        Vector2i startChunk = GetChunkPos(pos, ChunkPool.FullChunkSize);
+        Vector2i endChunk = GetChunkPosBiased(pos + size, false, false, ChunkPool.FullChunkSize);
+        HashSet<Vector2i> output = new();
+        for (int x = startChunk.X; x <= endChunk.X; x++)
+        {
+            for (int y = startChunk.Y; y <= endChunk.Y; y++)
             {
-                minXValues[i] = int.MaxValue;
-                maxXValues[i] = int.MinValue;
+                output.Add(new Vector2i(x, y));
             }
+        }
+        return output;
+    }
 
-            for (int i = 0; i < lines.Length; i++)
-            {
-                UpdateMinXValues(lines[i], minXValues, minY);
-                UpdateMaxXValues(lines[i], maxXValues, minY);
-            }
+    public static HashSet<Vector2i> FindChunksFullyInsideRectangle(Vector2d center, Vector2d size, double angle, int chunkSize)
+    {
+        if (size.X < chunkSize || size.Y < chunkSize || center.IsNaNOrInfinity() || size.IsNaNOrInfinity() || double.IsNaN(angle) || double.IsInfinity(angle))
+            return new HashSet<Vector2i>();
+        // draw a line on the inside of each side
+        var corners = FindRectangleCorners(center, size, angle);
+        List<Vector2i>[] lines = new List<Vector2i>[] {
+            FindChunksAlongLine(corners.Item1, corners.Item2, chunkSize),
+            FindChunksAlongLine(corners.Item2, corners.Item3, chunkSize),
+            FindChunksAlongLine(corners.Item3, corners.Item4, chunkSize),
+            FindChunksAlongLine(corners.Item4, corners.Item1, chunkSize)
+        };
 
-            //draw a line from min X to max X for each Y
-            HashSet<Vector2i> output = new();
-            for (int i = 0; i < minXValues.Length; i++)
-            {
-                int minX = minXValues[i];
-                int maxX = maxXValues[i];
-                for (int x = minX; x <= maxX; x++)
-                    output.Add(new(x, i + minY));
-            }
+        //find min and max X for each Y in lines
+        var ySel = (Vector2i vec) => vec.Y;
+        int minY = Math.Min(lines[0].Min(ySel), lines[2].Min(ySel));
+        int maxY = Math.Max(lines[0].Max(ySel), lines[2].Max(ySel));
 
-            return output;
+        int[] minXValues = new int[maxY - minY + 1];
+        int[] maxXValues = new int[maxY - minY + 1];
+        for (int i = 0; i < minXValues.Length; i++)
+        {
+            minXValues[i] = int.MaxValue;
+            maxXValues[i] = int.MinValue;
         }
 
-        public static HashSet<Vector2i> FindChunksFullyInsideRectangle(Vector2i pos, Vector2i size)
+        for (int i = 0; i < lines.Length; i++)
         {
-            Vector2i startChunk = GetChunkPos(pos, ChunkPool.FullChunkSize);
-            Vector2i endChunk = GetChunkPosBiased(pos + size, false, false, ChunkPool.FullChunkSize);
-            HashSet<Vector2i> output = new();
-            for (int x = startChunk.X; x <= endChunk.X; x++)
-            {
-                for (int y = startChunk.Y; y <= endChunk.Y; y++)
-                {
-                    output.Add(new Vector2i(x, y));
-                }
-            }
-            return output;
+            UpdateMinXValues(lines[i], minXValues, minY);
+            UpdateMaxXValues(lines[i], maxXValues, minY);
         }
 
-        public static HashSet<Vector2i> FindChunksFullyInsideRectangle(Vector2d center, Vector2d size, double angle, int chunkSize)
+        //draw a line from min X to max X for each Y and exclude lines
+        HashSet<Vector2i> output = new();
+        for (int i = 0; i < minXValues.Length; i++)
         {
-            if (size.X < chunkSize || size.Y < chunkSize || center.IsNaNOrInfinity() || size.IsNaNOrInfinity() || double.IsNaN(angle) || double.IsInfinity(angle))
-                return new HashSet<Vector2i>();
-            // draw a line on the inside of each side
-            var corners = FindRectangleCorners(center, size, angle);
-            List<Vector2i>[] lines = new List<Vector2i>[] {
-                FindChunksAlongLine(corners.Item1, corners.Item2, chunkSize),
-                FindChunksAlongLine(corners.Item2, corners.Item3, chunkSize),
-                FindChunksAlongLine(corners.Item3, corners.Item4, chunkSize),
-                FindChunksAlongLine(corners.Item4, corners.Item1, chunkSize)
-            };
+            int minX = minXValues[i];
+            int maxX = maxXValues[i];
+            for (int x = minX; x <= maxX; x++)
+                output.Add(new(x, i + minY));
+        }
+        for (int i = 0; i < lines.Length; i++)
+        {
+            output.ExceptWith(lines[i]);
+        }
 
-            //find min and max X for each Y in lines
-            var ySel = (Vector2i vec) => vec.Y;
-            int minY = Math.Min(lines[0].Min(ySel), lines[2].Min(ySel));
-            int maxY = Math.Max(lines[0].Max(ySel), lines[2].Max(ySel));
+        return output;
+    }
 
-            int[] minXValues = new int[maxY - minY + 1];
-            int[] maxXValues = new int[maxY - minY + 1];
-            for (int i = 0; i < minXValues.Length; i++)
-            {
-                minXValues[i] = int.MaxValue;
-                maxXValues[i] = int.MinValue;
-            }
+    private static void UpdateMinXValues(List<Vector2i> line, int[] minXValues, int minY)
+    {
+        for (int i = 0; i < line.Count; i++)
+        {
+            if (line[i].X < minXValues[line[i].Y - minY])
+                minXValues[line[i].Y - minY] = line[i].X;
+        }
+    }
 
-            for (int i = 0; i < lines.Length; i++)
-            {
-                UpdateMinXValues(lines[i], minXValues, minY);
-                UpdateMaxXValues(lines[i], maxXValues, minY);
-            }
+    private static void UpdateMaxXValues(List<Vector2i> line, int[] maxXValues, int minY)
+    {
+        for (int i = 0; i < line.Count; i++)
+        {
+            if (line[i].X > maxXValues[line[i].Y - minY])
+                maxXValues[line[i].Y - minY] = line[i].X;
+        }
+    }
 
-            //draw a line from min X to max X for each Y and exclude lines
-            HashSet<Vector2i> output = new();
-            for (int i = 0; i < minXValues.Length; i++)
-            {
-                int minX = minXValues[i];
-                int maxX = maxXValues[i];
-                for (int x = minX; x <= maxX; x++)
-                    output.Add(new(x, i + minY));
-            }
-            for (int i = 0; i < lines.Length; i++)
-            {
-                output.ExceptWith(lines[i]);
-            }
+    /// <summary>
+    /// Think of this function as a line drawing algorithm. 
+    /// The chosen chunks are guaranteed to be on the left side of the line (assuming y going upwards and looking from p1 towards p2).
+    /// This ensures that when you draw a filled shape all updated chunks will be covered (the filled part should go to the right of the line)
+    /// No parts of the line will stick out to the left and be left uncovered
+    /// </summary>
+    public static List<Vector2i> FindChunksAlongLine(Vector2d p1, Vector2d p2, int chunkSize)
+    {
+        if (p1 == p2 || p1.IsNaNOrInfinity() || p2.IsNaNOrInfinity())
+            return new List<Vector2i>();
 
-            return output;
+        //rotate the line into the first quadrant of the coordinate plane
+        int quadrant;
+        if (p2.X >= p1.X && p2.Y >= p1.Y)
+        {
+            quadrant = 1;
         }
-
-        private static void UpdateMinXValues(List<Vector2i> line, int[] minXValues, int minY)
+        else if (p2.X <= p1.X && p2.Y <= p1.Y)
         {
-            for (int i = 0; i < line.Count; i++)
-            {
-                if (line[i].X < minXValues[line[i].Y - minY])
-                    minXValues[line[i].Y - minY] = line[i].X;
-            }
+            quadrant = 3;
+            p1 = -p1;
+            p2 = -p2;
         }
-
-        private static void UpdateMaxXValues(List<Vector2i> line, int[] maxXValues, int minY)
+        else if (p2.X < p1.X)
         {
-            for (int i = 0; i < line.Count; i++)
-            {
-                if (line[i].X > maxXValues[line[i].Y - minY])
-                    maxXValues[line[i].Y - minY] = line[i].X;
-            }
+            quadrant = 2;
+            (p1.X, p1.Y) = (p1.Y, -p1.X);
+            (p2.X, p2.Y) = (p2.Y, -p2.X);
         }
-
-        /// <summary>
-        /// Think of this function as a line drawing algorithm. 
-        /// The chosen chunks are guaranteed to be on the left side of the line (assuming y going upwards and looking from p1 towards p2).
-        /// This ensures that when you draw a filled shape all updated chunks will be covered (the filled part should go to the right of the line)
-        /// No parts of the line will stick out to the left and be left uncovered
-        /// </summary>
-        public static List<Vector2i> FindChunksAlongLine(Vector2d p1, Vector2d p2, int chunkSize)
+        else
         {
-            if (p1 == p2 || p1.IsNaNOrInfinity() || p2.IsNaNOrInfinity())
-                return new List<Vector2i>();
-
-            //rotate the line into the first quadrant of the coordinate plane
-            int quadrant;
-            if (p2.X >= p1.X && p2.Y >= p1.Y)
-            {
-                quadrant = 1;
-            }
-            else if (p2.X <= p1.X && p2.Y <= p1.Y)
-            {
-                quadrant = 3;
-                p1 = -p1;
-                p2 = -p2;
-            }
-            else if (p2.X < p1.X)
-            {
-                quadrant = 2;
-                (p1.X, p1.Y) = (p1.Y, -p1.X);
-                (p2.X, p2.Y) = (p2.Y, -p2.X);
-            }
-            else
-            {
-                quadrant = 4;
-                (p1.X, p1.Y) = (-p1.Y, p1.X);
-                (p2.X, p2.Y) = (-p2.Y, p2.X);
-            }
+            quadrant = 4;
+            (p1.X, p1.Y) = (-p1.Y, p1.X);
+            (p2.X, p2.Y) = (-p2.Y, p2.X);
+        }
 
-            List<Vector2i> output = new();
-            //vertical line
-            if (p1.X == p2.X)
-            {
-                //if exactly on a chunk boundary, pick the chunk on the top-left
-                Vector2i start = GetChunkPosBiased(p1, false, true, chunkSize);
-                //if exactly on chunk boundary, pick the chunk on the bottom-left
-                Vector2i end = GetChunkPosBiased(p2, false, false, chunkSize);
-                for (int y = start.Y; y <= end.Y; y++)
-                    output.Add(new(start.X, y));
-            }
-            //horizontal line
-            else if (p1.Y == p2.Y)
+        List<Vector2i> output = new();
+        //vertical line
+        if (p1.X == p2.X)
+        {
+            //if exactly on a chunk boundary, pick the chunk on the top-left
+            Vector2i start = GetChunkPosBiased(p1, false, true, chunkSize);
+            //if exactly on chunk boundary, pick the chunk on the bottom-left
+            Vector2i end = GetChunkPosBiased(p2, false, false, chunkSize);
+            for (int y = start.Y; y <= end.Y; y++)
+                output.Add(new(start.X, y));
+        }
+        //horizontal line
+        else if (p1.Y == p2.Y)
+        {
+            //if exactly on a chunk boundary, pick the chunk on the top-right
+            Vector2i start = GetChunkPosBiased(p1, true, true, chunkSize);
+            //if exactly on chunk boundary, pick the chunk on the top-left
+            Vector2i end = GetChunkPosBiased(p2, false, true, chunkSize);
+            for (int x = start.X; x <= end.X; x++)
+                output.Add(new(x, start.Y));
+        }
+        //all other lines
+        else
+        {
+            //y = mx + b
+            double m = (p2.Y - p1.Y) / (p2.X - p1.X);
+            double b = p1.Y - (p1.X * m);
+            Vector2i cur = GetChunkPosBiased(p1, true, true, chunkSize);
+            output.Add(cur);
+            if (LineEq(m, cur.X * chunkSize + chunkSize, b) > cur.Y * chunkSize + chunkSize)
+                cur.X--;
+            Vector2i end = GetChunkPosBiased(p2, false, false, chunkSize);
+            if (m < 1)
             {
-                //if exactly on a chunk boundary, pick the chunk on the top-right
-                Vector2i start = GetChunkPosBiased(p1, true, true, chunkSize);
-                //if exactly on chunk boundary, pick the chunk on the top-left
-                Vector2i end = GetChunkPosBiased(p2, false, true, chunkSize);
-                for (int x = start.X; x <= end.X; x++)
-                    output.Add(new(x, start.Y));
+                while (true)
+                {
+                    if (LineEq(m, cur.X * chunkSize + chunkSize * 2, b) > cur.Y * chunkSize + chunkSize)
+                    {
+                        cur.X++;
+                        cur.Y++;
+                    }
+                    else
+                    {
+                        cur.X++;
+                    }
+                    if (cur.X >= end.X && cur.Y >= end.Y)
+                        break;
+                    output.Add(cur);
+                }
+                output.Add(end);
             }
-            //all other lines
             else
             {
-                //y = mx + b
-                double m = (p2.Y - p1.Y) / (p2.X - p1.X);
-                double b = p1.Y - (p1.X * m);
-                Vector2i cur = GetChunkPosBiased(p1, true, true, chunkSize);
-                output.Add(cur);
-                if (LineEq(m, cur.X * chunkSize + chunkSize, b) > cur.Y * chunkSize + chunkSize)
-                    cur.X--;
-                Vector2i end = GetChunkPosBiased(p2, false, false, chunkSize);
-                if (m < 1)
+                while (true)
                 {
-                    while (true)
+                    if (LineEq(m, cur.X * chunkSize + chunkSize, b) <= cur.Y * chunkSize + chunkSize)
                     {
-                        if (LineEq(m, cur.X * chunkSize + chunkSize * 2, b) > cur.Y * chunkSize + chunkSize)
-                        {
-                            cur.X++;
-                            cur.Y++;
-                        }
-                        else
-                        {
-                            cur.X++;
-                        }
-                        if (cur.X >= end.X && cur.Y >= end.Y)
-                            break;
-                        output.Add(cur);
+                        cur.X++;
+                        cur.Y++;
                     }
-                    output.Add(end);
-                }
-                else
-                {
-                    while (true)
+                    else
                     {
-                        if (LineEq(m, cur.X * chunkSize + chunkSize, b) <= cur.Y * chunkSize + chunkSize)
-                        {
-                            cur.X++;
-                            cur.Y++;
-                        }
-                        else
-                        {
-                            cur.Y++;
-                        }
-                        if (cur.X >= end.X && cur.Y >= end.Y)
-                            break;
-                        output.Add(cur);
+                        cur.Y++;
                     }
-                    output.Add(end);
+                    if (cur.X >= end.X && cur.Y >= end.Y)
+                        break;
+                    output.Add(cur);
                 }
+                output.Add(end);
             }
+        }
 
-            //rotate output back
-            if (quadrant == 1)
-                return output;
-            if (quadrant == 3)
-            {
-                for (int i = 0; i < output.Count; i++)
-                    output[i] = new(-output[i].X - 1, -output[i].Y - 1);
-                return output;
-            }
-            if (quadrant == 2)
-            {
-                for (int i = 0; i < output.Count; i++)
-                    output[i] = new(-output[i].Y - 1, output[i].X);
-                return output;
-            }
+        //rotate output back
+        if (quadrant == 1)
+            return output;
+        if (quadrant == 3)
+        {
             for (int i = 0; i < output.Count; i++)
-                output[i] = new(output[i].Y, -output[i].X - 1);
+                output[i] = new(-output[i].X - 1, -output[i].Y - 1);
             return output;
         }
-
-        private static double LineEq(double m, double x, double b)
+        if (quadrant == 2)
         {
-            return m * x + b;
+            for (int i = 0; i < output.Count; i++)
+                output[i] = new(-output[i].Y - 1, output[i].X);
+            return output;
         }
+        for (int i = 0; i < output.Count; i++)
+            output[i] = new(output[i].Y, -output[i].X - 1);
+        return output;
+    }
 
-        public static Vector2i GetChunkPosBiased(Vector2d pos, bool positiveX, bool positiveY, int chunkSize)
-        {
-            pos /= chunkSize;
-            return new Vector2i()
-            {
-                X = positiveX ? (int)Math.Floor(pos.X) : (int)Math.Ceiling(pos.X) - 1,
-                Y = positiveY ? (int)Math.Floor(pos.Y) : (int)Math.Ceiling(pos.Y) - 1,
-            };
-        }
+    private static double LineEq(double m, double x, double b)
+    {
+        return m * x + b;
+    }
 
-        /// <summary>
-        /// Returns corners in ccw direction (assuming y points up)
-        /// </summary>
-        private static (Vector2d, Vector2d, Vector2d, Vector2d) FindRectangleCorners(Vector2d center, Vector2d size, double angle)
+    public static Vector2i GetChunkPosBiased(Vector2d pos, bool positiveX, bool positiveY, int chunkSize)
+    {
+        pos /= chunkSize;
+        return new Vector2i()
         {
-            Vector2d right = Vector2d.FromAngleAndLength(angle, size.X / 2);
-            Vector2d up = Vector2d.FromAngleAndLength(angle + Math.PI / 2, size.Y / 2);
-            return (
-                center + right + up,
-                center - right + up,
-                center - right - up,
-                center + right - up
-                );
-        }
+            X = positiveX ? (int)Math.Floor(pos.X) : (int)Math.Ceiling(pos.X) - 1,
+            Y = positiveY ? (int)Math.Floor(pos.Y) : (int)Math.Ceiling(pos.Y) - 1,
+        };
+    }
+
+    /// <summary>
+    /// Returns corners in ccw direction (assuming y points up)
+    /// </summary>
+    private static (Vector2d, Vector2d, Vector2d, Vector2d) FindRectangleCorners(Vector2d center, Vector2d size, double angle)
+    {
+        Vector2d right = Vector2d.FromAngleAndLength(angle, size.X / 2);
+        Vector2d up = Vector2d.FromAngleAndLength(angle + Math.PI / 2, size.Y / 2);
+        return (
+            center + right + up,
+            center - right + up,
+            center - right - up,
+            center + right - up
+            );
     }
 }

+ 8 - 9
src/ChunkyImageLib/Operations/RasterClipOperation.cs

@@ -1,13 +1,12 @@
-namespace ChunkyImageLib.Operations
+namespace ChunkyImageLib.Operations;
+
+internal class RasterClipOperation : IOperation
 {
-    internal class RasterClipOperation : IOperation
+    public ChunkyImage ClippingMask { get; }
+    public RasterClipOperation(ChunkyImage clippingMask)
     {
-        public ChunkyImage ClippingMask { get; }
-        public RasterClipOperation(ChunkyImage clippingMask)
-        {
-            ClippingMask = clippingMask;
-        }
-
-        public void Dispose() { }
+        ClippingMask = clippingMask;
     }
+
+    public void Dispose() { }
 }

+ 89 - 90
src/ChunkyImageLib/Operations/RectangleOperation.cs

@@ -1,116 +1,115 @@
 using ChunkyImageLib.DataHolders;
 using SkiaSharp;
 
-namespace ChunkyImageLib.Operations
+namespace ChunkyImageLib.Operations;
+
+internal record class RectangleOperation : IDrawOperation
 {
-    internal record class RectangleOperation : IDrawOperation
+    public RectangleOperation(ShapeData rect)
     {
-        public RectangleOperation(ShapeData rect)
-        {
-            Data = rect;
-        }
+        Data = rect;
+    }
 
-        public ShapeData Data { get; }
+    public ShapeData Data { get; }
 
-        public bool IgnoreEmptyChunks => false;
+    public bool IgnoreEmptyChunks => false;
 
-        public void DrawOnChunk(Chunk chunk, Vector2i chunkPos)
-        {
-            var skiaSurf = chunk.Surface.SkiaSurface;
-            // use a clipping rectangle with 2x stroke width to make sure stroke doesn't stick outside rect bounds
-            skiaSurf.Canvas.Save();
+    public void DrawOnChunk(Chunk chunk, Vector2i chunkPos)
+    {
+        var skiaSurf = chunk.Surface.SkiaSurface;
+        // use a clipping rectangle with 2x stroke width to make sure stroke doesn't stick outside rect bounds
+        skiaSurf.Canvas.Save();
 
-            var convertedPos = OperationHelper.ConvertForResolution(Data.Pos, chunk.Resolution);
-            var convertedSize = OperationHelper.ConvertForResolution(Data.Size, chunk.Resolution);
-            int convertedStroke = (int)Math.Round(chunk.Resolution.Multiplier() * Data.StrokeWidth);
+        var convertedPos = OperationHelper.ConvertForResolution(Data.Pos, chunk.Resolution);
+        var convertedSize = OperationHelper.ConvertForResolution(Data.Size, chunk.Resolution);
+        int convertedStroke = (int)Math.Round(chunk.Resolution.Multiplier() * Data.StrokeWidth);
 
-            var rect = SKRect.Create(convertedPos - chunkPos.Multiply(chunk.PixelSize), convertedSize);
-            skiaSurf.Canvas.ClipRect(rect);
+        var rect = SKRect.Create(convertedPos - chunkPos.Multiply(chunk.PixelSize), convertedSize);
+        skiaSurf.Canvas.ClipRect(rect);
 
-            // draw fill
-            using SKPaint paint = new()
-            {
-                Color = Data.FillColor,
-                Style = SKPaintStyle.Fill,
-            };
+        // draw fill
+        using SKPaint paint = new()
+        {
+            Color = Data.FillColor,
+            Style = SKPaintStyle.Fill,
+        };
 
-            if (Data.FillColor.Alpha > 0)
-                skiaSurf.Canvas.DrawRect(rect, paint);
+        if (Data.FillColor.Alpha > 0)
+            skiaSurf.Canvas.DrawRect(rect, paint);
 
-            // draw stroke
-            paint.Color = Data.StrokeColor;
-            paint.Style = SKPaintStyle.Stroke;
-            paint.StrokeWidth = convertedStroke * 2;
+        // draw stroke
+        paint.Color = Data.StrokeColor;
+        paint.Style = SKPaintStyle.Stroke;
+        paint.StrokeWidth = convertedStroke * 2;
 
-            skiaSurf.Canvas.DrawRect(rect, paint);
+        skiaSurf.Canvas.DrawRect(rect, paint);
 
-            // get rid of the clipping rectangle
-            skiaSurf.Canvas.Restore();
-        }
+        // get rid of the clipping rectangle
+        skiaSurf.Canvas.Restore();
+    }
 
-        public HashSet<Vector2i> FindAffectedChunks()
-        {
-            if (Data.Size.X < 1 || Data.Size.Y < 1 || Data.StrokeColor.Alpha == 0 && Data.FillColor.Alpha == 0)
-                return new();
-            if (Data.FillColor.Alpha != 0 || Data.Size.X == 1 || Data.Size.Y == 1)
-                return GetChunksForFilled(ChunkPool.FullChunkSize);
-            return GetChunksForStroke(ChunkPool.FullChunkSize);
-        }
+    public HashSet<Vector2i> FindAffectedChunks()
+    {
+        if (Data.Size.X < 1 || Data.Size.Y < 1 || Data.StrokeColor.Alpha == 0 && Data.FillColor.Alpha == 0)
+            return new();
+        if (Data.FillColor.Alpha != 0 || Data.Size.X == 1 || Data.Size.Y == 1)
+            return GetChunksForFilled(ChunkPool.FullChunkSize);
+        return GetChunksForStroke(ChunkPool.FullChunkSize);
+    }
 
-        private static (int, int)? Inset(int min, int max, int inset)
-        {
-            int insetMin = Math.Min(min + inset - 1, max);
-            int insetMax = Math.Max(max - inset + 1, min);
-            //is rectangle fully filled by the stroke
-            if (insetMin + 1 >= insetMax)
-                return null;
-            return (insetMin, insetMax);
-        }
+    private static (int, int)? Inset(int min, int max, int inset)
+    {
+        int insetMin = Math.Min(min + inset - 1, max);
+        int insetMax = Math.Max(max - inset + 1, min);
+        //is rectangle fully filled by the stroke
+        if (insetMin + 1 >= insetMax)
+            return null;
+        return (insetMin, insetMax);
+    }
 
-        private HashSet<Vector2i> GetChunksForStroke(int chunkSize)
-        {
-            //we need to account for wide strokes covering multiple chunks
-            //find inner stroke boudaries in pixel coords
-            var xInset = Inset(Data.Pos.X, Data.MaxPos.X, Data.StrokeWidth);
-            var yInset = Inset(Data.Pos.Y, Data.MaxPos.Y, Data.StrokeWidth);
-            if (xInset is null || yInset is null)
-                return GetChunksForFilled(chunkSize);
-
-            //find two chunk rectanges, outer and inner
-            Vector2i min = OperationHelper.GetChunkPos(Data.Pos, chunkSize);
-            Vector2i max = OperationHelper.GetChunkPos(Data.MaxPos, chunkSize);
-            Vector2i minInset = OperationHelper.GetChunkPos(new(xInset.Value.Item1, yInset.Value.Item1), chunkSize);
-            Vector2i maxInset = OperationHelper.GetChunkPos(new(xInset.Value.Item2, yInset.Value.Item2), chunkSize);
-
-            //fill in sides
-            HashSet<Vector2i> chunks = new();
-            AddRectangle(min, new(max.X, minInset.Y), chunks); //top
-            AddRectangle(new(min.X, minInset.Y + 1), new(minInset.X, maxInset.Y - 1), chunks); //left
-            AddRectangle(new(maxInset.X, minInset.Y + 1), new(max.X, maxInset.Y - 1), chunks); //right
-            AddRectangle(new(min.X, maxInset.Y), max, chunks); //bottom
-            return chunks;
-        }
+    private HashSet<Vector2i> GetChunksForStroke(int chunkSize)
+    {
+        //we need to account for wide strokes covering multiple chunks
+        //find inner stroke boudaries in pixel coords
+        var xInset = Inset(Data.Pos.X, Data.MaxPos.X, Data.StrokeWidth);
+        var yInset = Inset(Data.Pos.Y, Data.MaxPos.Y, Data.StrokeWidth);
+        if (xInset is null || yInset is null)
+            return GetChunksForFilled(chunkSize);
+
+        //find two chunk rectanges, outer and inner
+        Vector2i min = OperationHelper.GetChunkPos(Data.Pos, chunkSize);
+        Vector2i max = OperationHelper.GetChunkPos(Data.MaxPos, chunkSize);
+        Vector2i minInset = OperationHelper.GetChunkPos(new(xInset.Value.Item1, yInset.Value.Item1), chunkSize);
+        Vector2i maxInset = OperationHelper.GetChunkPos(new(xInset.Value.Item2, yInset.Value.Item2), chunkSize);
+
+        //fill in sides
+        HashSet<Vector2i> chunks = new();
+        AddRectangle(min, new(max.X, minInset.Y), chunks); //top
+        AddRectangle(new(min.X, minInset.Y + 1), new(minInset.X, maxInset.Y - 1), chunks); //left
+        AddRectangle(new(maxInset.X, minInset.Y + 1), new(max.X, maxInset.Y - 1), chunks); //right
+        AddRectangle(new(min.X, maxInset.Y), max, chunks); //bottom
+        return chunks;
+    }
 
-        private HashSet<Vector2i> GetChunksForFilled(int chunkSize)
-        {
-            Vector2i min = OperationHelper.GetChunkPos(Data.Pos, chunkSize);
-            Vector2i max = OperationHelper.GetChunkPos(Data.MaxPos, chunkSize);
-            HashSet<Vector2i> output = new();
-            AddRectangle(min, max, output);
-            return output;
-        }
+    private HashSet<Vector2i> GetChunksForFilled(int chunkSize)
+    {
+        Vector2i min = OperationHelper.GetChunkPos(Data.Pos, chunkSize);
+        Vector2i max = OperationHelper.GetChunkPos(Data.MaxPos, chunkSize);
+        HashSet<Vector2i> output = new();
+        AddRectangle(min, max, output);
+        return output;
+    }
 
-        private static void AddRectangle(Vector2i min, Vector2i max, HashSet<Vector2i> set)
+    private static void AddRectangle(Vector2i min, Vector2i max, HashSet<Vector2i> set)
+    {
+        for (int x = min.X; x <= max.X; x++)
         {
-            for (int x = min.X; x <= max.X; x++)
+            for (int y = min.Y; y <= max.Y; y++)
             {
-                for (int y = min.Y; y <= max.Y; y++)
-                {
-                    set.Add(new(x, y));
-                }
+                set.Add(new(x, y));
             }
         }
-
-        public void Dispose() { }
     }
+
+    public void Dispose() { }
 }

+ 7 - 8
src/ChunkyImageLib/Operations/ResizeOperation.cs

@@ -1,14 +1,13 @@
 using ChunkyImageLib.DataHolders;
 
-namespace ChunkyImageLib.Operations
+namespace ChunkyImageLib.Operations;
+
+internal record class ResizeOperation : IOperation
 {
-    internal record class ResizeOperation : IOperation
+    public Vector2i Size { get; }
+    public ResizeOperation(Vector2i size)
     {
-        public Vector2i Size { get; }
-        public ResizeOperation(Vector2i size)
-        {
-            Size = size;
-        }
-        public void Dispose() { }
+        Size = size;
     }
+    public void Dispose() { }
 }

+ 82 - 83
src/ChunkyImageLib/Surface.cs

@@ -3,105 +3,104 @@ using SkiaSharp;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 
-namespace ChunkyImageLib
+namespace ChunkyImageLib;
+
+public class Surface : IDisposable
 {
-    public class Surface : IDisposable
-    {
-        private bool disposed;
-        private int bytesPerPixel;
-        public IntPtr PixelBuffer { get; }
-        public SKSurface SkiaSurface { get; }
-        public Vector2i Size { get; }
+    private bool disposed;
+    private int bytesPerPixel;
+    public IntPtr PixelBuffer { get; }
+    public SKSurface SkiaSurface { get; }
+    public Vector2i Size { get; }
 
-        public Surface(Vector2i size)
-        {
-            if (size.X < 1 || size.Y < 1)
-                throw new ArgumentException("Width and height must be >1");
-            if (size.X > 10000 || size.Y > 10000)
-                throw new ArgumentException("Width and height must be <=10000");
+    public Surface(Vector2i size)
+    {
+        if (size.X < 1 || size.Y < 1)
+            throw new ArgumentException("Width and height must be >1");
+        if (size.X > 10000 || size.Y > 10000)
+            throw new ArgumentException("Width and height must be <=10000");
 
-            Size = size;
+        Size = size;
 
-            bytesPerPixel = 8;
-            PixelBuffer = CreateBuffer(size.X, size.Y, bytesPerPixel);
-            SkiaSurface = CreateSKSurface();
-        }
+        bytesPerPixel = 8;
+        PixelBuffer = CreateBuffer(size.X, size.Y, bytesPerPixel);
+        SkiaSurface = CreateSKSurface();
+    }
 
-        public Surface(Surface original) : this(original.Size)
-        {
-            SkiaSurface.Canvas.DrawSurface(original.SkiaSurface, 0, 0);
-        }
+    public Surface(Surface original) : this(original.Size)
+    {
+        SkiaSurface.Canvas.DrawSurface(original.SkiaSurface, 0, 0);
+    }
 
-        public unsafe void CopyTo(Surface other)
-        {
-            if (other.Size != Size)
-                throw new ArgumentException("Target Surface must have the same dimensions");
-            int bytesC = Size.X * Size.Y * bytesPerPixel;
-            Buffer.MemoryCopy((void*)PixelBuffer, (void*)other.PixelBuffer, bytesC, bytesC);
-        }
+    public unsafe void CopyTo(Surface other)
+    {
+        if (other.Size != Size)
+            throw new ArgumentException("Target Surface must have the same dimensions");
+        int bytesC = Size.X * Size.Y * bytesPerPixel;
+        Buffer.MemoryCopy((void*)PixelBuffer, (void*)other.PixelBuffer, bytesC, bytesC);
+    }
 
-        public unsafe SKColor GetSRGBPixel(int x, int y)
-        {
-            throw new NotImplementedException("This function needs to be changed to account for linear -> srgb conversion");
-            /*
-            Half* ptr = (Half*)(PixelBuffer + (x + y * Size.X) * bytesPerPixel);
-            float a = (float)ptr[3];
-            return (SKColor)new SKColorF((float)ptr[0] / a, (float)ptr[1] / a, (float)ptr[2] / a, (float)ptr[3]);*/
-        }
+    public unsafe SKColor GetSRGBPixel(int x, int y)
+    {
+        throw new NotImplementedException("This function needs to be changed to account for linear -> srgb conversion");
+        /*
+        Half* ptr = (Half*)(PixelBuffer + (x + y * Size.X) * bytesPerPixel);
+        float a = (float)ptr[3];
+        return (SKColor)new SKColorF((float)ptr[0] / a, (float)ptr[1] / a, (float)ptr[2] / a, (float)ptr[3]);*/
+    }
 
-        public unsafe bool IsFullyTransparent()
+    public unsafe bool IsFullyTransparent()
+    {
+        ulong* ptr = (ulong*)PixelBuffer;
+        for (int i = 0; i < Size.X * Size.Y; i++)
         {
-            ulong* ptr = (ulong*)PixelBuffer;
-            for (int i = 0; i < Size.X * Size.Y; i++)
-            {
-                // ptr[i] actually contains 4 16-bit floats. We only care about the first one which is alpha.
-                // An empty pixel can have alpha of 0 or -0 (not sure if -0 actually ever comes up). 0 in hex is 0x0, -0 in hex is 0x8000
-                if ((ptr[i] & 0x1111_0000_0000_0000) != 0 && (ptr[i] & 0x1111_0000_0000_0000) != 0x8000_0000_0000_0000)
-                    return false;
-            }
-            return true;
+            // ptr[i] actually contains 4 16-bit floats. We only care about the first one which is alpha.
+            // An empty pixel can have alpha of 0 or -0 (not sure if -0 actually ever comes up). 0 in hex is 0x0, -0 in hex is 0x8000
+            if ((ptr[i] & 0x1111_0000_0000_0000) != 0 && (ptr[i] & 0x1111_0000_0000_0000) != 0x8000_0000_0000_0000)
+                return false;
         }
+        return true;
+    }
 
-        public void SaveToDesktop(string filename = "savedSurface.png")
+    public void SaveToDesktop(string filename = "savedSurface.png")
+    {
+        using var final = SKSurface.Create(new SKImageInfo(Size.X, Size.Y, SKColorType.Rgba8888, SKAlphaType.Premul, SKColorSpace.CreateSrgb()));
+        final.Canvas.DrawSurface(SkiaSurface, 0, 0);
+        using (var snapshot = final.Snapshot())
         {
-            using var final = SKSurface.Create(new SKImageInfo(Size.X, Size.Y, SKColorType.Rgba8888, SKAlphaType.Premul, SKColorSpace.CreateSrgb()));
-            final.Canvas.DrawSurface(SkiaSurface, 0, 0);
-            using (var snapshot = final.Snapshot())
-            {
-                using var stream = File.Create(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), filename));
-                using var png = snapshot.Encode();
-                png.SaveTo(stream);
-            }
+            using var stream = File.Create(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory), filename));
+            using var png = snapshot.Encode();
+            png.SaveTo(stream);
         }
+    }
 
-        private SKSurface CreateSKSurface()
-        {
-            var surface = SKSurface.Create(new SKImageInfo(Size.X, Size.Y, SKColorType.RgbaF16, SKAlphaType.Premul, SKColorSpace.CreateSrgbLinear()), PixelBuffer);
-            if (surface is null)
-                throw new InvalidOperationException($"Could not create surface (Size:{Size})");
-            return surface;
-        }
+    private SKSurface CreateSKSurface()
+    {
+        var surface = SKSurface.Create(new SKImageInfo(Size.X, Size.Y, SKColorType.RgbaF16, SKAlphaType.Premul, SKColorSpace.CreateSrgbLinear()), PixelBuffer);
+        if (surface is null)
+            throw new InvalidOperationException($"Could not create surface (Size:{Size})");
+        return surface;
+    }
 
-        private unsafe static IntPtr CreateBuffer(int width, int height, int bytesPerPixel)
-        {
-            int byteC = width * height * bytesPerPixel;
-            var buffer = Marshal.AllocHGlobal(byteC);
-            Unsafe.InitBlockUnaligned((byte*)buffer, 0, (uint)byteC);
-            return buffer;
-        }
+    private unsafe static IntPtr CreateBuffer(int width, int height, int bytesPerPixel)
+    {
+        int byteC = width * height * bytesPerPixel;
+        var buffer = Marshal.AllocHGlobal(byteC);
+        Unsafe.InitBlockUnaligned((byte*)buffer, 0, (uint)byteC);
+        return buffer;
+    }
 
-        public void Dispose()
-        {
-            if (disposed)
-                return;
-            disposed = true;
-            Marshal.FreeHGlobal(PixelBuffer);
-            GC.SuppressFinalize(this);
-        }
+    public void Dispose()
+    {
+        if (disposed)
+            return;
+        disposed = true;
+        Marshal.FreeHGlobal(PixelBuffer);
+        GC.SuppressFinalize(this);
+    }
 
-        ~Surface()
-        {
-            Marshal.FreeHGlobal(PixelBuffer);
-        }
+    ~Surface()
+    {
+        Marshal.FreeHGlobal(PixelBuffer);
     }
 }

+ 28 - 29
src/ChunkyImageLibTest/ClearRegionOperationTests.cs

@@ -4,38 +4,37 @@ using ChunkyImageLib.Operations;
 using System.Collections.Generic;
 using Xunit;
 
-namespace ChunkyImageLibTest
+namespace ChunkyImageLibTest;
+
+public class ClearRegionOperationTests
 {
-    public class ClearRegionOperationTests
+    const int chunkSize = ChunkPool.FullChunkSize;
+    [Fact]
+    public void FindAffectedChunks_SingleChunk_ReturnsSingleChunk()
     {
-        const int chunkSize = ChunkPool.FullChunkSize;
-        [Fact]
-        public void FindAffectedChunks_SingleChunk_ReturnsSingleChunk()
-        {
-            ClearRegionOperation operation = new(new(chunkSize, chunkSize), new(chunkSize, chunkSize));
-            var expected = new HashSet<Vector2i>() { new(1, 1) };
-            var actual = operation.FindAffectedChunks();
-            Assert.Equal(expected, actual);
-        }
+        ClearRegionOperation operation = new(new(chunkSize, chunkSize), new(chunkSize, chunkSize));
+        var expected = new HashSet<Vector2i>() { new(1, 1) };
+        var actual = operation.FindAffectedChunks();
+        Assert.Equal(expected, actual);
+    }
 
- // to keep expected aligned
+// to keep expected aligned
 #pragma warning disable format
-        [Fact]
-        public void FindAffectedChunks_BigArea_ReturnsCorrectChunks()
-        {
-            int from = -chunkSize - chunkSize / 2;
-            int to = chunkSize + chunkSize / 2;
-            ClearRegionOperation operation = new(new(from, from), new(to - from, to - from));
-            var expected = new HashSet<Vector2i>() 
-            { 
-                new(-2, -2), new(-1, -2), new(0, -2), new(1, -2),
-                new(-2, -1), new(-1, -1), new(0, -1), new(1, -1),
-                new(-2, -0), new(-1, -0), new(0, -0), new(1, -0),
-                new(-2,  1), new(-1,  1), new(0,  1), new(1,  1),
-            };
-            var actual = operation.FindAffectedChunks();
-            Assert.Equal(expected, actual);
-        }
-#pragma warning restore format
+    [Fact]
+    public void FindAffectedChunks_BigArea_ReturnsCorrectChunks()
+    {
+        int from = -chunkSize - chunkSize / 2;
+        int to = chunkSize + chunkSize / 2;
+        ClearRegionOperation operation = new(new(from, from), new(to - from, to - from));
+        var expected = new HashSet<Vector2i>() 
+        { 
+            new(-2, -2), new(-1, -2), new(0, -2), new(1, -2),
+            new(-2, -1), new(-1, -1), new(0, -1), new(1, -1),
+            new(-2, -0), new(-1, -0), new(0, -0), new(1, -0),
+            new(-2,  1), new(-1,  1), new(0,  1), new(1,  1),
+        };
+        var actual = operation.FindAffectedChunks();
+        Assert.Equal(expected, actual);
     }
+#pragma warning restore format
 }

+ 28 - 29
src/ChunkyImageLibTest/OperationHelperTests.cs

@@ -2,37 +2,36 @@ using ChunkyImageLib.DataHolders;
 using ChunkyImageLib.Operations;
 using Xunit;
 
-namespace ChunkyImageLibTest
+namespace ChunkyImageLibTest;
+
+public class OperationHelperTests
 {
-    public class OperationHelperTests
+    [Theory]
+    [InlineData(0, 0, 0, 0)]
+    [InlineData(-1, -1, -1, -1)]
+    [InlineData(32, 32, 1, 1)]
+    [InlineData(-32, -32, -1, -1)]
+    [InlineData(-33, -33, -2, -2)]
+    public void GetChunkPos_32ChunkSize_ReturnsCorrectValues(int x, int y, int expX, int expY)
     {
-        [Theory]
-        [InlineData(0, 0, 0, 0)]
-        [InlineData(-1, -1, -1, -1)]
-        [InlineData(32, 32, 1, 1)]
-        [InlineData(-32, -32, -1, -1)]
-        [InlineData(-33, -33, -2, -2)]
-        public void GetChunkPos_32ChunkSize_ReturnsCorrectValues(int x, int y, int expX, int expY)
-        {
-            Vector2i act = OperationHelper.GetChunkPos(new(x, y), 32);
-            Assert.Equal(expX, act.X);
-            Assert.Equal(expY, act.Y);
-        }
+        Vector2i act = OperationHelper.GetChunkPos(new(x, y), 32);
+        Assert.Equal(expX, act.X);
+        Assert.Equal(expY, act.Y);
+    }
 
-        [Theory]
-        [InlineData(0, 0, true, true, 0, 0)]
-        [InlineData(0, 0, false, true, -1, 0)]
-        [InlineData(0, 0, true, false, 0, -1)]
-        [InlineData(0, 0, false, false, -1, -1)]
-        [InlineData(48.5, 48.5, true, true, 1, 1)]
-        [InlineData(48.5, 48.5, false, true, 1, 1)]
-        [InlineData(48.5, 48.5, true, false, 1, 1)]
-        [InlineData(48.5, 48.5, false, false, 1, 1)]
-        public void GetChunkPosBiased_32ChunkSize_ReturnsCorrectValues(double x, double y, bool positiveX, bool positiveY, int expX, int expY)
-        {
-            Vector2i act = OperationHelper.GetChunkPosBiased(new(x, y), positiveX, positiveY, 32);
-            Assert.Equal(expX, act.X);
-            Assert.Equal(expY, act.Y);
-        }
+    [Theory]
+    [InlineData(0, 0, true, true, 0, 0)]
+    [InlineData(0, 0, false, true, -1, 0)]
+    [InlineData(0, 0, true, false, 0, -1)]
+    [InlineData(0, 0, false, false, -1, -1)]
+    [InlineData(48.5, 48.5, true, true, 1, 1)]
+    [InlineData(48.5, 48.5, false, true, 1, 1)]
+    [InlineData(48.5, 48.5, true, false, 1, 1)]
+    [InlineData(48.5, 48.5, false, false, 1, 1)]
+    public void GetChunkPosBiased_32ChunkSize_ReturnsCorrectValues(double x, double y, bool positiveX, bool positiveY, int expX, int expY)
+    {
+        Vector2i act = OperationHelper.GetChunkPosBiased(new(x, y), positiveX, positiveY, 32);
+        Assert.Equal(expX, act.X);
+        Assert.Equal(expY, act.Y);
     }
 }

+ 97 - 98
src/ChunkyImageLibTest/RectangleOperationTests.cs

@@ -5,118 +5,117 @@ using SkiaSharp;
 using System.Collections.Generic;
 using Xunit;
 
-namespace ChunkyImageLibTest
+namespace ChunkyImageLibTest;
+
+public class RectangleOperationTests
 {
-    public class RectangleOperationTests
-    {
-        const int chunkSize = ChunkPool.FullChunkSize;
+    const int chunkSize = ChunkPool.FullChunkSize;
 // to keep expected rectangles aligned
 #pragma warning disable format
-        [Fact]
-        public void FindAffectedChunks_SmallStrokeOnly_FindsCorrectChunks()
-        {
-            var (x, y, w, h) = (0, 0, chunkSize, chunkSize);
-            RectangleOperation operation = new(new(new(x, y), new(w, h), 1, SKColors.Black, SKColors.Transparent));
+    [Fact]
+    public void FindAffectedChunks_SmallStrokeOnly_FindsCorrectChunks()
+    {
+        var (x, y, w, h) = (0, 0, chunkSize, chunkSize);
+        RectangleOperation operation = new(new(new(x, y), new(w, h), 1, SKColors.Black, SKColors.Transparent));
 
-            HashSet<Vector2i> expected = new() { new(0, 0) };
-            var actual = operation.FindAffectedChunks();
+        HashSet<Vector2i> expected = new() { new(0, 0) };
+        var actual = operation.FindAffectedChunks();
 
-            Assert.Equal(expected, actual);
-        }
+        Assert.Equal(expected, actual);
+    }
 
-        [Fact]
-        public void FindAffectedChunks_2by2StrokeOnly_FindsCorrectChunks()
-        {
-            var (x, y, w, h) = (-chunkSize, -chunkSize, chunkSize * 2, chunkSize * 2);
-            RectangleOperation operation = new(new(new(x, y), new(w, h), 1, SKColors.Black, SKColors.Transparent));
+    [Fact]
+    public void FindAffectedChunks_2by2StrokeOnly_FindsCorrectChunks()
+    {
+        var (x, y, w, h) = (-chunkSize, -chunkSize, chunkSize * 2, chunkSize * 2);
+        RectangleOperation operation = new(new(new(x, y), new(w, h), 1, SKColors.Black, SKColors.Transparent));
+
+        HashSet<Vector2i> expected = new() { new(-1, -1), new(0, -1), new(-1, 0), new(0, 0) };
+        var actual = operation.FindAffectedChunks();
 
-            HashSet<Vector2i> expected = new() { new(-1, -1), new(0, -1), new(-1, 0), new(0, 0) };
-            var actual = operation.FindAffectedChunks();
+        Assert.Equal(expected, actual);
+    }
 
-            Assert.Equal(expected, actual);
-        }
+    [Fact]
+    public void FindAffectedChunks_3x3PositiveStrokeOnly_FindsCorrectChunks()
+    {
+        var (x, y, w, h) = (chunkSize + chunkSize / 2, chunkSize + chunkSize / 2, chunkSize * 2, chunkSize * 2);
+        RectangleOperation operation = new(new(new(x, y), new(w, h), 1, SKColors.Black, SKColors.Transparent));
 
-        [Fact]
-        public void FindAffectedChunks_3x3PositiveStrokeOnly_FindsCorrectChunks()
+        HashSet<Vector2i> expected = new()
         {
-            var (x, y, w, h) = (chunkSize + chunkSize / 2, chunkSize + chunkSize / 2, chunkSize * 2, chunkSize * 2);
-            RectangleOperation operation = new(new(new(x, y), new(w, h), 1, SKColors.Black, SKColors.Transparent));
-
-            HashSet<Vector2i> expected = new()
-            {
-                new(1, 1), new(2, 1), new(3, 1),
-                new(1, 2),            new(3, 2),
-                new(1, 3), new(2, 3), new(3, 3),
-            };
-            var actual = operation.FindAffectedChunks();
-
-            Assert.Equal(expected, actual);
-        }
-
-        [Fact]
-        public void FindAffectedChunks_3x3NegativeStrokeOnly_FindsCorrectChunks()
-        {
-            var (x, y, w, h) = (-chunkSize * 3 - chunkSize / 2, -chunkSize * 3 - chunkSize / 2, chunkSize * 2, chunkSize * 2);
-            RectangleOperation operation = new(new(new(x, y), new(w, h), 1, SKColors.Black, SKColors.Transparent));
-
-            HashSet<Vector2i> expected = new()
-            {
-                new(-4, -4), new(-3, -4), new(-2, -4),
-                new(-4, -3),              new(-2, -3),
-                new(-4, -2), new(-3, -2), new(-2, -2),
-            };
-            var actual = operation.FindAffectedChunks();
-
-            Assert.Equal(expected, actual);
-        }
-
-        [Fact]
-        public void FindAffectedChunks_3x3PositiveFilled_FindsCorrectChunks()
+            new(1, 1), new(2, 1), new(3, 1),
+            new(1, 2),            new(3, 2),
+            new(1, 3), new(2, 3), new(3, 3),
+        };
+        var actual = operation.FindAffectedChunks();
+
+        Assert.Equal(expected, actual);
+    }
+
+    [Fact]
+    public void FindAffectedChunks_3x3NegativeStrokeOnly_FindsCorrectChunks()
+    {
+        var (x, y, w, h) = (-chunkSize * 3 - chunkSize / 2, -chunkSize * 3 - chunkSize / 2, chunkSize * 2, chunkSize * 2);
+        RectangleOperation operation = new(new(new(x, y), new(w, h), 1, SKColors.Black, SKColors.Transparent));
+
+        HashSet<Vector2i> expected = new()
         {
-            var (x, y, w, h) = (chunkSize + chunkSize / 2, chunkSize + chunkSize / 2, chunkSize * 2, chunkSize * 2);
-            RectangleOperation operation = new(new(new(x, y), new(w, h), 1, SKColors.Black, SKColors.White));
-
-            HashSet<Vector2i> expected = new()
-            {
-                new(1, 1), new(2, 1), new(3, 1), 
-                new(1, 2), new(2, 2), new(3, 2),
-                new(1, 3), new(2, 3), new(3, 3),
-            };
-            var actual = operation.FindAffectedChunks();
-
-            Assert.Equal(expected, actual);
-        }
-
-        [Fact]
-        public void FindAffectedChunks_ThickPositiveStroke_FindsCorrectChunks()
+            new(-4, -4), new(-3, -4), new(-2, -4),
+            new(-4, -3),              new(-2, -3),
+            new(-4, -2), new(-3, -2), new(-2, -2),
+        };
+        var actual = operation.FindAffectedChunks();
+
+        Assert.Equal(expected, actual);
+    }
+
+    [Fact]
+    public void FindAffectedChunks_3x3PositiveFilled_FindsCorrectChunks()
+    {
+        var (x, y, w, h) = (chunkSize + chunkSize / 2, chunkSize + chunkSize / 2, chunkSize * 2, chunkSize * 2);
+        RectangleOperation operation = new(new(new(x, y), new(w, h), 1, SKColors.Black, SKColors.White));
+
+        HashSet<Vector2i> expected = new()
         {
-            var (x, y, w, h) = (chunkSize / 2, chunkSize / 2, chunkSize * 4, chunkSize * 4);
-            RectangleOperation operation = new(new(new(x, y), new(w, h), chunkSize, SKColors.Black, SKColors.Transparent));
-
-            HashSet<Vector2i> expected = new()
-            {
-                new(0, 0), new(1, 0), new(2, 0), new(3, 0), new(4, 0),
-                new(0, 1), new(1, 1), new(2, 1), new(3, 1), new(4, 1),
-                new(0, 2), new(1, 2),            new(3, 2), new(4, 2),
-                new(0, 3), new(1, 3), new(2, 3), new(3, 3), new(4, 3),
-                new(0, 4), new(1, 4), new(2, 4), new(3, 4), new(4, 4),
-            };
-            var actual = operation.FindAffectedChunks();
-
-            Assert.Equal(expected, actual);
-        }
-
-        [Fact]
-        public void FindAffectedChunks_SmallButThick_FindsCorrectChunks()
+            new(1, 1), new(2, 1), new(3, 1), 
+            new(1, 2), new(2, 2), new(3, 2),
+            new(1, 3), new(2, 3), new(3, 3),
+        };
+        var actual = operation.FindAffectedChunks();
+
+        Assert.Equal(expected, actual);
+    }
+
+    [Fact]
+    public void FindAffectedChunks_ThickPositiveStroke_FindsCorrectChunks()
+    {
+        var (x, y, w, h) = (chunkSize / 2, chunkSize / 2, chunkSize * 4, chunkSize * 4);
+        RectangleOperation operation = new(new(new(x, y), new(w, h), chunkSize, SKColors.Black, SKColors.Transparent));
+
+        HashSet<Vector2i> expected = new()
         {
-            var (x, y, w, h) = (chunkSize / 2, chunkSize / 2, 1, 1);
-            RectangleOperation operation = new(new(new(x, y), new(w, h), 256, SKColors.Black, SKColors.White));
+            new(0, 0), new(1, 0), new(2, 0), new(3, 0), new(4, 0),
+            new(0, 1), new(1, 1), new(2, 1), new(3, 1), new(4, 1),
+            new(0, 2), new(1, 2),            new(3, 2), new(4, 2),
+            new(0, 3), new(1, 3), new(2, 3), new(3, 3), new(4, 3),
+            new(0, 4), new(1, 4), new(2, 4), new(3, 4), new(4, 4),
+        };
+        var actual = operation.FindAffectedChunks();
+
+        Assert.Equal(expected, actual);
+    }
 
-            HashSet<Vector2i> expected = new() { new(0, 0) };
-            var actual = operation.FindAffectedChunks();
+    [Fact]
+    public void FindAffectedChunks_SmallButThick_FindsCorrectChunks()
+    {
+        var (x, y, w, h) = (chunkSize / 2, chunkSize / 2, 1, 1);
+        RectangleOperation operation = new(new(new(x, y), new(w, h), 256, SKColors.Black, SKColors.White));
 
-            Assert.Equal(expected, actual);
-        }
-#pragma warning restore format
+        HashSet<Vector2i> expected = new() { new(0, 0) };
+        var actual = operation.FindAffectedChunks();
+
+        Assert.Equal(expected, actual);
     }
+#pragma warning restore format
 }

+ 6 - 7
src/ChunkyImageLibVis/App.xaml.cs

@@ -6,12 +6,11 @@ using System.Linq;
 using System.Threading.Tasks;
 using System.Windows;
 
-namespace ChunkyImageLibVis
+namespace ChunkyImageLibVis;
+
+/// <summary>
+/// Interaction logic for App.xaml
+/// </summary>
+public partial class App : Application
 {
-    /// <summary>
-    /// Interaction logic for App.xaml
-    /// </summary>
-    public partial class App : Application
-    {
-    }
 }

+ 152 - 153
src/ChunkyImageLibVis/MainWindow.xaml.cs

@@ -9,198 +9,197 @@ using System.Windows.Input;
 using System.Windows.Media;
 using System.Windows.Shapes;
 
-namespace ChunkyImageLibVis
+namespace ChunkyImageLibVis;
+
+/// <summary>
+/// Interaction logic for MainWindow.xaml
+/// </summary>
+public partial class MainWindow : Window, INotifyPropertyChanged
 {
-    /// <summary>
-    /// Interaction logic for MainWindow.xaml
-    /// </summary>
-    public partial class MainWindow : Window, INotifyPropertyChanged
-    {
-        private double x1;
-        private double y1;
-        private double x2;
-        private double y2;
+    private double x1;
+    private double y1;
+    private double x2;
+    private double y2;
 
-        public double X1
+    public double X1
+    {
+        get => x1;
+        set
         {
-            get => x1;
-            set
-            {
-                x1 = value;
-                PropertyChanged?.Invoke(this, new(nameof(X1)));
-                PropertyChanged?.Invoke(this, new(nameof(RectWidth)));
-                PropertyChanged?.Invoke(this, new(nameof(HalfRectWidth)));
-            }
+            x1 = value;
+            PropertyChanged?.Invoke(this, new(nameof(X1)));
+            PropertyChanged?.Invoke(this, new(nameof(RectWidth)));
+            PropertyChanged?.Invoke(this, new(nameof(HalfRectWidth)));
         }
-        public double X2
+    }
+    public double X2
+    {
+        get => x2;
+        set
         {
-            get => x2;
-            set
-            {
-                x2 = value;
-                PropertyChanged?.Invoke(this, new(nameof(X2)));
-                PropertyChanged?.Invoke(this, new(nameof(RectWidth)));
-                PropertyChanged?.Invoke(this, new(nameof(HalfRectWidth)));
-            }
+            x2 = value;
+            PropertyChanged?.Invoke(this, new(nameof(X2)));
+            PropertyChanged?.Invoke(this, new(nameof(RectWidth)));
+            PropertyChanged?.Invoke(this, new(nameof(HalfRectWidth)));
         }
-        public double Y1
+    }
+    public double Y1
+    {
+        get => y1;
+        set
         {
-            get => y1;
-            set
-            {
-                y1 = value;
-                PropertyChanged?.Invoke(this, new(nameof(Y1)));
-                PropertyChanged?.Invoke(this, new(nameof(RectHeight)));
-                PropertyChanged?.Invoke(this, new(nameof(HalfRectHeight)));
-            }
+            y1 = value;
+            PropertyChanged?.Invoke(this, new(nameof(Y1)));
+            PropertyChanged?.Invoke(this, new(nameof(RectHeight)));
+            PropertyChanged?.Invoke(this, new(nameof(HalfRectHeight)));
         }
-        public double Y2
+    }
+    public double Y2
+    {
+        get => y2;
+        set
         {
-            get => y2;
-            set
-            {
-                y2 = value;
-                PropertyChanged?.Invoke(this, new(nameof(Y2)));
-                PropertyChanged?.Invoke(this, new(nameof(RectHeight)));
-                PropertyChanged?.Invoke(this, new(nameof(HalfRectHeight)));
-            }
+            y2 = value;
+            PropertyChanged?.Invoke(this, new(nameof(Y2)));
+            PropertyChanged?.Invoke(this, new(nameof(RectHeight)));
+            PropertyChanged?.Invoke(this, new(nameof(HalfRectHeight)));
         }
+    }
 
-        public double RectWidth { get => Math.Abs(X2 - X1); }
-        public double RectHeight { get => Math.Abs(Y2 - Y1); }
+    public double RectWidth { get => Math.Abs(X2 - X1); }
+    public double RectHeight { get => Math.Abs(Y2 - Y1); }
 
-        public double HalfRectWidth { get => Math.Abs(X2 - X1) / 2; }
-        public double HalfRectHeight { get => Math.Abs(Y2 - Y1) / 2; }
+    public double HalfRectWidth { get => Math.Abs(X2 - X1) / 2; }
+    public double HalfRectHeight { get => Math.Abs(Y2 - Y1) / 2; }
 
 
-        private double angle;
-        public double Angle
+    private double angle;
+    public double Angle
+    {
+        get => angle;
+        set
         {
-            get => angle;
-            set
-            {
-                angle = value;
-                PropertyChanged?.Invoke(this, new(nameof(Angle)));
-            }
+            angle = value;
+            PropertyChanged?.Invoke(this, new(nameof(Angle)));
         }
+    }
 
-        public MainWindow()
-        {
-            InitializeComponent();
-            DataContext = this;
-            CreateGrid();
-        }
+    public MainWindow()
+    {
+        InitializeComponent();
+        DataContext = this;
+        CreateGrid();
+    }
 
-        public void CreateGrid()
+    public void CreateGrid()
+    {
+        for (int i = 0; i < 20; i++)
         {
-            for (int i = 0; i < 20; i++)
+            Line ver = new()
             {
-                Line ver = new()
-                {
-                    X1 = i * 32,
-                    X2 = i * 32,
-                    Y1 = 0,
-                    Y2 = 1000,
-                    Stroke = Brushes.Gray,
-                    StrokeThickness = 1,
-                };
-                Line hor = new()
-                {
-                    X1 = 0,
-                    X2 = 1000,
-                    Y1 = i * 32,
-                    Y2 = i * 32,
-                    Stroke = Brushes.Gray,
-                    StrokeThickness = 1,
-                };
-                canvas.Children.Add(ver);
-                canvas.Children.Add(hor);
-            }
+                X1 = i * 32,
+                X2 = i * 32,
+                Y1 = 0,
+                Y2 = 1000,
+                Stroke = Brushes.Gray,
+                StrokeThickness = 1,
+            };
+            Line hor = new()
+            {
+                X1 = 0,
+                X2 = 1000,
+                Y1 = i * 32,
+                Y2 = i * 32,
+                Stroke = Brushes.Gray,
+                StrokeThickness = 1,
+            };
+            canvas.Children.Add(ver);
+            canvas.Children.Add(hor);
         }
+    }
 
-        public List<Rectangle> rectangles = new();
-        private void UpdateChunks()
+    public List<Rectangle> rectangles = new();
+    private void UpdateChunks()
+    {
+        foreach (var rect in rectangles)
         {
-            foreach (var rect in rectangles)
-            {
-                canvas.Children.Remove(rect);
-            }
-            rectangles.Clear();
-            var chunks = OperationHelper.FindChunksTouchingRectangle(new Vector2d(X1 + HalfRectWidth, Y1 + HalfRectHeight), new(X2 - X1, Y2 - Y1), Angle * Math.PI / 180, 32);
-            var innerChunks = OperationHelper.FindChunksFullyInsideRectangle(new Vector2d(X1 + HalfRectWidth, Y1 + HalfRectHeight), new(X2 - X1, Y2 - Y1), Angle * Math.PI / 180, 32);
-            chunks.ExceptWith(innerChunks);
-            foreach (var chunk in chunks)
+            canvas.Children.Remove(rect);
+        }
+        rectangles.Clear();
+        var chunks = OperationHelper.FindChunksTouchingRectangle(new Vector2d(X1 + HalfRectWidth, Y1 + HalfRectHeight), new(X2 - X1, Y2 - Y1), Angle * Math.PI / 180, 32);
+        var innerChunks = OperationHelper.FindChunksFullyInsideRectangle(new Vector2d(X1 + HalfRectWidth, Y1 + HalfRectHeight), new(X2 - X1, Y2 - Y1), Angle * Math.PI / 180, 32);
+        chunks.ExceptWith(innerChunks);
+        foreach (var chunk in chunks)
+        {
+            Rectangle rectangle = new()
             {
-                Rectangle rectangle = new()
-                {
-                    Fill = Brushes.Green,
-                    Width = 32,
-                    Height = 32,
-                };
-                Canvas.SetLeft(rectangle, chunk.X * 32);
-                Canvas.SetTop(rectangle, chunk.Y * 32);
-                canvas.Children.Add(rectangle);
-                rectangles.Add(rectangle);
-            }
+                Fill = Brushes.Green,
+                Width = 32,
+                Height = 32,
+            };
+            Canvas.SetLeft(rectangle, chunk.X * 32);
+            Canvas.SetTop(rectangle, chunk.Y * 32);
+            canvas.Children.Add(rectangle);
+            rectangles.Add(rectangle);
         }
+    }
 
-        public event PropertyChangedEventHandler? PropertyChanged;
+    public event PropertyChangedEventHandler? PropertyChanged;
 
-        private bool drawing = false;
-        private bool rotating = false;
-        private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
+    private bool drawing = false;
+    private bool rotating = false;
+    private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
+    {
+        if (rotating)
+        {
+            rotating = false;
+            return;
+        }
+        drawing = true;
+        Angle = 0;
+        var pos = e.GetPosition(canvas);
+        if (e.LeftButton == MouseButtonState.Pressed)
+        {
+            X1 = pos.X;
+            Y1 = pos.Y;
+        }
+        else
+        {
+            X1 = Math.Floor(pos.X / 32) * 32;
+            Y1 = Math.Floor(pos.Y / 32) * 32;
+        }
+    }
+
+    private void Canvas_MouseMove(object sender, MouseEventArgs e)
+    {
+        var pos = e.GetPosition(canvas);
+        if (drawing)
         {
-            if (rotating)
-            {
-                rotating = false;
-                return;
-            }
-            drawing = true;
-            Angle = 0;
-            var pos = e.GetPosition(canvas);
             if (e.LeftButton == MouseButtonState.Pressed)
             {
-                X1 = pos.X;
-                Y1 = pos.Y;
+                X2 = pos.X;
+                Y2 = pos.Y;
             }
             else
             {
-                X1 = Math.Floor(pos.X / 32) * 32;
-                Y1 = Math.Floor(pos.Y / 32) * 32;
+                X2 = Math.Floor(pos.X / 32) * 32;
+                Y2 = Math.Floor(pos.Y / 32) * 32;
             }
         }
-
-        private void Canvas_MouseMove(object sender, MouseEventArgs e)
+        else if (rotating)
         {
-            var pos = e.GetPosition(canvas);
-            if (drawing)
-            {
-                if (e.LeftButton == MouseButtonState.Pressed)
-                {
-                    X2 = pos.X;
-                    Y2 = pos.Y;
-                }
-                else
-                {
-                    X2 = Math.Floor(pos.X / 32) * 32;
-                    Y2 = Math.Floor(pos.Y / 32) * 32;
-                }
-            }
-            else if (rotating)
-            {
-                Vector2d center = new Vector2d(X1 + HalfRectWidth, Y1 + HalfRectHeight);
-                Angle = new Vector2d(pos.X - center.X, pos.Y - center.Y).CCWAngleTo(new Vector2d(X2 - center.X, Y2 - center.Y)) * -180 / Math.PI;
-            }
-            UpdateChunks();
+            Vector2d center = new Vector2d(X1 + HalfRectWidth, Y1 + HalfRectHeight);
+            Angle = new Vector2d(pos.X - center.X, pos.Y - center.Y).CCWAngleTo(new Vector2d(X2 - center.X, Y2 - center.Y)) * -180 / Math.PI;
         }
+        UpdateChunks();
+    }
 
-        private void Canvas_MouseUp(object sender, MouseButtonEventArgs e)
+    private void Canvas_MouseUp(object sender, MouseButtonEventArgs e)
+    {
+        if (drawing)
         {
-            if (drawing)
-            {
-                drawing = false;
-                rotating = true;
-            }
+            drawing = false;
+            rotating = true;
         }
     }
 }

+ 12 - 13
src/PixiEditor.ChangeableDocument/Actions/Drawing/CombineStructureMembersOnto_Action.cs

@@ -1,21 +1,20 @@
 using PixiEditor.ChangeableDocument.Changes;
 using PixiEditor.ChangeableDocument.Changes.Drawing;
 
-namespace PixiEditor.ChangeableDocument.Actions.Drawing
+namespace PixiEditor.ChangeableDocument.Actions.Drawing;
+
+public record class CombineStructureMembersOnto_Action : IMakeChangeAction
 {
-    public record class CombineStructureMembersOnto_Action : IMakeChangeAction
+    public CombineStructureMembersOnto_Action(Guid targetLayer, HashSet<Guid> membersToCombine)
     {
-        public CombineStructureMembersOnto_Action(Guid targetLayer, HashSet<Guid> membersToCombine)
-        {
-            TargetLayer = targetLayer;
-            MembersToCombine = membersToCombine;
-        }
+        TargetLayer = targetLayer;
+        MembersToCombine = membersToCombine;
+    }
 
-        public Guid TargetLayer { get; }
-        public HashSet<Guid> MembersToCombine { get; }
-        Change IMakeChangeAction.CreateCorrespondingChange()
-        {
-            return new CombineStructureMembersOnto_Change(MembersToCombine, TargetLayer);
-        }
+    public Guid TargetLayer { get; }
+    public HashSet<Guid> MembersToCombine { get; }
+    Change IMakeChangeAction.CreateCorrespondingChange()
+    {
+        return new CombineStructureMembersOnto_Change(MembersToCombine, TargetLayer);
     }
 }

+ 18 - 19
src/PixiEditor.ChangeableDocument/Actions/Drawing/Rectangle/DrawRectangle_Action.cs

@@ -2,29 +2,28 @@
 using PixiEditor.ChangeableDocument.Changes;
 using PixiEditor.ChangeableDocument.Changes.Drawing;
 
-namespace PixiEditor.ChangeableDocument.Actions.Drawing.Rectangle
+namespace PixiEditor.ChangeableDocument.Actions.Drawing.Rectangle;
+
+public record class DrawRectangle_Action : IStartOrUpdateChangeAction
 {
-    public record class DrawRectangle_Action : IStartOrUpdateChangeAction
+    public DrawRectangle_Action(Guid layerGuid, ShapeData rectangle, bool drawOnMask)
     {
-        public DrawRectangle_Action(Guid layerGuid, ShapeData rectangle, bool drawOnMask)
-        {
-            LayerGuid = layerGuid;
-            Rectangle = rectangle;
-            DrawOnMask = drawOnMask;
-        }
+        LayerGuid = layerGuid;
+        Rectangle = rectangle;
+        DrawOnMask = drawOnMask;
+    }
 
-        public Guid LayerGuid { get; }
-        public bool DrawOnMask { get; }
-        public ShapeData Rectangle { get; }
+    public Guid LayerGuid { get; }
+    public bool DrawOnMask { get; }
+    public ShapeData Rectangle { get; }
 
-        void IStartOrUpdateChangeAction.UpdateCorrespodingChange(UpdateableChange change)
-        {
-            ((DrawRectangle_UpdateableChange)change).Update(Rectangle);
-        }
+    void IStartOrUpdateChangeAction.UpdateCorrespodingChange(UpdateableChange change)
+    {
+        ((DrawRectangle_UpdateableChange)change).Update(Rectangle);
+    }
 
-        UpdateableChange IStartOrUpdateChangeAction.CreateCorrespondingChange()
-        {
-            return new DrawRectangle_UpdateableChange(LayerGuid, Rectangle, DrawOnMask);
-        }
+    UpdateableChange IStartOrUpdateChangeAction.CreateCorrespondingChange()
+    {
+        return new DrawRectangle_UpdateableChange(LayerGuid, Rectangle, DrawOnMask);
     }
 }

+ 5 - 6
src/PixiEditor.ChangeableDocument/Actions/Drawing/Rectangle/EndDrawRectangle_Action.cs

@@ -1,13 +1,12 @@
 using PixiEditor.ChangeableDocument.Changes;
 using PixiEditor.ChangeableDocument.Changes.Drawing;
 
-namespace PixiEditor.ChangeableDocument.Actions.Drawing.Rectangle
+namespace PixiEditor.ChangeableDocument.Actions.Drawing.Rectangle;
+
+public record class EndDrawRectangle_Action : IEndChangeAction
 {
-    public record class EndDrawRectangle_Action : IEndChangeAction
+    bool IEndChangeAction.IsChangeTypeMatching(Change change)
     {
-        bool IEndChangeAction.IsChangeTypeMatching(Change change)
-        {
-            return change is DrawRectangle_UpdateableChange;
-        }
+        return change is DrawRectangle_UpdateableChange;
     }
 }

+ 5 - 6
src/PixiEditor.ChangeableDocument/Actions/Drawing/Selection/ClearSelection_Action.cs

@@ -1,13 +1,12 @@
 using PixiEditor.ChangeableDocument.Changes;
 using PixiEditor.ChangeableDocument.Changes.Drawing;
 
-namespace PixiEditor.ChangeableDocument.Actions.Drawing.Selection
+namespace PixiEditor.ChangeableDocument.Actions.Drawing.Selection;
+
+public record class ClearSelection_Action : IMakeChangeAction
 {
-    public record class ClearSelection_Action : IMakeChangeAction
+    Change IMakeChangeAction.CreateCorrespondingChange()
     {
-        Change IMakeChangeAction.CreateCorrespondingChange()
-        {
-            return new ClearSelection_Change();
-        }
+        return new ClearSelection_Change();
     }
 }

+ 5 - 6
src/PixiEditor.ChangeableDocument/Actions/Drawing/Selection/EndSelectRectangle_Action.cs

@@ -1,13 +1,12 @@
 using PixiEditor.ChangeableDocument.Changes;
 using PixiEditor.ChangeableDocument.Changes.Drawing;
 
-namespace PixiEditor.ChangeableDocument.Actions.Drawing.Selection
+namespace PixiEditor.ChangeableDocument.Actions.Drawing.Selection;
+
+public record class EndSelectRectangle_Action : IEndChangeAction
 {
-    public record class EndSelectRectangle_Action : IEndChangeAction
+    bool IEndChangeAction.IsChangeTypeMatching(Change change)
     {
-        bool IEndChangeAction.IsChangeTypeMatching(Change change)
-        {
-            return change is SelectRectangle_UpdateableChange;
-        }
+        return change is SelectRectangle_UpdateableChange;
     }
 }

+ 16 - 17
src/PixiEditor.ChangeableDocument/Actions/Drawing/Selection/SelectRectangle_Action.cs

@@ -2,26 +2,25 @@
 using PixiEditor.ChangeableDocument.Changes;
 using PixiEditor.ChangeableDocument.Changes.Drawing;
 
-namespace PixiEditor.ChangeableDocument.Actions.Drawing.Selection
+namespace PixiEditor.ChangeableDocument.Actions.Drawing.Selection;
+
+public record class SelectRectangle_Action : IStartOrUpdateChangeAction
 {
-    public record class SelectRectangle_Action : IStartOrUpdateChangeAction
+    public Vector2i Pos { get; }
+    public Vector2i Size { get; }
+    public SelectRectangle_Action(Vector2i pos, Vector2i size)
     {
-        public Vector2i Pos { get; }
-        public Vector2i Size { get; }
-        public SelectRectangle_Action(Vector2i pos, Vector2i size)
-        {
-            Pos = pos;
-            Size = size;
-        }
+        Pos = pos;
+        Size = size;
+    }
 
-        UpdateableChange IStartOrUpdateChangeAction.CreateCorrespondingChange()
-        {
-            return new SelectRectangle_UpdateableChange(Pos, Size);
-        }
+    UpdateableChange IStartOrUpdateChangeAction.CreateCorrespondingChange()
+    {
+        return new SelectRectangle_UpdateableChange(Pos, Size);
+    }
 
-        void IStartOrUpdateChangeAction.UpdateCorrespodingChange(UpdateableChange change)
-        {
-            ((SelectRectangle_UpdateableChange)change).Update(Pos, Size);
-        }
+    void IStartOrUpdateChangeAction.UpdateCorrespodingChange(UpdateableChange change)
+    {
+        ((SelectRectangle_UpdateableChange)change).Update(Pos, Size);
     }
 }

+ 3 - 4
src/PixiEditor.ChangeableDocument/Actions/IAction.cs

@@ -1,6 +1,5 @@
-namespace PixiEditor.ChangeableDocument.Actions
+namespace PixiEditor.ChangeableDocument.Actions;
+
+public interface IAction
 {
-    public interface IAction
-    {
-    }
 }

+ 4 - 5
src/PixiEditor.ChangeableDocument/Actions/IEndChangeAction.cs

@@ -1,9 +1,8 @@
 using PixiEditor.ChangeableDocument.Changes;
 
-namespace PixiEditor.ChangeableDocument.Actions
+namespace PixiEditor.ChangeableDocument.Actions;
+
+internal interface IEndChangeAction : IAction
 {
-    internal interface IEndChangeAction : IAction
-    {
-        bool IsChangeTypeMatching(Change change);
-    }
+    bool IsChangeTypeMatching(Change change);
 }

+ 4 - 5
src/PixiEditor.ChangeableDocument/Actions/IMakeChangeAction.cs

@@ -1,9 +1,8 @@
 using PixiEditor.ChangeableDocument.Changes;
 
-namespace PixiEditor.ChangeableDocument.Actions
+namespace PixiEditor.ChangeableDocument.Actions;
+
+internal interface IMakeChangeAction : IAction
 {
-    internal interface IMakeChangeAction : IAction
-    {
-        Change CreateCorrespondingChange();
-    }
+    Change CreateCorrespondingChange();
 }

+ 5 - 6
src/PixiEditor.ChangeableDocument/Actions/IStartOrUpdateChangeAction.cs

@@ -1,10 +1,9 @@
 using PixiEditor.ChangeableDocument.Changes;
 
-namespace PixiEditor.ChangeableDocument.Actions
+namespace PixiEditor.ChangeableDocument.Actions;
+
+internal interface IStartOrUpdateChangeAction : IAction
 {
-    internal interface IStartOrUpdateChangeAction : IAction
-    {
-        void UpdateCorrespodingChange(UpdateableChange change);
-        UpdateableChange CreateCorrespondingChange();
-    }
+    void UpdateCorrespodingChange(UpdateableChange change);
+    UpdateableChange CreateCorrespondingChange();
 }

+ 11 - 12
src/PixiEditor.ChangeableDocument/Actions/Properties/CreateStructureMemberMask_Action.cs

@@ -1,20 +1,19 @@
 using PixiEditor.ChangeableDocument.Changes;
 using PixiEditor.ChangeableDocument.Changes.Properties;
 
-namespace PixiEditor.ChangeableDocument.Actions.Properties
+namespace PixiEditor.ChangeableDocument.Actions.Properties;
+
+public record class CreateStructureMemberMask_Action : IMakeChangeAction
 {
-    public record class CreateStructureMemberMask_Action : IMakeChangeAction
-    {
-        public Guid GuidValue { get; }
+    public Guid GuidValue { get; }
 
-        public CreateStructureMemberMask_Action(Guid guidValue)
-        {
-            GuidValue = guidValue;
-        }
+    public CreateStructureMemberMask_Action(Guid guidValue)
+    {
+        GuidValue = guidValue;
+    }
 
-        Change IMakeChangeAction.CreateCorrespondingChange()
-        {
-            return new CreateStructureMemberMask_Change(GuidValue);
-        }
+    Change IMakeChangeAction.CreateCorrespondingChange()
+    {
+        return new CreateStructureMemberMask_Change(GuidValue);
     }
 }

+ 11 - 12
src/PixiEditor.ChangeableDocument/Actions/Properties/DeleteStructureMemberMask_Action.cs

@@ -1,20 +1,19 @@
 using PixiEditor.ChangeableDocument.Changes;
 using PixiEditor.ChangeableDocument.Changes.Properties;
 
-namespace PixiEditor.ChangeableDocument.Actions.Properties
+namespace PixiEditor.ChangeableDocument.Actions.Properties;
+
+public record class DeleteStructureMemberMask_Action : IMakeChangeAction
 {
-    public record class DeleteStructureMemberMask_Action : IMakeChangeAction
-    {
-        public Guid GuidValue { get; }
+    public Guid GuidValue { get; }
 
-        public DeleteStructureMemberMask_Action(Guid guidValue)
-        {
-            GuidValue = guidValue;
-        }
+    public DeleteStructureMemberMask_Action(Guid guidValue)
+    {
+        GuidValue = guidValue;
+    }
 
-        Change IMakeChangeAction.CreateCorrespondingChange()
-        {
-            return new DeleteStructureMemberMask_Change(GuidValue);
-        }
+    Change IMakeChangeAction.CreateCorrespondingChange()
+    {
+        return new DeleteStructureMemberMask_Change(GuidValue);
     }
 }

+ 4 - 5
src/PixiEditor.ChangeableDocument/Actions/Properties/EndOpacityChange_Action.cs

@@ -1,10 +1,9 @@
 using PixiEditor.ChangeableDocument.Changes;
 using PixiEditor.ChangeableDocument.Changes.Properties;
 
-namespace PixiEditor.ChangeableDocument.Actions.Properties
+namespace PixiEditor.ChangeableDocument.Actions.Properties;
+
+public record class EndOpacityChange_Action : IEndChangeAction
 {
-    public record class EndOpacityChange_Action : IEndChangeAction
-    {
-        bool IEndChangeAction.IsChangeTypeMatching(Change change) => change is StructureMemberOpacity_UpdateableChange;
-    }
+    bool IEndChangeAction.IsChangeTypeMatching(Change change) => change is StructureMemberOpacity_UpdateableChange;
 }

+ 16 - 17
src/PixiEditor.ChangeableDocument/Actions/Properties/OpacityChange_Action.cs

@@ -1,27 +1,26 @@
 using PixiEditor.ChangeableDocument.Changes;
 using PixiEditor.ChangeableDocument.Changes.Properties;
 
-namespace PixiEditor.ChangeableDocument.Actions.Properties
+namespace PixiEditor.ChangeableDocument.Actions.Properties;
+
+public record class OpacityChange_Action : IStartOrUpdateChangeAction
 {
-    public record class OpacityChange_Action : IStartOrUpdateChangeAction
+    public OpacityChange_Action(Guid memberGuid, float opacity)
     {
-        public OpacityChange_Action(Guid memberGuid, float opacity)
-        {
-            Opacity = opacity;
-            MemberGuid = memberGuid;
-        }
+        Opacity = opacity;
+        MemberGuid = memberGuid;
+    }
 
-        public Guid MemberGuid { get; }
-        public float Opacity { get; }
+    public Guid MemberGuid { get; }
+    public float Opacity { get; }
 
-        UpdateableChange IStartOrUpdateChangeAction.CreateCorrespondingChange()
-        {
-            return new StructureMemberOpacity_UpdateableChange(MemberGuid, Opacity);
-        }
+    UpdateableChange IStartOrUpdateChangeAction.CreateCorrespondingChange()
+    {
+        return new StructureMemberOpacity_UpdateableChange(MemberGuid, Opacity);
+    }
 
-        void IStartOrUpdateChangeAction.UpdateCorrespodingChange(UpdateableChange change)
-        {
-            ((StructureMemberOpacity_UpdateableChange)change).Update(Opacity);
-        }
+    void IStartOrUpdateChangeAction.UpdateCorrespodingChange(UpdateableChange change)
+    {
+        ((StructureMemberOpacity_UpdateableChange)change).Update(Opacity);
     }
 }

+ 10 - 11
src/PixiEditor.ChangeableDocument/Actions/Root/ResizeCanvas_Action.cs

@@ -2,18 +2,17 @@
 using PixiEditor.ChangeableDocument.Changes;
 using PixiEditor.ChangeableDocument.Changes.Root;
 
-namespace PixiEditor.ChangeableDocument.Actions.Root
+namespace PixiEditor.ChangeableDocument.Actions.Root;
+
+public record class ResizeCanvas_Action : IMakeChangeAction
 {
-    public record class ResizeCanvas_Action : IMakeChangeAction
+    public Vector2i Size { get; }
+    public ResizeCanvas_Action(Vector2i size)
+    {
+        Size = size;
+    }
+    Change IMakeChangeAction.CreateCorrespondingChange()
     {
-        public Vector2i Size { get; }
-        public ResizeCanvas_Action(Vector2i size)
-        {
-            Size = size;
-        }
-        Change IMakeChangeAction.CreateCorrespondingChange()
-        {
-            return new ResizeCanvas_Change(Size);
-        }
+        return new ResizeCanvas_Change(Size);
     }
 }

+ 3 - 4
src/PixiEditor.ChangeableDocument/Actions/Undo/ChangeBoundary_Action.cs

@@ -1,6 +1,5 @@
-namespace PixiEditor.ChangeableDocument.Actions.Undo
+namespace PixiEditor.ChangeableDocument.Actions.Undo;
+
+public record class ChangeBoundary_Action : IAction
 {
-    public record class ChangeBoundary_Action : IAction
-    {
-    }
 }

+ 3 - 4
src/PixiEditor.ChangeableDocument/Actions/Undo/DeleteRecordedChanges_Action.cs

@@ -1,6 +1,5 @@
-namespace PixiEditor.ChangeableDocument.Actions.Undo
+namespace PixiEditor.ChangeableDocument.Actions.Undo;
+
+public record class DeleteRecordedChanges_Action : IAction
 {
-    public record class DeleteRecordedChanges_Action : IAction
-    {
-    }
 }

+ 4 - 5
src/PixiEditor.ChangeableDocument/ChangeInfos/CreateStructureMember_ChangeInfo.cs

@@ -1,7 +1,6 @@
-namespace PixiEditor.ChangeableDocument.ChangeInfos
+namespace PixiEditor.ChangeableDocument.ChangeInfos;
+
+public record class CreateStructureMember_ChangeInfo : IChangeInfo
 {
-    public record class CreateStructureMember_ChangeInfo : IChangeInfo
-    {
-        public Guid GuidValue { get; init; }
-    }
+    public Guid GuidValue { get; init; }
 }

+ 4 - 5
src/PixiEditor.ChangeableDocument/ChangeInfos/DeleteStructureMember_ChangeInfo.cs

@@ -1,7 +1,6 @@
-namespace PixiEditor.ChangeableDocument.ChangeInfos
+namespace PixiEditor.ChangeableDocument.ChangeInfos;
+
+public record class DeleteStructureMember_ChangeInfo : IChangeInfo
 {
-    public record class DeleteStructureMember_ChangeInfo : IChangeInfo
-    {
-        public Guid GuidValue { get; init; }
-    }
+    public Guid GuidValue { get; init; }
 }

+ 3 - 4
src/PixiEditor.ChangeableDocument/ChangeInfos/IChangeInfo.cs

@@ -1,6 +1,5 @@
-namespace PixiEditor.ChangeableDocument.ChangeInfos
+namespace PixiEditor.ChangeableDocument.ChangeInfos;
+
+public interface IChangeInfo
 {
-    public interface IChangeInfo
-    {
-    }
 }

+ 5 - 6
src/PixiEditor.ChangeableDocument/ChangeInfos/LayerImageChunks_ChangeInfo.cs

@@ -1,10 +1,9 @@
 using ChunkyImageLib.DataHolders;
 
-namespace PixiEditor.ChangeableDocument.ChangeInfos
+namespace PixiEditor.ChangeableDocument.ChangeInfos;
+
+public record class LayerImageChunks_ChangeInfo : IChangeInfo
 {
-    public record class LayerImageChunks_ChangeInfo : IChangeInfo
-    {
-        public Guid LayerGuid { get; init; }
-        public HashSet<Vector2i>? Chunks { get; init; }
-    }
+    public Guid LayerGuid { get; init; }
+    public HashSet<Vector2i>? Chunks { get; init; }
 }

+ 5 - 6
src/PixiEditor.ChangeableDocument/ChangeInfos/MaskChunks_ChangeInfo.cs

@@ -1,10 +1,9 @@
 using ChunkyImageLib.DataHolders;
 
-namespace PixiEditor.ChangeableDocument.ChangeInfos
+namespace PixiEditor.ChangeableDocument.ChangeInfos;
+
+public record class MaskChunks_ChangeInfo : IChangeInfo
 {
-    public record class MaskChunks_ChangeInfo : IChangeInfo
-    {
-        public Guid MemberGuid { get; init; }
-        public HashSet<Vector2i>? Chunks { get; init; }
-    }
+    public Guid MemberGuid { get; init; }
+    public HashSet<Vector2i>? Chunks { get; init; }
 }

+ 4 - 5
src/PixiEditor.ChangeableDocument/ChangeInfos/MoveStructureMember_ChangeInfo.cs

@@ -1,7 +1,6 @@
-namespace PixiEditor.ChangeableDocument.ChangeInfos
+namespace PixiEditor.ChangeableDocument.ChangeInfos;
+
+public record class MoveStructureMember_ChangeInfo : IChangeInfo
 {
-    public record class MoveStructureMember_ChangeInfo : IChangeInfo
-    {
-        public Guid GuidValue { get; init; }
-    }
+    public Guid GuidValue { get; init; }
 }

+ 4 - 5
src/PixiEditor.ChangeableDocument/ChangeInfos/Selection_ChangeInfo.cs

@@ -1,9 +1,8 @@
 using ChunkyImageLib.DataHolders;
 
-namespace PixiEditor.ChangeableDocument.ChangeInfos
+namespace PixiEditor.ChangeableDocument.ChangeInfos;
+
+public record class Selection_ChangeInfo : IChangeInfo
 {
-    public record class Selection_ChangeInfo : IChangeInfo
-    {
-        public HashSet<Vector2i>? Chunks { get; init; }
-    }
+    public HashSet<Vector2i>? Chunks { get; init; }
 }

+ 3 - 4
src/PixiEditor.ChangeableDocument/ChangeInfos/Size_ChangeInfo.cs

@@ -1,6 +1,5 @@
-namespace PixiEditor.ChangeableDocument.ChangeInfos
+namespace PixiEditor.ChangeableDocument.ChangeInfos;
+
+public record class Size_ChangeInfo : IChangeInfo
 {
-    public record class Size_ChangeInfo : IChangeInfo
-    {
-    }
 }

+ 4 - 5
src/PixiEditor.ChangeableDocument/ChangeInfos/StructureMemberIsVisible_ChangeInfo.cs

@@ -1,7 +1,6 @@
-namespace PixiEditor.ChangeableDocument.ChangeInfos
+namespace PixiEditor.ChangeableDocument.ChangeInfos;
+
+public record class StructureMemberIsVisible_ChangeInfo : IChangeInfo
 {
-    public record class StructureMemberIsVisible_ChangeInfo : IChangeInfo
-    {
-        public Guid GuidValue { get; init; }
-    }
+    public Guid GuidValue { get; init; }
 }

+ 4 - 5
src/PixiEditor.ChangeableDocument/ChangeInfos/StructureMemberMask_ChangeInfo.cs

@@ -1,7 +1,6 @@
-namespace PixiEditor.ChangeableDocument.ChangeInfos
+namespace PixiEditor.ChangeableDocument.ChangeInfos;
+
+public record class StructureMemberMask_ChangeInfo : IChangeInfo
 {
-    public record class StructureMemberMask_ChangeInfo : IChangeInfo
-    {
-        public Guid GuidValue { get; init; }
-    }
+    public Guid GuidValue { get; init; }
 }

+ 4 - 5
src/PixiEditor.ChangeableDocument/ChangeInfos/StructureMemberName_ChangeInfo.cs

@@ -1,7 +1,6 @@
-namespace PixiEditor.ChangeableDocument.ChangeInfos
+namespace PixiEditor.ChangeableDocument.ChangeInfos;
+
+public record class StructureMemberName_ChangeInfo : IChangeInfo
 {
-    public record class StructureMemberName_ChangeInfo : IChangeInfo
-    {
-        public Guid GuidValue { get; init; }
-    }
+    public Guid GuidValue { get; init; }
 }

+ 4 - 5
src/PixiEditor.ChangeableDocument/ChangeInfos/StructureMemberOpacity_ChangeInfo.cs

@@ -1,7 +1,6 @@
-namespace PixiEditor.ChangeableDocument.ChangeInfos
+namespace PixiEditor.ChangeableDocument.ChangeInfos;
+
+public record class StructureMemberOpacity_ChangeInfo : IChangeInfo
 {
-    public record class StructureMemberOpacity_ChangeInfo : IChangeInfo
-    {
-        public Guid GuidValue { get; init; }
-    }
+    public Guid GuidValue { get; init; }
 }

+ 50 - 51
src/PixiEditor.ChangeableDocument/Changeables/Document.cs

@@ -1,74 +1,73 @@
 using ChunkyImageLib.DataHolders;
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 
-namespace PixiEditor.ChangeableDocument.Changeables
+namespace PixiEditor.ChangeableDocument.Changeables;
+
+internal class Document : IChangeable, IReadOnlyDocument, IDisposable
 {
-    internal class Document : IChangeable, IReadOnlyDocument, IDisposable
-    {
-        public IReadOnlyFolder ReadOnlyStructureRoot => StructureRoot;
-        public IReadOnlySelection ReadOnlySelection => Selection;
-        IReadOnlyStructureMember? IReadOnlyDocument.FindMember(Guid guid) => FindMember(guid);
-        IReadOnlyList<IReadOnlyStructureMember> IReadOnlyDocument.FindMemberPath(Guid guid) => FindMemberPath(guid);
-        IReadOnlyStructureMember IReadOnlyDocument.FindMemberOrThrow(Guid guid) => FindMemberOrThrow(guid);
-        (IReadOnlyStructureMember, IReadOnlyFolder) IReadOnlyDocument.FindChildAndParentOrThrow(Guid guid) => FindChildAndParentOrThrow(guid);
+    public IReadOnlyFolder ReadOnlyStructureRoot => StructureRoot;
+    public IReadOnlySelection ReadOnlySelection => Selection;
+    IReadOnlyStructureMember? IReadOnlyDocument.FindMember(Guid guid) => FindMember(guid);
+    IReadOnlyList<IReadOnlyStructureMember> IReadOnlyDocument.FindMemberPath(Guid guid) => FindMemberPath(guid);
+    IReadOnlyStructureMember IReadOnlyDocument.FindMemberOrThrow(Guid guid) => FindMemberOrThrow(guid);
+    (IReadOnlyStructureMember, IReadOnlyFolder) IReadOnlyDocument.FindChildAndParentOrThrow(Guid guid) => FindChildAndParentOrThrow(guid);
 
-        public static Vector2i DefaultSize { get; } = new Vector2i(64, 64);
-        internal Folder StructureRoot { get; } = new() { GuidValue = Guid.Empty };
-        internal Selection Selection { get; } = new();
-        public Vector2i Size { get; set; } = DefaultSize;
+    public static Vector2i DefaultSize { get; } = new Vector2i(64, 64);
+    internal Folder StructureRoot { get; } = new() { GuidValue = Guid.Empty };
+    internal Selection Selection { get; } = new();
+    public Vector2i Size { get; set; } = DefaultSize;
 
-        public void Dispose()
-        {
-            StructureRoot.Dispose();
-            Selection.Dispose();
-        }
+    public void Dispose()
+    {
+        StructureRoot.Dispose();
+        Selection.Dispose();
+    }
 
-        public StructureMember FindMemberOrThrow(Guid guid) => FindMember(guid) ?? throw new ArgumentException("Could not find member with guid " + guid.ToString());
-        public StructureMember? FindMember(Guid guid)
-        {
-            var list = FindMemberPath(guid);
-            return list.Count > 0 ? list[0] : null;
-        }
+    public StructureMember FindMemberOrThrow(Guid guid) => FindMember(guid) ?? throw new ArgumentException("Could not find member with guid " + guid.ToString());
+    public StructureMember? FindMember(Guid guid)
+    {
+        var list = FindMemberPath(guid);
+        return list.Count > 0 ? list[0] : null;
+    }
 
-        public (StructureMember, Folder) FindChildAndParentOrThrow(Guid childGuid)
-        {
-            var path = FindMemberPath(childGuid);
-            if (path.Count < 2)
-                throw new ArgumentException("Couldn't find child and parent");
-            return (path[0], (Folder)path[1]);
-        }
+    public (StructureMember, Folder) FindChildAndParentOrThrow(Guid childGuid)
+    {
+        var path = FindMemberPath(childGuid);
+        if (path.Count < 2)
+            throw new ArgumentException("Couldn't find child and parent");
+        return (path[0], (Folder)path[1]);
+    }
+
+    public List<StructureMember> FindMemberPath(Guid guid)
+    {
+        var list = new List<StructureMember>();
+        if (FillMemberPath(StructureRoot, guid, list))
+            list.Add(StructureRoot);
+        return list;
+    }
 
-        public List<StructureMember> FindMemberPath(Guid guid)
+    private bool FillMemberPath(Folder folder, Guid guid, List<StructureMember> toFill)
+    {
+        if (folder.GuidValue == guid)
         {
-            var list = new List<StructureMember>();
-            if (FillMemberPath(StructureRoot, guid, list))
-                list.Add(StructureRoot);
-            return list;
+            return true;
         }
-
-        private bool FillMemberPath(Folder folder, Guid guid, List<StructureMember> toFill)
+        foreach (var member in folder.Children)
         {
-            if (folder.GuidValue == guid)
+            if (member is Layer childLayer && childLayer.GuidValue == guid)
             {
+                toFill.Add(member);
                 return true;
             }
-            foreach (var member in folder.Children)
+            if (member is Folder childFolder)
             {
-                if (member is Layer childLayer && childLayer.GuidValue == guid)
+                if (FillMemberPath(childFolder, guid, toFill))
                 {
-                    toFill.Add(member);
+                    toFill.Add(childFolder);
                     return true;
                 }
-                if (member is Folder childFolder)
-                {
-                    if (FillMemberPath(childFolder, guid, toFill))
-                    {
-                        toFill.Add(childFolder);
-                        return true;
-                    }
-                }
             }
-            return false;
         }
+        return false;
     }
 }

+ 26 - 27
src/PixiEditor.ChangeableDocument/Changeables/Folder.cs

@@ -1,38 +1,37 @@
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 
-namespace PixiEditor.ChangeableDocument.Changeables
+namespace PixiEditor.ChangeableDocument.Changeables;
+
+internal class Folder : StructureMember, IReadOnlyFolder
 {
-    internal class Folder : StructureMember, IReadOnlyFolder
-    {
-        internal List<StructureMember> Children { get; set; } = new();
-        public IReadOnlyList<IReadOnlyStructureMember> ReadOnlyChildren => Children;
+    internal List<StructureMember> Children { get; set; } = new();
+    public IReadOnlyList<IReadOnlyStructureMember> ReadOnlyChildren => Children;
 
-        internal override Folder Clone()
+    internal override Folder Clone()
+    {
+        List<StructureMember> clonedChildren = new();
+        foreach (var child in Children)
         {
-            List<StructureMember> clonedChildren = new();
-            foreach (var child in Children)
-            {
-                clonedChildren.Add(child.Clone());
-            }
-
-            return new Folder()
-            {
-                GuidValue = GuidValue,
-                IsVisible = IsVisible,
-                Name = Name,
-                Opacity = Opacity,
-                Children = clonedChildren,
-                Mask = Mask?.CloneFromLatest()
-            };
+            clonedChildren.Add(child.Clone());
         }
 
-        public override void Dispose()
+        return new Folder()
+        {
+            GuidValue = GuidValue,
+            IsVisible = IsVisible,
+            Name = Name,
+            Opacity = Opacity,
+            Children = clonedChildren,
+            Mask = Mask?.CloneFromLatest()
+        };
+    }
+
+    public override void Dispose()
+    {
+        foreach (var child in Children)
         {
-            foreach (var child in Children)
-            {
-                child.Dispose();
-            }
-            Mask?.Dispose();
+            child.Dispose();
         }
+        Mask?.Dispose();
     }
 }

+ 4 - 6
src/PixiEditor.ChangeableDocument/Changeables/IChangeable.cs

@@ -1,8 +1,6 @@
-namespace PixiEditor.ChangeableDocument.Changeables
-{
+namespace PixiEditor.ChangeableDocument.Changeables;
 
-    internal interface IChangeable
-    {
+internal interface IChangeable
+{
 
-    };
-}
+};

+ 10 - 11
src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyDocument.cs

@@ -1,15 +1,14 @@
 using ChunkyImageLib.DataHolders;
 
-namespace PixiEditor.ChangeableDocument.Changeables.Interfaces
+namespace PixiEditor.ChangeableDocument.Changeables.Interfaces;
+
+public interface IReadOnlyDocument
 {
-    public interface IReadOnlyDocument
-    {
-        IReadOnlyFolder ReadOnlyStructureRoot { get; }
-        IReadOnlySelection ReadOnlySelection { get; }
-        Vector2i Size { get; }
-        IReadOnlyStructureMember? FindMember(Guid guid);
-        IReadOnlyStructureMember FindMemberOrThrow(Guid guid);
-        (IReadOnlyStructureMember, IReadOnlyFolder) FindChildAndParentOrThrow(Guid guid);
-        IReadOnlyList<IReadOnlyStructureMember> FindMemberPath(Guid guid);
-    }
+    IReadOnlyFolder ReadOnlyStructureRoot { get; }
+    IReadOnlySelection ReadOnlySelection { get; }
+    Vector2i Size { get; }
+    IReadOnlyStructureMember? FindMember(Guid guid);
+    IReadOnlyStructureMember FindMemberOrThrow(Guid guid);
+    (IReadOnlyStructureMember, IReadOnlyFolder) FindChildAndParentOrThrow(Guid guid);
+    IReadOnlyList<IReadOnlyStructureMember> FindMemberPath(Guid guid);
 }

+ 4 - 5
src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyFolder.cs

@@ -1,7 +1,6 @@
-namespace PixiEditor.ChangeableDocument.Changeables.Interfaces
+namespace PixiEditor.ChangeableDocument.Changeables.Interfaces;
+
+public interface IReadOnlyFolder : IReadOnlyStructureMember
 {
-    public interface IReadOnlyFolder : IReadOnlyStructureMember
-    {
-        IReadOnlyList<IReadOnlyStructureMember> ReadOnlyChildren { get; }
-    }
+    IReadOnlyList<IReadOnlyStructureMember> ReadOnlyChildren { get; }
 }

+ 4 - 5
src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyLayer.cs

@@ -1,9 +1,8 @@
 using ChunkyImageLib;
 
-namespace PixiEditor.ChangeableDocument.Changeables.Interfaces
+namespace PixiEditor.ChangeableDocument.Changeables.Interfaces;
+
+public interface IReadOnlyLayer : IReadOnlyStructureMember
 {
-    public interface IReadOnlyLayer : IReadOnlyStructureMember
-    {
-        IReadOnlyChunkyImage ReadOnlyLayerImage { get; }
-    }
+    IReadOnlyChunkyImage ReadOnlyLayerImage { get; }
 }

+ 5 - 6
src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlySelection.cs

@@ -1,10 +1,9 @@
 using ChunkyImageLib;
 
-namespace PixiEditor.ChangeableDocument.Changeables.Interfaces
+namespace PixiEditor.ChangeableDocument.Changeables.Interfaces;
+
+public interface IReadOnlySelection
 {
-    public interface IReadOnlySelection
-    {
-        public IReadOnlyChunkyImage ReadOnlySelectionImage { get; }
-        public bool ReadOnlyIsEmptyAndInactive { get; }
-    }
+    public IReadOnlyChunkyImage ReadOnlySelectionImage { get; }
+    public bool ReadOnlyIsEmptyAndInactive { get; }
 }

+ 9 - 10
src/PixiEditor.ChangeableDocument/Changeables/Interfaces/IReadOnlyStructureMember.cs

@@ -1,15 +1,14 @@
 using ChunkyImageLib;
 using PixiEditor.ChangeableDocument.Enums;
 
-namespace PixiEditor.ChangeableDocument.Changeables.Interfaces
+namespace PixiEditor.ChangeableDocument.Changeables.Interfaces;
+
+public interface IReadOnlyStructureMember
 {
-    public interface IReadOnlyStructureMember
-    {
-        bool IsVisible { get; }
-        string Name { get; }
-        Guid GuidValue { get; }
-        float Opacity { get; }
-        BlendMode BlendMode { get; }
-        IReadOnlyChunkyImage? ReadOnlyMask { get; }
-    }
+    bool IsVisible { get; }
+    string Name { get; }
+    Guid GuidValue { get; }
+    float Opacity { get; }
+    BlendMode BlendMode { get; }
+    IReadOnlyChunkyImage? ReadOnlyMask { get; }
 }

+ 27 - 28
src/PixiEditor.ChangeableDocument/Changeables/Layer.cs

@@ -2,39 +2,38 @@
 using ChunkyImageLib.DataHolders;
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 
-namespace PixiEditor.ChangeableDocument.Changeables
+namespace PixiEditor.ChangeableDocument.Changeables;
+
+internal class Layer : StructureMember, IReadOnlyLayer
 {
-    internal class Layer : StructureMember, IReadOnlyLayer
-    {
-        public ChunkyImage LayerImage { get; set; }
-        IReadOnlyChunkyImage IReadOnlyLayer.ReadOnlyLayerImage => LayerImage;
+    public ChunkyImage LayerImage { get; set; }
+    IReadOnlyChunkyImage IReadOnlyLayer.ReadOnlyLayerImage => LayerImage;
 
-        public Layer(Vector2i size)
-        {
-            LayerImage = new(size);
-        }
+    public Layer(Vector2i size)
+    {
+        LayerImage = new(size);
+    }
 
-        public Layer(ChunkyImage image)
-        {
-            LayerImage = image;
-        }
+    public Layer(ChunkyImage image)
+    {
+        LayerImage = image;
+    }
 
-        public override void Dispose()
-        {
-            LayerImage.Dispose();
-            Mask?.Dispose();
-        }
+    public override void Dispose()
+    {
+        LayerImage.Dispose();
+        Mask?.Dispose();
+    }
 
-        internal override Layer Clone()
+    internal override Layer Clone()
+    {
+        return new Layer(LayerImage.CloneFromLatest())
         {
-            return new Layer(LayerImage.CloneFromLatest())
-            {
-                GuidValue = GuidValue,
-                IsVisible = IsVisible,
-                Name = Name,
-                Opacity = Opacity,
-                Mask = Mask?.CloneFromLatest(),
-            };
-        }
+            GuidValue = GuidValue,
+            IsVisible = IsVisible,
+            Name = Name,
+            Opacity = Opacity,
+            Mask = Mask?.CloneFromLatest(),
+        };
     }
 }

+ 11 - 12
src/PixiEditor.ChangeableDocument/Changeables/Selection.cs

@@ -2,20 +2,19 @@
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using SkiaSharp;
 
-namespace PixiEditor.ChangeableDocument.Changeables
+namespace PixiEditor.ChangeableDocument.Changeables;
+
+internal class Selection : IReadOnlySelection, IDisposable
 {
-    internal class Selection : IReadOnlySelection, IDisposable
-    {
-        public static SKColor SelectionColor { get; } = SKColors.CornflowerBlue;
-        public bool IsEmptyAndInactive { get; set; } = true;
-        public ChunkyImage SelectionImage { get; set; } = new(new(64, 64));
+    public static SKColor SelectionColor { get; } = SKColors.CornflowerBlue;
+    public bool IsEmptyAndInactive { get; set; } = true;
+    public ChunkyImage SelectionImage { get; set; } = new(new(64, 64));
 
-        public IReadOnlyChunkyImage ReadOnlySelectionImage => SelectionImage;
-        public bool ReadOnlyIsEmptyAndInactive => IsEmptyAndInactive;
+    public IReadOnlyChunkyImage ReadOnlySelectionImage => SelectionImage;
+    public bool ReadOnlyIsEmptyAndInactive => IsEmptyAndInactive;
 
-        public void Dispose()
-        {
-            SelectionImage.Dispose();
-        }
+    public void Dispose()
+    {
+        SelectionImage.Dispose();
     }
 }

+ 12 - 13
src/PixiEditor.ChangeableDocument/Changeables/StructureMember.cs

@@ -2,19 +2,18 @@
 using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.ChangeableDocument.Enums;
 
-namespace PixiEditor.ChangeableDocument.Changeables
+namespace PixiEditor.ChangeableDocument.Changeables;
+
+internal abstract class StructureMember : IChangeable, IReadOnlyStructureMember, IDisposable
 {
-    internal abstract class StructureMember : IChangeable, IReadOnlyStructureMember, IDisposable
-    {
-        public float Opacity { get; set; } = 1f;
-        public bool IsVisible { get; set; } = true;
-        public string Name { get; set; } = "Unnamed";
-        public BlendMode BlendMode { get; set; } = BlendMode.Normal;
-        public Guid GuidValue { get; init; }
-        public ChunkyImage? Mask { get; set; } = null;
-        public IReadOnlyChunkyImage? ReadOnlyMask => Mask;
+    public float Opacity { get; set; } = 1f;
+    public bool IsVisible { get; set; } = true;
+    public string Name { get; set; } = "Unnamed";
+    public BlendMode BlendMode { get; set; } = BlendMode.Normal;
+    public Guid GuidValue { get; init; }
+    public ChunkyImage? Mask { get; set; } = null;
+    public IReadOnlyChunkyImage? ReadOnlyMask => Mask;
 
-        internal abstract StructureMember Clone();
-        public abstract void Dispose();
-    }
+    internal abstract StructureMember Clone();
+    public abstract void Dispose();
 }

+ 9 - 10
src/PixiEditor.ChangeableDocument/Changes/Change.cs

@@ -1,14 +1,13 @@
 using PixiEditor.ChangeableDocument.Changeables;
 using PixiEditor.ChangeableDocument.ChangeInfos;
 
-namespace PixiEditor.ChangeableDocument.Changes
+namespace PixiEditor.ChangeableDocument.Changes;
+
+internal abstract class Change : IDisposable
 {
-    internal abstract class Change : IDisposable
-    {
-        public virtual bool IsMergeableWith(Change other) => false;
-        public abstract void Initialize(Document target);
-        public abstract IChangeInfo? Apply(Document target, out bool ignoreInUndo);
-        public abstract IChangeInfo? Revert(Document target);
-        public virtual void Dispose() { }
-    };
-}
+    public virtual bool IsMergeableWith(Change other) => false;
+    public abstract void Initialize(Document target);
+    public abstract IChangeInfo? Apply(Document target, out bool ignoreInUndo);
+    public abstract IChangeInfo? Revert(Document target);
+    public virtual void Dispose() { }
+};

+ 39 - 40
src/PixiEditor.ChangeableDocument/Changes/Drawing/ClearSelection_Change.cs

@@ -3,54 +3,53 @@ using ChunkyImageLib.DataHolders;
 using PixiEditor.ChangeableDocument.Changeables;
 using PixiEditor.ChangeableDocument.ChangeInfos;
 
-namespace PixiEditor.ChangeableDocument.Changes.Drawing
+namespace PixiEditor.ChangeableDocument.Changes.Drawing;
+
+internal class ClearSelection_Change : Change
 {
-    internal class ClearSelection_Change : Change
+    private bool originalIsEmpty;
+    private CommittedChunkStorage? savedSelection;
+    public override void Initialize(Document target)
     {
-        private bool originalIsEmpty;
-        private CommittedChunkStorage? savedSelection;
-        public override void Initialize(Document target)
-        {
-            originalIsEmpty = target.Selection.IsEmptyAndInactive;
-            if (!originalIsEmpty)
-                savedSelection = new(target.Selection.SelectionImage, target.Selection.SelectionImage.FindAllChunks());
-        }
+        originalIsEmpty = target.Selection.IsEmptyAndInactive;
+        if (!originalIsEmpty)
+            savedSelection = new(target.Selection.SelectionImage, target.Selection.SelectionImage.FindAllChunks());
+    }
 
-        public override IChangeInfo? Apply(Document target, out bool ignoreInUndo)
+    public override IChangeInfo? Apply(Document target, out bool ignoreInUndo)
+    {
+        if (originalIsEmpty)
         {
-            if (originalIsEmpty)
-            {
-                ignoreInUndo = true;
-                return null;
-            }
-            target.Selection.IsEmptyAndInactive = true;
-
-            target.Selection.SelectionImage.CancelChanges();
-            target.Selection.SelectionImage.Clear();
-            HashSet<Vector2i> affChunks = target.Selection.SelectionImage.FindAffectedChunks();
-            target.Selection.SelectionImage.CommitChanges();
-
-            ignoreInUndo = false;
-            return new Selection_ChangeInfo() { Chunks = affChunks };
+            ignoreInUndo = true;
+            return null;
         }
+        target.Selection.IsEmptyAndInactive = true;
 
-        public override IChangeInfo? Revert(Document target)
-        {
-            if (originalIsEmpty)
-                return new Selection_ChangeInfo() { Chunks = new() };
-            target.Selection.IsEmptyAndInactive = false;
+        target.Selection.SelectionImage.CancelChanges();
+        target.Selection.SelectionImage.Clear();
+        HashSet<Vector2i> affChunks = target.Selection.SelectionImage.FindAffectedChunks();
+        target.Selection.SelectionImage.CommitChanges();
 
-            target.Selection.SelectionImage.CancelChanges();
-            savedSelection!.ApplyChunksToImage(target.Selection.SelectionImage);
-            HashSet<Vector2i> affChunks = target.Selection.SelectionImage.FindAffectedChunks();
-            target.Selection.SelectionImage.CommitChanges();
+        ignoreInUndo = false;
+        return new Selection_ChangeInfo() { Chunks = affChunks };
+    }
 
-            return new Selection_ChangeInfo() { Chunks = affChunks };
-        }
+    public override IChangeInfo? Revert(Document target)
+    {
+        if (originalIsEmpty)
+            return new Selection_ChangeInfo() { Chunks = new() };
+        target.Selection.IsEmptyAndInactive = false;
 
-        public override void Dispose()
-        {
-            savedSelection?.Dispose();
-        }
+        target.Selection.SelectionImage.CancelChanges();
+        savedSelection!.ApplyChunksToImage(target.Selection.SelectionImage);
+        HashSet<Vector2i> affChunks = target.Selection.SelectionImage.FindAffectedChunks();
+        target.Selection.SelectionImage.CommitChanges();
+
+        return new Selection_ChangeInfo() { Chunks = affChunks };
+    }
+
+    public override void Dispose()
+    {
+        savedSelection?.Dispose();
     }
 }

+ 68 - 69
src/PixiEditor.ChangeableDocument/Changes/Drawing/CombineStructureMembersOnto_Change.cs

@@ -4,96 +4,95 @@ using PixiEditor.ChangeableDocument.Changeables;
 using PixiEditor.ChangeableDocument.ChangeInfos;
 using PixiEditor.ChangeableDocument.Rendering;
 
-namespace PixiEditor.ChangeableDocument.Changes.Drawing
+namespace PixiEditor.ChangeableDocument.Changes.Drawing;
+
+internal class CombineStructureMembersOnto_Change : Change
 {
-    internal class CombineStructureMembersOnto_Change : Change
-    {
-        private HashSet<Guid> membersToMerge;
+    private HashSet<Guid> membersToMerge;
+
+    private HashSet<Guid> layersToCombine = new();
 
-        private HashSet<Guid> layersToCombine = new();
+    private Guid targetLayer;
+    private CommittedChunkStorage? originalChunks;
 
-        private Guid targetLayer;
-        private CommittedChunkStorage? originalChunks;
+    public CombineStructureMembersOnto_Change(HashSet<Guid> membersToMerge, Guid targetLayer)
+    {
+        this.membersToMerge = new HashSet<Guid>(membersToMerge);
+        this.targetLayer = targetLayer;
+    }
 
-        public CombineStructureMembersOnto_Change(HashSet<Guid> membersToMerge, Guid targetLayer)
+    public override void Initialize(Document target)
+    {
+        foreach (Guid guid in membersToMerge)
         {
-            this.membersToMerge = new HashSet<Guid>(membersToMerge);
-            this.targetLayer = targetLayer;
+            var member = target.FindMemberOrThrow(guid);
+            if (member is Layer layer)
+                layersToCombine.Add(layer.GuidValue);
+            else if (member is Folder innerFolder)
+                AddChildren(innerFolder, layersToCombine);
         }
+    }
 
-        public override void Initialize(Document target)
+    private void AddChildren(Folder folder, HashSet<Guid> collection)
+    {
+        foreach (var child in folder.Children)
         {
-            foreach (Guid guid in membersToMerge)
-            {
-                var member = target.FindMemberOrThrow(guid);
-                if (member is Layer layer)
-                    layersToCombine.Add(layer.GuidValue);
-                else if (member is Folder innerFolder)
-                    AddChildren(innerFolder, layersToCombine);
-            }
+            if (child is Layer layer)
+                collection.Add(layer.GuidValue);
+            else if (child is Folder innerFolder)
+                AddChildren(innerFolder, collection);
         }
+    }
 
-        private void AddChildren(Folder folder, HashSet<Guid> collection)
+    public override IChangeInfo? Apply(Document target, out bool ignoreInUndo)
+    {
+        Layer toDrawOn = (Layer)target.FindMemberOrThrow(targetLayer);
+
+        var chunksToCombine = new HashSet<Vector2i>();
+        foreach (var guid in layersToCombine)
         {
-            foreach (var child in folder.Children)
-            {
-                if (child is Layer layer)
-                    collection.Add(layer.GuidValue);
-                else if (child is Folder innerFolder)
-                    AddChildren(innerFolder, collection);
-            }
+            var layer = (Layer)target.FindMemberOrThrow(guid);
+            chunksToCombine.UnionWith(layer.LayerImage.FindAllChunks());
         }
 
-        public override IChangeInfo? Apply(Document target, out bool ignoreInUndo)
+        toDrawOn.LayerImage.Clear();
+        foreach (var chunk in chunksToCombine)
         {
-            Layer toDrawOn = (Layer)target.FindMemberOrThrow(targetLayer);
-
-            var chunksToCombine = new HashSet<Vector2i>();
-            foreach (var guid in layersToCombine)
-            {
-                var layer = (Layer)target.FindMemberOrThrow(guid);
-                chunksToCombine.UnionWith(layer.LayerImage.FindAllChunks());
-            }
-
-            toDrawOn.LayerImage.Clear();
-            foreach (var chunk in chunksToCombine)
-            {
-                using var combined = ChunkRenderer.RenderSpecificLayers(chunk, ChunkResolution.Full, target.StructureRoot, layersToCombine);
-                toDrawOn.LayerImage.DrawImage(chunk * ChunkyImage.ChunkSize, combined.Surface);
-            }
-            var affectedChunks = toDrawOn.LayerImage.FindAffectedChunks();
-            originalChunks = new CommittedChunkStorage(toDrawOn.LayerImage, affectedChunks);
-            toDrawOn.LayerImage.CommitChanges();
-
-            ignoreInUndo = false;
-            return new LayerImageChunks_ChangeInfo()
-            {
-                LayerGuid = targetLayer,
-                Chunks = affectedChunks
-            };
+            using var combined = ChunkRenderer.RenderSpecificLayers(chunk, ChunkResolution.Full, target.StructureRoot, layersToCombine);
+            toDrawOn.LayerImage.DrawImage(chunk * ChunkyImage.ChunkSize, combined.Surface);
         }
+        var affectedChunks = toDrawOn.LayerImage.FindAffectedChunks();
+        originalChunks = new CommittedChunkStorage(toDrawOn.LayerImage, affectedChunks);
+        toDrawOn.LayerImage.CommitChanges();
 
-        public override IChangeInfo? Revert(Document target)
+        ignoreInUndo = false;
+        return new LayerImageChunks_ChangeInfo()
         {
-            Layer toDrawOn = (Layer)target.FindMemberOrThrow(targetLayer);
+            LayerGuid = targetLayer,
+            Chunks = affectedChunks
+        };
+    }
 
-            originalChunks!.ApplyChunksToImage(toDrawOn.LayerImage);
-            var affectedChunks = toDrawOn.LayerImage.FindAffectedChunks();
-            toDrawOn.LayerImage.CommitChanges();
+    public override IChangeInfo? Revert(Document target)
+    {
+        Layer toDrawOn = (Layer)target.FindMemberOrThrow(targetLayer);
 
-            originalChunks.Dispose();
-            originalChunks = null;
+        originalChunks!.ApplyChunksToImage(toDrawOn.LayerImage);
+        var affectedChunks = toDrawOn.LayerImage.FindAffectedChunks();
+        toDrawOn.LayerImage.CommitChanges();
 
-            return new LayerImageChunks_ChangeInfo()
-            {
-                LayerGuid = targetLayer,
-                Chunks = affectedChunks
-            };
-        }
+        originalChunks.Dispose();
+        originalChunks = null;
 
-        public override void Dispose()
+        return new LayerImageChunks_ChangeInfo()
         {
-            originalChunks?.Dispose();
-        }
+            LayerGuid = targetLayer,
+            Chunks = affectedChunks
+        };
+    }
+
+    public override void Dispose()
+    {
+        originalChunks?.Dispose();
     }
 }

+ 90 - 91
src/PixiEditor.ChangeableDocument/Changes/Drawing/DrawRectangle_UpdateableChange.cs

@@ -3,116 +3,115 @@ using ChunkyImageLib.DataHolders;
 using PixiEditor.ChangeableDocument.Changeables;
 using PixiEditor.ChangeableDocument.ChangeInfos;
 
-namespace PixiEditor.ChangeableDocument.Changes.Drawing
+namespace PixiEditor.ChangeableDocument.Changes.Drawing;
+
+internal class DrawRectangle_UpdateableChange : UpdateableChange
 {
-    internal class DrawRectangle_UpdateableChange : UpdateableChange
+    private readonly Guid memberGuid;
+    private ShapeData rect;
+    private readonly bool drawOnMask;
+    private CommittedChunkStorage? storedChunks;
+    public DrawRectangle_UpdateableChange(Guid memberGuid, ShapeData rectangle, bool drawOnMask)
     {
-        private readonly Guid memberGuid;
-        private ShapeData rect;
-        private readonly bool drawOnMask;
-        private CommittedChunkStorage? storedChunks;
-        public DrawRectangle_UpdateableChange(Guid memberGuid, ShapeData rectangle, bool drawOnMask)
-        {
-            this.memberGuid = memberGuid;
-            this.rect = rectangle;
-            this.drawOnMask = drawOnMask;
-        }
+        this.memberGuid = memberGuid;
+        this.rect = rectangle;
+        this.drawOnMask = drawOnMask;
+    }
 
-        public override void Initialize(Document target) { }
+    public override void Initialize(Document target) { }
 
-        public void Update(ShapeData updatedRectangle)
+    public void Update(ShapeData updatedRectangle)
+    {
+        rect = updatedRectangle;
+    }
+
+    private ChunkyImage GetTargetImage(Document target)
+    {
+        var member = target.FindMemberOrThrow(memberGuid);
+        if (drawOnMask)
         {
-            rect = updatedRectangle;
+            if (member.Mask is null)
+                throw new InvalidOperationException("Trying to draw on a mask that doesn't exist");
+            return member.Mask;
         }
-
-        private ChunkyImage GetTargetImage(Document target)
+        else if (member is Folder)
         {
-            var member = target.FindMemberOrThrow(memberGuid);
-            if (drawOnMask)
-            {
-                if (member.Mask is null)
-                    throw new InvalidOperationException("Trying to draw on a mask that doesn't exist");
-                return member.Mask;
-            }
-            else if (member is Folder)
-            {
-                throw new InvalidOperationException("Trying to draw on a folder");
-            }
-            return ((Layer)member).LayerImage;
+            throw new InvalidOperationException("Trying to draw on a folder");
         }
+        return ((Layer)member).LayerImage;
+    }
 
-        public override IChangeInfo? ApplyTemporarily(Document target)
-        {
-            ChunkyImage targetImage = GetTargetImage(target);
+    public override IChangeInfo? ApplyTemporarily(Document target)
+    {
+        ChunkyImage targetImage = GetTargetImage(target);
 
-            var oldChunks = targetImage.FindAffectedChunks();
-            targetImage.CancelChanges();
-            if (!target.Selection.IsEmptyAndInactive)
-                targetImage.ApplyRasterClip(target.Selection.SelectionImage);
-            targetImage.DrawRectangle(rect);
-            var newChunks = targetImage.FindAffectedChunks();
-            newChunks.UnionWith(oldChunks);
+        var oldChunks = targetImage.FindAffectedChunks();
+        targetImage.CancelChanges();
+        if (!target.Selection.IsEmptyAndInactive)
+            targetImage.ApplyRasterClip(target.Selection.SelectionImage);
+        targetImage.DrawRectangle(rect);
+        var newChunks = targetImage.FindAffectedChunks();
+        newChunks.UnionWith(oldChunks);
 
-            return drawOnMask switch
+        return drawOnMask switch
+        {
+            false => new LayerImageChunks_ChangeInfo()
             {
-                false => new LayerImageChunks_ChangeInfo()
-                {
-                    Chunks = newChunks,
-                    LayerGuid = memberGuid
-                },
-                true => new MaskChunks_ChangeInfo()
-                {
-                    Chunks = newChunks,
-                    MemberGuid = memberGuid
-                },
-            };
-        }
+                Chunks = newChunks,
+                LayerGuid = memberGuid
+            },
+            true => new MaskChunks_ChangeInfo()
+            {
+                Chunks = newChunks,
+                MemberGuid = memberGuid
+            },
+        };
+    }
 
-        public override IChangeInfo? Apply(Document target, out bool ignoreInUndo)
-        {
-            ChunkyImage targetImage = GetTargetImage(target);
-            var changes = ApplyTemporarily(target);
+    public override IChangeInfo? Apply(Document target, out bool ignoreInUndo)
+    {
+        ChunkyImage targetImage = GetTargetImage(target);
+        var changes = ApplyTemporarily(target);
 
-            var changedChunks = changes! switch
-            {
-                LayerImageChunks_ChangeInfo info => info.Chunks,
-                MaskChunks_ChangeInfo info => info.Chunks,
-                _ => throw new InvalidOperationException("Unknown chunk type"),
-            };
+        var changedChunks = changes! switch
+        {
+            LayerImageChunks_ChangeInfo info => info.Chunks,
+            MaskChunks_ChangeInfo info => info.Chunks,
+            _ => throw new InvalidOperationException("Unknown chunk type"),
+        };
 
-            storedChunks = new CommittedChunkStorage(targetImage, changedChunks!);
-            targetImage.CommitChanges();
+        storedChunks = new CommittedChunkStorage(targetImage, changedChunks!);
+        targetImage.CommitChanges();
 
-            ignoreInUndo = false;
-            return changes;
-        }
+        ignoreInUndo = false;
+        return changes;
+    }
 
-        public override IChangeInfo? Revert(Document target)
+    public override IChangeInfo? Revert(Document target)
+    {
+        ChunkyImage targetImage = GetTargetImage(target);
+        storedChunks!.ApplyChunksToImage(targetImage);
+        storedChunks.Dispose();
+        storedChunks = null;
+        IChangeInfo changes = drawOnMask switch
         {
-            ChunkyImage targetImage = GetTargetImage(target);
-            storedChunks!.ApplyChunksToImage(targetImage);
-            storedChunks.Dispose();
-            storedChunks = null;
-            IChangeInfo changes = drawOnMask switch
+            false => new LayerImageChunks_ChangeInfo()
             {
-                false => new LayerImageChunks_ChangeInfo()
-                {
-                    Chunks = targetImage.FindAffectedChunks(),
-                    LayerGuid = memberGuid,
-                },
-                true => new MaskChunks_ChangeInfo()
-                {
-                    Chunks = targetImage.FindAffectedChunks(),
-                    MemberGuid = memberGuid,
-                },
-            };
-            targetImage.CommitChanges();
-            return changes;
-        }
+                Chunks = targetImage.FindAffectedChunks(),
+                LayerGuid = memberGuid,
+            },
+            true => new MaskChunks_ChangeInfo()
+            {
+                Chunks = targetImage.FindAffectedChunks(),
+                MemberGuid = memberGuid,
+            },
+        };
+        targetImage.CommitChanges();
+        return changes;
+    }
 
-        public override void Dispose()
-        {
-            storedChunks?.Dispose();
-        }
+    public override void Dispose()
+    {
+        storedChunks?.Dispose();
     }
 }

+ 50 - 51
src/PixiEditor.ChangeableDocument/Changes/Drawing/SelectRectangle_UpdateableChange.cs

@@ -4,64 +4,63 @@ using PixiEditor.ChangeableDocument.Changeables;
 using PixiEditor.ChangeableDocument.ChangeInfos;
 using SkiaSharp;
 
-namespace PixiEditor.ChangeableDocument.Changes.Drawing
+namespace PixiEditor.ChangeableDocument.Changes.Drawing;
+
+internal class SelectRectangle_UpdateableChange : UpdateableChange
 {
-    internal class SelectRectangle_UpdateableChange : UpdateableChange
+    private bool originalIsEmpty;
+    private Vector2i pos;
+    private Vector2i size;
+    private CommittedChunkStorage? originalSelectionState;
+    public SelectRectangle_UpdateableChange(Vector2i pos, Vector2i size)
+    {
+        Update(pos, size);
+    }
+    public override void Initialize(Document target)
     {
-        private bool originalIsEmpty;
-        private Vector2i pos;
-        private Vector2i size;
-        private CommittedChunkStorage? originalSelectionState;
-        public SelectRectangle_UpdateableChange(Vector2i pos, Vector2i size)
-        {
-            Update(pos, size);
-        }
-        public override void Initialize(Document target)
-        {
-            originalIsEmpty = target.Selection.IsEmptyAndInactive;
-        }
+        originalIsEmpty = target.Selection.IsEmptyAndInactive;
+    }
 
-        public void Update(Vector2i pos, Vector2i size)
-        {
-            this.pos = pos;
-            this.size = size;
-        }
+    public void Update(Vector2i pos, Vector2i size)
+    {
+        this.pos = pos;
+        this.size = size;
+    }
 
-        public override IChangeInfo? ApplyTemporarily(Document target)
-        {
-            var oldChunks = target.Selection.SelectionImage.FindAffectedChunks();
-            target.Selection.SelectionImage.CancelChanges();
-            target.Selection.IsEmptyAndInactive = false;
-            target.Selection.SelectionImage.DrawRectangle(new ShapeData(pos, size, 0, SKColors.Transparent, Selection.SelectionColor));
+    public override IChangeInfo? ApplyTemporarily(Document target)
+    {
+        var oldChunks = target.Selection.SelectionImage.FindAffectedChunks();
+        target.Selection.SelectionImage.CancelChanges();
+        target.Selection.IsEmptyAndInactive = false;
+        target.Selection.SelectionImage.DrawRectangle(new ShapeData(pos, size, 0, SKColors.Transparent, Selection.SelectionColor));
 
-            oldChunks.UnionWith(target.Selection.SelectionImage.FindAffectedChunks());
-            return new Selection_ChangeInfo() { Chunks = oldChunks };
-        }
+        oldChunks.UnionWith(target.Selection.SelectionImage.FindAffectedChunks());
+        return new Selection_ChangeInfo() { Chunks = oldChunks };
+    }
 
-        public override IChangeInfo? Apply(Document target, out bool ignoreInUndo)
-        {
-            var changes = ApplyTemporarily(target);
-            originalSelectionState = new CommittedChunkStorage(target.Selection.SelectionImage, ((Selection_ChangeInfo)changes!).Chunks!);
-            target.Selection.SelectionImage.CommitChanges();
-            target.Selection.IsEmptyAndInactive = target.Selection.SelectionImage.CheckIfCommittedIsEmpty();
-            ignoreInUndo = false;
-            return changes;
-        }
+    public override IChangeInfo? Apply(Document target, out bool ignoreInUndo)
+    {
+        var changes = ApplyTemporarily(target);
+        originalSelectionState = new CommittedChunkStorage(target.Selection.SelectionImage, ((Selection_ChangeInfo)changes!).Chunks!);
+        target.Selection.SelectionImage.CommitChanges();
+        target.Selection.IsEmptyAndInactive = target.Selection.SelectionImage.CheckIfCommittedIsEmpty();
+        ignoreInUndo = false;
+        return changes;
+    }
 
-        public override IChangeInfo? Revert(Document target)
-        {
-            target.Selection.IsEmptyAndInactive = originalIsEmpty;
-            originalSelectionState!.ApplyChunksToImage(target.Selection.SelectionImage);
-            originalSelectionState.Dispose();
-            originalSelectionState = null;
-            var changes = new Selection_ChangeInfo() { Chunks = target.Selection.SelectionImage.FindAffectedChunks() };
-            target.Selection.SelectionImage.CommitChanges();
-            return changes;
-        }
+    public override IChangeInfo? Revert(Document target)
+    {
+        target.Selection.IsEmptyAndInactive = originalIsEmpty;
+        originalSelectionState!.ApplyChunksToImage(target.Selection.SelectionImage);
+        originalSelectionState.Dispose();
+        originalSelectionState = null;
+        var changes = new Selection_ChangeInfo() { Chunks = target.Selection.SelectionImage.FindAffectedChunks() };
+        target.Selection.SelectionImage.CommitChanges();
+        return changes;
+    }
 
-        public override void Dispose()
-        {
-            originalSelectionState?.Dispose();
-        }
+    public override void Dispose()
+    {
+        originalSelectionState?.Dispose();
     }
 }

+ 25 - 26
src/PixiEditor.ChangeableDocument/Changes/Properties/CreateStructureMemberMask_Change.cs

@@ -2,37 +2,36 @@
 using PixiEditor.ChangeableDocument.Changeables;
 using PixiEditor.ChangeableDocument.ChangeInfos;
 
-namespace PixiEditor.ChangeableDocument.Changes.Properties
+namespace PixiEditor.ChangeableDocument.Changes.Properties;
+
+internal class CreateStructureMemberMask_Change : Change
 {
-    internal class CreateStructureMemberMask_Change : Change
+    private Guid targetMember;
+    public CreateStructureMemberMask_Change(Guid memberGuid)
     {
-        private Guid targetMember;
-        public CreateStructureMemberMask_Change(Guid memberGuid)
-        {
-            targetMember = memberGuid;
-        }
+        targetMember = memberGuid;
+    }
 
-        public override void Initialize(Document target) { }
+    public override void Initialize(Document target) { }
 
-        public override IChangeInfo? Apply(Document target, out bool ignoreInUndo)
-        {
-            var member = target.FindMemberOrThrow(targetMember);
-            if (member.Mask is not null)
-                throw new InvalidOperationException("Cannot create a mask; the target member already has one");
-            member.Mask = new ChunkyImage(target.Size);
+    public override IChangeInfo? Apply(Document target, out bool ignoreInUndo)
+    {
+        var member = target.FindMemberOrThrow(targetMember);
+        if (member.Mask is not null)
+            throw new InvalidOperationException("Cannot create a mask; the target member already has one");
+        member.Mask = new ChunkyImage(target.Size);
 
-            ignoreInUndo = false;
-            return new StructureMemberMask_ChangeInfo() { GuidValue = targetMember };
-        }
+        ignoreInUndo = false;
+        return new StructureMemberMask_ChangeInfo() { GuidValue = targetMember };
+    }
 
-        public override IChangeInfo? Revert(Document target)
-        {
-            var member = target.FindMemberOrThrow(targetMember);
-            if (member.Mask is null)
-                throw new InvalidOperationException("Cannot delete the mask; the target member has no mask");
-            member.Mask.Dispose();
-            member.Mask = null;
-            return new StructureMemberMask_ChangeInfo() { GuidValue = targetMember };
-        }
+    public override IChangeInfo? Revert(Document target)
+    {
+        var member = target.FindMemberOrThrow(targetMember);
+        if (member.Mask is null)
+            throw new InvalidOperationException("Cannot delete the mask; the target member has no mask");
+        member.Mask.Dispose();
+        member.Mask = null;
+        return new StructureMemberMask_ChangeInfo() { GuidValue = targetMember };
     }
 }

+ 43 - 44
src/PixiEditor.ChangeableDocument/Changes/Properties/DeleteStructureMemberMask_Change.cs

@@ -2,51 +2,50 @@
 using PixiEditor.ChangeableDocument.Changeables;
 using PixiEditor.ChangeableDocument.ChangeInfos;
 
-namespace PixiEditor.ChangeableDocument.Changes.Properties
+namespace PixiEditor.ChangeableDocument.Changes.Properties;
+
+internal class DeleteStructureMemberMask_Change : Change
 {
-    internal class DeleteStructureMemberMask_Change : Change
+    private readonly Guid memberGuid;
+    private ChunkyImage? storedMask;
+
+    public DeleteStructureMemberMask_Change(Guid memberGuid)
+    {
+        this.memberGuid = memberGuid;
+    }
+
+    public override void Initialize(Document target)
+    {
+        var member = target.FindMemberOrThrow(memberGuid);
+        if (member.Mask is null)
+            throw new InvalidOperationException("Cannot delete the mask; Target member has no mask");
+        storedMask = member.Mask.CloneFromLatest();
+    }
+
+    public override IChangeInfo? Apply(Document target, out bool ignoreInUndo)
+    {
+        var member = target.FindMemberOrThrow(memberGuid);
+        if (member.Mask is null)
+            throw new InvalidOperationException("Cannot delete the mask; Target member has no mask");
+        member.Mask.Dispose();
+        member.Mask = null;
+
+        ignoreInUndo = false;
+        return new StructureMemberMask_ChangeInfo() { GuidValue = memberGuid };
+    }
+
+    public override IChangeInfo? Revert(Document target)
+    {
+        var member = target.FindMemberOrThrow(memberGuid);
+        if (member.Mask is not null)
+            throw new InvalidOperationException("Cannot revert mask deletion; The target member already has a mask");
+        member.Mask = storedMask!.CloneFromLatest();
+
+        return new StructureMemberMask_ChangeInfo() { GuidValue = memberGuid };
+    }
+
+    public override void Dispose()
     {
-        private readonly Guid memberGuid;
-        private ChunkyImage? storedMask;
-
-        public DeleteStructureMemberMask_Change(Guid memberGuid)
-        {
-            this.memberGuid = memberGuid;
-        }
-
-        public override void Initialize(Document target)
-        {
-            var member = target.FindMemberOrThrow(memberGuid);
-            if (member.Mask is null)
-                throw new InvalidOperationException("Cannot delete the mask; Target member has no mask");
-            storedMask = member.Mask.CloneFromLatest();
-        }
-
-        public override IChangeInfo? Apply(Document target, out bool ignoreInUndo)
-        {
-            var member = target.FindMemberOrThrow(memberGuid);
-            if (member.Mask is null)
-                throw new InvalidOperationException("Cannot delete the mask; Target member has no mask");
-            member.Mask.Dispose();
-            member.Mask = null;
-
-            ignoreInUndo = false;
-            return new StructureMemberMask_ChangeInfo() { GuidValue = memberGuid };
-        }
-
-        public override IChangeInfo? Revert(Document target)
-        {
-            var member = target.FindMemberOrThrow(memberGuid);
-            if (member.Mask is not null)
-                throw new InvalidOperationException("Cannot revert mask deletion; The target member already has a mask");
-            member.Mask = storedMask!.CloneFromLatest();
-
-            return new StructureMemberMask_ChangeInfo() { GuidValue = memberGuid };
-        }
-
-        public override void Dispose()
-        {
-            storedMask?.Dispose();
-        }
+        storedMask?.Dispose();
     }
 }

+ 28 - 29
src/PixiEditor.ChangeableDocument/Changes/Properties/StructureMemberIsVisible_Change.cs

@@ -1,40 +1,39 @@
 using PixiEditor.ChangeableDocument.Changeables;
 using PixiEditor.ChangeableDocument.ChangeInfos;
 
-namespace PixiEditor.ChangeableDocument.Changes.Properties
+namespace PixiEditor.ChangeableDocument.Changes.Properties;
+
+internal class StructureMemberIsVisible_Change : Change
 {
-    internal class StructureMemberIsVisible_Change : Change
+    private bool? originalIsVisible;
+    private bool newIsVisible;
+    private Guid targetMember;
+    public StructureMemberIsVisible_Change(Guid targetMember, bool newIsVisible)
     {
-        private bool? originalIsVisible;
-        private bool newIsVisible;
-        private Guid targetMember;
-        public StructureMemberIsVisible_Change(Guid targetMember, bool newIsVisible)
-        {
-            this.targetMember = targetMember;
-            this.newIsVisible = newIsVisible;
-        }
+        this.targetMember = targetMember;
+        this.newIsVisible = newIsVisible;
+    }
 
-        public override void Initialize(Document target)
-        {
-            var member = target.FindMemberOrThrow(targetMember);
-            originalIsVisible = member.IsVisible;
-        }
+    public override void Initialize(Document target)
+    {
+        var member = target.FindMemberOrThrow(targetMember);
+        originalIsVisible = member.IsVisible;
+    }
 
-        public override IChangeInfo? Apply(Document target, out bool ignoreInUndo)
-        {
-            // don't record layer/folder visibility changes - it's just more convenient this way
-            ignoreInUndo = true;
-            if (originalIsVisible == newIsVisible)
-                return null;
-            target.FindMemberOrThrow(targetMember).IsVisible = newIsVisible;
+    public override IChangeInfo? Apply(Document target, out bool ignoreInUndo)
+    {
+        // don't record layer/folder visibility changes - it's just more convenient this way
+        ignoreInUndo = true;
+        if (originalIsVisible == newIsVisible)
+            return null;
+        target.FindMemberOrThrow(targetMember).IsVisible = newIsVisible;
 
-            return new StructureMemberIsVisible_ChangeInfo() { GuidValue = targetMember };
-        }
+        return new StructureMemberIsVisible_ChangeInfo() { GuidValue = targetMember };
+    }
 
-        public override IChangeInfo? Revert(Document target)
-        {
-            target.FindMemberOrThrow(targetMember).IsVisible = originalIsVisible!.Value;
-            return new StructureMemberIsVisible_ChangeInfo() { GuidValue = targetMember };
-        }
+    public override IChangeInfo? Revert(Document target)
+    {
+        target.FindMemberOrThrow(targetMember).IsVisible = originalIsVisible!.Value;
+        return new StructureMemberIsVisible_ChangeInfo() { GuidValue = targetMember };
     }
 }

+ 37 - 38
src/PixiEditor.ChangeableDocument/Changes/Properties/StructureMemberName_Change.cs

@@ -1,51 +1,50 @@
 using PixiEditor.ChangeableDocument.Changeables;
 using PixiEditor.ChangeableDocument.ChangeInfos;
 
-namespace PixiEditor.ChangeableDocument.Changes.Properties
+namespace PixiEditor.ChangeableDocument.Changes.Properties;
+
+internal class StructureMemberName_Change : Change
 {
-    internal class StructureMemberName_Change : Change
+    private string? originalName;
+    private string newName;
+    private Guid targetMember;
+    public StructureMemberName_Change(Guid targetMember, string newName)
     {
-        private string? originalName;
-        private string newName;
-        private Guid targetMember;
-        public StructureMemberName_Change(Guid targetMember, string newName)
-        {
-            this.targetMember = targetMember;
-            this.newName = newName;
-        }
+        this.targetMember = targetMember;
+        this.newName = newName;
+    }
 
-        public override void Initialize(Document target)
-        {
-            var member = target.FindMemberOrThrow(targetMember);
-            originalName = member.Name;
-        }
+    public override void Initialize(Document target)
+    {
+        var member = target.FindMemberOrThrow(targetMember);
+        originalName = member.Name;
+    }
 
-        public override IChangeInfo? Apply(Document target, out bool ignoreInUndo)
+    public override IChangeInfo? Apply(Document target, out bool ignoreInUndo)
+    {
+        if (originalName == newName)
         {
-            if (originalName == newName)
-            {
-                ignoreInUndo = true;
-                return null;
-            }
-            target.FindMemberOrThrow(targetMember).Name = newName;
-
-            ignoreInUndo = false;
-            return new StructureMemberName_ChangeInfo() { GuidValue = targetMember };
+            ignoreInUndo = true;
+            return null;
         }
+        target.FindMemberOrThrow(targetMember).Name = newName;
 
-        public override IChangeInfo? Revert(Document target)
-        {
-            if (originalName is null)
-                throw new InvalidOperationException("No name to revert to");
-            target.FindMemberOrThrow(targetMember).Name = originalName;
-            return new StructureMemberName_ChangeInfo() { GuidValue = targetMember };
-        }
+        ignoreInUndo = false;
+        return new StructureMemberName_ChangeInfo() { GuidValue = targetMember };
+    }
 
-        public override bool IsMergeableWith(Change other)
-        {
-            if (other is not StructureMemberName_Change same)
-                return false;
-            return same.targetMember == targetMember;
-        }
+    public override IChangeInfo? Revert(Document target)
+    {
+        if (originalName is null)
+            throw new InvalidOperationException("No name to revert to");
+        target.FindMemberOrThrow(targetMember).Name = originalName;
+        return new StructureMemberName_ChangeInfo() { GuidValue = targetMember };
+    }
+
+    public override bool IsMergeableWith(Change other)
+    {
+        if (other is not StructureMemberName_Change same)
+            return false;
+        return same.targetMember == targetMember;
     }
 }

+ 45 - 46
src/PixiEditor.ChangeableDocument/Changes/Properties/StructureMemberOpacity_UpdateableChange.cs

@@ -1,65 +1,64 @@
 using PixiEditor.ChangeableDocument.Changeables;
 using PixiEditor.ChangeableDocument.ChangeInfos;
 
-namespace PixiEditor.ChangeableDocument.Changes.Properties
+namespace PixiEditor.ChangeableDocument.Changes.Properties;
+
+internal class StructureMemberOpacity_UpdateableChange : UpdateableChange
 {
-    internal class StructureMemberOpacity_UpdateableChange : UpdateableChange
-    {
-        private Guid memberGuid;
+    private Guid memberGuid;
 
-        private float originalOpacity;
-        private float newOpacity;
+    private float originalOpacity;
+    private float newOpacity;
 
-        public StructureMemberOpacity_UpdateableChange(Guid memberGuid, float opacity)
-        {
-            this.memberGuid = memberGuid;
-            newOpacity = opacity;
-        }
+    public StructureMemberOpacity_UpdateableChange(Guid memberGuid, float opacity)
+    {
+        this.memberGuid = memberGuid;
+        newOpacity = opacity;
+    }
 
-        public void Update(float updatedOpacity)
-        {
-            newOpacity = updatedOpacity;
-        }
+    public void Update(float updatedOpacity)
+    {
+        newOpacity = updatedOpacity;
+    }
 
-        public override void Initialize(Document document)
-        {
-            var member = document.FindMemberOrThrow(memberGuid);
-            originalOpacity = member.Opacity;
-        }
+    public override void Initialize(Document document)
+    {
+        var member = document.FindMemberOrThrow(memberGuid);
+        originalOpacity = member.Opacity;
+    }
 
-        public override IChangeInfo? ApplyTemporarily(Document target) => Apply(target, out _);
+    public override IChangeInfo? ApplyTemporarily(Document target) => Apply(target, out _);
 
-        public override IChangeInfo? Apply(Document document, out bool ignoreInUndo)
+    public override IChangeInfo? Apply(Document document, out bool ignoreInUndo)
+    {
+        if (originalOpacity == newOpacity)
         {
-            if (originalOpacity == newOpacity)
-            {
-                ignoreInUndo = true;
-                return null;
-            }
+            ignoreInUndo = true;
+            return null;
+        }
 
-            var member = document.FindMemberOrThrow(memberGuid);
-            member.Opacity = newOpacity;
+        var member = document.FindMemberOrThrow(memberGuid);
+        member.Opacity = newOpacity;
 
-            ignoreInUndo = false;
-            return new StructureMemberOpacity_ChangeInfo() { GuidValue = memberGuid };
-        }
+        ignoreInUndo = false;
+        return new StructureMemberOpacity_ChangeInfo() { GuidValue = memberGuid };
+    }
 
-        public override IChangeInfo? Revert(Document document)
-        {
-            if (originalOpacity == newOpacity)
-                return null;
+    public override IChangeInfo? Revert(Document document)
+    {
+        if (originalOpacity == newOpacity)
+            return null;
 
-            var member = document.FindMemberOrThrow(memberGuid);
-            member.Opacity = originalOpacity;
+        var member = document.FindMemberOrThrow(memberGuid);
+        member.Opacity = originalOpacity;
 
-            return new StructureMemberOpacity_ChangeInfo() { GuidValue = memberGuid };
-        }
+        return new StructureMemberOpacity_ChangeInfo() { GuidValue = memberGuid };
+    }
 
-        public override bool IsMergeableWith(Change other)
-        {
-            if (other is not StructureMemberOpacity_UpdateableChange same)
-                return false;
-            return same.memberGuid == memberGuid;
-        }
+    public override bool IsMergeableWith(Change other)
+    {
+        if (other is not StructureMemberOpacity_UpdateableChange same)
+            return false;
+        return same.memberGuid == memberGuid;
     }
 }

+ 80 - 81
src/PixiEditor.ChangeableDocument/Changes/Root/ResizeCanvas_Change.cs

@@ -3,109 +3,108 @@ using ChunkyImageLib.DataHolders;
 using PixiEditor.ChangeableDocument.Changeables;
 using PixiEditor.ChangeableDocument.ChangeInfos;
 
-namespace PixiEditor.ChangeableDocument.Changes.Root
+namespace PixiEditor.ChangeableDocument.Changes.Root;
+
+internal class ResizeCanvas_Change : Change
 {
-    internal class ResizeCanvas_Change : Change
+    private Vector2i originalSize;
+    private Dictionary<Guid, CommittedChunkStorage> deletedChunks = new();
+    private Dictionary<Guid, CommittedChunkStorage> deletedMaskChunks = new();
+    private CommittedChunkStorage? selectionChunkStorage;
+    private Vector2i newSize;
+    public ResizeCanvas_Change(Vector2i size)
     {
-        private Vector2i originalSize;
-        private Dictionary<Guid, CommittedChunkStorage> deletedChunks = new();
-        private Dictionary<Guid, CommittedChunkStorage> deletedMaskChunks = new();
-        private CommittedChunkStorage? selectionChunkStorage;
-        private Vector2i newSize;
-        public ResizeCanvas_Change(Vector2i size)
-        {
-            newSize = size;
-        }
-        public override void Initialize(Document target)
-        {
-            originalSize = target.Size;
-        }
+        newSize = size;
+    }
+    public override void Initialize(Document target)
+    {
+        originalSize = target.Size;
+    }
 
-        private void ForEachLayer(Folder folder, Action<Layer> action)
+    private void ForEachLayer(Folder folder, Action<Layer> action)
+    {
+        foreach (var child in folder.Children)
         {
-            foreach (var child in folder.Children)
+            if (child is Layer layer)
             {
-                if (child is Layer layer)
-                {
-                    action(layer);
-                }
-                else if (child is Folder innerFolder)
-                    ForEachLayer(innerFolder, action);
+                action(layer);
             }
+            else if (child is Folder innerFolder)
+                ForEachLayer(innerFolder, action);
         }
+    }
 
-        public override IChangeInfo? Apply(Document target, out bool ignoreInUndo)
+    public override IChangeInfo? Apply(Document target, out bool ignoreInUndo)
+    {
+        if (originalSize == newSize)
         {
-            if (originalSize == newSize)
-            {
-                ignoreInUndo = true;
-                return null;
-            }
+            ignoreInUndo = true;
+            return null;
+        }
 
-            target.Size = newSize;
+        target.Size = newSize;
 
-            ForEachLayer(target.StructureRoot, (layer) =>
-            {
-                layer.LayerImage.Resize(newSize);
-                deletedChunks.Add(layer.GuidValue, new CommittedChunkStorage(layer.LayerImage, layer.LayerImage.FindAffectedChunks()));
-                layer.LayerImage.CommitChanges();
+        ForEachLayer(target.StructureRoot, (layer) =>
+        {
+            layer.LayerImage.Resize(newSize);
+            deletedChunks.Add(layer.GuidValue, new CommittedChunkStorage(layer.LayerImage, layer.LayerImage.FindAffectedChunks()));
+            layer.LayerImage.CommitChanges();
 
-                if (layer.Mask is null)
-                    return;
+            if (layer.Mask is null)
+                return;
 
-                layer.Mask.Resize(newSize);
-                deletedMaskChunks.Add(layer.GuidValue, new CommittedChunkStorage(layer.Mask, layer.Mask.FindAffectedChunks()));
-                layer.Mask.CommitChanges();
-            });
+            layer.Mask.Resize(newSize);
+            deletedMaskChunks.Add(layer.GuidValue, new CommittedChunkStorage(layer.Mask, layer.Mask.FindAffectedChunks()));
+            layer.Mask.CommitChanges();
+        });
 
-            target.Selection.SelectionImage.Resize(newSize);
-            selectionChunkStorage = new(target.Selection.SelectionImage, target.Selection.SelectionImage.FindAffectedChunks());
-            target.Selection.SelectionImage.CommitChanges();
+        target.Selection.SelectionImage.Resize(newSize);
+        selectionChunkStorage = new(target.Selection.SelectionImage, target.Selection.SelectionImage.FindAffectedChunks());
+        target.Selection.SelectionImage.CommitChanges();
 
-            ignoreInUndo = false;
-            return new Size_ChangeInfo();
-        }
+        ignoreInUndo = false;
+        return new Size_ChangeInfo();
+    }
 
-        public override IChangeInfo? Revert(Document target)
-        {
-            if (originalSize == newSize)
-                return null;
+    public override IChangeInfo? Revert(Document target)
+    {
+        if (originalSize == newSize)
+            return null;
 
-            target.Size = originalSize;
-            ForEachLayer(target.StructureRoot, (layer) =>
-            {
-                layer.LayerImage.Resize(originalSize);
-                deletedChunks[layer.GuidValue].ApplyChunksToImage(layer.LayerImage);
-                layer.LayerImage.CommitChanges();
+        target.Size = originalSize;
+        ForEachLayer(target.StructureRoot, (layer) =>
+        {
+            layer.LayerImage.Resize(originalSize);
+            deletedChunks[layer.GuidValue].ApplyChunksToImage(layer.LayerImage);
+            layer.LayerImage.CommitChanges();
 
-                if (layer.Mask is null)
-                    return;
+            if (layer.Mask is null)
+                return;
 
-                layer.Mask.Resize(originalSize);
-                deletedMaskChunks[layer.GuidValue].ApplyChunksToImage(layer.Mask);
-                layer.Mask.CommitChanges();
-            });
+            layer.Mask.Resize(originalSize);
+            deletedMaskChunks[layer.GuidValue].ApplyChunksToImage(layer.Mask);
+            layer.Mask.CommitChanges();
+        });
 
-            target.Selection.SelectionImage.Resize(originalSize);
-            selectionChunkStorage!.ApplyChunksToImage(target.Selection.SelectionImage);
-            target.Selection.SelectionImage.CommitChanges();
-            selectionChunkStorage.Dispose();
-            selectionChunkStorage = null;
+        target.Selection.SelectionImage.Resize(originalSize);
+        selectionChunkStorage!.ApplyChunksToImage(target.Selection.SelectionImage);
+        target.Selection.SelectionImage.CommitChanges();
+        selectionChunkStorage.Dispose();
+        selectionChunkStorage = null;
 
-            foreach (var stored in deletedChunks)
-                stored.Value.Dispose();
-            deletedChunks = new();
+        foreach (var stored in deletedChunks)
+            stored.Value.Dispose();
+        deletedChunks = new();
 
-            return new Size_ChangeInfo();
-        }
+        return new Size_ChangeInfo();
+    }
 
-        public override void Dispose()
-        {
-            foreach (var layer in deletedChunks)
-                layer.Value.Dispose();
-            foreach (var mask in deletedMaskChunks)
-                mask.Value.Dispose();
-            selectionChunkStorage?.Dispose();
-        }
+    public override void Dispose()
+    {
+        foreach (var layer in deletedChunks)
+            layer.Value.Dispose();
+        foreach (var mask in deletedMaskChunks)
+            mask.Value.Dispose();
+        selectionChunkStorage?.Dispose();
     }
 }

+ 35 - 36
src/PixiEditor.ChangeableDocument/Changes/Structure/CreateStructureMember_Change.cs

@@ -2,51 +2,50 @@
 using PixiEditor.ChangeableDocument.ChangeInfos;
 using PixiEditor.ChangeableDocument.Enums;
 
-namespace PixiEditor.ChangeableDocument.Changes.Structure
+namespace PixiEditor.ChangeableDocument.Changes.Structure;
+
+internal class CreateStructureMember_Change : Change
 {
-    internal class CreateStructureMember_Change : Change
-    {
-        private Guid newMemberGuid;
+    private Guid newMemberGuid;
 
-        private Guid parentFolderGuid;
-        private int parentFolderIndex;
-        private StructureMemberType type;
+    private Guid parentFolderGuid;
+    private int parentFolderIndex;
+    private StructureMemberType type;
 
-        public CreateStructureMember_Change(Guid parentFolder, Guid newGuid, int parentFolderIndex, StructureMemberType type)
-        {
-            this.parentFolderGuid = parentFolder;
-            this.parentFolderIndex = parentFolderIndex;
-            this.type = type;
-            newMemberGuid = newGuid;
-        }
+    public CreateStructureMember_Change(Guid parentFolder, Guid newGuid, int parentFolderIndex, StructureMemberType type)
+    {
+        this.parentFolderGuid = parentFolder;
+        this.parentFolderIndex = parentFolderIndex;
+        this.type = type;
+        newMemberGuid = newGuid;
+    }
 
-        public override void Initialize(Document target) { }
+    public override void Initialize(Document target) { }
 
-        public override IChangeInfo Apply(Document document, out bool ignoreInUndo)
-        {
-            var folder = (Folder)document.FindMemberOrThrow(parentFolderGuid);
+    public override IChangeInfo Apply(Document document, out bool ignoreInUndo)
+    {
+        var folder = (Folder)document.FindMemberOrThrow(parentFolderGuid);
 
-            StructureMember member = type switch
-            {
-                StructureMemberType.Layer => new Layer(document.Size) { GuidValue = newMemberGuid },
-                StructureMemberType.Folder => new Folder() { GuidValue = newMemberGuid },
-                _ => throw new InvalidOperationException("Cannon create member of type " + type.ToString())
-            };
+        StructureMember member = type switch
+        {
+            StructureMemberType.Layer => new Layer(document.Size) { GuidValue = newMemberGuid },
+            StructureMemberType.Folder => new Folder() { GuidValue = newMemberGuid },
+            _ => throw new InvalidOperationException("Cannon create member of type " + type.ToString())
+        };
 
-            folder.Children.Insert(parentFolderIndex, member);
+        folder.Children.Insert(parentFolderIndex, member);
 
-            ignoreInUndo = false;
-            return new CreateStructureMember_ChangeInfo() { GuidValue = newMemberGuid };
-        }
+        ignoreInUndo = false;
+        return new CreateStructureMember_ChangeInfo() { GuidValue = newMemberGuid };
+    }
 
-        public override IChangeInfo Revert(Document document)
-        {
-            var folder = (Folder)document.FindMemberOrThrow(parentFolderGuid);
-            var child = document.FindMemberOrThrow(newMemberGuid);
-            child.Dispose();
-            folder.Children.RemoveAt(folder.Children.FindIndex(child => child.GuidValue == newMemberGuid));
+    public override IChangeInfo Revert(Document document)
+    {
+        var folder = (Folder)document.FindMemberOrThrow(parentFolderGuid);
+        var child = document.FindMemberOrThrow(newMemberGuid);
+        child.Dispose();
+        folder.Children.RemoveAt(folder.Children.FindIndex(child => child.GuidValue == newMemberGuid));
 
-            return new DeleteStructureMember_ChangeInfo() { GuidValue = newMemberGuid };
-        }
+        return new DeleteStructureMember_ChangeInfo() { GuidValue = newMemberGuid };
     }
 }

+ 40 - 41
src/PixiEditor.ChangeableDocument/Changes/Structure/DeleteStructureMember_Change.cs

@@ -1,48 +1,47 @@
 using PixiEditor.ChangeableDocument.Changeables;
 using PixiEditor.ChangeableDocument.ChangeInfos;
 
-namespace PixiEditor.ChangeableDocument.Changes.Structure
+namespace PixiEditor.ChangeableDocument.Changes.Structure;
+
+internal class DeleteStructureMember_Change : Change
 {
-    internal class DeleteStructureMember_Change : Change
+    private Guid memberGuid;
+    private Guid parentGuid;
+    private int originalIndex;
+    private StructureMember? savedCopy;
+    public DeleteStructureMember_Change(Guid memberGuid)
+    {
+        this.memberGuid = memberGuid;
+    }
+
+    public override void Initialize(Document document)
+    {
+        var (member, parent) = document.FindChildAndParentOrThrow(memberGuid);
+
+        originalIndex = parent.Children.IndexOf(member);
+        parentGuid = parent.GuidValue;
+        savedCopy = member.Clone();
+    }
+
+    public override IChangeInfo Apply(Document document, out bool ignoreInUndo)
+    {
+        var (member, parent) = document.FindChildAndParentOrThrow(memberGuid);
+        parent.Children.Remove(member);
+        member.Dispose();
+        ignoreInUndo = false;
+        return new DeleteStructureMember_ChangeInfo() { GuidValue = memberGuid };
+    }
+
+    public override IChangeInfo Revert(Document doc)
+    {
+        var parent = (Folder)doc.FindMemberOrThrow(parentGuid);
+
+        parent.Children.Insert(originalIndex, savedCopy!.Clone());
+        return new CreateStructureMember_ChangeInfo() { GuidValue = memberGuid };
+    }
+
+    public override void Dispose()
     {
-        private Guid memberGuid;
-        private Guid parentGuid;
-        private int originalIndex;
-        private StructureMember? savedCopy;
-        public DeleteStructureMember_Change(Guid memberGuid)
-        {
-            this.memberGuid = memberGuid;
-        }
-
-        public override void Initialize(Document document)
-        {
-            var (member, parent) = document.FindChildAndParentOrThrow(memberGuid);
-
-            originalIndex = parent.Children.IndexOf(member);
-            parentGuid = parent.GuidValue;
-            savedCopy = member.Clone();
-        }
-
-        public override IChangeInfo Apply(Document document, out bool ignoreInUndo)
-        {
-            var (member, parent) = document.FindChildAndParentOrThrow(memberGuid);
-            parent.Children.Remove(member);
-            member.Dispose();
-            ignoreInUndo = false;
-            return new DeleteStructureMember_ChangeInfo() { GuidValue = memberGuid };
-        }
-
-        public override IChangeInfo Revert(Document doc)
-        {
-            var parent = (Folder)doc.FindMemberOrThrow(parentGuid);
-
-            parent.Children.Insert(originalIndex, savedCopy!.Clone());
-            return new CreateStructureMember_ChangeInfo() { GuidValue = memberGuid };
-        }
-
-        public override void Dispose()
-        {
-            savedCopy!.Dispose();
-        }
+        savedCopy!.Dispose();
     }
 }

+ 44 - 45
src/PixiEditor.ChangeableDocument/Changes/Structure/MoveStructureMember_Change.cs

@@ -1,52 +1,51 @@
 using PixiEditor.ChangeableDocument.Changeables;
 using PixiEditor.ChangeableDocument.ChangeInfos;
 
-namespace PixiEditor.ChangeableDocument.Changes.Structure
+namespace PixiEditor.ChangeableDocument.Changes.Structure;
+
+internal class MoveStructureMember_Change : Change
 {
-    internal class MoveStructureMember_Change : Change
+    private Guid memberGuid;
+
+    private Guid targetFolderGuid;
+    private int targetFolderIndex;
+
+    private Guid originalFolderGuid;
+    private int originalFolderIndex;
+
+    public MoveStructureMember_Change(Guid memberGuid, Guid targetFolder, int targetFolderIndex)
+    {
+        this.memberGuid = memberGuid;
+        this.targetFolderGuid = targetFolder;
+        this.targetFolderIndex = targetFolderIndex;
+    }
+
+    public override void Initialize(Document document)
+    {
+        var (member, curFolder) = document.FindChildAndParentOrThrow(memberGuid);
+        originalFolderGuid = curFolder.GuidValue;
+        originalFolderIndex = curFolder.Children.IndexOf(member);
+    }
+
+    private static void Move(Document document, Guid memberGuid, Guid targetFolderGuid, int targetIndex)
+    {
+        var targetFolder = (Folder)document.FindMemberOrThrow(targetFolderGuid);
+        var (member, curFolder) = document.FindChildAndParentOrThrow(memberGuid);
+
+        curFolder.Children.Remove(member);
+        targetFolder.Children.Insert(targetIndex, member);
+    }
+
+    public override IChangeInfo? Apply(Document target, out bool ignoreInUndo)
+    {
+        Move(target, memberGuid, targetFolderGuid, targetFolderIndex);
+        ignoreInUndo = false;
+        return new MoveStructureMember_ChangeInfo() { GuidValue = memberGuid };
+    }
+
+    public override IChangeInfo? Revert(Document target)
     {
-        private Guid memberGuid;
-
-        private Guid targetFolderGuid;
-        private int targetFolderIndex;
-
-        private Guid originalFolderGuid;
-        private int originalFolderIndex;
-
-        public MoveStructureMember_Change(Guid memberGuid, Guid targetFolder, int targetFolderIndex)
-        {
-            this.memberGuid = memberGuid;
-            this.targetFolderGuid = targetFolder;
-            this.targetFolderIndex = targetFolderIndex;
-        }
-
-        public override void Initialize(Document document)
-        {
-            var (member, curFolder) = document.FindChildAndParentOrThrow(memberGuid);
-            originalFolderGuid = curFolder.GuidValue;
-            originalFolderIndex = curFolder.Children.IndexOf(member);
-        }
-
-        private static void Move(Document document, Guid memberGuid, Guid targetFolderGuid, int targetIndex)
-        {
-            var targetFolder = (Folder)document.FindMemberOrThrow(targetFolderGuid);
-            var (member, curFolder) = document.FindChildAndParentOrThrow(memberGuid);
-
-            curFolder.Children.Remove(member);
-            targetFolder.Children.Insert(targetIndex, member);
-        }
-
-        public override IChangeInfo? Apply(Document target, out bool ignoreInUndo)
-        {
-            Move(target, memberGuid, targetFolderGuid, targetFolderIndex);
-            ignoreInUndo = false;
-            return new MoveStructureMember_ChangeInfo() { GuidValue = memberGuid };
-        }
-
-        public override IChangeInfo? Revert(Document target)
-        {
-            Move(target, memberGuid, originalFolderGuid, originalFolderIndex);
-            return new MoveStructureMember_ChangeInfo() { GuidValue = memberGuid };
-        }
+        Move(target, memberGuid, originalFolderGuid, originalFolderIndex);
+        return new MoveStructureMember_ChangeInfo() { GuidValue = memberGuid };
     }
 }

+ 4 - 5
src/PixiEditor.ChangeableDocument/Changes/UpdateableChange.cs

@@ -1,10 +1,9 @@
 using PixiEditor.ChangeableDocument.Changeables;
 using PixiEditor.ChangeableDocument.ChangeInfos;
 
-namespace PixiEditor.ChangeableDocument.Changes
+namespace PixiEditor.ChangeableDocument.Changes;
+
+internal abstract class UpdateableChange : Change
 {
-    internal abstract class UpdateableChange : Change
-    {
-        public abstract IChangeInfo? ApplyTemporarily(Document target);
-    }
+    public abstract IChangeInfo? ApplyTemporarily(Document target);
 }

+ 201 - 202
src/PixiEditor.ChangeableDocument/DocumentChangeTracker.cs

@@ -5,240 +5,239 @@ using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.ChangeableDocument.ChangeInfos;
 using PixiEditor.ChangeableDocument.Changes;
 
-namespace ChangeableDocument
+namespace ChangeableDocument;
+
+public class DocumentChangeTracker : IDisposable
 {
-    public class DocumentChangeTracker : IDisposable
-    {
-        private Document document;
-        private bool disposed = false;
-        private bool running = false;
-        public IReadOnlyDocument Document => document;
+    private Document document;
+    private bool disposed = false;
+    private bool running = false;
+    public IReadOnlyDocument Document => document;
 
-        private UpdateableChange? activeChange = null;
-        private List<Change>? activePacket = null;
+    private UpdateableChange? activeChange = null;
+    private List<Change>? activePacket = null;
 
-        private Stack<List<Change>> undoStack = new();
-        private Stack<List<Change>> redoStack = new();
+    private Stack<List<Change>> undoStack = new();
+    private Stack<List<Change>> redoStack = new();
 
-        public void Dispose()
-        {
-            if (running)
-                throw new InvalidOperationException("Something is currently being processed");
-            if (disposed)
-                return;
-            disposed = true;
+    public void Dispose()
+    {
+        if (running)
+            throw new InvalidOperationException("Something is currently being processed");
+        if (disposed)
+            return;
+        disposed = true;
 
-            document.Dispose();
+        document.Dispose();
 
-            activeChange?.Dispose();
+        activeChange?.Dispose();
 
-            if (activePacket != null)
-                foreach (var change in activePacket)
-                    change.Dispose();
+        if (activePacket != null)
+            foreach (var change in activePacket)
+                change.Dispose();
 
-            foreach (var list in undoStack)
-                foreach (var change in list)
-                    change.Dispose();
+        foreach (var list in undoStack)
+            foreach (var change in list)
+                change.Dispose();
 
-            foreach (var list in redoStack)
-                foreach (var change in list)
-                    change.Dispose();
-        }
+        foreach (var list in redoStack)
+            foreach (var change in list)
+                change.Dispose();
+    }
 
-        public DocumentChangeTracker()
-        {
-            document = new Document();
-        }
+    public DocumentChangeTracker()
+    {
+        document = new Document();
+    }
 
-        private void AddToUndo(Change change)
-        {
-            if (activePacket is null)
-                activePacket = new();
-            activePacket.Add(change);
-
-            foreach (var changesToDispose in redoStack)
-                foreach (var changeToDispose in changesToDispose)
-                    changeToDispose.Dispose();
-            redoStack.Clear();
-        }
+    private void AddToUndo(Change change)
+    {
+        if (activePacket is null)
+            activePacket = new();
+        activePacket.Add(change);
+
+        foreach (var changesToDispose in redoStack)
+            foreach (var changeToDispose in changesToDispose)
+                changeToDispose.Dispose();
+        redoStack.Clear();
+    }
 
-        private void CompletePacket()
+    private void CompletePacket()
+    {
+        if (activePacket is null)
+            return;
+
+        // maybe merge with previous
+        if (activePacket.Count == 1 &&
+            undoStack.Count > 0 &&
+            IsHomologous(undoStack.Peek()) &&
+            undoStack.Peek()[^1].IsMergeableWith(activePacket[0]))
         {
-            if (activePacket is null)
-                return;
-
-            // maybe merge with previous
-            if (activePacket.Count == 1 &&
-                undoStack.Count > 0 &&
-                IsHomologous(undoStack.Peek()) &&
-                undoStack.Peek()[^1].IsMergeableWith(activePacket[0]))
-            {
-                undoStack.Peek().Add(activePacket[0]);
-            }
-            else
-            {
-                undoStack.Push(activePacket);
-            }
-
-            activePacket = null;
+            undoStack.Peek().Add(activePacket[0]);
         }
-
-        private bool IsHomologous(List<Change> changes)
+        else
         {
-            for (int i = 1; i < changes.Count; i++)
-            {
-                if (!changes[i].IsMergeableWith(changes[i - 1]))
-                    return false;
-            }
-            return true;
+            undoStack.Push(activePacket);
         }
 
-        private List<IChangeInfo?> Undo()
-        {
-            if (undoStack.Count == 0)
-                return new List<IChangeInfo?>();
-            if (activePacket is not null || activeChange is not null)
-                throw new InvalidOperationException("Cannot undo while there is an active updateable change or an unfinished undo packet");
-            List<IChangeInfo?> changeInfos = new();
-            List<Change> changePacket = undoStack.Pop();
-
-            for (int i = changePacket.Count - 1; i >= 0; i--)
-                changeInfos.Add(changePacket[i].Revert(document));
-
-            redoStack.Push(changePacket);
-            return changeInfos;
-        }
+        activePacket = null;
+    }
 
-        private List<IChangeInfo?> Redo()
+    private bool IsHomologous(List<Change> changes)
+    {
+        for (int i = 1; i < changes.Count; i++)
         {
-            if (redoStack.Count == 0)
-                return new List<IChangeInfo?>();
-            if (activePacket is not null || activeChange is not null)
-                throw new InvalidOperationException("Cannot redo while there is an active updateable change or an unfinished undo packet");
-            List<IChangeInfo?> changeInfos = new();
-            List<Change> changePacket = redoStack.Pop();
-
-            for (int i = 0; i < changePacket.Count; i++)
-                changeInfos.Add(changePacket[i].Apply(document, out _));
-
-            undoStack.Push(changePacket);
-            return changeInfos;
+            if (!changes[i].IsMergeableWith(changes[i - 1]))
+                return false;
         }
+        return true;
+    }
 
-        private void DeleteAllChanges()
-        {
-            if (activeChange is not null || activePacket is not null)
-                throw new InvalidOperationException("Cannot delete all changes while there is an active updateable change or an unfinished undo packet");
-            foreach (var changesToDispose in redoStack)
-                foreach (var changeToDispose in changesToDispose)
-                    changeToDispose.Dispose();
-            foreach (var changesToDispose in undoStack)
-                foreach (var changeToDispose in changesToDispose)
-                    changeToDispose.Dispose();
-            redoStack.Clear();
-            undoStack.Clear();
-        }
+    private List<IChangeInfo?> Undo()
+    {
+        if (undoStack.Count == 0)
+            return new List<IChangeInfo?>();
+        if (activePacket is not null || activeChange is not null)
+            throw new InvalidOperationException("Cannot undo while there is an active updateable change or an unfinished undo packet");
+        List<IChangeInfo?> changeInfos = new();
+        List<Change> changePacket = undoStack.Pop();
+
+        for (int i = changePacket.Count - 1; i >= 0; i--)
+            changeInfos.Add(changePacket[i].Revert(document));
+
+        redoStack.Push(changePacket);
+        return changeInfos;
+    }
 
-        private IChangeInfo? ProcessMakeChangeAction(IMakeChangeAction act)
-        {
-            if (activeChange is not null)
-                throw new InvalidOperationException("Can't make a change while another change is active");
-            var change = act.CreateCorrespondingChange();
-            change.Initialize(document);
-            var info = change.Apply(document, out bool ignoreInUndo);
-            if (!ignoreInUndo)
-                AddToUndo(change);
-            else
-                change.Dispose();
-            return info;
-        }
+    private List<IChangeInfo?> Redo()
+    {
+        if (redoStack.Count == 0)
+            return new List<IChangeInfo?>();
+        if (activePacket is not null || activeChange is not null)
+            throw new InvalidOperationException("Cannot redo while there is an active updateable change or an unfinished undo packet");
+        List<IChangeInfo?> changeInfos = new();
+        List<Change> changePacket = redoStack.Pop();
+
+        for (int i = 0; i < changePacket.Count; i++)
+            changeInfos.Add(changePacket[i].Apply(document, out _));
+
+        undoStack.Push(changePacket);
+        return changeInfos;
+    }
 
-        private IChangeInfo? ProcessStartOrUpdateChangeAction(IStartOrUpdateChangeAction act)
-        {
-            if (activeChange is null)
-            {
-                activeChange = act.CreateCorrespondingChange();
-                activeChange.Initialize(document);
-            }
-            act.UpdateCorrespodingChange(activeChange);
-            return activeChange.ApplyTemporarily(document);
-        }
+    private void DeleteAllChanges()
+    {
+        if (activeChange is not null || activePacket is not null)
+            throw new InvalidOperationException("Cannot delete all changes while there is an active updateable change or an unfinished undo packet");
+        foreach (var changesToDispose in redoStack)
+            foreach (var changeToDispose in changesToDispose)
+                changeToDispose.Dispose();
+        foreach (var changesToDispose in undoStack)
+            foreach (var changeToDispose in changesToDispose)
+                changeToDispose.Dispose();
+        redoStack.Clear();
+        undoStack.Clear();
+    }
 
-        private IChangeInfo? ProcessEndChangeAction(IEndChangeAction act)
+    private IChangeInfo? ProcessMakeChangeAction(IMakeChangeAction act)
+    {
+        if (activeChange is not null)
+            throw new InvalidOperationException("Can't make a change while another change is active");
+        var change = act.CreateCorrespondingChange();
+        change.Initialize(document);
+        var info = change.Apply(document, out bool ignoreInUndo);
+        if (!ignoreInUndo)
+            AddToUndo(change);
+        else
+            change.Dispose();
+        return info;
+    }
+
+    private IChangeInfo? ProcessStartOrUpdateChangeAction(IStartOrUpdateChangeAction act)
+    {
+        if (activeChange is null)
         {
-            if (activeChange is null)
-                throw new InvalidOperationException("Can't end a change: no changes are active");
-            if (!act.IsChangeTypeMatching(activeChange))
-                throw new InvalidOperationException($"Trying to end a change with an action of type {act.GetType()} while a change of type {activeChange.GetType()} is active");
-
-            var info = activeChange.Apply(document, out bool ignoreInUndo);
-            if (!ignoreInUndo)
-                AddToUndo(activeChange);
-            else
-                activeChange.Dispose();
-            activeChange = null;
-            return info;
+            activeChange = act.CreateCorrespondingChange();
+            activeChange.Initialize(document);
         }
+        act.UpdateCorrespodingChange(activeChange);
+        return activeChange.ApplyTemporarily(document);
+    }
+
+    private IChangeInfo? ProcessEndChangeAction(IEndChangeAction act)
+    {
+        if (activeChange is null)
+            throw new InvalidOperationException("Can't end a change: no changes are active");
+        if (!act.IsChangeTypeMatching(activeChange))
+            throw new InvalidOperationException($"Trying to end a change with an action of type {act.GetType()} while a change of type {activeChange.GetType()} is active");
+
+        var info = activeChange.Apply(document, out bool ignoreInUndo);
+        if (!ignoreInUndo)
+            AddToUndo(activeChange);
+        else
+            activeChange.Dispose();
+        activeChange = null;
+        return info;
+    }
 
-        private List<IChangeInfo?> ProcessActionList(List<IAction> actions)
+    private List<IChangeInfo?> ProcessActionList(List<IAction> actions)
+    {
+        List<IChangeInfo?> changeInfos = new();
+        foreach (var action in actions)
         {
-            List<IChangeInfo?> changeInfos = new();
-            foreach (var action in actions)
+            switch (action)
             {
-                switch (action)
-                {
-                    case IMakeChangeAction act:
-                        changeInfos.Add(ProcessMakeChangeAction(act));
-                        break;
-                    case IStartOrUpdateChangeAction act:
-                        changeInfos.Add(ProcessStartOrUpdateChangeAction(act));
-                        break;
-                    case IEndChangeAction act:
-                        changeInfos.Add(ProcessEndChangeAction(act));
-                        break;
-                    case Undo_Action act:
-                        changeInfos.AddRange(Undo());
-                        break;
-                    case Redo_Action act:
-                        changeInfos.AddRange(Redo());
-                        break;
-                    case ChangeBoundary_Action:
-                        CompletePacket();
-                        break;
-                    case DeleteRecordedChanges_Action:
-                        DeleteAllChanges();
-                        break;
-                    //used for "passthrough" actions (move viewport)
-                    case IChangeInfo act:
-                        changeInfos.Add(act);
-                        break;
-                }
+                case IMakeChangeAction act:
+                    changeInfos.Add(ProcessMakeChangeAction(act));
+                    break;
+                case IStartOrUpdateChangeAction act:
+                    changeInfos.Add(ProcessStartOrUpdateChangeAction(act));
+                    break;
+                case IEndChangeAction act:
+                    changeInfos.Add(ProcessEndChangeAction(act));
+                    break;
+                case Undo_Action act:
+                    changeInfos.AddRange(Undo());
+                    break;
+                case Redo_Action act:
+                    changeInfos.AddRange(Redo());
+                    break;
+                case ChangeBoundary_Action:
+                    CompletePacket();
+                    break;
+                case DeleteRecordedChanges_Action:
+                    DeleteAllChanges();
+                    break;
+                //used for "passthrough" actions (move viewport)
+                case IChangeInfo act:
+                    changeInfos.Add(act);
+                    break;
             }
-            return changeInfos;
         }
+        return changeInfos;
+    }
 
-        public async Task<List<IChangeInfo?>> ProcessActions(List<IAction> actions)
-        {
-            if (disposed)
-                throw new ObjectDisposedException(nameof(DocumentChangeTracker));
-            if (running)
-                throw new InvalidOperationException("Already currently processing");
-            running = true;
-            var result = await Task.Run(() => ProcessActionList(actions)).ConfigureAwait(true);
-            running = false;
-            return result;
-        }
+    public async Task<List<IChangeInfo?>> ProcessActions(List<IAction> actions)
+    {
+        if (disposed)
+            throw new ObjectDisposedException(nameof(DocumentChangeTracker));
+        if (running)
+            throw new InvalidOperationException("Already currently processing");
+        running = true;
+        var result = await Task.Run(() => ProcessActionList(actions)).ConfigureAwait(true);
+        running = false;
+        return result;
+    }
 
-        public List<IChangeInfo?> ProcessActionsSync(List<IAction> actions)
-        {
-            if (disposed)
-                throw new ObjectDisposedException(nameof(DocumentChangeTracker));
-            if (running)
-                throw new InvalidOperationException("Already currently processing");
-            running = true;
-            var result = ProcessActionList(actions);
-            running = false;
-            return result;
-        }
+    public List<IChangeInfo?> ProcessActionsSync(List<IAction> actions)
+    {
+        if (disposed)
+            throw new ObjectDisposedException(nameof(DocumentChangeTracker));
+        if (running)
+            throw new InvalidOperationException("Already currently processing");
+        running = true;
+        var result = ProcessActionList(actions);
+        running = false;
+        return result;
     }
 }

+ 5 - 6
src/PixiEditor.ChangeableDocument/Enums/StructureMemberType.cs

@@ -1,8 +1,7 @@
-namespace PixiEditor.ChangeableDocument.Enums
+namespace PixiEditor.ChangeableDocument.Enums;
+
+public enum StructureMemberType
 {
-    public enum StructureMemberType
-    {
-        Layer,
-        Folder
-    }
+    Layer,
+    Folder
 }

+ 75 - 76
src/PixiEditor.ChangeableDocument/Rendering/ChunkRenderer.cs

@@ -4,100 +4,99 @@ using PixiEditor.ChangeableDocument.Changeables.Interfaces;
 using PixiEditor.ChangeableDocument.Enums;
 using SkiaSharp;
 
-namespace PixiEditor.ChangeableDocument.Rendering
+namespace PixiEditor.ChangeableDocument.Rendering;
+
+public static class ChunkRenderer
 {
-    public static class ChunkRenderer
+    private static SKPaint PaintToDrawChunksWith = new SKPaint() { BlendMode = SKBlendMode.SrcOver };
+    private static SKPaint ReplacingPaint = new SKPaint() { BlendMode = SKBlendMode.Src };
+    private static SKPaint ClippingPaint = new SKPaint() { BlendMode = SKBlendMode.DstIn };
+    public static Chunk RenderWholeStructure(Vector2i pos, ChunkResolution resolution, IReadOnlyFolder root)
     {
-        private static SKPaint PaintToDrawChunksWith = new SKPaint() { BlendMode = SKBlendMode.SrcOver };
-        private static SKPaint ReplacingPaint = new SKPaint() { BlendMode = SKBlendMode.Src };
-        private static SKPaint ClippingPaint = new SKPaint() { BlendMode = SKBlendMode.DstIn };
-        public static Chunk RenderWholeStructure(Vector2i pos, ChunkResolution resolution, IReadOnlyFolder root)
-        {
-            return RenderChunkRecursively(pos, resolution, 0, root, null);
-        }
+        return RenderChunkRecursively(pos, resolution, 0, root, null);
+    }
 
-        public static Chunk RenderSpecificLayers(Vector2i pos, ChunkResolution resolution, IReadOnlyFolder root, HashSet<Guid> layers)
-        {
-            return RenderChunkRecursively(pos, resolution, 0, root, layers);
-        }
+    public static Chunk RenderSpecificLayers(Vector2i pos, ChunkResolution resolution, IReadOnlyFolder root, HashSet<Guid> layers)
+    {
+        return RenderChunkRecursively(pos, resolution, 0, root, layers);
+    }
 
-        private static SKBlendMode GetSKBlendMode(BlendMode blendMode)
+    private static SKBlendMode GetSKBlendMode(BlendMode blendMode)
+    {
+        return blendMode switch
         {
-            return blendMode switch
-            {
-                BlendMode.Normal => SKBlendMode.SrcOver,
-                BlendMode.Darken => SKBlendMode.Darken,
-                BlendMode.Multiply => SKBlendMode.Multiply,
-                BlendMode.ColorBurn => SKBlendMode.ColorBurn,
-                BlendMode.Lighten => SKBlendMode.Lighten,
-                BlendMode.Screen => SKBlendMode.Screen,
-                BlendMode.ColorDodge => SKBlendMode.ColorDodge,
-                BlendMode.LinearDodge => SKBlendMode.Plus,
-                BlendMode.Overlay => SKBlendMode.Overlay,
-                BlendMode.SoftLight => SKBlendMode.SoftLight,
-                BlendMode.HardLight => SKBlendMode.HardLight,
-                BlendMode.Difference => SKBlendMode.Difference,
-                BlendMode.Exclusion => SKBlendMode.Exclusion,
-                BlendMode.Hue => SKBlendMode.Hue,
-                BlendMode.Saturation => SKBlendMode.Saturation,
-                BlendMode.Luminosity => SKBlendMode.Luminosity,
-                BlendMode.Color => SKBlendMode.Color,
-                _ => SKBlendMode.SrcOver,
-            };
-        }
+            BlendMode.Normal => SKBlendMode.SrcOver,
+            BlendMode.Darken => SKBlendMode.Darken,
+            BlendMode.Multiply => SKBlendMode.Multiply,
+            BlendMode.ColorBurn => SKBlendMode.ColorBurn,
+            BlendMode.Lighten => SKBlendMode.Lighten,
+            BlendMode.Screen => SKBlendMode.Screen,
+            BlendMode.ColorDodge => SKBlendMode.ColorDodge,
+            BlendMode.LinearDodge => SKBlendMode.Plus,
+            BlendMode.Overlay => SKBlendMode.Overlay,
+            BlendMode.SoftLight => SKBlendMode.SoftLight,
+            BlendMode.HardLight => SKBlendMode.HardLight,
+            BlendMode.Difference => SKBlendMode.Difference,
+            BlendMode.Exclusion => SKBlendMode.Exclusion,
+            BlendMode.Hue => SKBlendMode.Hue,
+            BlendMode.Saturation => SKBlendMode.Saturation,
+            BlendMode.Luminosity => SKBlendMode.Luminosity,
+            BlendMode.Color => SKBlendMode.Color,
+            _ => SKBlendMode.SrcOver,
+        };
+    }
 
-        private static Chunk RenderChunkRecursively(Vector2i chunkPos, ChunkResolution resolution, int depth, IReadOnlyFolder folder, HashSet<Guid>? visibleLayers)
+    private static Chunk RenderChunkRecursively(Vector2i chunkPos, ChunkResolution resolution, int depth, IReadOnlyFolder folder, HashSet<Guid>? visibleLayers)
+    {
+        Chunk targetChunk = Chunk.Create(resolution);
+        targetChunk.Surface.SkiaSurface.Canvas.Clear();
+        foreach (var child in folder.ReadOnlyChildren)
         {
-            Chunk targetChunk = Chunk.Create(resolution);
-            targetChunk.Surface.SkiaSurface.Canvas.Clear();
-            foreach (var child in folder.ReadOnlyChildren)
-            {
-                if (!child.IsVisible)
-                    continue;
+            if (!child.IsVisible)
+                continue;
 
-                // chunk fully masked out
-                if (child.ReadOnlyMask is not null && !child.ReadOnlyMask.LatestChunkExists(chunkPos, resolution))
-                    continue;
+            // chunk fully masked out
+            if (child.ReadOnlyMask is not null && !child.ReadOnlyMask.LatestChunkExists(chunkPos, resolution))
+                continue;
 
-                // layer
-                if (child is IReadOnlyLayer layer && (visibleLayers is null || visibleLayers.Contains(layer.GuidValue)))
+            // layer
+            if (child is IReadOnlyLayer layer && (visibleLayers is null || visibleLayers.Contains(layer.GuidValue)))
+            {
+                if (layer.ReadOnlyMask is null)
+                {
+                    PaintToDrawChunksWith.Color = new SKColor(255, 255, 255, (byte)Math.Round(child.Opacity * 255));
+                    PaintToDrawChunksWith.BlendMode = GetSKBlendMode(layer.BlendMode);
+                    layer.ReadOnlyLayerImage.DrawLatestChunkOn(chunkPos, resolution, targetChunk.Surface.SkiaSurface, new(0, 0), PaintToDrawChunksWith);
+                }
+                else
                 {
-                    if (layer.ReadOnlyMask is null)
+                    using (Chunk tempChunk = Chunk.Create(resolution))
                     {
+                        if (!layer.ReadOnlyLayerImage.DrawLatestChunkOn(chunkPos, resolution, tempChunk.Surface.SkiaSurface, new(0, 0), ReplacingPaint))
+                            continue;
+                        layer.ReadOnlyMask.DrawLatestChunkOn(chunkPos, resolution, tempChunk.Surface.SkiaSurface, new(0, 0), ClippingPaint);
+
                         PaintToDrawChunksWith.Color = new SKColor(255, 255, 255, (byte)Math.Round(child.Opacity * 255));
                         PaintToDrawChunksWith.BlendMode = GetSKBlendMode(layer.BlendMode);
-                        layer.ReadOnlyLayerImage.DrawLatestChunkOn(chunkPos, resolution, targetChunk.Surface.SkiaSurface, new(0, 0), PaintToDrawChunksWith);
+                        targetChunk.Surface.SkiaSurface.Canvas.DrawSurface(tempChunk.Surface.SkiaSurface, 0, 0, PaintToDrawChunksWith);
                     }
-                    else
-                    {
-                        using (Chunk tempChunk = Chunk.Create(resolution))
-                        {
-                            if (!layer.ReadOnlyLayerImage.DrawLatestChunkOn(chunkPos, resolution, tempChunk.Surface.SkiaSurface, new(0, 0), ReplacingPaint))
-                                continue;
-                            layer.ReadOnlyMask.DrawLatestChunkOn(chunkPos, resolution, tempChunk.Surface.SkiaSurface, new(0, 0), ClippingPaint);
-
-                            PaintToDrawChunksWith.Color = new SKColor(255, 255, 255, (byte)Math.Round(child.Opacity * 255));
-                            PaintToDrawChunksWith.BlendMode = GetSKBlendMode(layer.BlendMode);
-                            targetChunk.Surface.SkiaSurface.Canvas.DrawSurface(tempChunk.Surface.SkiaSurface, 0, 0, PaintToDrawChunksWith);
-                        }
-                    }
-                    continue;
                 }
+                continue;
+            }
 
-                // folder
-                if (child is IReadOnlyFolder innerFolder)
-                {
-                    using Chunk renderedChunk = RenderChunkRecursively(chunkPos, resolution, depth + 1, innerFolder, visibleLayers);
-                    if (innerFolder.ReadOnlyMask is not null)
-                        innerFolder.ReadOnlyMask.DrawLatestChunkOn(chunkPos, resolution, renderedChunk.Surface.SkiaSurface, new(0, 0), ClippingPaint);
+            // folder
+            if (child is IReadOnlyFolder innerFolder)
+            {
+                using Chunk renderedChunk = RenderChunkRecursively(chunkPos, resolution, depth + 1, innerFolder, visibleLayers);
+                if (innerFolder.ReadOnlyMask is not null)
+                    innerFolder.ReadOnlyMask.DrawLatestChunkOn(chunkPos, resolution, renderedChunk.Surface.SkiaSurface, new(0, 0), ClippingPaint);
 
-                    PaintToDrawChunksWith.Color = new SKColor(255, 255, 255, (byte)Math.Round(child.Opacity * 255));
-                    PaintToDrawChunksWith.BlendMode = GetSKBlendMode(innerFolder.BlendMode);
-                    renderedChunk.DrawOnSurface(targetChunk.Surface.SkiaSurface, new(0, 0), PaintToDrawChunksWith);
-                    continue;
-                }
+                PaintToDrawChunksWith.Color = new SKColor(255, 255, 255, (byte)Math.Round(child.Opacity * 255));
+                PaintToDrawChunksWith.BlendMode = GetSKBlendMode(innerFolder.BlendMode);
+                renderedChunk.DrawOnSurface(targetChunk.Surface.SkiaSurface, new(0, 0), PaintToDrawChunksWith);
+                continue;
             }
-            return targetChunk;
         }
+        return targetChunk;
     }
 }

+ 6 - 7
src/PixiEditor.Zoombox/IDragOperation.cs

@@ -1,13 +1,12 @@
 using System.Windows.Input;
 
-namespace PixiEditor.Zoombox
+namespace PixiEditor.Zoombox;
+
+internal interface IDragOperation
 {
-    internal interface IDragOperation
-    {
-        void Start(MouseButtonEventArgs e);
+    void Start(MouseButtonEventArgs e);
 
-        void Update(MouseEventArgs e);
+    void Update(MouseEventArgs e);
 
-        void Terminate();
-    }
+    void Terminate();
 }

+ 23 - 24
src/PixiEditor.Zoombox/MoveDragOperation.cs

@@ -1,33 +1,32 @@
 using ChunkyImageLib.DataHolders;
 using System.Windows.Input;
 
-namespace PixiEditor.Zoombox
+namespace PixiEditor.Zoombox;
+
+internal class MoveDragOperation : IDragOperation
 {
-    internal class MoveDragOperation : IDragOperation
-    {
-        private Zoombox parent;
-        private Vector2d prevMousePos;
+    private Zoombox parent;
+    private Vector2d prevMousePos;
 
-        public MoveDragOperation(Zoombox zoomBox)
-        {
-            parent = zoomBox;
-        }
-        public void Start(MouseButtonEventArgs e)
-        {
-            prevMousePos = Zoombox.ToVector2d(e.GetPosition(parent.mainCanvas));
-            parent.mainCanvas.CaptureMouse();
-        }
+    public MoveDragOperation(Zoombox zoomBox)
+    {
+        parent = zoomBox;
+    }
+    public void Start(MouseButtonEventArgs e)
+    {
+        prevMousePos = Zoombox.ToVector2d(e.GetPosition(parent.mainCanvas));
+        parent.mainCanvas.CaptureMouse();
+    }
 
-        public void Update(MouseEventArgs e)
-        {
-            var curMousePos = Zoombox.ToVector2d(e.GetPosition(parent.mainCanvas));
-            parent.Center += parent.ToZoomboxSpace(prevMousePos) - parent.ToZoomboxSpace(curMousePos);
-            prevMousePos = curMousePos;
-        }
+    public void Update(MouseEventArgs e)
+    {
+        var curMousePos = Zoombox.ToVector2d(e.GetPosition(parent.mainCanvas));
+        parent.Center += parent.ToZoomboxSpace(prevMousePos) - parent.ToZoomboxSpace(curMousePos);
+        prevMousePos = curMousePos;
+    }
 
-        public void Terminate()
-        {
-            parent.mainCanvas.ReleaseMouseCapture();
-        }
+    public void Terminate()
+    {
+        parent.mainCanvas.ReleaseMouseCapture();
     }
 }

+ 41 - 42
src/PixiEditor.Zoombox/RotateDragOperation.cs

@@ -2,49 +2,48 @@
 using System.Windows;
 using System.Windows.Input;
 
-namespace PixiEditor.Zoombox
+namespace PixiEditor.Zoombox;
+
+internal class RotateDragOperation : IDragOperation
 {
-    internal class RotateDragOperation : IDragOperation
+    private Zoombox parent;
+    private double prevAngle;
+
+
+    public RotateDragOperation(Zoombox zoomBox)
+    {
+        parent = zoomBox;
+    }
+    public void Start(MouseButtonEventArgs e)
+    {
+        Point pointCur = e.GetPosition(parent.mainCanvas);
+        prevAngle = GetAngle(new(pointCur.X, pointCur.Y));
+
+        parent.mainCanvas.CaptureMouse();
+    }
+
+    private double GetAngle(Vector2d point)
+    {
+        Vector2d center = new(parent.mainCanvas.ActualWidth / 2, parent.mainCanvas.ActualHeight / 2);
+        double angle = (point - center).Angle;
+        if (double.IsNaN(angle) || double.IsInfinity(angle))
+            return 0;
+        return angle;
+    }
+
+    public void Update(MouseEventArgs e)
+    {
+        Point pointCur = e.GetPosition(parent.mainCanvas);
+        double curAngle = GetAngle(new(pointCur.X, pointCur.Y));
+        double delta = curAngle - prevAngle;
+        if (parent.FlipX ^ parent.FlipY)
+            delta = -delta;
+        prevAngle = curAngle;
+        parent.Angle += delta;
+    }
+
+    public void Terminate()
     {
-        private Zoombox parent;
-        private double prevAngle;
-
-
-        public RotateDragOperation(Zoombox zoomBox)
-        {
-            parent = zoomBox;
-        }
-        public void Start(MouseButtonEventArgs e)
-        {
-            Point pointCur = e.GetPosition(parent.mainCanvas);
-            prevAngle = GetAngle(new(pointCur.X, pointCur.Y));
-
-            parent.mainCanvas.CaptureMouse();
-        }
-
-        private double GetAngle(Vector2d point)
-        {
-            Vector2d center = new(parent.mainCanvas.ActualWidth / 2, parent.mainCanvas.ActualHeight / 2);
-            double angle = (point - center).Angle;
-            if (double.IsNaN(angle) || double.IsInfinity(angle))
-                return 0;
-            return angle;
-        }
-
-        public void Update(MouseEventArgs e)
-        {
-            Point pointCur = e.GetPosition(parent.mainCanvas);
-            double curAngle = GetAngle(new(pointCur.X, pointCur.Y));
-            double delta = curAngle - prevAngle;
-            if (parent.FlipX ^ parent.FlipY)
-                delta = -delta;
-            prevAngle = curAngle;
-            parent.Angle += delta;
-        }
-
-        public void Terminate()
-        {
-            parent.mainCanvas.ReleaseMouseCapture();
-        }
+        parent.mainCanvas.ReleaseMouseCapture();
     }
 }

+ 13 - 14
src/PixiEditor.Zoombox/ViewportRoutedEventArgs.cs

@@ -1,21 +1,20 @@
 using ChunkyImageLib.DataHolders;
 using System.Windows;
 
-namespace PixiEditor.Zoombox
+namespace PixiEditor.Zoombox;
+
+public class ViewportRoutedEventArgs : RoutedEventArgs
 {
-    public class ViewportRoutedEventArgs : RoutedEventArgs
+    public ViewportRoutedEventArgs(RoutedEvent e, Vector2d center, Vector2d size, Vector2d realSize, double angle) : base(e)
     {
-        public ViewportRoutedEventArgs(RoutedEvent e, Vector2d center, Vector2d size, Vector2d realSize, double angle) : base(e)
-        {
-            Center = center;
-            Size = size;
-            RealSize = realSize;
-            Angle = angle;
-        }
-
-        public Vector2d Center { get; }
-        public Vector2d Size { get; }
-        public Vector2d RealSize { get; }
-        public double Angle { get; }
+        Center = center;
+        Size = size;
+        RealSize = realSize;
+        Angle = angle;
     }
+
+    public Vector2d Center { get; }
+    public Vector2d Size { get; }
+    public Vector2d RealSize { get; }
+    public double Angle { get; }
 }

+ 36 - 37
src/PixiEditor.Zoombox/ZoomDragOperation.cs

@@ -2,44 +2,43 @@
 using System;
 using System.Windows.Input;
 
-namespace PixiEditor.Zoombox
+namespace PixiEditor.Zoombox;
+
+internal class ZoomDragOperation : IDragOperation
 {
-    internal class ZoomDragOperation : IDragOperation
+    private Zoombox parent;
+
+    private double initScale;
+
+    private Vector2d scaleOrigin;
+    private Vector2d screenScaleOrigin;
+
+    public ZoomDragOperation(Zoombox zoomBox)
+    {
+        parent = zoomBox;
+    }
+    public void Start(MouseButtonEventArgs e)
+    {
+        screenScaleOrigin = parent.ToZoomboxSpace(Zoombox.ToVector2d(e.GetPosition(parent.mainCanvas)));
+        scaleOrigin = parent.ToZoomboxSpace(screenScaleOrigin);
+        initScale = parent.Scale;
+        parent.mainCanvas.CaptureMouse();
+    }
+
+    public void Update(MouseEventArgs e)
+    {
+        var curScreenPos = e.GetPosition(parent.mainCanvas);
+        double deltaX = screenScaleOrigin.X - curScreenPos.X;
+        double deltaPower = deltaX / 10.0;
+
+        parent.Scale *= Math.Pow(Zoombox.ScaleFactor, deltaPower);
+
+        var shiftedOrigin = parent.ToZoomboxSpace(screenScaleOrigin);
+        parent.Center += scaleOrigin - shiftedOrigin;
+    }
+
+    public void Terminate()
     {
-        private Zoombox parent;
-
-        private double initScale;
-
-        private Vector2d scaleOrigin;
-        private Vector2d screenScaleOrigin;
-
-        public ZoomDragOperation(Zoombox zoomBox)
-        {
-            parent = zoomBox;
-        }
-        public void Start(MouseButtonEventArgs e)
-        {
-            screenScaleOrigin = parent.ToZoomboxSpace(Zoombox.ToVector2d(e.GetPosition(parent.mainCanvas)));
-            scaleOrigin = parent.ToZoomboxSpace(screenScaleOrigin);
-            initScale = parent.Scale;
-            parent.mainCanvas.CaptureMouse();
-        }
-
-        public void Update(MouseEventArgs e)
-        {
-            var curScreenPos = e.GetPosition(parent.mainCanvas);
-            double deltaX = screenScaleOrigin.X - curScreenPos.X;
-            double deltaPower = deltaX / 10.0;
-
-            parent.Scale *= Math.Pow(Zoombox.ScaleFactor, deltaPower);
-
-            var shiftedOrigin = parent.ToZoomboxSpace(screenScaleOrigin);
-            parent.Center += scaleOrigin - shiftedOrigin;
-        }
-
-        public void Terminate()
-        {
-            parent.mainCanvas.ReleaseMouseCapture();
-        }
+        parent.mainCanvas.ReleaseMouseCapture();
     }
 }

+ 302 - 303
src/PixiEditor.Zoombox/Zoombox.xaml.cs

@@ -6,377 +6,376 @@ using System.Windows.Input;
 using System.Windows.Markup;
 using ChunkyImageLib.DataHolders;
 
-namespace PixiEditor.Zoombox
+namespace PixiEditor.Zoombox;
+
+[ContentProperty(nameof(AdditionalContent))]
+public partial class Zoombox : ContentControl, INotifyPropertyChanged
 {
-    [ContentProperty(nameof(AdditionalContent))]
-    public partial class Zoombox : ContentControl, INotifyPropertyChanged
-    {
-        public static readonly DependencyProperty AdditionalContentProperty =
-            DependencyProperty.Register(nameof(AdditionalContent), typeof(object), typeof(Zoombox),
-              new PropertyMetadata(null));
+    public static readonly DependencyProperty AdditionalContentProperty =
+        DependencyProperty.Register(nameof(AdditionalContent), typeof(object), typeof(Zoombox),
+          new PropertyMetadata(null));
 
-        public static readonly DependencyProperty ZoomModeProperty =
-            DependencyProperty.Register(nameof(ZoomMode), typeof(ZoomboxMode), typeof(Zoombox),
-              new PropertyMetadata(ZoomboxMode.Normal, ZoomModeChanged));
+    public static readonly DependencyProperty ZoomModeProperty =
+        DependencyProperty.Register(nameof(ZoomMode), typeof(ZoomboxMode), typeof(Zoombox),
+          new PropertyMetadata(ZoomboxMode.Normal, ZoomModeChanged));
 
-        public static readonly DependencyProperty ZoomOutOnClickProperty =
-            DependencyProperty.Register(nameof(ZoomOutOnClick), typeof(bool), typeof(Zoombox),
-              new PropertyMetadata(false));
+    public static readonly DependencyProperty ZoomOutOnClickProperty =
+        DependencyProperty.Register(nameof(ZoomOutOnClick), typeof(bool), typeof(Zoombox),
+          new PropertyMetadata(false));
 
-        public static readonly DependencyProperty UseTouchGesturesProperty =
-            DependencyProperty.Register(nameof(UseTouchGestures), typeof(bool), typeof(Zoombox));
+    public static readonly DependencyProperty UseTouchGesturesProperty =
+        DependencyProperty.Register(nameof(UseTouchGestures), typeof(bool), typeof(Zoombox));
 
 
-        public static readonly DependencyProperty ScaleProperty =
-            DependencyProperty.Register(nameof(Scale), typeof(double), typeof(Zoombox), new(1.0, OnPropertyChange));
+    public static readonly DependencyProperty ScaleProperty =
+        DependencyProperty.Register(nameof(Scale), typeof(double), typeof(Zoombox), new(1.0, OnPropertyChange));
 
-        public static readonly DependencyProperty CenterProperty =
-            DependencyProperty.Register(nameof(Center), typeof(Vector2d), typeof(Zoombox), new(new Vector2d(0, 0), OnPropertyChange));
+    public static readonly DependencyProperty CenterProperty =
+        DependencyProperty.Register(nameof(Center), typeof(Vector2d), typeof(Zoombox), new(new Vector2d(0, 0), OnPropertyChange));
 
-        public static readonly DependencyProperty DimensionsProperty =
-            DependencyProperty.Register(nameof(Dimensions), typeof(Vector2d), typeof(Zoombox));
+    public static readonly DependencyProperty DimensionsProperty =
+        DependencyProperty.Register(nameof(Dimensions), typeof(Vector2d), typeof(Zoombox));
 
-        public static readonly DependencyProperty RealDimensionsProperty =
-            DependencyProperty.Register(nameof(RealDimensions), typeof(Vector2d), typeof(Zoombox));
+    public static readonly DependencyProperty RealDimensionsProperty =
+        DependencyProperty.Register(nameof(RealDimensions), typeof(Vector2d), typeof(Zoombox));
 
-        public static readonly DependencyProperty AngleProperty =
-            DependencyProperty.Register(nameof(Angle), typeof(double), typeof(Zoombox), new(0.0, OnPropertyChange));
+    public static readonly DependencyProperty AngleProperty =
+        DependencyProperty.Register(nameof(Angle), typeof(double), typeof(Zoombox), new(0.0, OnPropertyChange));
 
-        public static readonly DependencyProperty FlipXProperty =
-            DependencyProperty.Register(nameof(FlipX), typeof(bool), typeof(Zoombox), new(false, OnPropertyChange));
+    public static readonly DependencyProperty FlipXProperty =
+        DependencyProperty.Register(nameof(FlipX), typeof(bool), typeof(Zoombox), new(false, OnPropertyChange));
 
-        public static readonly DependencyProperty FlipYProperty =
-            DependencyProperty.Register(nameof(FlipY), typeof(bool), typeof(Zoombox), new(false, OnPropertyChange));
+    public static readonly DependencyProperty FlipYProperty =
+        DependencyProperty.Register(nameof(FlipY), typeof(bool), typeof(Zoombox), new(false, OnPropertyChange));
 
-        public static readonly RoutedEvent ViewportMovedEvent = EventManager.RegisterRoutedEvent(
-            nameof(ViewportMoved), RoutingStrategy.Bubble, typeof(EventHandler<ViewportRoutedEventArgs>), typeof(Zoombox));
+    public static readonly RoutedEvent ViewportMovedEvent = EventManager.RegisterRoutedEvent(
+        nameof(ViewportMoved), RoutingStrategy.Bubble, typeof(EventHandler<ViewportRoutedEventArgs>), typeof(Zoombox));
 
-        public object? AdditionalContent
-        {
-            get => GetValue(AdditionalContentProperty);
-            set => SetValue(AdditionalContentProperty, value);
-        }
-        public ZoomboxMode ZoomMode
-        {
-            get => (ZoomboxMode)GetValue(ZoomModeProperty);
-            set => SetValue(ZoomModeProperty, value);
-        }
+    public object? AdditionalContent
+    {
+        get => GetValue(AdditionalContentProperty);
+        set => SetValue(AdditionalContentProperty, value);
+    }
+    public ZoomboxMode ZoomMode
+    {
+        get => (ZoomboxMode)GetValue(ZoomModeProperty);
+        set => SetValue(ZoomModeProperty, value);
+    }
 
-        public bool ZoomOutOnClick
-        {
-            get => (bool)GetValue(ZoomOutOnClickProperty);
-            set => SetValue(ZoomOutOnClickProperty, value);
-        }
+    public bool ZoomOutOnClick
+    {
+        get => (bool)GetValue(ZoomOutOnClickProperty);
+        set => SetValue(ZoomOutOnClickProperty, value);
+    }
 
-        public bool UseTouchGestures
-        {
-            get => (bool)GetValue(UseTouchGesturesProperty);
-            set => SetValue(UseTouchGesturesProperty, value);
-        }
+    public bool UseTouchGestures
+    {
+        get => (bool)GetValue(UseTouchGesturesProperty);
+        set => SetValue(UseTouchGesturesProperty, value);
+    }
 
-        public bool FlipX
-        {
-            get => (bool)GetValue(FlipXProperty);
-            set => SetValue(FlipXProperty, value);
-        }
+    public bool FlipX
+    {
+        get => (bool)GetValue(FlipXProperty);
+        set => SetValue(FlipXProperty, value);
+    }
 
-        public bool FlipY
-        {
-            get => (bool)GetValue(FlipYProperty);
-            set => SetValue(FlipYProperty, value);
-        }
+    public bool FlipY
+    {
+        get => (bool)GetValue(FlipYProperty);
+        set => SetValue(FlipYProperty, value);
+    }
 
-        public double Scale
-        {
-            get => (double)GetValue(ScaleProperty);
-            set => SetValue(ScaleProperty, value);
-        }
+    public double Scale
+    {
+        get => (double)GetValue(ScaleProperty);
+        set => SetValue(ScaleProperty, value);
+    }
 
-        public double Angle
-        {
-            get => (double)GetValue(AngleProperty);
-            set => SetValue(AngleProperty, value);
-        }
+    public double Angle
+    {
+        get => (double)GetValue(AngleProperty);
+        set => SetValue(AngleProperty, value);
+    }
 
-        public Vector2d Center
-        {
-            get => (Vector2d)GetValue(CenterProperty);
-            set => SetValue(CenterProperty, value);
-        }
+    public Vector2d Center
+    {
+        get => (Vector2d)GetValue(CenterProperty);
+        set => SetValue(CenterProperty, value);
+    }
 
-        public Vector2d Dimensions
-        {
-            get => (Vector2d)GetValue(DimensionsProperty);
-            set => SetValue(DimensionsProperty, value);
-        }
+    public Vector2d Dimensions
+    {
+        get => (Vector2d)GetValue(DimensionsProperty);
+        set => SetValue(DimensionsProperty, value);
+    }
 
-        public Vector2d RealDimensions
-        {
-            get => (Vector2d)GetValue(RealDimensionsProperty);
-            set => SetValue(RealDimensionsProperty, value);
-        }
+    public Vector2d RealDimensions
+    {
+        get => (Vector2d)GetValue(RealDimensionsProperty);
+        set => SetValue(RealDimensionsProperty, value);
+    }
 
-        public event EventHandler<ViewportRoutedEventArgs> ViewportMoved
-        {
-            add => AddHandler(ViewportMovedEvent, value);
-            remove => RemoveHandler(ViewportMovedEvent, value);
-        }
+    public event EventHandler<ViewportRoutedEventArgs> ViewportMoved
+    {
+        add => AddHandler(ViewportMovedEvent, value);
+        remove => RemoveHandler(ViewportMovedEvent, value);
+    }
 
-        public double CanvasX => ToScreenSpace(new(0, 0)).X;
-        public double CanvasY => ToScreenSpace(new(0, 0)).Y;
+    public double CanvasX => ToScreenSpace(new(0, 0)).X;
+    public double CanvasY => ToScreenSpace(new(0, 0)).Y;
 
-        public double ScaleTransformXY => Scale;
-        public double FlipTransformX => FlipX ? -1 : 1;
-        public double FlipTransformY => FlipY ? -1 : 1;
-        public double RotateTransformAngle => Angle * 180 / Math.PI;
-        internal const double MaxScale = 70;
-        internal double MinScale
+    public double ScaleTransformXY => Scale;
+    public double FlipTransformX => FlipX ? -1 : 1;
+    public double FlipTransformY => FlipY ? -1 : 1;
+    public double RotateTransformAngle => Angle * 180 / Math.PI;
+    internal const double MaxScale = 70;
+    internal double MinScale
+    {
+        get
         {
-            get
-            {
-                double fraction = Math.Max(
-                    mainCanvas.ActualWidth / mainGrid.ActualWidth,
-                    mainCanvas.ActualHeight / mainGrid.ActualHeight);
-                return Math.Min(fraction / 8, 0.1);
-            }
+            double fraction = Math.Max(
+                mainCanvas.ActualWidth / mainGrid.ActualWidth,
+                mainCanvas.ActualHeight / mainGrid.ActualHeight);
+            return Math.Min(fraction / 8, 0.1);
         }
+    }
 
-        internal const double ScaleFactor = 1.09050773267; //2^(1/8)
+    internal const double ScaleFactor = 1.09050773267; //2^(1/8)
 
-        private double[] roundZoomValues = new double[] { .01, .02, .03, .04, .05, .06, .07, .08, .1, .13, .17, .2, .25, .33, .5, .67, 1, 1.5, 2, 3, 4, 5, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64 };
+    private double[] roundZoomValues = new double[] { .01, .02, .03, .04, .05, .06, .07, .08, .1, .13, .17, .2, .25, .33, .5, .67, 1, 1.5, 2, 3, 4, 5, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 64 };
 
-        internal Vector2d ToScreenSpace(Vector2d p)
-        {
-            Vector2d delta = p - Center;
-            delta = delta.Rotate(Angle) * Scale;
-            if (FlipX)
-                delta.X = -delta.X;
-            if (FlipY)
-                delta.Y = -delta.Y;
-            delta += new Vector2d(mainCanvas.ActualWidth / 2, mainCanvas.ActualHeight / 2);
-            return delta;
-        }
+    internal Vector2d ToScreenSpace(Vector2d p)
+    {
+        Vector2d delta = p - Center;
+        delta = delta.Rotate(Angle) * Scale;
+        if (FlipX)
+            delta.X = -delta.X;
+        if (FlipY)
+            delta.Y = -delta.Y;
+        delta += new Vector2d(mainCanvas.ActualWidth / 2, mainCanvas.ActualHeight / 2);
+        return delta;
+    }
 
-        internal Vector2d ToZoomboxSpace(Vector2d mousePos)
-        {
-            Vector2d delta = mousePos - new Vector2d(mainCanvas.ActualWidth / 2, mainCanvas.ActualHeight / 2);
-            if (FlipX)
-                delta.X = -delta.X;
-            if (FlipY)
-                delta.Y = -delta.Y;
-            delta = (delta / Scale).Rotate(-Angle);
-            return delta + Center;
-        }
+    internal Vector2d ToZoomboxSpace(Vector2d mousePos)
+    {
+        Vector2d delta = mousePos - new Vector2d(mainCanvas.ActualWidth / 2, mainCanvas.ActualHeight / 2);
+        if (FlipX)
+            delta.X = -delta.X;
+        if (FlipY)
+            delta.Y = -delta.Y;
+        delta = (delta / Scale).Rotate(-Angle);
+        return delta + Center;
+    }
 
-        private IDragOperation? activeDragOperation = null;
-        private MouseButtonEventArgs? activeMouseDownEventArgs = null;
-        private Point activeMouseDownPos;
+    private IDragOperation? activeDragOperation = null;
+    private MouseButtonEventArgs? activeMouseDownEventArgs = null;
+    private Point activeMouseDownPos;
 
-        public event PropertyChangedEventHandler? PropertyChanged;
+    public event PropertyChangedEventHandler? PropertyChanged;
 
-        private static void ZoomModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
-        {
-            Zoombox sender = (Zoombox)d;
-            sender.activeDragOperation?.Terminate();
-            sender.activeDragOperation = null;
-            sender.activeMouseDownEventArgs = null;
-        }
+    private static void ZoomModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
+    {
+        Zoombox sender = (Zoombox)d;
+        sender.activeDragOperation?.Terminate();
+        sender.activeDragOperation = null;
+        sender.activeMouseDownEventArgs = null;
+    }
 
-        public Zoombox()
-        {
-            InitializeComponent();
-            Loaded += (_, _) => OnPropertyChange(this, new DependencyPropertyChangedEventArgs());
-        }
+    public Zoombox()
+    {
+        InitializeComponent();
+        Loaded += (_, _) => OnPropertyChange(this, new DependencyPropertyChangedEventArgs());
+    }
 
-        private void RaiseViewportEvent()
-        {
-            var realDim = new Vector2d(mainCanvas.ActualWidth, mainCanvas.ActualHeight);
-            RealDimensions = realDim;
-            RaiseEvent(new ViewportRoutedEventArgs(
-                ViewportMovedEvent,
-                Center,
-                Dimensions,
-                realDim,
-                Angle));
-        }
+    private void RaiseViewportEvent()
+    {
+        var realDim = new Vector2d(mainCanvas.ActualWidth, mainCanvas.ActualHeight);
+        RealDimensions = realDim;
+        RaiseEvent(new ViewportRoutedEventArgs(
+            ViewportMovedEvent,
+            Center,
+            Dimensions,
+            realDim,
+            Angle));
+    }
 
-        public void CenterContent() => CenterContent(new(mainGrid.ActualWidth, mainGrid.ActualHeight));
+    public void CenterContent() => CenterContent(new(mainGrid.ActualWidth, mainGrid.ActualHeight));
 
-        public void CenterContent(Vector2d newSize)
-        {
+    public void CenterContent(Vector2d newSize)
+    {
 
-            const double marginFactor = 1.1;
-            double scaleFactor = Math.Max(
-                newSize.X * marginFactor / mainCanvas.ActualWidth,
-                newSize.Y * marginFactor / mainCanvas.ActualHeight);
+        const double marginFactor = 1.1;
+        double scaleFactor = Math.Max(
+            newSize.X * marginFactor / mainCanvas.ActualWidth,
+            newSize.Y * marginFactor / mainCanvas.ActualHeight);
 
-            Angle = 0;
-            FlipX = false;
-            FlipY = false;
-            Scale = scaleFactor;
-            Center = newSize / 2;
-        }
+        Angle = 0;
+        FlipX = false;
+        FlipY = false;
+        Scale = scaleFactor;
+        Center = newSize / 2;
+    }
 
-        public void ZoomIntoCenter(double delta)
-        {
-            ZoomInto(new Vector2d(mainCanvas.ActualWidth / 2, mainCanvas.ActualHeight / 2), delta);
-        }
+    public void ZoomIntoCenter(double delta)
+    {
+        ZoomInto(new Vector2d(mainCanvas.ActualWidth / 2, mainCanvas.ActualHeight / 2), delta);
+    }
 
-        public void ZoomInto(Vector2d mousePos, double delta)
-        {
-            if (delta == 0)
-                return;
-            var oldZoomboxMousePos = ToZoomboxSpace(mousePos);
-
-            int curIndex = GetClosestRoundZoomValueIndex(Scale);
-            int nextIndex = curIndex;
-            if (!(curIndex == 0 && delta < 0 || curIndex == roundZoomValues.Length - 1 && delta > 0))
-                nextIndex = delta < 0 ? curIndex - 1 : curIndex + 1;
-            double newScale = roundZoomValues[nextIndex];
-
-            if (Math.Abs(newScale - 1) < 0.1) newScale = 1;
-            newScale = Math.Clamp(newScale, MinScale, MaxScale);
-            Scale = newScale;
-
-            var newZoomboxMousePos = ToZoomboxSpace(mousePos);
-            var deltaCenter = oldZoomboxMousePos - newZoomboxMousePos;
-            Center += deltaCenter;
-        }
+    public void ZoomInto(Vector2d mousePos, double delta)
+    {
+        if (delta == 0)
+            return;
+        var oldZoomboxMousePos = ToZoomboxSpace(mousePos);
+
+        int curIndex = GetClosestRoundZoomValueIndex(Scale);
+        int nextIndex = curIndex;
+        if (!(curIndex == 0 && delta < 0 || curIndex == roundZoomValues.Length - 1 && delta > 0))
+            nextIndex = delta < 0 ? curIndex - 1 : curIndex + 1;
+        double newScale = roundZoomValues[nextIndex];
+
+        if (Math.Abs(newScale - 1) < 0.1) newScale = 1;
+        newScale = Math.Clamp(newScale, MinScale, MaxScale);
+        Scale = newScale;
+
+        var newZoomboxMousePos = ToZoomboxSpace(mousePos);
+        var deltaCenter = oldZoomboxMousePos - newZoomboxMousePos;
+        Center += deltaCenter;
+    }
 
-        private int GetClosestRoundZoomValueIndex(double value)
+    private int GetClosestRoundZoomValueIndex(double value)
+    {
+        int index = -1;
+        double delta = double.MaxValue;
+        for (int i = 0; i < roundZoomValues.Length; i++)
         {
-            int index = -1;
-            double delta = double.MaxValue;
-            for (int i = 0; i < roundZoomValues.Length; i++)
+            double curDelta = Math.Abs(roundZoomValues[i] - value);
+            if (curDelta < delta)
             {
-                double curDelta = Math.Abs(roundZoomValues[i] - value);
-                if (curDelta < delta)
-                {
-                    delta = curDelta;
-                    index = i;
-                }
+                delta = curDelta;
+                index = i;
             }
-            return index;
         }
+        return index;
+    }
 
-        private void OnMouseDown(object sender, MouseButtonEventArgs e)
-        {
-            if (e.ChangedButton == MouseButton.Right)
-                return;
-            activeMouseDownEventArgs = e;
-            activeMouseDownPos = e.GetPosition(mainCanvas);
-            Keyboard.Focus(this);
-        }
+    private void OnMouseDown(object sender, MouseButtonEventArgs e)
+    {
+        if (e.ChangedButton == MouseButton.Right)
+            return;
+        activeMouseDownEventArgs = e;
+        activeMouseDownPos = e.GetPosition(mainCanvas);
+        Keyboard.Focus(this);
+    }
 
-        private void InitiateDrag(MouseButtonEventArgs e)
-        {
-            if (ZoomMode == ZoomboxMode.Normal)
-                return;
+    private void InitiateDrag(MouseButtonEventArgs e)
+    {
+        if (ZoomMode == ZoomboxMode.Normal)
+            return;
 
-            activeDragOperation?.Terminate();
+        activeDragOperation?.Terminate();
 
-            if (ZoomMode == ZoomboxMode.Move)
-                activeDragOperation = new MoveDragOperation(this);
-            else if (ZoomMode == ZoomboxMode.Zoom)
-                activeDragOperation = new ZoomDragOperation(this);
-            else if (ZoomMode == ZoomboxMode.Rotate)
-                activeDragOperation = new RotateDragOperation(this);
-            else
-                throw new InvalidOperationException("Unknown zoombox mode");
+        if (ZoomMode == ZoomboxMode.Move)
+            activeDragOperation = new MoveDragOperation(this);
+        else if (ZoomMode == ZoomboxMode.Zoom)
+            activeDragOperation = new ZoomDragOperation(this);
+        else if (ZoomMode == ZoomboxMode.Rotate)
+            activeDragOperation = new RotateDragOperation(this);
+        else
+            throw new InvalidOperationException("Unknown zoombox mode");
 
-            activeDragOperation.Start(e);
-        }
+        activeDragOperation.Start(e);
+    }
 
-        private void OnMouseUp(object sender, MouseButtonEventArgs e)
+    private void OnMouseUp(object sender, MouseButtonEventArgs e)
+    {
+        if (e.ChangedButton == MouseButton.Right)
+            return;
+        if (activeDragOperation is not null)
         {
-            if (e.ChangedButton == MouseButton.Right)
-                return;
-            if (activeDragOperation is not null)
-            {
-                activeDragOperation.Terminate();
-                activeDragOperation = null;
-            }
-            else
-            {
-                if (ZoomMode == ZoomboxMode.Zoom && e.ChangedButton == MouseButton.Left)
-                    ZoomInto(ToVector2d(e.GetPosition(mainCanvas)), ZoomOutOnClick ? -1 : 1);
-            }
-            activeMouseDownEventArgs = null;
+            activeDragOperation.Terminate();
+            activeDragOperation = null;
         }
-
-        private void OnMouseMove(object sender, MouseEventArgs e)
+        else
         {
-            if (activeDragOperation is null && activeMouseDownEventArgs is not null)
-            {
-                var cur = e.GetPosition(mainCanvas);
-
-                if (Math.Abs(cur.X - activeMouseDownPos.X) > 3)
-                    InitiateDrag(activeMouseDownEventArgs);
-            }
-            activeDragOperation?.Update(e);
+            if (ZoomMode == ZoomboxMode.Zoom && e.ChangedButton == MouseButton.Left)
+                ZoomInto(ToVector2d(e.GetPosition(mainCanvas)), ZoomOutOnClick ? -1 : 1);
         }
+        activeMouseDownEventArgs = null;
+    }
 
-        private void OnScroll(object sender, MouseWheelEventArgs e)
+    private void OnMouseMove(object sender, MouseEventArgs e)
+    {
+        if (activeDragOperation is null && activeMouseDownEventArgs is not null)
         {
-            for (int i = 0; i < Math.Abs(e.Delta / 100); i++)
-            {
-                ZoomInto(ToVector2d(e.GetPosition(mainCanvas)), e.Delta / 100);
-            }
-        }
+            var cur = e.GetPosition(mainCanvas);
 
-        private void OnManipulationDelta(object sender, ManipulationDeltaEventArgs e)
-        {
-            if (!UseTouchGestures)
-                return;
-            e.Handled = true;
-            Vector2d screenTranslation = new(e.DeltaManipulation.Translation.X, e.DeltaManipulation.Translation.Y);
-            Vector2d screenOrigin = new(e.ManipulationOrigin.X, e.ManipulationOrigin.Y);
-            Manipulate(e.DeltaManipulation.Scale.X, screenTranslation, screenOrigin, e.DeltaManipulation.Rotation / 180 * Math.PI);
+            if (Math.Abs(cur.X - activeMouseDownPos.X) > 3)
+                InitiateDrag(activeMouseDownEventArgs);
         }
+        activeDragOperation?.Update(e);
+    }
 
-        private void Manipulate(double deltaScale, Vector2d screenTranslation, Vector2d screenOrigin, double rotation)
+    private void OnScroll(object sender, MouseWheelEventArgs e)
+    {
+        for (int i = 0; i < Math.Abs(e.Delta / 100); i++)
         {
-            double newScale = Math.Clamp(Scale * deltaScale, MinScale, MaxScale);
-            double newAngle = Angle + rotation;
-
-            Vector2d originalPos = ToZoomboxSpace(screenOrigin);
-            Angle = newAngle;
-            Scale = newScale;
-            Vector2d newPos = ToZoomboxSpace(screenOrigin);
-            Vector2d centerTranslation = originalPos - newPos;
-            Center += centerTranslation;
-
-            Vector2d translatedZoomboxPos = ToZoomboxSpace(screenOrigin + screenTranslation);
-            Center -= translatedZoomboxPos - originalPos;
+            ZoomInto(ToVector2d(e.GetPosition(mainCanvas)), e.Delta / 100);
         }
+    }
+
+    private void OnManipulationDelta(object sender, ManipulationDeltaEventArgs e)
+    {
+        if (!UseTouchGestures)
+            return;
+        e.Handled = true;
+        Vector2d screenTranslation = new(e.DeltaManipulation.Translation.X, e.DeltaManipulation.Translation.Y);
+        Vector2d screenOrigin = new(e.ManipulationOrigin.X, e.ManipulationOrigin.Y);
+        Manipulate(e.DeltaManipulation.Scale.X, screenTranslation, screenOrigin, e.DeltaManipulation.Rotation / 180 * Math.PI);
+    }
 
-        internal static Vector2d ToVector2d(Point point) => new Vector2d(point.X, point.Y);
+    private void Manipulate(double deltaScale, Vector2d screenTranslation, Vector2d screenOrigin, double rotation)
+    {
+        double newScale = Math.Clamp(Scale * deltaScale, MinScale, MaxScale);
+        double newAngle = Angle + rotation;
+
+        Vector2d originalPos = ToZoomboxSpace(screenOrigin);
+        Angle = newAngle;
+        Scale = newScale;
+        Vector2d newPos = ToZoomboxSpace(screenOrigin);
+        Vector2d centerTranslation = originalPos - newPos;
+        Center += centerTranslation;
+
+        Vector2d translatedZoomboxPos = ToZoomboxSpace(screenOrigin + screenTranslation);
+        Center -= translatedZoomboxPos - originalPos;
+    }
 
-        private static void OnPropertyChange(DependencyObject obj, DependencyPropertyChangedEventArgs args)
-        {
-            var zoombox = (Zoombox)obj;
-
-            Vector2d topLeft = zoombox.ToZoomboxSpace(new(0, 0)).Rotate(zoombox.Angle);
-            Vector2d bottomRight = zoombox.ToZoomboxSpace(new(zoombox.mainCanvas.ActualWidth, zoombox.mainCanvas.ActualHeight)).Rotate(zoombox.Angle);
-
-            zoombox.Dimensions = (bottomRight - topLeft).Abs();
-            zoombox.PropertyChanged?.Invoke(zoombox, new(nameof(zoombox.ScaleTransformXY)));
-            zoombox.PropertyChanged?.Invoke(zoombox, new(nameof(zoombox.RotateTransformAngle)));
-            zoombox.PropertyChanged?.Invoke(zoombox, new(nameof(zoombox.FlipTransformX)));
-            zoombox.PropertyChanged?.Invoke(zoombox, new(nameof(zoombox.FlipTransformY)));
-            zoombox.PropertyChanged?.Invoke(zoombox, new(nameof(zoombox.CanvasX)));
-            zoombox.PropertyChanged?.Invoke(zoombox, new(nameof(zoombox.CanvasY)));
-            zoombox.RaiseViewportEvent();
-        }
+    internal static Vector2d ToVector2d(Point point) => new Vector2d(point.X, point.Y);
 
-        private void OnMainCanvasSizeChanged(object sender, SizeChangedEventArgs e)
-        {
-            RaiseViewportEvent();
-        }
+    private static void OnPropertyChange(DependencyObject obj, DependencyPropertyChangedEventArgs args)
+    {
+        var zoombox = (Zoombox)obj;
+
+        Vector2d topLeft = zoombox.ToZoomboxSpace(new(0, 0)).Rotate(zoombox.Angle);
+        Vector2d bottomRight = zoombox.ToZoomboxSpace(new(zoombox.mainCanvas.ActualWidth, zoombox.mainCanvas.ActualHeight)).Rotate(zoombox.Angle);
+
+        zoombox.Dimensions = (bottomRight - topLeft).Abs();
+        zoombox.PropertyChanged?.Invoke(zoombox, new(nameof(zoombox.ScaleTransformXY)));
+        zoombox.PropertyChanged?.Invoke(zoombox, new(nameof(zoombox.RotateTransformAngle)));
+        zoombox.PropertyChanged?.Invoke(zoombox, new(nameof(zoombox.FlipTransformX)));
+        zoombox.PropertyChanged?.Invoke(zoombox, new(nameof(zoombox.FlipTransformY)));
+        zoombox.PropertyChanged?.Invoke(zoombox, new(nameof(zoombox.CanvasX)));
+        zoombox.PropertyChanged?.Invoke(zoombox, new(nameof(zoombox.CanvasY)));
+        zoombox.RaiseViewportEvent();
+    }
 
-        private void OnGridSizeChanged(object sender, SizeChangedEventArgs args)
-        {
-            RaiseViewportEvent();
-        }
+    private void OnMainCanvasSizeChanged(object sender, SizeChangedEventArgs e)
+    {
+        RaiseViewportEvent();
+    }
+
+    private void OnGridSizeChanged(object sender, SizeChangedEventArgs args)
+    {
+        RaiseViewportEvent();
     }
 }

+ 7 - 8
src/PixiEditor.Zoombox/ZoomboxMode.cs

@@ -1,10 +1,9 @@
-namespace PixiEditor.Zoombox
+namespace PixiEditor.Zoombox;
+
+public enum ZoomboxMode
 {
-    public enum ZoomboxMode
-    {
-        Normal,
-        Move,
-        Rotate,
-        Zoom
-    }
+    Normal,
+    Move,
+    Rotate,
+    Zoom
 }

+ 6 - 7
src/PixiEditorPrototype/App.xaml.cs

@@ -6,12 +6,11 @@ using System.Linq;
 using System.Threading.Tasks;
 using System.Windows;
 
-namespace PixiEditorPrototype
+namespace PixiEditorPrototype;
+
+/// <summary>
+/// Interaction logic for App.xaml
+/// </summary>
+public partial class App : Application
 {
-    /// <summary>
-    /// Interaction logic for App.xaml
-    /// </summary>
-    public partial class App : Application
-    {
-    }
 }

+ 83 - 84
src/PixiEditorPrototype/Behaviors/SliderUpdateBehavior.cs

@@ -4,105 +4,104 @@ using System.Windows.Controls;
 using System.Windows.Controls.Primitives;
 using System.Windows.Input;
 
-namespace PixiEditorPrototype.Behaviors
+namespace PixiEditorPrototype.Behaviors;
+
+internal class SliderUpdateBehavior : Behavior<Slider>
 {
-    internal class SliderUpdateBehavior : Behavior<Slider>
+    public static DependencyProperty DragValueChangedProperty = DependencyProperty.Register(nameof(DragValueChanged), typeof(ICommand), typeof(SliderUpdateBehavior));
+    public ICommand? DragValueChanged
     {
-        public static DependencyProperty DragValueChangedProperty = DependencyProperty.Register(nameof(DragValueChanged), typeof(ICommand), typeof(SliderUpdateBehavior));
-        public ICommand? DragValueChanged
-        {
-            get => (ICommand)GetValue(DragValueChangedProperty);
-            set => SetValue(DragValueChangedProperty, value);
-        }
-        public static DependencyProperty DragEndedProperty = DependencyProperty.Register(nameof(DragEnded), typeof(ICommand), typeof(SliderUpdateBehavior));
-        public ICommand? DragEnded
-        {
-            get => (ICommand)GetValue(DragEndedProperty);
-            set => SetValue(DragEndedProperty, value);
-        }
-
-        public static DependencyProperty ValueFromSliderProperty =
-            DependencyProperty.Register(nameof(ValueFromSlider), typeof(double), typeof(SliderUpdateBehavior), new(OnSliderValuePropertyChange));
-        public double ValueFromSlider
-        {
-            get => (double)GetValue(ValueFromSliderProperty);
-            set => SetValue(ValueFromSliderProperty, value);
-        }
+        get => (ICommand)GetValue(DragValueChangedProperty);
+        set => SetValue(DragValueChangedProperty, value);
+    }
+    public static DependencyProperty DragEndedProperty = DependencyProperty.Register(nameof(DragEnded), typeof(ICommand), typeof(SliderUpdateBehavior));
+    public ICommand? DragEnded
+    {
+        get => (ICommand)GetValue(DragEndedProperty);
+        set => SetValue(DragEndedProperty, value);
+    }
 
-        private bool attached = false;
-        private bool dragging = false;
-        private bool valueChangedWhileDragging = false;
-        protected override void OnAttached()
-        {
-            AssociatedObject.Loaded += AssociatedObject_Loaded;
-            AssociatedObject.Focusable = false;
+    public static DependencyProperty ValueFromSliderProperty =
+        DependencyProperty.Register(nameof(ValueFromSlider), typeof(double), typeof(SliderUpdateBehavior), new(OnSliderValuePropertyChange));
+    public double ValueFromSlider
+    {
+        get => (double)GetValue(ValueFromSliderProperty);
+        set => SetValue(ValueFromSliderProperty, value);
+    }
 
+    private bool attached = false;
+    private bool dragging = false;
+    private bool valueChangedWhileDragging = false;
+    protected override void OnAttached()
+    {
+        AssociatedObject.Loaded += AssociatedObject_Loaded;
+        AssociatedObject.Focusable = false;
 
-            if (AssociatedObject.IsLoaded)
-                AttachEvents();
-        }
 
-        private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
-        {
+        if (AssociatedObject.IsLoaded)
             AttachEvents();
-        }
-
-        private void AttachEvents()
-        {
-            if (attached)
-                return;
-            attached = true;
-            var thumb = GetThumb(AssociatedObject);
-            if (thumb is null)
-                return;
-
-            thumb.DragStarted += Thumb_DragStarted;
-            thumb.DragCompleted += Thumb_DragCompleted;
-        }
+    }
 
-        protected override void OnDetaching()
-        {
-            AssociatedObject.Loaded -= AssociatedObject_Loaded;
-            if (!attached)
-                return;
-            var thumb = GetThumb(AssociatedObject);
-            if (thumb is null)
-                return;
-
-            thumb.DragStarted -= Thumb_DragStarted;
-            thumb.DragCompleted -= Thumb_DragCompleted;
-        }
+    private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
+    {
+        AttachEvents();
+    }
 
-        private static void OnSliderValuePropertyChange(DependencyObject slider, DependencyPropertyChangedEventArgs e)
-        {
-            var obj = (SliderUpdateBehavior)slider;
-            if (obj.dragging)
-            {
-                if (obj.DragValueChanged is not null && obj.DragValueChanged.CanExecute(e.NewValue))
-                    obj.DragValueChanged.Execute(e.NewValue);
-                obj.valueChangedWhileDragging = true;
-            }
-        }
+    private void AttachEvents()
+    {
+        if (attached)
+            return;
+        attached = true;
+        var thumb = GetThumb(AssociatedObject);
+        if (thumb is null)
+            return;
+
+        thumb.DragStarted += Thumb_DragStarted;
+        thumb.DragCompleted += Thumb_DragCompleted;
+    }
 
-        private void Thumb_DragCompleted(object sender, DragCompletedEventArgs e)
-        {
-            dragging = false;
-            if (valueChangedWhileDragging == true && DragEnded is not null && DragEnded.CanExecute(null))
-                DragEnded.Execute(null);
-            valueChangedWhileDragging = false;
-        }
+    protected override void OnDetaching()
+    {
+        AssociatedObject.Loaded -= AssociatedObject_Loaded;
+        if (!attached)
+            return;
+        var thumb = GetThumb(AssociatedObject);
+        if (thumb is null)
+            return;
+
+        thumb.DragStarted -= Thumb_DragStarted;
+        thumb.DragCompleted -= Thumb_DragCompleted;
+    }
 
-        private void Thumb_DragStarted(object sender, DragStartedEventArgs e)
+    private static void OnSliderValuePropertyChange(DependencyObject slider, DependencyPropertyChangedEventArgs e)
+    {
+        var obj = (SliderUpdateBehavior)slider;
+        if (obj.dragging)
         {
-            dragging = true;
+            if (obj.DragValueChanged is not null && obj.DragValueChanged.CanExecute(e.NewValue))
+                obj.DragValueChanged.Execute(e.NewValue);
+            obj.valueChangedWhileDragging = true;
         }
+    }
 
-        private static Thumb? GetThumb(Slider slider)
-        {
-            var track = slider.Template.FindName("PART_Track", slider) as Track;
-            return track is null ? null : track.Thumb;
-        }
+    private void Thumb_DragCompleted(object sender, DragCompletedEventArgs e)
+    {
+        dragging = false;
+        if (valueChangedWhileDragging == true && DragEnded is not null && DragEnded.CanExecute(null))
+            DragEnded.Execute(null);
+        valueChangedWhileDragging = false;
+    }
 
+    private void Thumb_DragStarted(object sender, DragStartedEventArgs e)
+    {
+        dragging = true;
+    }
 
+    private static Thumb? GetThumb(Slider slider)
+    {
+        var track = slider.Template.FindName("PART_Track", slider) as Track;
+        return track is null ? null : track.Thumb;
     }
+
+
 }

+ 10 - 11
src/PixiEditorPrototype/Converters/BoolToVisibilityConverter.cs

@@ -3,19 +3,18 @@ using System.Globalization;
 using System.Windows;
 using System.Windows.Data;
 
-namespace PixiEditorPrototype.Converters
+namespace PixiEditorPrototype.Converters;
+
+internal class BoolToVisibilityConverter : IValueConverter
 {
-    internal class BoolToVisibilityConverter : IValueConverter
+    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
     {
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            bool boolean = (bool)value;
-            return boolean ? Visibility.Visible : Visibility.Collapsed;
-        }
+        bool boolean = (bool)value;
+        return boolean ? Visibility.Visible : Visibility.Collapsed;
+    }
 
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            throw new NotImplementedException();
-        }
+    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+    {
+        throw new NotImplementedException();
     }
 }

+ 17 - 18
src/PixiEditorPrototype/Converters/IndexToChunkResolutionConverter.cs

@@ -3,27 +3,26 @@ using System;
 using System.Globalization;
 using System.Windows.Data;
 
-namespace PixiEditorPrototype.Converters
+namespace PixiEditorPrototype.Converters;
+
+internal class IndexToChunkResolutionConverter : IValueConverter
 {
-    internal class IndexToChunkResolutionConverter : IValueConverter
+    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
     {
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            throw new NotImplementedException();
-        }
+        throw new NotImplementedException();
+    }
 
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+    {
+        if (value is not int res)
+            return ChunkResolution.Full;
+        return res switch
         {
-            if (value is not int res)
-                return ChunkResolution.Full;
-            return res switch
-            {
-                0 => ChunkResolution.Full,
-                1 => ChunkResolution.Half,
-                2 => ChunkResolution.Quarter,
-                3 => ChunkResolution.Eighth,
-                _ => ChunkResolution.Full
-            };
-        }
+            0 => ChunkResolution.Full,
+            1 => ChunkResolution.Half,
+            2 => ChunkResolution.Quarter,
+            3 => ChunkResolution.Eighth,
+            _ => ChunkResolution.Full
+        };
     }
 }

+ 72 - 73
src/PixiEditorPrototype/Models/ActionAccumulator.cs

@@ -8,101 +8,100 @@ using PixiEditorPrototype.Models.Rendering.RenderInfos;
 using PixiEditorPrototype.ViewModels;
 using SkiaSharp;
 
-namespace PixiEditorPrototype.Models
+namespace PixiEditorPrototype.Models;
+
+internal class ActionAccumulator
 {
-    internal class ActionAccumulator
-    {
-        private bool executing = false;
+    private bool executing = false;
 
-        private List<IAction> queuedActions = new();
-        private DocumentViewModel document;
-        private DocumentHelpers helpers;
+    private List<IAction> queuedActions = new();
+    private DocumentViewModel document;
+    private DocumentHelpers helpers;
 
-        private WriteableBitmapUpdater renderer;
+    private WriteableBitmapUpdater renderer;
 
-        public ActionAccumulator(DocumentViewModel doc, DocumentHelpers helpers)
-        {
-            this.document = doc;
-            this.helpers = helpers;
-
-            renderer = new(doc, helpers);
-        }
+    public ActionAccumulator(DocumentViewModel doc, DocumentHelpers helpers)
+    {
+        this.document = doc;
+        this.helpers = helpers;
 
-        public void AddFinishedActions(params IAction[] actions)
-        {
-            queuedActions.AddRange(actions);
-            queuedActions.Add(new ChangeBoundary_Action());
-            TryExecuteAccumulatedActions();
-        }
+        renderer = new(doc, helpers);
+    }
 
-        public void AddActions(params IAction[] actions)
-        {
-            queuedActions.AddRange(actions);
-            TryExecuteAccumulatedActions();
-        }
+    public void AddFinishedActions(params IAction[] actions)
+    {
+        queuedActions.AddRange(actions);
+        queuedActions.Add(new ChangeBoundary_Action());
+        TryExecuteAccumulatedActions();
+    }
 
-        private async void TryExecuteAccumulatedActions()
-        {
-            if (executing || queuedActions.Count == 0)
-                return;
-            executing = true;
+    public void AddActions(params IAction[] actions)
+    {
+        queuedActions.AddRange(actions);
+        TryExecuteAccumulatedActions();
+    }
 
-            while (queuedActions.Count > 0)
-            {
-                var toExecute = queuedActions;
-                queuedActions = new List<IAction>();
+    private async void TryExecuteAccumulatedActions()
+    {
+        if (executing || queuedActions.Count == 0)
+            return;
+        executing = true;
 
-                List<IChangeInfo?> result = AreAllPassthrough(toExecute) ?
-                    toExecute.Select(a => (IChangeInfo?)a).ToList() :
-                    await helpers.Tracker.ProcessActions(toExecute);
+        while (queuedActions.Count > 0)
+        {
+            var toExecute = queuedActions;
+            queuedActions = new List<IAction>();
 
-                foreach (IChangeInfo? info in result)
-                {
-                    helpers.Updater.ApplyChangeFromChangeInfo(info);
-                }
+            List<IChangeInfo?> result = AreAllPassthrough(toExecute) ?
+                toExecute.Select(a => (IChangeInfo?)a).ToList() :
+                await helpers.Tracker.ProcessActions(toExecute);
 
-                foreach (var (_, bitmap) in document.Bitmaps)
-                {
-                    bitmap.Lock();
-                }
+            foreach (IChangeInfo? info in result)
+            {
+                helpers.Updater.ApplyChangeFromChangeInfo(info);
+            }
 
-                var renderResult = await renderer.ProcessChanges(result);
-                AddDirtyRects(renderResult);
+            foreach (var (_, bitmap) in document.Bitmaps)
+            {
+                bitmap.Lock();
+            }
 
-                foreach (var (_, bitmap) in document.Bitmaps)
-                {
-                    bitmap.Unlock();
-                }
+            var renderResult = await renderer.ProcessChanges(result);
+            AddDirtyRects(renderResult);
 
-                document.ForceRefreshView();
+            foreach (var (_, bitmap) in document.Bitmaps)
+            {
+                bitmap.Unlock();
             }
 
-            executing = false;
+            document.ForceRefreshView();
         }
 
-        private bool AreAllPassthrough(List<IAction> actions)
+        executing = false;
+    }
+
+    private bool AreAllPassthrough(List<IAction> actions)
+    {
+        foreach (var action in actions)
         {
-            foreach (var action in actions)
-            {
-                if (action is not IChangeInfo)
-                    return false;
-            }
-            return true;
+            if (action is not IChangeInfo)
+                return false;
         }
+        return true;
+    }
 
-        private void AddDirtyRects(List<IRenderInfo> changes)
+    private void AddDirtyRects(List<IRenderInfo> changes)
+    {
+        foreach (IRenderInfo info in changes)
         {
-            foreach (IRenderInfo info in changes)
-            {
-                if (info is not DirtyRect_RenderInfo dirtyRectInfo)
-                    continue;
-                var bitmap = document.Bitmaps[dirtyRectInfo.Resolution];
-                SKRectI finalRect = SKRectI.Create(0, 0, bitmap.PixelWidth, bitmap.PixelHeight);
-
-                SKRectI dirtyRect = SKRectI.Create(dirtyRectInfo.Pos, dirtyRectInfo.Size);
-                dirtyRect.Intersect(finalRect);
-                bitmap.AddDirtyRect(new(dirtyRect.Left, dirtyRect.Top, dirtyRect.Width, dirtyRect.Height));
-            }
+            if (info is not DirtyRect_RenderInfo dirtyRectInfo)
+                continue;
+            var bitmap = document.Bitmaps[dirtyRectInfo.Resolution];
+            SKRectI finalRect = SKRectI.Create(0, 0, bitmap.PixelWidth, bitmap.PixelHeight);
+
+            SKRectI dirtyRect = SKRectI.Create(dirtyRectInfo.Pos, dirtyRectInfo.Size);
+            dirtyRect.Intersect(finalRect);
+            bitmap.AddDirtyRect(new(dirtyRect.Left, dirtyRect.Top, dirtyRect.Width, dirtyRect.Height));
         }
     }
 }

+ 14 - 15
src/PixiEditorPrototype/Models/DocumentHelpers.cs

@@ -1,22 +1,21 @@
 using ChangeableDocument;
 using PixiEditorPrototype.ViewModels;
 
-namespace PixiEditorPrototype.Models
+namespace PixiEditorPrototype.Models;
+
+internal class DocumentHelpers
 {
-    internal class DocumentHelpers
+    public DocumentHelpers(DocumentViewModel doc)
     {
-        public DocumentHelpers(DocumentViewModel doc)
-        {
-            Tracker = new DocumentChangeTracker();
-            StructureHelper = new DocumentStructureHelper(doc, this);
-            Updater = new DocumentUpdater(doc, this);
-            ActionAccumulator = new ActionAccumulator(doc, this);
-            State = new DocumentState();
-        }
-        public ActionAccumulator ActionAccumulator { get; }
-        public DocumentChangeTracker Tracker { get; }
-        public DocumentStructureHelper StructureHelper { get; }
-        public DocumentUpdater Updater { get; }
-        public DocumentState State { get; }
+        Tracker = new DocumentChangeTracker();
+        StructureHelper = new DocumentStructureHelper(doc, this);
+        Updater = new DocumentUpdater(doc, this);
+        ActionAccumulator = new ActionAccumulator(doc, this);
+        State = new DocumentState();
     }
+    public ActionAccumulator ActionAccumulator { get; }
+    public DocumentChangeTracker Tracker { get; }
+    public DocumentStructureHelper StructureHelper { get; }
+    public DocumentUpdater Updater { get; }
+    public DocumentState State { get; }
 }

+ 4 - 5
src/PixiEditorPrototype/Models/DocumentState.cs

@@ -1,10 +1,9 @@
 using System;
 using System.Collections.Generic;
 
-namespace PixiEditorPrototype.Models
+namespace PixiEditorPrototype.Models;
+
+internal class DocumentState
 {
-    internal class DocumentState
-    {
-        public Dictionary<Guid, ViewportLocation> Viewports { get; set; } = new();
-    }
+    public Dictionary<Guid, ViewportLocation> Viewports { get; set; } = new();
 }

+ 84 - 85
src/PixiEditorPrototype/Models/DocumentStructureHelper.cs

@@ -4,116 +4,115 @@ using PixiEditor.ChangeableDocument.Actions.Structure;
 using PixiEditor.ChangeableDocument.Enums;
 using PixiEditorPrototype.ViewModels;
 
-namespace PixiEditorPrototype.Models
+namespace PixiEditorPrototype.Models;
+
+internal class DocumentStructureHelper
 {
-    internal class DocumentStructureHelper
+    private DocumentViewModel doc;
+    private DocumentHelpers helpers;
+    public DocumentStructureHelper(DocumentViewModel doc, DocumentHelpers helpers)
     {
-        private DocumentViewModel doc;
-        private DocumentHelpers helpers;
-        public DocumentStructureHelper(DocumentViewModel doc, DocumentHelpers helpers)
-        {
-            this.doc = doc;
-            this.helpers = helpers;
-        }
+        this.doc = doc;
+        this.helpers = helpers;
+    }
 
-        public void CreateNewStructureMember(StructureMemberType type)
+    public void CreateNewStructureMember(StructureMemberType type)
+    {
+        if (doc.SelectedStructureMember is null)
         {
-            if (doc.SelectedStructureMember is null)
-            {
-                //put member on top
-                helpers.ActionAccumulator.AddFinishedActions(new CreateStructureMember_Action(doc.StructureRoot.GuidValue, Guid.NewGuid(), doc.StructureRoot.Children.Count, type));
-                return;
-            }
-            if (doc.SelectedStructureMember is FolderViewModel folder)
-            {
-                //put member inside folder on top
-                helpers.ActionAccumulator.AddFinishedActions(new CreateStructureMember_Action(folder.GuidValue, Guid.NewGuid(), folder.Children.Count, type));
-                return;
-            }
-            if (doc.SelectedStructureMember is LayerViewModel layer)
-            {
-                //put member above the layer
-                var path = FindPath(layer.GuidValue);
-                if (path.Count < 2)
-                    throw new InvalidOperationException("Couldn't find a path to the selected member");
-                var parent = (FolderViewModel)path[1];
-                helpers.ActionAccumulator.AddFinishedActions(new CreateStructureMember_Action(parent.GuidValue, Guid.NewGuid(), parent.Children.IndexOf(layer) + 1, type));
-                return;
-            }
-            throw new ArgumentException("Unknown member type: " + type.ToString());
+            //put member on top
+            helpers.ActionAccumulator.AddFinishedActions(new CreateStructureMember_Action(doc.StructureRoot.GuidValue, Guid.NewGuid(), doc.StructureRoot.Children.Count, type));
+            return;
         }
-
-        public StructureMemberViewModel FindOrThrow(Guid guid) => Find(guid) ?? throw new ArgumentException("Could not find member with guid " + guid.ToString());
-        public StructureMemberViewModel? Find(Guid guid)
+        if (doc.SelectedStructureMember is FolderViewModel folder)
         {
-            var list = FindPath(guid);
-            return list.Count > 0 ? list[0] : null;
+            //put member inside folder on top
+            helpers.ActionAccumulator.AddFinishedActions(new CreateStructureMember_Action(folder.GuidValue, Guid.NewGuid(), folder.Children.Count, type));
+            return;
         }
-
-        public (StructureMemberViewModel, FolderViewModel) FindChildAndParentOrThrow(Guid childGuid)
+        if (doc.SelectedStructureMember is LayerViewModel layer)
         {
-            var path = FindPath(childGuid);
+            //put member above the layer
+            var path = FindPath(layer.GuidValue);
             if (path.Count < 2)
-                throw new ArgumentException("Couldn't find child and parent");
-            return (path[0], (FolderViewModel)path[1]);
+                throw new InvalidOperationException("Couldn't find a path to the selected member");
+            var parent = (FolderViewModel)path[1];
+            helpers.ActionAccumulator.AddFinishedActions(new CreateStructureMember_Action(parent.GuidValue, Guid.NewGuid(), parent.Children.IndexOf(layer) + 1, type));
+            return;
         }
-        public List<StructureMemberViewModel> FindPath(Guid guid)
+        throw new ArgumentException("Unknown member type: " + type.ToString());
+    }
+
+    public StructureMemberViewModel FindOrThrow(Guid guid) => Find(guid) ?? throw new ArgumentException("Could not find member with guid " + guid.ToString());
+    public StructureMemberViewModel? Find(Guid guid)
+    {
+        var list = FindPath(guid);
+        return list.Count > 0 ? list[0] : null;
+    }
+
+    public (StructureMemberViewModel, FolderViewModel) FindChildAndParentOrThrow(Guid childGuid)
+    {
+        var path = FindPath(childGuid);
+        if (path.Count < 2)
+            throw new ArgumentException("Couldn't find child and parent");
+        return (path[0], (FolderViewModel)path[1]);
+    }
+    public List<StructureMemberViewModel> FindPath(Guid guid)
+    {
+        var list = new List<StructureMemberViewModel>();
+        if (FillPath(doc.StructureRoot, guid, list))
+            list.Add(doc.StructureRoot);
+        return list;
+    }
+
+    private bool FillPath(FolderViewModel folder, Guid guid, List<StructureMemberViewModel> toFill)
+    {
+        if (folder.GuidValue == guid)
         {
-            var list = new List<StructureMemberViewModel>();
-            if (FillPath(doc.StructureRoot, guid, list))
-                list.Add(doc.StructureRoot);
-            return list;
+            return true;
         }
-
-        private bool FillPath(FolderViewModel folder, Guid guid, List<StructureMemberViewModel> toFill)
+        foreach (var member in folder.Children)
         {
-            if (folder.GuidValue == guid)
+            if (member is LayerViewModel childLayer && childLayer.GuidValue == guid)
             {
+                toFill.Add(member);
                 return true;
             }
-            foreach (var member in folder.Children)
+            if (member is FolderViewModel childFolder)
             {
-                if (member is LayerViewModel childLayer && childLayer.GuidValue == guid)
+                if (FillPath(childFolder, guid, toFill))
                 {
-                    toFill.Add(member);
+                    toFill.Add(childFolder);
                     return true;
                 }
-                if (member is FolderViewModel childFolder)
-                {
-                    if (FillPath(childFolder, guid, toFill))
-                    {
-                        toFill.Add(childFolder);
-                        return true;
-                    }
-                }
             }
-            return false;
         }
+        return false;
+    }
 
-        public void MoveStructureMember(Guid guid, bool toSmallerIndex)
+    public void MoveStructureMember(Guid guid, bool toSmallerIndex)
+    {
+        var path = FindPath(guid);
+        if (path.Count < 2)
+            throw new ArgumentException("Couldn't find the member to be moved");
+        if (path.Count == 2)
         {
-            var path = FindPath(guid);
-            if (path.Count < 2)
-                throw new ArgumentException("Couldn't find the member to be moved");
-            if (path.Count == 2)
-            {
-                int curIndex = doc.StructureRoot.Children.IndexOf(path[0]);
-                if (curIndex == 0 && toSmallerIndex || curIndex == doc.StructureRoot.Children.Count - 1 && !toSmallerIndex)
-                    return;
-                helpers.ActionAccumulator.AddFinishedActions(new MoveStructureMember_Action(guid, doc.StructureRoot.GuidValue, toSmallerIndex ? curIndex - 1 : curIndex + 1));
+            int curIndex = doc.StructureRoot.Children.IndexOf(path[0]);
+            if (curIndex == 0 && toSmallerIndex || curIndex == doc.StructureRoot.Children.Count - 1 && !toSmallerIndex)
                 return;
-            }
-            var folder = (FolderViewModel)path[1];
-            int index = folder.Children.IndexOf(path[0]);
-            if (toSmallerIndex && index > 0 || !toSmallerIndex && index < folder.Children.Count - 1)
-            {
-                helpers.ActionAccumulator.AddFinishedActions(new MoveStructureMember_Action(guid, path[1].GuidValue, toSmallerIndex ? index - 1 : index + 1));
-            }
-            else
-            {
-                int parentIndex = ((FolderViewModel)path[2]).Children.IndexOf(folder);
-                helpers.ActionAccumulator.AddFinishedActions(new MoveStructureMember_Action(guid, path[2].GuidValue, toSmallerIndex ? parentIndex : parentIndex + 1));
-            }
+            helpers.ActionAccumulator.AddFinishedActions(new MoveStructureMember_Action(guid, doc.StructureRoot.GuidValue, toSmallerIndex ? curIndex - 1 : curIndex + 1));
+            return;
+        }
+        var folder = (FolderViewModel)path[1];
+        int index = folder.Children.IndexOf(path[0]);
+        if (toSmallerIndex && index > 0 || !toSmallerIndex && index < folder.Children.Count - 1)
+        {
+            helpers.ActionAccumulator.AddFinishedActions(new MoveStructureMember_Action(guid, path[1].GuidValue, toSmallerIndex ? index - 1 : index + 1));
+        }
+        else
+        {
+            int parentIndex = ((FolderViewModel)path[2]).Children.IndexOf(folder);
+            helpers.ActionAccumulator.AddFinishedActions(new MoveStructureMember_Action(guid, path[2].GuidValue, toSmallerIndex ? parentIndex : parentIndex + 1));
         }
     }
 }

+ 131 - 132
src/PixiEditorPrototype/Models/DocumentUpdater.cs

@@ -7,168 +7,167 @@ using PixiEditor.ChangeableDocument.ChangeInfos;
 using PixiEditorPrototype.ViewModels;
 using SkiaSharp;
 
-namespace PixiEditorPrototype.Models
+namespace PixiEditorPrototype.Models;
+
+internal class DocumentUpdater
 {
-    internal class DocumentUpdater
+    private DocumentViewModel doc;
+    private DocumentHelpers helper;
+    public DocumentUpdater(DocumentViewModel doc, DocumentHelpers helper)
+    {
+        this.doc = doc;
+        this.helper = helper;
+    }
+
+    public void ApplyChangeFromChangeInfo(IChangeInfo? arbitraryInfo)
     {
-        private DocumentViewModel doc;
-        private DocumentHelpers helper;
-        public DocumentUpdater(DocumentViewModel doc, DocumentHelpers helper)
+        if (arbitraryInfo is null)
+            return;
+
+        switch (arbitraryInfo)
         {
-            this.doc = doc;
-            this.helper = helper;
+            case CreateStructureMember_ChangeInfo info:
+                ProcessCreateStructureMember(info);
+                break;
+            case DeleteStructureMember_ChangeInfo info:
+                ProcessDeleteStructureMember(info);
+                break;
+            case StructureMemberName_ChangeInfo info:
+                ProcessUpdateStructureMemberName(info);
+                break;
+            case StructureMemberIsVisible_ChangeInfo info:
+                ProcessUpdateStructureMemberIsVisible(info);
+                break;
+            case StructureMemberOpacity_ChangeInfo info:
+                ProcessUpdateStructureMemberOpacity(info);
+                break;
+            case MoveStructureMember_ChangeInfo info:
+                ProcessMoveStructureMember(info);
+                break;
+            case Size_ChangeInfo info:
+                ProcessSize(info);
+                break;
+            case RefreshViewport_PassthroughAction info:
+                ProcessRefreshViewport(info);
+                break;
+            case StructureMemberMask_ChangeInfo info:
+                ProcessStructureMemberMask(info);
+                break;
+            case StructureMemberBlendMode_ChangeInfo info:
+                ProcessStructureMemberBlendMode(info);
+                break;
         }
+    }
 
-        public void ApplyChangeFromChangeInfo(IChangeInfo? arbitraryInfo)
-        {
-            if (arbitraryInfo is null)
-                return;
+    private void ProcessStructureMemberBlendMode(StructureMemberBlendMode_ChangeInfo info)
+    {
+        var memberVm = helper.StructureHelper.FindOrThrow(info.GuidValue);
+        memberVm.RaisePropertyChanged(nameof(memberVm.BlendMode));
+    }
 
-            switch (arbitraryInfo)
-            {
-                case CreateStructureMember_ChangeInfo info:
-                    ProcessCreateStructureMember(info);
-                    break;
-                case DeleteStructureMember_ChangeInfo info:
-                    ProcessDeleteStructureMember(info);
-                    break;
-                case StructureMemberName_ChangeInfo info:
-                    ProcessUpdateStructureMemberName(info);
-                    break;
-                case StructureMemberIsVisible_ChangeInfo info:
-                    ProcessUpdateStructureMemberIsVisible(info);
-                    break;
-                case StructureMemberOpacity_ChangeInfo info:
-                    ProcessUpdateStructureMemberOpacity(info);
-                    break;
-                case MoveStructureMember_ChangeInfo info:
-                    ProcessMoveStructureMember(info);
-                    break;
-                case Size_ChangeInfo info:
-                    ProcessSize(info);
-                    break;
-                case RefreshViewport_PassthroughAction info:
-                    ProcessRefreshViewport(info);
-                    break;
-                case StructureMemberMask_ChangeInfo info:
-                    ProcessStructureMemberMask(info);
-                    break;
-                case StructureMemberBlendMode_ChangeInfo info:
-                    ProcessStructureMemberBlendMode(info);
-                    break;
-            }
-        }
+    private void ProcessStructureMemberMask(StructureMemberMask_ChangeInfo info)
+    {
+        var memberVm = helper.StructureHelper.FindOrThrow(info.GuidValue);
+        memberVm.RaisePropertyChanged(nameof(memberVm.HasMask));
+    }
 
-        private void ProcessStructureMemberBlendMode(StructureMemberBlendMode_ChangeInfo info)
+    private void ProcessRefreshViewport(RefreshViewport_PassthroughAction info)
+    {
+        var viewport = doc.GetViewport(info.GuidValue);
+        if (viewport is null)
         {
-            var memberVm = helper.StructureHelper.FindOrThrow(info.GuidValue);
-            memberVm.RaisePropertyChanged(nameof(memberVm.BlendMode));
+            helper.State.Viewports.Remove(info.GuidValue);
+            return;
         }
+        helper.State.Viewports[info.GuidValue] = viewport.Value with { Dimensions = viewport.Value.Dimensions / 2, RealDimensions = viewport.Value.RealDimensions / 2 };
+        doc.UpdateViewportResolution(info.GuidValue, viewport.Value.Resolution);
+    }
 
-        private void ProcessStructureMemberMask(StructureMemberMask_ChangeInfo info)
+    private void ProcessSize(Size_ChangeInfo info)
+    {
+        var size = helper.Tracker.Document.Size;
+        foreach (var (res, surf) in doc.Surfaces)
         {
-            var memberVm = helper.StructureHelper.FindOrThrow(info.GuidValue);
-            memberVm.RaisePropertyChanged(nameof(memberVm.HasMask));
+            surf.Dispose();
+            doc.Bitmaps[res] = CreateBitmap((Vector2i)(size * res.Multiplier()));
+            doc.Surfaces[res] = CreateSKSurface(doc.Bitmaps[res]);
         }
 
-        private void ProcessRefreshViewport(RefreshViewport_PassthroughAction info)
-        {
-            var viewport = doc.GetViewport(info.GuidValue);
-            if (viewport is null)
-            {
-                helper.State.Viewports.Remove(info.GuidValue);
-                return;
-            }
-            helper.State.Viewports[info.GuidValue] = viewport.Value with { Dimensions = viewport.Value.Dimensions / 2, RealDimensions = viewport.Value.RealDimensions / 2 };
-            doc.UpdateViewportResolution(info.GuidValue, viewport.Value.Resolution);
-        }
+        doc.RaisePropertyChanged(nameof(doc.Width));
+        doc.RaisePropertyChanged(nameof(doc.Height));
+    }
 
-        private void ProcessSize(Size_ChangeInfo info)
-        {
-            var size = helper.Tracker.Document.Size;
-            foreach (var (res, surf) in doc.Surfaces)
-            {
-                surf.Dispose();
-                doc.Bitmaps[res] = CreateBitmap((Vector2i)(size * res.Multiplier()));
-                doc.Surfaces[res] = CreateSKSurface(doc.Bitmaps[res]);
-            }
+    private WriteableBitmap CreateBitmap(Vector2i size)
+    {
+        return new WriteableBitmap(Math.Max(size.X, 1), Math.Max(size.Y, 1), 96, 96, PixelFormats.Pbgra32, null);
+    }
 
-            doc.RaisePropertyChanged(nameof(doc.Width));
-            doc.RaisePropertyChanged(nameof(doc.Height));
-        }
+    private SKSurface CreateSKSurface(WriteableBitmap bitmap)
+    {
+        return SKSurface.Create(
+            new SKImageInfo(bitmap.PixelWidth, bitmap.PixelHeight, SKColorType.Bgra8888, SKAlphaType.Premul, SKColorSpace.CreateSrgb()),
+            bitmap.BackBuffer,
+            bitmap.BackBufferStride);
+    }
 
-        private WriteableBitmap CreateBitmap(Vector2i size)
-        {
-            return new WriteableBitmap(Math.Max(size.X, 1), Math.Max(size.Y, 1), 96, 96, PixelFormats.Pbgra32, null);
-        }
+    private void ProcessCreateStructureMember(CreateStructureMember_ChangeInfo info)
+    {
+        var (member, parentFolder) = helper.Tracker.Document.FindChildAndParentOrThrow(info.GuidValue);
+        var parentFolderVM = (FolderViewModel)helper.StructureHelper.FindOrThrow(parentFolder.GuidValue);
 
-        private SKSurface CreateSKSurface(WriteableBitmap bitmap)
-        {
-            return SKSurface.Create(
-                new SKImageInfo(bitmap.PixelWidth, bitmap.PixelHeight, SKColorType.Bgra8888, SKAlphaType.Premul, SKColorSpace.CreateSrgb()),
-                bitmap.BackBuffer,
-                bitmap.BackBufferStride);
-        }
+        int index = parentFolder.ReadOnlyChildren.IndexOf(member);
 
-        private void ProcessCreateStructureMember(CreateStructureMember_ChangeInfo info)
+        StructureMemberViewModel memberVM = member switch
         {
-            var (member, parentFolder) = helper.Tracker.Document.FindChildAndParentOrThrow(info.GuidValue);
-            var parentFolderVM = (FolderViewModel)helper.StructureHelper.FindOrThrow(parentFolder.GuidValue);
-
-            int index = parentFolder.ReadOnlyChildren.IndexOf(member);
-
-            StructureMemberViewModel memberVM = member switch
-            {
-                IReadOnlyLayer layer => new LayerViewModel(doc, helper, layer),
-                IReadOnlyFolder folder => new FolderViewModel(doc, helper, folder),
-                _ => throw new InvalidOperationException("Unsupposed member type")
-            };
+            IReadOnlyLayer layer => new LayerViewModel(doc, helper, layer),
+            IReadOnlyFolder folder => new FolderViewModel(doc, helper, folder),
+            _ => throw new InvalidOperationException("Unsupposed member type")
+        };
 
-            parentFolderVM.Children.Insert(index, memberVM);
+        parentFolderVM.Children.Insert(index, memberVM);
 
-            if (member is IReadOnlyFolder folder2)
+        if (member is IReadOnlyFolder folder2)
+        {
+            foreach (IReadOnlyStructureMember child in folder2.ReadOnlyChildren)
             {
-                foreach (IReadOnlyStructureMember child in folder2.ReadOnlyChildren)
-                {
-                    ProcessCreateStructureMember(new CreateStructureMember_ChangeInfo() { GuidValue = child.GuidValue });
-                }
+                ProcessCreateStructureMember(new CreateStructureMember_ChangeInfo() { GuidValue = child.GuidValue });
             }
         }
+    }
 
-        private void ProcessDeleteStructureMember(DeleteStructureMember_ChangeInfo info)
-        {
-            var (memberVM, folderVM) = helper.StructureHelper.FindChildAndParentOrThrow(info.GuidValue);
-            folderVM.Children.Remove(memberVM);
-        }
+    private void ProcessDeleteStructureMember(DeleteStructureMember_ChangeInfo info)
+    {
+        var (memberVM, folderVM) = helper.StructureHelper.FindChildAndParentOrThrow(info.GuidValue);
+        folderVM.Children.Remove(memberVM);
+    }
 
-        private void ProcessUpdateStructureMemberIsVisible(StructureMemberIsVisible_ChangeInfo info)
-        {
-            var memberVM = helper.StructureHelper.FindOrThrow(info.GuidValue);
-            memberVM.RaisePropertyChanged(nameof(memberVM.IsVisible));
-        }
+    private void ProcessUpdateStructureMemberIsVisible(StructureMemberIsVisible_ChangeInfo info)
+    {
+        var memberVM = helper.StructureHelper.FindOrThrow(info.GuidValue);
+        memberVM.RaisePropertyChanged(nameof(memberVM.IsVisible));
+    }
 
-        private void ProcessUpdateStructureMemberName(StructureMemberName_ChangeInfo info)
-        {
-            var memberVM = helper.StructureHelper.FindOrThrow(info.GuidValue);
-            memberVM.RaisePropertyChanged(nameof(memberVM.Name));
-        }
+    private void ProcessUpdateStructureMemberName(StructureMemberName_ChangeInfo info)
+    {
+        var memberVM = helper.StructureHelper.FindOrThrow(info.GuidValue);
+        memberVM.RaisePropertyChanged(nameof(memberVM.Name));
+    }
 
-        private void ProcessUpdateStructureMemberOpacity(StructureMemberOpacity_ChangeInfo info)
-        {
-            var memberVM = helper.StructureHelper.FindOrThrow(info.GuidValue);
-            memberVM.RaisePropertyChanged(nameof(memberVM.Opacity));
-        }
+    private void ProcessUpdateStructureMemberOpacity(StructureMemberOpacity_ChangeInfo info)
+    {
+        var memberVM = helper.StructureHelper.FindOrThrow(info.GuidValue);
+        memberVM.RaisePropertyChanged(nameof(memberVM.Opacity));
+    }
 
-        private void ProcessMoveStructureMember(MoveStructureMember_ChangeInfo info)
-        {
-            var (memberVM, curFolderVM) = helper.StructureHelper.FindChildAndParentOrThrow(info.GuidValue);
-            var (member, targetFolder) = helper.Tracker.Document.FindChildAndParentOrThrow(info.GuidValue);
+    private void ProcessMoveStructureMember(MoveStructureMember_ChangeInfo info)
+    {
+        var (memberVM, curFolderVM) = helper.StructureHelper.FindChildAndParentOrThrow(info.GuidValue);
+        var (member, targetFolder) = helper.Tracker.Document.FindChildAndParentOrThrow(info.GuidValue);
 
-            int index = targetFolder.ReadOnlyChildren.IndexOf(member);
-            var targetFolderVM = (FolderViewModel)helper.StructureHelper.FindOrThrow(targetFolder.GuidValue);
+        int index = targetFolder.ReadOnlyChildren.IndexOf(member);
+        var targetFolderVM = (FolderViewModel)helper.StructureHelper.FindOrThrow(targetFolder.GuidValue);
 
-            curFolderVM.Children.Remove(memberVM);
-            targetFolderVM.Children.Insert(index, memberVM);
-        }
+        curFolderVM.Children.Remove(memberVM);
+        targetFolderVM.Children.Insert(index, memberVM);
     }
 }

Some files were not shown because too many files changed in this diff