Browse Source

Unaligned vectors

flabbet 8 months ago
parent
commit
dbbe0d9487

+ 18 - 9
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/EllipseVectorData.cs

@@ -46,15 +46,24 @@ public class EllipseVectorData : ShapeVectorData, IReadOnlyEllipseData
         }
 
         using Paint shapePaint = new Paint() { IsAntiAliased = true };
-        
-        shapePaint.Color = FillColor;
-        shapePaint.Style = PaintStyle.Fill;
-        drawingSurface.Canvas.DrawOval(Center, Radius, shapePaint);
-
-        shapePaint.Color = StrokeColor;
-        shapePaint.Style = PaintStyle.Stroke;
-        shapePaint.StrokeWidth = StrokeWidth;
-        drawingSurface.Canvas.DrawOval(Center, Radius - new VecD(StrokeWidth / 2f), shapePaint);
+
+        if (Radius.ShortestAxis < StrokeWidth)
+        {
+            shapePaint.Color = StrokeColor;
+            shapePaint.Style = PaintStyle.Fill;
+            drawingSurface.Canvas.DrawOval(Center, Radius, shapePaint);
+        }
+        else
+        {
+            shapePaint.Color = FillColor;
+            shapePaint.Style = PaintStyle.Fill;
+            drawingSurface.Canvas.DrawOval(Center, Radius, shapePaint);
+
+            shapePaint.Color = StrokeColor;
+            shapePaint.Style = PaintStyle.Stroke;
+            shapePaint.StrokeWidth = StrokeWidth;
+            drawingSurface.Canvas.DrawOval(Center, Radius - new VecD(StrokeWidth / 2f), shapePaint);
+        }
 
         if (applyTransform)
         {

+ 20 - 10
src/PixiEditor.ChangeableDocument/Changeables/Graph/Nodes/Shapes/Data/RectangleVectorData.cs

@@ -12,7 +12,7 @@ public class RectangleVectorData : ShapeVectorData, IReadOnlyRectangleData
     public VecD Center { get; }
     public VecD Size { get; }
 
-    public override RectD GeometryAABB => RectD.FromCenterAndSize(Center, Size); 
+    public override RectD GeometryAABB => RectD.FromCenterAndSize(Center, Size);
 
     public override ShapeCorners TransformationCorners =>
         new ShapeCorners(Center, Size).WithMatrix(TransformationMatrix);
@@ -44,15 +44,25 @@ public class RectangleVectorData : ShapeVectorData, IReadOnlyRectangleData
         }
 
         using Paint paint = new Paint() { IsAntiAliased = true };
-        
-        paint.Color = FillColor;
-        paint.Style = PaintStyle.Fill;
-        drawingSurface.Canvas.DrawRect(RectD.FromCenterAndSize(Center, Size), paint);
-
-        paint.Color = StrokeColor;
-        paint.Style = PaintStyle.Stroke;
-        paint.StrokeWidth = StrokeWidth;
-        drawingSurface.Canvas.DrawRect(RectD.FromCenterAndSize(Center, Size - new VecD(StrokeWidth)), paint);
+
+        if (Size.ShortestAxis < StrokeWidth)
+        {
+            paint.Color = StrokeColor;
+            paint.Style = PaintStyle.Fill;
+            drawingSurface.Canvas.DrawRect(RectD.FromCenterAndSize(Center, Size), paint);
+        }
+        else
+        {
+            paint.Color = FillColor;
+            paint.Style = PaintStyle.Fill;
+            drawingSurface.Canvas.DrawRect(RectD.FromCenterAndSize(Center, Size), paint);
+
+            paint.Color = StrokeColor;
+            paint.Style = PaintStyle.Stroke;
+
+            paint.StrokeWidth = StrokeWidth;
+            drawingSurface.Canvas.DrawRect(RectD.FromCenterAndSize(Center, Size - new VecD(StrokeWidth)), paint);
+        }
 
         if (applyTransform)
         {

+ 36 - 13
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/DrawableShapeToolExecutor.cs

@@ -26,7 +26,7 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
     protected bool drawOnMask;
 
     protected T? toolViewModel;
-    protected RectI lastRect;
+    protected RectD lastRect;
     protected double lastRadians;
     
     private ShapeCorners initialCorners;
@@ -61,6 +61,8 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
                 toolbar.FillColor = colorsVM.PrimaryColor.ToColor();
                 toolbar.StrokeColor = colorsVM.PrimaryColor.ToColor();
             }
+            
+            lastRect = new RectD(startDrawingPos, VecD.Zero);
 
             document!.TransformHandler.ShowTransform(TransformMode, false, new ShapeCorners((RectD)lastRect.Inflate(1)),
                 false);
@@ -94,7 +96,7 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
         return ExecutionState.Success;
     }
 
-    protected abstract void DrawShape(VecI currentPos, double rotationRad, bool firstDraw);
+    protected abstract void DrawShape(VecD currentPos, double rotationRad, bool firstDraw);
     protected abstract IAction SettingsChangedAction();
     protected abstract IAction TransformMovedAction(ShapeData data, ShapeCorners corners);
     protected virtual bool InitShapeData(IReadOnlyShapeVectorData data) { return true; }
@@ -113,6 +115,17 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
         return (VecI)pos1;
     }
 
+    public static VecD GetSquaredPosition(VecD startPos, VecD curPos)
+    {
+        VecD pos1 = curPos.ProjectOntoLine(startPos, startPos + new VecD(1, 1)) -
+                    new VecD(0.25).Multiply((curPos - (VecI)startPos).Signs());
+        VecD pos2 = curPos.ProjectOntoLine(startPos, startPos + new VecD(1, -1)) -
+                    new VecD(0.25).Multiply((curPos - (VecI)startPos).Signs());
+        if ((pos1 - curPos).LengthSquared > (pos2 - curPos).LengthSquared)
+            return pos2;
+        return pos1;
+    }
+
     public static RectI GetSquaredCoordinates(VecI startPos, VecI curPos)
     {
         VecI pos = GetSquaredPosition(startPos, curPos);
@@ -178,12 +191,12 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
 
     protected override void PrecisePositionChangeDrawingMode(VecD pos)
     {
-        VecI adjustedPos = (VecI)pos.Floor();
+        VecD adjustedPos = AlignToPixels ? (VecI)pos.Floor() : pos;
 
         VecD snapped = adjustedPos;
         if (toolViewModel.DrawEven)
         {
-            adjustedPos = GetSquaredPosition((VecI)startDrawingPos, adjustedPos);
+            adjustedPos = AlignToPixels ? GetSquaredPosition((VecI)startDrawingPos, (VecI)adjustedPos) : GetSquaredPosition(startDrawingPos, adjustedPos);
             VecD dir = (adjustedPos - startDrawingPos).Normalize();
             snapped = Snap(adjustedPos, startDrawingPos, dir, true);
         }
@@ -194,7 +207,14 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
 
         noMovement = false;
 
-        DrawShape((VecI)snapped.Floor(), lastRadians, false);
+        if (AlignToPixels)
+        {
+            DrawShape((VecI)snapped.Floor(), lastRadians, false);
+        }
+        else
+        {
+            DrawShape(snapped, lastRadians, false);
+        }
 
         document!.TransformHandler.ShowTransform(TransformMode, false, new ShapeCorners((RectD)lastRect), false);
         document!.TransformHandler.Corners = new ShapeCorners((RectD)lastRect);
@@ -211,16 +231,19 @@ internal abstract class DrawableShapeToolExecutor<T> : SimpleShapeToolExecutor w
             HighlightSnapAxis(snapXAxis, snapYAxis);
         }
 
-        if (snapped != VecI.Zero)
+        if (AlignToPixels)
         {
-            if (adjustPos.X < pos.X)
+            if (snapped != VecI.Zero)
             {
-                snapped -= new VecI(1, 0);
-            }
-
-            if (adjustPos.Y < pos.Y)
-            {
-                snapped -= new VecI(0, 1);
+                if (adjustPos.X < pos.X)
+                {
+                    snapped -= new VecI(1, 0);
+                }
+
+                if (adjustPos.Y < pos.Y)
+                {
+                    snapped -= new VecI(0, 1);
+                }
             }
         }
 

+ 9 - 11
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/RasterEllipseToolExecutor.cs

@@ -11,18 +11,16 @@ namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 #nullable enable
 internal class RasterEllipseToolExecutor : DrawableShapeToolExecutor<IRasterEllipseToolHandler>
 {
-    private void DrawEllipseOrCircle(VecI curPos, double rotationRad, bool firstDraw)
+    private void DrawEllipseOrCircle(VecD curPos, double rotationRad, bool firstDraw)
     {
         RectI rect;
         VecI startPos = (VecI)Snap(startDrawingPos, curPos).Floor();
         if (firstDraw)
-            rect = new RectI(curPos, VecI.Zero);
-        /*else if (toolViewModel!.DrawCircle)
-            rect = GetSquaredCoordinates(startPos, curPos);
-        else*/
-            rect = RectI.FromTwoPixels(startPos, curPos);
+            rect = new RectI((VecI)curPos, VecI.Zero);
+        else
+            rect = RectI.FromTwoPixels(startPos, (VecI)curPos);
 
-        lastRect = rect;
+        lastRect = (RectD)rect;
         lastRadians = rotationRad;
 
         internals!.ActionAccumulator.AddActions(new DrawRasterEllipse_Action(memberId, rect, rotationRad, StrokeColor, FillColor, StrokeWidth, toolbar.AntiAliasing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable));
@@ -30,10 +28,10 @@ internal class RasterEllipseToolExecutor : DrawableShapeToolExecutor<IRasterElli
 
     public override ExecutorType Type => ExecutorType.ToolLinked;
     protected override DocumentTransformMode TransformMode => DocumentTransformMode.Scale_Rotate_NoShear_NoPerspective;
-    protected override void DrawShape(VecI currentPos, double rotationRad, bool firstDraw) => DrawEllipseOrCircle(currentPos, rotationRad, firstDraw);
+    protected override void DrawShape(VecD currentPos, double rotationRad, bool firstDraw) => DrawEllipseOrCircle(currentPos, rotationRad, firstDraw);
     protected override IAction SettingsChangedAction()
     {
-        return new DrawRasterEllipse_Action(memberId, lastRect, lastRadians, StrokeColor, FillColor, StrokeWidth, toolbar.AntiAliasing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
+        return new DrawRasterEllipse_Action(memberId, (RectI)lastRect, lastRadians, StrokeColor, FillColor, StrokeWidth, toolbar.AntiAliasing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
     }
 
     protected override IAction TransformMovedAction(ShapeData data, ShapeCorners corners)
@@ -41,10 +39,10 @@ internal class RasterEllipseToolExecutor : DrawableShapeToolExecutor<IRasterElli
         RectI rect = (RectI)RectD.FromCenterAndSize(data.Center, data.Size);
         double radians = corners.RectRotation;
         
-        lastRect = rect;
+        lastRect = (RectD)rect;
         lastRadians = radians;
         
-        return new DrawRasterEllipse_Action(memberId, lastRect, lastRadians, StrokeColor,
+        return new DrawRasterEllipse_Action(memberId, (RectI)lastRect, lastRadians, StrokeColor,
             FillColor, StrokeWidth, toolbar.AntiAliasing, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
     }
 

+ 19 - 14
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/RasterRectangleToolExecutor.cs

@@ -12,44 +12,49 @@ internal class RasterRectangleToolExecutor : DrawableShapeToolExecutor<IRasterRe
 {
     private ShapeData lastData;
     public override ExecutorType Type => ExecutorType.ToolLinked;
-    private void DrawRectangle(VecI curPos, double rotationRad, bool firstDraw)
+
+    private void DrawRectangle(VecD curPos, double rotationRad, bool firstDraw)
     {
         RectI rect;
         VecI startPos = (VecI)Snap(startDrawingPos, curPos).Floor();
         if (firstDraw)
-            rect = new RectI(curPos, VecI.Zero);
-        /*else if (toolViewModel!.DrawSquare)
-            rect = GetSquaredCoordinates(startPos, curPos);
-        else*/
-            rect = RectI.FromTwoPixels(startPos, curPos);
-        lastRect = rect;
-        lastRadians = rotationRad;
+            rect = new RectI((VecI)curPos, VecI.Zero);
+        else
+            rect = RectI.FromTwoPixels(startPos, (VecI)curPos);
         
+        lastRect = (RectD)rect;
+        lastRadians = rotationRad;
+
         lastData = new ShapeData(rect.Center, rect.Size, rotationRad, StrokeWidth, StrokeColor, FillColor)
         {
             AntiAliasing = toolbar.AntiAliasing
         };
 
-        internals!.ActionAccumulator.AddActions(new DrawRasterRectangle_Action(memberId, lastData, drawOnMask, document!.AnimationHandler.ActiveFrameBindable));
+        internals!.ActionAccumulator.AddActions(new DrawRasterRectangle_Action(memberId, lastData, drawOnMask,
+            document!.AnimationHandler.ActiveFrameBindable));
     }
 
-    protected override void DrawShape(VecI currentPos, double rotationRad, bool first) => DrawRectangle(currentPos, rotationRad, first);
+    protected override void DrawShape(VecD currentPos, double rotationRad, bool first) =>
+        DrawRectangle(currentPos, rotationRad, first);
+
     protected override IAction SettingsChangedAction()
     {
         lastData = new ShapeData(lastData.Center, lastData.Size, lastRadians, StrokeWidth, StrokeColor, FillColor)
         {
             AntiAliasing = toolbar.AntiAliasing
         };
-        return new DrawRasterRectangle_Action(memberId, lastData, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);   
+        return new DrawRasterRectangle_Action(memberId, lastData, drawOnMask,
+            document!.AnimationHandler.ActiveFrameBindable);
     }
 
     protected override IAction TransformMovedAction(ShapeData data, ShapeCorners corners)
     {
         lastData = data;
-        
+
         lastRadians = corners.RectRotation;
-        
-        return new DrawRasterRectangle_Action(memberId, data, drawOnMask, document!.AnimationHandler.ActiveFrameBindable);
+
+        return new DrawRasterRectangle_Action(memberId, data, drawOnMask,
+            document!.AnimationHandler.ActiveFrameBindable);
     }
 
     protected override IAction EndDrawAction() => new EndDrawRasterRectangle_Action();

+ 12 - 3
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/SimpleShapeToolExecutor.cs

@@ -40,7 +40,8 @@ internal abstract class SimpleShapeToolExecutor : UpdateableChangeExecutor,
             StartMode(activeMode);
         }
     }
-
+    protected virtual bool AlignToPixels { get; } = true;
+    
     protected Guid memberId;
     protected VecD startDrawingPos;
 
@@ -85,7 +86,15 @@ internal abstract class SimpleShapeToolExecutor : UpdateableChangeExecutor,
 
     private void StartDrawingMode()
     {
-        startDrawingPos = SnapAndHighlight(controller.LastPrecisePosition);
+        var snapped = SnapAndHighlight(controller.LastPrecisePosition);
+        if (AlignToPixels)
+        {
+            startDrawingPos = (VecI)snapped;
+        }
+        else
+        {
+            startDrawingPos = snapped;
+        }
     }
 
     protected virtual void StopMode(ShapeToolMode mode)
@@ -180,7 +189,7 @@ internal abstract class SimpleShapeToolExecutor : UpdateableChangeExecutor,
     {
         VecD snapped = document.SnappingHandler.SnappingController.GetSnapPoint(pos, out string snapX, out string snapY);
         HighlightSnapping(snapX, snapY);
-        return (VecI)snapped;
+        return snapped;
     }
 
     protected virtual void PrecisePositionChangePreviewMode(VecD pos)

+ 16 - 14
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorEllipseToolExecutor.cs

@@ -18,31 +18,31 @@ internal class VectorEllipseToolExecutor : DrawableShapeToolExecutor<IVectorElli
 
     private VecD firstRadius;
     private VecD firstCenter;
-    
+
     private Matrix3X3 lastMatrix = Matrix3X3.Identity;
 
+    protected override bool AlignToPixels => false;
+
     protected override bool InitShapeData(IReadOnlyShapeVectorData data)
     {
         if (data is not EllipseVectorData ellipseData)
             return false;
-        
+
         firstCenter = ellipseData.Center;
         firstRadius = ellipseData.Radius;
         lastMatrix = ellipseData.TransformationMatrix;
-        
+
         return true;
     }
 
-    protected override void DrawShape(VecI curPos, double rotationRad, bool firstDraw)
+    protected override void DrawShape(VecD curPos, double rotationRad, bool firstDraw)
     {
-        RectI rect;
-        VecI startPos = (VecI)Snap(startDrawingPos, curPos).Floor();
-        if (firstDraw)
-            rect = new RectI(curPos, VecI.Zero);
-        /*else if (toolViewModel!.DrawCircle)
-            rect = GetSquaredCoordinates(startPos, curPos);
-        else*/
-            rect = RectI.FromTwoPixels(startPos, curPos);
+        RectD rect;
+        VecD startPos = Snap(startDrawingPos, curPos);
+        if (!firstDraw)
+            rect = RectD.FromTwoPoints(startPos, curPos);
+        else
+            rect = new RectD(curPos, VecD.Zero);
 
         firstCenter = rect.Center;
         firstRadius = rect.Size / 2f;
@@ -62,14 +62,16 @@ internal class VectorEllipseToolExecutor : DrawableShapeToolExecutor<IVectorElli
         return new SetShapeGeometry_Action(memberId,
             new EllipseVectorData(firstCenter, firstRadius)
             {
-                StrokeColor = StrokeColor, FillColor = FillColor, StrokeWidth = StrokeWidth,
+                StrokeColor = StrokeColor,
+                FillColor = FillColor,
+                StrokeWidth = StrokeWidth,
                 TransformationMatrix = lastMatrix
             });
     }
 
     protected override IAction TransformMovedAction(ShapeData data, ShapeCorners corners)
     {
-        RectI rect = (RectI)RectD.FromCenterAndSize(data.Center, data.Size);
+        RectD rect = RectD.FromCenterAndSize(data.Center, data.Size);
         RectD firstRect = RectD.FromCenterAndSize(firstCenter, firstRadius * 2);
         Matrix3X3 matrix = OperationHelper.CreateMatrixFromPoints(corners, firstRadius * 2);
         matrix = matrix.Concat(Matrix3X3.CreateTranslation(-(float)firstRect.TopLeft.X, -(float)firstRect.TopLeft.Y));

+ 19 - 11
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/VectorRectangleToolExecutor.cs

@@ -21,27 +21,31 @@ internal class VectorRectangleToolExecutor : DrawableShapeToolExecutor<IVectorRe
 
     private Matrix3X3 lastMatrix = Matrix3X3.Identity;
 
+    protected override bool AlignToPixels => false;
+
     protected override bool InitShapeData(IReadOnlyShapeVectorData data)
     {
         if (data is not RectangleVectorData rectData)
             return false;
-        
+
         firstCenter = rectData.Center;
         firstSize = rectData.Size;
         lastMatrix = rectData.TransformationMatrix;
         return true;
     }
 
-    protected override void DrawShape(VecI curPos, double rotationRad, bool firstDraw)
+    protected override void DrawShape(VecD curPos, double rotationRad, bool firstDraw)
     {
-        RectI rect;
-        VecI startPos = (VecI)Snap(startDrawingPos, curPos).Floor();
+        RectD rect;
+        VecD startPos = Snap(startDrawingPos, curPos);
         if (firstDraw)
-            rect = new RectI(curPos, VecI.Zero);
-        /*else if (toolViewModel!.DrawSquare)
-            rect = GetSquaredCoordinates(startPos, curPos);
-        else*/
-            rect = RectI.FromTwoPixels(startPos, curPos);
+        {
+            rect = new RectD(curPos, VecD.Zero);
+        }
+        else
+        {
+            rect = RectD.FromTwoPoints(startPos, curPos);
+        }
 
         firstCenter = rect.Center;
         firstSize = rect.Size;
@@ -61,7 +65,9 @@ internal class VectorRectangleToolExecutor : DrawableShapeToolExecutor<IVectorRe
         return new SetShapeGeometry_Action(memberId,
             new RectangleVectorData(firstCenter, firstSize)
             {
-                StrokeColor = StrokeColor, FillColor = FillColor, StrokeWidth = StrokeWidth,
+                StrokeColor = StrokeColor,
+                FillColor = FillColor,
+                StrokeWidth = StrokeWidth,
                 TransformationMatrix = lastMatrix
             });
     }
@@ -80,7 +86,9 @@ internal class VectorRectangleToolExecutor : DrawableShapeToolExecutor<IVectorRe
 
         RectangleVectorData newData = new RectangleVectorData(firstCenter, firstSize)
         {
-            StrokeColor = data.StrokeColor, FillColor = data.FillColor, StrokeWidth = data.StrokeWidth,
+            StrokeColor = data.StrokeColor,
+            FillColor = data.FillColor,
+            StrokeWidth = data.StrokeWidth,
             TransformationMatrix = matrix
         };