Browse Source

Transform selection path

Equbuxu 3 years ago
parent
commit
eac6ad75c8

+ 8 - 1
src/ChunkyImageLib/DataHolders/VecD.cs

@@ -177,7 +177,14 @@ public struct VecD
     {
         return !(a.X == b.X && a.Y == b.Y);
     }
-
+    public static implicit operator VecD(SKPoint point)
+    {
+        return new VecD(point.X, point.Y);
+    }
+    public static implicit operator VecD(SKSize size)
+    {
+        return new VecD(size.Width, size.Height);
+    }
     public static explicit operator VecI(VecD vec)
     {
         return new VecI((int)vec.X, (int)vec.Y);

+ 71 - 0
src/PixiEditor.ChangeableDocument/Changes/Selection/TransformSelectionPath_UpdateableChange.cs

@@ -0,0 +1,71 @@
+using ChunkyImageLib.Operations;
+using SkiaSharp;
+
+namespace PixiEditor.ChangeableDocument.Changes.Selection;
+internal class TransformSelectionPath_UpdateableChange : UpdateableChange
+{
+    private SKPath? originalPath;
+    private ShapeCorners originalCorners;
+    private ShapeCorners newCorners;
+
+    [GenerateUpdateableChangeActions]
+    public TransformSelectionPath_UpdateableChange(ShapeCorners corners)
+    {
+        this.newCorners = corners;
+    }
+
+    [UpdateChangeMethod]
+    public void Update(ShapeCorners corners)
+    {
+        this.newCorners = corners;
+    }
+
+    public override OneOf<Success, Error> InitializeAndValidate(Document target)
+    {
+        if (target.Selection.SelectionPath.IsEmpty)
+            return new Error();
+        originalPath = new(target.Selection.SelectionPath);
+        var bounds = originalPath.TightBounds;
+        originalCorners = new(bounds.Location, bounds.Size);
+        return new Success();
+    }
+
+    private Selection_ChangeInfo CommonApply(Document target)
+    {
+        SKPath newPath = new(originalPath);
+
+        var matrix = SKMatrix.CreateTranslation((float)-originalCorners.TopLeft.X, (float)-originalCorners.TopLeft.Y).PostConcat(
+            OperationHelper.CreateMatrixFromPoints(newCorners, originalCorners.RectSize));
+        newPath.Transform(matrix);
+
+        var toDispose = target.Selection.SelectionPath;
+        target.Selection.SelectionPath = newPath;
+        toDispose.Dispose();
+
+        return new Selection_ChangeInfo();
+    }
+
+    public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target, out bool ignoreInUndo)
+    {
+        ignoreInUndo = false;
+        return CommonApply(target);
+    }
+
+    public override OneOf<None, IChangeInfo, List<IChangeInfo>> ApplyTemporarily(Document target)
+    {
+        return CommonApply(target);
+    }
+
+    public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
+    {
+        var toDispose = target.Selection.SelectionPath;
+        target.Selection.SelectionPath = new SKPath(originalPath);
+        toDispose.Dispose();
+        return new Selection_ChangeInfo();
+    }
+
+    public override void Dispose()
+    {
+        originalPath?.Dispose();
+    }
+}

+ 29 - 1
src/PixiEditorPrototype/ViewModels/DocumentViewModel.cs

@@ -67,6 +67,7 @@ internal class DocumentViewModel : INotifyPropertyChanged
     public RelayCommand? DragSymmetryCommand { get; }
     public RelayCommand? EndDragSymmetryCommand { get; }
     public RelayCommand? ClipToMemberBelowCommand { get; }
+    public RelayCommand? TransformSelectionPathCommand { get; }
 
     public int Width => Helpers.Tracker.Document.Size.X;
     public int Height => Helpers.Tracker.Document.Size.Y;
@@ -121,6 +122,7 @@ internal class DocumentViewModel : INotifyPropertyChanged
         EndDragSymmetryCommand = new RelayCommand(EndDragSymmetry);
         ClipToMemberBelowCommand = new RelayCommand(ClipToMemberBelow);
         ApplyMaskCommand = new RelayCommand(ApplyMask);
+        TransformSelectionPathCommand = new RelayCommand(TransformSelectionPath);
 
         foreach (var bitmap in Bitmaps)
         {
@@ -156,6 +158,9 @@ internal class DocumentViewModel : INotifyPropertyChanged
     private bool transformingRectangle = false;
     private bool shiftingLayer = false;
 
+    private bool transformingSelectionPath = false;
+    ShapeCorners initialSelectionCorners = new();
+
     private bool pastingImage = false;
     private Surface? pastedImage;
 
@@ -219,6 +224,19 @@ internal class DocumentViewModel : INotifyPropertyChanged
         Helpers.ActionAccumulator.AddFinishedActions(new EndShiftLayer_Action());
     }
 
+    private void TransformSelectionPath(object? arg)
+    {
+        var path = SelectionPath;
+        if (path.IsEmpty)
+            return;
+        updateableChangeActive = true;
+        transformingSelectionPath = true;
+        var bounds = path.TightBounds;
+        initialSelectionCorners = new ShapeCorners(bounds.Location, bounds.Size);
+        TransformViewModel.ShowShapeTransform(initialSelectionCorners);
+        Helpers.ActionAccumulator.AddActions(new TransformSelectionPath_Action(initialSelectionCorners));
+    }
+
     public void StartUpdateLassoSelection(VecI startPos, SelectionMode mode)
     {
         updateableChangeActive = true;
@@ -237,7 +255,7 @@ internal class DocumentViewModel : INotifyPropertyChanged
 
     public void ApplyTransform(object? param)
     {
-        if (!transformingRectangle && !pastingImage)
+        if (!transformingRectangle && !pastingImage && !transformingSelectionPath)
             return;
 
         if (transformingRectangle)
@@ -254,6 +272,12 @@ internal class DocumentViewModel : INotifyPropertyChanged
             pastedImage?.Dispose();
             pastedImage = null;
         }
+        else if (transformingSelectionPath)
+        {
+            transformingSelectionPath = false;
+            TransformViewModel.HideTransform();
+            Helpers.ActionAccumulator.AddFinishedActions(new EndTransformSelectionPath_Action());
+        }
         updateableChangeActive = false;
     }
 
@@ -292,6 +316,10 @@ internal class DocumentViewModel : INotifyPropertyChanged
                 return;
             Helpers.ActionAccumulator.AddActions(new PasteImage_Action(pastedImage, newCorners, SelectedStructureMember.GuidValue, false));
         }
+        else if (transformingSelectionPath)
+        {
+            Helpers.ActionAccumulator.AddActions(new TransformSelectionPath_Action(newCorners));
+        }
     }
 
     public void FloodFill(VecI pos, SKColor color)

+ 2 - 1
src/PixiEditorPrototype/Views/MainWindow.xaml

@@ -183,7 +183,8 @@
                     <Button Width="50" Margin="5" Command="{Binding ActiveDocument.UndoCommand}">Undo</Button>
                     <Button Width="50" Margin="5" Command="{Binding ActiveDocument.RedoCommand}">Redo</Button>
                     <Button Width="100" Margin="5" Command="{Binding ActiveDocument.ClearSelectionCommand}">Clear selection</Button>
-                    <ComboBox Width="50" Height="20" Margin="5" SelectedIndex="0" x:Name="selectionModeComboBox">
+                    <Button Width="110" Margin="5" Command="{Binding ActiveDocument.TransformSelectionPathCommand}">Transform sel. path</Button>
+                    <ComboBox Width="70" Height="20" Margin="5" SelectedIndex="0" x:Name="selectionModeComboBox">
                         <ComboBoxItem Tag="{x:Static chen:SelectionMode.New}">New</ComboBoxItem>
                         <ComboBoxItem Tag="{x:Static chen:SelectionMode.Add}">Add</ComboBoxItem>
                         <ComboBoxItem Tag="{x:Static chen:SelectionMode.Subtract}">Subtract</ComboBoxItem>

+ 1 - 1
src/README.md

@@ -104,7 +104,7 @@ Decouples the state of a document from the UI.
         - [ ] Magic wand
         - [x] Lasso
         - [x] Shift layer image
-        - [ ] Move/Rotate selection
+        - [ ] Move/Rotate selection path
         - [ ] Transform selected area
         - [ ] Fill selection (fill with transparent = delete)
         - [x] Clip to selection