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.
     ///     Gets the intersection of closest snap axis along projected axis.
     /// </summary>
     /// </summary>
     /// <param name="pos">Position to snap</param>
     /// <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="xAxis">Intersected horizontal axis</param>
     /// <param name="yAxis">Intersected vertical 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)
     public VecD GetSnapPoint(VecD pos, VecD direction, out string xAxis, out string yAxis)
     {
     {
         if (!SnappingEnabled)
         if (!SnappingEnabled)
@@ -277,7 +277,7 @@ public class SnappingController
             return pos;
             return pos;
         }
         }
 
 
-        if (direction.X == 0 || direction.Y == 0)
+        if (direction is { X: 0, Y: 0 })
         {
         {
             return GetSnapPoint(pos, out xAxis, out yAxis);
             return GetSnapPoint(pos, out xAxis, out yAxis);
         }
         }
@@ -286,14 +286,17 @@ public class SnappingController
 
 
         double? closestX = closestXAxis != string.Empty ? snapDelta.X : null;
         double? closestX = closestXAxis != string.Empty ? snapDelta.X : null;
         double? closestY = closestYAxis != string.Empty ? snapDelta.Y : null;
         double? closestY = closestYAxis != string.Empty ? snapDelta.Y : null;
-
-
+        
         VecD? xIntersect = null;
         VecD? xIntersect = null;
         if (closestX != null)
         if (closestX != null)
         {
         {
             double x = closestX.Value;
             double x = closestX.Value;
             double y = pos.Y + direction.Y * (x - pos.X) / direction.X;
             double y = pos.Y + direction.Y * (x - pos.X) / direction.X;
             xIntersect = new VecD(x, y);
             xIntersect = new VecD(x, y);
+            if (xIntersect.Value.IsNaNOrInfinity())
+            {
+                xIntersect = null;
+            }
         }
         }
 
 
         VecD? yIntersect = null;
         VecD? yIntersect = null;
@@ -302,6 +305,10 @@ public class SnappingController
             double y = closestY.Value;
             double y = closestY.Value;
             double x = pos.X + direction.X * (y - pos.Y) / direction.Y;
             double x = pos.X + direction.X * (y - pos.Y) / direction.Y;
             yIntersect = new VecD(x, y);
             yIntersect = new VecD(x, y);
+            if (yIntersect.Value.IsNaNOrInfinity())
+            {
+                yIntersect = null;
+            }
         }
         }
 
 
         if (xIntersect.HasValue && yIntersect.HasValue)
         if (xIntersect.HasValue && yIntersect.HasValue)
@@ -309,27 +316,41 @@ public class SnappingController
             if (Math.Abs(xIntersect.Value.X - yIntersect.Value.X) < float.Epsilon
             if (Math.Abs(xIntersect.Value.X - yIntersect.Value.X) < float.Epsilon
                 && Math.Abs(xIntersect.Value.Y - yIntersect.Value.Y) < 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 xDist = (xIntersect.Value - pos).LengthSquared;
             double yDist = (yIntersect.Value - pos).LengthSquared;
             double yDist = (yIntersect.Value - pos).LengthSquared;
 
 
-            if (xDist < yDist)
+            if (xDist < yDist && IsWithinSnapDistance(xIntersect.Value, pos))
             {
             {
                 xAxis = closestXAxis;
                 xAxis = closestXAxis;
                 yAxis = null;
                 yAxis = null;
                 return xIntersect.Value;
                 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;
             xAxis = closestXAxis;
             yAxis = null;
             yAxis = null;
@@ -337,7 +358,7 @@ public class SnappingController
             return xIntersect.Value;
             return xIntersect.Value;
         }
         }
 
 
-        if (yIntersect != null)
+        if (yIntersect != null && IsWithinSnapDistance(yIntersect.Value, pos))
         {
         {
             xAxis = null;
             xAxis = null;
             yAxis = closestYAxis;
             yAxis = closestYAxis;
@@ -355,4 +376,9 @@ public class SnappingController
         HorizontalSnapPoints[identifier] = () => pointFunc().X;
         HorizontalSnapPoints[identifier] = () => pointFunc().X;
         VerticalSnapPoints[identifier] = () => pointFunc().Y;
         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);
             ActionCompleted.Execute(null);
 
 
         IsSizeBoxEnabled = false;
         IsSizeBoxEnabled = false;
+        
+        SnappingController.HighlightedXAxis = string.Empty;
+        SnappingController.HighlightedYAxis = string.Empty;
     }
     }
 
 
     private Handle? GetSnapHandleOfOrigin()
     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.Backend.Core.Numerics;
 using Drawie.Numerics;
 using Drawie.Numerics;
 using PixiEditor.Models.Controllers.InputDevice;
 using PixiEditor.Models.Controllers.InputDevice;
@@ -172,7 +173,8 @@ internal static class TransformUpdateHelper
 
 
     public static ShapeCorners? UpdateShapeFromSide
     public static ShapeCorners? UpdateShapeFromSide
     (Anchor targetSide, TransformSideFreedom freedom, double propAngle1, double propAngle2, ShapeCorners corners,
     (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))
         if (!TransformHelper.IsSide(targetSide))
             throw new ArgumentException($"{targetSide} is not a side");
             throw new ArgumentException($"{targetSide} is not a side");
@@ -195,7 +197,8 @@ internal static class TransformUpdateHelper
 
 
             if (snappingController is not null)
             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;
             double scalingFactor = (desiredPos - oppositePos) * direction;
@@ -204,9 +207,24 @@ internal static class TransformUpdateHelper
 
 
             if (corners.IsRect)
             if (corners.IsRect)
             {
             {
-                var delta = desiredPos - targetPos;
+                var delta = (desiredPos - targetPos);
                 var center = oppositePos.Lerp(desiredPos, 0.5);
                 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 (leftCorn, rightCorn) = TransformHelper.GetCornersOnSide(targetSide);
                 var (leftOppCorn, _) = TransformHelper.GetNeighboringCorners(leftCorn);
                 var (leftOppCorn, _) = TransformHelper.GetNeighboringCorners(leftCorn);
                 var (_, rightOppCorn) = TransformHelper.GetNeighboringCorners(rightCorn);
                 var (_, rightOppCorn) = TransformHelper.GetNeighboringCorners(rightCorn);
@@ -225,23 +243,10 @@ internal static class TransformUpdateHelper
                 var updRightCorn = TransformHelper.TwoLineIntersection(leftCornPos + delta, rightCornPos + delta,
                 var updRightCorn = TransformHelper.TwoLineIntersection(leftCornPos + delta, rightCornPos + delta,
                     center, center + VecD.FromAngleAndLength(rightAngle, 1));
                     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)
                 if (updLeftCorn is null || updRightCorn is null || updLeftOppCorn is null || updRightOppCorn is null)
                     goto fallback;
                     goto fallback;
@@ -284,6 +289,11 @@ internal static class TransformUpdateHelper
             if (freedom == TransformSideFreedom.Shear)
             if (freedom == TransformSideFreedom.Shear)
             {
             {
                 desiredPos = desiredPos.ProjectOntoLine(leftCornerPos, rightCornerPos);
                 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)
             else if (freedom == TransformSideFreedom.Stretch)
             {
             {
@@ -292,10 +302,16 @@ internal static class TransformUpdateHelper
                 else
                 else
                     desiredPos = desiredPos.ProjectOntoLine(targetPos,
                     desiredPos = desiredPos.ProjectOntoLine(targetPos,
                         (leftCornerPos - targetPos).Rotate(Math.PI / 2) + 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 delta = desiredPos - targetPos;
-            
+
             var newCorners = TransformHelper.UpdateCorner(corners, leftCorner, leftCornerPos + delta);
             var newCorners = TransformHelper.UpdateCorner(corners, leftCorner, leftCornerPos + delta);
             newCorners = TransformHelper.UpdateCorner(newCorners, rightCorner, rightCornerPos + delta);
             newCorners = TransformHelper.UpdateCorner(newCorners, rightCorner, rightCornerPos + delta);
 
 
@@ -304,15 +320,15 @@ internal static class TransformUpdateHelper
                 VecD oppositeDelta = -delta;
                 VecD oppositeDelta = -delta;
                 Anchor leftCornerOpp = TransformHelper.GetOpposite(leftCorner);
                 Anchor leftCornerOpp = TransformHelper.GetOpposite(leftCorner);
                 Anchor rightCornerOpp = TransformHelper.GetOpposite(rightCorner);
                 Anchor rightCornerOpp = TransformHelper.GetOpposite(rightCorner);
-                
+
                 var leftCornerOppPos = TransformHelper.GetAnchorPosition(corners, leftCornerOpp);
                 var leftCornerOppPos = TransformHelper.GetAnchorPosition(corners, leftCornerOpp);
                 var rightCornerOppPos = TransformHelper.GetAnchorPosition(corners, rightCornerOpp);
                 var rightCornerOppPos = TransformHelper.GetAnchorPosition(corners, rightCornerOpp);
-                
+
                 newCorners = TransformHelper.UpdateCorner(newCorners, leftCornerOpp, leftCornerOppPos + oppositeDelta);
                 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;
             return newCorners.IsLegal ? newCorners : null;
         }
         }