Browse Source

Snapping Overlay

flabbet 11 months ago
parent
commit
51d6e01034

+ 6 - 0
src/PixiEditor.UI.Common/Accents/Base.axaml

@@ -80,6 +80,9 @@
             <Color x:Key="NumbersCategoryBackgroundColor">#666666</Color>
             <Color x:Key="NumbersCategoryBackgroundColor">#666666</Color>
             <Color x:Key="ColorCategoryBackgroundColor">#3B665D</Color>
             <Color x:Key="ColorCategoryBackgroundColor">#3B665D</Color>
             <Color x:Key="AnimationCategoryBackgroundColor">#4D4466</Color>
             <Color x:Key="AnimationCategoryBackgroundColor">#4D4466</Color>
+            
+            <Color x:Key="HorizontalSnapAxisColor">#B00022</Color>
+            <Color x:Key="VerticalSnapAxisColor">#5fad65</Color>
 
 
             <system:Double x:Key="ThemeDisabledOpacity">0.4</system:Double>
             <system:Double x:Key="ThemeDisabledOpacity">0.4</system:Double>
 
 
@@ -163,6 +166,9 @@
             <SolidColorBrush x:Key="ColorCategoryBackgroundBrush" Color="{StaticResource ColorCategoryBackgroundColor}" />
             <SolidColorBrush x:Key="ColorCategoryBackgroundBrush" Color="{StaticResource ColorCategoryBackgroundColor}" />
             <SolidColorBrush x:Key="AnimationCategoryBackgroundBrush" Color="{StaticResource AnimationCategoryBackgroundColor}" />
             <SolidColorBrush x:Key="AnimationCategoryBackgroundBrush" Color="{StaticResource AnimationCategoryBackgroundColor}" />
 
 
+            <SolidColorBrush x:Key="HorizontalSnapAxisBrush" Color="{StaticResource HorizontalSnapAxisColor}"/>
+            <SolidColorBrush x:Key="VerticalSnapAxisBrush" Color="{StaticResource VerticalSnapAxisColor}"/>
+            
             <CornerRadius x:Key="ControlCornerRadius">5</CornerRadius>
             <CornerRadius x:Key="ControlCornerRadius">5</CornerRadius>
             <CornerRadius x:Key="ControlCornerRadiusTop">5, 5, 0, 0</CornerRadius>
             <CornerRadius x:Key="ControlCornerRadiusTop">5, 5, 0, 0</CornerRadius>
             <system:Double x:Key="ControlCornerRadiusValue">5</system:Double>
             <system:Double x:Key="ControlCornerRadiusValue">5</system:Double>

+ 22 - 11
src/PixiEditor/Models/Controllers/InputDevice/SnappingController.cs

@@ -12,51 +12,62 @@ public class SnappingController
     public double SnapDistance { get; set; } = DefaultSnapDistance;
     public double SnapDistance { get; set; } = DefaultSnapDistance;
 
 
     public Dictionary<string, double> HorizontalSnapPoints { get; } = new();
     public Dictionary<string, double> HorizontalSnapPoints { get; } = new();
-    public Dictionary<string, double> VerticalSnapPoints { get; } = new(); 
+    public Dictionary<string, double> VerticalSnapPoints { get; } = new();
     
     
+    public string HighlightedXAxis { get; set; } = string.Empty;
+    public string HighlightedYAxis { get; set; } = string.Empty;
     
     
-    public double? SnapToHorizontal(double xPos)
+    
+    public double? SnapToHorizontal(double xPos, out string snapAxis)
     {
     {
         if (HorizontalSnapPoints.Count == 0)
         if (HorizontalSnapPoints.Count == 0)
         {
         {
+            snapAxis = string.Empty;
             return null;
             return null;
         }
         }
-
+        
+        snapAxis = HorizontalSnapPoints.First().Key;
         double closest = HorizontalSnapPoints.First().Value;
         double closest = HorizontalSnapPoints.First().Value;
-        foreach (double snapPoint in HorizontalSnapPoints.Values)
+        foreach (var snapPoint in HorizontalSnapPoints)
         {
         {
-            if (Math.Abs(snapPoint - xPos) < Math.Abs(closest - xPos))
+            if (Math.Abs(snapPoint.Value - xPos) < Math.Abs(closest - xPos))
             {
             {
-                closest = snapPoint;
+                closest = snapPoint.Value;
+                snapAxis = snapPoint.Key;
             }
             }
         }
         }
         
         
         if (Math.Abs(closest - xPos) > SnapDistance)
         if (Math.Abs(closest - xPos) > SnapDistance)
         {
         {
+            snapAxis = string.Empty;
             return null;
             return null;
         }
         }
-
+        
         return closest;
         return closest;
     }
     }
     
     
-    public double? SnapToVertical(double yPos)
+    public double? SnapToVertical(double yPos, out string snapAxisKey)
     {
     {
         if (VerticalSnapPoints.Count == 0)
         if (VerticalSnapPoints.Count == 0)
         {
         {
+            snapAxisKey = string.Empty;
             return null;
             return null;
         }
         }
 
 
+        snapAxisKey = VerticalSnapPoints.First().Key;
         double closest = VerticalSnapPoints.First().Value;
         double closest = VerticalSnapPoints.First().Value;
-        foreach (double snapPoint in VerticalSnapPoints.Values)
+        foreach (var snapPoint in VerticalSnapPoints)
         {
         {
-            if (Math.Abs(snapPoint - yPos) < Math.Abs(closest - yPos))
+            if (Math.Abs(snapPoint.Value - yPos) < Math.Abs(closest - yPos))
             {
             {
-                closest = snapPoint;
+                closest = snapPoint.Value;
+                snapAxisKey = snapPoint.Key;
             }
             }
         }
         }
         
         
         if (Math.Abs(closest - yPos) > SnapDistance)
         if (Math.Abs(closest - yPos) > SnapDistance)
         {
         {
+            snapAxisKey = string.Empty;
             return null;
             return null;
         }
         }
 
 

+ 18 - 0
src/PixiEditor/Views/Main/ViewportControls/ViewportOverlays.cs

@@ -25,6 +25,7 @@ internal class ViewportOverlays
     private LineToolOverlay lineToolOverlay;
     private LineToolOverlay lineToolOverlay;
     private TransformOverlay transformOverlay;
     private TransformOverlay transformOverlay;
     private ReferenceLayerOverlay referenceLayerOverlay;
     private ReferenceLayerOverlay referenceLayerOverlay;
+    private SnappingOverlay snappingOverlay;
 
 
     public void Init(Viewport viewport)
     public void Init(Viewport viewport)
     {
     {
@@ -46,6 +47,10 @@ internal class ViewportOverlays
 
 
         referenceLayerOverlay = new ReferenceLayerOverlay();
         referenceLayerOverlay = new ReferenceLayerOverlay();
         BindReferenceLayerOverlay();
         BindReferenceLayerOverlay();
+        
+        snappingOverlay = new SnappingOverlay();
+        BindSnappingOverlay();
+        
 
 
         Viewport.ActiveOverlays.Add(gridLinesOverlay);
         Viewport.ActiveOverlays.Add(gridLinesOverlay);
         Viewport.ActiveOverlays.Add(referenceLayerOverlay);
         Viewport.ActiveOverlays.Add(referenceLayerOverlay);
@@ -53,6 +58,7 @@ internal class ViewportOverlays
         Viewport.ActiveOverlays.Add(symmetryOverlay);
         Viewport.ActiveOverlays.Add(symmetryOverlay);
         Viewport.ActiveOverlays.Add(lineToolOverlay);
         Viewport.ActiveOverlays.Add(lineToolOverlay);
         Viewport.ActiveOverlays.Add(transformOverlay);
         Viewport.ActiveOverlays.Add(transformOverlay);
+        Viewport.ActiveOverlays.Add(snappingOverlay);
     }
     }
 
 
     private void BindReferenceLayerOverlay()
     private void BindReferenceLayerOverlay()
@@ -308,4 +314,16 @@ internal class ViewportOverlays
         transformOverlay.Bind(TransformOverlay.InternalStateProperty, internalStateBinding);
         transformOverlay.Bind(TransformOverlay.InternalStateProperty, internalStateBinding);
         transformOverlay.Bind(TransformOverlay.ZoomboxAngleProperty, zoomboxAngleBinding);
         transformOverlay.Bind(TransformOverlay.ZoomboxAngleProperty, zoomboxAngleBinding);
     }
     }
+
+    private void BindSnappingOverlay()
+    {
+        Binding snappingControllerBinding = new()
+        {
+            Source = Viewport,
+            Path = "Document.SnappingViewModel.SnappingController",
+            Mode = BindingMode.OneWay
+        };
+        
+        snappingOverlay.Bind(SnappingOverlay.SnappingControllerProperty, snappingControllerBinding);
+    }
 }
 }

+ 60 - 0
src/PixiEditor/Views/Overlays/SnappingOverlay.cs

@@ -0,0 +1,60 @@
+using Avalonia;
+using Avalonia.Media;
+using Avalonia.Styling;
+using PixiEditor.Models.Controllers.InputDevice;
+using PixiEditor.Numerics;
+using Point = Avalonia.Point;
+
+namespace PixiEditor.Views.Overlays;
+
+internal class SnappingOverlay : Overlay
+{
+    public static readonly StyledProperty<SnappingController?> SnappingControllerProperty = AvaloniaProperty.Register<SnappingOverlay, SnappingController?>(
+        nameof(SnappingController));
+
+    public SnappingController? SnappingController
+    {
+        get => GetValue(SnappingControllerProperty);
+        set => SetValue(SnappingControllerProperty, value);
+    }
+
+    private Pen horizontalAxisPen;
+    private Pen verticalAxisPen; 
+    
+    public SnappingOverlay()
+    {
+        /*TODO: Theme variant is not present, that's why Dark is hardcoded*/        
+        horizontalAxisPen = Application.Current.Styles.TryGetResource("HorizontalSnapAxisBrush", ThemeVariant.Dark, out var horizontalAxisBrush) ? new Pen((IBrush)horizontalAxisBrush, 0.2f) : new Pen(Brushes.Red, 0.2f);
+        verticalAxisPen = Application.Current.Styles.TryGetResource("VerticalSnapAxisBrush", ThemeVariant.Dark, out var verticalAxisBrush) ? new Pen((IBrush)verticalAxisBrush, 0.2f) : new Pen(Brushes.Green, 0.2f);
+    }
+
+    public override void RenderOverlay(DrawingContext context, RectD canvasBounds)
+    {
+        if (SnappingController is null)
+        {
+            return;
+        }
+
+        if (!string.IsNullOrEmpty(SnappingController.HighlightedXAxis))
+        {
+            foreach (var snapPoint in SnappingController.HorizontalSnapPoints)
+            {
+                if (snapPoint.Key == SnappingController.HighlightedXAxis)
+                {
+                    context.DrawLine(horizontalAxisPen, new Point(snapPoint.Value, 0), new Point(snapPoint.Value, canvasBounds.Height));
+                }
+            }
+        }
+        
+        if (!string.IsNullOrEmpty(SnappingController.HighlightedYAxis))
+        {
+            foreach (var snapPoint in SnappingController.VerticalSnapPoints)
+            {
+                if (snapPoint.Key == SnappingController.HighlightedYAxis)
+                {
+                    context.DrawLine(verticalAxisPen, new Point(0, snapPoint.Value), new Point(canvasBounds.Width, snapPoint.Value));
+                }
+            }
+        }
+    }
+}

+ 86 - 48
src/PixiEditor/Views/Overlays/TransformOverlay/TransformOverlay.cs

@@ -31,7 +31,8 @@ internal class TransformOverlay : Overlay
     }
     }
 
 
     public static readonly StyledProperty<TransformSideFreedom> SideFreedomProperty =
     public static readonly StyledProperty<TransformSideFreedom> SideFreedomProperty =
-        AvaloniaProperty.Register<TransformOverlay, TransformSideFreedom>(nameof(SideFreedom), defaultValue: TransformSideFreedom.Locked);
+        AvaloniaProperty.Register<TransformOverlay, TransformSideFreedom>(nameof(SideFreedom),
+            defaultValue: TransformSideFreedom.Locked);
 
 
     public TransformSideFreedom SideFreedom
     public TransformSideFreedom SideFreedom
     {
     {
@@ -40,7 +41,8 @@ internal class TransformOverlay : Overlay
     }
     }
 
 
     public static readonly StyledProperty<TransformCornerFreedom> CornerFreedomProperty =
     public static readonly StyledProperty<TransformCornerFreedom> CornerFreedomProperty =
-        AvaloniaProperty.Register<TransformOverlay, TransformCornerFreedom>(nameof(CornerFreedom), defaultValue: TransformCornerFreedom.Locked);
+        AvaloniaProperty.Register<TransformOverlay, TransformCornerFreedom>(nameof(CornerFreedom),
+            defaultValue: TransformCornerFreedom.Locked);
 
 
     public TransformCornerFreedom CornerFreedom
     public TransformCornerFreedom CornerFreedom
     {
     {
@@ -67,7 +69,8 @@ internal class TransformOverlay : Overlay
     }
     }
 
 
     public static readonly StyledProperty<TransformState> InternalStateProperty =
     public static readonly StyledProperty<TransformState> InternalStateProperty =
-        AvaloniaProperty.Register<TransformOverlay, TransformState>(nameof(InternalState), defaultValue: default(TransformState));
+        AvaloniaProperty.Register<TransformOverlay, TransformState>(nameof(InternalState),
+            defaultValue: default(TransformState));
 
 
     public TransformState InternalState
     public TransformState InternalState
     {
     {
@@ -93,8 +96,9 @@ internal class TransformOverlay : Overlay
         set => SetValue(CoverWholeScreenProperty, value);
         set => SetValue(CoverWholeScreenProperty, value);
     }
     }
 
 
-    public static readonly StyledProperty<ExecutionTrigger<ShapeCorners>> RequestCornersExecutorProperty = AvaloniaProperty.Register<TransformOverlay, ExecutionTrigger<ShapeCorners>>(
-        nameof(RequestCornersExecutor));
+    public static readonly StyledProperty<ExecutionTrigger<ShapeCorners>> RequestCornersExecutorProperty =
+        AvaloniaProperty.Register<TransformOverlay, ExecutionTrigger<ShapeCorners>>(
+            nameof(RequestCornersExecutor));
 
 
     public ExecutionTrigger<ShapeCorners> RequestCornersExecutor
     public ExecutionTrigger<ShapeCorners> RequestCornersExecutor
     {
     {
@@ -111,8 +115,9 @@ internal class TransformOverlay : Overlay
         set => SetValue(ActionCompletedProperty, value);
         set => SetValue(ActionCompletedProperty, value);
     }
     }
 
 
-    public static readonly StyledProperty<bool> SnappingEnabledProperty = AvaloniaProperty.Register<TransformOverlay, bool>(
-        nameof(SnappingEnabled), defaultValue: true);
+    public static readonly StyledProperty<bool> SnappingEnabledProperty =
+        AvaloniaProperty.Register<TransformOverlay, bool>(
+            nameof(SnappingEnabled), defaultValue: true);
 
 
     public bool SnappingEnabled
     public bool SnappingEnabled
     {
     {
@@ -120,8 +125,9 @@ internal class TransformOverlay : Overlay
         set => SetValue(SnappingEnabledProperty, value);
         set => SetValue(SnappingEnabledProperty, value);
     }
     }
 
 
-    public static readonly StyledProperty<SnappingController> SnappingControllerProperty = AvaloniaProperty.Register<TransformOverlay, SnappingController>(
-        nameof(SnappingController));
+    public static readonly StyledProperty<SnappingController> SnappingControllerProperty =
+        AvaloniaProperty.Register<TransformOverlay, SnappingController>(
+            nameof(SnappingController));
 
 
     public SnappingController SnappingController
     public SnappingController SnappingController
     {
     {
@@ -131,7 +137,9 @@ internal class TransformOverlay : Overlay
 
 
     static TransformOverlay()
     static TransformOverlay()
     {
     {
-        AffectsRender<TransformOverlay>(CornersProperty, ZoomScaleProperty, SideFreedomProperty, CornerFreedomProperty, LockRotationProperty, SnapToAnglesProperty, InternalStateProperty, ZoomboxAngleProperty, CoverWholeScreenProperty);
+        AffectsRender<TransformOverlay>(CornersProperty, ZoomScaleProperty, SideFreedomProperty, CornerFreedomProperty,
+            LockRotationProperty, SnapToAnglesProperty, InternalStateProperty, ZoomboxAngleProperty,
+            CoverWholeScreenProperty);
         RequestCornersExecutorProperty.Changed.Subscribe(OnCornersExecutorChanged);
         RequestCornersExecutorProperty.Changed.Subscribe(OnCornersExecutorChanged);
     }
     }
 
 
@@ -195,7 +203,9 @@ internal class TransformOverlay : Overlay
 
 
         originHandle = new(this)
         originHandle = new(this)
         {
         {
-            HandlePen = blackFreqDashedPen, SecondaryHandlePen = whiteFreqDashedPen, HandleBrush = Brushes.Transparent
+            HandlePen = blackFreqDashedPen,
+            SecondaryHandlePen = whiteFreqDashedPen,
+            HandleBrush = Brushes.Transparent
         };
         };
 
 
         AddHandle(originHandle);
         AddHandle(originHandle);
@@ -248,7 +258,8 @@ internal class TransformOverlay : Overlay
     {
     {
         if (CoverWholeScreen)
         if (CoverWholeScreen)
         {
         {
-            context.DrawRectangle(Brushes.Transparent, null, new Rect(new Point(-size.X * 50, -size.Y * 50), new Size(size.X * 101, size.Y * 101)));
+            context.DrawRectangle(Brushes.Transparent, null,
+                new Rect(new Point(-size.X * 50, -size.Y * 50), new Size(size.X * 101, size.Y * 101)));
             return;
             return;
         }
         }
 
 
@@ -295,7 +306,7 @@ internal class TransformOverlay : Overlay
         context.DrawLine(whiteDashedPen, TransformHelper.ToPoint(bottomRight), TransformHelper.ToPoint(bottomLeft));
         context.DrawLine(whiteDashedPen, TransformHelper.ToPoint(bottomRight), TransformHelper.ToPoint(bottomLeft));
         context.DrawLine(blackDashedPen, TransformHelper.ToPoint(bottomRight), TransformHelper.ToPoint(topRight));
         context.DrawLine(blackDashedPen, TransformHelper.ToPoint(bottomRight), TransformHelper.ToPoint(topRight));
         context.DrawLine(whiteDashedPen, TransformHelper.ToPoint(bottomRight), TransformHelper.ToPoint(topRight));
         context.DrawLine(whiteDashedPen, TransformHelper.ToPoint(bottomRight), TransformHelper.ToPoint(topRight));
-        
+
         // corner anchors
         // corner anchors
 
 
         centerHandle.Position = VecD.Zero;
         centerHandle.Position = VecD.Zero;
@@ -360,7 +371,7 @@ internal class TransformOverlay : Overlay
         if (args.PointerButton != MouseButton.Left)
         if (args.PointerButton != MouseButton.Left)
             return;
             return;
 
 
-        if(Handles.Any(x => x.IsWithinHandle(x.Position, args.Point, ZoomScale))) return;
+        if (Handles.Any(x => x.IsWithinHandle(x.Position, args.Point, ZoomScale))) return;
 
 
         if (!CanRotate(args.Point))
         if (!CanRotate(args.Point))
         {
         {
@@ -378,7 +389,7 @@ internal class TransformOverlay : Overlay
         {
         {
             return;
             return;
         }
         }
-        
+
         args.Pointer.Capture(this);
         args.Pointer.Capture(this);
         args.Handled = true;
         args.Handled = true;
     }
     }
@@ -407,7 +418,8 @@ internal class TransformOverlay : Overlay
             finalCursor = new Cursor(StandardCursorType.None);
             finalCursor = new Cursor(StandardCursorType.None);
         }
         }
 
 
-        Anchor? anchor = TransformHelper.GetAnchorInPosition(pos, Corners, InternalState.Origin, ZoomScale, topLeftHandle.Size);
+        Anchor? anchor =
+            TransformHelper.GetAnchorInPosition(pos, Corners, InternalState.Origin, ZoomScale, topLeftHandle.Size);
 
 
         if (isRotating)
         if (isRotating)
         {
         {
@@ -461,6 +473,9 @@ internal class TransformOverlay : Overlay
 
 
         if (ActionCompleted is not null && ActionCompleted.CanExecute(null))
         if (ActionCompleted is not null && ActionCompleted.CanExecute(null))
             ActionCompleted.Execute(null);
             ActionCompleted.Execute(null);
+        
+        SnappingController.HighlightedXAxis = string.Empty;
+        SnappingController.HighlightedYAxis = string.Empty;
     }
     }
 
 
     private void StartMoving(VecD position)
     private void StartMoving(VecD position)
@@ -485,9 +500,14 @@ internal class TransformOverlay : Overlay
             TopLeft = cornersOnStartMove.TopLeft + delta,
             TopLeft = cornersOnStartMove.TopLeft + delta,
             TopRight = cornersOnStartMove.TopRight + delta,
             TopRight = cornersOnStartMove.TopRight + delta,
         };
         };
-        
-        VecD snapDelta = TrySnapCorners(rawCorners);
-        
+
+        ((string, string), VecD) snapDeltaResult = TrySnapCorners(rawCorners);
+
+        VecD snapDelta = snapDeltaResult.Item2;
+
+        SnappingController.HighlightedXAxis = snapDeltaResult.Item1.Item1;
+        SnappingController.HighlightedYAxis = snapDeltaResult.Item1.Item2;
+
         Corners = new ShapeCorners()
         Corners = new ShapeCorners()
         {
         {
             BottomLeft = cornersOnStartMove.BottomLeft + delta + snapDelta,
             BottomLeft = cornersOnStartMove.BottomLeft + delta + snapDelta,
@@ -499,50 +519,52 @@ internal class TransformOverlay : Overlay
         InternalState = InternalState with { Origin = originOnStartMove + delta + snapDelta };
         InternalState = InternalState with { Origin = originOnStartMove + delta + snapDelta };
     }
     }
 
 
-    private VecD TrySnapCorners(ShapeCorners rawCorners)
+    private ((string, string), VecD) TrySnapCorners(ShapeCorners rawCorners)
     {
     {
         if (!SnappingEnabled || SnappingController is null)
         if (!SnappingEnabled || SnappingController is null)
         {
         {
-            return VecD.Zero;
+            return ((string.Empty, string.Empty), VecD.Zero);
         }
         }
-        
+
         VecD[] pointsToTest = new VecD[]
         VecD[] pointsToTest = new VecD[]
         {
         {
-            rawCorners.RectCenter,
-            rawCorners.TopLeft, 
-            rawCorners.TopRight, 
-            rawCorners.BottomLeft, 
+            rawCorners.RectCenter, rawCorners.TopLeft, rawCorners.TopRight, rawCorners.BottomLeft,
             rawCorners.BottomRight
             rawCorners.BottomRight
         };
         };
-        
+
         VecD snapDelta = new();
         VecD snapDelta = new();
         bool hasXSnap = false;
         bool hasXSnap = false;
         bool hasYSnap = false;
         bool hasYSnap = false;
-        
+
+        string snapAxisX = string.Empty;
+        string snapAxisY = string.Empty;
+
         foreach (var point in pointsToTest)
         foreach (var point in pointsToTest)
         {
         {
-            double? snapX = SnappingController.SnapToHorizontal(point.X);
-            double? snapY = SnappingController.SnapToVertical(point.Y);
-            
+            double? snapX = SnappingController.SnapToHorizontal(point.X, out string newSnapAxisX);
+            double? snapY = SnappingController.SnapToVertical(point.Y, out string newSnapAxisY);
+
             if (snapX is not null && !hasXSnap)
             if (snapX is not null && !hasXSnap)
             {
             {
                 snapDelta += new VecD(snapX.Value - point.X, 0);
                 snapDelta += new VecD(snapX.Value - point.X, 0);
+                snapAxisX = newSnapAxisX;
                 hasXSnap = true;
                 hasXSnap = true;
             }
             }
-            
+
             if (snapY is not null && !hasYSnap)
             if (snapY is not null && !hasYSnap)
             {
             {
                 snapDelta += new VecD(0, snapY.Value - point.Y);
                 snapDelta += new VecD(0, snapY.Value - point.Y);
+                snapAxisY = newSnapAxisY;
                 hasYSnap = true;
                 hasYSnap = true;
             }
             }
-            
+
             if (hasXSnap && hasYSnap)
             if (hasXSnap && hasYSnap)
             {
             {
                 break;
                 break;
             }
             }
         }
         }
-        
-        return snapDelta;
+
+        return ((snapAxisX, snapAxisY), snapDelta);
     }
     }
 
 
     private Cursor HandleRotate(VecD pos)
     private Cursor HandleRotate(VecD pos)
@@ -554,7 +576,8 @@ internal class TransformOverlay : Overlay
             angle = TransformHelper.FindSnappingAngle(cornersOnStartRotate, angle);
             angle = TransformHelper.FindSnappingAngle(cornersOnStartRotate, angle);
         InternalState = InternalState with
         InternalState = InternalState with
         {
         {
-            ProportionalAngle1 = propAngle1OnStartRotate + angle, ProportionalAngle2 = propAngle2OnStartRotate + angle,
+            ProportionalAngle1 = propAngle1OnStartRotate + angle,
+            ProportionalAngle2 = propAngle2OnStartRotate + angle,
         };
         };
 
 
         Corners = TransformUpdateHelper.UpdateShapeFromRotation(cornersOnStartRotate, InternalState.Origin, angle);
         Corners = TransformUpdateHelper.UpdateShapeFromRotation(cornersOnStartRotate, InternalState.Origin, angle);
@@ -564,7 +587,8 @@ internal class TransformOverlay : Overlay
 
 
     private bool CanRotate(VecD mousePos)
     private bool CanRotate(VecD mousePos)
     {
     {
-        return !Corners.IsPointInside(mousePos) && Handles.All(x => !x.IsWithinHandle(x.Position, mousePos, ZoomScale)) && TestHit(mousePos);
+        return !Corners.IsPointInside(mousePos) &&
+               Handles.All(x => !x.IsWithinHandle(x.Position, mousePos, ZoomScale)) && TestHit(mousePos);
     }
     }
 
 
     private bool UpdateRotationCursor(VecD mousePos)
     private bool UpdateRotationCursor(VecD mousePos)
@@ -596,26 +620,40 @@ internal class TransformOverlay : Overlay
 
 
         if (TransformHelper.IsCorner((Anchor)capturedAnchor))
         if (TransformHelper.IsCorner((Anchor)capturedAnchor))
         {
         {
-            VecD targetPos = TransformHelper.GetAnchorPosition(cornersOnStartAnchorDrag, (Anchor)capturedAnchor) + pos - mousePosOnStartAnchorDrag;
+            VecD targetPos = TransformHelper.GetAnchorPosition(cornersOnStartAnchorDrag, (Anchor)capturedAnchor) + pos -
+                             mousePosOnStartAnchorDrag;
             ShapeCorners? newCorners = TransformUpdateHelper.UpdateShapeFromCorner
             ShapeCorners? newCorners = TransformUpdateHelper.UpdateShapeFromCorner
-                ((Anchor)capturedAnchor, CornerFreedom, InternalState.ProportionalAngle1, InternalState.ProportionalAngle2, cornersOnStartAnchorDrag, targetPos);
+            ((Anchor)capturedAnchor, CornerFreedom, InternalState.ProportionalAngle1,
+                InternalState.ProportionalAngle2, cornersOnStartAnchorDrag, targetPos);
             if (newCorners is not null)
             if (newCorners is not null)
             {
             {
-                bool shouldSnap = (CornerFreedom is TransformCornerFreedom.ScaleProportionally or TransformCornerFreedom.Scale) && Corners.IsSnappedToPixels;
-                Corners = shouldSnap ? TransformHelper.SnapToPixels((ShapeCorners)newCorners) : (ShapeCorners)newCorners;
+                bool shouldSnap =
+                    (CornerFreedom is TransformCornerFreedom.ScaleProportionally or TransformCornerFreedom.Scale) &&
+                    Corners.IsSnappedToPixels;
+                Corners = shouldSnap
+                    ? TransformHelper.SnapToPixels((ShapeCorners)newCorners)
+                    : (ShapeCorners)newCorners;
             }
             }
+
             UpdateOriginPos();
             UpdateOriginPos();
         }
         }
         else if (TransformHelper.IsSide((Anchor)capturedAnchor))
         else if (TransformHelper.IsSide((Anchor)capturedAnchor))
         {
         {
-            VecD targetPos = TransformHelper.GetAnchorPosition(cornersOnStartAnchorDrag, (Anchor)capturedAnchor) + pos - mousePosOnStartAnchorDrag;
+            VecD targetPos = TransformHelper.GetAnchorPosition(cornersOnStartAnchorDrag, (Anchor)capturedAnchor) + pos -
+                             mousePosOnStartAnchorDrag;
             ShapeCorners? newCorners = TransformUpdateHelper.UpdateShapeFromSide
             ShapeCorners? newCorners = TransformUpdateHelper.UpdateShapeFromSide
-                ((Anchor)capturedAnchor, SideFreedom, InternalState.ProportionalAngle1, InternalState.ProportionalAngle2, cornersOnStartAnchorDrag, targetPos);
+            ((Anchor)capturedAnchor, SideFreedom, InternalState.ProportionalAngle1,
+                InternalState.ProportionalAngle2, cornersOnStartAnchorDrag, targetPos);
             if (newCorners is not null)
             if (newCorners is not null)
             {
             {
-                bool shouldSnap = (SideFreedom is TransformSideFreedom.ScaleProportionally or TransformSideFreedom.Stretch) && Corners.IsSnappedToPixels;
-                Corners = shouldSnap ? TransformHelper.SnapToPixels((ShapeCorners)newCorners) : (ShapeCorners)newCorners;
+                bool shouldSnap =
+                    (SideFreedom is TransformSideFreedom.ScaleProportionally or TransformSideFreedom.Stretch) &&
+                    Corners.IsSnappedToPixels;
+                Corners = shouldSnap
+                    ? TransformHelper.SnapToPixels((ShapeCorners)newCorners)
+                    : (ShapeCorners)newCorners;
             }
             }
+
             UpdateOriginPos();
             UpdateOriginPos();
         }
         }
         else if (capturedAnchor == Anchor.Origin)
         else if (capturedAnchor == Anchor.Origin)
@@ -665,7 +703,7 @@ internal class TransformOverlay : Overlay
     {
     {
         capturedAnchor = null;
         capturedAnchor = null;
 
 
-        if(source == originHandle)
+        if (source == originHandle)
         {
         {
             snapHandleOfOrigin = GetSnapHandleOfOrigin();
             snapHandleOfOrigin = GetSnapHandleOfOrigin();
             InternalState = InternalState with { OriginWasManuallyDragged = snapHandleOfOrigin is null };
             InternalState = InternalState with { OriginWasManuallyDragged = snapHandleOfOrigin is null };
@@ -695,7 +733,7 @@ internal class TransformOverlay : Overlay
     {
     {
         isMoving = false;
         isMoving = false;
         isRotating = false;
         isRotating = false;
-        Corners = corners; 
+        Corners = corners;
         InternalState = new()
         InternalState = new()
         {
         {
             ProportionalAngle1 = (Corners.BottomRight - Corners.TopLeft).Angle,
             ProportionalAngle1 = (Corners.BottomRight - Corners.TopLeft).Angle,
@@ -704,7 +742,7 @@ internal class TransformOverlay : Overlay
             Origin = TransformHelper.OriginFromCorners(Corners),
             Origin = TransformHelper.OriginFromCorners(Corners),
         };
         };
     }
     }
-    
+
     private static void OnCornersExecutorChanged(AvaloniaPropertyChangedEventArgs<ExecutionTrigger<ShapeCorners>> args)
     private static void OnCornersExecutorChanged(AvaloniaPropertyChangedEventArgs<ExecutionTrigger<ShapeCorners>> args)
     {
     {
         TransformOverlay overlay = (TransformOverlay)args.Sender;
         TransformOverlay overlay = (TransformOverlay)args.Sender;