Преглед на файлове

Added draw from center to shapes

flabbet преди 8 месеца
родител
ревизия
dd91f68192

+ 30 - 1
src/PixiEditor/Helpers/GeometryHelper.cs

@@ -4,7 +4,36 @@ namespace PixiEditor.Helpers;
 
 public static class GeometryHelper
 {
-    public static VecI Get45IncrementedPosition(VecD startPos, VecD curPos)
+    public static VecD Get45IncrementedPosition(VecD startPos, VecD curPos)
+    {
+        Span<VecD> positions =
+        [
+            (curPos.ProjectOntoLine(startPos, startPos + new VecD(1, 1)) -
+                   new VecD(0.25).Multiply((curPos - startPos).Signs())),
+            (curPos.ProjectOntoLine(startPos, startPos + new VecD(1, -1)) -
+                   new VecD(0.25).Multiply((curPos - startPos).Signs())),
+            (curPos.ProjectOntoLine(startPos, startPos + new VecD(1, 0)) -
+                   new VecD(0.25).Multiply((curPos - startPos).Signs())),
+            (curPos.ProjectOntoLine(startPos, startPos + new VecD(0, 1)) -
+                   new VecD(0.25).Multiply((curPos - startPos).Signs()))
+        ];
+
+        VecD 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 Get45IncrementedPositionAligned(VecD startPos, VecD curPos)
     {
         Span<VecI> positions =
         [

+ 23 - 14
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/DrawableShapeToolExecutor.cs

@@ -29,7 +29,7 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
     protected T? toolViewModel;
     protected RectD lastRect;
     protected double lastRadians;
-    
+
     private ShapeCorners initialCorners;
     private bool noMovement = true;
     protected IFillableShapeToolbar toolbar;
@@ -64,7 +64,7 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
                 toolbar.StrokeColor = colorsVM.PrimaryColor.ToColor();
                 ignoreNextColorChange = colorsVM.ColorsTempSwapped;
             }
-            
+
             lastRect = new RectD(startDrawingPos, VecD.Zero);
 
             document!.TransformHandler.ShowTransform(TransformMode, false, new ShapeCorners((RectD)lastRect.Inflate(1)),
@@ -89,7 +89,7 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
             toolbar.ToolSize = shapeData.StrokeWidth;
             toolbar.Fill = shapeData.FillColor != Colors.Transparent;
             initialCorners = shapeData.TransformationCorners;
-            
+
             ActiveMode = ShapeToolMode.Transform;
         }
         else
@@ -131,19 +131,14 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
         return 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)
     {
         if (ActiveMode != ShapeToolMode.Transform)
             return;
 
         var rect = RectD.FromCenterAndSize(corners.RectCenter, corners.RectSize);
-        ShapeData shapeData = new ShapeData(rect.Center, rect.Size, corners.RectRotation, (float)StrokeWidth, StrokeColor,
+        ShapeData shapeData = new ShapeData(rect.Center, rect.Size, corners.RectRotation, (float)StrokeWidth,
+            StrokeColor,
             FillColor) { AntiAliasing = toolbar.AntiAliasing };
         IAction drawAction = TransformMovedAction(shapeData, corners);
 
@@ -172,9 +167,10 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
             {
                 ignoreNextColorChange = false;
             }
+
             return;
         }
-        
+
         ignoreNextColorChange = ActiveMode == ShapeToolMode.Drawing;
 
         toolbar.StrokeColor = color.ToColor();
@@ -206,10 +202,14 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
     {
         VecD adjustedPos = AlignToPixels ? (VecI)pos.Floor() : pos;
 
-        VecD snapped = adjustedPos;
+        VecD startPos = startDrawingPos;
+
+        VecD snapped;
         if (toolViewModel.DrawEven)
         {
-            adjustedPos = AlignToPixels ? GetSquaredPosition((VecI)startDrawingPos, (VecI)adjustedPos) : GetSquaredPosition(startDrawingPos, adjustedPos);
+            adjustedPos = AlignToPixels
+                ? GetSquaredPosition((VecI)startDrawingPos, (VecI)adjustedPos)
+                : GetSquaredPosition(startPos, adjustedPos);
             VecD dir = (adjustedPos - startDrawingPos).Normalize();
             snapped = Snap(adjustedPos, startDrawingPos, dir, true);
         }
@@ -220,6 +220,13 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
 
         noMovement = false;
 
+        if (toolViewModel.DrawFromCenter)
+        {
+            VecD center = startDrawingPos;
+
+            startDrawingPos = center + (center - snapped);
+        }
+
         if (AlignToPixels)
         {
             DrawShape((VecI)snapped.Floor(), lastRadians, false);
@@ -229,6 +236,8 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
             DrawShape(snapped, lastRadians, false);
         }
 
+        startDrawingPos = startPos;
+
         document!.TransformHandler.ShowTransform(TransformMode, false, new ShapeCorners((RectD)lastRect), false);
         document!.TransformHandler.Corners = new ShapeCorners((RectD)lastRect);
     }
@@ -302,7 +311,7 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
         var layer = document.StructureHelper.Find(memberId);
         if (layer is null)
             return;
-        
+
         if (CanEditShape(layer))
         {
             internals!.ActionAccumulator.AddActions(SettingsChangedAction());

+ 26 - 6
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/LineExecutor.cs

@@ -20,7 +20,7 @@ internal abstract class LineExecutor<T> : SimpleShapeToolExecutor where T : ILin
 
     protected Color StrokeColor => toolbar!.StrokeColor.ToColor();
     protected double StrokeWidth => toolViewModel!.ToolSize;
-    
+
     protected bool drawOnMask;
 
     protected VecD curPos;
@@ -29,6 +29,7 @@ internal abstract class LineExecutor<T> : SimpleShapeToolExecutor where T : ILin
     private IColorsHandler? colorsVM;
     protected IShapeToolbar? toolbar;
     private bool ignoreNextColorChange = false;
+    private VecD lastStartPos;
 
     public override bool CanUndo => document.LineToolOverlayHandler.HasUndo;
     public override bool CanRedo => document.LineToolOverlayHandler.HasRedo;
@@ -63,7 +64,7 @@ internal abstract class LineExecutor<T> : SimpleShapeToolExecutor where T : ILin
             document.LineToolOverlayHandler.Show(startDrawingPos, startDrawingPos, false);
             document.LineToolOverlayHandler.ShowHandles = false;
             document.LineToolOverlayHandler.IsSizeBoxEnabled = true;
-            
+
             return ExecutionState.Success;
         }
 
@@ -107,10 +108,20 @@ internal abstract class LineExecutor<T> : SimpleShapeToolExecutor where T : ILin
         VecD snapped = endPos;
         string snapX = "";
         string snapY = "";
-        
+
+        VecD startPos = startDrawingPos;
+
         if (toolViewModel!.Snap)
         {
-            endPos = GeometryHelper.Get45IncrementedPosition(startDrawingPos, pos);
+            if (AlignToPixels)
+            {
+                endPos = GeometryHelper.Get45IncrementedPositionAligned(startDrawingPos, pos);
+            }
+            else
+            {
+                endPos = GeometryHelper.Get45IncrementedPosition(startDrawingPos, pos);
+            }
+
             VecD directionConstraint = endPos - startDrawingPos;
             snapped =
                 document!.SnappingHandler.SnappingController.GetSnapPoint(endPos, directionConstraint, out snapX,
@@ -120,7 +131,12 @@ internal abstract class LineExecutor<T> : SimpleShapeToolExecutor where T : ILin
         {
             snapped = document!.SnappingHandler.SnappingController.GetSnapPoint(endPos, out snapX, out snapY);
         }
-        
+
+        if (toolViewModel.DrawFromCenter)
+        {
+            VecD center = startDrawingPos;
+            startDrawingPos = center + (center - snapped);
+        }
 
         HighlightSnapping(snapX, snapY);
         document!.LineToolOverlayHandler.LineEnd = snapped;
@@ -129,6 +145,9 @@ internal abstract class LineExecutor<T> : SimpleShapeToolExecutor where T : ILin
 
         var drawLineAction = DrawLine(curPos);
         internals!.ActionAccumulator.AddActions(drawLineAction);
+
+        lastStartPos = startDrawingPos;
+        startDrawingPos = startPos;
     }
 
     public override void OnLeftMouseButtonUp(VecD argsPositionOnCanvas)
@@ -141,7 +160,7 @@ internal abstract class LineExecutor<T> : SimpleShapeToolExecutor where T : ILin
         }
 
         document!.LineToolOverlayHandler.Hide();
-        document!.LineToolOverlayHandler.Show(startDrawingPos, curPos, true);
+        document!.LineToolOverlayHandler.Show(lastStartPos, curPos, true);
         base.OnLeftMouseButtonUp(argsPositionOnCanvas);
     }
 
@@ -165,6 +184,7 @@ internal abstract class LineExecutor<T> : SimpleShapeToolExecutor where T : ILin
             {
                 ignoreNextColorChange = false;
             }
+
             return;
         }
 

+ 1 - 0
src/PixiEditor/Models/Handlers/Tools/ILineToolHandler.cs

@@ -4,4 +4,5 @@ internal interface ILineToolHandler : IToolHandler
 {
     public double ToolSize { get; }
     public bool Snap { get; }
+    public bool DrawFromCenter { get; }
 }

+ 1 - 0
src/PixiEditor/Models/Handlers/Tools/IShapeToolHandler.cs

@@ -3,4 +3,5 @@
 internal interface IShapeToolHandler : IToolHandler
 {
     public bool DrawEven { get; }
+    public bool DrawFromCenter { get; }
 }

+ 1 - 0
src/PixiEditor/ViewModels/Tools/ShapeTool.cs

@@ -14,6 +14,7 @@ internal abstract class ShapeTool : ToolViewModel, IShapeToolHandler
 
     public override bool IsErasable => true;
     public bool DrawEven { get; protected set; }
+    public bool DrawFromCenter { get; protected set; }
 
     public ShapeTool()
     {

+ 2 - 0
src/PixiEditor/ViewModels/Tools/Tools/RasterEllipseToolViewModel.cs

@@ -30,6 +30,8 @@ internal class RasterEllipseToolViewModel : ShapeTool, IRasterEllipseToolHandler
 
     public override void ModifierKeyChanged(bool ctrlIsDown, bool shiftIsDown, bool altIsDown)
     {
+        DrawFromCenter = ctrlIsDown;
+        
         if (shiftIsDown)
         {
             ActionDisplay = "ELLIPSE_TOOL_ACTION_DISPLAY_SHIFT";

+ 5 - 4
src/PixiEditor/ViewModels/Tools/Tools/RasterLineToolViewModel.cs

@@ -29,8 +29,7 @@ internal class RasterLineToolViewModel : ShapeTool, ILineToolHandler
     public override Type[]? SupportedLayerTypes { get; } = { typeof(IRasterLayerHandler) };
     public override string DefaultIcon => PixiPerfectIcons.LowResLine;
 
-    [Settings.Inherited] 
-    public double ToolSize => GetValue<double>();
+    [Settings.Inherited] public double ToolSize => GetValue<double>();
 
     public bool Snap { get; private set; }
 
@@ -38,6 +37,8 @@ internal class RasterLineToolViewModel : ShapeTool, ILineToolHandler
 
     public override void ModifierKeyChanged(bool ctrlIsDown, bool shiftIsDown, bool altIsDown)
     {
+        DrawFromCenter = ctrlIsDown;
+
         if (shiftIsDown)
         {
             ActionDisplay = "LINE_TOOL_ACTION_DISPLAY_SHIFT";
@@ -58,8 +59,8 @@ internal class RasterLineToolViewModel : ShapeTool, ILineToolHandler
     public override void OnSelected(bool restoring)
     {
         if (restoring) return;
-        
+
         var document = ViewModelMain.Current?.DocumentManagerSubViewModel.ActiveDocument;
         document.Tools.UseRasterLineTool();
-    } 
+    }
 }

+ 5 - 2
src/PixiEditor/ViewModels/Tools/Tools/RasterRectangleToolViewModel.cs

@@ -14,6 +14,7 @@ namespace PixiEditor.ViewModels.Tools.Tools;
 internal class RasterRectangleToolViewModel : ShapeTool, IRasterRectangleToolHandler
 {
     private string defaultActionDisplay = "RECTANGLE_TOOL_ACTION_DISPLAY_DEFAULT";
+
     public RasterRectangleToolViewModel()
     {
         ActionDisplay = defaultActionDisplay;
@@ -31,6 +32,8 @@ internal class RasterRectangleToolViewModel : ShapeTool, IRasterRectangleToolHan
 
     public override void ModifierKeyChanged(bool ctrlIsDown, bool shiftIsDown, bool altIsDown)
     {
+        DrawFromCenter = ctrlIsDown;
+
         if (shiftIsDown)
         {
             DrawEven = true;
@@ -50,8 +53,8 @@ internal class RasterRectangleToolViewModel : ShapeTool, IRasterRectangleToolHan
 
     public override void OnSelected(bool restoring)
     {
-        if(restoring) return;
-        
+        if (restoring) return;
+
         ViewModelMain.Current?.DocumentManagerSubViewModel.ActiveDocument?.Tools.UseRasterRectangleTool();
     }
 }

+ 3 - 1
src/PixiEditor/ViewModels/Tools/Tools/VectorEllipseToolViewModel.cs

@@ -15,7 +15,7 @@ namespace PixiEditor.ViewModels.Tools.Tools;
 [Command.Tool(Key = Key.C)]
 internal class VectorEllipseToolViewModel : ShapeTool, IVectorEllipseToolHandler
 {
-    public const string NewLayerKey = "NEW_ELLIPSE_LAYER_NAME"; 
+    public const string NewLayerKey = "NEW_ELLIPSE_LAYER_NAME";
     private string defaultActionDisplay = "ELLIPSE_TOOL_ACTION_DISPLAY_DEFAULT";
     public override string ToolNameLocalizationKey => "ELLIPSE_TOOL";
 
@@ -43,6 +43,8 @@ internal class VectorEllipseToolViewModel : ShapeTool, IVectorEllipseToolHandler
 
     public override void ModifierKeyChanged(bool ctrlIsDown, bool shiftIsDown, bool altIsDown)
     {
+        DrawFromCenter = ctrlIsDown;
+
         if (shiftIsDown)
         {
             DrawEven = true;

+ 7 - 5
src/PixiEditor/ViewModels/Tools/Tools/VectorLineToolViewModel.cs

@@ -23,7 +23,7 @@ internal class VectorLineToolViewModel : ShapeTool, IVectorLineToolHandler
     private string defaultActionDisplay = "LINE_TOOL_ACTION_DISPLAY_DEFAULT";
 
     public override bool IsErasable => false;
-    
+
     public VectorLineToolViewModel()
     {
         ActionDisplay = defaultActionDisplay;
@@ -37,8 +37,7 @@ internal class VectorLineToolViewModel : ShapeTool, IVectorLineToolHandler
     public override Type[]? SupportedLayerTypes { get; } = [];
     public string? DefaultNewLayerName { get; } = new LocalizedString(NewLayerKey);
 
-    [Settings.Inherited] 
-    public double ToolSize => GetValue<double>();
+    [Settings.Inherited] public double ToolSize => GetValue<double>();
 
     public bool Snap { get; private set; }
 
@@ -46,6 +45,8 @@ internal class VectorLineToolViewModel : ShapeTool, IVectorLineToolHandler
 
     public override void ModifierKeyChanged(bool ctrlIsDown, bool shiftIsDown, bool altIsDown)
     {
+        DrawFromCenter = ctrlIsDown;
+
         if (shiftIsDown)
         {
             ActionDisplay = "LINE_TOOL_ACTION_DISPLAY_SHIFT";
@@ -66,12 +67,13 @@ internal class VectorLineToolViewModel : ShapeTool, IVectorLineToolHandler
     public override void OnSelected(bool restoring)
     {
         if (restoring) return;
-        
+
         var document = ViewModelMain.Current?.DocumentManagerSubViewModel.ActiveDocument;
         var layer = document.SelectedStructureMember;
         if (layer is IVectorLayerHandler vectorLayer)
         {
-            if (vectorLayer.GetShapeData(document.AnimationDataViewModel.ActiveFrameTime) is IReadOnlyLineData lineVectorData)
+            if (vectorLayer.GetShapeData(document.AnimationDataViewModel.ActiveFrameTime) is IReadOnlyLineData
+                lineVectorData)
             {
                 document.LineToolOverlayViewModel.Show(lineVectorData.TransformedStart, lineVectorData.TransformedEnd,
                     false);

+ 3 - 1
src/PixiEditor/ViewModels/Tools/Tools/VectorRectangleToolViewModel.cs

@@ -16,7 +16,7 @@ namespace PixiEditor.ViewModels.Tools.Tools;
 internal class VectorRectangleToolViewModel : ShapeTool, IVectorRectangleToolHandler
 {
     public const string NewLayerKey = "NEW_RECTANGLE_LAYER_NAME";
-    
+
     private string defaultActionDisplay = "RECTANGLE_TOOL_ACTION_DISPLAY_DEFAULT";
     public override string ToolNameLocalizationKey => "RECTANGLE_TOOL";
     public override bool IsErasable => false;
@@ -36,6 +36,8 @@ internal class VectorRectangleToolViewModel : ShapeTool, IVectorRectangleToolHan
 
     public override void ModifierKeyChanged(bool ctrlIsDown, bool shiftIsDown, bool altIsDown)
     {
+        DrawFromCenter = ctrlIsDown;
+
         if (shiftIsDown)
         {
             DrawEven = true;