Browse Source

Transform overlay snap to 90/45 degrees

Equbuxu 3 years ago
parent
commit
7e1505175c

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

@@ -46,6 +46,32 @@ internal static class TransformHelper
         return corners;
     }
 
+    private static double GetSnappingAngle(double angle)
+    {
+        return Math.Round(angle * 8 / (Math.PI * 2)) * (Math.PI * 2) / 8;
+    }
+    public static double FindSnappingAngle(ShapeCorners corners, double desiredAngle)
+    {
+        var desTop = (corners.TopLeft - corners.TopRight).Rotate(desiredAngle).Angle;
+        var desRight = (corners.TopRight - corners.BottomRight).Rotate(desiredAngle).Angle;
+        var desBottom = (corners.BottomRight - corners.BottomLeft).Rotate(desiredAngle).Angle;
+        var desLeft = (corners.BottomLeft - corners.TopLeft).Rotate(desiredAngle).Angle;
+
+        var deltaTop = GetSnappingAngle(desTop) - desTop;
+        var deltaRight = GetSnappingAngle(desRight) - desRight;
+        var deltaLeft = GetSnappingAngle(desLeft) - desLeft;
+        var deltaBottom = GetSnappingAngle(desBottom) - desBottom;
+
+        var minDelta = deltaTop;
+        if (Math.Abs(minDelta) > Math.Abs(deltaRight))
+            minDelta = deltaRight;
+        if (Math.Abs(minDelta) > Math.Abs(deltaLeft))
+            minDelta = deltaLeft;
+        if (Math.Abs(minDelta) > Math.Abs(deltaBottom))
+            minDelta = deltaBottom;
+        return minDelta + desiredAngle;
+    }
+
     public static Vector2d OriginFromCorners(ShapeCorners corners)
     {
         var maybeOrigin = TwoLineIntersection(

+ 26 - 17
src/PixiEditorPrototype/CustomControls/TransformOverlay/TransformOverlay.cs

@@ -27,16 +27,25 @@ internal class TransformOverlay : Control
 
     public static readonly DependencyProperty SideFreedomProperty =
         DependencyProperty.Register(nameof(SideFreedom), typeof(TransformSideFreedom), typeof(TransformOverlay),
-            new FrameworkPropertyMetadata(TransformSideFreedom.Locked));
+            new PropertyMetadata(TransformSideFreedom.Locked));
 
     public static readonly DependencyProperty CornerFreedomProperty =
         DependencyProperty.Register(nameof(CornerFreedom), typeof(TransformCornerFreedom), typeof(TransformOverlay),
-            new FrameworkPropertyMetadata(TransformCornerFreedom.Locked));
+            new PropertyMetadata(TransformCornerFreedom.Locked));
+
+    public static readonly DependencyProperty SnapToAnglesProperty =
+        DependencyProperty.Register(nameof(SnapToAngles), typeof(bool), typeof(TransformOverlay), new PropertyMetadata(false));
+
+    public bool SnapToAngles
+    {
+        get => (bool)GetValue(SnapToAnglesProperty);
+        set => SetValue(SnapToAnglesProperty, value);
+    }
 
     public TransformCornerFreedom CornerFreedom
     {
-        get { return (TransformCornerFreedom)GetValue(CornerFreedomProperty); }
-        set { SetValue(CornerFreedomProperty, value); }
+        get => (TransformCornerFreedom)GetValue(CornerFreedomProperty);
+        set => SetValue(CornerFreedomProperty, value);
     }
 
     public TransformSideFreedom SideFreedom
@@ -70,11 +79,11 @@ internal class TransformOverlay : Control
     private bool isMoving = false;
     private Vector2d mousePosOnStartMove = new();
     private Vector2d originOnStartMove = new();
-    private ShapeCorners preciseCornersOnStartMove = new();
+    private ShapeCorners cornersOnStartMove = new();
 
     private bool isRotating = false;
     private Vector2d mousePosOnStartRotate = new();
-    private ShapeCorners preciseCornersOnStartRotate = new();
+    private ShapeCorners cornersOnStartRotate = new();
     private double proportionalAngle1 = 0;
     private double proportionalAngle2 = 0;
     private double propAngle1OnStartRotate = 0;
@@ -82,7 +91,6 @@ internal class TransformOverlay : Control
 
     private Anchor? capturedAnchor;
     private bool originWasManuallyDragged = false;
-    private ShapeCorners preciseCornersOnStartAnchorDrag;
     private ShapeCorners cornersOnStartAnchorDrag;
     private Vector2d mousePosOnStartAnchorDrag;
     private Vector2d originOnStartAnchorDrag;
@@ -103,7 +111,6 @@ internal class TransformOverlay : Control
         if (anchor is not null)
         {
             capturedAnchor = anchor;
-            preciseCornersOnStartAnchorDrag = Corners;
             cornersOnStartAnchorDrag = Corners;
             originOnStartAnchorDrag = Origin;
             mousePosOnStartAnchorDrag = pos;
@@ -113,13 +120,13 @@ internal class TransformOverlay : Control
             isMoving = true;
             mousePosOnStartMove = TransformHelper.ToVector2d(e.GetPosition(this));
             originOnStartMove = Origin;
-            preciseCornersOnStartMove = Corners;
+            cornersOnStartMove = Corners;
         }
         else
         {
             isRotating = true;
             mousePosOnStartRotate = TransformHelper.ToVector2d(e.GetPosition(this));
-            preciseCornersOnStartRotate = Corners;
+            cornersOnStartRotate = Corners;
             propAngle1OnStartRotate = proportionalAngle1;
             propAngle2OnStartRotate = proportionalAngle2;
         }
@@ -143,10 +150,10 @@ internal class TransformOverlay : Control
 
             Corners = new ShapeCorners()
             {
-                BottomLeft = preciseCornersOnStartMove.BottomLeft + delta,
-                BottomRight = preciseCornersOnStartMove.BottomRight + delta,
-                TopLeft = preciseCornersOnStartMove.TopLeft + delta,
-                TopRight = preciseCornersOnStartMove.TopRight + delta,
+                BottomLeft = cornersOnStartMove.BottomLeft + delta,
+                BottomRight = cornersOnStartMove.BottomRight + delta,
+                TopLeft = cornersOnStartMove.TopLeft + delta,
+                TopRight = cornersOnStartMove.TopRight + delta,
             };
 
             Origin = originOnStartMove + delta;
@@ -155,9 +162,11 @@ internal class TransformOverlay : Control
         {
             var pos = TransformHelper.ToVector2d(e.GetPosition(this));
             var angle = (mousePosOnStartRotate - Origin).CCWAngleTo(pos - Origin);
+            if (SnapToAngles)
+                angle = TransformHelper.FindSnappingAngle(cornersOnStartRotate, angle);
             proportionalAngle1 = propAngle1OnStartRotate + angle;
             proportionalAngle2 = propAngle2OnStartRotate + angle;
-            Corners = TransformUpdateHelper.UpdateShapeFromRotation(preciseCornersOnStartRotate, Origin, angle);
+            Corners = TransformUpdateHelper.UpdateShapeFromRotation(cornersOnStartRotate, Origin, angle);
         }
     }
 
@@ -175,7 +184,7 @@ internal class TransformOverlay : Control
         if (TransformHelper.IsCorner((Anchor)capturedAnchor))
         {
             var targetPos = TransformHelper.GetAnchorPosition(cornersOnStartAnchorDrag, (Anchor)capturedAnchor) + pos - mousePosOnStartAnchorDrag;
-            var newCorners = TransformUpdateHelper.UpdateShapeFromCorner((Anchor)capturedAnchor, CornerFreedom, proportionalAngle1, proportionalAngle2, preciseCornersOnStartAnchorDrag, targetPos);
+            var newCorners = TransformUpdateHelper.UpdateShapeFromCorner((Anchor)capturedAnchor, CornerFreedom, proportionalAngle1, proportionalAngle2, cornersOnStartAnchorDrag, targetPos);
             if (newCorners is not null)
             {
                 bool shouldSnap = (CornerFreedom is TransformCornerFreedom.ScaleProportionally or TransformCornerFreedom.Scale) && Corners.IsSnappedToPixels;
@@ -187,7 +196,7 @@ internal class TransformOverlay : Control
         else if (TransformHelper.IsSide((Anchor)capturedAnchor))
         {
             var targetPos = TransformHelper.GetAnchorPosition(cornersOnStartAnchorDrag, (Anchor)capturedAnchor) + pos - mousePosOnStartAnchorDrag;
-            var newCorners = TransformUpdateHelper.UpdateShapeFromSide((Anchor)capturedAnchor, SideFreedom, proportionalAngle1, proportionalAngle2, preciseCornersOnStartAnchorDrag, targetPos);
+            var newCorners = TransformUpdateHelper.UpdateShapeFromSide((Anchor)capturedAnchor, SideFreedom, proportionalAngle1, proportionalAngle2, cornersOnStartAnchorDrag, targetPos);
             if (newCorners is not null)
             {
                 bool shouldSnap = (SideFreedom is TransformSideFreedom.ScaleProportionally or TransformSideFreedom.Stretch) && Corners.IsSnappedToPixels;

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

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