flabbet 9 месяцев назад
Родитель
Сommit
4c337c748a

+ 89 - 6
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorPathToolExecutor.cs

@@ -2,10 +2,14 @@
 using ChunkyImageLib.DataHolders;
 using Drawie.Backend.Core.Vector;
 using Drawie.Numerics;
+using PixiEditor.ChangeableDocument;
 using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes.Shapes.Data;
 using PixiEditor.Models.Handlers;
 using PixiEditor.Models.Handlers.Tools;
 using PixiEditor.ChangeableDocument.Actions.Generated;
+using PixiEditor.ChangeableDocument.Changeables.Animations;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Interfaces.Shapes;
+using PixiEditor.ChangeableDocument.Changeables.Graph.Nodes;
 using PixiEditor.Helpers.Extensions;
 using PixiEditor.Models.Controllers.InputDevice;
 using PixiEditor.Models.DocumentModels.UpdateableChangeExecutors.Features;
@@ -22,6 +26,7 @@ internal class VectorPathToolExecutor : UpdateableChangeExecutor, IPathExecutorF
     private VectorPath startingPath;
     private IVectorPathToolHandler vectorPathToolHandler;
     private IBasicShapeToolbar toolbar;
+    private IColorsHandler colorHandler;
 
     public override ExecutorType Type => ExecutorType.ToolLinked;
 
@@ -30,6 +35,8 @@ internal class VectorPathToolExecutor : UpdateableChangeExecutor, IPathExecutorF
 
     public override bool BlocksOtherActions => false;
 
+    private bool mouseDown;
+
     public override ExecutionState Start()
     {
         vectorPathToolHandler = GetHandler<IVectorPathToolHandler>();
@@ -42,6 +49,7 @@ internal class VectorPathToolExecutor : UpdateableChangeExecutor, IPathExecutorF
         }
 
         toolbar = (IBasicShapeToolbar)vectorPathToolHandler.Toolbar;
+        colorHandler = GetHandler<IColorsHandler>();
 
         if (member is IVectorLayerHandler vectorLayerHandler)
         {
@@ -62,24 +70,32 @@ internal class VectorPathToolExecutor : UpdateableChangeExecutor, IPathExecutorF
                 return ExecutionState.Error;
             }
 
+            document.PathOverlayHandler.Show(startingPath, false);
             if (controller.LeftMousePressed)
             {
+                var snapped =
+                    document.SnappingHandler.SnappingController.GetSnapPoint(controller.LastPrecisePosition, out _,
+                        out _);
                 if (wasNull)
                 {
-                    startingPath.MoveTo((VecF)controller.LastPrecisePosition);
+                    startingPath.MoveTo((VecF)snapped);
                 }
                 else
                 {
-                    startingPath.LineTo((VecF)controller.LastPrecisePosition);
+                    startingPath.LineTo((VecF)snapped);
+                }
+
+                if (toolbar.SyncWithPrimaryColor)
+                {
+                    toolbar.StrokeColor = colorHandler.PrimaryColor.ToColor();
+                    toolbar.FillColor = colorHandler.PrimaryColor.ToColor();
                 }
 
                 //below forces undo before starting new path
                 internals.ActionAccumulator.AddFinishedActions(new EndSetShapeGeometry_Action());
-                
+
                 internals.ActionAccumulator.AddActions(new SetShapeGeometry_Action(member.Id, ConstructShapeData()));
             }
-
-            document.PathOverlayHandler.Show(startingPath, false);
         }
         else
         {
@@ -90,19 +106,68 @@ internal class VectorPathToolExecutor : UpdateableChangeExecutor, IPathExecutorF
         return ExecutionState.Success;
     }
 
+    public override void OnPrecisePositionChange(VecD pos)
+    {
+        if (mouseDown)
+        {
+            return;
+        }
+
+        VecD mouseSnap =
+            document.SnappingHandler.SnappingController.GetSnapPoint(pos, out string snapXAxis,
+                out string snapYAxis);
+        HighlightSnapping(snapXAxis, snapYAxis);
+
+        if (!string.IsNullOrEmpty(snapXAxis) || !string.IsNullOrEmpty(snapYAxis))
+        {
+            document.SnappingHandler.SnappingController.HighlightedPoint = mouseSnap;
+        }
+        else
+        {
+            document.SnappingHandler.SnappingController.HighlightedPoint = null;
+        }
+    }
+
     public override void OnLeftMouseButtonDown(MouseOnCanvasEventArgs args)
     {
         if (startingPath.IsClosed)
         {
+            if (NeedsNewLayer(document.SelectedStructureMember, document.AnimationHandler.ActiveFrameTime))
+            {
+                Guid? created =
+                    document.Operations.CreateStructureMember(typeof(VectorLayerNode), ActionSource.Automated);
+
+                if (created is null) return;
+
+                document.Operations.SetSelectedMember(created.Value);
+            }
+
             return;
         }
 
-        startingPath.LineTo((VecF)args.PositionOnCanvas);
+        VecD mouseSnap =
+            document.SnappingHandler.SnappingController.GetSnapPoint(args.PositionOnCanvas, out _,
+                out _);
+
+        if (startingPath.Points.Count > 0 && startingPath.Points[0] == (VecF)mouseSnap)
+        {
+            startingPath.Close();
+        }
+        else
+        {
+            startingPath.LineTo((VecF)mouseSnap);
+        }
+
         PathVectorData vectorData = ConstructShapeData();
 
         internals.ActionAccumulator.AddActions(new SetShapeGeometry_Action(member.Id, vectorData));
+        mouseDown = true;
     }
 
+    public override void OnLeftMouseButtonUp(VecD pos)
+    {
+        mouseDown = false;
+    }
 
     public override void OnColorChanged(Color color, bool primary)
     {
@@ -164,4 +229,22 @@ internal class VectorPathToolExecutor : UpdateableChangeExecutor, IPathExecutorF
     {
         document.PathOverlayHandler.Redo();
     }
+
+    protected void HighlightSnapping(string? snapX, string? snapY)
+    {
+        document!.SnappingHandler.SnappingController.HighlightedXAxis = snapX;
+        document!.SnappingHandler.SnappingController.HighlightedYAxis = snapY;
+        document.SnappingHandler.SnappingController.HighlightedPoint = null;
+    }
+
+    private bool NeedsNewLayer(IStructureMemberHandler? member, KeyFrameTime frameTime)
+    {
+        var shapeData = (member as IVectorLayerHandler).GetShapeData(frameTime);
+        if (shapeData is null)
+        {
+            return false;
+        }
+
+        return shapeData is not IReadOnlyPathData pathData || pathData.Path.IsClosed;
+    }
 }

+ 3 - 1
src/PixiEditor/Models/Handlers/IDocumentOperations.cs

@@ -1,4 +1,5 @@
-using PixiEditor.Models.Layers;
+using PixiEditor.ChangeableDocument;
+using PixiEditor.Models.Layers;
 
 namespace PixiEditor.Models.Handlers;
 
@@ -10,4 +11,5 @@ internal interface IDocumentOperations
     public void MoveStructureMember(Guid memberGuidValue, Guid target, StructureMemberPlacement placement);
     public void SetSelectedMember(Guid memberId);
     public void ClearSoftSelectedMembers();
+    public Guid? CreateStructureMember(Type type, ActionSource source, string? name = null);
 }

+ 15 - 32
src/PixiEditor/ViewModels/Tools/Tools/VectorPathToolViewModel.cs

@@ -18,7 +18,9 @@ internal class VectorPathToolViewModel : ShapeTool, IVectorPathToolHandler
     public override Type LayerTypeToCreateOnEmptyUse { get; } = typeof(VectorLayerNode);
     public override LocalizedString Tooltip => new LocalizedString("PATH_TOOL_TOOLTIP", Shortcut);
 
-    public override bool StopsLinkedToolOnUse => false; 
+    public override bool StopsLinkedToolOnUse => false;
+
+    private bool isActivated;
 
     public VectorPathToolViewModel()
     {
@@ -33,37 +35,22 @@ internal class VectorPathToolViewModel : ShapeTool, IVectorPathToolHandler
     {
         var doc =
             ViewModelMain.Current?.DocumentManagerSubViewModel.ActiveDocument;
-        
-        if (doc is null) return;
-        
-        if(NeedsNewLayer(doc.SelectedStructureMember, doc.AnimationDataViewModel.ActiveFrameTime))
-        {
-            Guid? created = doc.Operations.CreateStructureMember(typeof(VectorLayerNode), ActionSource.Automated);
-            
-            if (created is null) return;
 
-            doc.Operations.SetSelectedMember(created.Value);
-            
-            doc.Operations.InvokeCustomAction(() =>
-            {
-                OnDeselecting(false);
-                OnSelected(false);
-            });
-        }
-        else
+        if (doc is null || isActivated) return;
+        
+        if (!doc.PathOverlayViewModel.IsActive)
         {
-            if (!doc.PathOverlayViewModel.IsActive)
-            {
-                doc?.Tools.UseVectorPathTool();
-            }
+            doc?.Tools.UseVectorPathTool();
+            isActivated = true;
         }
     }
-    
+
     public override void OnSelected(bool restoring)
     {
         if (restoring) return;
 
         ViewModelMain.Current?.DocumentManagerSubViewModel.ActiveDocument?.Tools.UseVectorPathTool();
+        isActivated = true;
     }
 
     public override void OnDeselecting(bool transient)
@@ -71,17 +58,13 @@ internal class VectorPathToolViewModel : ShapeTool, IVectorPathToolHandler
         if (!transient)
         {
             ViewModelMain.Current.DocumentManagerSubViewModel.ActiveDocument?.Operations.TryStopToolLinkedExecutor();
+            isActivated = false;
         }
     }
-    
-    private bool NeedsNewLayer(IStructureMemberHandler? member, KeyFrameTime frameTime)
+
+    protected override void OnSelectedLayersChanged(IStructureMemberHandler[] layers)
     {
-        var shapeData = (member as IVectorLayerHandler).GetShapeData(frameTime);
-        if (shapeData is null)
-        {
-            return false;
-        }
-        
-        return shapeData is not IReadOnlyPathData pathData || pathData.Path.IsClosed;
+        OnDeselecting(false);
+        OnSelected(false);
     }
 }

+ 1 - 0
src/PixiEditor/Views/Overlays/PathOverlay/VectorPathOverlay.cs

@@ -253,6 +253,7 @@ public class VectorPathOverlay : Overlay
     {
         SnappingController.HighlightedXAxis = axisX;
         SnappingController.HighlightedYAxis = axisY;
+        SnappingController.HighlightedPoint = null;
     }
     
     private AnchorHandle GetHandleAt(int index)