From d79437b3fd9c4a3d2500bed0c190e1f340b960d8 Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Fri, 17 Jan 2020 00:29:43 +0100 Subject: [PATCH] WIP on making edit tool compatible with proper translations --- .../Visualization/Tools/EditToolView.xaml | 174 ++++++++++++------ .../Visualization/Tools/EditToolViewModel.cs | 82 ++++++--- src/Artemis.UI/Services/LayerShapeService.cs | 149 +++++++++++---- 3 files changed, 292 insertions(+), 113 deletions(-) diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EditToolView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EditToolView.xaml index 9efd421cd..42956448e 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EditToolView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EditToolView.xaml @@ -10,119 +10,183 @@ d:DesignWidth="800" d:DataContext="{d:DesignInstance {x:Type local:EditToolViewModel}}"> - - - - - - + + + + - - + + + + + + + + + + + + + + + + + - - - + Width="6" + Height="6" + Canvas.Left="{Binding ShapeRectangle.Left}" + Canvas.Top="{Binding ShapeRectangle.Top}" + Fill="Red" + Margin="-3,-3,0,0" + Cursor="SizeNWSE" /> + + Width="10" + Height="10" + Canvas.Left="{Binding ShapeRectangle.MidX}" + Canvas.Top="{Binding ShapeRectangle.Top}" + Fill="Red" + Margin="-5,-5,0,0" + Cursor="SizeNS" /> - - + Width="6" + Height="6" + Canvas.Left="{Binding ShapeRectangle.Right}" + Canvas.Top="{Binding ShapeRectangle.Top}" + Fill="Red" + Margin="-3,-3,0,0" + Cursor="SizeNESW" /> - - - + + Width="10" + Height="10" + Canvas.Left="{Binding ShapeRectangle.Right}" + Canvas.Top="{Binding ShapeRectangle.MidY}" + Fill="Red" + Margin="-5,-5,0,0" + Cursor="SizeWE" /> - - + + Width="6" + Height="6" + Canvas.Left="{Binding ShapeRectangle.Right}" + Canvas.Top="{Binding ShapeRectangle.Bottom}" + Fill="Red" + Margin="-3,-3,0,0" + Cursor="SizeNWSE" /> - - + + Width="10" + Height="10" + Canvas.Left="{Binding ShapeRectangle.MidX}" + Canvas.Top="{Binding ShapeRectangle.Bottom}" + Fill="Red" + Margin="-5,-5,0,0" + Cursor="SizeNS" /> - - + + Width="6" + Height="6" + Canvas.Left="{Binding ShapeRectangle.Left}" + Canvas.Top="{Binding ShapeRectangle.Bottom}" + Fill="Red" + Margin="-3, -3, 0,0" + Cursor="SizeNESW" /> - - + - - - - + Width="10" + Height="10" + Canvas.Left="{Binding ShapeRectangle.Left}" + Canvas.Top="{Binding ShapeRectangle.MidY}" + Fill="Red" + Margin="-5,-5,0,0" + Cursor="SizeWE" /> + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EditToolViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EditToolViewModel.cs index 282b26781..b98cd4043 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EditToolViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EditToolViewModel.cs @@ -33,9 +33,10 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools profileEditorService.ProfilePreviewUpdated += (sender, args) => Update(); } - public SKRect ShapeSkRect { get; set; } - public SKPoint AnchorSkPoint { get; set; } - public TransformCollection LayerTransformChildren { get; set; } + public SKRect ShapeRectangle { get; set; } + public SKPoint ShapeAnchor { get; set; } + public RectangleGeometry ShapeGeometry { get; set; } + public TransformCollection ShapeTransformCollection { get; set; } private void Update() { @@ -43,13 +44,41 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools { if (layer.LayerShape != null) { - ShapeSkRect = _layerEditorService.GetShapeRenderRect(layer.LayerShape).ToSKRect(); - AnchorSkPoint = layer.LayerShape.GetUnscaledAnchor(); - Execute.PostToUIThread(() => LayerTransformChildren = _layerEditorService.GetLayerTransformGroup(layer).Children); + ShapeRectangle = _layerEditorService.GetShapeRenderRect(layer.LayerShape).ToSKRect(); + ShapeAnchor = _layerEditorService.GetLayerAnchor(layer, true); + + Execute.PostToUIThread(() => + { + var shapeGeometry = new RectangleGeometry(_layerEditorService.GetShapeRenderRect(layer.LayerShape)); + shapeGeometry.Transform = _layerEditorService.GetLayerTransformGroup(layer); + shapeGeometry.Freeze(); + ShapeGeometry = shapeGeometry; + ShapeTransformCollection = _layerEditorService.GetLayerTransformGroup(layer).Children; + + // Get a square path to use for mutation point placement + var path = _layerEditorService.GetLayerPath(layer); + TopLeft = path.Points[0]; + TopRight = path.Points[1]; + BottomRight = path.Points[2]; + BottomLeft = path.Points[3]; + + TopCenter = new SKPoint((TopLeft.X + TopRight.X) / 2, (TopLeft.Y + TopRight.Y) / 2); + RightCenter = new SKPoint((TopRight.X + BottomRight.X) / 2, (TopRight.Y + BottomRight.Y) / 2); + BottomCenter = new SKPoint((BottomLeft.X + BottomRight.X) / 2, (BottomLeft.Y + BottomRight.Y) / 2); + LeftCenter = new SKPoint((TopLeft.X + BottomLeft.X) / 2, (TopLeft.Y + BottomLeft.Y) / 2); + }); } } } + public SKPoint TopLeft { get; set; } + public SKPoint TopRight { get; set; } + public SKPoint BottomRight { get; set; } + public SKPoint BottomLeft { get; set; } + public SKPoint TopCenter { get; set; } + public SKPoint RightCenter { get; set; } + public SKPoint BottomCenter { get; set; } + public SKPoint LeftCenter { get; set; } public void ShapeEditMouseDown(object sender, MouseButtonEventArgs e) { @@ -82,8 +111,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools ShapeEditMouseDown(sender, e); // Override the drag offset var dragStartPosition = GetRelativePosition(sender, e); - _dragOffsetX = AnchorSkPoint.X - dragStartPosition.X; - _dragOffsetY = AnchorSkPoint.Y - dragStartPosition.Y; +// _dragOffsetX = AnchorSkPoint.X - dragStartPosition.X; +// _dragOffsetY = AnchorSkPoint.Y - dragStartPosition.Y; _dragStart = dragStartPosition; } @@ -117,25 +146,30 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools return; var position = GetRelativePosition(sender, e); - var skRect = layer.LayerShape.RenderRectangle; + var skRect = _layerEditorService.GetShapeRenderRect(layer.LayerShape).ToSKRect(); var x = (float) (position.X + _dragOffsetX); var y = (float) (position.Y + _dragOffsetY); - // TODO: Update the translation - // if (!Keyboard.IsKeyDown(Key.LeftShift) && !Keyboard.IsKeyDown(Key.RightShift)) - // layer.LayerShape.SetFromUnscaledRectangle(SKRect.Create(x, y, skRect.Width, skRect.Height), ProfileEditorService.CurrentTime); - // else - // { - // if (_draggingVertically) - // layer.LayerShape.SetFromUnscaledRectangle(SKRect.Create(skRect.Left, y, skRect.Width, skRect.Height), ProfileEditorService.CurrentTime); - // else if (_draggingHorizontally) - // layer.LayerShape.SetFromUnscaledRectangle(SKRect.Create(x, skRect.Top, skRect.Width, skRect.Height), ProfileEditorService.CurrentTime); - // else - // { - // _draggingHorizontally = Math.Abs(position.X - _dragStart.X) > Math.Abs(position.Y - _dragStart.Y); - // _draggingVertically = Math.Abs(position.X - _dragStart.X) < Math.Abs(position.Y - _dragStart.Y); - // } - // } + if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) + { + if (_draggingVertically) + x = skRect.Left; + else if (_draggingHorizontally) + y = skRect.Top; + else + { + _draggingHorizontally = Math.Abs(position.X - _dragStart.X) > Math.Abs(position.Y - _dragStart.Y); + _draggingVertically = Math.Abs(position.X - _dragStart.X) < Math.Abs(position.Y - _dragStart.Y); + return; + } + } + + // Convert the desired X and Y position to a translation + var layerRect = _layerEditorService.GetLayerRenderRect(layer).ToSKRect(); + var translation = layer.PositionProperty.CurrentValue; + var scaled = _layerEditorService.GetScaledPoint(layer, new SKPoint(x , y)); + Console.WriteLine(scaled); + layer.PositionProperty.SetCurrentValue(scaled, ProfileEditorService.CurrentTime); ProfileEditorService.UpdateProfilePreview(); } diff --git a/src/Artemis.UI/Services/LayerShapeService.cs b/src/Artemis.UI/Services/LayerShapeService.cs index af7f00d81..06baf9529 100644 --- a/src/Artemis.UI/Services/LayerShapeService.cs +++ b/src/Artemis.UI/Services/LayerShapeService.cs @@ -21,18 +21,6 @@ namespace Artemis.UI.Services } /// - public Rect GetShapeRenderRect(LayerShape layerShape) - { - // Adjust the render rectangle for the difference in render scale - var renderScale = _settingsService.GetSetting("Core.RenderScale", 1.0).Value; - return new Rect( - layerShape.RenderRectangle.Left / renderScale * 1, - layerShape.RenderRectangle.Top / renderScale * 1, - Math.Max(0, layerShape.RenderRectangle.Width / renderScale * 1), - Math.Max(0, layerShape.RenderRectangle.Height / renderScale * 1) - ); - } - public Rect GetLayerRenderRect(Layer layer) { // Adjust the render rectangle for the difference in render scale @@ -45,6 +33,80 @@ namespace Artemis.UI.Services ); } + /// + public SKPath GetLayerPath(Layer layer) + { + var layerRect = GetLayerRenderRect(layer).ToSKRect(); + var shapeRect = GetShapeRenderRect(layer.LayerShape).ToSKRect(); + + // Apply transformation like done by the core during layer rendering + var anchor = GetLayerAnchor(layer, false); + var position = layer.PositionProperty.CurrentValue; + var size = layer.SizeProperty.CurrentValue; + var rotation = layer.RotationProperty.CurrentValue; + + var path = new SKPath(); + path.AddRect(shapeRect); + + path.Transform(SKMatrix.MakeScale(size.Width, size.Height, anchor.X, anchor.Y)); + path.Transform(SKMatrix.MakeRotationDegrees(rotation, anchor.X, anchor.Y)); + path.Transform(SKMatrix.MakeTranslation(position.X * layerRect.Width, position.Y * layerRect.Height)); + + return path; + } + + /// + public SKPoint GetLayerAnchor(Layer layer, bool includeTranslate) + { + var layerRect = GetLayerRenderRect(layer).ToSKRect(); + var shapeRect = GetShapeRenderRect(layer.LayerShape).ToSKRect(); + var anchor = layer.AnchorPointProperty.CurrentValue; + + // Scale the anchor and make it originate from the center of the untranslated layer shape + anchor.X = anchor.X * layerRect.Width + shapeRect.MidX; + anchor.Y = anchor.Y * layerRect.Height + shapeRect.MidY; + + if (includeTranslate) + { + var position = layer.PositionProperty.CurrentValue; + anchor.X += position.X * layerRect.Width; + anchor.Y += position.Y * layerRect.Height; + } + + return anchor; + } + + /// + public TransformGroup GetLayerTransformGroup(Layer layer) + { + var layerRect = GetLayerRenderRect(layer).ToSKRect(); + + // Apply transformation like done by the core during layer rendering + var anchor = GetLayerAnchor(layer, false); + var position = layer.PositionProperty.CurrentValue; + var size = layer.SizeProperty.CurrentValue; + var rotation = layer.RotationProperty.CurrentValue; + + var transformGroup = new TransformGroup(); + transformGroup.Children.Add(new ScaleTransform(size.Width, size.Height, anchor.X, anchor.Y)); + transformGroup.Children.Add(new RotateTransform(rotation, anchor.X, anchor.Y)); + transformGroup.Children.Add(new TranslateTransform(position.X * layerRect.Width, position.Y * layerRect.Height)); + return transformGroup; + } + + /// + public Rect GetShapeRenderRect(LayerShape layerShape) + { + // Adjust the render rectangle for the difference in render scale + var renderScale = _settingsService.GetSetting("Core.RenderScale", 1.0).Value; + return new Rect( + layerShape.RenderRectangle.Left / renderScale * 1, + layerShape.RenderRectangle.Top / renderScale * 1, + Math.Max(0, layerShape.RenderRectangle.Width / renderScale * 1), + Math.Max(0, layerShape.RenderRectangle.Height / renderScale * 1) + ); + } + /// public void SetShapeRenderRect(LayerShape layerShape, Rect rect) { @@ -66,31 +128,50 @@ namespace Artemis.UI.Services } /// - public TransformGroup GetLayerTransformGroup(Layer layer) + public SKPoint GetScaledPoint(Layer layer, SKPoint point) { - var layerRect = GetLayerRenderRect(layer).ToSKRect(); - var shapeRect = GetShapeRenderRect(layer.LayerShape).ToSKRect(); - - // Apply transformation like done by the core during layer rendering - var anchor = layer.AnchorPointProperty.CurrentValue; - var position = layer.PositionProperty.CurrentValue; - var size = layer.SizeProperty.CurrentValue; - var rotation = layer.RotationProperty.CurrentValue; - // Scale the anchor and make it originate from the center of the untranslated layer shape - anchor.X = anchor.X * layerRect.Width + shapeRect.MidX; - anchor.Y = anchor.Y * layerRect.Height + shapeRect.MidY; - - var transformGroup = new TransformGroup(); - transformGroup.Children.Add(new ScaleTransform(size.Width, size.Height, anchor.X, anchor.Y)); - transformGroup.Children.Add(new RotateTransform(rotation, anchor.X, anchor.Y)); - transformGroup.Children.Add(new TranslateTransform(position.X * layerRect.Width, position.Y * layerRect.Height)); - - return transformGroup; + var rect = GetLayerRenderRect(layer).ToSKRect(); + // Adjust the provided rect for the difference in render scale + var renderScale = _settingsService.GetSetting("Core.RenderScale", 1.0).Value; + return new SKPoint( + 100f / layer.AbsoluteRectangle.Width * ((float) (point.X * renderScale) - rect.Left) / 100f, + 100f / layer.AbsoluteRectangle.Height * ((float) (point.Y * renderScale) - rect.Top) / 100f + ); } } public interface ILayerEditorService : IArtemisUIService { + /// + /// Returns an absolute and scaled rectangle for the given layer that is corrected for the current render scale. + /// + /// + /// + Rect GetLayerRenderRect(Layer layer); + + /// + /// Returns an absolute and scaled rectangular path for the given layer that is corrected for the current render scale. + /// + /// + /// + SKPath GetLayerPath(Layer layer); + + /// + /// Returns an absolute and scaled anchor for the given layer, optionally with the translation applied. + /// + /// + /// + /// + SKPoint GetLayerAnchor(Layer layer, bool includeTranslate); + + /// + /// Creates a WPF transform group that contains all the transformations required to render the provided layer. + /// Note: Run on UI thread. + /// + /// + /// + TransformGroup GetLayerTransformGroup(Layer layer); + /// /// Returns an absolute and scaled rectangle for the given shape that is corrected for the current render scale. /// @@ -106,11 +187,11 @@ namespace Artemis.UI.Services void SetShapeRenderRect(LayerShape layerShape, Rect rect); /// - /// Creates a WPF transform group that contains all the transformations required to render the provided layer. - /// Note: Run on UI thread. + /// Returns a new point scaled to the layer. /// /// + /// /// - TransformGroup GetLayerTransformGroup(Layer layer); + SKPoint GetScaledPoint(Layer layer, SKPoint point); } } \ No newline at end of file