Browse Source

Line tool snapping

Equbuxu 2 years ago
parent
commit
b0bb2b6cfc

+ 7 - 2
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/LineToolExecutor.cs

@@ -5,6 +5,7 @@ using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Surface;
 using PixiEditor.Models.Enums;
 using PixiEditor.ViewModels.SubViewModels.Document;
+using PixiEditor.ViewModels.SubViewModels.Tools;
 using PixiEditor.ViewModels.SubViewModels.Tools.Tools;
 using PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Toolbars;
 
@@ -23,11 +24,12 @@ internal class LineToolExecutor : UpdateableChangeExecutor
     private VecI curPos;
     private bool started = false;
     private bool transforming = false;
+    private LineToolViewModel? toolViewModel;
 
     public override ExecutionState Start()
     {
         ColorsViewModel? colorsVM = ViewModelMain.Current?.ColorsSubViewModel;
-        LineToolViewModel? toolViewModel = ViewModelMain.Current?.ToolsSubViewModel.GetTool<LineToolViewModel>();
+        toolViewModel = ViewModelMain.Current?.ToolsSubViewModel.GetTool<LineToolViewModel>();
         StructureMemberViewModel? member = document?.SelectedStructureMember;
         if (colorsVM is null || toolViewModel is null || member is null)
             return ExecutionState.Error;
@@ -54,7 +56,10 @@ internal class LineToolExecutor : UpdateableChangeExecutor
             return;
         started = true;
         curPos = pos;
-        internals!.ActionAccumulator.AddActions(new DrawLine_Action(memberGuid, startPos, curPos, strokeWidth, strokeColor, StrokeCap.Butt, drawOnMask));
+        VecI targetPos = pos;
+        if (toolViewModel!.Snap)
+            targetPos = ShapeToolExecutor<ShapeTool>.Get45IncrementedPosition(startPos, curPos);
+        internals!.ActionAccumulator.AddActions(new DrawLine_Action(memberGuid, startPos, targetPos, strokeWidth, strokeColor, StrokeCap.Butt, drawOnMask));
     }
 
     public override void OnLeftMouseButtonUp()

+ 32 - 4
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/ShapeToolExecutor.cs

@@ -56,14 +56,42 @@ internal abstract class ShapeToolExecutor<T> : UpdateableChangeExecutor where T
     protected abstract IAction EndDrawAction();
     protected virtual DocumentTransformMode TransformMode => DocumentTransformMode.Scale_Rotate_NoShear_NoPerspective;
 
-    protected RectI GetSquaredCoordinates(VecI startPos, VecI curPos)
+    public static VecI Get45IncrementedPosition(VecI startPos, VecI curPos)
+    {
+        Span<VecI> positions = stackalloc VecI[]
+        {
+            (VecI)(((VecD)curPos).ProjectOntoLine(startPos, startPos + new VecD(1, 1)) - new VecD(0.25).Multiply((curPos - startPos).Signs())).Round(),
+            (VecI)(((VecD)curPos).ProjectOntoLine(startPos, startPos + new VecD(1, -1)) - new VecD(0.25).Multiply((curPos - startPos).Signs())).Round(),
+            (VecI)(((VecD)curPos).ProjectOntoLine(startPos, startPos + new VecD(1, 0)) - new VecD(0.25).Multiply((curPos - startPos).Signs())).Round(),
+            (VecI)(((VecD)curPos).ProjectOntoLine(startPos, startPos + new VecD(0, 1)) - new VecD(0.25).Multiply((curPos - startPos).Signs())).Round()
+        };
+        VecI max = positions[0];
+        double maxLength = double.MaxValue;
+        foreach (var pos in positions)
+        {
+            double length = (pos - curPos).LengthSquared;
+            if (length < maxLength)
+            {
+                maxLength = length;
+                max = pos;
+            }
+        }
+        return max;
+    }
+
+    public static VecI GetSquaredPosition(VecI startPos, VecI curPos)
     {
         VecI pos1 = (VecI)(((VecD)curPos).ProjectOntoLine(startPos, startPos + new VecD(1, 1)) - new VecD(0.25).Multiply((curPos - startPos).Signs())).Round();
         VecI pos2 = (VecI)(((VecD)curPos).ProjectOntoLine(startPos, startPos + new VecD(1, -1)) - new VecD(0.25).Multiply((curPos - startPos).Signs())).Round();
-
         if ((pos1 - curPos).LengthSquared > (pos2 - curPos).LengthSquared)
-            return RectI.FromTwoPixels(startPos, (VecI)pos2);
-        return RectI.FromTwoPixels(startPos, (VecI)pos1);
+            return (VecI)pos2;
+        return (VecI)pos1;
+    }
+
+    public static RectI GetSquaredCoordinates(VecI startPos, VecI curPos)
+    {
+        VecI pos = GetSquaredPosition(startPos, curPos);
+        return RectI.FromTwoPixels(startPos, pos);
     }
 
     public override void OnTransformMoved(ShapeCorners corners)

+ 11 - 3
src/PixiEditor/ViewModels/SubViewModels/Tools/Tools/LineToolViewModel.cs

@@ -9,7 +9,7 @@ namespace PixiEditor.ViewModels.SubViewModels.Tools.Tools;
 [Command.Tool(Key = Key.L)]
 internal class LineToolViewModel : ShapeTool
 {
-    private string defaultActionDisplay = "Click and move to draw a line. Hold Shift to draw an even one.";
+    private string defaultActionDisplay = "Click and move to draw a line. Hold Shift to enable snapping.";
 
     public LineToolViewModel()
     {
@@ -17,17 +17,25 @@ internal class LineToolViewModel : ShapeTool
         Toolbar = ToolbarFactory.Create<LineToolViewModel, BasicToolbar>();
     }
 
-    public override string Tooltip => $"Draws line on canvas ({Shortcut}). Hold Shift to draw even line.";
+    public override string Tooltip => $"Draws line on canvas ({Shortcut}). Hold Shift to enable snapping.";
 
     [Settings.Inherited]
     public int ToolSize => GetValue<int>();
 
+    public bool Snap { get; private set; }
+
     public override void UpdateActionDisplay(bool ctrlIsDown, bool shiftIsDown, bool altIsDown)
     {
         if (shiftIsDown)
-            ActionDisplay = "Click and move mouse to draw an even line.";
+        {
+            ActionDisplay = "Click and move mouse to draw a line with snapping enabled.";
+            Snap = true;
+        }
         else
+        {
             ActionDisplay = defaultActionDisplay;
+            Snap = false;
+        }
     }
 
     public override void OnLeftMouseButtonDown(VecD pos)