Forráskód Böngészése

Transform overlay is working

Krzysztof Krysiński 1 éve
szülő
commit
6a61452bf4

+ 22 - 0
src/ChunkyImageLib/DataHolders/ShapeCorners.cs

@@ -162,6 +162,28 @@ public struct ShapeCorners
         TopRight = TopRight + delta
     };
 
+    public ShapeCorners AsScaled(float uniformScale)
+    {
+        VecD center = RectCenter;
+        VecD topLeftDelta = TopLeft - center;
+        VecD topRightDelta = TopRight - center;
+        VecD bottomLeftDelta = BottomLeft - center;
+        VecD bottomRightDelta = BottomRight - center;
+
+        topLeftDelta *= uniformScale;
+        topRightDelta *= uniformScale;
+        bottomLeftDelta *= uniformScale;
+        bottomRightDelta *= uniformScale;
+
+        return new ShapeCorners()
+        {
+            TopLeft = center + topLeftDelta,
+            TopRight = center + topRightDelta,
+            BottomLeft = center + bottomLeftDelta,
+            BottomRight = center + bottomRightDelta
+        };
+    }
+
     public static bool operator !=(ShapeCorners left, ShapeCorners right) => !(left == right);
     public static bool operator == (ShapeCorners left, ShapeCorners right)
     {

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

@@ -118,23 +118,6 @@
                 </Border>
             </overlays:TogglableFlyout.Child>
         </overlays:TogglableFlyout>
-        <visuals:Scene
-            Focusable="False" Name="scene"
-            ZIndex="1"
-            Width="{Binding RealDimensions.X, ElementName=vpUc}"
-            Height="{Binding RealDimensions.Y, ElementName=vpUc}"
-            Surface="{Binding TargetBitmap, ElementName=vpUc}"
-            Scale="{Binding Scale, ElementName=zoombox, Mode=OneWay}"
-            Document="{Binding Document, ElementName=vpUc, Mode=OneWay}"
-            ContentPosition="{Binding CanvasPos, 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}"
-            ActiveOverlays="{Binding ElementName=vpUc, Path=ActiveOverlays}"
-            FadeOut="{Binding Source={viewModels:ToolVM ColorPickerToolViewModel}, Path=PickOnlyFromReferenceLayer, Mode=OneWay}"
-            CheckerImagePath="/Images/CheckerTile.png"
-            ui1:RenderOptionsBindable.BitmapInterpolationMode="{Binding Scale, Converter={converters:ScaleToBitmapScalingModeConverter}, ElementName=zoombox}">
-        </visuals:Scene>
         <zoombox:Zoombox
             Tag="{Binding ElementName=vpUc}"
             x:Name="zoombox"
@@ -185,6 +168,23 @@
                 </Border>
             </zoombox:Zoombox.AdditionalContent>
         </zoombox:Zoombox>
+        <visuals:Scene
+            Focusable="False" Name="scene"
+            ZIndex="1"
+            Width="{Binding RealDimensions.X, ElementName=vpUc}"
+            Height="{Binding RealDimensions.Y, ElementName=vpUc}"
+            Surface="{Binding TargetBitmap, ElementName=vpUc}"
+            Scale="{Binding Scale, ElementName=zoombox, Mode=OneWay}"
+            Document="{Binding Document, ElementName=vpUc, Mode=OneWay}"
+            ContentPosition="{Binding CanvasPos, 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}"
+            ActiveOverlays="{Binding ElementName=vpUc, Path=ActiveOverlays}"
+            FadeOut="{Binding Source={viewModels:ToolVM ColorPickerToolViewModel}, Path=PickOnlyFromReferenceLayer, Mode=OneWay}"
+            CheckerImagePath="/Images/CheckerTile.png"
+            ui1:RenderOptionsBindable.BitmapInterpolationMode="{Binding Scale, Converter={converters:ScaleToBitmapScalingModeConverter}, ElementName=zoombox}">
+        </visuals:Scene>
         <overlays:ReferenceLayerOverlay SizeChanged="OnReferenceImageSizeChanged"
                                         ReferenceLayer="{Binding Document.ReferenceLayerViewModel}"
                                         ReferenceLayerScale="{Binding ReferenceLayerScale}"

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

@@ -9,6 +9,7 @@ using PixiEditor.AvaloniaUI.Views.Overlays;
 using PixiEditor.AvaloniaUI.Views.Overlays.LineToolOverlay;
 using PixiEditor.AvaloniaUI.Views.Overlays.SelectionOverlay;
 using PixiEditor.AvaloniaUI.Views.Overlays.SymmetryOverlay;
+using PixiEditor.AvaloniaUI.Views.Overlays.TransformOverlay;
 using PixiEditor.AvaloniaUI.Views.Visuals;
 
 namespace PixiEditor.AvaloniaUI.Views.Main.ViewportControls;
@@ -21,6 +22,7 @@ internal class ViewportOverlays
     private SelectionOverlay selectionOverlay;
     private SymmetryOverlay symmetryOverlay;
     private LineToolOverlay lineToolOverlay;
+    private TransformOverlay transformOverlay;
 
     public void Init(Viewport viewport)
     {
@@ -37,10 +39,14 @@ internal class ViewportOverlays
         lineToolOverlay = new LineToolOverlay();
         BindLineToolOverlay();
 
+        transformOverlay = new TransformOverlay();
+        BindTransformOverlay();
+
         Viewport.ActiveOverlays.Add(gridLinesOverlay);
         Viewport.ActiveOverlays.Add(selectionOverlay);
         Viewport.ActiveOverlays.Add(symmetryOverlay);
         Viewport.ActiveOverlays.Add(lineToolOverlay);
+        Viewport.ActiveOverlays.Add(transformOverlay);
     }
 
     private void BindGridLines()
@@ -159,4 +165,96 @@ internal class ViewportOverlays
         lineToolOverlay.Bind(LineToolOverlay.LineStartProperty, lineStartBinding);
         lineToolOverlay.Bind(LineToolOverlay.LineEndProperty, lineEndBinding);
     }
+
+    private void BindTransformOverlay()
+    {
+        Binding isVisibleBinding = new()
+        {
+            Source = Viewport,
+            Path = "Document.TransformViewModel.TransformActive",
+            Mode = BindingMode.OneWay
+        };
+
+        Binding actionCompletedBinding = new()
+        {
+            Source = Viewport,
+            Path = "Document.TransformViewModel.ActionCompletedCommand",
+            Mode = BindingMode.OneWay
+        };
+
+        Binding cornersBinding = new()
+        {
+            Source = Viewport,
+            Path = "Document.TransformViewModel.Corners",
+            Mode = BindingMode.TwoWay
+        };
+
+        Binding requestedCornersBinding = new()
+        {
+            Source = Viewport,
+            Path = "Document.TransformViewModel.RequestedCorners",
+            Mode = BindingMode.TwoWay
+        };
+
+        Binding cornerFreedomBinding = new()
+        {
+            Source = Viewport,
+            Path = "Document.TransformViewModel.CornerFreedom",
+            Mode = BindingMode.OneWay
+        };
+
+        Binding sideFreedomBinding = new()
+        {
+            Source = Viewport,
+            Path = "Document.TransformViewModel.SideFreedom",
+            Mode = BindingMode.OneWay
+        };
+
+        Binding lockRotationBinding = new()
+        {
+            Source = Viewport,
+            Path = "Document.TransformViewModel.LockRotation",
+            Mode = BindingMode.OneWay
+        };
+
+        Binding coverWholeScreenBinding = new()
+        {
+            Source = Viewport,
+            Path = "Document.TransformViewModel.CoverWholeScreen",
+            Mode = BindingMode.OneWay
+        };
+
+        Binding snapToAnglesBinding = new()
+        {
+            Source = Viewport,
+            Path = "Document.TransformViewModel.SnapToAngles",
+            Mode = BindingMode.OneWay
+        };
+
+        Binding internalStateBinding = new()
+        {
+            Source = Viewport,
+            Path = "Document.TransformViewModel.InternalState",
+            Mode = BindingMode.TwoWay
+        };
+
+        Binding zoomboxAngleBinding = new()
+        {
+            Source = Viewport,
+            Path = "Zoombox.Angle",
+            Mode = BindingMode.OneWay
+        };
+
+        transformOverlay.Bind(Visual.IsVisibleProperty, isVisibleBinding);
+        transformOverlay.Bind(TransformOverlay.ActionCompletedProperty, actionCompletedBinding);
+        transformOverlay.Bind(TransformOverlay.CornersProperty, cornersBinding);
+        transformOverlay.Bind(TransformOverlay.RequestedCornersProperty, requestedCornersBinding);
+        transformOverlay.Bind(TransformOverlay.CornerFreedomProperty, cornerFreedomBinding);
+        transformOverlay.Bind(TransformOverlay.SideFreedomProperty, sideFreedomBinding);
+        transformOverlay.Bind(TransformOverlay.LockRotationProperty, lockRotationBinding);
+        transformOverlay.Bind(TransformOverlay.CoverWholeScreenProperty, coverWholeScreenBinding);
+        transformOverlay.Bind(TransformOverlay.SnapToAnglesProperty, snapToAnglesBinding);
+        transformOverlay.Bind(TransformOverlay.InternalStateProperty, internalStateBinding);
+        transformOverlay.Bind(TransformOverlay.ZoomboxAngleProperty, zoomboxAngleBinding);
+    }
 }

+ 34 - 30
src/PixiEditor.AvaloniaUI/Views/Overlays/TransformOverlay/TransformOverlay.cs

@@ -10,6 +10,7 @@ using PixiEditor.AvaloniaUI.Helpers;
 using PixiEditor.AvaloniaUI.Helpers.Extensions;
 using PixiEditor.AvaloniaUI.Views.Overlays.Handles;
 using PixiEditor.DrawingApi.Core.Numerics;
+using PixiEditor.Extensions.UI.Overlays;
 
 namespace PixiEditor.AvaloniaUI.Views.Overlays.TransformOverlay;
 #nullable enable
@@ -155,7 +156,7 @@ internal class TransformOverlay : Overlay
 
     private Geometry rotateCursorGeometry = Handle.GetHandleGeometry("RotateHandle");
 
-    private Point lastPointerPos;
+    private VecD lastPointerPos;
 
     public TransformOverlay()
     {
@@ -219,7 +220,7 @@ internal class TransformOverlay : Overlay
         DrawOverlay(drawingContext, new(Bounds.Width, Bounds.Height), Corners, InternalState.Origin, ZoomScale);
 
         if (capturedAnchor is null)
-            UpdateRotationCursor(TransformHelper.ToVecD(lastPointerPos));
+            UpdateRotationCursor(lastPointerPos);
     }
 
     private void DrawMouseInputArea(DrawingContext context, VecD size)
@@ -309,18 +310,12 @@ internal class TransformOverlay : Overlay
         context.DrawGeometry(Brushes.White, blackPen, rotateCursorGeometry);
     }
 
-    protected override void OnPointerExited(PointerEventArgs e)
-    {
-        base.OnPointerExited(e);
-        rotateCursorGeometry.Transform = new ScaleTransform(0, 0);
-    }
-
     private void OnAnchorHandlePressed(Handle source, VecD position)
     {
         capturedAnchor = anchorMap[source];
         cornersOnStartAnchorDrag = Corners;
         originOnStartAnchorDrag = InternalState.Origin;
-        mousePosOnStartAnchorDrag = TransformHelper.ToVecD(lastPointerPos);
+        mousePosOnStartAnchorDrag = lastPointerPos;
 
         if (source == originHandle)
         {
@@ -333,24 +328,27 @@ internal class TransformOverlay : Overlay
         StartMoving(position);
     }
 
-    protected override void OnPointerPressed(PointerPressedEventArgs e)
+    protected override void OnOverlayPointerExited(OverlayPointerArgs args)
     {
-        base.OnPointerPressed(e);
-        if (e.GetMouseButton(this) != MouseButton.Left)
-            return;
+        rotateCursorGeometry.Transform = new ScaleTransform(0, 0);
+        Refresh();
+    }
 
-        VecD pos = TransformHelper.ToVecD(e.GetPosition(this));
+    protected override void OnOverlayPointerPressed(OverlayPointerArgs args)
+    {
+        if (args.PointerButton != MouseButton.Left)
+            return;
 
-        if(Handles.Any(x => x.IsWithinHandle(x.Position, pos, ZoomScale))) return;
+        if(Handles.Any(x => x.IsWithinHandle(x.Position, args.Point, ZoomScale))) return;
 
-        if (!CanRotate(pos))
+        if (!CanRotate(args.Point))
         {
-            StartMoving(pos);
+            StartMoving(args.Point);
         }
         else if (!LockRotation)
         {
             isRotating = true;
-            mousePosOnStartRotate = pos;
+            mousePosOnStartRotate = args.Point;
             cornersOnStartRotate = Corners;
             propAngle1OnStartRotate = InternalState.ProportionalAngle1;
             propAngle2OnStartRotate = InternalState.ProportionalAngle2;
@@ -360,16 +358,16 @@ internal class TransformOverlay : Overlay
             return;
         }
         
-        e.Pointer.Capture(this);
-        e.Handled = true;
+        args.Pointer.Capture(this);
+        args.Handled = true;
     }
 
-    protected override void OnPointerMoved(PointerEventArgs e)
+    protected override void OnOverlayPointerMoved(OverlayPointerArgs e)
     {
         Cursor finalCursor = new Cursor(StandardCursorType.Arrow);
 
-        lastPointerPos = e.GetPosition(this);
-        VecD pos = TransformHelper.ToVecD(lastPointerPos);
+        lastPointerPos = e.Point;
+        VecD pos = lastPointerPos;
 
         if (isMoving)
         {
@@ -383,7 +381,7 @@ internal class TransformOverlay : Overlay
             return;
         }
 
-        if (UpdateRotationCursor(TransformHelper.ToVecD(e.GetPosition(this))))
+        if (UpdateRotationCursor(e.Point))
         {
             finalCursor = new Cursor(StandardCursorType.None);
         }
@@ -406,12 +404,11 @@ internal class TransformOverlay : Overlay
         if (Cursor != finalCursor)
             Cursor = finalCursor;
 
-        InvalidateVisual();
+        Refresh();
     }
 
-    protected override void OnPointerReleased(PointerReleasedEventArgs e)
+    protected override void OnOverlayPointerReleased(OverlayPointerArgs e)
     {
-        base.OnPointerReleased(e);
         if (e.InitialPressMouseButton != MouseButton.Left)
             return;
 
@@ -420,13 +417,18 @@ internal class TransformOverlay : Overlay
             isRotating = false;
             e.Pointer.Capture(null);
             Cursor = new Cursor(StandardCursorType.Arrow);
-            var pos = TransformHelper.ToVecD(e.GetPosition(this));
+            var pos = e.Point;
             UpdateRotationCursor(pos);
         }
 
         StopMoving();
     }
 
+    public override bool TestHit(VecD point)
+    {
+        return base.TestHit(point) || Corners.AsScaled(1.25f).IsPointInside(point);
+    }
+
     private void OnMoveHandleReleased(Handle obj)
     {
         StopMoving();
@@ -504,7 +506,7 @@ internal class TransformOverlay : Overlay
         return true;
     }
 
-    private void HandleCapturedAnchorMovement(PointerEventArgs e)
+    private void HandleCapturedAnchorMovement(OverlayPointerArgs e)
     {
         if (capturedAnchor is null)
             throw new InvalidOperationException("No anchor is captured");
@@ -513,7 +515,7 @@ internal class TransformOverlay : Overlay
             (TransformHelper.IsSide((Anchor)capturedAnchor) && SideFreedom == TransformSideFreedom.Locked))
             return;
 
-        VecD pos = TransformHelper.ToVecD(e.GetPosition(this));
+        VecD pos = e.Point;
 
         if (TransformHelper.IsCorner((Anchor)capturedAnchor))
         {
@@ -544,6 +546,8 @@ internal class TransformOverlay : Overlay
             pos = HandleSnap(pos, out bool snapped);
             InternalState = InternalState with { OriginWasManuallyDragged = !snapped, Origin = pos, };
         }
+
+        Refresh();
     }
 
     private void UpdateOriginPos()

+ 4 - 0
src/PixiEditor.AvaloniaUI/Views/Visuals/Scene.cs

@@ -190,6 +190,7 @@ internal class Scene : Control, ICustomHitTest
 
     protected override void OnPointerEntered(PointerEventArgs e)
     {
+        //TODO: Invoke on overlay that is within bounds
         base.OnPointerEntered(e);
         if (ActiveOverlays != null)
         {
@@ -213,6 +214,7 @@ internal class Scene : Control, ICustomHitTest
 
     protected override void OnPointerMoved(PointerEventArgs e)
     {
+        //TODO: Invoke on overlay that is within bounds
         base.OnPointerMoved(e);
         if (ActiveOverlays != null)
         {
@@ -261,6 +263,7 @@ internal class Scene : Control, ICustomHitTest
 
     protected override void OnPointerExited(PointerEventArgs e)
     {
+        //TODO: Invoke on overlay that is out of bounds
         base.OnPointerExited(e);
         if (ActiveOverlays != null)
         {
@@ -428,6 +431,7 @@ internal class Scene : Control, ICustomHitTest
 
         foreach (Overlay overlay in ActiveOverlays)
         {
+            if (!overlay.IsVisible) continue;
             VecD pointInOverlay = ToCanvasSpace(point);
             if (overlay.TestHit(pointInOverlay))
             {