From fabb3855561a2c1656c04644ca1a7464edb0f4b2 Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Sun, 26 Jan 2020 11:35:39 +0100 Subject: [PATCH] Implemented profile default loading and rendering without the editor Changed edit tool rotation to allow rotation past 360 degrees --- .../Profile/KeyframeEngines/KeyframeEngine.cs | 6 +-- src/Artemis.Core/Models/Profile/Layer.cs | 9 ++-- .../LayerProperties/BaseLayerProperty.cs | 2 + .../Plugins/Abstract/ProfileModule.cs | 5 ++ src/Artemis.Core/Services/CoreService.cs | 31 ++++++++---- .../Services/Interfaces/ICoreService.cs | 13 ++++- .../Storage/Interfaces/IProfileService.cs | 1 + .../Services/Storage/ProfileService.cs | 15 +++++- .../PropertyInput/PropertyInputViewModel.cs | 1 + .../ProfileEditor/ProfileEditorViewModel.cs | 2 + .../Visualization/Tools/EditToolViewModel.cs | 48 ++++++++++++------- .../Interfaces/IProfileEditorService.cs | 3 ++ .../Services/ProfileEditorService.cs | 21 ++++++-- 13 files changed, 116 insertions(+), 41 deletions(-) diff --git a/src/Artemis.Core/Models/Profile/KeyframeEngines/KeyframeEngine.cs b/src/Artemis.Core/Models/Profile/KeyframeEngines/KeyframeEngine.cs index ab6b93532..a48a74457 100644 --- a/src/Artemis.Core/Models/Profile/KeyframeEngines/KeyframeEngine.cs +++ b/src/Artemis.Core/Models/Profile/KeyframeEngines/KeyframeEngine.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using Artemis.Core.Exceptions; using Artemis.Core.Models.Profile.LayerProperties; @@ -77,8 +78,7 @@ namespace Artemis.Core.Models.Profile.KeyframeEngines return; var keyframes = LayerProperty.UntypedKeyframes.ToList(); - Progress = Progress.Add(TimeSpan.FromMilliseconds(deltaTime)); - + Progress = Progress.Add(TimeSpan.FromSeconds(deltaTime)); // The current keyframe is the last keyframe before the current time CurrentKeyframe = keyframes.LastOrDefault(k => k.Position <= Progress); // The next keyframe is the first keyframe that's after the current time @@ -112,7 +112,7 @@ namespace Artemis.Core.Models.Profile.KeyframeEngines public void OverrideProgress(TimeSpan progress) { Progress = TimeSpan.Zero; - Update(progress.TotalMilliseconds); + Update(progress.TotalSeconds); } /// diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs index 36a6c7535..a5c323295 100644 --- a/src/Artemis.Core/Models/Profile/Layer.cs +++ b/src/Artemis.Core/Models/Profile/Layer.cs @@ -135,7 +135,7 @@ namespace Artemis.Core.Models.Profile /// /// The rotation property of this layer range 0 - 360, also found in /// - public LayerProperty RotationProperty { get; private set; } + public LayerProperty RotationProperty { get; private set; } /// /// The opacity property of this layer range 0 - 100, also found in @@ -153,11 +153,8 @@ namespace Artemis.Core.Models.Profile { property.KeyframeEngine?.Update(deltaTime); // This is a placeholder method of repeating the animation until repeat modes are implemented - if (property.KeyframeEngine != null && property.IsUsingKeyframes && property.KeyframeEngine.NextKeyframe == null) - { - Debug.WriteLine($"Reset {property} progress"); + if (property.KeyframeEngine != null && property.IsUsingKeyframes && property.KeyframeEngine.NextKeyframe == null) property.KeyframeEngine.OverrideProgress(TimeSpan.Zero); - } } LayerBrush?.Update(deltaTime); @@ -421,7 +418,7 @@ namespace Artemis.Core.Models.Profile AnchorPointProperty = new LayerProperty(this, transformProperty, "Core.AnchorPoint", "Anchor Point", "The point at which the shape is attached to its position."); PositionProperty = new LayerProperty(this, transformProperty, "Core.Position", "Position", "The position of the shape."); SizeProperty = new LayerProperty(this, transformProperty, "Core.Size", "Size", "The size of the shape.") {InputAffix = "%"}; - RotationProperty = new LayerProperty(this, transformProperty, "Core.Rotation", "Rotation", "The rotation of the shape in degrees.") {InputAffix = "°"}; + RotationProperty = new LayerProperty(this, transformProperty, "Core.Rotation", "Rotation", "The rotation of the shape in degrees.") {InputAffix = "°"}; OpacityProperty = new LayerProperty(this, transformProperty, "Core.Opacity", "Opacity", "The opacity of the shape.") {InputAffix = "%"}; transformProperty.Children.Add(AnchorPointProperty); transformProperty.Children.Add(PositionProperty); diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/BaseLayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/BaseLayerProperty.cs index ed8906600..be69fd077 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/BaseLayerProperty.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/BaseLayerProperty.cs @@ -222,6 +222,8 @@ namespace Artemis.Core.Models.Profile.LayerProperties currentKeyframe.BaseValue = value; } + + OnValueChanged(); } /// diff --git a/src/Artemis.Core/Plugins/Abstract/ProfileModule.cs b/src/Artemis.Core/Plugins/Abstract/ProfileModule.cs index 7fd760828..ff72cdde9 100644 --- a/src/Artemis.Core/Plugins/Abstract/ProfileModule.cs +++ b/src/Artemis.Core/Plugins/Abstract/ProfileModule.cs @@ -13,6 +13,11 @@ namespace Artemis.Core.Plugins.Abstract { } + public override void EnablePlugin() + { + throw new NotImplementedException(); + } + public Profile ActiveProfile { get; private set; } /// diff --git a/src/Artemis.Core/Services/CoreService.cs b/src/Artemis.Core/Services/CoreService.cs index 6fc200143..67075912c 100644 --- a/src/Artemis.Core/Services/CoreService.cs +++ b/src/Artemis.Core/Services/CoreService.cs @@ -23,17 +23,19 @@ namespace Artemis.Core.Services private readonly IPluginService _pluginService; private readonly IRgbService _rgbService; private readonly ISurfaceService _surfaceService; + private readonly IProfileService _profileService; private List _modules; - internal CoreService(ILogger logger, IPluginService pluginService, IRgbService rgbService, ISurfaceService surfaceService) + internal CoreService(ILogger logger, IPluginService pluginService, IRgbService rgbService, ISurfaceService surfaceService, IProfileService profileService) { _logger = logger; _pluginService = pluginService; _rgbService = rgbService; _surfaceService = surfaceService; + _profileService = profileService; _rgbService.Surface.Updating += SurfaceOnUpdating; _rgbService.Surface.Updated += SurfaceOnUpdated; - + _modules = _pluginService.GetPluginsOfType(); _pluginService.PluginEnabled += (sender, args) => _modules = _pluginService.GetPluginsOfType(); _pluginService.PluginDisabled += (sender, args) => _modules = _pluginService.GetPluginsOfType(); @@ -42,6 +44,9 @@ namespace Artemis.Core.Services Task.Run(Initialize); } + public bool ModuleUpdatingDisabled { get; set; } + public bool ModuleRenderingDisabled { get; set; } + public void Dispose() { // Dispose services @@ -75,6 +80,8 @@ namespace Artemis.Core.Services else _logger.Information("Initialized without an active surface entity"); + await Task.Run(() => _profileService.ActivateDefaultProfiles()); + OnInitialized(); } @@ -82,11 +89,14 @@ namespace Artemis.Core.Services { try { - lock (_modules) + if (!ModuleUpdatingDisabled) { - // Update all active modules - foreach (var module in _modules) - module.Update(args.DeltaTime); + lock (_modules) + { + // Update all active modules + foreach (var module in _modules) + module.Update(args.DeltaTime); + } } // If there is no ready bitmap brush, skip the frame @@ -102,10 +112,13 @@ namespace Artemis.Core.Services using (var canvas = new SKCanvas(_rgbService.BitmapBrush.Bitmap)) { canvas.Clear(new SKColor(0, 0, 0)); - lock (_modules) + if (!ModuleRenderingDisabled) { - foreach (var module in _modules) - module.Render(args.DeltaTime, _surfaceService.ActiveSurface, canvas); + lock (_modules) + { + foreach (var module in _modules) + module.Render(args.DeltaTime, _surfaceService.ActiveSurface, canvas); + } } OnFrameRendering(new FrameRenderingEventArgs(_modules, canvas, args.DeltaTime, _rgbService.Surface)); diff --git a/src/Artemis.Core/Services/Interfaces/ICoreService.cs b/src/Artemis.Core/Services/Interfaces/ICoreService.cs index 2aced5183..e3a3d712e 100644 --- a/src/Artemis.Core/Services/Interfaces/ICoreService.cs +++ b/src/Artemis.Core/Services/Interfaces/ICoreService.cs @@ -6,9 +6,18 @@ namespace Artemis.Core.Services.Interfaces public interface ICoreService : IArtemisService, IDisposable { /// - /// Indicates wether or not the core has been initialized + /// Gets whether the or not the core has been initialized /// - bool IsInitialized { get; set; } + bool IsInitialized { get; } + + /// + /// Gets or sets whether modules are updated each frame by calling their Update method + /// + bool ModuleUpdatingDisabled { get; set; } + /// + /// Gets or sets whether modules are rendered each frame by calling their Render method + /// + bool ModuleRenderingDisabled { get; set; } /// /// Occurs the core has finished initializing diff --git a/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs b/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs index d9139cd9e..30b2b12bf 100644 --- a/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs +++ b/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs @@ -7,6 +7,7 @@ namespace Artemis.Core.Services.Storage.Interfaces { public interface IProfileService : IArtemisService { + void ActivateDefaultProfiles(); Profile CreateProfile(ProfileModule module, string name); List GetProfiles(ProfileModule module); Profile GetActiveProfile(ProfileModule module); diff --git a/src/Artemis.Core/Services/Storage/ProfileService.cs b/src/Artemis.Core/Services/Storage/ProfileService.cs index 86863dd60..11ef0a265 100644 --- a/src/Artemis.Core/Services/Storage/ProfileService.cs +++ b/src/Artemis.Core/Services/Storage/ProfileService.cs @@ -14,7 +14,7 @@ using Newtonsoft.Json; namespace Artemis.Core.Services.Storage { /// - /// Provides access to profile storage + /// Provides access to profile storage and is responsible for activating default profiles /// public class ProfileService : IProfileService { @@ -35,6 +35,14 @@ namespace Artemis.Core.Services.Storage _pluginService.PluginLoaded += OnPluginLoaded; } + public void ActivateDefaultProfiles() + { + foreach (var profileModule in _pluginService.GetPluginsOfType()) + { + var activeProfile = GetActiveProfile(profileModule); + ActivateProfile(profileModule, activeProfile); + } + } public List GetProfiles(ProfileModule module) { @@ -204,6 +212,11 @@ namespace Artemis.Core.Services.Storage ActiveProfilesInstantiateProfileLayerBrushes(); ActiveProfilesInstantiateKeyframeEngines(); } + else if (e.PluginInfo.Instance is ProfileModule profileModule) + { + var activeProfile = GetActiveProfile(profileModule); + ActivateProfile(profileModule, activeProfile); + } } #endregion diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/PropertyInputViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/PropertyInputViewModel.cs index 3500ec06f..6c882f827 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/PropertyInputViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/PropertyInputViewModel.cs @@ -34,6 +34,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P throw new ArtemisUIException($"This input VM does not support the provided type {layerPropertyViewModel.LayerProperty.Type.Name}"); LayerPropertyViewModel = layerPropertyViewModel; + layerPropertyViewModel.LayerProperty.ValueChanged += (sender, args) => Update(); Update(); Initialized = true; diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileEditorViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileEditorViewModel.cs index 59440501c..0e00b26bf 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileEditorViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/ProfileEditorViewModel.cs @@ -146,6 +146,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor protected override void OnActivate() { LoadWorkspaceSettings(); + _profileEditorService.StopRegularRender(); Task.Run(LoadProfiles); base.OnActivate(); } @@ -153,6 +154,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor protected override void OnDeactivate() { SaveWorkspaceSettings(); + _profileEditorService.ResumeRegularRender(); base.OnDeactivate(); } 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 45c821fcc..1723f3b14 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EditToolViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/Tools/EditToolViewModel.cs @@ -20,8 +20,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools private SKPoint _dragOffset; private SKPoint _dragStart; private SKPoint _dragStartAnchor; - private double _dragStartAngle; - private double _dragAngleOffset; + private float _previousDragAngle; private SKSize _dragStartScale; private bool _isDragging; @@ -111,8 +110,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools // The path starts at 0,0 so there's no simple way to get the position relative to the top-left of the path _dragStart = GetRelativePosition(sender, e).ToSKPoint(); _dragStartScale = layer.SizeProperty.CurrentValue; - _dragStartAngle = layer.RotationProperty.CurrentValue; - _dragAngleOffset = _dragStartAngle - CalculateAngle(_layerEditorService.GetLayerAnchor(layer, true), _dragStart); + _previousDragAngle = CalculateAngle(_layerEditorService.GetLayerAnchor(layer, true), _dragStart); + // Store the original position and do a test to figure out the mouse offset var originalPosition = layer.PositionProperty.CurrentValue; var scaledDragStart = _layerEditorService.GetScaledPoint(layer, _dragStart, true); @@ -358,15 +357,30 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools { if (!_isDragging || !(ProfileEditorService.SelectedProfileElement is Layer layer)) return; - - var newRotation = CalculateAngle(_layerEditorService.GetLayerAnchor(layer, true), GetRelativePosition(sender, e).ToSKPoint()) + _dragAngleOffset; - if (newRotation < 0) - newRotation = 360 + newRotation; - - var current = layer.RotationProperty.CurrentValue; - layer.RotationProperty.SetCurrentValue((int) Math.Round(newRotation, MidpointRounding.AwayFromZero), ProfileEditorService.CurrentTime); - Debug.WriteLine($"New rotation: {layer.RotationProperty.CurrentValue} Difference: {(layer.RotationProperty.CurrentValue - current)}"); + var previousDragAngle = _previousDragAngle; + var newRotation = CalculateAngle(_layerEditorService.GetLayerAnchor(layer, true), GetRelativePosition(sender, e).ToSKPoint()); + _previousDragAngle = newRotation; + + // Allow the user to rotate the shape in increments of 5 + if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) + newRotation = (float)Math.Round(newRotation / 5f) * 5f; + + var difference = newRotation - previousDragAngle; + if (difference < -350) + difference += 360; + else if (difference > 350) + difference -= 360; + newRotation = layer.RotationProperty.CurrentValue + difference; + + // Round the end-result to increments of 5 as well, to avoid staying on an offset + if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) + newRotation = (float)Math.Round(newRotation / 5f) * 5f; + else + newRotation = (float)Math.Round(newRotation, 2, MidpointRounding.AwayFromZero); + + Debug.WriteLine(newRotation); + layer.RotationProperty.SetCurrentValue(newRotation, ProfileEditorService.CurrentTime); ProfileEditorService.UpdateProfilePreview(); } @@ -459,12 +473,12 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization.Tools return Math.Max(0.001f, _dragStartScale.Height + scaleToAdd); } - private double CalculateAngle(SKPoint start, SKPoint arrival) + private float CalculateAngle(SKPoint start, SKPoint arrival) { - var radian = Math.Atan2(start.Y - arrival.Y, start.X - arrival.X); - var angle = radian * (180 / Math.PI); - if (angle < 0.0) - angle += 360.0; + var radian = (float) Math.Atan2(start.Y - arrival.Y, start.X - arrival.X); + var angle = radian * (180f / (float) Math.PI); + if (angle < 0f) + angle += 360f; return angle; } diff --git a/src/Artemis.UI/Services/Interfaces/IProfileEditorService.cs b/src/Artemis.UI/Services/Interfaces/IProfileEditorService.cs index 3463c72c8..2a7fb99b0 100644 --- a/src/Artemis.UI/Services/Interfaces/IProfileEditorService.cs +++ b/src/Artemis.UI/Services/Interfaces/IProfileEditorService.cs @@ -47,5 +47,8 @@ namespace Artemis.UI.Services.Interfaces /// Occurs when the profile preview has been updated /// event EventHandler ProfilePreviewUpdated; + + void StopRegularRender(); + void ResumeRegularRender(); } } \ No newline at end of file diff --git a/src/Artemis.UI/Services/ProfileEditorService.cs b/src/Artemis.UI/Services/ProfileEditorService.cs index 486ba8989..c3bb8bc0d 100644 --- a/src/Artemis.UI/Services/ProfileEditorService.cs +++ b/src/Artemis.UI/Services/ProfileEditorService.cs @@ -2,6 +2,7 @@ using System.Linq; using Artemis.Core.Models.Profile; using Artemis.Core.Plugins.Abstract; +using Artemis.Core.Services.Interfaces; using Artemis.Core.Services.Storage.Interfaces; using Artemis.UI.Services.Interfaces; @@ -9,12 +10,14 @@ namespace Artemis.UI.Services { public class ProfileEditorService : IProfileEditorService { + private readonly ICoreService _coreService; private readonly IProfileService _profileService; private TimeSpan _currentTime; private TimeSpan _lastUpdateTime; - public ProfileEditorService(IProfileService profileService) + public ProfileEditorService(ICoreService coreService, IProfileService profileService) { + _coreService = coreService; _profileService = profileService; } @@ -75,8 +78,10 @@ namespace Artemis.UI.Services // Force layer shape to redraw layer.LayerShape?.CalculateRenderProperties(); - // Update the brush with the delta (which can now be negative ^^) - layer.Update(delta.TotalSeconds); + // Manually update the layer's engine and brush + foreach (var property in layer.Properties) + property.KeyframeEngine?.Update(delta.TotalSeconds); + layer.LayerBrush?.Update(delta.TotalSeconds); } _lastUpdateTime = CurrentTime; @@ -122,6 +127,16 @@ namespace Artemis.UI.Services public event EventHandler CurrentTimeChanged; public event EventHandler ProfilePreviewUpdated; + public void StopRegularRender() + { + _coreService.ModuleUpdatingDisabled = true; + } + + public void ResumeRegularRender() + { + _coreService.ModuleUpdatingDisabled = false; + } + protected virtual void OnSelectedProfileElementUpdated() { SelectedProfileElementUpdated?.Invoke(this, EventArgs.Empty);