Browse Source

Rotate individual layers

flabbet 2 years ago
parent
commit
8a6b2229b6

+ 97 - 50
src/PixiEditor.ChangeableDocument/Changes/Root/RotateImage_Change.cs

@@ -53,14 +53,10 @@ internal sealed class RotateImage_Change : Change
         return changes;
         return changes;
     }
     }
 
 
-    public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
+    private void Resize(ChunkyImage img, Guid memberGuid,
+        Dictionary<Guid, CommittedChunkStorage> deletedChunksDict, List<IChangeInfo>? changes)
     {
     {
-        
-    }
-
-    private void Resize(ChunkyImage img, Guid memberGuid, Dictionary<Guid, CommittedChunkStorage> deletedChunksDict)
-    {
-        RectI bounds = new RectI(VecI.Zero, img.LatestSize);
+        RectI bounds = new RectI(VecI.Zero, img.CommittedSize);
         if (membersToRotate.Count > 0)
         if (membersToRotate.Count > 0)
         {
         {
             var preciseBounds = img.FindPreciseCommittedBounds();
             var preciseBounds = img.FindPreciseCommittedBounds();
@@ -69,35 +65,40 @@ internal sealed class RotateImage_Change : Change
                 bounds = preciseBounds.Value;
                 bounds = preciseBounds.Value;
             }
             }
         }
         }
+
+        int originalWidth = bounds.Width;
+        int originalHeight = bounds.Height;
         
         
-        int newWidth = rotation == RotationAngle.D180 ? bounds.Size.X : bounds.Size.Y;
-        int newHeight = rotation == RotationAngle.D180 ? bounds.Size.Y : bounds.Size.X;
-        
-        VecI size = new VecI(newWidth, newHeight)''
+        int newWidth = rotation == RotationAngle.D180 ? originalWidth : originalHeight;
+        int newHeight = rotation == RotationAngle.D180 ? originalHeight : originalWidth;
+
+        VecI originalSize = new VecI(originalWidth, originalHeight);
+        VecI newSize = new VecI(newWidth, newHeight);
         
         
         using Paint paint = new()
         using Paint paint = new()
         {
         {
             BlendMode = DrawingApi.Core.Surface.BlendMode.Src
             BlendMode = DrawingApi.Core.Surface.BlendMode.Src
         };
         };
         
         
-        using Surface originalSurface = new(img.LatestSize);
+        using Surface originalSurface = new(originalSize);
         img.DrawMostUpToDateRegionOn(
         img.DrawMostUpToDateRegionOn(
-            new RectI(VecI.Zero, img.LatestSize), 
+            bounds, 
             ChunkResolution.Full,
             ChunkResolution.Full,
             originalSurface.DrawingSurface,
             originalSurface.DrawingSurface,
             VecI.Zero);
             VecI.Zero);
 
 
-        using Surface flipped = new Surface(img.LatestSize);
+        using Surface flipped = new Surface(newSize);
 
 
-        float translationX = size.X;
-        float translationY = size.Y;
-        if (rotation == RotationAngle.D90)
-        {
-            translationY = 0;
-        }
-        else if (rotation == RotationAngle.D270)
+        float translationX = newSize.X;
+        float translationY = newSize.Y;
+        switch (rotation)
         {
         {
-            translationX = 0;
+            case RotationAngle.D90:
+                translationY = 0;
+                break;
+            case RotationAngle.D270:
+                translationX = 0;
+                break;
         }
         }
         
         
         flipped.DrawingSurface.Canvas.Save();
         flipped.DrawingSurface.Canvas.Save();
@@ -106,11 +107,13 @@ internal sealed class RotateImage_Change : Change
         flipped.DrawingSurface.Canvas.DrawSurface(originalSurface.DrawingSurface, 0, 0, paint);
         flipped.DrawingSurface.Canvas.DrawSurface(originalSurface.DrawingSurface, 0, 0, paint);
         flipped.DrawingSurface.Canvas.Restore();
         flipped.DrawingSurface.Canvas.Restore();
         
         
-        img.EnqueueResize(size);
         img.EnqueueClear();
         img.EnqueueClear();
-        img.EnqueueDrawImage(VecI.Zero, flipped);
+        img.EnqueueDrawImage(bounds.Pos, flipped);
 
 
-        deletedChunksDict.Add(memberGuid, new CommittedChunkStorage(img, img.FindAffectedChunks()));
+        var affectedChunks = img.FindAffectedChunks();
+        deletedChunksDict.Add(memberGuid, new CommittedChunkStorage(img, affectedChunks));
+        changes?.Add(new LayerImageChunks_ChangeInfo(memberGuid, affectedChunks));
+        img.CommitChanges();
     }
     }
 
 
     private OneOf<None, IChangeInfo, List<IChangeInfo>> Rotate(Document target)
     private OneOf<None, IChangeInfo, List<IChangeInfo>> Rotate(Document target)
@@ -131,32 +134,15 @@ internal sealed class RotateImage_Change : Change
         {
         {
             if (guids.Contains(member.GuidValue))
             if (guids.Contains(member.GuidValue))
             {
             {
-                int newWidth;
-                int newHeight;
-
                 if (member is Layer layer)
                 if (member is Layer layer)
                 {
                 {
-                    Resize(layer.LayerImage, layer.GuidValue,
-                            deletedChunks);
-                    changes.Add(
-                            new LayerImageChunks_ChangeInfo(member.GuidValue, layer.LayerImage.FindAffectedChunks()));
-                    layer.LayerImage.CommitChanges();
+                    Resize(layer.LayerImage, layer.GuidValue, deletedChunks, changes);
                 }
                 }
 
 
                 if (member.Mask is null)
                 if (member.Mask is null)
                     return;
                     return;
 
 
-                var maskBounds = member.Mask.FindPreciseCommittedBounds();
-                if (maskBounds.HasValue)
-                {
-                    newWidth = rotation == RotationAngle.D180 ? maskBounds.Value.Size.X : maskBounds.Value.Size.Y;
-                    newHeight = rotation == RotationAngle.D180 ? maskBounds.Value.Size.Y : maskBounds.Value.Size.X;
-
-                    Resize(member.Mask, member.GuidValue, new VecI(newWidth, newHeight), VecI.Zero, deletedMaskChunks);
-                    changes.Add(
-                        new LayerImageChunks_ChangeInfo(member.GuidValue, member.Mask.FindAffectedChunks()));
-                    member.Mask.CommitChanges();
-                }
+                Resize(member.Mask, member.GuidValue, deletedMaskChunks, null);
             }
             }
         });
         });
 
 
@@ -170,8 +156,8 @@ internal sealed class RotateImage_Change : Change
 
 
         VecI newSize = new VecI(newWidth, newHeight);
         VecI newSize = new VecI(newWidth, newHeight);
 
 
-        float normalizedSymmX = _originalVerAxisX / Math.Max(target.Size.X, 0.1f);
-        float normalizedSymmY = _originalHorAxisY / Math.Max(target.Size.Y, 0.1f);
+        float normalizedSymmX = originalVerAxisX / Math.Max(target.Size.X, 0.1f);
+        float normalizedSymmY = originalHorAxisY / Math.Max(target.Size.Y, 0.1f);
 
 
         target.Size = newSize;
         target.Size = newSize;
         target.VerticalSymmetryAxisX = (int)(newSize.X * normalizedSymmX);
         target.VerticalSymmetryAxisX = (int)(newSize.X * normalizedSymmX);
@@ -181,20 +167,81 @@ internal sealed class RotateImage_Change : Change
         {
         {
             if (member is Layer layer)
             if (member is Layer layer)
             {
             {
-                Resize(layer.LayerImage, layer.GuidValue, newSize, VecI.Zero, deletedChunks);
-                layer.LayerImage.CommitChanges();
+                Resize(layer.LayerImage, layer.GuidValue, deletedChunks, null);
             }
             }
 
 
             if (member.Mask is null)
             if (member.Mask is null)
                 return;
                 return;
 
 
-            Resize(member.Mask, member.GuidValue, newSize, VecI.Zero, deletedMaskChunks);
-            member.Mask.CommitChanges();
+            Resize(member.Mask, member.GuidValue, deletedMaskChunks, null);
         });
         });
 
 
         return new Size_ChangeInfo(newSize, target.VerticalSymmetryAxisX, target.HorizontalSymmetryAxisY);
         return new Size_ChangeInfo(newSize, target.VerticalSymmetryAxisX, target.HorizontalSymmetryAxisY);
     }
     }
     
     
+    public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
+    {
+        if (membersToRotate.Count == 0)
+        {
+            return RevertRotateWholeImage(target);
+        }
+
+        return RevertRotateMembers(target);
+    }
+
+    private OneOf<None, IChangeInfo, List<IChangeInfo>> RevertRotateWholeImage(Document target)
+    {
+        target.Size = originalSize;
+        RevertRotateMembers(target);
+
+        target.HorizontalSymmetryAxisY = originalHorAxisY;
+        target.VerticalSymmetryAxisX = originalVerAxisX;
+
+        return new Size_ChangeInfo(originalSize, originalVerAxisX, originalHorAxisY);
+    }
+
+    private List<IChangeInfo> RevertRotateMembers(Document target)
+    {
+        List<IChangeInfo> revertChanges = new List<IChangeInfo>();
+        target.ForEveryMember((member) =>
+        {
+            if(membersToRotate.Count > 0 && !membersToRotate.Contains(member.GuidValue)) return;
+            if (member is Layer layer)
+            {
+                layer.LayerImage.EnqueueResize(originalSize);
+                deletedChunks[layer.GuidValue].ApplyChunksToImage(layer.LayerImage);
+                revertChanges.Add(new LayerImageChunks_ChangeInfo(layer.GuidValue, layer.LayerImage.FindAffectedChunks()));
+                layer.LayerImage.CommitChanges();
+            }
+
+            if (member.Mask is null)
+                return;
+            member.Mask.EnqueueResize(originalSize);
+            deletedMaskChunks[member.GuidValue].ApplyChunksToImage(member.Mask);
+            revertChanges.Add(new LayerImageChunks_ChangeInfo(member.GuidValue, member.Mask.FindAffectedChunks()));
+            member.Mask.CommitChanges();
+        });
+
+        DisposeDeletedChunks();
+        return revertChanges;
+    }
+
+    private void DisposeDeletedChunks()
+    {
+        foreach (var stored in deletedChunks)
+            stored.Value.Dispose();
+        deletedChunks = new();
+
+        foreach (var stored in deletedMaskChunks)
+            stored.Value.Dispose();
+        deletedMaskChunks = new();
+    }
+
+    public override void Dispose()
+    {
+        DisposeDeletedChunks();
+    }
+
     private float RotationAngleToRadians(RotationAngle rotationAngle)
     private float RotationAngleToRadians(RotationAngle rotationAngle)
     {
     {
         return rotationAngle switch
         return rotationAngle switch

+ 3 - 1
src/PixiEditor/Models/DocumentModels/Public/DocumentOperationsModule.cs

@@ -300,6 +300,8 @@ internal class DocumentOperationsModule
         Internals.ActionAccumulator.AddFinishedActions(new FlipImage_Action(flipType, membersToFlip));
         Internals.ActionAccumulator.AddFinishedActions(new FlipImage_Action(flipType, membersToFlip));
     }
     }
 
 
+    public void RotateImage(RotationAngle rotation) => RotateImage(rotation, null);
+
     public void RotateImage(RotationAngle rotation, List<Guid> membersToRotate)
     public void RotateImage(RotationAngle rotation, List<Guid> membersToRotate)
     {
     {
         if (Internals.ChangeController.IsChangeActive)
         if (Internals.ChangeController.IsChangeActive)
@@ -307,7 +309,7 @@ internal class DocumentOperationsModule
         
         
         Internals.ActionAccumulator.AddFinishedActions(new RotateImage_Action(rotation, membersToRotate));
         Internals.ActionAccumulator.AddFinishedActions(new RotateImage_Action(rotation, membersToRotate));
     }
     }
-
+    
     public void CenterContent(IReadOnlyList<Guid> structureMembers)
     public void CenterContent(IReadOnlyList<Guid> structureMembers)
     {
     {
         if (Internals.ChangeController.IsChangeActive)
         if (Internals.ChangeController.IsChangeActive)

+ 24 - 4
src/PixiEditor/ViewModels/SubViewModels/Document/DocumentManagerViewModel.cs

@@ -97,7 +97,13 @@ internal class DocumentManagerViewModel : SubViewModel<ViewModelMain>
     [Command.Basic("PixiEditor.Document.Rotate90Deg", "Rotate Image 90 deg", "Rotate Image 90 deg", CanExecute = "PixiEditor.HasDocument")]
     [Command.Basic("PixiEditor.Document.Rotate90Deg", "Rotate Image 90 deg", "Rotate Image 90 deg", CanExecute = "PixiEditor.HasDocument")]
     public void Rotate90Deg()
     public void Rotate90Deg()
     {
     {
-        if (ActiveDocument == null)
+        ActiveDocument?.Operations.RotateImage(RotationAngle.D90);
+    }
+    
+    [Command.Basic("PixiEditor.Document.Rotate90DegLayers", "Rotate Selected Layers 90 deg", "Rotate Selected Layers 90 deg", CanExecute = "PixiEditor.HasDocument")]
+    public void Rotate90DegLayers()
+    {
+        if (ActiveDocument?.SelectedStructureMember == null)
             return;
             return;
         
         
         ActiveDocument?.Operations.RotateImage(RotationAngle.D90, ActiveDocument.GetSelectedMembers());
         ActiveDocument?.Operations.RotateImage(RotationAngle.D90, ActiveDocument.GetSelectedMembers());
@@ -106,22 +112,36 @@ internal class DocumentManagerViewModel : SubViewModel<ViewModelMain>
     [Command.Basic("PixiEditor.Document.Rotate180Deg", "Rotate Image 180 deg", "Rotate Image 180 deg", CanExecute = "PixiEditor.HasDocument")]
     [Command.Basic("PixiEditor.Document.Rotate180Deg", "Rotate Image 180 deg", "Rotate Image 180 deg", CanExecute = "PixiEditor.HasDocument")]
     public void Rotate180Deg()
     public void Rotate180Deg()
     {
     {
-        if (ActiveDocument == null)
+        ActiveDocument?.Operations.RotateImage(RotationAngle.D180);
+    }
+    
+    [Command.Basic("PixiEditor.Document.Rotate180DegLayers", "Rotate Selected Layers 180 deg", "Rotate Selected Layers 180 deg", CanExecute = "PixiEditor.HasDocument")]
+    public void Rotate180DegLayers()
+    {
+        if (ActiveDocument?.SelectedStructureMember == null)
             return;
             return;
         
         
         ActiveDocument?.Operations.RotateImage(RotationAngle.D180, ActiveDocument.GetSelectedMembers());
         ActiveDocument?.Operations.RotateImage(RotationAngle.D180, ActiveDocument.GetSelectedMembers());
     }
     }
-    
+
     [Command.Basic("PixiEditor.Document.Rotate270Deg", "Rotate Image 270 deg", "Rotate Image 270 deg", CanExecute = "PixiEditor.HasDocument")]
     [Command.Basic("PixiEditor.Document.Rotate270Deg", "Rotate Image 270 deg", "Rotate Image 270 deg", CanExecute = "PixiEditor.HasDocument")]
     public void Rotate270Deg()
     public void Rotate270Deg()
     {
     {
         if (ActiveDocument == null)
         if (ActiveDocument == null)
             return;
             return;
         
         
+        ActiveDocument?.Operations.RotateImage(RotationAngle.D270);
+    }
+    
+    [Command.Basic("PixiEditor.Document.Rotate270DegLayers", "Rotate Selected Layers 270 deg", "Rotate Selected Layers 270 deg", CanExecute = "PixiEditor.HasDocument")]
+    public void Rotate270DegLayers()
+    {
+        if (ActiveDocument?.SelectedStructureMember == null)
+            return;
+        
         ActiveDocument?.Operations.RotateImage(RotationAngle.D270, ActiveDocument.GetSelectedMembers());
         ActiveDocument?.Operations.RotateImage(RotationAngle.D270, ActiveDocument.GetSelectedMembers());
     }
     }
 
 
-
     [Command.Basic("PixiEditor.Document.ToggleVerticalSymmetryAxis", "Toggle vertical symmetry axis", "Toggle vertical symmetry axis", CanExecute = "PixiEditor.HasDocument", IconPath = "SymmetryVertical.png")]
     [Command.Basic("PixiEditor.Document.ToggleVerticalSymmetryAxis", "Toggle vertical symmetry axis", "Toggle vertical symmetry axis", CanExecute = "PixiEditor.HasDocument", IconPath = "SymmetryVertical.png")]
     public void ToggleVerticalSymmetryAxis()
     public void ToggleVerticalSymmetryAxis()
     {
     {

+ 12 - 2
src/PixiEditor/Views/MainWindow.xaml

@@ -257,7 +257,7 @@
                             IsChecked="{Binding DocumentManagerSubViewModel.ActiveDocument.VerticalSymmetryAxisEnabledBindable}"
                             IsChecked="{Binding DocumentManagerSubViewModel.ActiveDocument.VerticalSymmetryAxisEnabledBindable}"
                             Header="_Vertical Line Symmetry"/>
                             Header="_Vertical Line Symmetry"/>
                         <Separator/>
                         <Separator/>
-                        <MenuItem Header="_Rotate">
+                        <MenuItem Header="_Rotation">
                             <MenuItem Header="Flip Image _Horizontally" cmds:Menu.Command="PixiEditor.Document.FlipImageHorizontal"/>
                             <MenuItem Header="Flip Image _Horizontally" cmds:Menu.Command="PixiEditor.Document.FlipImageHorizontal"/>
                             <MenuItem Header="Flip Image _Vertically" cmds:Menu.Command="PixiEditor.Document.FlipImageVertical"/>
                             <MenuItem Header="Flip Image _Vertically" cmds:Menu.Command="PixiEditor.Document.FlipImageVertical"/>
                             <MenuItem Header="Flip Selected Layers _Horizontally" cmds:Menu.Command="PixiEditor.Document.FlipLayersHorizontal"/>
                             <MenuItem Header="Flip Selected Layers _Horizontally" cmds:Menu.Command="PixiEditor.Document.FlipLayersHorizontal"/>
@@ -267,6 +267,11 @@
                             <MenuItem Header="Rotate Image 90&#186;" cmds:Menu.Command="PixiEditor.Document.Rotate90Deg"/>
                             <MenuItem Header="Rotate Image 90&#186;" cmds:Menu.Command="PixiEditor.Document.Rotate90Deg"/>
                             <MenuItem Header="Rotate Image 180&#186;" cmds:Menu.Command="PixiEditor.Document.Rotate180Deg"/>
                             <MenuItem Header="Rotate Image 180&#186;" cmds:Menu.Command="PixiEditor.Document.Rotate180Deg"/>
                             <MenuItem Header="Rotate Image 270&#186;" cmds:Menu.Command="PixiEditor.Document.Rotate270Deg"/>
                             <MenuItem Header="Rotate Image 270&#186;" cmds:Menu.Command="PixiEditor.Document.Rotate270Deg"/>
+                            
+                            <Separator/>
+                            <MenuItem Header="Rotate Selected Layers 90&#186;" cmds:Menu.Command="PixiEditor.Document.Rotate90DegLayers"/>
+                            <MenuItem Header="Rotate Selected Layers 180&#186;" cmds:Menu.Command="PixiEditor.Document.Rotate180DegLayers"/>
+                            <MenuItem Header="Rotate Selected Layers 270&#186;" cmds:Menu.Command="PixiEditor.Document.Rotate270DegLayers"/>
                         </MenuItem>
                         </MenuItem>
                     </MenuItem>
                     </MenuItem>
                     <MenuItem
                     <MenuItem
@@ -549,7 +554,7 @@
                                                     <ContextMenu.Template>
                                                     <ContextMenu.Template>
                                                         <ControlTemplate>
                                                         <ControlTemplate>
                                                             <Border Background="{StaticResource AccentColor}" BorderBrush="Black" BorderThickness="1" CornerRadius="5">
                                                             <Border Background="{StaticResource AccentColor}" BorderBrush="Black" BorderThickness="1" CornerRadius="5">
-                                                                <Grid Height="165">
+                                                                <Grid Height="235">
                                                                     <Grid.ColumnDefinitions>
                                                                     <Grid.ColumnDefinitions>
                                                                         <ColumnDefinition Width="{Binding Palette, Converter={converters:PaletteItemsToWidthConverter}}"/>
                                                                         <ColumnDefinition Width="{Binding Palette, Converter={converters:PaletteItemsToWidthConverter}}"/>
                                                                         <ColumnDefinition />
                                                                         <ColumnDefinition />
@@ -572,8 +577,13 @@
                                                                             <MenuItem
                                                                             <MenuItem
 																		Header="_Paste"
 																		Header="_Paste"
 																		cmds:ContextMenu.Command="PixiEditor.Clipboard.Paste" />
 																		cmds:ContextMenu.Command="PixiEditor.Clipboard.Paste" />
+                                                                            <Separator />
                                                                             <MenuItem Header="Flip _Horizontally" cmds:Menu.Command="PixiEditor.Document.FlipLayersHorizontal"/>
                                                                             <MenuItem Header="Flip _Horizontally" cmds:Menu.Command="PixiEditor.Document.FlipLayersHorizontal"/>
                                                                             <MenuItem Header="Flip _Vertically" cmds:Menu.Command="PixiEditor.Document.FlipLayersVertical"/>
                                                                             <MenuItem Header="Flip _Vertically" cmds:Menu.Command="PixiEditor.Document.FlipLayersVertical"/>
+                                                                            <Separator />
+                                                                            <MenuItem Header="Rotate Right" cmds:Menu.Command="PixiEditor.Document.Rotate90DegLayers"/>
+                                                                            <MenuItem Header="Rotate 180&#186;" cmds:Menu.Command="PixiEditor.Document.Rotate180DegLayers"/>
+                                                                            <MenuItem Header="Rotate Left" cmds:Menu.Command="PixiEditor.Document.Rotate270DegLayers"/>
                                                                         </StackPanel>
                                                                         </StackPanel>
                                                                     </Border>
                                                                     </Border>
                                                                     <ScrollViewer Margin="5" Grid.Column="0" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">
                                                                     <ScrollViewer Margin="5" Grid.Column="0" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto">