Browse Source

Merge pull request #742 from PixiEditor/fixes/27.01.2025

Fixes/27.01.2025
Krzysztof Krysiński 7 months ago
parent
commit
6210511846

+ 1 - 1
src/Drawie

@@ -1 +1 @@
-Subproject commit 50007fb51e74bde8cfbb1a30f8d040ef0804e43e
+Subproject commit 7fe29ad3ea438046a66b300e85608af3c8544309

+ 1 - 1
src/PixiDocks

@@ -1 +1 @@
-Subproject commit b83ba013241e6d6b6d280eea8836e49c5c6b9f81
+Subproject commit 47107d7dc284e04ed92e4c470a6ed2f972e5d9cd

+ 11 - 1
src/PixiEditor.ChangeableDocument/Changes/Root/FlipImage_Change.cs

@@ -6,6 +6,7 @@ using Drawie.Backend.Core;
 using Drawie.Backend.Core.Numerics;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Numerics;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using BlendMode = PixiEditor.ChangeableDocument.Enums.BlendMode;
 
 namespace PixiEditor.ChangeableDocument.Changes.Root;
@@ -111,7 +112,16 @@ internal sealed class FlipImage_Change : Change
                         new LayerImageArea_ChangeInfo(member.Id, image.FindAffectedArea()));
                     image.CommitChanges();
                 }
-                // TODO: Add support for non-raster layers
+                else if (member is ITransformableObject transformableObject)
+                {
+                    RectD? tightBounds = member.GetTightBounds(frame);
+                    if(tightBounds == null) return;
+                    transformableObject.TransformationMatrix = transformableObject.TransformationMatrix.PostConcat(
+                        Matrix3X3.CreateScale(
+                            flipType == FlipType.Horizontal ? -1 : 1,
+                            flipType == FlipType.Vertical ? -1 : 1, 
+                            (float)tightBounds.Value.Center.X, (float)tightBounds.Value.Center.Y));
+                }
 
                 if (member.EmbeddedMask is not null)
                 {

+ 9 - 2
src/PixiEditor.ChangeableDocument/Changes/Root/RotateImage_Change.cs

@@ -6,6 +6,7 @@ using Drawie.Backend.Core;
 using Drawie.Backend.Core.Numerics;
 using Drawie.Backend.Core.Surfaces.PaintImpl;
 using Drawie.Numerics;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
 using BlendMode = PixiEditor.ChangeableDocument.Enums.BlendMode;
 
 namespace PixiEditor.ChangeableDocument.Changes.Root;
@@ -172,8 +173,14 @@ internal sealed class RotateImage_Change : Change
                         });
                     }
                 }
-
-                // TODO: Add support for different Layer types
+                else if (member is ITransformableObject transformableObject)
+                {
+                    RectD? tightBounds = member.GetTightBounds(frame.Value);
+                    transformableObject.TransformationMatrix = transformableObject.TransformationMatrix.PostConcat(
+                        Matrix3X3.CreateRotation(
+                            RotationAngleToRadians(rotation),
+                            (float?)tightBounds?.Center.X ?? 0, (float?)tightBounds?.Center.Y ?? 0));
+                }
 
                 if (member.EmbeddedMask is null)
                     return;

+ 67 - 0
src/PixiEditor.ChangeableDocument/Changes/Vectors/ConvertToCurve_Change.cs

@@ -0,0 +1,67 @@
+using ChunkyImageLib.Operations;
+using Drawie.Numerics;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
+using PixiEditor.ChangeableDocument.ChangeInfos.Vectors;
+
+namespace PixiEditor.ChangeableDocument.Changes.Vectors;
+
+internal class ConvertToCurve_Change : Change
+{
+    public readonly Guid memberId;
+
+    private ShapeVectorData originalData;
+
+    [GenerateMakeChangeAction]
+    public ConvertToCurve_Change(Guid memberId)
+    {
+        this.memberId = memberId;
+    }
+
+    public override bool InitializeAndValidate(Document target)
+    {
+        if (target.TryFindNode(memberId, out VectorLayerNode? node))
+        {
+            return node.ShapeData != null && node.ShapeData is not PathVectorData;
+        }
+
+        return false;
+    }
+
+    public override OneOf<None, IChangeInfo, List<IChangeInfo>> Apply(Document target, bool firstApply,
+        out bool ignoreInUndo)
+    {
+        VectorLayerNode node = target.FindNodeOrThrow<VectorLayerNode>(memberId);
+        originalData = node.ShapeData;
+
+        node.ShapeData = new PathVectorData(originalData.ToPath())
+        {
+            Fill = originalData.Fill,
+            FillColor = originalData.FillColor,
+            StrokeColor = originalData.StrokeColor,
+            StrokeWidth = originalData.StrokeWidth,
+            TransformationMatrix = originalData.TransformationMatrix
+        };
+
+        ignoreInUndo = false;
+
+        var aabb = node.ShapeData.TransformedVisualAABB;
+        var affected = new AffectedArea(OperationHelper.FindChunksTouchingRectangle(
+            (RectI)aabb, ChunkyImage.FullChunkSize));
+
+        return new VectorShape_ChangeInfo(memberId, affected);
+    }
+
+    public override OneOf<None, IChangeInfo, List<IChangeInfo>> Revert(Document target)
+    {
+        VectorLayerNode node = target.FindNodeOrThrow<VectorLayerNode>(memberId);
+        node.ShapeData = originalData;
+
+        var aabb = node.ShapeData.TransformedVisualAABB;
+        var affected = new AffectedArea(OperationHelper.FindChunksTouchingRectangle(
+            (RectI)aabb, ChunkyImage.FullChunkSize));
+
+        return new VectorShape_ChangeInfo(memberId, affected);
+    }
+}

+ 3 - 1
src/PixiEditor/Data/Localization/Languages/en.json

@@ -834,5 +834,7 @@
   "BLUR_FILTER_NODE": "Gaussian Blur Filter",
   "LENGTH": "Length",
   "GREATER_THAN_OR_EQUAL": "Greater than or equal",
-  "COLOR_NODE": "Color"
+  "COLOR_NODE": "Color",
+  "CONVERT_TO_CURVE": "Convert to curve",
+  "CONVERT_TO_CURVE_DESCRIPTIVE": "Convert selected vector layer to a curve/path"
 }

+ 10 - 0
src/PixiEditor/Models/DocumentModels/Public/DocumentOperationsModule.cs

@@ -906,4 +906,14 @@ internal class DocumentOperationsModule : IDocumentOperations
 
         Internals.ActionAccumulator.AddFinishedActions(actions.ToArray());
     }
+
+    public void ConvertToCurve(Guid memberId)
+    {
+        if (Internals.ChangeController.IsBlockingChangeActive)
+            return;
+
+        Internals.ChangeController.TryStopActiveExecutor();
+
+        Internals.ActionAccumulator.AddFinishedActions(new ConvertToCurve_Action(memberId));
+    }
 }

+ 1 - 1
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/LineExecutor.cs

@@ -97,7 +97,7 @@ internal abstract class LineExecutor<T> : SimpleShapeToolExecutor where T : ILin
 
             ActiveMode = ShapeToolMode.Transform;
 
-            document.LineToolOverlayHandler.Show(data.Start, data.End, false, AddToUndo);
+            document.LineToolOverlayHandler.Show(data.TransformedStart, data.TransformedEnd, false, AddToUndo);
         }
         else
         {

+ 2 - 2
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorLineToolExecutor.cs

@@ -24,8 +24,8 @@ internal class VectorLineToolExecutor : LineExecutor<IVectorLineToolHandler>
         if (data is null)
             return false;
 
-        startPoint = data.Start;
-        endPoint = data.End;
+        startPoint = data.TransformedStart;
+        endPoint = data.TransformedEnd;
 
         return true;
     }

+ 8 - 2
src/PixiEditor/ViewModels/Document/TransformOverlays/LineToolOverlayViewModel.cs

@@ -18,7 +18,7 @@ internal class LineToolOverlayViewModel : ObservableObject, ILineOverlayHandler
         get => lineStart;
         set
         {
-            if (SetProperty(ref lineStart, value))
+            if (SetProperty(ref lineStart, value) && isInitialized)
                 LineMoved?.Invoke(this, (lineStart, lineEnd));
         }
     }
@@ -30,7 +30,7 @@ internal class LineToolOverlayViewModel : ObservableObject, ILineOverlayHandler
         get => lineEnd;
         set
         {
-            if (SetProperty(ref lineEnd, value))
+            if (SetProperty(ref lineEnd, value) && isInitialized)
                 LineMoved?.Invoke(this, (lineStart, lineEnd));
         }
     }
@@ -80,12 +80,15 @@ internal class LineToolOverlayViewModel : ObservableObject, ILineOverlayHandler
         set => SetProperty(ref showApplyButton, value);
     }
 
+    private bool isInitialized;
+    
     public LineToolOverlayViewModel()
     {
     }
 
     public void Show(VecD lineStart, VecD endPos, bool showApplyButton, Action<(VecD, VecD)> addToUndo)
     {
+        isInitialized = false;
         LineStart = lineStart;
         LineEnd = endPos; 
         IsEnabled = true;
@@ -93,6 +96,8 @@ internal class LineToolOverlayViewModel : ObservableObject, ILineOverlayHandler
         ShowHandles = true;
         IsSizeBoxEnabled = false;
         AddToUndoCommand = new RelayCommand(() => addToUndo((LineStart, LineEnd)));
+        
+        isInitialized = true;
     }
 
     public void Hide()
@@ -100,6 +105,7 @@ internal class LineToolOverlayViewModel : ObservableObject, ILineOverlayHandler
         IsEnabled = false;
         ShowApplyButton = false;
         IsSizeBoxEnabled = false;
+        isInitialized = false;
     }
 
     public bool Nudge(VecD distance)

+ 19 - 0
src/PixiEditor/ViewModels/SubViewModels/LayersViewModel.cs

@@ -206,6 +206,13 @@ internal class LayersViewModel : SubViewModel<ViewModelMain>
         var member = Owner.DocumentManagerSubViewModel.ActiveDocument?.SelectedStructureMember;
         return member is ILayerHandler && member is not IRasterLayerHandler;
     }
+    
+    [Evaluator.CanExecute("PixiEditor.Layer.SelectedMemberIsVectorLayer")]
+    public bool SelectedMemberIsVectorLayer(object property)
+    {
+        var member = Owner.DocumentManagerSubViewModel.ActiveDocument?.SelectedStructureMember;
+        return member is IVectorLayerHandler;
+    }
 
     private bool HasSelectedMember(bool above)
     {
@@ -500,6 +507,18 @@ internal class LayersViewModel : SubViewModel<ViewModelMain>
 
         doc!.Operations.Rasterize(member.Id);
     }
+    
+    [Command.Basic("PixiEditor.Layer.ConvertToCurve", "CONVERT_TO_CURVE", "CONVERT_TO_CURVE_DESCRIPTIVE",
+        CanExecute = "PixiEditor.Layer.SelectedMemberIsVectorLayer")]
+    public void ConvertActiveLayerToCurve()
+    {
+        var doc = Owner.DocumentManagerSubViewModel.ActiveDocument;
+        var member = doc?.SelectedStructureMember;
+        if (member is null)
+            return;
+
+        doc!.Operations.ConvertToCurve(member.Id);
+    }
 
     [Evaluator.Icon("PixiEditor.Layer.ToggleReferenceLayerTopMostIcon")]
     public IImage GetAboveEverythingReferenceLayerIcon()

+ 1 - 0
src/PixiEditor/Views/Layers/LayerControl.axaml

@@ -200,6 +200,7 @@
                 <MenuItem ui:Translator.Key="MERGE_WITH_BELOW" Command="{xaml:Command PixiEditor.Layer.MergeWithBelow}" />
                 <Separator />
                 <MenuItem ui:Translator.Key="RASTERIZE" Command="{xaml:Command PixiEditor.Layer.Rasterize}" />
+                <MenuItem ui:Translator.Key="CONVERT_TO_CURVE" Command="{xaml:Command PixiEditor.Layer.ConvertToCurve}" />
             </ContextMenu>
         </Border.ContextMenu>
     </Border>

+ 1 - 1
src/PixiEditor/Views/Main/ViewportControls/Viewport.axaml

@@ -181,7 +181,7 @@
         <rendering:Scene
             Focusable="True" Name="scene"
             ZIndex="1"
-            SceneRenderer="{Binding Source={viewModels:MainVM DocumentManagerSVM}, Path=ActiveDocument.SceneRenderer}"
+            SceneRenderer="{Binding Document.SceneRenderer, ElementName=vpUc, Mode=OneWay}"
             Document="{Binding Document, ElementName=vpUc, Mode=OneWay}"
             UseTouchGestures="{Binding UseTouchGestures, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=OneWay}"
             Center="{Binding Center, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=OneWayToSource}"