diff --git a/src/Artemis.Core/Models/Profile/KeyframeEngines/IntKeyframeEngine.cs b/src/Artemis.Core/Models/Profile/KeyframeEngines/IntKeyframeEngine.cs index b31e87b8d..9d25f8148 100644 --- a/src/Artemis.Core/Models/Profile/KeyframeEngines/IntKeyframeEngine.cs +++ b/src/Artemis.Core/Models/Profile/KeyframeEngines/IntKeyframeEngine.cs @@ -15,7 +15,7 @@ namespace Artemis.Core.Models.Profile.KeyframeEngines var nextKeyframe = (Keyframe) NextKeyframe; var diff = nextKeyframe.Value - currentKeyframe.Value; - return currentKeyframe.Value + diff * KeyframeProgressEased; + return (int) Math.Round(currentKeyframe.Value + diff * KeyframeProgressEased, MidpointRounding.AwayFromZero); } } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs index 3132d7210..9b6e44fac 100644 --- a/src/Artemis.Core/Models/Profile/Layer.cs +++ b/src/Artemis.Core/Models/Profile/Layer.cs @@ -132,12 +132,12 @@ namespace Artemis.Core.Models.Profile public LayerProperty SizeProperty { get; private set; } /// - /// The rotation property of this layer, also found in + /// The rotation property of this layer range 0 - 360, also found in /// public LayerProperty RotationProperty { get; private set; } /// - /// The opacity property of this layer, also found in + /// The opacity property of this layer range 0 - 100, also found in /// public LayerProperty OpacityProperty { get; private set; } @@ -158,9 +158,36 @@ namespace Artemis.Core.Models.Profile canvas.Save(); canvas.ClipPath(Path); + + // Apply transformations + var anchor = AnchorPointProperty.CurrentValue; + var position = PositionProperty.CurrentValue; + var size = SizeProperty.CurrentValue; + var rotation = RotationProperty.CurrentValue; + // Scale the anchor and make it originate from the center of the untranslated layer shape + anchor.X = anchor.X * AbsoluteRectangle.Width + LayerShape.RenderRectangle.MidX; + anchor.Y = anchor.Y * AbsoluteRectangle.Height + LayerShape.RenderRectangle.MidY; + + canvas.Translate(position.X * AbsoluteRectangle.Width, position.Y * AbsoluteRectangle.Height); + canvas.RotateDegrees(rotation, anchor.X, anchor.Y); + canvas.Scale(size.Width, size.Height, anchor.X, anchor.Y); + // Placeholder if (LayerShape?.RenderPath != null) - canvas.DrawPath(LayerShape.RenderPath, new SKPaint {Color = new SKColor(255, 0, 0)}); + { + var testColors = new List(); + for (var i = 0; i < 9; i++) + { + if (i != 8) + testColors.Add(SKColor.FromHsv(i * 32, 100, 100)); + else + testColors.Add(SKColor.FromHsv(0, 100, 100)); + } + + var shader = SKShader.CreateSweepGradient(new SKPoint(LayerShape.RenderRectangle.MidX, LayerShape.RenderRectangle.MidY), testColors.ToArray()); + canvas.DrawPath(LayerShape.RenderPath, new SKPaint {Shader = shader}); + } + LayerBrush?.Render(canvas); canvas.Restore(); } @@ -290,7 +317,7 @@ namespace Artemis.Core.Models.Profile Path = path; // This is called here so that the shape's render properties are up to date when other code // responds to OnRenderPropertiesUpdated - LayerShape?.CalculateRenderProperties(PositionProperty.CurrentValue, SizeProperty.CurrentValue); + LayerShape?.CalculateRenderProperties(); OnRenderPropertiesUpdated(); } diff --git a/src/Artemis.Core/Models/Profile/LayerShapes/Ellipse.cs b/src/Artemis.Core/Models/Profile/LayerShapes/Ellipse.cs index fa25cf18d..2ddf66601 100644 --- a/src/Artemis.Core/Models/Profile/LayerShapes/Ellipse.cs +++ b/src/Artemis.Core/Models/Profile/LayerShapes/Ellipse.cs @@ -13,18 +13,13 @@ namespace Artemis.Core.Models.Profile.LayerShapes { } - public override void CalculateRenderProperties(SKPoint shapePosition, SKSize shapeSize) + public override void CalculateRenderProperties() { - var width = Layer.AbsoluteRectangle.Width; - var height = Layer.AbsoluteRectangle.Height; - var rect = SKRect.Create(shapePosition.X * width, shapePosition.Y * height, shapeSize.Width * width, shapeSize.Height * height); + RenderRectangle = GetUnscaledRectangle(); var path = new SKPath(); - path.AddOval(rect); - path.Transform(SKMatrix.MakeTranslation(Layer.Rectangle.Left, Layer.Rectangle.Top)); - + path.AddOval(RenderRectangle); RenderPath = path; - RenderRectangle = path.GetRect(); } public override void ApplyToEntity() diff --git a/src/Artemis.Core/Models/Profile/LayerShapes/Fill.cs b/src/Artemis.Core/Models/Profile/LayerShapes/Fill.cs index 078137b62..f1d437ab9 100644 --- a/src/Artemis.Core/Models/Profile/LayerShapes/Fill.cs +++ b/src/Artemis.Core/Models/Profile/LayerShapes/Fill.cs @@ -13,11 +13,10 @@ namespace Artemis.Core.Models.Profile.LayerShapes { } - public override void CalculateRenderProperties(SKPoint shapePosition, SKSize shapeSize) + public override void CalculateRenderProperties() { - // TODO: Scale the path? Not sure if desirable + RenderRectangle = GetUnscaledRectangle(); RenderPath = Layer.Path; - RenderRectangle = Layer.Path.GetRect(); } public override void ApplyToEntity() diff --git a/src/Artemis.Core/Models/Profile/LayerShapes/LayerShape.cs b/src/Artemis.Core/Models/Profile/LayerShapes/LayerShape.cs index eb4231581..261a5ef4e 100644 --- a/src/Artemis.Core/Models/Profile/LayerShapes/LayerShape.cs +++ b/src/Artemis.Core/Models/Profile/LayerShapes/LayerShape.cs @@ -15,6 +15,7 @@ namespace Artemis.Core.Models.Profile.LayerShapes protected LayerShape(Layer layer, ShapeEntity shapeEntity) { Layer = layer; + ScaledRectangle = SKRect.Create(shapeEntity.X, shapeEntity.Y, shapeEntity.Width, shapeEntity.Height); } /// @@ -23,7 +24,13 @@ namespace Artemis.Core.Models.Profile.LayerShapes public Layer Layer { get; set; } /// - /// A render rectangle relative to the layer + /// A relative and scaled rectangle that defines where the shape is located in relation to the layer's size + /// Note: scaled means a range of 0.0 to 1.0. 1.0 being full width/height, 0.5 being half + /// + public SKRect ScaledRectangle { get; set; } + + /// + /// An absolute and scaled render rectangle /// public SKRect RenderRectangle { get; protected set; } @@ -32,56 +39,29 @@ namespace Artemis.Core.Models.Profile.LayerShapes /// public SKPath RenderPath { get; protected set; } - public abstract void CalculateRenderProperties(SKPoint shapePosition, SKSize shapeSize); + public abstract void CalculateRenderProperties(); public virtual void ApplyToEntity() { - Layer.LayerEntity.ShapeEntity = new ShapeEntity(); - } - - /// - /// Updates Position and Size using the provided unscaled rectangle - /// - /// An unscaled rectangle which is relative to the layer (1.0 being full width/height, 0.5 being half). - /// - /// An optional timespan to indicate where to set the properties, if null the properties' base values - /// will be used. - /// - public void SetFromUnscaledRectangle(SKRect rect, TimeSpan? time) - { - if (!Layer.Leds.Any()) + Layer.LayerEntity.ShapeEntity = new ShapeEntity { - Layer.PositionProperty.SetCurrentValue(SKPoint.Empty, time); - Layer.SizeProperty.SetCurrentValue(SKSize.Empty, time); - return; - } - - var x = Layer.Leds.Min(l => l.RgbLed.AbsoluteLedRectangle.Location.X); - var y = Layer.Leds.Min(l => l.RgbLed.AbsoluteLedRectangle.Location.Y); - var width = Layer.Leds.Max(l => l.RgbLed.AbsoluteLedRectangle.Location.X + l.RgbLed.AbsoluteLedRectangle.Size.Width) - x; - var height = Layer.Leds.Max(l => l.RgbLed.AbsoluteLedRectangle.Location.Y + l.RgbLed.AbsoluteLedRectangle.Size.Height) - y; - - Layer.PositionProperty.SetCurrentValue(new SKPoint((float) (100f / width * (rect.Left - x)) / 100f, (float) (100f / height * (rect.Top - y)) / 100f), time); - Layer.SizeProperty.SetCurrentValue(new SKSize((float) (100f / width * rect.Width) / 100f, (float) (100f / height * rect.Height) / 100f), time); - - CalculateRenderProperties(Layer.PositionProperty.CurrentValue, Layer.SizeProperty.CurrentValue); + X = ScaledRectangle.Left, + Y = ScaledRectangle.Top, + Width = ScaledRectangle.Width, + Height = ScaledRectangle.Height + }; } - public SKRect GetUnscaledRectangle() + protected SKRect GetUnscaledRectangle() { if (!Layer.Leds.Any()) return SKRect.Empty; - var x = Layer.Leds.Min(l => l.RgbLed.AbsoluteLedRectangle.Location.X); - var y = Layer.Leds.Min(l => l.RgbLed.AbsoluteLedRectangle.Location.Y); - var width = Layer.Leds.Max(l => l.RgbLed.AbsoluteLedRectangle.Location.X + l.RgbLed.AbsoluteLedRectangle.Size.Width) - x; - var height = Layer.Leds.Max(l => l.RgbLed.AbsoluteLedRectangle.Location.Y + l.RgbLed.AbsoluteLedRectangle.Size.Height) - y; - return SKRect.Create( - (float) (x + width * Layer.PositionProperty.CurrentValue.X), - (float) (y + height * Layer.PositionProperty.CurrentValue.Y), - (float) (width * Layer.SizeProperty.CurrentValue.Width), - (float) (height * Layer.SizeProperty.CurrentValue.Height) + Layer.AbsoluteRectangle.Left + Layer.AbsoluteRectangle.Width * ScaledRectangle.Left, + Layer.AbsoluteRectangle.Top + Layer.AbsoluteRectangle.Height * ScaledRectangle.Top, + Layer.AbsoluteRectangle.Width * ScaledRectangle.Width, + Layer.AbsoluteRectangle.Height * ScaledRectangle.Height ); } @@ -94,31 +74,21 @@ namespace Artemis.Core.Models.Profile.LayerShapes return; } - var x = Layer.Leds.Min(l => l.RgbLed.AbsoluteLedRectangle.Location.X); - var y = Layer.Leds.Min(l => l.RgbLed.AbsoluteLedRectangle.Location.Y); - var width = Layer.Leds.Max(l => l.RgbLed.AbsoluteLedRectangle.Location.X + l.RgbLed.AbsoluteLedRectangle.Size.Width) - x; - var height = Layer.Leds.Max(l => l.RgbLed.AbsoluteLedRectangle.Location.Y + l.RgbLed.AbsoluteLedRectangle.Size.Height) - y; - Layer.AnchorPointProperty.SetCurrentValue(new SKPoint( - (float) (100f / width * (anchor.X - x - Layer.PositionProperty.CurrentValue.X)) / 100f, - (float) (100f / height * (anchor.Y - y - Layer.PositionProperty.CurrentValue.Y)) / 100f + 100f / Layer.AbsoluteRectangle.Width * (anchor.X - Layer.AbsoluteRectangle.Left - Layer.PositionProperty.CurrentValue.X) / 100f, + 100f / Layer.AbsoluteRectangle.Height * (anchor.Y - Layer.AbsoluteRectangle.Top - Layer.PositionProperty.CurrentValue.Y) / 100f ), time); - CalculateRenderProperties(Layer.PositionProperty.CurrentValue, Layer.SizeProperty.CurrentValue); + CalculateRenderProperties(); } public SKPoint GetUnscaledAnchor() { if (!Layer.Leds.Any()) return SKPoint.Empty; - - var x = Layer.Leds.Min(l => l.RgbLed.AbsoluteLedRectangle.Location.X); - var y = Layer.Leds.Min(l => l.RgbLed.AbsoluteLedRectangle.Location.Y); - var width = Layer.Leds.Max(l => l.RgbLed.AbsoluteLedRectangle.Location.X + l.RgbLed.AbsoluteLedRectangle.Size.Width) - x; - var height = Layer.Leds.Max(l => l.RgbLed.AbsoluteLedRectangle.Location.Y + l.RgbLed.AbsoluteLedRectangle.Size.Height) - y; - + return new SKPoint( - (float) (x + width * (Layer.AnchorPointProperty.CurrentValue.X + Layer.PositionProperty.CurrentValue.X)), - (float) (y + height * (Layer.AnchorPointProperty.CurrentValue.Y + Layer.PositionProperty.CurrentValue.Y)) + Layer.AbsoluteRectangle.Left + Layer.AbsoluteRectangle.Width * (Layer.AnchorPointProperty.CurrentValue.X + Layer.PositionProperty.CurrentValue.X), + Layer.AbsoluteRectangle.Top + Layer.AbsoluteRectangle.Height * (Layer.AnchorPointProperty.CurrentValue.Y + Layer.PositionProperty.CurrentValue.Y) ); } } diff --git a/src/Artemis.Core/Models/Profile/LayerShapes/Polygon.cs b/src/Artemis.Core/Models/Profile/LayerShapes/Polygon.cs index f8d432ea8..f5248c80a 100644 --- a/src/Artemis.Core/Models/Profile/LayerShapes/Polygon.cs +++ b/src/Artemis.Core/Models/Profile/LayerShapes/Polygon.cs @@ -23,9 +23,19 @@ namespace Artemis.Core.Models.Profile.LayerShapes /// /// The points of this polygon where they need to be rendered inside the layer /// - public List RenderPoints => Points.Select(p => new SKPoint(p.X * Layer.AbsoluteRectangle.Width, p.Y * Layer.AbsoluteRectangle.Height)).ToList(); + public List RenderPoints + { + get + { + var x = Layer.Leds.Min(l => l.RgbLed.AbsoluteLedRectangle.Location.X); + var y = Layer.Leds.Min(l => l.RgbLed.AbsoluteLedRectangle.Location.Y); + var width = Layer.Leds.Max(l => l.RgbLed.AbsoluteLedRectangle.Location.X + l.RgbLed.AbsoluteLedRectangle.Size.Width) - x; + var height = Layer.Leds.Max(l => l.RgbLed.AbsoluteLedRectangle.Location.Y + l.RgbLed.AbsoluteLedRectangle.Size.Height) - y; + return Points.Select(p => new SKPoint((float) (p.X * width), (float) (p.Y * height))).ToList(); + } + } - public override void CalculateRenderProperties(SKPoint shapePosition, SKSize shapeSize) + public override void CalculateRenderProperties() { var path = new SKPath(); path.AddPoly(RenderPoints.ToArray()); diff --git a/src/Artemis.Core/Models/Profile/LayerShapes/Rectangle.cs b/src/Artemis.Core/Models/Profile/LayerShapes/Rectangle.cs index adffbccbb..17c18eca4 100644 --- a/src/Artemis.Core/Models/Profile/LayerShapes/Rectangle.cs +++ b/src/Artemis.Core/Models/Profile/LayerShapes/Rectangle.cs @@ -13,17 +13,13 @@ namespace Artemis.Core.Models.Profile.LayerShapes { } - public override void CalculateRenderProperties(SKPoint shapePosition, SKSize shapeSize) + public override void CalculateRenderProperties() { - var width = Layer.AbsoluteRectangle.Width; - var height = Layer.AbsoluteRectangle.Height; - var rect = SKRect.Create(shapePosition.X * width, shapePosition.Y * height, shapeSize.Width * width, shapeSize.Height * height); - var path = new SKPath(); - path.AddRect(rect); - path.Transform(SKMatrix.MakeTranslation(Layer.Rectangle.Left, Layer.Rectangle.Top)); + RenderRectangle = GetUnscaledRectangle(); + var path = new SKPath(); + path.AddRect(RenderRectangle); RenderPath = path; - RenderRectangle = path.GetRect(); } public override void ApplyToEntity() diff --git a/src/Artemis.Storage/Entities/Profile/ShapeEntity.cs b/src/Artemis.Storage/Entities/Profile/ShapeEntity.cs index fc415ab80..e8d07ca68 100644 --- a/src/Artemis.Storage/Entities/Profile/ShapeEntity.cs +++ b/src/Artemis.Storage/Entities/Profile/ShapeEntity.cs @@ -4,6 +4,10 @@ namespace Artemis.Storage.Entities.Profile { public class ShapeEntity { + public float X { get; set; } + public float Y { get; set; } + public float Width { get; set; } + public float Height { get; set; } public ShapeEntityType Type { get; set; } public List Points { get; set; } } diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj index 396eb369e..481cff2a0 100644 --- a/src/Artemis.UI/Artemis.UI.csproj +++ b/src/Artemis.UI/Artemis.UI.csproj @@ -222,6 +222,7 @@ + diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileLayerViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileLayerViewModel.cs index 4e0d2c958..746067ad3 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileLayerViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileLayerViewModel.cs @@ -5,9 +5,13 @@ using System.Windows.Media; using Artemis.Core.Models.Profile; using Artemis.Core.Models.Profile.LayerShapes; using Artemis.Core.Models.Surface; +using Artemis.Core.Services; using Artemis.UI.Extensions; +using Artemis.UI.Services; using Artemis.UI.Services.Interfaces; using RGB.NET.Core; +using SkiaSharp.Views.WPF; +using Stylet; using Rectangle = Artemis.Core.Models.Profile.LayerShapes.Rectangle; namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization @@ -15,10 +19,12 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization public class ProfileLayerViewModel : CanvasViewModel { private readonly IProfileEditorService _profileEditorService; + private readonly ILayerEditorService _layerEditorService; - public ProfileLayerViewModel(Layer layer, IProfileEditorService profileEditorService) + public ProfileLayerViewModel(Layer layer, IProfileEditorService profileEditorService, ILayerEditorService layerEditorService) { _profileEditorService = profileEditorService; + _layerEditorService = layerEditorService; Layer = layer; Update(); @@ -83,7 +89,6 @@ 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(); @@ -99,29 +104,31 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization return; } - var skRect = Layer.LayerShape.GetUnscaledRectangle(); - var rect = new Rect(skRect.Left, skRect.Top, Math.Max(0, skRect.Width), Math.Max(0, skRect.Height)); - - var shapeGeometry = Geometry.Empty; - switch (Layer.LayerShape) + Execute.PostToUIThread(() => { - case Ellipse _: - shapeGeometry = new EllipseGeometry(rect); - break; - case Fill _: - shapeGeometry = LayerGeometry; - break; - case Polygon _: - // TODO - shapeGeometry = new RectangleGeometry(rect); - break; - case Rectangle _: - shapeGeometry = new RectangleGeometry(rect); - break; - } - - shapeGeometry.Freeze(); - ShapeGeometry = shapeGeometry; + var rect = _layerEditorService.GetShapeRenderRect(Layer.LayerShape); + var shapeGeometry = Geometry.Empty; + switch (Layer.LayerShape) + { + case Ellipse _: + shapeGeometry = new EllipseGeometry(rect); + break; + case Fill _: + shapeGeometry = LayerGeometry; + break; + case Polygon _: + // TODO + shapeGeometry = new RectangleGeometry(rect); + break; + case Rectangle _: + shapeGeometry = new RectangleGeometry(rect); + break; + } + + shapeGeometry.Transform = _layerEditorService.GetLayerTransformGroup(Layer); + shapeGeometry.Freeze(); + ShapeGeometry = shapeGeometry; + }); } private void CreateViewportRectangle() diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileViewModel.cs index 16c62b899..7a6e16806 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileViewModel.cs @@ -14,6 +14,7 @@ using Artemis.UI.Events; using Artemis.UI.Ninject.Factories; using Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools; using Artemis.UI.Screens.Shared; +using Artemis.UI.Services; using Artemis.UI.Services.Interfaces; using RGB.NET.Core; using Stylet; @@ -23,6 +24,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization public class ProfileViewModel : ProfileEditorPanelViewModel, IHandle, IHandle { private readonly IProfileEditorService _profileEditorService; + private readonly ILayerEditorService _layerEditorService; private readonly IProfileLayerViewModelFactory _profileLayerViewModelFactory; private readonly ISettingsService _settingsService; private readonly ISurfaceService _surfaceService; @@ -32,12 +34,14 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization private TimerUpdateTrigger _updateTrigger; public ProfileViewModel(IProfileEditorService profileEditorService, + ILayerEditorService layerEditorService, ISurfaceService surfaceService, ISettingsService settingsService, IEventAggregator eventAggregator, IProfileLayerViewModelFactory profileLayerViewModelFactory) { _profileEditorService = profileEditorService; + _layerEditorService = layerEditorService; _surfaceService = surfaceService; _settingsService = settingsService; _profileLayerViewModelFactory = profileLayerViewModelFactory; @@ -46,7 +50,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization { CanvasViewModels = new ObservableCollection(); DeviceViewModels = new ObservableCollection(); - PanZoomViewModel = new PanZoomViewModel(); + PanZoomViewModel = new PanZoomViewModel {LimitToZero = false}; }); ApplySurfaceConfiguration(surfaceService.ActiveSurface); @@ -246,16 +250,17 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization private void ActivateToolByIndex(int value) { + // Consider using DI if dependencies start to add up switch (value) { case 0: ActiveToolViewModel = new ViewpointMoveToolViewModel(this, _profileEditorService); break; case 1: - ActiveToolViewModel = new EditToolViewModel(this, _profileEditorService); + ActiveToolViewModel = new EditToolViewModel(this, _profileEditorService, _layerEditorService); break; case 2: - ActiveToolViewModel = new SelectionToolViewModel(this, _profileEditorService); + ActiveToolViewModel = new SelectionToolViewModel(this, _profileEditorService, _layerEditorService); break; case 3: ActiveToolViewModel = new SelectionAddToolViewModel(this, _profileEditorService); @@ -264,10 +269,10 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization ActiveToolViewModel = new SelectionRemoveToolViewModel(this, _profileEditorService); break; case 6: - ActiveToolViewModel = new EllipseToolViewModel(this, _profileEditorService); + ActiveToolViewModel = new EllipseToolViewModel(this, _profileEditorService, _layerEditorService); break; case 7: - ActiveToolViewModel = new RectangleToolViewModel(this, _profileEditorService); + ActiveToolViewModel = new RectangleToolViewModel(this, _profileEditorService, _layerEditorService); break; case 8: ActiveToolViewModel = new PolygonToolViewModel(this, _profileEditorService); 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 053932f15..9efd421cd 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EditToolView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EditToolView.xaml @@ -10,112 +10,119 @@ d:DesignWidth="800" d:DataContext="{d:DesignInstance {x:Type local:EditToolViewModel}}"> - - + + + + + + - - + + + + + + - - - - - - + + + + - - - - + + + + + + - - - - - - + + + + - - - - + + + + + + - - - - - - + + + + - - - - + + + + + + - - - - - - - - - - - + + + + + + + + + \ 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 df87b7aa9..282b26781 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EditToolViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EditToolViewModel.cs @@ -3,13 +3,17 @@ using System.Windows; using System.Windows.Input; using System.Windows.Media; using Artemis.Core.Models.Profile; +using Artemis.UI.Services; using Artemis.UI.Services.Interfaces; using SkiaSharp; +using SkiaSharp.Views.WPF; +using Stylet; namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools { public class EditToolViewModel : VisualizationToolViewModel { + private readonly ILayerEditorService _layerEditorService; private bool _draggingHorizontally; private bool _draggingVertically; private double _dragOffsetX; @@ -17,8 +21,10 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools private Point _dragStart; private bool _isDragging; - public EditToolViewModel(ProfileViewModel profileViewModel, IProfileEditorService profileEditorService) : base(profileViewModel, profileEditorService) + public EditToolViewModel(ProfileViewModel profileViewModel, IProfileEditorService profileEditorService, ILayerEditorService layerEditorService) + : base(profileViewModel, profileEditorService) { + _layerEditorService = layerEditorService; Cursor = Cursors.Arrow; Update(); @@ -29,6 +35,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools public SKRect ShapeSkRect { get; set; } public SKPoint AnchorSkPoint { get; set; } + public TransformCollection LayerTransformChildren { get; set; } private void Update() { @@ -36,12 +43,14 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools { if (layer.LayerShape != null) { - ShapeSkRect = layer.LayerShape.GetUnscaledRectangle(); + ShapeSkRect = _layerEditorService.GetShapeRenderRect(layer.LayerShape).ToSKRect(); AnchorSkPoint = layer.LayerShape.GetUnscaledAnchor(); + Execute.PostToUIThread(() => LayerTransformChildren = _layerEditorService.GetLayerTransformGroup(layer).Children); } } } + public void ShapeEditMouseDown(object sender, MouseButtonEventArgs e) { if (_isDragging) @@ -51,7 +60,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools if (ProfileEditorService.SelectedProfileElement is Layer layer) { var dragStartPosition = GetRelativePosition(sender, e); - var skRect = layer.LayerShape.GetUnscaledRectangle(); + var skRect = layer.LayerShape.RenderRectangle; _dragOffsetX = skRect.Left - dragStartPosition.X; _dragOffsetY = skRect.Top - dragStartPosition.Y; _dragStart = dragStartPosition; @@ -64,15 +73,29 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools e.Handled = true; } + public void AnchorEditMouseDown(object sender, MouseButtonEventArgs e) + { + if (_isDragging) + return; + + // Use the regular mousedown + ShapeEditMouseDown(sender, e); + // Override the drag offset + var dragStartPosition = GetRelativePosition(sender, e); + _dragOffsetX = AnchorSkPoint.X - dragStartPosition.X; + _dragOffsetY = AnchorSkPoint.Y - dragStartPosition.Y; + _dragStart = dragStartPosition; + } + public void ShapeEditMouseUp(object sender, MouseButtonEventArgs e) { - ((IInputElement) sender).ReleaseMouseCapture(); ProfileEditorService.UpdateSelectedProfileElement(); _isDragging = false; _draggingHorizontally = false; _draggingVertically = false; + ((IInputElement) sender).ReleaseMouseCapture(); e.Handled = true; } @@ -82,7 +105,9 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools return; var position = GetRelativePosition(sender, e); - layer.LayerShape.SetFromUnscaledAnchor(new SKPoint((float) position.X, (float) position.Y), ProfileEditorService.CurrentTime); + var x = (float) (position.X + _dragOffsetX); + var y = (float) (position.Y + _dragOffsetY); + layer.LayerShape.SetFromUnscaledAnchor(new SKPoint(x, y), ProfileEditorService.CurrentTime); ProfileEditorService.UpdateProfilePreview(); } @@ -92,24 +117,25 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools return; var position = GetRelativePosition(sender, e); - var skRect = layer.LayerShape.GetUnscaledRectangle(); + var skRect = layer.LayerShape.RenderRectangle; var x = (float) (position.X + _dragOffsetX); var y = (float) (position.Y + _dragOffsetY); - 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); - } - } + // 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); + // } + // } ProfileEditorService.UpdateProfilePreview(); } @@ -124,7 +150,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools return; var position = GetRelativePosition(sender, e); - var skRect = layer.LayerShape.GetUnscaledRectangle(); + var skRect = layer.LayerShape.RenderRectangle; if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) { // Take the greatest difference @@ -136,8 +162,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools skRect.Left = (float) Math.Min(position.X, skRect.Bottom); } - layer.LayerShape.SetFromUnscaledRectangle(skRect, ProfileEditorService.CurrentTime); - ProfileEditorService.UpdateProfilePreview(); + ApplyShapeResize(skRect); } public void TopCenterResize(object sender, MouseEventArgs e) @@ -147,10 +172,9 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools var position = GetRelativePosition(sender, e); - var skRect = layer.LayerShape.GetUnscaledRectangle(); + var skRect = layer.LayerShape.RenderRectangle; skRect.Top = (float) Math.Min(position.Y, skRect.Bottom); - layer.LayerShape.SetFromUnscaledRectangle(skRect, ProfileEditorService.CurrentTime); - ProfileEditorService.UpdateProfilePreview(); + ApplyShapeResize(skRect); } public void TopRightRotate(object sender, MouseEventArgs e) @@ -163,7 +187,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools return; var position = GetRelativePosition(sender, e); - var skRect = layer.LayerShape.GetUnscaledRectangle(); + var skRect = layer.LayerShape.RenderRectangle; if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) { // Take the greatest difference @@ -175,8 +199,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools skRect.Right = (float) Math.Max(position.X, skRect.Left); } - layer.LayerShape.SetFromUnscaledRectangle(skRect, ProfileEditorService.CurrentTime); - ProfileEditorService.UpdateProfilePreview(); + ApplyShapeResize(skRect); } public void CenterRightResize(object sender, MouseEventArgs e) @@ -186,16 +209,9 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools var position = GetRelativePosition(sender, e); - var skRect = layer.LayerShape.GetUnscaledRectangle(); + var skRect = layer.LayerShape.RenderRectangle; skRect.Right = (float) Math.Max(position.X, skRect.Left); - layer.LayerShape.SetFromUnscaledRectangle(skRect, ProfileEditorService.CurrentTime); - ProfileEditorService.UpdateProfilePreview(); - } - - private Point GetRelativePosition(object sender, MouseEventArgs mouseEventArgs) - { - var parent = VisualTreeHelper.GetParent((DependencyObject) sender); - return mouseEventArgs.GetPosition((IInputElement) parent); + ApplyShapeResize(skRect); } public void BottomRightRotate(object sender, MouseEventArgs e) @@ -208,7 +224,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools return; var position = GetRelativePosition(sender, e); - var skRect = layer.LayerShape.GetUnscaledRectangle(); + var skRect = layer.LayerShape.RenderRectangle; if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) { // Take the greatest difference @@ -220,8 +236,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools skRect.Right = (float) Math.Max(position.X, skRect.Left); } - layer.LayerShape.SetFromUnscaledRectangle(skRect, ProfileEditorService.CurrentTime); - ProfileEditorService.UpdateProfilePreview(); + ApplyShapeResize(skRect); } public void BottomCenterResize(object sender, MouseEventArgs e) @@ -231,10 +246,9 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools var position = GetRelativePosition(sender, e); - var skRect = layer.LayerShape.GetUnscaledRectangle(); + var skRect = layer.LayerShape.RenderRectangle; skRect.Bottom = (float) Math.Max(position.Y, skRect.Top); - layer.LayerShape.SetFromUnscaledRectangle(skRect, ProfileEditorService.CurrentTime); - ProfileEditorService.UpdateProfilePreview(); + ApplyShapeResize(skRect); } public void BottomLeftRotate(object sender, MouseEventArgs e) @@ -247,7 +261,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools return; var position = GetRelativePosition(sender, e); - var skRect = layer.LayerShape.GetUnscaledRectangle(); + var skRect = layer.LayerShape.RenderRectangle; if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) { // Take the greatest difference @@ -259,8 +273,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools skRect.Left = (float) Math.Min(position.X, skRect.Right); } - layer.LayerShape.SetFromUnscaledRectangle(skRect, ProfileEditorService.CurrentTime); - ProfileEditorService.UpdateProfilePreview(); + ApplyShapeResize(skRect); } public void CenterLeftResize(object sender, MouseEventArgs e) @@ -270,9 +283,34 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools var position = GetRelativePosition(sender, e); - var skRect = layer.LayerShape.GetUnscaledRectangle(); + var skRect = layer.LayerShape.RenderRectangle; skRect.Left = (float) Math.Min(position.X, skRect.Right); - layer.LayerShape.SetFromUnscaledRectangle(skRect, ProfileEditorService.CurrentTime); + ApplyShapeResize(skRect); + } + + private Point GetRelativePosition(object sender, MouseEventArgs mouseEventArgs) + { + var parent = VisualTreeHelper.GetParent((DependencyObject) sender); + return mouseEventArgs.GetPosition((IInputElement) parent); + } + + private void ApplyShapeResize(SKRect newRect) + { + if (!(ProfileEditorService.SelectedProfileElement is Layer layer)) + return; + + // TODO: Apply the translation + // Store the original position to create an offset for the anchor + // var original = layer.PositionProperty.CurrentValue; + // layer.LayerShape.SetFromUnscaledRectangle(newRect, ProfileEditorService.CurrentTime); + // var updated = layer.PositionProperty.CurrentValue; + // // Apply the offset to the anchor so it stays in at same spot + // layer.AnchorPointProperty.SetCurrentValue(new SKPoint( + // layer.AnchorPointProperty.CurrentValue.X + (original.X - updated.X), + // layer.AnchorPointProperty.CurrentValue.Y + (original.Y - updated.Y) + // ), ProfileEditorService.CurrentTime); + + // Update the preview ProfileEditorService.UpdateProfilePreview(); } } diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EllipseToolViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EllipseToolViewModel.cs index b5666c6b4..93d1d703b 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EllipseToolViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EllipseToolViewModel.cs @@ -4,6 +4,7 @@ using System.Windows.Input; using Artemis.Core.Models.Profile; using Artemis.Core.Models.Profile.LayerShapes; using Artemis.UI.Properties; +using Artemis.UI.Services; using Artemis.UI.Services.Interfaces; using SkiaSharp.Views.WPF; @@ -11,10 +12,13 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools { public class EllipseToolViewModel : VisualizationToolViewModel { + private readonly ILayerEditorService _layerEditorService; private bool _shiftDown; - public EllipseToolViewModel(ProfileViewModel profileViewModel, IProfileEditorService profileEditorService) : base(profileViewModel, profileEditorService) + public EllipseToolViewModel(ProfileViewModel profileViewModel, IProfileEditorService profileEditorService, ILayerEditorService layerEditorService) + : base(profileViewModel, profileEditorService) { + _layerEditorService = layerEditorService; using (var stream = new MemoryStream(Resources.aero_crosshair)) { Cursor = new Cursor(stream); @@ -48,7 +52,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools layer.LayerShape = new Ellipse(layer); // Apply the drag rectangle - layer.LayerShape.SetFromUnscaledRectangle(DragRectangle.ToSKRect(), ProfileEditorService.CurrentTime); + _layerEditorService.SetShapeRenderRect(layer.LayerShape, DragRectangle); ProfileEditorService.UpdateSelectedProfileElement(); } } diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/RectangleToolViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/RectangleToolViewModel.cs index 84682ccaf..fc5c43589 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/RectangleToolViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/RectangleToolViewModel.cs @@ -4,6 +4,7 @@ using System.Windows.Input; using Artemis.Core.Models.Profile; using Artemis.Core.Models.Profile.LayerShapes; using Artemis.UI.Properties; +using Artemis.UI.Services; using Artemis.UI.Services.Interfaces; using SkiaSharp.Views.WPF; @@ -11,10 +12,13 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools { public class RectangleToolViewModel : VisualizationToolViewModel { + private readonly ILayerEditorService _layerEditorService; private bool _shiftDown; - public RectangleToolViewModel(ProfileViewModel profileViewModel, IProfileEditorService profileEditorService) : base(profileViewModel, profileEditorService) + public RectangleToolViewModel(ProfileViewModel profileViewModel, IProfileEditorService profileEditorService, ILayerEditorService layerEditorService) + : base(profileViewModel, profileEditorService) { + _layerEditorService = layerEditorService; using (var stream = new MemoryStream(Resources.aero_crosshair)) { Cursor = new Cursor(stream); @@ -48,7 +52,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools layer.LayerShape = new Rectangle(layer); // Apply the drag rectangle - layer.LayerShape.SetFromUnscaledRectangle(DragRectangle.ToSKRect(), ProfileEditorService.CurrentTime); + _layerEditorService.SetShapeRenderRect(layer.LayerShape, DragRectangle); ProfileEditorService.UpdateSelectedProfileElement(); } } diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/SelectionToolViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/SelectionToolViewModel.cs index fe2d1bf5a..81e2026fa 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/SelectionToolViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/SelectionToolViewModel.cs @@ -6,6 +6,7 @@ using Artemis.Core.Models.Profile; using Artemis.Core.Models.Surface; using Artemis.UI.Extensions; using Artemis.UI.Properties; +using Artemis.UI.Services; using Artemis.UI.Services.Interfaces; using SkiaSharp; @@ -13,8 +14,12 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools { public class SelectionToolViewModel : VisualizationToolViewModel { - public SelectionToolViewModel(ProfileViewModel profileViewModel, IProfileEditorService profileEditorService) : base(profileViewModel, profileEditorService) + private readonly ILayerEditorService _layerEditorService; + + public SelectionToolViewModel(ProfileViewModel profileViewModel, IProfileEditorService profileEditorService, ILayerEditorService layerEditorService) + : base(profileViewModel, profileEditorService) { + _layerEditorService = layerEditorService; using (var stream = new MemoryStream(Resources.aero_crosshair)) { Cursor = new Cursor(stream); @@ -48,14 +53,14 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools if (ProfileEditorService.SelectedProfileElement is Layer layer) { // If the layer has a shape, save it's size - var shapeSize = SKRect.Empty; + var shapeSize = Rect.Empty; if (layer.LayerShape != null) - shapeSize = layer.LayerShape.GetUnscaledRectangle(); + shapeSize = _layerEditorService.GetShapeRenderRect(layer.LayerShape); layer.ClearLeds(); layer.AddLeds(selectedLeds); // Restore the saved size if (layer.LayerShape != null) - layer.LayerShape.SetFromUnscaledRectangle(shapeSize, ProfileEditorService.CurrentTime); + _layerEditorService.SetShapeRenderRect(layer.LayerShape, shapeSize); ProfileEditorService.UpdateSelectedProfileElement(); } diff --git a/src/Artemis.UI/Screens/Shared/PanZoomViewModel.cs b/src/Artemis.UI/Screens/Shared/PanZoomViewModel.cs index 727b815ff..45cc0ee60 100644 --- a/src/Artemis.UI/Screens/Shared/PanZoomViewModel.cs +++ b/src/Artemis.UI/Screens/Shared/PanZoomViewModel.cs @@ -25,6 +25,7 @@ namespace Artemis.UI.Screens.Shared public double PanY { get; set; } public double CanvasWidth { get; set; } public double CanvasHeight { get; set; } + public bool LimitToZero { get; set; } public Rect BackgroundViewport => new Rect(PanX, PanY, 20, 20); @@ -61,8 +62,16 @@ namespace Artemis.UI.Screens.Shared var position = e.GetPosition((IInputElement) sender); var delta = LastPanPosition - position; - PanX = Math.Min(0, PanX - delta.Value.X); - PanY = Math.Min(0, PanY - delta.Value.Y); + if (LimitToZero) + { + PanX = Math.Min(0, PanX - delta.Value.X); + PanY = Math.Min(0, PanY - delta.Value.Y); + } + else + { + PanX -= delta.Value.X; + PanY -= delta.Value.Y; + } LastPanPosition = position; } diff --git a/src/Artemis.UI/Services/LayerShapeService.cs b/src/Artemis.UI/Services/LayerShapeService.cs new file mode 100644 index 000000000..af7f00d81 --- /dev/null +++ b/src/Artemis.UI/Services/LayerShapeService.cs @@ -0,0 +1,116 @@ +using System; +using System.Linq; +using System.Windows; +using System.Windows.Media; +using Artemis.Core.Models.Profile; +using Artemis.Core.Models.Profile.LayerShapes; +using Artemis.Core.Services; +using Artemis.UI.Services.Interfaces; +using SkiaSharp; +using SkiaSharp.Views.WPF; + +namespace Artemis.UI.Services +{ + public class LayerEditorService : ILayerEditorService + { + private readonly ISettingsService _settingsService; + + public LayerEditorService(ISettingsService settingsService) + { + _settingsService = settingsService; + } + + /// + 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 + var renderScale = _settingsService.GetSetting("Core.RenderScale", 1.0).Value; + return new Rect( + layer.AbsoluteRectangle.Left / renderScale * 1, + layer.AbsoluteRectangle.Top / renderScale * 1, + Math.Max(0, layer.AbsoluteRectangle.Width / renderScale * 1), + Math.Max(0, layer.AbsoluteRectangle.Height / renderScale * 1) + ); + } + + /// + public void SetShapeRenderRect(LayerShape layerShape, Rect rect) + { + if (!layerShape.Layer.Leds.Any()) + { + layerShape.ScaledRectangle = SKRect.Empty; + return; + } + + // Adjust the provided rect for the difference in render scale + var renderScale = _settingsService.GetSetting("Core.RenderScale", 1.0).Value; + layerShape.ScaledRectangle = SKRect.Create( + 100f / layerShape.Layer.AbsoluteRectangle.Width * ((float) (rect.Left * renderScale) - layerShape.Layer.AbsoluteRectangle.Left) / 100f, + 100f / layerShape.Layer.AbsoluteRectangle.Height * ((float) (rect.Top * renderScale) - layerShape.Layer.AbsoluteRectangle.Top) / 100f, + 100f / layerShape.Layer.AbsoluteRectangle.Width * (float) (rect.Width * renderScale) / 100f, + 100f / layerShape.Layer.AbsoluteRectangle.Height * (float) (rect.Height * renderScale) / 100f + ); + layerShape.CalculateRenderProperties(); + } + + /// + public TransformGroup GetLayerTransformGroup(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 = 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; + } + } + + public interface ILayerEditorService : IArtemisUIService + { + /// + /// Returns an absolute and scaled rectangle for the given shape that is corrected for the current render scale. + /// + /// + Rect GetShapeRenderRect(LayerShape layerShape); + + /// + /// Sets the render rectangle of the given shape to match the provided unscaled rectangle. The rectangle is corrected + /// for the current render scale. + /// + /// + /// + 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. + /// + /// + /// + TransformGroup GetLayerTransformGroup(Layer layer); + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Services/ProfileEditorService.cs b/src/Artemis.UI/Services/ProfileEditorService.cs index 211fd56bf..486ba8989 100644 --- a/src/Artemis.UI/Services/ProfileEditorService.cs +++ b/src/Artemis.UI/Services/ProfileEditorService.cs @@ -74,7 +74,7 @@ namespace Artemis.UI.Services baseLayerProperty.KeyframeEngine?.OverrideProgress(CurrentTime); // Force layer shape to redraw - layer.LayerShape?.CalculateRenderProperties(layer.PositionProperty.CurrentValue, layer.SizeProperty.CurrentValue); + layer.LayerShape?.CalculateRenderProperties(); // Update the brush with the delta (which can now be negative ^^) layer.Update(delta.TotalSeconds); }