From 7966d3243c8251ac1322b6a53548aa202d1352fd Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Wed, 22 Jan 2020 23:22:50 +0100 Subject: [PATCH] Finished anchor movement Fixed a few issues when creating new layers --- src/Artemis.Core/Models/Profile/Layer.cs | 5 +- .../Profile/LayerProperties/Keyframe.cs | 2 +- .../Visualization/ProfileLayerViewModel.cs | 4 +- .../Visualization/Tools/EditToolViewModel.cs | 132 +++++++++--------- src/Artemis.UI/Services/LayerShapeService.cs | 16 ++- 5 files changed, 84 insertions(+), 75 deletions(-) diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs index 51d3eb32a..756ce6828 100644 --- a/src/Artemis.Core/Models/Profile/Layer.cs +++ b/src/Artemis.Core/Models/Profile/Layer.cs @@ -153,7 +153,7 @@ namespace Artemis.Core.Models.Profile public override void Render(double deltaTime, SKCanvas canvas) { - if (Path == null) + if (Path == null || LayerShape == null) return; canvas.Save(); @@ -198,6 +198,9 @@ namespace Artemis.Core.Models.Profile private SKPoint GetLayerAnchor(bool absolute) { + if (LayerShape == null) + return SKPoint.Empty; + if (!absolute) { var anchor = AnchorPointProperty.CurrentValue; diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/Keyframe.cs b/src/Artemis.Core/Models/Profile/LayerProperties/Keyframe.cs index 63af5027b..5cd31a61e 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/Keyframe.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/Keyframe.cs @@ -11,7 +11,7 @@ public T Value { - get => (T) BaseValue; + get => BaseValue != null ? (T) BaseValue : default; set => BaseValue = value; } } diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileLayerViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileLayerViewModel.cs index 746067ad3..b7d7a1015 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileLayerViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileLayerViewModel.cs @@ -90,8 +90,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization var layerGeometry = group.GetOutlinedPathGeometry(); var opacityGeometry = Geometry.Combine(Geometry.Empty, layerGeometry, GeometryCombineMode.Exclude, new TranslateTransform()); - layerGeometry.Freeze(); - opacityGeometry.Freeze(); + LayerGeometry = layerGeometry; OpacityGeometry = opacityGeometry; } @@ -126,7 +125,6 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization } shapeGeometry.Transform = _layerEditorService.GetLayerTransformGroup(Layer); - shapeGeometry.Freeze(); ShapeGeometry = shapeGeometry; }); } 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 ba612c9cb..17a89a1e1 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EditToolViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EditToolViewModel.cs @@ -3,7 +3,6 @@ using System.Diagnostics; using System.Windows; using System.Windows.Input; using System.Windows.Media; -using System.Windows.Shapes; using Artemis.Core.Models.Profile; using Artemis.UI.Services; using Artemis.UI.Services.Interfaces; @@ -18,10 +17,10 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools private readonly ILayerEditorService _layerEditorService; private bool _draggingHorizontally; private bool _draggingVertically; - private bool _isDragging; - private Point _dragStart; private SKPoint _dragOffset; + private Point _dragStart; private SKPoint _dragStartAnchor; + private bool _isDragging; public EditToolViewModel(ProfileViewModel profileViewModel, IProfileEditorService profileEditorService, ILayerEditorService layerEditorService) : base(profileViewModel, profileEditorService) @@ -51,35 +50,34 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools private void Update() { - if (ProfileEditorService.SelectedProfileElement is Layer layer) + if (!(ProfileEditorService.SelectedProfileElement is Layer layer) || layer.LayerShape == null) + return; + + ShapeRectangle = _layerEditorService.GetShapeRenderRect(layer.LayerShape).ToSKRect(); + ShapeAnchor = _layerEditorService.GetLayerAnchor(layer, true); + + // Get a square path to use for mutation point placement + var path = _layerEditorService.GetLayerPath(layer, true, true, true); + 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); + + Execute.PostToUIThread(() => { - if (layer.LayerShape != null) + var shapeGeometry = new RectangleGeometry(_layerEditorService.GetShapeRenderRect(layer.LayerShape)) { - 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); - }); - } - } + Transform = _layerEditorService.GetLayerTransformGroup(layer) + }; + shapeGeometry.Freeze(); + ShapeGeometry = shapeGeometry; + ShapeTransformCollection = _layerEditorService.GetLayerTransformGroup(layer).Children; + }); } public void ShapeEditMouseDown(object sender, MouseButtonEventArgs e) @@ -113,8 +111,17 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools if (ProfileEditorService.SelectedProfileElement is Layer layer) { var dragStartPosition = GetRelativePosition(sender, e).ToSKPoint(); - _dragOffset = TopLeft + (dragStartPosition - TopLeft); - _dragStartAnchor = _layerEditorService.GetLayerAnchor(layer, false); + + // Mouse doesn't care about rotation so get the layer path without rotation + var path = _layerEditorService.GetLayerPath(layer, true, true, false); + var topLeft = path.Points[0]; + // Measure from the top-left of the shape (without rotation) + _dragOffset = topLeft + (dragStartPosition - topLeft); + // Get the absolute layer anchor and make it relative to the unrotated shape + _dragStartAnchor = _layerEditorService.GetLayerAnchor(layer, true) - topLeft; + // Ensure the anchor starts in the center of the shape it is now relative to + _dragStartAnchor.X -= path.Bounds.Width / 2f; + _dragStartAnchor.Y -= path.Bounds.Height / 2f; } _isDragging = true; @@ -129,6 +136,9 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools { ProfileEditorService.UpdateSelectedProfileElement(); + _dragOffset = SKPoint.Empty; + _dragStartAnchor = SKPoint.Empty; + _isDragging = false; _draggingHorizontally = false; _draggingVertically = false; @@ -141,45 +151,27 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools { if (!_isDragging || !(ProfileEditorService.SelectedProfileElement is Layer layer)) return; - - var position = GetRelativePosition(sender, e); - var start = _dragStartAnchor; - var current = start + (position.ToSKPoint() - _dragOffset); - var transformedPoints = UnTransformPoints(new[] {start, current}, layer, start); - var scaled = _layerEditorService.GetScaledPoint(layer, transformedPoints[1], false); - Debug.WriteLine("Current value before mousedown " + _dragStartAnchor); - Debug.WriteLine("Current value before move " + layer.AnchorPointProperty.CurrentValue); - var before = TopLeft; + // The start anchor is relative to an unrotated version of the shape + var start = _dragStartAnchor; + // Add the current position to the start anchor to determine the new position + var current = start + (GetRelativePosition(sender, e).ToSKPoint() - _dragOffset); + // In order to keep the mouse movement unrotated, counter-act the active rotation + var countered = UnTransformPoints(new[] {start, current}, layer, start); + var scaled = _layerEditorService.GetScaledPoint(layer, countered[1], false); + + // Update the anchor point, this causes the shape to move layer.AnchorPointProperty.SetCurrentValue(scaled, ProfileEditorService.CurrentTime); - var path = _layerEditorService.GetLayerPath(layer); - var difference = before - path.Points[0]; - var scaledDifference = _layerEditorService.GetScaledPoint(layer, difference, false); + // TopLeft is not updated yet and acts as a snapshot of the top-left before changing the anchor + var path = _layerEditorService.GetLayerPath(layer, true, true, true); + // Calculate the (scaled) difference between the old and now position + var difference = _layerEditorService.GetScaledPoint(layer, TopLeft - path.Points[0], false); + // Apply the difference so that the shape effectively stays in place + layer.PositionProperty.SetCurrentValue(layer.PositionProperty.CurrentValue + difference, ProfileEditorService.CurrentTime); - layer.PositionProperty.SetCurrentValue(layer.PositionProperty.CurrentValue + scaledDifference, ProfileEditorService.CurrentTime); ProfileEditorService.UpdateProfilePreview(); } - private SKPoint[] UnTransformPoints(SKPoint[] skPoints, Layer layer, SKPoint pivot) - { - var counterRotatePath = new SKPath(); - counterRotatePath.AddPoly(skPoints, false); - counterRotatePath.Transform(SKMatrix.MakeRotationDegrees(layer.RotationProperty.CurrentValue * -1, pivot.X, pivot.Y)); - counterRotatePath.Transform(SKMatrix.MakeScale(1f / layer.SizeProperty.CurrentValue.Width, 1f / layer.SizeProperty.CurrentValue.Height)); - - return counterRotatePath.Points; - } - - private SKPoint[] TransformPoints(SKPoint[] skPoints, Layer layer, SKPoint pivot) - { - var counterRotatePath = new SKPath(); - counterRotatePath.AddPoly(skPoints, false); - counterRotatePath.Transform(SKMatrix.MakeRotationDegrees(layer.RotationProperty.CurrentValue , pivot.X, pivot.Y)); - counterRotatePath.Transform(SKMatrix.MakeScale( layer.SizeProperty.CurrentValue.Width, layer.SizeProperty.CurrentValue.Height)); - - return counterRotatePath.Points; - } - public void Move(object sender, MouseEventArgs e) { if (!_isDragging || !(ProfileEditorService.SelectedProfileElement is Layer layer)) @@ -357,6 +349,16 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools ApplyShapeResize(skRect); } + private SKPoint[] UnTransformPoints(SKPoint[] skPoints, Layer layer, SKPoint pivot) + { + var counterRotatePath = new SKPath(); + counterRotatePath.AddPoly(skPoints, false); + counterRotatePath.Transform(SKMatrix.MakeRotationDegrees(layer.RotationProperty.CurrentValue * -1, pivot.X, pivot.Y)); + counterRotatePath.Transform(SKMatrix.MakeScale(1f / layer.SizeProperty.CurrentValue.Width, 1f / layer.SizeProperty.CurrentValue.Height)); + + return counterRotatePath.Points; + } + private Point GetRelativePosition(object sender, MouseEventArgs mouseEventArgs) { var parent = VisualTreeHelper.GetParent((DependencyObject) sender); diff --git a/src/Artemis.UI/Services/LayerShapeService.cs b/src/Artemis.UI/Services/LayerShapeService.cs index ef7c9f3e5..b2412cee8 100644 --- a/src/Artemis.UI/Services/LayerShapeService.cs +++ b/src/Artemis.UI/Services/LayerShapeService.cs @@ -46,7 +46,7 @@ namespace Artemis.UI.Services } /// - public SKPath GetLayerPath(Layer layer) + public SKPath GetLayerPath(Layer layer, bool includeTranslation, bool includeScale, bool includeRotation) { var layerRect = GetLayerRenderRect(layer).ToSKRect(); var shapeRect = GetShapeRenderRect(layer.LayerShape).ToSKRect(); @@ -61,9 +61,12 @@ namespace Artemis.UI.Services var path = new SKPath(); path.AddRect(shapeRect); - path.Transform(SKMatrix.MakeTranslation(x, y)); - path.Transform(SKMatrix.MakeScale(layer.SizeProperty.CurrentValue.Width, layer.SizeProperty.CurrentValue.Height, anchor.X, anchor.Y)); - path.Transform(SKMatrix.MakeRotationDegrees(layer.RotationProperty.CurrentValue, anchor.X, anchor.Y)); + if (includeTranslation) + path.Transform(SKMatrix.MakeTranslation(x, y)); + if (includeScale) + path.Transform(SKMatrix.MakeScale(layer.SizeProperty.CurrentValue.Width, layer.SizeProperty.CurrentValue.Height, anchor.X, anchor.Y)); + if (includeRotation) + path.Transform(SKMatrix.MakeRotationDegrees(layer.RotationProperty.CurrentValue, anchor.X, anchor.Y)); return path; } @@ -207,8 +210,11 @@ namespace Artemis.UI.Services /// Returns an absolute and scaled rectangular path for the given layer that is corrected for the current render scale. /// /// + /// + /// + /// /// - SKPath GetLayerPath(Layer layer); + SKPath GetLayerPath(Layer layer, bool includeTranslation, bool includeScale, bool includeRotation); void ReverseLayerPath(Layer layer, SKPath path);