Browse Source

Final fixes

Krzysztof Krysiński 1 year ago
parent
commit
5723069bd6

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

@@ -125,7 +125,6 @@
                         Dimensions="{Binding Dimensions, ElementName=zoombox, Mode=OneWay}"
                         Document="{Binding Document, ElementName=vpUc, Mode=OneWay}"
                         ContentPosition="{Binding CanvasPos, ElementName=zoombox, Mode=OneWay}"
-                        Center="{Binding Center, ElementName=zoombox, Mode=OneWay}"
                         Angle="{Binding RotateTransformAngle, ElementName=zoombox, Mode=OneWay}"
                         ui1:RenderOptionsBindable.BitmapInterpolationMode="{Binding Scale, Converter={converters:ScaleToBitmapScalingModeConverter}, ElementName=zoombox}"
                         FlowDirection="LeftToRight">

+ 7 - 200
src/PixiEditor.AvaloniaUI/Views/Visuals/Scene.cs

@@ -1,8 +1,5 @@
-using System.Collections.Generic;
-using System.Diagnostics;
 using System.Linq;
 using Avalonia;
-using Avalonia.Media;
 using Avalonia.OpenGL;
 using Avalonia.OpenGL.Controls;
 using ChunkyImageLib;
@@ -10,7 +7,6 @@ using ChunkyImageLib.DataHolders;
 using PixiEditor.AvaloniaUI.ViewModels.Document;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Surface.ImageData;
-using PixiEditor.DrawingApi.Skia;
 
 namespace PixiEditor.AvaloniaUI.Views.Visuals;
 
@@ -24,10 +20,7 @@ internal class Scene : OpenGlControlBase
 
     public static readonly StyledProperty<VecI> ContentPositionProperty = AvaloniaProperty.Register<Scene, VecI>(
         nameof(ContentPosition));
-    
-    public static readonly StyledProperty<VecD> CenterProperty = AvaloniaProperty.Register<Scene, VecD>(
-        nameof(Center));
-    
+
     public static readonly StyledProperty<VecD> DimensionsProperty = AvaloniaProperty.Register<Scene, VecD>(
         nameof(Dimensions));
 
@@ -55,12 +48,6 @@ internal class Scene : OpenGlControlBase
         set => SetValue(ContentPositionProperty, value);
     }
     
-    public VecD Center
-    {
-        get => GetValue(CenterProperty);
-        set => SetValue(CenterProperty, value);
-    }
-    
     public VecD Dimensions
     {
         get => GetValue(DimensionsProperty);
@@ -79,7 +66,7 @@ internal class Scene : OpenGlControlBase
         set => SetValue(SurfaceProperty, value);
     }
 
-    public Rect FinalBounds => new Rect((Bounds.Width - Bounds.Width / 2 ) / 2, (Bounds.Height - Bounds.Height / 2 ) / 2, Bounds.Width / 2, Bounds.Height / 2);
+    public Rect FinalBounds => Bounds;
 
     private SKSurface _outputSurface;
     private SKPaint _paint = new SKPaint();
@@ -131,10 +118,6 @@ internal class Scene : OpenGlControlBase
 
         RectD viewport = new(FinalBounds.X, FinalBounds.Y, FinalBounds.Width, FinalBounds.Height);
 
-        _paint.Color = SKColors.Blue;
-        DrawDebugRect(canvas, viewport);
-
-
         ShapeCorners surfaceInViewportSpace = SurfaceToViewport(new RectI(VecI.Zero, Surface.Size), finalScale, radians);
         RectI surfaceBoundsInViewportSpace = (RectI)surfaceInViewportSpace.AABBBounds.RoundOutwards();
         RectI viewportBoundsInViewportSpace = (RectI)(new RectD(FinalBounds.X, FinalBounds.Y, FinalBounds.Width, FinalBounds.Height)).RoundOutwards();
@@ -142,12 +125,15 @@ internal class Scene : OpenGlControlBase
         ShapeCorners firstIntersectionInSurfaceSpace = ViewportToSurface(firstIntersectionInViewportSpace, finalScale, radians);
         RectI firstIntersectionBoundsInSurfaceSpace = (RectI)firstIntersectionInSurfaceSpace.AABBBounds.RoundOutwards();
 
-        ShapeCorners viewportInSurfaceSpace = new ShapeCorners(Center, Dimensions / 2f * resolutionScale).AsRotated(-radians, Center);
+        VecD scaledCenter = ViewportToSurface(new VecD(FinalBounds.Center.X, FinalBounds.Center.Y), finalScale, radians);
+        ShapeCorners viewportInSurfaceSpace = new ShapeCorners(scaledCenter, Dimensions * resolutionScale).AsRotated(-radians, scaledCenter);
         RectD viewportBoundsInSurfaceSpace = viewportInSurfaceSpace.AABBBounds;
         RectD surfaceBoundsInSurfaceSpace = new(VecD.Zero, Surface.Size);
         RectI secondIntersectionInSurfaceSpace = (RectI)viewportBoundsInSurfaceSpace.Intersect(surfaceBoundsInSurfaceSpace).RoundOutwards();
 
-        RectI surfaceRectToRender = firstIntersectionBoundsInSurfaceSpace.Intersect(secondIntersectionInSurfaceSpace);
+        //Inflate makes sure rounding doesn't cut any pixels.
+        RectI surfaceRectToRender = firstIntersectionBoundsInSurfaceSpace.Intersect(secondIntersectionInSurfaceSpace).Inflate(1);
+        surfaceRectToRender = surfaceRectToRender.Intersect(new RectI(VecI.Zero, Surface.Size)); // Clamp to surface size
 
         if (surfaceRectToRender.IsZeroOrNegativeArea)
         {
@@ -159,12 +145,8 @@ internal class Scene : OpenGlControlBase
 
         canvas.RotateDegrees((float)Angle, ContentPosition.X, ContentPosition.Y);
         canvas.Scale(finalScale, finalScale, ContentPosition.X, ContentPosition.Y);
-
         canvas.Translate(ContentPosition.X, ContentPosition.Y);
 
-
-        _paint.Color = SKColors.Red;
-        DrawDebugRect(canvas, (RectD)surfaceRectToRender);
         using Image snapshot = Surface.DrawingSurface.Snapshot((RectI)surfaceRectToRender);
         canvas.DrawImage((SKImage)snapshot.Native, (float)surfaceRectToRender.X, (float)surfaceRectToRender.Y, _paint);
 
@@ -205,18 +187,6 @@ internal class Scene : OpenGlControlBase
         };
     }
 
-    private bool IsWithinBounds(VecD point, Rect bounds)
-    {
-        return point.X >= bounds.X && point.X <= bounds.Right && point.Y >= bounds.Y && point.Y <= bounds.Bottom;
-    }
-
-    private bool IsSurfaceWithinViewportBounds(VecI surfacePoint)
-    {
-        float angle = (float)(Angle * Math.PI / 180);
-        VecD viewportPoint = SurfaceToViewport(surfacePoint, (float)Scale, angle);
-        return IsWithinBounds(viewportPoint, FinalBounds);
-    }
-
     private float CalculateFinalScale()
     {
         var scaleUniform = CalculateResolutionScale();
@@ -232,20 +202,6 @@ internal class Scene : OpenGlControlBase
         return scaleUniform;
     }
 
-    private bool IsOutOfBounds(VecI surfaceStart, VecI surfaceEnd)
-    {
-        return surfaceStart.X >= Surface.Size.X || surfaceStart.Y >= Surface.Size.Y || surfaceEnd.X <= 0 || surfaceEnd.Y <= 0;
-    }
-
-    /*private VecI ViewportToSurface(VecI surfacePoint, float scale)
-    {
-        float radians = (float)(Angle * Math.PI / 180);
-        VecD rotatedSurfacePoint = ((VecD)surfacePoint).Rotate(radians, ContentPosition / scale);
-        return new VecI(
-            (int)((rotatedSurfacePoint.X - ContentPosition.X) / scale),
-            (int)((rotatedSurfacePoint.Y - ContentPosition.Y) / scale));
-    }*/
-
     private VecD SurfaceToViewport(VecI surfacePoint, float scale, float angleRadians)
     {
         VecD unscaledPoint = surfacePoint * scale;
@@ -271,152 +227,3 @@ internal class Scene : OpenGlControlBase
         }
     }
 }
-
-class CornerInViewport(VecD position, bool isWithinBounds, VecI surfacePosition)
-{
-    public VecD Position { get; set; } = position;
-    public VecI SurfacePosition { get; set; } = surfacePosition;
-    public bool IsWithinBounds { get; set; } = isWithinBounds;
-    public CornerInViewport[] ConnectedCorners { get; set; }
-}
-
-class CornersInViewport
-{
-    public CornerInViewport TopLeft { get; set; }
-    public CornerInViewport TopRight { get; set; }
-    public CornerInViewport BottomRight { get; set; }
-    public CornerInViewport BottomLeft { get; set; }
-
-    public CornersInViewport(CornerInViewport topLeft, CornerInViewport topRight, CornerInViewport bottomRight, CornerInViewport bottomLeft)
-    {
-        TopLeft = topLeft;
-        TopRight = topRight;
-        BottomRight = bottomRight;
-        BottomLeft = bottomLeft;
-
-        TopLeft.ConnectedCorners = new[] { TopRight, BottomLeft };
-        TopRight.ConnectedCorners = new[] { TopLeft, BottomRight };
-        BottomRight.ConnectedCorners = new[] { BottomLeft, TopRight };
-        BottomLeft.ConnectedCorners = new[] { TopLeft, BottomRight };
-    }
-
-    public RectI ShrinkToViewport(RectI surfaceToRender, Func<VecI, bool> isWithinBoundsAction)
-    {
-        // start with first corner that is not within bounds, find next that is not within bounds and go to the direction of visible corner
-        // check if each pixel column and row is within bounds, if both are not visible, shrink surfaceToRender by 1
-
-        VecI maxSize = surfaceToRender.Size;
-
-        CornerInViewport firstNotWithinBounds = GetStartingCorner();
-        CornerInViewport? invisibleNeighbor = firstNotWithinBounds.ConnectedCorners.FirstOrDefault(x => !x.IsWithinBounds);
-        if (invisibleNeighbor == null) return surfaceToRender;
-
-        surfaceToRender = TraverseShrinkCorners(surfaceToRender, isWithinBoundsAction, invisibleNeighbor, firstNotWithinBounds, maxSize);
-        invisibleNeighbor = firstNotWithinBounds.ConnectedCorners.First(x => x != invisibleNeighbor);
-        if (!invisibleNeighbor.IsWithinBounds)
-        {
-            surfaceToRender = TraverseShrinkCorners(surfaceToRender, isWithinBoundsAction, invisibleNeighbor,
-                firstNotWithinBounds, maxSize);
-        }
-
-        /*invisibleNeighbor = GetOppositeCorner(invisibleNeighbor);
-        if (!invisibleNeighbor.IsWithinBounds)
-        {
-            surfaceToRender = TraverseShrinkCorners(surfaceToRender, isWithinBoundsAction, invisibleNeighbor,
-                firstNotWithinBounds, maxSize);
-        }*/
-
-        return surfaceToRender;
-    }
-
-    private CornerInViewport GetOppositeCorner(CornerInViewport invisibleNeighbor)
-    {
-        if (invisibleNeighbor == TopLeft) return BottomRight;
-        if (invisibleNeighbor == TopRight) return BottomLeft;
-        if (invisibleNeighbor == BottomRight) return TopLeft;
-        if (invisibleNeighbor == BottomLeft) return TopRight;
-
-        throw new Exception("Invalid corner");
-    }
-
-    private static RectI TraverseShrinkCorners(RectI surfaceToRender, Func<VecI, bool> isWithinBoundsAction,
-        CornerInViewport invisibleNeighbor, CornerInViewport firstNotWithinBounds, VecI maxSize)
-    {
-        VecI crossDirection = firstNotWithinBounds.ConnectedCorners.First(x => x != invisibleNeighbor).SurfacePosition -
-                              firstNotWithinBounds.SurfacePosition;
-        VecI crossDirectionNormalized = crossDirection.SignsWithZero();
-
-        VecI currentSurfacePosition = firstNotWithinBounds.SurfacePosition;
-        VecI oppositeSurfacePosition = invisibleNeighbor.SurfacePosition;
-
-        if (!isWithinBoundsAction(currentSurfacePosition) && !isWithinBoundsAction(oppositeSurfacePosition))
-        {
-            surfaceToRender = ShrinkToDir(surfaceToRender, crossDirectionNormalized);
-
-            VecI currentDirection = crossDirectionNormalized;
-
-            int maxIterations = currentDirection.X != 0 ? maxSize.X : maxSize.Y;
-            for (int i = 0; i < maxIterations; i++)
-            {
-                VecI nextSurfacePosition = currentSurfacePosition + currentDirection;
-                VecI oppositeNextSurfacePosition = oppositeSurfacePosition + currentDirection;
-                if (!isWithinBoundsAction(nextSurfacePosition) && !isWithinBoundsAction(oppositeNextSurfacePosition))
-                {
-                    surfaceToRender = ShrinkToDir(surfaceToRender, currentDirection);
-                    currentSurfacePosition = nextSurfacePosition;
-                    oppositeSurfacePosition = oppositeNextSurfacePosition;
-                }
-                else
-                {
-                    break;
-                }
-            }
-        }
-
-        return surfaceToRender;
-    }
-
-    private static RectI ShrinkToDir(RectI surfaceToRender, VecI crossDirectionNormalized)
-    {
-        // if crossDirectionNormalized is (1, 0) then we shrink width, if it's (0, 1) we shrink height, if it's (-1, 0) we shrink X, if it's (0, -1) we shrink Y
-
-        int x = surfaceToRender.X;
-        int y = surfaceToRender.Y;
-        int width = surfaceToRender.Width;
-        int height = surfaceToRender.Height;
-
-        if (crossDirectionNormalized.X == 1)
-        {
-            x++;
-            width--;
-        }
-        else if (crossDirectionNormalized.X == -1)
-        {
-            width--;
-        }
-        else if (crossDirectionNormalized.Y == 1)
-        {
-            y++;
-            height--;
-        }
-        else if (crossDirectionNormalized.Y == -1)
-        {
-            height--;
-        }
-
-        return new RectI(x, y, width, height);
-    }
-
-    private CornerInViewport GetStartingCorner()
-    {
-       CornerInViewport[] corners = { TopLeft, TopRight, BottomRight, BottomLeft };
-       CornerInViewport? firstWithInvisibleNeighbors = corners.FirstOrDefault(x => !x.IsWithinBounds && x.ConnectedCorners.All(y => !y.IsWithinBounds));
-
-       if (firstWithInvisibleNeighbors != null)
-       {
-           return firstWithInvisibleNeighbors;
-       }
-
-       return corners.First(x => !x.IsWithinBounds);
-    }
-}