Browse Source

scaling to center and snapping fixes/improvements

flabbet 8 months ago
parent
commit
dc2a5971ae

+ 40 - 14
src/PixiEditor/Models/Controllers/InputDevice/SnappingController.cs

@@ -264,10 +264,10 @@ public class SnappingController
     ///     Gets the intersection of closest snap axis along projected axis.
     /// </summary>
     /// <param name="pos">Position to snap</param>
-    /// <param name="direction">Direction to project from <paramref name="pos">/></param>
+    /// <param name="direction">Direction to project from position</param>
     /// <param name="xAxis">Intersected horizontal axis</param>
     /// <param name="yAxis">Intersected vertical axis</param>
-    /// <returns>Snapped position to the closest snap point along projected axis from <paramref name="pos">/></returns> 
+    /// <returns>Snapped position to the closest snap point along projected axis from position.</returns>
     public VecD GetSnapPoint(VecD pos, VecD direction, out string xAxis, out string yAxis)
     {
         if (!SnappingEnabled)
@@ -277,7 +277,7 @@ public class SnappingController
             return pos;
         }
 
-        if (direction.X == 0 || direction.Y == 0)
+        if (direction is { X: 0, Y: 0 })
         {
             return GetSnapPoint(pos, out xAxis, out yAxis);
         }
@@ -286,14 +286,17 @@ public class SnappingController
 
         double? closestX = closestXAxis != string.Empty ? snapDelta.X : null;
         double? closestY = closestYAxis != string.Empty ? snapDelta.Y : null;
-
-
+        
         VecD? xIntersect = null;
         if (closestX != null)
         {
             double x = closestX.Value;
             double y = pos.Y + direction.Y * (x - pos.X) / direction.X;
             xIntersect = new VecD(x, y);
+            if (xIntersect.Value.IsNaNOrInfinity())
+            {
+                xIntersect = null;
+            }
         }
 
         VecD? yIntersect = null;
@@ -302,6 +305,10 @@ public class SnappingController
             double y = closestY.Value;
             double x = pos.X + direction.X * (y - pos.Y) / direction.Y;
             yIntersect = new VecD(x, y);
+            if (yIntersect.Value.IsNaNOrInfinity())
+            {
+                yIntersect = null;
+            }
         }
 
         if (xIntersect.HasValue && yIntersect.HasValue)
@@ -309,27 +316,41 @@ public class SnappingController
             if (Math.Abs(xIntersect.Value.X - yIntersect.Value.X) < float.Epsilon
                 && Math.Abs(xIntersect.Value.Y - yIntersect.Value.Y) < float.Epsilon)
             {
-                xAxis = closestXAxis;
-                yAxis = closestYAxis;
-                return xIntersect.Value;
+                if(IsWithinSnapDistance(xIntersect.Value, pos))
+                {
+                    xAxis = closestXAxis;
+                    yAxis = closestYAxis;
+                    return xIntersect.Value;
+                }
+                
+                xAxis = string.Empty;
+                yAxis = string.Empty;
+                return pos;
             }
 
             double xDist = (xIntersect.Value - pos).LengthSquared;
             double yDist = (yIntersect.Value - pos).LengthSquared;
 
-            if (xDist < yDist)
+            if (xDist < yDist && IsWithinSnapDistance(xIntersect.Value, pos))
             {
                 xAxis = closestXAxis;
                 yAxis = null;
                 return xIntersect.Value;
             }
 
-            xAxis = null;
-            yAxis = closestYAxis;
-            return yIntersect.Value;
+            if (IsWithinSnapDistance(yIntersect.Value, pos))
+            {
+                xAxis = null;
+                yAxis = closestYAxis;
+                return yIntersect.Value;
+            }
+            
+            xAxis = string.Empty;
+            yAxis = string.Empty;
+            return pos;
         }
 
-        if (xIntersect != null)
+        if (xIntersect != null && IsWithinSnapDistance(xIntersect.Value, pos))
         {
             xAxis = closestXAxis;
             yAxis = null;
@@ -337,7 +358,7 @@ public class SnappingController
             return xIntersect.Value;
         }
 
-        if (yIntersect != null)
+        if (yIntersect != null && IsWithinSnapDistance(yIntersect.Value, pos))
         {
             xAxis = null;
             yAxis = closestYAxis;
@@ -355,4 +376,9 @@ public class SnappingController
         HorizontalSnapPoints[identifier] = () => pointFunc().X;
         VerticalSnapPoints[identifier] = () => pointFunc().Y;
     }
+    
+    private bool IsWithinSnapDistance(VecD snapPoint, VecD pos)
+    {
+        return (snapPoint - pos).LengthSquared < SnapDistance * SnapDistance;
+    }
 }

+ 3 - 0
src/PixiEditor/Views/Overlays/TransformOverlay/TransformOverlay.cs

@@ -1104,6 +1104,9 @@ internal class TransformOverlay : Overlay
             ActionCompleted.Execute(null);
 
         IsSizeBoxEnabled = false;
+        
+        SnappingController.HighlightedXAxis = string.Empty;
+        SnappingController.HighlightedYAxis = string.Empty;
     }
 
     private Handle? GetSnapHandleOfOrigin()

+ 42 - 26
src/PixiEditor/Views/Overlays/TransformOverlay/TransformUpdateHelper.cs

@@ -1,4 +1,5 @@
-using ChunkyImageLib.DataHolders;
+using Avalonia;
+using ChunkyImageLib.DataHolders;
 using Drawie.Backend.Core.Numerics;
 using Drawie.Numerics;
 using PixiEditor.Models.Controllers.InputDevice;
@@ -172,7 +173,8 @@ internal static class TransformUpdateHelper
 
     public static ShapeCorners? UpdateShapeFromSide
     (Anchor targetSide, TransformSideFreedom freedom, double propAngle1, double propAngle2, ShapeCorners corners,
-        VecD desiredPos, bool scaleFromCenter, SnappingController? snappingController, out string snapX, out string snapY)
+        VecD desiredPos, bool scaleFromCenter, SnappingController? snappingController, out string snapX,
+        out string snapY)
     {
         if (!TransformHelper.IsSide(targetSide))
             throw new ArgumentException($"{targetSide} is not a side");
@@ -195,7 +197,8 @@ internal static class TransformUpdateHelper
 
             if (snappingController is not null)
             {
-                desiredPos = snappingController.GetSnapPoint(desiredPos, direction, out snapX, out snapY);
+                VecD scaleDirection = corners.RectCenter - oppositePos;
+                desiredPos = snappingController.GetSnapPoint(desiredPos, scaleDirection, out snapX, out snapY);
             }
 
             double scalingFactor = (desiredPos - oppositePos) * direction;
@@ -204,9 +207,24 @@ internal static class TransformUpdateHelper
 
             if (corners.IsRect)
             {
-                var delta = desiredPos - targetPos;
+                var delta = (desiredPos - targetPos);
                 var center = oppositePos.Lerp(desiredPos, 0.5);
 
+                if (scaleFromCenter)
+                {
+                    VecD currentCenter = corners.RectCenter;
+                    float targetPosToCenter = (float)(targetPos - currentCenter).Length;
+                    
+                    VecD reflectedDesiredPos = desiredPos.ReflectAcrossLine(currentCenter, targetPos);
+                    
+                    float desiredPosToCenter = (float)(reflectedDesiredPos - currentCenter).Length;
+                    
+                    float scaling = desiredPosToCenter / targetPosToCenter;
+
+                    corners = corners.AsScaled(scaling);
+                    return corners;
+                }
+
                 var (leftCorn, rightCorn) = TransformHelper.GetCornersOnSide(targetSide);
                 var (leftOppCorn, _) = TransformHelper.GetNeighboringCorners(leftCorn);
                 var (_, rightOppCorn) = TransformHelper.GetNeighboringCorners(rightCorn);
@@ -225,23 +243,10 @@ internal static class TransformUpdateHelper
                 var updRightCorn = TransformHelper.TwoLineIntersection(leftCornPos + delta, rightCornPos + delta,
                     center, center + VecD.FromAngleAndLength(rightAngle, 1));
 
-                VecD? updLeftOppCorn = null, updRightOppCorn = null;
-                if (!scaleFromCenter)
-                {
-                    updLeftOppCorn = TransformHelper.TwoLineIntersection(leftOppCornPos, rightOppCornPos, center,
-                        center + VecD.FromAngleAndLength(rightAngle, 1));
-                    updRightOppCorn = TransformHelper.TwoLineIntersection(leftOppCornPos, rightOppCornPos, center,
-                        center + VecD.FromAngleAndLength(leftAngle, 1));
-                }
-                else if(updLeftCorn is not null && updRightCorn is not null)
-                {
-                    // Mirror the corners across the center
-                    VecD mirrorLeftOppCorn = 2 * center - ((VecD)updRightCorn + delta);
-                    VecD mirrorRightOppCorn = 2 * center - ((VecD)updLeftCorn + delta);
-
-                    updLeftOppCorn = mirrorLeftOppCorn;
-                    updRightOppCorn = mirrorRightOppCorn; 
-                }
+                var updLeftOppCorn = TransformHelper.TwoLineIntersection(leftOppCornPos, rightOppCornPos, center,
+                    center + VecD.FromAngleAndLength(rightAngle, 1));
+                var updRightOppCorn = TransformHelper.TwoLineIntersection(leftOppCornPos, rightOppCornPos, center,
+                    center + VecD.FromAngleAndLength(leftAngle, 1));
 
                 if (updLeftCorn is null || updRightCorn is null || updLeftOppCorn is null || updRightOppCorn is null)
                     goto fallback;
@@ -284,6 +289,11 @@ internal static class TransformUpdateHelper
             if (freedom == TransformSideFreedom.Shear)
             {
                 desiredPos = desiredPos.ProjectOntoLine(leftCornerPos, rightCornerPos);
+                if (snappingController is not null)
+                {
+                    VecD direction = corners.RectCenter - desiredPos;
+                    desiredPos = snappingController.GetSnapPoint(desiredPos, direction, out snapX, out snapY);
+                }
             }
             else if (freedom == TransformSideFreedom.Stretch)
             {
@@ -292,10 +302,16 @@ internal static class TransformUpdateHelper
                 else
                     desiredPos = desiredPos.ProjectOntoLine(targetPos,
                         (leftCornerPos - targetPos).Rotate(Math.PI / 2) + targetPos);
+
+                if (snappingController is not null)
+                {
+                    VecD direction = corners.RectCenter - desiredPos;
+                    desiredPos = snappingController.GetSnapPoint(desiredPos, direction, out snapX, out snapY);
+                }
             }
 
             var delta = desiredPos - targetPos;
-            
+
             var newCorners = TransformHelper.UpdateCorner(corners, leftCorner, leftCornerPos + delta);
             newCorners = TransformHelper.UpdateCorner(newCorners, rightCorner, rightCornerPos + delta);
 
@@ -304,15 +320,15 @@ internal static class TransformUpdateHelper
                 VecD oppositeDelta = -delta;
                 Anchor leftCornerOpp = TransformHelper.GetOpposite(leftCorner);
                 Anchor rightCornerOpp = TransformHelper.GetOpposite(rightCorner);
-                
+
                 var leftCornerOppPos = TransformHelper.GetAnchorPosition(corners, leftCornerOpp);
                 var rightCornerOppPos = TransformHelper.GetAnchorPosition(corners, rightCornerOpp);
-                
+
                 newCorners = TransformHelper.UpdateCorner(newCorners, leftCornerOpp, leftCornerOppPos + oppositeDelta);
-                newCorners = TransformHelper.UpdateCorner(newCorners, rightCornerOpp, rightCornerOppPos + oppositeDelta);
+                newCorners =
+                    TransformHelper.UpdateCorner(newCorners, rightCornerOpp, rightCornerOppPos + oppositeDelta);
             }
 
-
             return newCorners.IsLegal ? newCorners : null;
         }