Browse Source

Added flipping support

Krzysztof Krysiński 1 year ago
parent
commit
9f21207117

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

@@ -128,6 +128,8 @@
             Document="{Binding Document, ElementName=vpUc, Mode=OneWay}"
             Document="{Binding Document, ElementName=vpUc, Mode=OneWay}"
             ContentPosition="{Binding CanvasPos, ElementName=zoombox, Mode=OneWay}"
             ContentPosition="{Binding CanvasPos, ElementName=zoombox, Mode=OneWay}"
             Angle="{Binding RotateTransformAngle, ElementName=zoombox, Mode=OneWay}"
             Angle="{Binding RotateTransformAngle, ElementName=zoombox, Mode=OneWay}"
+            FlipX="{Binding FlipX, ElementName=zoombox, Mode=OneWay}"
+            FlipY="{Binding FlipY, ElementName=zoombox, Mode=OneWay}"
             ui1:RenderOptionsBindable.BitmapInterpolationMode="{Binding Scale, Converter={converters:ScaleToBitmapScalingModeConverter}, ElementName=zoombox}"
             ui1:RenderOptionsBindable.BitmapInterpolationMode="{Binding Scale, Converter={converters:ScaleToBitmapScalingModeConverter}, ElementName=zoombox}"
             FlowDirection="LeftToRight">
             FlowDirection="LeftToRight">
             <visuals:Scene.Styles>
             <visuals:Scene.Styles>

+ 94 - 24
src/PixiEditor.AvaloniaUI/Views/Visuals/Scene.cs

@@ -28,6 +28,11 @@ internal class Scene : OpenGlControlBase
     public static readonly StyledProperty<double> AngleProperty = AvaloniaProperty.Register<Scene, double>(
     public static readonly StyledProperty<double> AngleProperty = AvaloniaProperty.Register<Scene, double>(
         nameof(Angle), 0);
         nameof(Angle), 0);
 
 
+    public static readonly StyledProperty<bool> FlipXProperty = AvaloniaProperty.Register<Scene, bool>(
+        nameof(FlipX), false);
+    public static readonly StyledProperty<bool> FlipYProperty = AvaloniaProperty.Register<Scene, bool>(
+        nameof(FlipY), false);
+
     public double Angle
     public double Angle
     {
     {
         get => GetValue(AngleProperty);
         get => GetValue(AngleProperty);
@@ -60,16 +65,28 @@ internal class Scene : OpenGlControlBase
 
 
     public Rect FinalBounds => Bounds;
     public Rect FinalBounds => Bounds;
 
 
+    public bool FlipX
+    {
+        get { return (bool)GetValue(FlipXProperty); }
+        set { SetValue(FlipXProperty, value); }
+    }
+
+    public bool FlipY
+    {
+        get { return (bool)GetValue(FlipYProperty); }
+        set { SetValue(FlipYProperty, value); }
+    }
+
     private SKSurface _outputSurface;
     private SKSurface _outputSurface;
     private SKPaint _paint = new SKPaint();
     private SKPaint _paint = new SKPaint();
     private GRContext? gr;
     private GRContext? gr;
 
 
     static Scene()
     static Scene()
     {
     {
-        AffectsRender<Scene>(BoundsProperty, WidthProperty, HeightProperty);
+        AffectsRender<Scene>(BoundsProperty, WidthProperty, HeightProperty, ScaleProperty, AngleProperty, FlipXProperty, FlipYProperty, ContentPositionProperty, DocumentProperty, SurfaceProperty);
         BoundsProperty.Changed.AddClassHandler<Scene>(BoundsChanged);
         BoundsProperty.Changed.AddClassHandler<Scene>(BoundsChanged);
-        WidthProperty.Changed.AddClassHandler<Scene>(BoundsChanged);
-        HeightProperty.Changed.AddClassHandler<Scene>(BoundsChanged);
+        FlipXProperty.Changed.AddClassHandler<Scene>(RequestRendering);
+        FlipYProperty.Changed.AddClassHandler<Scene>(RequestRendering);
     }
     }
 
 
     public Scene()
     public Scene()
@@ -104,9 +121,8 @@ internal class Scene : OpenGlControlBase
         canvas.Clear(SKColors.Transparent);
         canvas.Clear(SKColors.Transparent);
 
 
         float finalScale = CalculateFinalScale();
         float finalScale = CalculateFinalScale();
-        float radians = (float)(Angle * Math.PI / 180);
 
 
-        RectI surfaceRectToRender = FindRectToRender(finalScale, radians);
+        RectI surfaceRectToRender = FindRectToRender(finalScale);
 
 
         if (surfaceRectToRender.IsZeroOrNegativeArea)
         if (surfaceRectToRender.IsZeroOrNegativeArea)
         {
         {
@@ -116,8 +132,19 @@ internal class Scene : OpenGlControlBase
             return;
             return;
         }
         }
 
 
-        canvas.RotateDegrees((float)Angle, ContentPosition.X, ContentPosition.Y);
         canvas.Scale(finalScale, finalScale, ContentPosition.X, ContentPosition.Y);
         canvas.Scale(finalScale, finalScale, ContentPosition.X, ContentPosition.Y);
+        float angle = (float)Angle;
+        if (FlipX)
+        {
+            angle = 360 - angle;
+        }
+        if (FlipY)
+        {
+            angle = 360 - angle;
+        }
+
+        canvas.RotateDegrees(angle, ContentPosition.X, ContentPosition.Y);
+        canvas.Scale(FlipX ? -1 : 1, FlipY ? -1 : 1, ContentPosition.X, ContentPosition.Y);
         canvas.Translate(ContentPosition.X, ContentPosition.Y);
         canvas.Translate(ContentPosition.X, ContentPosition.Y);
 
 
         using Image snapshot = Surface.DrawingSurface.Snapshot(surfaceRectToRender);
         using Image snapshot = Surface.DrawingSurface.Snapshot(surfaceRectToRender);
@@ -128,16 +155,16 @@ internal class Scene : OpenGlControlBase
         canvas.Flush();
         canvas.Flush();
     }
     }
 
 
-    private RectI FindRectToRender(float finalScale, float radians)
+    private RectI FindRectToRender(float finalScale)
     {
     {
-        ShapeCorners surfaceInViewportSpace = SurfaceToViewport(new RectI(VecI.Zero, Surface.Size), finalScale, radians);
+        ShapeCorners surfaceInViewportSpace = SurfaceToViewport(new RectI(VecI.Zero, Surface.Size), finalScale);
         RectI surfaceBoundsInViewportSpace = (RectI)surfaceInViewportSpace.AABBBounds.RoundOutwards();
         RectI surfaceBoundsInViewportSpace = (RectI)surfaceInViewportSpace.AABBBounds.RoundOutwards();
         RectI viewportBoundsInViewportSpace = (RectI)(new RectD(FinalBounds.X, FinalBounds.Y, FinalBounds.Width, FinalBounds.Height)).RoundOutwards();
         RectI viewportBoundsInViewportSpace = (RectI)(new RectD(FinalBounds.X, FinalBounds.Y, FinalBounds.Width, FinalBounds.Height)).RoundOutwards();
         RectI firstIntersectionInViewportSpace = surfaceBoundsInViewportSpace.Intersect(viewportBoundsInViewportSpace);
         RectI firstIntersectionInViewportSpace = surfaceBoundsInViewportSpace.Intersect(viewportBoundsInViewportSpace);
-        ShapeCorners firstIntersectionInSurfaceSpace = ViewportToSurface(firstIntersectionInViewportSpace, finalScale, radians);
+        ShapeCorners firstIntersectionInSurfaceSpace = ViewportToSurface(firstIntersectionInViewportSpace, finalScale);
         RectI firstIntersectionBoundsInSurfaceSpace = (RectI)firstIntersectionInSurfaceSpace.AABBBounds.RoundOutwards();
         RectI firstIntersectionBoundsInSurfaceSpace = (RectI)firstIntersectionInSurfaceSpace.AABBBounds.RoundOutwards();
 
 
-        ShapeCorners viewportInSurfaceSpace = ViewportToSurface(viewportBoundsInViewportSpace, finalScale, radians);
+        ShapeCorners viewportInSurfaceSpace = ViewportToSurface(viewportBoundsInViewportSpace, finalScale);
         RectD viewportBoundsInSurfaceSpace = viewportInSurfaceSpace.AABBBounds;
         RectD viewportBoundsInSurfaceSpace = viewportInSurfaceSpace.AABBBounds;
         RectD surfaceBoundsInSurfaceSpace = new(VecD.Zero, Surface.Size);
         RectD surfaceBoundsInSurfaceSpace = new(VecD.Zero, Surface.Size);
         RectI secondIntersectionInSurfaceSpace = (RectI)viewportBoundsInSurfaceSpace.Intersect(surfaceBoundsInSurfaceSpace).RoundOutwards();
         RectI secondIntersectionInSurfaceSpace = (RectI)viewportBoundsInSurfaceSpace.Intersect(surfaceBoundsInSurfaceSpace).RoundOutwards();
@@ -156,25 +183,25 @@ internal class Scene : OpenGlControlBase
         canvas.DrawLine((float)rect.X, (float)rect.Bottom, (float)rect.X, (float)rect.Y, _paint);
         canvas.DrawLine((float)rect.X, (float)rect.Bottom, (float)rect.X, (float)rect.Y, _paint);
     }
     }
 
 
-    private ShapeCorners ViewportToSurface(RectI viewportRect, float scale, float angleRadians)
+    private ShapeCorners ViewportToSurface(RectI viewportRect, float scale)
     {
     {
         return new ShapeCorners()
         return new ShapeCorners()
         {
         {
-            TopLeft = ViewportToSurface(viewportRect.TopLeft, scale, angleRadians),
-            TopRight = ViewportToSurface(viewportRect.TopRight, scale, angleRadians),
-            BottomLeft = ViewportToSurface(viewportRect.BottomLeft, scale, angleRadians),
-            BottomRight = ViewportToSurface(viewportRect.BottomRight, scale, angleRadians),
+            TopLeft = ViewportToSurface(viewportRect.TopLeft, scale),
+            TopRight = ViewportToSurface(viewportRect.TopRight, scale),
+            BottomLeft = ViewportToSurface(viewportRect.BottomLeft, scale),
+            BottomRight = ViewportToSurface(viewportRect.BottomRight, scale),
         };
         };
     }
     }
     
     
-    private ShapeCorners SurfaceToViewport(RectI viewportRect, float scale, float angleRadians)
+    private ShapeCorners SurfaceToViewport(RectI viewportRect, float scale)
     {
     {
         return new ShapeCorners()
         return new ShapeCorners()
         {
         {
-            TopLeft = SurfaceToViewport(viewportRect.TopLeft, scale, angleRadians),
-            TopRight = SurfaceToViewport(viewportRect.TopRight, scale, angleRadians),
-            BottomLeft = SurfaceToViewport(viewportRect.BottomLeft, scale, angleRadians),
-            BottomRight = SurfaceToViewport(viewportRect.BottomRight, scale, angleRadians),
+            TopLeft = SurfaceToViewport(viewportRect.TopLeft, scale),
+            TopRight = SurfaceToViewport(viewportRect.TopRight, scale),
+            BottomLeft = SurfaceToViewport(viewportRect.BottomLeft, scale),
+            BottomRight = SurfaceToViewport(viewportRect.BottomRight, scale),
         };
         };
     }
     }
 
 
@@ -193,21 +220,57 @@ internal class Scene : OpenGlControlBase
         return scaleUniform;
         return scaleUniform;
     }
     }
 
 
-    private VecD SurfaceToViewport(VecI surfacePoint, float scale, float angleRadians)
+    private VecD SurfaceToViewport(VecI surfacePoint, float scale)
     {
     {
         VecD unscaledPoint = surfacePoint * scale;
         VecD unscaledPoint = surfacePoint * scale;
+
+        float angle = (float)Angle;
+        if (FlipX)
+        {
+            unscaledPoint.X = -unscaledPoint.X;
+            angle = 360 - angle;
+        }
+        if (FlipY)
+        {
+            unscaledPoint.Y = -unscaledPoint.Y;
+            angle = 360 - angle;
+        }
+
         VecD offseted = unscaledPoint + ContentPosition;
         VecD offseted = unscaledPoint + ContentPosition;
 
 
-        return offseted.Rotate(angleRadians, ContentPosition);
+        float angleRadians = (float)(angle * Math.PI / 180);
+        VecD rotated = offseted.Rotate(angleRadians, ContentPosition);
+
+        return rotated;
     }
     }
 
 
-    private VecI ViewportToSurface(VecD viewportPoint, float scale, float angleRadians)
+    private VecI ViewportToSurface(VecD viewportPoint, float scale)
     {
     {
+        float angle = (float)Angle;
+        if (FlipX)
+        {
+            angle = 360 - angle;
+        }
+        if (FlipY)
+        {
+            angle = 360 - angle;
+        }
+
+        float angleRadians = (float)(angle * Math.PI / 180);
         VecD rotatedViewportPoint = (viewportPoint).Rotate(-angleRadians, ContentPosition);
         VecD rotatedViewportPoint = (viewportPoint).Rotate(-angleRadians, ContentPosition);
+
         VecD unscaledPoint = rotatedViewportPoint - ContentPosition;
         VecD unscaledPoint = rotatedViewportPoint - ContentPosition;
-        return new VecI(
+
+        if (FlipX)
+            unscaledPoint.X = -unscaledPoint.X;
+        if (FlipY)
+            unscaledPoint.Y = -unscaledPoint.Y;
+
+        VecI pos = new VecI(
             (int)Math.Round(unscaledPoint.X / scale),
             (int)Math.Round(unscaledPoint.X / scale),
             (int)Math.Round(unscaledPoint.Y / scale));
             (int)Math.Round(unscaledPoint.Y / scale));
+
+        return pos;
     }
     }
 
 
     private static void BoundsChanged(Scene sender, AvaloniaPropertyChangedEventArgs e)
     private static void BoundsChanged(Scene sender, AvaloniaPropertyChangedEventArgs e)
@@ -216,5 +279,12 @@ internal class Scene : OpenGlControlBase
         {
         {
             sender.CreateOutputSurface();
             sender.CreateOutputSurface();
         }
         }
+
+        sender.RequestNextFrameRendering();
+    }
+
+    private static void RequestRendering(Scene sender, AvaloniaPropertyChangedEventArgs e)
+    {
+        sender.RequestNextFrameRendering();
     }
     }
 }
 }

+ 1 - 0
src/PixiEditor.Zoombox/Zoombox.axaml.cs

@@ -132,6 +132,7 @@ public partial class Zoombox : UserControl, INotifyPropertyChanged
         {
         {
             new ScaleTransform(Scale, Scale),
             new ScaleTransform(Scale, Scale),
             new RotateTransform(Angle * 180 / Math.PI),
             new RotateTransform(Angle * 180 / Math.PI),
+            new ScaleTransform(FlipX ? -1 : 1, FlipY ? -1 : 1),
             new TranslateTransform(CanvasX, CanvasY),
             new TranslateTransform(CanvasX, CanvasY),
         },
         },
     };
     };