Browse Source

Transform overlay snap to pixels

Equbuxu 3 years ago
parent
commit
b27b26acf9

+ 12 - 0
src/ChunkyImageLib/DataHolders/ShapeCorners.cs

@@ -31,6 +31,18 @@ public struct ShapeCorners
         (TopLeft - TopRight).Cross(TopLeft - BottomLeft) > 0 ?
         RectSize.CCWAngleTo(BottomRight - TopLeft) :
         RectSize.CCWAngleTo(BottomLeft - TopRight);
+    public bool IsSnappedToPixels
+    {
+        get
+        {
+            double epsilon = 0.01;
+            return
+                (TopLeft - TopLeft.Round()).TaxicabLength < epsilon &&
+                (TopRight - TopRight.Round()).TaxicabLength < epsilon &&
+                (BottomLeft - BottomLeft.Round()).TaxicabLength < epsilon &&
+                (BottomRight - BottomRight.Round()).TaxicabLength < epsilon;
+        }
+    }
     public bool IsPointInside(Vector2d point)
     {
         var top = TopLeft - TopRight;

+ 4 - 1
src/ChunkyImageLib/DataHolders/Vector2d.cs

@@ -21,7 +21,10 @@ public struct Vector2d
     {
         return new Vector2d(Math.Cos(angle) * length, Math.Sin(angle) * length);
     }
-
+    public Vector2d Round()
+    {
+        return new(Math.Round(X), Math.Round(Y));
+    }
     public Vector2d Rotate(double angle)
     {
         Vector2d result = new Vector2d();

+ 9 - 0
src/PixiEditorPrototype/CustomControls/TransformOverlay/TransformHelper.cs

@@ -37,6 +37,15 @@ internal static class TransformHelper
     public static Vector2d ToVector2d(Point pos) => new Vector2d(pos.X, pos.Y);
     public static Point ToPoint(Vector2d vec) => new Point(vec.X, vec.Y);
 
+    public static ShapeCorners SnapToPixels(ShapeCorners corners)
+    {
+        corners.TopLeft = corners.TopLeft.Round();
+        corners.TopRight = corners.TopRight.Round();
+        corners.BottomLeft = corners.BottomLeft.Round();
+        corners.BottomRight = corners.BottomRight.Round();
+        return corners;
+    }
+
     public static Vector2d OriginFromCorners(ShapeCorners corners)
     {
         var maybeOrigin = TwoLineIntersection(

+ 29 - 14
src/PixiEditorPrototype/CustomControls/TransformOverlay/TransformOverlay.cs

@@ -70,14 +70,15 @@ internal class TransformOverlay : Control
     private bool isMoving = false;
     private Vector2d mousePosOnStartMove = new();
     private Vector2d originOnStartMove = new();
-    private ShapeCorners cornersOnStartMove = new();
+    private ShapeCorners preciseCornersOnStartMove = new();
 
     private bool isRotating = false;
     private Vector2d mousePosOnStartRotate = new();
-    private ShapeCorners cornersOnStartRotate = new();
+    private ShapeCorners preciseCornersOnStartRotate = new();
 
     private Anchor? capturedAnchor;
     private bool originWasManuallyDragged = false;
+    private ShapeCorners preciseCornersOnStartAnchorDrag;
     private ShapeCorners cornersOnStartAnchorDrag;
     private Vector2d mousePosOnStartAnchorDrag;
     private Vector2d originOnStartAnchorDrag;
@@ -98,6 +99,7 @@ internal class TransformOverlay : Control
         if (anchor is not null)
         {
             capturedAnchor = anchor;
+            preciseCornersOnStartAnchorDrag = Corners;
             cornersOnStartAnchorDrag = Corners;
             originOnStartAnchorDrag = Origin;
             mousePosOnStartAnchorDrag = pos;
@@ -107,13 +109,13 @@ internal class TransformOverlay : Control
             isMoving = true;
             mousePosOnStartMove = TransformHelper.ToVector2d(e.GetPosition(this));
             originOnStartMove = Origin;
-            cornersOnStartMove = Corners;
+            preciseCornersOnStartMove = Corners;
         }
         else
         {
             isRotating = true;
             mousePosOnStartRotate = TransformHelper.ToVector2d(e.GetPosition(this));
-            cornersOnStartRotate = Corners;
+            preciseCornersOnStartRotate = Corners;
         }
         CaptureMouse();
     }
@@ -129,20 +131,25 @@ internal class TransformOverlay : Control
         {
             var pos = TransformHelper.ToVector2d(e.GetPosition(this));
             var delta = pos - mousePosOnStartMove;
-            Origin = originOnStartMove + delta;
+
+            if (Corners.IsSnappedToPixels)
+                delta = delta.Round();
+
             Corners = new ShapeCorners()
             {
-                BottomLeft = cornersOnStartMove.BottomLeft + delta,
-                BottomRight = cornersOnStartMove.BottomRight + delta,
-                TopLeft = cornersOnStartMove.TopLeft + delta,
-                TopRight = cornersOnStartMove.TopRight + delta,
+                BottomLeft = preciseCornersOnStartMove.BottomLeft + delta,
+                BottomRight = preciseCornersOnStartMove.BottomRight + delta,
+                TopLeft = preciseCornersOnStartMove.TopLeft + delta,
+                TopRight = preciseCornersOnStartMove.TopRight + delta,
             };
+
+            Origin = originOnStartMove + delta;
         }
         else if (isRotating)
         {
             var pos = TransformHelper.ToVector2d(e.GetPosition(this));
             var angle = (mousePosOnStartRotate - Origin).CCWAngleTo(pos - Origin);
-            Corners = TransformUpdateHelper.UpdateShapeFromRotation(cornersOnStartRotate, Origin, angle);
+            Corners = Corners = TransformUpdateHelper.UpdateShapeFromRotation(preciseCornersOnStartRotate, Origin, angle);
         }
     }
 
@@ -159,17 +166,25 @@ internal class TransformOverlay : Control
 
         if (TransformHelper.IsCorner((Anchor)capturedAnchor))
         {
-            var newCorners = TransformUpdateHelper.UpdateShapeFromCorner((Anchor)capturedAnchor, CornerFreedom, cornersOnStartAnchorDrag, pos - mousePosOnStartAnchorDrag);
+            var targetPos = TransformHelper.GetAnchorPosition(cornersOnStartAnchorDrag, (Anchor)capturedAnchor) + pos - mousePosOnStartAnchorDrag;
+            var newCorners = TransformUpdateHelper.UpdateShapeFromCorner((Anchor)capturedAnchor, CornerFreedom, preciseCornersOnStartAnchorDrag, targetPos);
             if (newCorners is not null)
-                Corners = (ShapeCorners)newCorners;
+            {
+                bool shouldSnap = (CornerFreedom is TransformCornerFreedom.ScaleProportionally or TransformCornerFreedom.Scale) && Corners.IsSnappedToPixels;
+                Corners = shouldSnap ? TransformHelper.SnapToPixels((ShapeCorners)newCorners) : (ShapeCorners)newCorners;
+            }
             if (!originWasManuallyDragged)
                 Origin = TransformHelper.OriginFromCorners(Corners);
         }
         else if (TransformHelper.IsSide((Anchor)capturedAnchor))
         {
-            var newCorners = TransformUpdateHelper.UpdateShapeFromSide((Anchor)capturedAnchor, SideFreedom, cornersOnStartAnchorDrag, pos - mousePosOnStartAnchorDrag);
+            var targetPos = TransformHelper.GetAnchorPosition(cornersOnStartAnchorDrag, (Anchor)capturedAnchor) + pos - mousePosOnStartAnchorDrag;
+            var newCorners = TransformUpdateHelper.UpdateShapeFromSide((Anchor)capturedAnchor, SideFreedom, preciseCornersOnStartAnchorDrag, targetPos);
             if (newCorners is not null)
-                Corners = (ShapeCorners)newCorners;
+            {
+                bool shouldSnap = (SideFreedom is TransformSideFreedom.ScaleProportionally or TransformSideFreedom.Stretch) && Corners.IsSnappedToPixels;
+                Corners = shouldSnap ? TransformHelper.SnapToPixels((ShapeCorners)newCorners) : (ShapeCorners)newCorners;
+            }
             if (!originWasManuallyDragged)
                 Origin = TransformHelper.OriginFromCorners(Corners);
         }

+ 2 - 6
src/PixiEditorPrototype/CustomControls/TransformOverlay/TransformUpdateHelper.cs

@@ -5,7 +5,7 @@ namespace PixiEditorPrototype.CustomControls.TransformOverlay;
 internal static class TransformUpdateHelper
 {
     public static ShapeCorners? UpdateShapeFromCorner
-        (Anchor targetCorner, TransformCornerFreedom freedom, ShapeCorners corners, Vector2d movement)
+        (Anchor targetCorner, TransformCornerFreedom freedom, ShapeCorners corners, Vector2d desiredPos)
     {
         if (!TransformHelper.IsCorner(targetCorner))
             throw new ArgumentException($"{targetCorner} is not a corner");
@@ -16,7 +16,6 @@ internal static class TransformUpdateHelper
         if (freedom is TransformCornerFreedom.ScaleProportionally or TransformCornerFreedom.Scale)
         {
             var targetPos = TransformHelper.GetAnchorPosition(corners, targetCorner);
-            Vector2d desiredPos = targetPos + movement;
             var opposite = TransformHelper.GetOpposite(targetCorner);
             var oppositePos = TransformHelper.GetAnchorPosition(corners, opposite);
 
@@ -47,7 +46,6 @@ internal static class TransformUpdateHelper
         if (freedom == TransformCornerFreedom.Free)
         {
             var targetPos = TransformHelper.GetAnchorPosition(corners, targetCorner);
-            Vector2d desiredPos = targetPos + movement;
             var newCorners = TransformHelper.UpdateCorner(corners, targetCorner, desiredPos);
             return newCorners.IsLegal ? newCorners : null;
         }
@@ -55,7 +53,7 @@ internal static class TransformUpdateHelper
     }
 
     public static ShapeCorners? UpdateShapeFromSide
-        (Anchor targetSide, TransformSideFreedom freedom, ShapeCorners corners, Vector2d movement)
+        (Anchor targetSide, TransformSideFreedom freedom, ShapeCorners corners, Vector2d desiredPos)
     {
         if (!TransformHelper.IsSide(targetSide))
             throw new ArgumentException($"{targetSide} is not a side");
@@ -66,7 +64,6 @@ internal static class TransformUpdateHelper
         if (freedom is TransformSideFreedom.ScaleProportionally)
         {
             var targetPos = TransformHelper.GetAnchorPosition(corners, targetSide);
-            Vector2d desiredPos = targetPos + movement;
             var opposite = TransformHelper.GetOpposite(targetSide);
             var oppositePos = TransformHelper.GetAnchorPosition(corners, opposite);
 
@@ -100,7 +97,6 @@ internal static class TransformUpdateHelper
             var side1Pos = TransformHelper.GetAnchorPosition(corners, side1);
             var side2Pos = TransformHelper.GetAnchorPosition(corners, side2);
             var targetPos = TransformHelper.GetAnchorPosition(corners, targetSide);
-            var desiredPos = targetPos + movement;
 
             var opposite = TransformHelper.GetOpposite(targetSide);
             var oppPos = TransformHelper.GetAnchorPosition(corners, opposite);

+ 2 - 2
src/PixiEditorPrototype/ViewModels/DocumentViewModel.cs

@@ -210,8 +210,8 @@ internal class DocumentViewModel : INotifyPropertyChanged
         drawingRectangle = false;
 
         RequestedTransformCorners = lastShape;
-        TransformCornerFreedom = TransformCornerFreedom.Free;
-        TransformSideFreedom = TransformSideFreedom.Free;
+        TransformCornerFreedom = TransformCornerFreedom.ScaleProportionally;
+        TransformSideFreedom = TransformSideFreedom.Stretch;
         TransformActive = true;
         transformingRectangle = true;
     }