Pārlūkot izejas kodu

Fixed dirty rects for scene and overlays

Krzysztof Krysiński 1 gadu atpakaļ
vecāks
revīzija
a68599149e

+ 5 - 5
src/ChunkyImageLib/DataHolders/ShapeCorners.cs

@@ -146,12 +146,12 @@ public struct ShapeCorners
         TopRight = TopRight.ReflectX(verAxisX)
     };
 
-    public ShapeCorners AsRotated(double angle, VecD around) => new ShapeCorners
+    public ShapeCorners AsRotated(double angleRad, VecD around) => new ShapeCorners
     {
-        BottomLeft = BottomLeft.Rotate(angle, around),
-        BottomRight = BottomRight.Rotate(angle, around),
-        TopLeft = TopLeft.Rotate(angle, around),
-        TopRight = TopRight.Rotate(angle, around)
+        BottomLeft = BottomLeft.Rotate(angleRad, around),
+        BottomRight = BottomRight.Rotate(angleRad, around),
+        TopLeft = TopLeft.Rotate(angleRad, around),
+        TopRight = TopRight.Rotate(angleRad, around)
     };
 
     public ShapeCorners AsTranslated(VecD delta) => new ShapeCorners

+ 7 - 2
src/PixiEditor.AvaloniaUI/Helpers/MathUtil.cs

@@ -1,9 +1,14 @@
 namespace PixiEditor.AvaloniaUI.Helpers;
 
-public class MathUtil
+public static class MathUtil
 {
-    public static double AngleToRadians(double angle)
+    public static double DegreesToRadians(double angle)
     {
         return angle * Math.PI / 180;
     }
+
+    public static double RadiansToDegrees(double angle)
+    {
+        return angle * 180 / Math.PI;
+    }
 }

+ 2 - 31
src/PixiEditor.AvaloniaUI/Views/Main/ViewportControls/Viewport.axaml

@@ -54,7 +54,7 @@
                     <StackPanel Orientation="Vertical">
                         <StackPanel Orientation="Horizontal">
                             <TextBlock Margin="5 0" TextAlignment="Center"
-                                       Text="{Binding Path=Angle, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport},
+                                       Text="{Binding Path=AngleRadians, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport},
              Converter={converters:RadiansToDegreesConverter}, StringFormat={}{0}°}"
                                        Width="35" Foreground="White" VerticalAlignment="Center" FontSize="16" />
                             <Button Width="32" Height="32" ui:Translator.TooltipKey="RESET_VIEWPORT"
@@ -126,7 +126,7 @@
             UseTouchGestures="{Binding UseTouchGestures, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=OneWay}"
             Scale="{Binding ZoomboxScale, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=OneWayToSource}"
             Center="{Binding Center, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=OneWayToSource}"
-            Angle="{Binding Angle, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=OneWayToSource}"
+            AngleRadians="{Binding AngleRadians, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=OneWayToSource}"
             RealDimensions="{Binding RealDimensions, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=OneWayToSource}"
             Dimensions="{Binding Dimensions, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=OneWayToSource}"
             ZoomMode="{Binding ZoomMode, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=viewportControls:Viewport}, Mode=TwoWay}"
@@ -208,35 +208,6 @@
             BrushSize="{Binding ToolsSubViewModel.ActiveBasicToolbar.ToolSize, Source={viewModels:MainVM}}"
             BrushShape="{Binding ToolsSubViewModel.ActiveTool.BrushShape, Source={viewModels:MainVM}, FallbackValue={x:Static brushShapeOverlay:BrushShape.Hidden}}"
             FlowDirection="LeftToRight" />
-        <!--<Grid ZIndex="5" DataContext="{Binding ElementName=vpUc}"
-              RenderTransformOrigin="0, 0" RenderTransform="{Binding #zoombox.CanvasTransform}">
-            <transformOverlay:TransformOverlay
-                Focusable="False"
-                Cursor="Arrow"
-                IsHitTestVisible="{Binding ZoomMode, Converter={converters:ZoomModeToHitTestVisibleConverter}}"
-                HorizontalAlignment="Stretch"
-                VerticalAlignment="Stretch"
-                IsVisible="{Binding Document.TransformViewModel.TransformActive}"
-                ActionCompleted="{Binding Document.TransformViewModel.ActionCompletedCommand}"
-                Corners="{Binding Document.TransformViewModel.Corners, Mode=TwoWay}"
-                RequestedCorners="{Binding Document.TransformViewModel.RequestedCorners, Mode=TwoWay}"
-                CornerFreedom="{Binding Document.TransformViewModel.CornerFreedom}"
-                SideFreedom="{Binding Document.TransformViewModel.SideFreedom}"
-                LockRotation="{Binding Document.TransformViewModel.LockRotation}"
-                CoverWholeScreen="{Binding Document.TransformViewModel.CoverWholeScreen}"
-                SnapToAngles="{Binding Document.TransformViewModel.SnapToAngles}"
-                InternalState="{Binding Document.TransformViewModel.InternalState, Mode=TwoWay}"
-                ZoomboxScale="{Binding #zoombox.Scale}"
-                ZoomboxAngle="{Binding #zoombox.Angle}" />
-            <lineToolOverlay:LineToolOverlay
-                Focusable="False"
-                IsVisible="{Binding Document.LineToolOverlayViewModel.IsEnabled}"
-                ActionCompleted="{Binding Document.LineToolOverlayViewModel.ActionCompletedCommand}"
-                LineStart="{Binding Document.LineToolOverlayViewModel.LineStart, Mode=TwoWay}"
-                LineEnd="{Binding Document.LineToolOverlayViewModel.LineEnd, Mode=TwoWay}"
-                ZoomboxScale="{Binding #zoombox.Scale}"
-                FlowDirection="LeftToRight" />
-        </Grid>-->
         <Button
             ZIndex="99999"
             DockPanel.Dock="Bottom"

+ 7 - 7
src/PixiEditor.AvaloniaUI/Views/Main/ViewportControls/Viewport.axaml.cs

@@ -200,15 +200,15 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
         set => SetValue(FlipYProperty, value);
     }
 
-    private double angle = 0;
+    private double angleRadians = 0;
 
-    public double Angle
+    public double AngleRadians
     {
-        get => angle;
+        get => angleRadians;
         set
         {
-            angle = value;
-            PropertyChanged?.Invoke(this, new(nameof(Angle)));
+            angleRadians = value;
+            PropertyChanged?.Invoke(this, new(nameof(AngleRadians)));
             Document?.Operations.AddOrUpdateViewport(GetLocation());
         }
     }
@@ -373,7 +373,7 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
 
     private ViewportInfo GetLocation()
     {
-        return new(Angle, Center, RealDimensions, Dimensions, CalculateResolution(), GuidValue, Delayed, ForceRefreshFinalImage);
+        return new(AngleRadians, Center, RealDimensions, Dimensions, CalculateResolution(), GuidValue, Delayed, ForceRefreshFinalImage);
     }
 
     private void OnReferenceImageSizeChanged(object? sender, SizeChangedEventArgs sizeChangedEventArgs)
@@ -452,7 +452,7 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
     
     private void ResetViewportClicked(object sender, RoutedEventArgs e)
     {
-        scene.Angle = 0;
+        scene.AngleRadians = 0;
         scene.CenterContent();
     }
 

+ 19 - 4
src/PixiEditor.AvaloniaUI/Views/Overlays/SelectionOverlay/SelectionOverlay.cs

@@ -1,4 +1,5 @@
 using System.Linq;
+using System.Threading;
 using Avalonia;
 using Avalonia.Animation;
 using Avalonia.Media;
@@ -37,6 +38,7 @@ internal class SelectionOverlay : Overlay
     {
         AffectsRender<SelectionOverlay>(PathProperty);
         ShowFillProperty.Changed.Subscribe(OnShowFillChanged);
+        PathProperty.Changed.Subscribe(OnPathChanged);
     }
 
     private Pen whitePen = new Pen(Brushes.White, 1);
@@ -44,15 +46,16 @@ internal class SelectionOverlay : Overlay
     private Brush fillBrush = new SolidColorBrush(Color.FromArgb(80, 0, 80, 255));
 
     private static DashStyle startingFrame = new DashStyle(new double[] { 2, 4 }, 0);
-    private static DashStyle endingFrame = new DashStyle(new double[] { 2, 4 }, 6);
 
     private Geometry renderPath = new PathGeometry();
+    private Avalonia.Animation.Animation animation;
+    private CancellationTokenSource cancelAnimationToken;
 
     public SelectionOverlay()
     {
         IsHitTestVisible = false;
 
-        Avalonia.Animation.Animation animation = new Avalonia.Animation.Animation()
+        animation = new Avalonia.Animation.Animation()
         {
             Duration = new TimeSpan(0, 0, 0, 2, 0),
             IterationCount = IterationCount.Infinite,
@@ -70,8 +73,6 @@ internal class SelectionOverlay : Overlay
                 Setters = { new Setter(BlackDashedPenProperty, SelectionDashAnimator.Interpolate(cue.CueValue, 6, blackDashedPen.DashStyle.Dashes.ToArray())) }
             });
         }
-
-        animation.RunAsync(this);
     }
 
     public override void Render(DrawingContext drawingContext)
@@ -107,4 +108,18 @@ internal class SelectionOverlay : Overlay
         var self = (SelectionOverlay)args.Sender;
         self.fillBrush.Opacity = args.NewValue.Value ? 1 : 0;
     }
+
+    private static void OnPathChanged(AvaloniaPropertyChangedEventArgs<VectorPath?> args)
+    {
+        var self = (SelectionOverlay)args.Sender;
+        if (args.NewValue.HasValue && !args.NewValue.Value.IsEmpty && self.IsVisible)
+        {
+            self.cancelAnimationToken = new CancellationTokenSource();
+            self.animation.RunAsync(self, self.cancelAnimationToken.Token);
+        }
+        else if (self.cancelAnimationToken != null)
+        {
+            self.cancelAnimationToken.Cancel();
+        }
+    }
 }

+ 15 - 23
src/PixiEditor.AvaloniaUI/Views/Visuals/Scene.cs

@@ -83,7 +83,7 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
 
     static Scene()
     {
-        AffectsRender<Scene>(BoundsProperty, WidthProperty, HeightProperty, ScaleProperty, AngleProperty, FlipXProperty,
+        AffectsRender<Scene>(BoundsProperty, WidthProperty, HeightProperty, ScaleProperty, AngleRadiansProperty, FlipXProperty,
             FlipYProperty, DocumentProperty, SurfaceProperty, ActiveOverlaysProperty);
 
         FadeOutProperty.Changed.AddClassHandler<Scene>(FadeOutChanged);
@@ -107,7 +107,7 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
 
         float finalScale = CalculateFinalScale();
 
-        float angle = (float)Angle;
+        float angle = (float)MathUtil.RadiansToDegrees(AngleRadians);
         if (FlipX)
         {
             angle = 360 - angle;
@@ -118,16 +118,21 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
             angle = 360 - angle;
         }
 
-        var operation = new DrawSceneOperation(Surface, Document, CanvasPos, finalScale, Angle, FlipX, FlipY,
-            new Rect(CanvasPos.X, CanvasPos.Y, ContentDimensions.X * finalScale, ContentDimensions.Y * finalScale),
+        VecD dirtyDimensions = new VecD(ContentDimensions.X * finalScale, ContentDimensions.Y * finalScale);
+        VecD dirtyCenterShift = new VecD(FlipX ? -dirtyDimensions.X / 2 : dirtyDimensions.X / 2, FlipY ? -dirtyDimensions.Y / 2 : dirtyDimensions.Y / 2);
+        VecD dirtyCenter = new VecD(CanvasPos.X + dirtyCenterShift.X, CanvasPos.Y + dirtyCenterShift.Y);
+        RectD dirtyBounds = new ShapeCorners(dirtyCenter, dirtyDimensions)
+            .AsRotated(MathUtil.DegreesToRadians(angle), new VecD(CanvasPos.X, CanvasPos.Y)).AABBBounds;
+        /*dirtyBounds.Flip*/
+
+        using var operation = new DrawSceneOperation(Surface, Document, CanvasPos, finalScale, angle, FlipX, FlipY,
+            new Rect(dirtyBounds.X, dirtyBounds.Y, dirtyBounds.Width, dirtyBounds.Height),
             Bounds,
             Opacity, (SKBitmap)checkerBitmap.Native);
         context.Custom(operation);
 
-        context.PushTransform(Matrix.CreateTranslation(CanvasPos.X, CanvasPos.Y));
-        context.PushTransform(Matrix.CreateScale(finalScale, finalScale));
-        context.PushTransform(Matrix.CreateRotation(MathUtil.AngleToRadians(angle)));
-        context.PushTransform(Matrix.CreateScale(FlipX ? -1 : 1, FlipY ? -1 : 1));
+        var matrix = CalculateTransformMatrix();
+        context.PushTransform(matrix);
 
         if (ActiveOverlays != null)
         {
@@ -295,7 +300,7 @@ internal class Scene : Zoombox.Zoombox, ICustomHitTest
     {
         Matrix transform = Matrix.Identity;
         float finalScale = CalculateFinalScale();
-        transform = transform.Append(Matrix.CreateRotation(MathUtil.AngleToRadians((float)Angle)));
+        transform = transform.Append(Matrix.CreateRotation((float)AngleRadians));
         transform = transform.Append(Matrix.CreateScale(FlipX ? -1 : 1, FlipY ? -1 : 1));
         transform = transform.Append(Matrix.CreateScale(finalScale, finalScale));
         transform = transform.Append(Matrix.CreateTranslation(CanvasPos.X, CanvasPos.Y));
@@ -458,23 +463,12 @@ internal class DrawSceneOperation : SkiaDrawOperation
         if (surfaceRectToRender.IsZeroOrNegativeArea)
         {
             canvas.Restore();
-            canvas.Flush();
             return;
         }
 
         canvas.Scale((float)Scale, (float)Scale, (float)ContentPosition.X, (float)ContentPosition.Y);
-        float angle = (float)Angle;
-        if (FlipX)
-        {
-            angle = 360 - angle;
-        }
 
-        if (FlipY)
-        {
-            angle = 360 - angle;
-        }
-
-        canvas.RotateDegrees(angle, (float)ContentPosition.X, (float)ContentPosition.Y);
+        canvas.RotateDegrees((float)Angle, (float)ContentPosition.X, (float)ContentPosition.Y);
         canvas.Scale(FlipX ? -1 : 1, FlipY ? -1 : 1, (float)ContentPosition.X, (float)ContentPosition.Y);
         canvas.Translate((float)ContentPosition.X, (float)ContentPosition.Y);
 
@@ -484,8 +478,6 @@ internal class DrawSceneOperation : SkiaDrawOperation
         canvas.DrawImage((SKImage)snapshot.Native, surfaceRectToRender.X, surfaceRectToRender.Y, _paint);
 
         canvas.Restore();
-
-        canvas.Flush();
     }
 
     private void DrawCheckerboard(SKCanvas canvas, RectI surfaceRectToRender)

+ 11 - 2
src/PixiEditor.AvaloniaUI/Views/Visuals/SkiaDrawOperation.cs

@@ -10,6 +10,7 @@ internal abstract class SkiaDrawOperation : ICustomDrawOperation
 {
     public Rect Bounds { get; }
 
+
     public SkiaDrawOperation(Rect dirtyBounds)
     {
         Bounds = dirtyBounds;
@@ -17,9 +18,17 @@ internal abstract class SkiaDrawOperation : ICustomDrawOperation
 
     public abstract bool Equals(ICustomDrawOperation? other);
 
-    public virtual void Dispose() { }
+    public virtual void Dispose()
+    {
+
+    }
+
+    void IDisposable.Dispose()
+    {
+        Dispose();
+    }
 
-    public bool HitTest(Point p) => false;
+    public virtual bool HitTest(Point p) => false;
 
     public void Render(ImmediateDrawingContext context)
     {

+ 6 - 6
src/PixiEditor.DrawingApi.Core/Numerics/VecD.cs

@@ -45,18 +45,18 @@ public struct VecD : IEquatable<VecD>
     /// <summary>
     ///     Rotates the vector by the specified angle in radians
     /// </summary>
-    /// <param name="angle">Angle in radians</param>
+    /// <param name="angleRad">Angle in radians</param>
     /// <returns>Rotated vector</returns>
-    public VecD Rotate(double angle)
+    public VecD Rotate(double angleRad)
     {
         VecD result = new VecD();
-        result.X = X * Math.Cos(angle) - Y * Math.Sin(angle);
-        result.Y = X * Math.Sin(angle) + Y * Math.Cos(angle);
+        result.X = X * Math.Cos(angleRad) - Y * Math.Sin(angleRad);
+        result.Y = X * Math.Sin(angleRad) + Y * Math.Cos(angleRad);
         return result;
     }
-    public VecD Rotate(double angle, VecD around)
+    public VecD Rotate(double angleRad, VecD around)
     {
-        return (this - around).Rotate(angle) + around;
+        return (this - around).Rotate(angleRad) + around;
     }
     public double DistanceToLineSegment(VecD pos1, VecD pos2)
     {

+ 4 - 4
src/PixiEditor.Zoombox/Operations/ManipulationOperation.cs

@@ -22,9 +22,9 @@ internal class ManipulationOperation
 
     public void Start()
     {
-        updatedAngle = owner.Angle;
-        initialAngle = owner.Angle;
-        rotationProcess = new LockingRotationProcess(owner.Angle);
+        updatedAngle = owner.AngleRadians;
+        initialAngle = owner.AngleRadians;
+        rotationProcess = new LockingRotationProcess(owner.AngleRadians);
     }
 
     //TODO: Implement this
@@ -58,7 +58,7 @@ internal class ManipulationOperation
         double newAngle = startedRotating ? rotationProcess!.UpdateRotation(updatedAngle) : initialAngle;
 
         VecD originalPos = owner.ToZoomboxSpace(screenOrigin);
-        owner.Angle = newAngle;
+        owner.AngleRadians = newAngle;
         owner.Scale = newScale;
         VecD newPos = owner.ToZoomboxSpace(screenOrigin);
         VecD centerTranslation = originalPos - newPos;

+ 2 - 2
src/PixiEditor.Zoombox/Operations/RotateDragOperation.cs

@@ -24,7 +24,7 @@ internal class RotateDragOperation : IDragOperation
     {
         Point pointCur = e.GetPosition(owner);
         initialClickAngle = GetAngle(new(pointCur.X, pointCur.Y));
-        initialZoomboxAngle = owner.Angle;
+        initialZoomboxAngle = owner.AngleRadians;
         rotationProcess = new LockingRotationProcess(initialZoomboxAngle);
         e.Pointer.Capture(owner);
         capturedPointer = e.Pointer;
@@ -48,7 +48,7 @@ internal class RotateDragOperation : IDragOperation
             newZoomboxAngle += initialClickAngle - clickAngle;
         else
             newZoomboxAngle += clickAngle - initialClickAngle;
-        owner.Angle = rotationProcess!.UpdateRotation(newZoomboxAngle);
+        owner.AngleRadians = rotationProcess!.UpdateRotation(newZoomboxAngle);
     }
 
     public void Terminate()

+ 15 - 24
src/PixiEditor.Zoombox/Zoombox.cs

@@ -45,8 +45,8 @@ public partial class Zoombox : ContentControl, INotifyPropertyChanged
         set => SetValue(ContentDimensionsProperty, value);
     }
 
-    public static readonly StyledProperty<double> AngleProperty =
-        AvaloniaProperty.Register<Zoombox, double>(nameof(Angle), defaultValue: 0.0);
+    public static readonly StyledProperty<double> AngleRadiansProperty =
+        AvaloniaProperty.Register<Zoombox, double>(nameof(AngleRadians), defaultValue: 0.0);
 
     public static readonly StyledProperty<bool> FlipXProperty =
         AvaloniaProperty.Register<Zoombox, bool>(nameof(FlipX), defaultValue: false);
@@ -93,10 +93,10 @@ public partial class Zoombox : ContentControl, INotifyPropertyChanged
         set => SetValue(ScaleProperty, value);
     }
 
-    public double Angle
+    public double AngleRadians
     {
-        get => (double)GetValue(AngleProperty);
-        set => SetValue(AngleProperty, value);
+        get => (double)GetValue(AngleRadiansProperty);
+        set => SetValue(AngleRadiansProperty, value);
     }
 
     public VecD Center
@@ -132,7 +132,7 @@ public partial class Zoombox : ContentControl, INotifyPropertyChanged
         Children =
         {
             new ScaleTransform(Scale, Scale),
-            new RotateTransform(Angle * 180 / Math.PI),
+            new RotateTransform(AngleRadians * 180 / Math.PI),
             new ScaleTransform(FlipX ? -1 : 1, FlipY ? -1 : 1),
             new TranslateTransform(CanvasX, CanvasY),
         },
@@ -141,7 +141,7 @@ public partial class Zoombox : ContentControl, INotifyPropertyChanged
     public double ScaleTransformXY => Scale;
     public double FlipTransformX => FlipX ? -1 : 1;
     public double FlipTransformY => FlipY ? -1 : 1;
-    public double RotateTransformAngle => Angle * 180 / Math.PI;
+    public double RotateTransformAngle => AngleRadians * 180 / Math.PI;
     internal const double MaxScale = 384;
 
     internal double MinScale
@@ -158,7 +158,7 @@ public partial class Zoombox : ContentControl, INotifyPropertyChanged
     public VecD ToScreenSpace(VecD p)
     {
         VecD delta = p - Center;
-        delta = delta.Rotate(Angle) * Scale;
+        delta = delta.Rotate(AngleRadians) * Scale;
         if (FlipX)
             delta.X = -delta.X;
         if (FlipY)
@@ -174,7 +174,7 @@ public partial class Zoombox : ContentControl, INotifyPropertyChanged
             delta.X = -delta.X;
         if (FlipY)
             delta.Y = -delta.Y;
-        delta = (delta / Scale).Rotate(-Angle);
+        delta = (delta / Scale).Rotate(-AngleRadians);
         return delta + Center;
     }
 
@@ -204,7 +204,7 @@ public partial class Zoombox : ContentControl, INotifyPropertyChanged
     {
         ZoomModeProperty.Changed.Subscribe(ZoomModeChanged);
         ScaleProperty.Changed.Subscribe(OnPropertyChange);
-        AngleProperty.Changed.Subscribe(OnPropertyChange);
+        AngleRadiansProperty.Changed.Subscribe(OnPropertyChange);
         FlipXProperty.Changed.Subscribe(OnPropertyChange);
         FlipYProperty.Changed.Subscribe(OnPropertyChange);
         CenterProperty.Changed.Subscribe(OnPropertyChange);
@@ -296,7 +296,7 @@ public partial class Zoombox : ContentControl, INotifyPropertyChanged
             Center,
             Dimensions,
             realDim,
-            Angle));
+            AngleRadians));
     }
 
     public void CenterContent() => CenterContent(new(ContentDimensions.X, ContentDimensions.Y));
@@ -308,7 +308,7 @@ public partial class Zoombox : ContentControl, INotifyPropertyChanged
             newSize.X * marginFactor / Bounds.Width,
             newSize.Y * marginFactor / Bounds.Height);
 
-        Angle = 0;
+        AngleRadians = 0;
         FlipX = false;
         FlipY = false;
         Scale = Math.Clamp(1 / scaleFactor, MinScale, MaxScale);
@@ -468,8 +468,9 @@ public partial class Zoombox : ContentControl, INotifyPropertyChanged
 
     private static void OnPropertyChange(Zoombox zoombox)
     {
-        VecD topLeft = zoombox.ToZoomboxSpace(VecD.Zero).Rotate(zoombox.Angle);
-        VecD bottomRight = zoombox.ToZoomboxSpace(new(zoombox.Bounds.Width, zoombox.Bounds.Height)).Rotate(zoombox.Angle);
+        VecD topLeft = zoombox.ToZoomboxSpace(VecD.Zero).Rotate(zoombox.AngleRadians);
+        VecD bottomRight = zoombox.ToZoomboxSpace(new(zoombox.Bounds.Width, zoombox.Bounds.Height))
+            .Rotate(zoombox.AngleRadians);
 
         zoombox.Dimensions = (bottomRight - topLeft).Abs();
         zoombox.PropertyChanged?.Invoke(zoombox, new(nameof(zoombox.ScaleTransformXY)));
@@ -482,14 +483,4 @@ public partial class Zoombox : ContentControl, INotifyPropertyChanged
         zoombox.PropertyChanged?.Invoke(zoombox, new(nameof(zoombox.CanvasTransform)));
         zoombox.RaiseViewportEvent();
     }
-
-    private void OnMainCanvasSizeChanged(object sender, SizeChangedEventArgs e)
-    {
-        OnPropertyChange(this);
-    }
-
-    private void OnGridSizeChanged(object sender, SizeChangedEventArgs args)
-    {
-        OnPropertyChange(this);
-    }
 }

+ 2 - 2
src/PixiEditor/Views/UserControls/Viewport.xaml

@@ -124,7 +124,7 @@
             UseTouchGestures="{Binding UseTouchGestures, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}, Mode=OneWay}"
             Scale="{Binding ZoomboxScale, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}, Mode=OneWayToSource}"
             Center="{Binding Center, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}, Mode=OneWayToSource}"
-            Angle="{Binding Angle, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}, Mode=OneWayToSource}"
+            AngleRadians="{Binding Angle, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}, Mode=OneWayToSource}"
             RealDimensions="{Binding RealDimensions, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}, Mode=OneWayToSource}"
             Dimensions="{Binding Dimensions, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}, Mode=OneWayToSource}"
             ZoomMode="{Binding ZoomMode, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}, Mode=TwoWay}"
@@ -302,7 +302,7 @@
                             SnapToAngles="{Binding Document.TransformViewModel.SnapToAngles}"
                             InternalState="{Binding Document.TransformViewModel.InternalState, Mode=TwoWay}"
                             ZoomboxScale="{Binding Zoombox.Scale}"
-                            ZoomboxAngle="{Binding Zoombox.Angle}" />
+                            ZoomboxAngle="{Binding Zoombox.AngleRadians}" />
                         <lineOverlay:LineToolOverlay
                             Focusable="False"
                             Visibility="{Binding Document.LineToolOverlayViewModel.IsEnabled, Converter={converters:BoolToVisibilityConverter}}"

+ 1 - 1
src/PixiEditor/Views/UserControls/Viewport.xaml.cs

@@ -402,7 +402,7 @@ internal partial class Viewport : UserControl, INotifyPropertyChanged
     
     private void ResetViewportClicked(object sender, RoutedEventArgs e)
     {
-        zoombox.Angle = 0;
+        zoombox.AngleRadians = 0;
         zoombox.CenterContent();
     }