Browse Source

Line tool overlay

Equbuxu 2 years ago
parent
commit
15c8a9b760

+ 17 - 0
src/PixiEditor/Helpers/Converters/BoolOrToVisibilityConverter.cs

@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace PixiEditor.Helpers.Converters;
+internal class BoolOrToVisibilityConverter : SingleInstanceMultiValueConverter<BoolOrToVisibilityConverter>
+{
+    public override object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
+    {
+        bool boolean = values.Aggregate(false, (acc, cur) => acc |= ((cur as bool?) ?? false));
+        return boolean ? Visibility.Visible : Visibility.Collapsed;
+    }
+}

+ 5 - 0
src/PixiEditor/Models/DocumentModels/ChangeExecutionController.cs

@@ -170,4 +170,9 @@ internal class ChangeExecutionController
     }
     }
 
 
     public void TransformAppliedInlet() => currentSession?.OnTransformApplied();
     public void TransformAppliedInlet() => currentSession?.OnTransformApplied();
+
+    public void LineOverlayMovedInlet(VecD start, VecD end)
+    {
+        currentSession?.OnLineOverlayMoved(start, end);
+    }
 }
 }

+ 56 - 20
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/LineToolExecutor.cs

@@ -1,5 +1,6 @@
 using ChunkyImageLib.DataHolders;
 using ChunkyImageLib.DataHolders;
 using PixiEditor.ChangeableDocument.Actions;
 using PixiEditor.ChangeableDocument.Actions;
+using PixiEditor.DrawingApi.Core.ColorsImpl;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Numerics;
 using PixiEditor.DrawingApi.Core.Surface;
 using PixiEditor.DrawingApi.Core.Surface;
 using PixiEditor.Models.Enums;
 using PixiEditor.Models.Enums;
@@ -9,17 +10,28 @@ using PixiEditor.ViewModels.SubViewModels.Tools.ToolSettings.Toolbars;
 
 
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 namespace PixiEditor.Models.DocumentModels.UpdateableChangeExecutors;
 #nullable enable
 #nullable enable
-internal class LineToolExecutor : ShapeToolExecutor<LineToolViewModel>
+internal class LineToolExecutor : UpdateableChangeExecutor
 {
 {
     public override ExecutorType Type => ExecutorType.ToolLinked;
     public override ExecutorType Type => ExecutorType.ToolLinked;
+
+    private VecI startPos;
+    private Color strokeColor;
+    private int strokeWidth;
+    private Guid memberGuid;
+    private bool drawOnMask;
+
+    private VecI curPos;
+    private bool started = false;
+    private bool transforming = false;
+
     public override ExecutionState Start()
     public override ExecutionState Start()
     {
     {
         ColorsViewModel? colorsVM = ViewModelMain.Current?.ColorsSubViewModel;
         ColorsViewModel? colorsVM = ViewModelMain.Current?.ColorsSubViewModel;
-        toolViewModel = ViewModelMain.Current?.ToolsSubViewModel.GetTool<LineToolViewModel>();
-        BasicToolbar? toolbar = (BasicToolbar?)toolViewModel?.Toolbar;
+        LineToolViewModel? toolViewModel = ViewModelMain.Current?.ToolsSubViewModel.GetTool<LineToolViewModel>();
         StructureMemberViewModel? member = document?.SelectedStructureMember;
         StructureMemberViewModel? member = document?.SelectedStructureMember;
-        if (colorsVM is null || toolbar is null || member is null)
+        if (colorsVM is null || toolViewModel is null || member is null)
             return ExecutionState.Error;
             return ExecutionState.Error;
+
         drawOnMask = member is LayerViewModel layer ? layer.ShouldDrawOnMask : true;
         drawOnMask = member is LayerViewModel layer ? layer.ShouldDrawOnMask : true;
         if (drawOnMask && !member.HasMaskBindable)
         if (drawOnMask && !member.HasMaskBindable)
             return ExecutionState.Error;
             return ExecutionState.Error;
@@ -28,34 +40,58 @@ internal class LineToolExecutor : ShapeToolExecutor<LineToolViewModel>
 
 
         startPos = controller!.LastPixelPosition;
         startPos = controller!.LastPixelPosition;
         strokeColor = colorsVM.PrimaryColor;
         strokeColor = colorsVM.PrimaryColor;
-        strokeWidth = toolbar.ToolSize;
+        strokeWidth = toolViewModel.ToolSize;
         memberGuid = member.GuidValue;
         memberGuid = member.GuidValue;
 
 
         colorsVM.AddSwatch(strokeColor);
         colorsVM.AddSwatch(strokeColor);
-        DrawShape(startPos, true);
+
         return ExecutionState.Success;
         return ExecutionState.Success;
     }
     }
 
 
-    private void DrawLine(VecI curPos, bool firstDraw)
+    public override void OnPixelPositionChange(VecI pos)
     {
     {
-        RectI rect = firstDraw ? new RectI(curPos, VecI.Zero) : RectI.FromTwoPixels(startPos, curPos);
-        if (rect.Width == 0)
-            rect.Width = 1;
-        if (rect.Height == 0)
-            rect.Height = 1;
-
-        lastRect = rect;
-
+        if (transforming)
+            return;
+        started = true;
+        curPos = pos;
         internals!.ActionAccumulator.AddActions(new DrawLine_Action(memberGuid, startPos, curPos, strokeWidth, strokeColor, StrokeCap.Butt, drawOnMask));
         internals!.ActionAccumulator.AddActions(new DrawLine_Action(memberGuid, startPos, curPos, strokeWidth, strokeColor, StrokeCap.Butt, drawOnMask));
     }
     }
 
 
-    protected override void DrawShape(VecI currentPos, bool firstDraw) => DrawLine(currentPos, firstDraw);
+    public override void OnLeftMouseButtonUp()
+    {
+        if (!started)
+        {
+            onEnded(this);
+            return;
+        }
+        transforming = true;
+        document!.LineToolOverlayViewModel.LineStart = startPos + new VecD(0.5);
+        document!.LineToolOverlayViewModel.LineEnd = curPos + new VecD(0.5);
+        document!.LineToolOverlayViewModel.IsEnabled = true;
+    }
+
+    public override void OnLineOverlayMoved(VecD start, VecD end)
+    {
+        if (!transforming)
+            return;
+        internals!.ActionAccumulator.AddActions(new DrawLine_Action(memberGuid, (VecI)start, (VecI)end, strokeWidth, strokeColor, StrokeCap.Butt, drawOnMask));
+    }
 
 
-    protected override IAction TransformMovedAction(ShapeData data, ShapeCorners corners)
+    public override void OnTransformApplied()
     {
     {
-        return new DrawLine_Action(memberGuid, (VecI)corners.TopLeft, (VecI)corners.BottomRight.Ceiling(), strokeWidth, strokeColor, StrokeCap.Butt,
-            drawOnMask);
+        if (!transforming)
+            return;
+
+        document!.LineToolOverlayViewModel.IsEnabled = false;
+        internals!.ActionAccumulator.AddFinishedActions(new EndDrawLine_Action());
+        onEnded(this);
     }
     }
 
 
-    protected override IAction EndDrawAction() => new EndDrawLine_Action();
+    public override void ForceStop()
+    {
+        if (transforming)
+            document!.LineToolOverlayViewModel.IsEnabled = false;
+
+        internals!.ActionAccumulator.AddFinishedActions(new EndDrawLine_Action());
+    }
 }
 }

+ 1 - 0
src/PixiEditor/Models/DocumentModels/UpdateableChangeExecutors/UpdateableChangeExecutor.cs

@@ -46,4 +46,5 @@ internal abstract class UpdateableChangeExecutor
     public virtual void OnConvertedKeyUp(Key key) { }
     public virtual void OnConvertedKeyUp(Key key) { }
     public virtual void OnTransformMoved(ShapeCorners corners) { }
     public virtual void OnTransformMoved(ShapeCorners corners) { }
     public virtual void OnTransformApplied() { }
     public virtual void OnTransformApplied() { }
+    public virtual void OnLineOverlayMoved(VecD start, VecD end) { }
 }
 }

+ 4 - 0
src/PixiEditor/ViewModels/SubViewModels/Document/DocumentViewModel.cs

@@ -142,6 +142,7 @@ internal partial class DocumentViewModel : NotifyableObject
 
 
     public DocumentTransformViewModel TransformViewModel { get; }
     public DocumentTransformViewModel TransformViewModel { get; }
     public ReferenceLayerViewModel ReferenceLayerViewModel { get; }
     public ReferenceLayerViewModel ReferenceLayerViewModel { get; }
+    public LineToolOverlayViewModel LineToolOverlayViewModel { get; }
 
 
     private DocumentInternalParts Internals { get; }
     private DocumentInternalParts Internals { get; }
 
 
@@ -158,6 +159,9 @@ internal partial class DocumentViewModel : NotifyableObject
         TransformViewModel = new();
         TransformViewModel = new();
         TransformViewModel.TransformMoved += (_, args) => Internals.ChangeController.TransformMovedInlet(args);
         TransformViewModel.TransformMoved += (_, args) => Internals.ChangeController.TransformMovedInlet(args);
 
 
+        LineToolOverlayViewModel = new();
+        LineToolOverlayViewModel.LineMoved += (_, args) => Internals.ChangeController.LineOverlayMovedInlet(args.Item1, args.Item2);
+
         foreach (KeyValuePair<ChunkResolution, WriteableBitmap> bitmap in Bitmaps)
         foreach (KeyValuePair<ChunkResolution, WriteableBitmap> bitmap in Bitmaps)
         {
         {
             DrawingSurface? surface = DrawingSurface.Create(
             DrawingSurface? surface = DrawingSurface.Create(

+ 41 - 0
src/PixiEditor/ViewModels/SubViewModels/Document/LineToolOverlayViewModel.cs

@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using PixiEditor.DrawingApi.Core.Numerics;
+
+namespace PixiEditor.ViewModels.SubViewModels.Document;
+internal class LineToolOverlayViewModel : NotifyableObject
+{
+    public event EventHandler<(VecD, VecD)> LineMoved;
+
+    private VecD lineStart;
+    public VecD LineStart
+    {
+        get => lineStart;
+        set 
+        {
+            if (SetProperty(ref lineStart, value))
+                LineMoved?.Invoke(this, (lineStart, lineEnd));
+        }
+    }
+
+    private VecD lineEnd;
+    public VecD LineEnd
+    {
+        get => lineEnd;
+        set
+        {
+            if (SetProperty(ref lineEnd, value))
+                LineMoved?.Invoke(this, (lineStart, lineEnd));
+        }
+    }
+
+    private bool isEnabled;
+    public bool IsEnabled
+    {
+        get => isEnabled;
+        set => SetProperty(ref isEnabled, value);
+    }
+}

+ 161 - 0
src/PixiEditor/Views/UserControls/Overlays/LineToolOverlay/LineToolOverlay.cs

@@ -0,0 +1,161 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+using ChunkyImageLib.DataHolders;
+using PixiEditor;
+using PixiEditor.DrawingApi.Core.Numerics;
+using PixiEditor.Views;
+using PixiEditor.Views.UserControls;
+using PixiEditor.Views.UserControls.Overlays;
+using PixiEditor.Views.UserControls.Overlays.LineToolOverlay;
+using PixiEditor.Views.UserControls.Overlays.TransformOverlay;
+using TerraFX.Interop.Windows;
+
+namespace PixiEditor.Views.UserControls.Overlays.LineToolOverlay;
+internal class LineToolOverlay : Control
+{
+    public static readonly DependencyProperty ZoomboxScaleProperty =
+    DependencyProperty.Register(nameof(ZoomboxScale), typeof(double), typeof(LineToolOverlay),
+         new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.AffectsRender, OnZoomboxScaleChanged));
+
+    public static readonly DependencyProperty LineStartProperty =
+        DependencyProperty.Register(nameof(LineStart), typeof(VecD), typeof(LineToolOverlay),
+            new FrameworkPropertyMetadata(VecD.Zero, FrameworkPropertyMetadataOptions.AffectsRender));
+
+    public static readonly DependencyProperty LineEndProperty =
+        DependencyProperty.Register(nameof(LineEnd), typeof(VecD), typeof(LineToolOverlay),
+            new FrameworkPropertyMetadata(VecD.Zero, FrameworkPropertyMetadataOptions.AffectsRender));
+
+    public VecD LineEnd
+    {
+        get => (VecD)GetValue(LineEndProperty);
+        set => SetValue(LineEndProperty, value);
+    }
+
+    public VecD LineStart
+    {
+        get => (VecD)GetValue(LineStartProperty);
+        set => SetValue(LineStartProperty, value);
+    }
+
+    public double ZoomboxScale
+    {
+        get => (double)GetValue(ZoomboxScaleProperty);
+        set => SetValue(ZoomboxScaleProperty, value);
+    }
+
+    private Pen blackPen = new Pen(Brushes.Black, 1);
+
+    private VecD mouseDownPos = VecD.Zero;
+    private VecD lineStartOnMouseDown = VecD.Zero;
+    private VecD lineEndOnMouseDown = VecD.Zero;
+
+    private LineToolOverlayAnchor? capturedAnchor = null;
+    private bool dragging = false;
+
+    private PathGeometry handleGeometry = new()
+    {
+        FillRule = FillRule.Nonzero,
+        Figures = (PathFigureCollection)new PathFigureCollectionConverter()
+            .ConvertFrom("M 0.50025839 0 0.4248062 0.12971572 0.34987079 0.25994821 h 0.1002584 V 0.45012906 H 0.25994831 V 0.34987066 L 0.12971577 0.42480604 0 0.5002582 0.12971577 0.57519373 0.25994831 0.65012926 V 0.5498709 H 0.45012919 V 0.74005175 H 0.34987079 L 0.42480619 0.87028439 0.50025839 1 0.57519399 0.87028439 0.65012959 0.74005175 H 0.54987119 V 0.5498709 H 0.74005211 V 0.65012926 L 0.87028423 0.57519358 1 0.5002582 0.87028423 0.42480604 0.74005169 0.34987066 v 0.1002584 H 0.54987077 V 0.25994821 h 0.1002584 L 0.5751938 0.12971572 Z"),
+    };
+
+    public LineToolOverlay()
+    {
+        Cursor = Cursors.Arrow;
+    }
+
+    private static void OnZoomboxScaleChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
+    {
+        var self = (LineToolOverlay)obj;
+        double newScale = (double)args.NewValue;
+        self.blackPen.Thickness = 1.0 / newScale;
+    }
+
+    protected override void OnRender(DrawingContext context)
+    {
+        context.DrawRectangle(Brushes.White, blackPen, TransformHelper.ToAnchorRect(LineStart, ZoomboxScale));
+        context.DrawRectangle(Brushes.White, blackPen, TransformHelper.ToAnchorRect(LineEnd, ZoomboxScale));
+
+        VecD handlePos = TransformHelper.GetDragHandlePos(new ShapeCorners(new RectD(LineStart, LineEnd - LineStart)), ZoomboxScale);
+        const double CrossSize = TransformHelper.MoveHandleSize - 1;
+        context.DrawRectangle(Brushes.White, blackPen, TransformHelper.ToHandleRect(handlePos, ZoomboxScale));
+        handleGeometry.Transform = new MatrixTransform(
+            0, CrossSize / ZoomboxScale,
+            CrossSize / ZoomboxScale, 0,
+            handlePos.X - CrossSize / (ZoomboxScale * 2), handlePos.Y - CrossSize / (ZoomboxScale * 2)
+        );
+        context.DrawGeometry(Brushes.Black, null, handleGeometry);
+    }
+
+    protected override void OnMouseDown(MouseButtonEventArgs e)
+    {
+        base.OnMouseDown(e);
+        if (e.ChangedButton != MouseButton.Left)
+            return;
+
+        e.Handled = true;
+
+        VecD pos = TransformHelper.ToVecD(e.GetPosition(this));
+        VecD handlePos = TransformHelper.GetDragHandlePos(new ShapeCorners(new RectD(LineStart, LineEnd - LineStart)), ZoomboxScale);
+
+        if (TransformHelper.IsWithinAnchor(LineStart, pos, ZoomboxScale))
+            capturedAnchor = LineToolOverlayAnchor.Start;
+        else if (TransformHelper.IsWithinAnchor(LineEnd, pos, ZoomboxScale))
+            capturedAnchor = LineToolOverlayAnchor.End;
+        else if (TransformHelper.IsWithinTransformHandle(handlePos, pos, ZoomboxScale))
+            dragging = true;
+
+        mouseDownPos = pos;
+        lineStartOnMouseDown = LineStart;
+        lineEndOnMouseDown = LineEnd;
+
+        CaptureMouse();
+    }
+
+    protected override void OnMouseMove(MouseEventArgs e)
+    {
+        base.OnMouseMove(e);
+        e.Handled = true;
+
+        VecD pos = TransformHelper.ToVecD(e.GetPosition(this));
+        if (capturedAnchor == LineToolOverlayAnchor.Start)
+        {
+            LineStart = pos;
+            return;
+        }
+
+        if (capturedAnchor == LineToolOverlayAnchor.End)
+        {
+            LineEnd = pos;
+            return;
+        }
+
+        if (dragging)
+        {
+            var delta = pos - mouseDownPos;
+            LineStart = lineStartOnMouseDown + delta;
+            LineEnd = lineEndOnMouseDown + delta;
+            return;
+        }
+    }
+
+    protected override void OnMouseUp(MouseButtonEventArgs e)
+    {
+        base.OnMouseUp(e);
+        if (e.ChangedButton != MouseButton.Left)
+            return;
+
+        e.Handled = true;
+        capturedAnchor = null;
+        dragging = false;
+
+        ReleaseMouseCapture();
+    }
+}

+ 6 - 0
src/PixiEditor/Views/UserControls/Overlays/LineToolOverlay/LineToolOverlayAnchor.cs

@@ -0,0 +1,6 @@
+namespace PixiEditor.Views.UserControls.Overlays.LineToolOverlay;
+internal enum LineToolOverlayAnchor
+{
+    Start,
+    End
+}

+ 23 - 10
src/PixiEditor/Views/UserControls/Viewport.xaml

@@ -8,11 +8,12 @@
     xmlns:local="clr-namespace:PixiEditor.Views.UserControls"
     xmlns:local="clr-namespace:PixiEditor.Views.UserControls"
     xmlns:zoombox="clr-namespace:PixiEditor.Zoombox;assembly=PixiEditor.Zoombox"
     xmlns:zoombox="clr-namespace:PixiEditor.Zoombox;assembly=PixiEditor.Zoombox"
     xmlns:sys="clr-namespace:System;assembly=System.Runtime"
     xmlns:sys="clr-namespace:System;assembly=System.Runtime"
-    xmlns:to="clr-namespace:PixiEditor.Views.UserControls.Overlays.TransformOverlay"
+    xmlns:transformOverlay="clr-namespace:PixiEditor.Views.UserControls.Overlays.TransformOverlay"
+    xmlns:lineOverlay="clr-namespace:PixiEditor.Views.UserControls.Overlays.LineToolOverlay"
     xmlns:uc="clr-namespace:PixiEditor.Views.UserControls"
     xmlns:uc="clr-namespace:PixiEditor.Views.UserControls"
-    xmlns:ov="clr-namespace:PixiEditor.Views.UserControls.Overlays"
-    xmlns:sym="clr-namespace:PixiEditor.Views.UserControls.Overlays.SymmetryOverlay"
-    xmlns:brush="clr-namespace:PixiEditor.Views.UserControls.Overlays.BrushShapeOverlay"
+    xmlns:overlays="clr-namespace:PixiEditor.Views.UserControls.Overlays"
+    xmlns:symOverlay="clr-namespace:PixiEditor.Views.UserControls.Overlays.SymmetryOverlay"
+    xmlns:brushOverlay="clr-namespace:PixiEditor.Views.UserControls.Overlays.BrushShapeOverlay"
     xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
     xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
     xmlns:vm="clr-namespace:PixiEditor.ViewModels"
     xmlns:vm="clr-namespace:PixiEditor.ViewModels"
     xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
     xmlns:converters="clr-namespace:PixiEditor.Helpers.Converters"
@@ -171,7 +172,7 @@
                         Source="{Binding TargetBitmap}"
                         Source="{Binding TargetBitmap}"
                         Visibility="{Binding Source={vm:ToolVM ColorPickerToolViewModel}, Path=PickFromCanvas, Converter={converters:BoolToHiddenVisibilityConverter}}"
                         Visibility="{Binding Source={vm:ToolVM ColorPickerToolViewModel}, Path=PickFromCanvas, Converter={converters:BoolToHiddenVisibilityConverter}}"
                         RenderOptions.BitmapScalingMode="{Binding Zoombox.Scale, Converter={converters:ScaleToBitmapScalingModeConverter}}"/>
                         RenderOptions.BitmapScalingMode="{Binding Zoombox.Scale, Converter={converters:ScaleToBitmapScalingModeConverter}}"/>
-                    <sym:SymmetryOverlay
+                    <symOverlay:SymmetryOverlay
                         IsHitTestVisible="{Binding ZoomMode, Converter={converters:ZoomModeToHitTestVisibleConverter}}"
                         IsHitTestVisible="{Binding ZoomMode, Converter={converters:ZoomModeToHitTestVisibleConverter}}"
                         ZoomboxScale="{Binding Zoombox.Scale}"
                         ZoomboxScale="{Binding Zoombox.Scale}"
                         HorizontalAxisVisible="{Binding Document.HorizontalSymmetryAxisEnabledBindable}"
                         HorizontalAxisVisible="{Binding Document.HorizontalSymmetryAxisEnabledBindable}"
@@ -181,20 +182,20 @@
                         DragCommand="{cmds:Command PixiEditor.Document.DragSymmetry, UseProvided=True}"
                         DragCommand="{cmds:Command PixiEditor.Document.DragSymmetry, UseProvided=True}"
                         DragEndCommand="{cmds:Command PixiEditor.Document.EndDragSymmetry, UseProvided=True}" 
                         DragEndCommand="{cmds:Command PixiEditor.Document.EndDragSymmetry, UseProvided=True}" 
                         DragStartCommand="{cmds:Command PixiEditor.Document.StartDragSymmetry, UseProvided=True}" />
                         DragStartCommand="{cmds:Command PixiEditor.Document.StartDragSymmetry, UseProvided=True}" />
-                    <ov:SelectionOverlay
+                    <overlays:SelectionOverlay
                         ShowFill="{Binding ToolsSubViewModel.ActiveTool, Source={vm:MainVM}, Converter={converters:IsSelectionToolConverter}}"
                         ShowFill="{Binding ToolsSubViewModel.ActiveTool, Source={vm:MainVM}, Converter={converters:IsSelectionToolConverter}}"
                         Path="{Binding Document.SelectionPathBindable}"
                         Path="{Binding Document.SelectionPathBindable}"
                         ZoomboxScale="{Binding Zoombox.Scale}" />
                         ZoomboxScale="{Binding Zoombox.Scale}" />
-                    <brush:BrushShapeOverlay
+                    <brushOverlay:BrushShapeOverlay
                         IsHitTestVisible="False"
                         IsHitTestVisible="False"
                         Visibility="{Binding Document.TransformViewModel.TransformActive, Converter={converters:InverseBoolToVisibilityConverter}}"
                         Visibility="{Binding Document.TransformViewModel.TransformActive, Converter={converters:InverseBoolToVisibilityConverter}}"
                         ZoomboxScale="{Binding Zoombox.Scale}"
                         ZoomboxScale="{Binding Zoombox.Scale}"
                         MouseEventSource="{Binding Zoombox.Tag.BackgroundGrid, Mode=OneTime}"
                         MouseEventSource="{Binding Zoombox.Tag.BackgroundGrid, Mode=OneTime}"
                         MouseReference="{Binding Zoombox.Tag.MainImage, Mode=OneTime}"
                         MouseReference="{Binding Zoombox.Tag.MainImage, Mode=OneTime}"
                         BrushSize="{Binding ToolsSubViewModel.ActiveBasicToolbar.ToolSize, Source={vm:MainVM}}"
                         BrushSize="{Binding ToolsSubViewModel.ActiveBasicToolbar.ToolSize, Source={vm:MainVM}}"
-                        BrushShape="{Binding ToolsSubViewModel.ActiveTool.BrushShape, Source={vm:MainVM}, FallbackValue={x:Static brush:BrushShape.Hidden}}"
+                        BrushShape="{Binding ToolsSubViewModel.ActiveTool.BrushShape, Source={vm:MainVM}, FallbackValue={x:Static brushOverlay:BrushShape.Hidden}}"
                         />
                         />
-                    <to:TransformOverlay
+                    <transformOverlay:TransformOverlay
                         Cursor="Arrow"
                         Cursor="Arrow"
                         IsHitTestVisible="{Binding ZoomMode, Converter={converters:ZoomModeToHitTestVisibleConverter}}"
                         IsHitTestVisible="{Binding ZoomMode, Converter={converters:ZoomModeToHitTestVisibleConverter}}"
                         HorizontalAlignment="Stretch"
                         HorizontalAlignment="Stretch"
@@ -210,6 +211,11 @@
                         InternalState="{Binding Document.TransformViewModel.InternalState, Mode=TwoWay}"
                         InternalState="{Binding Document.TransformViewModel.InternalState, Mode=TwoWay}"
                         ZoomboxScale="{Binding Zoombox.Scale}"
                         ZoomboxScale="{Binding Zoombox.Scale}"
                         ZoomboxAngle="{Binding Zoombox.Angle}"/>
                         ZoomboxAngle="{Binding Zoombox.Angle}"/>
+                    <lineOverlay:LineToolOverlay
+                        Visibility="{Binding Document.LineToolOverlayViewModel.IsEnabled, Converter={converters:BoolToVisibilityConverter}}"
+                        LineStart="{Binding Document.LineToolOverlayViewModel.LineStart, Mode=TwoWay}"
+                        LineEnd="{Binding Document.LineToolOverlayViewModel.LineEnd, Mode=TwoWay}"
+                        ZoomboxScale="{Binding Zoombox.Scale}"/>
                     <Grid IsHitTestVisible="False" 
                     <Grid IsHitTestVisible="False" 
                         ShowGridLines="True" Width="{Binding Document.Width}" Height="{Binding Document.Height}" Panel.ZIndex="10" 
                         ShowGridLines="True" Width="{Binding Document.Width}" Height="{Binding Document.Height}" Panel.ZIndex="10" 
                         Visibility="{Binding GridLinesVisible, Converter={converters:BoolToVisibilityConverter}, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}}">
                         Visibility="{Binding GridLinesVisible, Converter={converters:BoolToVisibilityConverter}, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:Viewport}}">
@@ -287,9 +293,16 @@
             Margin="5" 
             Margin="5" 
             VerticalAlignment="Bottom" 
             VerticalAlignment="Bottom" 
             Style="{StaticResource GrayRoundButton}"
             Style="{StaticResource GrayRoundButton}"
-            Visibility="{Binding Document.TransformViewModel.TransformActive, Converter={converters:BoolToVisibilityConverter}, ElementName=vpUc}"
             Command="{cmds:Command PixiEditor.Tools.ApplyTransform}">
             Command="{cmds:Command PixiEditor.Tools.ApplyTransform}">
             Apply transform
             Apply transform
+            <Button.Visibility>
+                <MultiBinding Converter="{converters:BoolOrToVisibilityConverter}">
+                    <MultiBinding.Bindings>
+                        <Binding ElementName="vpUc" Path="Document.TransformViewModel.TransformActive"/>
+                        <Binding ElementName="vpUc" Path="Document.LineToolOverlayViewModel.IsEnabled"/>
+                    </MultiBinding.Bindings>
+                </MultiBinding>
+            </Button.Visibility>
         </Button>
         </Button>
     </Grid>
     </Grid>
 </UserControl>
 </UserControl>