Browse Source

Transform overlay use initial proportions for proportional scaling

Equbuxu 3 years ago
parent
commit
99c4c45286

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

@@ -25,6 +25,7 @@ public struct ShapeCorners
         }
     }
     public bool HasNaNOrInfinity => TopLeft.IsNaNOrInfinity() || TopRight.IsNaNOrInfinity() || BottomLeft.IsNaNOrInfinity() || BottomRight.IsNaNOrInfinity();
+    public bool IsRect => Math.Abs((TopLeft - BottomRight).Length - (TopRight - BottomLeft).Length) < 0.001;
     public Vector2d RectSize => new((TopLeft - TopRight).Length, (TopLeft - BottomLeft).Length);
     public Vector2d RectCenter => (TopLeft - BottomRight) / 2 + BottomRight;
     public double RectRotation =>

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

@@ -124,7 +124,10 @@ internal static class TransformHelper
         };
     }
 
-    public static (Anchor, Anchor) GetCornersOnSide(Anchor side)
+    /// <summary>
+    /// The first anchor would be on your left if you were standing on the side and looking inside the shape; the second anchor is to the right.
+    /// </summary>
+    public static (Anchor leftAnchor, Anchor rightAnchor) GetCornersOnSide(Anchor side)
     {
         return side switch
         {
@@ -136,14 +139,17 @@ internal static class TransformHelper
         };
     }
 
+    /// <summary>
+    /// The first corner would be on your left if you were standing on the passed corner and looking inside the shape; the second corner is to the right.
+    /// </summary>
     public static (Anchor, Anchor) GetNeighboringCorners(Anchor corner)
     {
         return corner switch
         {
             Anchor.TopLeft => (Anchor.TopRight, Anchor.BottomLeft),
-            Anchor.TopRight => (Anchor.TopLeft, Anchor.BottomRight),
+            Anchor.TopRight => (Anchor.BottomRight, Anchor.TopLeft),
             Anchor.BottomLeft => (Anchor.TopLeft, Anchor.BottomRight),
-            Anchor.BottomRight => (Anchor.TopRight, Anchor.BottomLeft),
+            Anchor.BottomRight => (Anchor.BottomLeft, Anchor.TopRight),
             _ => throw new ArgumentException($"{corner} is not a corner anchor"),
         };
     }

+ 13 - 3
src/PixiEditorPrototype/CustomControls/TransformOverlay/TransformOverlay.cs

@@ -75,6 +75,10 @@ internal class TransformOverlay : Control
     private bool isRotating = false;
     private Vector2d mousePosOnStartRotate = new();
     private ShapeCorners preciseCornersOnStartRotate = new();
+    private double proportionalAngle1 = 0;
+    private double proportionalAngle2 = 0;
+    private double propAngle1OnStartRotate = 0;
+    private double propAngle2OnStartRotate = 0;
 
     private Anchor? capturedAnchor;
     private bool originWasManuallyDragged = false;
@@ -116,6 +120,8 @@ internal class TransformOverlay : Control
             isRotating = true;
             mousePosOnStartRotate = TransformHelper.ToVector2d(e.GetPosition(this));
             preciseCornersOnStartRotate = Corners;
+            propAngle1OnStartRotate = proportionalAngle1;
+            propAngle2OnStartRotate = proportionalAngle2;
         }
         CaptureMouse();
     }
@@ -149,7 +155,9 @@ internal class TransformOverlay : Control
         {
             var pos = TransformHelper.ToVector2d(e.GetPosition(this));
             var angle = (mousePosOnStartRotate - Origin).CCWAngleTo(pos - Origin);
-            Corners = Corners = TransformUpdateHelper.UpdateShapeFromRotation(preciseCornersOnStartRotate, Origin, angle);
+            proportionalAngle1 = propAngle1OnStartRotate + angle;
+            proportionalAngle2 = propAngle2OnStartRotate + angle;
+            Corners = TransformUpdateHelper.UpdateShapeFromRotation(preciseCornersOnStartRotate, Origin, angle);
         }
     }
 
@@ -167,7 +175,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, preciseCornersOnStartAnchorDrag, targetPos);
+            var newCorners = TransformUpdateHelper.UpdateShapeFromCorner((Anchor)capturedAnchor, CornerFreedom, proportionalAngle1, proportionalAngle2, preciseCornersOnStartAnchorDrag, targetPos);
             if (newCorners is not null)
             {
                 bool shouldSnap = (CornerFreedom is TransformCornerFreedom.ScaleProportionally or TransformCornerFreedom.Scale) && Corners.IsSnappedToPixels;
@@ -179,7 +187,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, preciseCornersOnStartAnchorDrag, targetPos);
+            var newCorners = TransformUpdateHelper.UpdateShapeFromSide((Anchor)capturedAnchor, SideFreedom, proportionalAngle1, proportionalAngle2, preciseCornersOnStartAnchorDrag, targetPos);
             if (newCorners is not null)
             {
                 bool shouldSnap = (SideFreedom is TransformSideFreedom.ScaleProportionally or TransformSideFreedom.Stretch) && Corners.IsSnappedToPixels;
@@ -219,6 +227,8 @@ internal class TransformOverlay : Control
         TransformOverlay overlay = (TransformOverlay)obj;
         overlay.originWasManuallyDragged = false;
         overlay.Corners = (ShapeCorners)args.NewValue;
+        overlay.proportionalAngle1 = (overlay.Corners.BottomRight - overlay.Corners.TopLeft).Angle;
+        overlay.proportionalAngle2 = (overlay.Corners.TopRight - overlay.Corners.BottomLeft).Angle;
         overlay.Origin = TransformHelper.OriginFromCorners(overlay.Corners);
     }
 

+ 80 - 18
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 desiredPos)
+        (Anchor targetCorner, TransformCornerFreedom freedom, double propAngle1, double propAngle2, ShapeCorners corners, Vector2d desiredPos)
     {
         if (!TransformHelper.IsCorner(targetCorner))
             throw new ArgumentException($"{targetCorner} is not a corner");
@@ -20,25 +20,28 @@ internal static class TransformUpdateHelper
             var oppositePos = TransformHelper.GetAnchorPosition(corners, opposite);
 
             if (freedom == TransformCornerFreedom.ScaleProportionally)
-                desiredPos = desiredPos.ProjectOntoLine(targetPos, oppositePos);
+            {
+                double correctAngle = targetCorner is Anchor.TopLeft or Anchor.BottomRight ? propAngle1 : propAngle2;
+                desiredPos = desiredPos.ProjectOntoLine(oppositePos, oppositePos + Vector2d.FromAngleAndLength(correctAngle, 1));
+            }
 
-            var (neighbor1, neighbor2) = TransformHelper.GetNeighboringCorners(targetCorner);
-            var neighbor1Pos = TransformHelper.GetAnchorPosition(corners, neighbor1);
-            var neighbor2Pos = TransformHelper.GetAnchorPosition(corners, neighbor2);
+            var (leftNeighbor, rightNeighbor) = TransformHelper.GetNeighboringCorners(targetCorner);
+            var leftNeighborPos = TransformHelper.GetAnchorPosition(corners, leftNeighbor);
+            var rightNeighborPos = TransformHelper.GetAnchorPosition(corners, rightNeighbor);
 
             double angle = corners.RectRotation;
             var targetTrans = (targetPos - oppositePos).Rotate(-angle);
-            var neigh1Trans = (neighbor1Pos - oppositePos).Rotate(-angle);
-            var neigh2Trans = (neighbor2Pos - oppositePos).Rotate(-angle);
+            var leftNeighTrans = (leftNeighborPos - oppositePos).Rotate(-angle);
+            var rightNeighTrans = (rightNeighborPos - oppositePos).Rotate(-angle);
 
             Vector2d delta = (desiredPos - targetPos).Rotate(-angle);
 
             corners = TransformHelper.UpdateCorner(corners, targetCorner,
                 (targetTrans + delta).Rotate(angle) + oppositePos);
-            corners = TransformHelper.UpdateCorner(corners, neighbor1,
-                (neigh1Trans + delta.Multiply(neigh1Trans.Divide(targetTrans))).Rotate(angle) + oppositePos);
-            corners = TransformHelper.UpdateCorner(corners, neighbor2,
-                (neigh2Trans + delta.Multiply(neigh2Trans.Divide(targetTrans))).Rotate(angle) + oppositePos);
+            corners = TransformHelper.UpdateCorner(corners, leftNeighbor,
+                (leftNeighTrans + delta.Multiply(leftNeighTrans.Divide(targetTrans))).Rotate(angle) + oppositePos);
+            corners = TransformHelper.UpdateCorner(corners, rightNeighbor,
+                (rightNeighTrans + delta.Multiply(rightNeighTrans.Divide(targetTrans))).Rotate(angle) + oppositePos);
 
             return corners;
         }
@@ -53,7 +56,7 @@ internal static class TransformUpdateHelper
     }
 
     public static ShapeCorners? UpdateShapeFromSide
-        (Anchor targetSide, TransformSideFreedom freedom, ShapeCorners corners, Vector2d desiredPos)
+        (Anchor targetSide, TransformSideFreedom freedom, double propAngle1, double propAngle2, ShapeCorners corners, Vector2d desiredPos)
     {
         if (!TransformHelper.IsSide(targetSide))
             throw new ArgumentException($"{targetSide} is not a side");
@@ -63,6 +66,33 @@ internal static class TransformUpdateHelper
 
         if (freedom is TransformSideFreedom.ScaleProportionally)
         {
+            /*
+            var targetPos = TransformHelper.GetAnchorPosition(corners, targetSide);
+            var opposite = TransformHelper.GetOpposite(targetSide);
+            var oppositePos = TransformHelper.GetAnchorPosition(corners, opposite);
+
+            Vector2d thing = targetPos - oppositePos;
+            thing = Vector2d.FromAngleAndLength(thing.Angle, 1 / thing.Length);
+            double scalingFactor = (desiredPos - oppositePos) * thing;
+            Vector2d scalingVector = new(1, scalingFactor / 2);
+            corners.TopLeft = ((corners.TopLeft - oppositePos).Rotate(-propAngle1).Multiply(scalingVector)).Rotate(propAngle1) + oppositePos;
+            corners.TopRight = ((corners.TopRight - oppositePos).Rotate(-propAngle1).Multiply(scalingVector)).Rotate(propAngle1) + oppositePos;
+            corners.BottomLeft = ((corners.BottomLeft - oppositePos).Rotate(-propAngle1).Multiply(scalingVector)).Rotate(propAngle1) + oppositePos;
+            corners.BottomRight = ((corners.BottomRight - oppositePos).Rotate(-propAngle1).Multiply(scalingVector)).Rotate(propAngle1) + oppositePos;
+
+            corners.TopLeft = ((corners.TopLeft - oppositePos).Rotate(-propAngle2).Multiply(scalingVector)).Rotate(propAngle2) + oppositePos;
+            corners.TopRight = ((corners.TopRight - oppositePos).Rotate(-propAngle2).Multiply(scalingVector)).Rotate(propAngle2) + oppositePos;
+            corners.BottomLeft = ((corners.BottomLeft - oppositePos).Rotate(-propAngle2).Multiply(scalingVector)).Rotate(propAngle2) + oppositePos;
+            corners.BottomRight = ((corners.BottomRight - oppositePos).Rotate(-propAngle2).Multiply(scalingVector)).Rotate(propAngle2) + oppositePos;
+
+            return corners;
+            */
+
+            //var (corner1, corner2) = TransformHelper.GetCornersOnSide(targetSide);
+            //UpdateShapeFromCorner(corner1, TransformCornerFreedom.ScaleProportionally, propAngle1, propAngle2, corners,)
+
+
+
             var targetPos = TransformHelper.GetAnchorPosition(corners, targetSide);
             var opposite = TransformHelper.GetOpposite(targetSide);
             var oppositePos = TransformHelper.GetAnchorPosition(corners, opposite);
@@ -75,6 +105,38 @@ internal static class TransformUpdateHelper
             if (!double.IsNormal(scalingFactor))
                 return corners;
 
+            if (corners.IsRect)
+            {
+                var delta = desiredPos - targetPos;
+                var center = oppositePos.Lerp(desiredPos, 0.5);
+
+                var (leftCorn, rightCorn) = TransformHelper.GetCornersOnSide(targetSide);
+                var (leftOppCorn, _) = TransformHelper.GetNeighboringCorners(leftCorn);
+                var (_, rightOppCorn) = TransformHelper.GetNeighboringCorners(rightCorn);
+
+                var leftCornPos = TransformHelper.GetAnchorPosition(corners, leftCorn);
+                var rightCornPos = TransformHelper.GetAnchorPosition(corners, rightCorn);
+                var leftOppCornPos = TransformHelper.GetAnchorPosition(corners, leftOppCorn);
+                var rightOppCornPos = TransformHelper.GetAnchorPosition(corners, rightOppCorn);
+
+                var (leftAngle, rightAngle) = leftCorn is Anchor.TopLeft or Anchor.BottomRight ? (propAngle1, propAngle2) : (propAngle2, propAngle1);
+
+                var updLeftCorn = TransformHelper.TwoLineIntersection(leftCornPos + delta, rightCornPos + delta, center, center + Vector2d.FromAngleAndLength(leftAngle, 1));
+                var updRightCorn = TransformHelper.TwoLineIntersection(leftCornPos + delta, rightCornPos + delta, center, center + Vector2d.FromAngleAndLength(rightAngle, 1));
+                var updLeftOppCorn = TransformHelper.TwoLineIntersection(leftOppCornPos, rightOppCornPos, center, center + Vector2d.FromAngleAndLength(rightAngle, 1));
+                var updRightOppCorn = TransformHelper.TwoLineIntersection(leftOppCornPos, rightOppCornPos, center, center + Vector2d.FromAngleAndLength(leftAngle, 1));
+
+                if (updLeftCorn is null || updRightCorn is null || updLeftOppCorn is null || updRightOppCorn is null)
+                    goto fallback;
+
+                corners = TransformHelper.UpdateCorner(corners, leftCorn, (Vector2d)updLeftCorn);
+                corners = TransformHelper.UpdateCorner(corners, rightCorn, (Vector2d)updRightCorn);
+                corners = TransformHelper.UpdateCorner(corners, leftOppCorn, (Vector2d)updLeftOppCorn);
+                corners = TransformHelper.UpdateCorner(corners, rightOppCorn, (Vector2d)updRightOppCorn);
+
+                return corners;
+            }
+fallback:
             corners.TopLeft = (corners.TopLeft - oppositePos) * scalingFactor + oppositePos;
             corners.BottomRight = (corners.BottomRight - oppositePos) * scalingFactor + oppositePos;
             corners.TopRight = (corners.TopRight - oppositePos) * scalingFactor + oppositePos;
@@ -93,22 +155,22 @@ internal static class TransformUpdateHelper
 
         if (freedom is TransformSideFreedom.Shear or TransformSideFreedom.Stretch or TransformSideFreedom.Free)
         {
-            var (side1, side2) = TransformHelper.GetCornersOnSide(targetSide);
-            var side1Pos = TransformHelper.GetAnchorPosition(corners, side1);
-            var side2Pos = TransformHelper.GetAnchorPosition(corners, side2);
+            var (leftCorner, rightCorner) = TransformHelper.GetCornersOnSide(targetSide);
+            var leftCornerPos = TransformHelper.GetAnchorPosition(corners, leftCorner);
+            var rightCornerPos = TransformHelper.GetAnchorPosition(corners, rightCorner);
             var targetPos = TransformHelper.GetAnchorPosition(corners, targetSide);
 
             var opposite = TransformHelper.GetOpposite(targetSide);
             var oppPos = TransformHelper.GetAnchorPosition(corners, opposite);
 
             if (freedom == TransformSideFreedom.Shear)
-                desiredPos = desiredPos.ProjectOntoLine(side1Pos, side2Pos);
+                desiredPos = desiredPos.ProjectOntoLine(leftCornerPos, rightCornerPos);
             else if (freedom == TransformSideFreedom.Stretch)
                 desiredPos = desiredPos.ProjectOntoLine(targetPos, oppPos);
 
             var delta = desiredPos - targetPos;
-            var newCorners = TransformHelper.UpdateCorner(corners, side1, side1Pos + delta);
-            newCorners = TransformHelper.UpdateCorner(newCorners, side2, side2Pos + delta);
+            var newCorners = TransformHelper.UpdateCorner(corners, leftCorner, leftCornerPos + delta);
+            newCorners = TransformHelper.UpdateCorner(newCorners, rightCorner, rightCornerPos + delta);
 
             return newCorners.IsLegal ? newCorners : null;
         }

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

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