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);