diff --git a/src/Artemis.Core/Artemis.Core.csproj b/src/Artemis.Core/Artemis.Core.csproj index afb0d5028..7ab16b561 100644 --- a/src/Artemis.Core/Artemis.Core.csproj +++ b/src/Artemis.Core/Artemis.Core.csproj @@ -70,4 +70,9 @@ ..\..\..\RGB.NET\bin\netstandard2.0\RGB.NET.Groups.dll + + + PreserveNewest + + \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/Folder.cs b/src/Artemis.Core/Models/Profile/Folder.cs index 53e25ccf8..0eefa1da7 100644 --- a/src/Artemis.Core/Models/Profile/Folder.cs +++ b/src/Artemis.Core/Models/Profile/Folder.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using Artemis.Core.Plugins.LayerEffect.Abstract; +using Artemis.Core.Utilities; using Artemis.Storage.Entities.Profile; using Artemis.Storage.Entities.Profile.Abstract; using SkiaSharp; @@ -61,9 +62,13 @@ namespace Artemis.Core.Models.Profile internal FolderEntity FolderEntity { get; set; } internal override RenderElementEntity RenderElementEntity => FolderEntity; + public bool IsRootFolder => Parent == Profile; public override void Update(double deltaTime) { + if (_disposed) + throw new ObjectDisposedException("Folder"); + if (!Enabled) return; @@ -89,6 +94,9 @@ namespace Artemis.Core.Models.Profile public override void OverrideProgress(TimeSpan timeOverride, bool stickToMainSegment) { + if (_disposed) + throw new ObjectDisposedException("Folder"); + if (!Enabled) return; @@ -125,6 +133,9 @@ namespace Artemis.Core.Models.Profile public override void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo) { + if (_disposed) + throw new ObjectDisposedException("Folder"); + if (Path == null || !Enabled || !Children.Any(c => c.Enabled)) return; @@ -172,6 +183,13 @@ namespace Artemis.Core.Models.Profile profileElement.Render(deltaTime, folderCanvas, _folderBitmap.Info); folderCanvas.Restore(); } + + // If required, apply the opacity override of the module to the root folder + if (IsRootFolder && Profile.Module.OpacityOverride < 1) + { + var multiplier = Easings.SineEaseInOut(Profile.Module.OpacityOverride); + folderPaint.Color = folderPaint.Color.WithAlpha((byte) (folderPaint.Color.Alpha * multiplier)); + } foreach (var baseLayerEffect in LayerEffects.Where(e => e.Enabled)) baseLayerEffect.PostProcess(canvas, canvasInfo, folderPath, folderPaint); @@ -187,6 +205,9 @@ namespace Artemis.Core.Models.Profile /// public Folder AddFolder(string name) { + if (_disposed) + throw new ObjectDisposedException("Folder"); + var folder = new Folder(Profile, this, name) {Order = Children.LastOrDefault()?.Order ?? 1}; AddChild(folder); return folder; @@ -195,6 +216,9 @@ namespace Artemis.Core.Models.Profile /// public override void AddChild(ProfileElement child, int? order = null) { + if (_disposed) + throw new ObjectDisposedException("Folder"); + base.AddChild(child, order); CalculateRenderProperties(); } @@ -202,6 +226,9 @@ namespace Artemis.Core.Models.Profile /// public override void RemoveChild(ProfileElement child) { + if (_disposed) + throw new ObjectDisposedException("Folder"); + base.RemoveChild(child); CalculateRenderProperties(); } @@ -213,6 +240,9 @@ namespace Artemis.Core.Models.Profile public void CalculateRenderProperties() { + if (_disposed) + throw new ObjectDisposedException("Folder"); + var path = new SKPath {FillType = SKPathFillType.Winding}; foreach (var child in Children) { @@ -234,16 +264,27 @@ namespace Artemis.Core.Models.Profile if (!disposing) return; - _folderBitmap?.Dispose(); foreach (var baseLayerEffect in LayerEffects) baseLayerEffect.Dispose(); + _layerEffects.Clear(); + foreach (var profileElement in Children) profileElement.Dispose(); + ChildrenList.Clear(); + + _folderBitmap?.Dispose(); + _folderBitmap = null; + + Profile = null; + _disposed = true; } internal override void ApplyToEntity() { + if (_disposed) + throw new ObjectDisposedException("Folder"); + FolderEntity.Id = EntityId; FolderEntity.ParentId = Parent?.EntityId ?? new Guid(); @@ -262,16 +303,6 @@ namespace Artemis.Core.Models.Profile DisplayConditionGroup?.ApplyToEntity(); } - internal void Deactivate() - { - _folderBitmap?.Dispose(); - _folderBitmap = null; - - var layerEffects = new List(LayerEffects); - foreach (var baseLayerEffect in layerEffects) - DeactivateLayerEffect(baseLayerEffect); - } - #region Events public event EventHandler RenderPropertiesUpdated; diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs index 0aa341779..fdd212c4f 100644 --- a/src/Artemis.Core/Models/Profile/Layer.cs +++ b/src/Artemis.Core/Models/Profile/Layer.cs @@ -126,6 +126,9 @@ namespace Artemis.Core.Models.Profile /// public override List GetAllKeyframes() { + if (_disposed) + throw new ObjectDisposedException("Layer"); + var keyframes = base.GetAllKeyframes(); foreach (var baseLayerProperty in General.GetAllLayerProperties()) @@ -143,22 +146,36 @@ namespace Artemis.Core.Models.Profile protected override void Dispose(bool disposing) { - if (!disposing) + if (!disposing) return; - _general?.Dispose(); - _layerBitmap?.Dispose(); + _disposed = true; + + // Brush first in case it depends on any of the other disposables during it's own disposal _layerBrush?.Dispose(); - _transform?.Dispose(); + _layerBrush = null; foreach (var baseLayerEffect in LayerEffects) baseLayerEffect.Dispose(); + _layerEffects.Clear(); + + _general?.Dispose(); + _general = null; + _layerBitmap?.Dispose(); + _layerBitmap = null; + _transform?.Dispose(); + _transform = null; + + Profile = null; } #region Storage internal override void ApplyToEntity() { + if (_disposed) + throw new ObjectDisposedException("Layer"); + // Properties LayerEntity.Id = EntityId; LayerEntity.ParentId = Parent?.EntityId ?? new Guid(); @@ -231,6 +248,9 @@ namespace Artemis.Core.Models.Profile /// public override void Update(double deltaTime) { + if (_disposed) + throw new ObjectDisposedException("Layer"); + if (!Enabled || LayerBrush?.BaseProperties == null || !LayerBrush.BaseProperties.PropertiesInitialized) return; @@ -259,6 +279,9 @@ namespace Artemis.Core.Models.Profile public override void OverrideProgress(TimeSpan timeOverride, bool stickToMainSegment) { + if (_disposed) + throw new ObjectDisposedException("Layer"); + if (!Enabled || LayerBrush?.BaseProperties == null || !LayerBrush.BaseProperties.PropertiesInitialized) return; @@ -268,9 +291,7 @@ namespace Artemis.Core.Models.Profile { if (!DisplayContinuously) { - var position = timeOverride + StartSegmentLength; - if (position > StartSegmentLength + EndSegmentLength) - TimelinePosition = StartSegmentLength + EndSegmentLength; + TimelinePosition = StartSegmentLength + timeOverride; } else { @@ -301,6 +322,9 @@ namespace Artemis.Core.Models.Profile /// public override void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo) { + if (_disposed) + throw new ObjectDisposedException("Layer"); + if (!Enabled || TimelinePosition > TimelineLength) return; @@ -424,6 +448,9 @@ namespace Artemis.Core.Models.Profile internal void CalculateRenderProperties() { + if (_disposed) + throw new ObjectDisposedException("Layer"); + if (!Leds.Any()) Path = new SKPath(); else @@ -448,6 +475,9 @@ namespace Artemis.Core.Models.Profile internal SKPoint GetLayerAnchorPosition(SKPath layerPath, bool zeroBased = false) { + if (_disposed) + throw new ObjectDisposedException("Layer"); + var positionProperty = Transform.Position.CurrentValue; // Start at the center of the shape @@ -469,6 +499,9 @@ namespace Artemis.Core.Models.Profile /// public void IncludePathInTranslation(SKPath path, bool zeroBased) { + if (_disposed) + throw new ObjectDisposedException("Layer"); + var sizeProperty = Transform.Scale.CurrentValue; var rotationProperty = Transform.Rotation.CurrentValue; @@ -498,6 +531,9 @@ namespace Artemis.Core.Models.Profile /// public void ExcludePathFromTranslation(SKPath path, bool zeroBased) { + if (_disposed) + throw new ObjectDisposedException("Layer"); + var sizeProperty = Transform.Scale.CurrentValue; var rotationProperty = Transform.Rotation.CurrentValue; @@ -531,6 +567,9 @@ namespace Artemis.Core.Models.Profile /// The number of transformations applied public int ExcludeCanvasFromTranslation(SKCanvas canvas, bool zeroBased) { + if (_disposed) + throw new ObjectDisposedException("Layer"); + var sizeProperty = Transform.Scale.CurrentValue; var rotationProperty = Transform.Rotation.CurrentValue; @@ -568,6 +607,9 @@ namespace Artemis.Core.Models.Profile /// The LED to add public void AddLed(ArtemisLed led) { + if (_disposed) + throw new ObjectDisposedException("Layer"); + _leds.Add(led); CalculateRenderProperties(); } @@ -578,6 +620,9 @@ namespace Artemis.Core.Models.Profile /// The LEDs to add public void AddLeds(IEnumerable leds) { + if (_disposed) + throw new ObjectDisposedException("Layer"); + _leds.AddRange(leds); CalculateRenderProperties(); } @@ -588,6 +633,9 @@ namespace Artemis.Core.Models.Profile /// The LED to remove public void RemoveLed(ArtemisLed led) { + if (_disposed) + throw new ObjectDisposedException("Layer"); + _leds.Remove(led); CalculateRenderProperties(); } @@ -597,12 +645,18 @@ namespace Artemis.Core.Models.Profile /// public void ClearLeds() { + if (_disposed) + throw new ObjectDisposedException("Layer"); + _leds.Clear(); CalculateRenderProperties(); } internal void PopulateLeds(ArtemisSurface surface) { + if (_disposed) + throw new ObjectDisposedException("Layer"); + var leds = new List(); // Get the surface LEDs for this layer @@ -623,17 +677,6 @@ namespace Artemis.Core.Models.Profile #region Activation - internal void Deactivate() - { - _layerBitmap?.Dispose(); - _layerBitmap = null; - - DeactivateLayerBrush(); - var layerEffects = new List(LayerEffects); - foreach (var baseLayerEffect in layerEffects) - DeactivateLayerEffect(baseLayerEffect); - } - internal void DeactivateLayerBrush() { if (LayerBrush == null) diff --git a/src/Artemis.Core/Models/Profile/LayerGeneralProperties.cs b/src/Artemis.Core/Models/Profile/LayerGeneralProperties.cs index 8b1a11b2f..6ce5fa92f 100644 --- a/src/Artemis.Core/Models/Profile/LayerGeneralProperties.cs +++ b/src/Artemis.Core/Models/Profile/LayerGeneralProperties.cs @@ -28,5 +28,9 @@ namespace Artemis.Core.Models.Profile protected override void EnableProperties() { } + + protected override void DisableProperties() + { + } } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs b/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs index 009e46301..3c29ea526 100644 --- a/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs +++ b/src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs @@ -110,6 +110,8 @@ namespace Artemis.Core.Models.Profile public void Dispose() { DisableProperties(); + foreach (var layerPropertyGroup in _layerPropertyGroups) + layerPropertyGroup.Dispose(); } /// diff --git a/src/Artemis.Core/Models/Profile/LayerTransformProperties.cs b/src/Artemis.Core/Models/Profile/LayerTransformProperties.cs index 141d8cfa0..8a6afca51 100644 --- a/src/Artemis.Core/Models/Profile/LayerTransformProperties.cs +++ b/src/Artemis.Core/Models/Profile/LayerTransformProperties.cs @@ -30,5 +30,9 @@ namespace Artemis.Core.Models.Profile protected override void EnableProperties() { } + + protected override void DisableProperties() + { + } } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/Profile.cs b/src/Artemis.Core/Models/Profile/Profile.cs index 2394fd43c..b130da1bd 100644 --- a/src/Artemis.Core/Models/Profile/Profile.cs +++ b/src/Artemis.Core/Models/Profile/Profile.cs @@ -57,6 +57,8 @@ namespace Artemis.Core.Models.Profile { lock (this) { + if (_disposed) + throw new ObjectDisposedException("Profile"); if (!IsActivated) throw new ArtemisCoreException($"Cannot update inactive profile: {this}"); @@ -69,6 +71,8 @@ namespace Artemis.Core.Models.Profile { lock (this) { + if (_disposed) + throw new ObjectDisposedException("Profile"); if (!IsActivated) throw new ArtemisCoreException($"Cannot render inactive profile: {this}"); @@ -79,21 +83,26 @@ namespace Artemis.Core.Models.Profile public Folder GetRootFolder() { + if (_disposed) + throw new ObjectDisposedException("Profile"); + return (Folder) Children.Single(); } public void ApplyToProfile() { + if (_disposed) + throw new ObjectDisposedException("Profile"); + Name = ProfileEntity.Name; lock (ChildrenList) { - foreach (var folder in GetAllFolders()) - folder.Deactivate(); - foreach (var layer in GetAllLayers()) - layer.Deactivate(); - + // Remove the old profile tree + foreach (var profileElement in Children) + profileElement.Dispose(); ChildrenList.Clear(); + // Populate the profile starting at the root, the rest is populated recursively var rootFolder = ProfileEntity.Folders.FirstOrDefault(f => f.ParentId == EntityId); if (rootFolder == null) @@ -113,13 +122,21 @@ namespace Artemis.Core.Models.Profile if (!disposing) return; - Deactivate(); + OnDeactivating(); + foreach (var profileElement in Children) profileElement.Dispose(); + ChildrenList.Clear(); + + IsActivated = false; + _disposed = true; } internal override void ApplyToEntity() { + if (_disposed) + throw new ObjectDisposedException("Profile"); + ProfileEntity.Id = EntityId; ProfileEntity.PluginGuid = Module.PluginInfo.Guid; ProfileEntity.Name = Name; @@ -139,6 +156,8 @@ namespace Artemis.Core.Models.Profile { lock (this) { + if (_disposed) + throw new ObjectDisposedException("Profile"); if (IsActivated) return; @@ -148,25 +167,11 @@ namespace Artemis.Core.Models.Profile } } - internal void Deactivate() - { - lock (this) - { - if (!IsActivated) - return; - - foreach (var folder in GetAllFolders()) - folder.Deactivate(); - foreach (var layer in GetAllLayers()) - layer.Deactivate(); - - IsActivated = false; - OnDeactivated(); - } - } - internal void PopulateLeds(ArtemisSurface surface) { + if (_disposed) + throw new ObjectDisposedException("Profile"); + foreach (var layer in GetAllLayers()) layer.PopulateLeds(surface); } @@ -174,7 +179,7 @@ namespace Artemis.Core.Models.Profile #region Events /// - /// Occurs when the profile is being activated. + /// Occurs when the profile has been activated. /// public event EventHandler Activated; @@ -188,7 +193,7 @@ namespace Artemis.Core.Models.Profile Activated?.Invoke(this, EventArgs.Empty); } - private void OnDeactivated() + private void OnDeactivating() { Deactivated?.Invoke(this, EventArgs.Empty); } diff --git a/src/Artemis.Core/Models/Profile/ProfileDescriptor.cs b/src/Artemis.Core/Models/Profile/ProfileDescriptor.cs index 5dead225a..2ff49ab28 100644 --- a/src/Artemis.Core/Models/Profile/ProfileDescriptor.cs +++ b/src/Artemis.Core/Models/Profile/ProfileDescriptor.cs @@ -9,16 +9,16 @@ namespace Artemis.Core.Models.Profile internal ProfileDescriptor(ProfileModule profileModule, ProfileEntity profileEntity) { ProfileModule = profileModule; - ProfileEntity = profileEntity; Id = profileEntity.Id; Name = profileEntity.Name; + IsLastActiveProfile = profileEntity.IsActive; } - + + public bool IsLastActiveProfile { get; set; } + public Guid Id { get; } public ProfileModule ProfileModule { get; } public string Name { get; } - - internal ProfileEntity ProfileEntity { get; } } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/ProfileElement.cs b/src/Artemis.Core/Models/Profile/ProfileElement.cs index a54fa5dc4..5316a4b69 100644 --- a/src/Artemis.Core/Models/Profile/ProfileElement.cs +++ b/src/Artemis.Core/Models/Profile/ProfileElement.cs @@ -22,6 +22,7 @@ namespace Artemis.Core.Models.Profile GC.SuppressFinalize(this); } + protected bool _disposed; private bool _enabled; private Guid _entityId; private string _name; @@ -98,6 +99,9 @@ namespace Artemis.Core.Models.Profile public List GetAllFolders() { + if (_disposed) + throw new ObjectDisposedException(GetType().Name); + var folders = new List(); foreach (var childFolder in Children.Where(c => c is Folder).Cast()) { @@ -112,6 +116,9 @@ namespace Artemis.Core.Models.Profile public List GetAllLayers() { + if (_disposed) + throw new ObjectDisposedException(GetType().Name); + var layers = new List(); // Add all layers in this element @@ -131,6 +138,9 @@ namespace Artemis.Core.Models.Profile /// The order where to place the child (1-based), defaults to the end of the collection public virtual void AddChild(ProfileElement child, int? order = null) { + if (_disposed) + throw new ObjectDisposedException(GetType().Name); + lock (ChildrenList) { // Add to the end of the list @@ -169,6 +179,9 @@ namespace Artemis.Core.Models.Profile /// The profile element to remove public virtual void RemoveChild(ProfileElement child) { + if (_disposed) + throw new ObjectDisposedException(GetType().Name); + lock (ChildrenList) { ChildrenList.Remove(child); diff --git a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs index 77d2b356d..8408a6b31 100644 --- a/src/Artemis.Core/Models/Profile/RenderProfileElement.cs +++ b/src/Artemis.Core/Models/Profile/RenderProfileElement.cs @@ -203,8 +203,6 @@ namespace Artemis.Core.Models.Profile // If we are at the end of the main timeline, wrap around back to the beginning if (DisplayContinuously && TimelinePosition >= mainSegmentEnd) TimelinePosition = StartSegmentLength + (mainSegmentEnd - TimelinePosition); - else if (TimelinePosition >= TimelineLength) - TimelinePosition = TimelineLength; } else { diff --git a/src/Artemis.Core/Plugins/Abstract/ProfileModule.cs b/src/Artemis.Core/Plugins/Abstract/ProfileModule.cs index f2a2d952a..55965ca63 100644 --- a/src/Artemis.Core/Plugins/Abstract/ProfileModule.cs +++ b/src/Artemis.Core/Plugins/Abstract/ProfileModule.cs @@ -4,6 +4,7 @@ using System.Collections.ObjectModel; using System.Linq; using System.Linq.Expressions; using System.Reflection; +using System.Threading.Tasks; using Artemis.Core.Exceptions; using Artemis.Core.Models.Profile; using Artemis.Core.Models.Surface; @@ -94,6 +95,11 @@ namespace Artemis.Core.Plugins.Abstract /// public abstract class ProfileModule : Module { + protected ProfileModule() + { + OpacityOverride = 1; + } + protected readonly List HiddenPropertiesList = new List(); /// @@ -113,6 +119,10 @@ namespace Artemis.Core.Plugins.Abstract { lock (this) { + OpacityOverride = AnimatingProfileChange + ? Math.Max(0, OpacityOverride - 0.1) + : Math.Min(1, OpacityOverride + 0.1); + // Update the profile if (!IsProfileUpdatingDisabled) ActiveProfile?.Update(deltaTime); @@ -129,6 +139,27 @@ namespace Artemis.Core.Plugins.Abstract } } + internal async Task ChangeActiveProfileAnimated(Profile profile, ArtemisSurface surface) + { + if (profile != null && profile.Module != this) + throw new ArtemisCoreException($"Cannot activate a profile of module {profile.Module} on a module of plugin {PluginInfo}."); + + + if (profile == ActiveProfile || AnimatingProfileChange) + return; + + AnimatingProfileChange = true; + + while (OpacityOverride > 0) + await Task.Delay(50); + + ChangeActiveProfile(profile, surface); + AnimatingProfileChange = false; + + while (OpacityOverride < 1) + await Task.Delay(50); + } + internal void ChangeActiveProfile(Profile profile, ArtemisSurface surface) { if (profile != null && profile.Module != this) @@ -147,6 +178,16 @@ namespace Artemis.Core.Plugins.Abstract OnActiveProfileChanged(); } + /// + /// Overrides the opacity of the root folder + /// + public double OpacityOverride { get; set; } + + /// + /// Indicates whether or not a profile change is being animated + /// + public bool AnimatingProfileChange { get; private set; } + #region Events public event EventHandler ActiveProfileChanged; diff --git a/src/Artemis.Core/Plugins/LayerBrush/Abstract/PerLedLayerBrush.cs b/src/Artemis.Core/Plugins/LayerBrush/Abstract/PerLedLayerBrush.cs index 150a08c3d..758ba6d69 100644 --- a/src/Artemis.Core/Plugins/LayerBrush/Abstract/PerLedLayerBrush.cs +++ b/src/Artemis.Core/Plugins/LayerBrush/Abstract/PerLedLayerBrush.cs @@ -52,6 +52,8 @@ namespace Artemis.Core.Plugins.LayerBrush.Abstract { var artemisLed = Layer.Leds[index]; var renderPoint = points[index * 2 + 1]; + if (!float.IsFinite(renderPoint.X) || !float.IsFinite(renderPoint.Y)) + continue; // Let the brush determine the color ledPaint.Color = GetColor(artemisLed, renderPoint); diff --git a/src/Artemis.Core/Resources/intro-profile.json b/src/Artemis.Core/Resources/intro-profile.json new file mode 100644 index 000000000..895a03515 --- /dev/null +++ b/src/Artemis.Core/Resources/intro-profile.json @@ -0,0 +1,1045 @@ +{ + "$type": "Artemis.Storage.Entities.Profile.ProfileEntity, Artemis.Storage", + "Id": "eb4f487b-475b-408f-a84f-733412d41b44", + "PluginGuid": "0de2991a-d7b8-4f61-ae4e-6623849215b5", + "Name": "Intro animation", + "IsActive": true, + "Folders": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.FolderEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [ + { + "$type": "Artemis.Storage.Entities.Profile.FolderEntity, Artemis.Storage", + "Id": "cc21b67c-3485-4dc6-b2af-105fda42a915", + "ParentId": "eb4f487b-475b-408f-a84f-733412d41b44", + "Order": 1, + "Name": "Root folder", + "Enabled": true, + "Profile": null, + "ProfileId": "eb4f487b-475b-408f-a84f-733412d41b44", + "StartSegmentLength": "00:00:00", + "MainSegmentLength": "00:00:05", + "EndSegmentLength": "00:00:00", + "DisplayContinuously": true, + "AlwaysFinishTimeline": false, + "LayerEffects": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.LayerEffectEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + }, + "PropertyEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + }, + "ExpandedPropertyGroups": { + "$type": "System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib", + "$values": [] + }, + "RootDisplayCondition": { + "$type": "Artemis.Storage.Entities.Profile.DisplayConditionGroupEntity, Artemis.Storage", + "BooleanOperator": 0, + "Children": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.Abstract.DisplayConditionPartEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + } + } + ] + }, + "Layers": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.LayerEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [ + { + "$type": "Artemis.Storage.Entities.Profile.LayerEntity, Artemis.Storage", + "Id": "2464a52c-4cec-4f17-87ed-edfe4bfed6f6", + "ParentId": "cc21b67c-3485-4dc6-b2af-105fda42a915", + "Order": 1, + "Name": "Teal layer", + "Enabled": true, + "Leds": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.LedEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + }, + "Profile": null, + "ProfileId": "eb4f487b-475b-408f-a84f-733412d41b44", + "StartSegmentLength": "00:00:00", + "MainSegmentLength": "00:00:05", + "EndSegmentLength": "00:00:00", + "DisplayContinuously": false, + "AlwaysFinishTimeline": false, + "LayerEffects": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.LayerEffectEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + }, + "PropertyEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [ + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "General.ShapeType", + "Value": "1", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "General.ResizeMode", + "Value": "0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "General.BlendMode", + "Value": "3", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "General.BrushReference", + "Value": "{\"BrushPluginGuid\":\"61cbbf01-8d69-4ede-a972-f3f269da66d9\",\"BrushType\":\"NoiseBrush\"}", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "Transform.AnchorPoint", + "Value": "{\"IsEmpty\":true,\"Length\":0.0,\"LengthSquared\":0.0,\"X\":0.0,\"Y\":0.0}", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "Transform.Position", + "Value": "{\"IsEmpty\":true,\"Length\":0.0,\"LengthSquared\":0.0,\"X\":0.0,\"Y\":0.0}", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "Transform.Scale", + "Value": "{\"IsEmpty\":false,\"Width\":500.0,\"Height\":500.0}", + "KeyframesEnabled": true, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [ + { + "$type": "Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage", + "Position": "00:00:02.5000000", + "Timeline": 0, + "Value": "{\"IsEmpty\":false,\"Width\":300.0,\"Height\":0.0}", + "EasingFunction": 3 + }, + { + "$type": "Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage", + "Position": "00:00:03.7500000", + "Timeline": 0, + "Value": "{\"IsEmpty\":false,\"Width\":300.0,\"Height\":400.0}", + "EasingFunction": 0 + } + ] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "Transform.Rotation", + "Value": "-45.0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "Transform.Opacity", + "Value": "100.0", + "KeyframesEnabled": true, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [ + { + "$type": "Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage", + "Position": "00:00:04", + "Timeline": 0, + "Value": "100.0", + "EasingFunction": 0 + }, + { + "$type": "Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage", + "Position": "00:00:04.4000000", + "Timeline": 0, + "Value": "0.0", + "EasingFunction": 0 + } + ] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "61cbbf01-8d69-4ede-a972-f3f269da66d9", + "Path": "LayerBrush.ColorType", + "Value": "0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "61cbbf01-8d69-4ede-a972-f3f269da66d9", + "Path": "LayerBrush.MainColor", + "Value": "\"#ff009688\"", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "61cbbf01-8d69-4ede-a972-f3f269da66d9", + "Path": "LayerBrush.SecondaryColor", + "Value": "\"#ff00ffe7\"", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "61cbbf01-8d69-4ede-a972-f3f269da66d9", + "Path": "LayerBrush.GradientColor", + "Value": "{\"Stops\":[{\"Color\":\"#ffff0000\",\"Position\":0.0},{\"Color\":\"#ffff8800\",\"Position\":0.125},{\"Color\":\"#ffedff00\",\"Position\":0.25},{\"Color\":\"#ff65ff00\",\"Position\":0.375},{\"Color\":\"#ff00ff22\",\"Position\":0.5},{\"Color\":\"#ff00ffaa\",\"Position\":0.625},{\"Color\":\"#ff00cbff\",\"Position\":0.75},{\"Color\":\"#ff0043ff\",\"Position\":0.875},{\"Color\":\"#ffff0000\",\"Position\":1.0}],\"Rotation\":0.0}", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "61cbbf01-8d69-4ede-a972-f3f269da66d9", + "Path": "LayerBrush.Scale", + "Value": "{\"IsEmpty\":false,\"Width\":100.0,\"Height\":100.0}", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "61cbbf01-8d69-4ede-a972-f3f269da66d9", + "Path": "LayerBrush.Hardness", + "Value": "300.0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "61cbbf01-8d69-4ede-a972-f3f269da66d9", + "Path": "LayerBrush.ScrollSpeed", + "Value": "{\"IsEmpty\":true,\"Length\":0.0,\"LengthSquared\":0.0,\"X\":0.0,\"Y\":0.0}", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "61cbbf01-8d69-4ede-a972-f3f269da66d9", + "Path": "LayerBrush.AnimationSpeed", + "Value": "25.0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + } + ] + }, + "ExpandedPropertyGroups": { + "$type": "System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib", + "$values": [ + "Transform", + "General" + ] + }, + "RootDisplayCondition": { + "$type": "Artemis.Storage.Entities.Profile.DisplayConditionGroupEntity, Artemis.Storage", + "BooleanOperator": 0, + "Children": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.Abstract.DisplayConditionPartEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.LayerEntity, Artemis.Storage", + "Id": "8d5fa358-88ed-4501-8cb2-fd9c31a34274", + "ParentId": "cc21b67c-3485-4dc6-b2af-105fda42a915", + "Order": 2, + "Name": "RGB layer", + "Enabled": true, + "Leds": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.LedEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + }, + "Profile": null, + "ProfileId": "eb4f487b-475b-408f-a84f-733412d41b44", + "StartSegmentLength": "00:00:00", + "MainSegmentLength": "00:00:03.7500000", + "EndSegmentLength": "00:00:00", + "DisplayContinuously": false, + "AlwaysFinishTimeline": false, + "LayerEffects": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.LayerEffectEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + }, + "PropertyEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [ + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "General.ShapeType", + "Value": "1", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "General.ResizeMode", + "Value": "0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "General.BlendMode", + "Value": "3", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "General.BrushReference", + "Value": "{\"BrushPluginGuid\":\"92a9d6ba-6f7a-4937-94d5-c1d715b4141a\",\"BrushType\":\"ColorBrush\"}", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "Transform.AnchorPoint", + "Value": "{\"IsEmpty\":true,\"Length\":0.0,\"LengthSquared\":0.0,\"X\":0.0,\"Y\":0.0}", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "Transform.Position", + "Value": "{\"IsEmpty\":false,\"Length\":0.5,\"LengthSquared\":0.25,\"X\":-0.5,\"Y\":-0.0}", + "KeyframesEnabled": true, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [ + { + "$type": "Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage", + "Position": "00:00:01.2500000", + "Timeline": 0, + "Value": "{\"IsEmpty\":false,\"Length\":0.25,\"LengthSquared\":0.0625,\"X\":-0.25,\"Y\":0.0}", + "EasingFunction": 0 + }, + { + "$type": "Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage", + "Position": "00:00:03.7500000", + "Timeline": 0, + "Value": "{\"IsEmpty\":false,\"Length\":0.25,\"LengthSquared\":0.0625,\"X\":0.25,\"Y\":0.0}", + "EasingFunction": 0 + } + ] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "Transform.Scale", + "Value": "{\"IsEmpty\":false,\"Width\":200.0,\"Height\":0.0}", + "KeyframesEnabled": true, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [ + { + "$type": "Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage", + "Position": "00:00:01.2500000", + "Timeline": 0, + "Value": "{\"IsEmpty\":false,\"Width\":150.0,\"Height\":0.0}", + "EasingFunction": 5 + }, + { + "$type": "Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage", + "Position": "00:00:02.2700000", + "Timeline": 0, + "Value": "{\"IsEmpty\":false,\"Width\":150.0,\"Height\":100.0}", + "EasingFunction": 0 + } + ] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "Transform.Rotation", + "Value": "0.0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "Transform.Opacity", + "Value": "100.0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a", + "Path": "LayerBrush.GradientType", + "Value": "1", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a", + "Path": "LayerBrush.TileMode", + "Value": "0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a", + "Path": "LayerBrush.Color", + "Value": "\"#ffff0000\"", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a", + "Path": "LayerBrush.Colors", + "Value": "{\"Stops\":[{\"Color\":\"#ffff0000\",\"Position\":0.0},{\"Color\":\"#ffff8800\",\"Position\":0.125},{\"Color\":\"#ffedff00\",\"Position\":0.25},{\"Color\":\"#ff65ff00\",\"Position\":0.375},{\"Color\":\"#ff00ff22\",\"Position\":0.5},{\"Color\":\"#ff00ffaa\",\"Position\":0.625},{\"Color\":\"#ff00cbff\",\"Position\":0.75},{\"Color\":\"#ff0043ff\",\"Position\":0.875},{\"Color\":\"#ffff0000\",\"Position\":1.0}],\"Rotation\":0.0}", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a", + "Path": "LayerBrush.ColorsMultiplier", + "Value": "2", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a", + "Path": "LayerBrush.LinearGradientRotation", + "Value": "0.0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a", + "Path": "LayerBrush.RadialGradient.CenterOffset", + "Value": "{\"IsEmpty\":true,\"Length\":0.0,\"LengthSquared\":0.0,\"X\":0.0,\"Y\":0.0}", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a", + "Path": "LayerBrush.RadialGradient.ResizeMode", + "Value": "0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + } + ] + }, + "ExpandedPropertyGroups": { + "$type": "System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib", + "$values": [ + "Transform", + "LayerBrush" + ] + }, + "RootDisplayCondition": { + "$type": "Artemis.Storage.Entities.Profile.DisplayConditionGroupEntity, Artemis.Storage", + "BooleanOperator": 0, + "Children": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.Abstract.DisplayConditionPartEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.LayerEntity, Artemis.Storage", + "Id": "72a7e56c-0649-4449-8dca-eb937161f228", + "ParentId": "cc21b67c-3485-4dc6-b2af-105fda42a915", + "Order": 3, + "Name": "Exploison layer", + "Enabled": true, + "Leds": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.LedEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + }, + "Profile": null, + "ProfileId": "eb4f487b-475b-408f-a84f-733412d41b44", + "StartSegmentLength": "00:00:00", + "MainSegmentLength": "00:00:02.2500000", + "EndSegmentLength": "00:00:00", + "DisplayContinuously": false, + "AlwaysFinishTimeline": false, + "LayerEffects": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.LayerEffectEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + }, + "PropertyEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [ + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "General.ShapeType", + "Value": "0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "General.ResizeMode", + "Value": "0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "General.BlendMode", + "Value": "3", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "General.BrushReference", + "Value": "{\"BrushPluginGuid\":\"92a9d6ba-6f7a-4937-94d5-c1d715b4141a\",\"BrushType\":\"ColorBrush\"}", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "Transform.AnchorPoint", + "Value": "{\"IsEmpty\":true,\"Length\":0.0,\"LengthSquared\":0.0,\"X\":0.0,\"Y\":0.0}", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "Transform.Position", + "Value": "{\"IsEmpty\":true,\"Length\":0.0,\"LengthSquared\":0.0,\"X\":0.0,\"Y\":0.0}", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "Transform.Scale", + "Value": "{\"IsEmpty\":false,\"Width\":110.03,\"Height\":340.37}", + "KeyframesEnabled": true, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [ + { + "$type": "Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage", + "Position": "00:00:00", + "Timeline": 0, + "Value": "{\"IsEmpty\":true,\"Width\":0.0,\"Height\":0.0}", + "EasingFunction": 6 + }, + { + "$type": "Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage", + "Position": "00:00:02", + "Timeline": 0, + "Value": "{\"IsEmpty\":false,\"Width\":200.0,\"Height\":200.0}", + "EasingFunction": 0 + } + ] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "Transform.Rotation", + "Value": "0.0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "Transform.Opacity", + "Value": "100.0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a", + "Path": "LayerBrush.GradientType", + "Value": "2", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a", + "Path": "LayerBrush.TileMode", + "Value": "0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a", + "Path": "LayerBrush.Color", + "Value": "\"#ffff0000\"", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a", + "Path": "LayerBrush.Colors", + "Value": "{\"Stops\":[{\"Color\":\"#00ff0000\",\"Position\":0.0},{\"Color\":\"#ffff8800\",\"Position\":0.492},{\"Color\":\"#ffedff00\",\"Position\":0.905},{\"Color\":\"#00ff0000\",\"Position\":1.0}],\"Rotation\":0.0}", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a", + "Path": "LayerBrush.ColorsMultiplier", + "Value": "0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a", + "Path": "LayerBrush.LinearGradientRotation", + "Value": "0.0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a", + "Path": "LayerBrush.RadialGradient.CenterOffset", + "Value": "{\"IsEmpty\":true,\"Length\":0.0,\"LengthSquared\":0.0,\"X\":0.0,\"Y\":0.0}", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a", + "Path": "LayerBrush.RadialGradient.ResizeMode", + "Value": "0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + } + ] + }, + "ExpandedPropertyGroups": { + "$type": "System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib", + "$values": [ + "LayerBrush" + ] + }, + "RootDisplayCondition": { + "$type": "Artemis.Storage.Entities.Profile.DisplayConditionGroupEntity, Artemis.Storage", + "BooleanOperator": 0, + "Children": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.Abstract.DisplayConditionPartEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.LayerEntity, Artemis.Storage", + "Id": "f046f56f-a236-4ed6-bbd9-b5a4731878cf", + "ParentId": "cc21b67c-3485-4dc6-b2af-105fda42a915", + "Order": 4, + "Name": "Background", + "Enabled": true, + "Leds": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.LedEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + }, + "Profile": null, + "ProfileId": "eb4f487b-475b-408f-a84f-733412d41b44", + "StartSegmentLength": "00:00:00", + "MainSegmentLength": "00:00:03.7500000", + "EndSegmentLength": "00:00:00", + "DisplayContinuously": false, + "AlwaysFinishTimeline": false, + "LayerEffects": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.LayerEffectEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + }, + "PropertyEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [ + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "General.ShapeType", + "Value": "1", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "General.ResizeMode", + "Value": "0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "General.BlendMode", + "Value": "3", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "General.BrushReference", + "Value": "{\"BrushPluginGuid\":\"92a9d6ba-6f7a-4937-94d5-c1d715b4141a\",\"BrushType\":\"ColorBrush\"}", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "Transform.AnchorPoint", + "Value": "{\"IsEmpty\":true,\"Length\":0.0,\"LengthSquared\":0.0,\"X\":0.0,\"Y\":0.0}", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "Transform.Position", + "Value": "{\"IsEmpty\":true,\"Length\":0.0,\"LengthSquared\":0.0,\"X\":0.0,\"Y\":0.0}", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "Transform.Scale", + "Value": "{\"IsEmpty\":false,\"Width\":100.0,\"Height\":100.0}", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "Transform.Rotation", + "Value": "0.0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Path": "Transform.Opacity", + "Value": "100.0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a", + "Path": "LayerBrush.GradientType", + "Value": "0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a", + "Path": "LayerBrush.TileMode", + "Value": "0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a", + "Path": "LayerBrush.Color", + "Value": "\"#ff000000\"", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a", + "Path": "LayerBrush.Colors", + "Value": "{\"Stops\":[{\"Color\":\"#ffff0000\",\"Position\":0.0},{\"Color\":\"#ffff8800\",\"Position\":0.125},{\"Color\":\"#ffedff00\",\"Position\":0.25},{\"Color\":\"#ff65ff00\",\"Position\":0.375},{\"Color\":\"#ff00ff22\",\"Position\":0.5},{\"Color\":\"#ff00ffaa\",\"Position\":0.625},{\"Color\":\"#ff00cbff\",\"Position\":0.75},{\"Color\":\"#ff0043ff\",\"Position\":0.875},{\"Color\":\"#ffff0000\",\"Position\":1.0}],\"Rotation\":0.0}", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a", + "Path": "LayerBrush.ColorsMultiplier", + "Value": "0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a", + "Path": "LayerBrush.LinearGradientRotation", + "Value": "0.0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a", + "Path": "LayerBrush.RadialGradient.CenterOffset", + "Value": "{\"IsEmpty\":true,\"Length\":0.0,\"LengthSquared\":0.0,\"X\":0.0,\"Y\":0.0}", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + }, + { + "$type": "Artemis.Storage.Entities.Profile.PropertyEntity, Artemis.Storage", + "PluginGuid": "92a9d6ba-6f7a-4937-94d5-c1d715b4141a", + "Path": "LayerBrush.RadialGradient.ResizeMode", + "Value": "0", + "KeyframesEnabled": false, + "KeyframeEntities": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.KeyframeEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + } + ] + }, + "ExpandedPropertyGroups": { + "$type": "System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib", + "$values": [ + "LayerBrush" + ] + }, + "RootDisplayCondition": { + "$type": "Artemis.Storage.Entities.Profile.DisplayConditionGroupEntity, Artemis.Storage", + "BooleanOperator": 0, + "Children": { + "$type": "System.Collections.Generic.List`1[[Artemis.Storage.Entities.Profile.Abstract.DisplayConditionPartEntity, Artemis.Storage]], System.Private.CoreLib", + "$values": [] + } + } + } + ] + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/CoreService.cs b/src/Artemis.Core/Services/CoreService.cs index 1cf8fd5b8..ec0654995 100644 --- a/src/Artemis.Core/Services/CoreService.cs +++ b/src/Artemis.Core/Services/CoreService.cs @@ -3,14 +3,17 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; +using System.Threading.Tasks; using Artemis.Core.Events; using Artemis.Core.Exceptions; using Artemis.Core.JsonConverters; +using Artemis.Core.Models.Profile; using Artemis.Core.Ninject; using Artemis.Core.Plugins.Abstract; using Artemis.Core.Plugins.Models; using Artemis.Core.Services.Interfaces; using Artemis.Core.Services.Storage.Interfaces; +using Artemis.Core.Utilities; using Artemis.Storage; using Newtonsoft.Json; using RGB.NET.Core; @@ -35,6 +38,7 @@ namespace Artemis.Core.Services private readonly ISurfaceService _surfaceService; private List _dataModelExpansions; private List _modules; + private IntroAnimation _introAnimation; // ReSharper disable once UnusedParameter.Local - Storage migration service is injected early to ensure it runs before anything else internal CoreService(ILogger logger, StorageMigrationService _, ISettingsService settingsService, IPluginService pluginService, @@ -90,11 +94,38 @@ namespace Artemis.Core.Services else _logger.Information("Initialized without an active surface entity"); + PlayIntroAnimation(); _profileService.ActivateLastActiveProfiles(); OnInitialized(); } + private void PlayIntroAnimation() + { + FrameRendering += DrawOverlay; + _introAnimation = new IntroAnimation(_logger, _profileService, _surfaceService); + + // Draw a white overlay over the device + void DrawOverlay(object sender, FrameRenderingEventArgs args) + { + if (_introAnimation == null) + args.Canvas.Clear(new SKColor(0, 0, 0)); + else + _introAnimation.Render(args.DeltaTime, args.Canvas, _rgbService.BitmapBrush.Bitmap.Info); + } + + var introLength = _introAnimation.AnimationProfile.GetAllLayers().Max(l => l.TimelineLength); + + // Stop rendering after the profile finishes (take 1 second extra in case of slow updates) + Task.Run(async () => + { + await Task.Delay(introLength.Add(TimeSpan.FromSeconds(1))); + FrameRendering -= DrawOverlay; + + _introAnimation.AnimationProfile?.Dispose(); + _introAnimation = null; + }); + } public void SetMainWindowHandle(IntPtr handle) { diff --git a/src/Artemis.Core/Services/Interfaces/ICoreService.cs b/src/Artemis.Core/Services/Interfaces/ICoreService.cs index 34a3969ee..f4e6b7750 100644 --- a/src/Artemis.Core/Services/Interfaces/ICoreService.cs +++ b/src/Artemis.Core/Services/Interfaces/ICoreService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using Artemis.Core.Events; namespace Artemis.Core.Services.Interfaces diff --git a/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs b/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs index ed087426c..31bd8efcb 100644 --- a/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs +++ b/src/Artemis.Core/Services/Storage/Interfaces/IProfileService.cs @@ -1,10 +1,14 @@ using System.Collections.Generic; +using System.Threading.Tasks; using Artemis.Core.Models.Profile; using Artemis.Core.Plugins.Abstract; using Artemis.Core.Services.Interfaces; namespace Artemis.Core.Services.Storage.Interfaces { + /// + /// Provides access to profile storage and is responsible for activating default profiles + /// public interface IProfileService : IArtemisService { /// @@ -18,14 +22,14 @@ namespace Artemis.Core.Services.Storage.Interfaces /// The profile module to create the profile for /// The name of the new profile /// - ProfileDescriptor CreateProfile(ProfileModule module, string name); + ProfileDescriptor CreateProfileDescriptor(ProfileModule module, string name); /// /// Gets a descriptor for each profile stored for the given /// /// The module to return profile descriptors for /// - List GetProfiles(ProfileModule module); + List GetProfileDescriptors(ProfileModule module); /// /// Writes the profile to persistent storage @@ -40,30 +44,53 @@ namespace Artemis.Core.Services.Storage.Interfaces /// The profile to delete void DeleteProfile(Profile profile); + /// + /// Permanently deletes the profile described by the provided profile descriptor + /// + /// The descriptor pointing to the profile to delete + void DeleteProfile(ProfileDescriptor profileDescriptor); + /// /// Activates the profile described in the given with the currently active surface /// /// The descriptor describing the profile to activate Profile ActivateProfile(ProfileDescriptor profileDescriptor); + /// + /// Asynchronously activates the profile described in the given with the currently + /// active surface using a fade animation + /// + /// The descriptor describing the profile to activate + Task ActivateProfileAnimated(ProfileDescriptor profileDescriptor); + /// /// Clears the active profile on the given /// /// The profile module to deactivate the active profile on void ClearActiveProfile(ProfileModule module); + /// + /// Asynchronously clears the active profile on the given using a fade animation + /// + /// The profile module to deactivate the active profile on + Task ClearActiveProfileAnimated(ProfileModule module); + /// /// Attempts to restore the profile to the state it had before the last call. /// - /// - /// - bool UndoUpdateProfile(Profile selectedProfile, ProfileModule module); + /// + bool UndoUpdateProfile(Profile profile); /// /// Attempts to restore the profile to the state it had before the last call. /// - /// - /// - bool RedoUpdateProfile(Profile selectedProfile, ProfileModule module); + /// + bool RedoUpdateProfile(Profile profile); + + /// + /// Prepares the profile for rendering. You should not need to call this, it is exposed for some niche usage in the core + /// + /// + void InstantiateProfile(Profile profile); } } \ No newline at end of file diff --git a/src/Artemis.Core/Services/Storage/ProfileService.cs b/src/Artemis.Core/Services/Storage/ProfileService.cs index e71830e3e..9212eb87f 100644 --- a/src/Artemis.Core/Services/Storage/ProfileService.cs +++ b/src/Artemis.Core/Services/Storage/ProfileService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using Artemis.Core.Events; using Artemis.Core.Models.Profile; using Artemis.Core.Models.Surface; @@ -15,9 +16,6 @@ using Serilog; namespace Artemis.Core.Services.Storage { - /// - /// Provides access to profile storage and is responsible for activating default profiles - /// public class ProfileService : IProfileService { private readonly ILogger _logger; @@ -51,20 +49,13 @@ namespace Artemis.Core.Services.Storage } } - public List GetProfiles(ProfileModule module) + public List GetProfileDescriptors(ProfileModule module) { var profileEntities = _profileRepository.GetByPluginGuid(module.PluginInfo.Guid); return profileEntities.Select(e => new ProfileDescriptor(module, e)).ToList(); } - public ProfileDescriptor GetLastActiveProfile(ProfileModule module) - { - var moduleProfiles = _profileRepository.GetByPluginGuid(module.PluginInfo.Guid); - var profileEntity = moduleProfiles.FirstOrDefault(p => p.IsActive) ?? moduleProfiles.FirstOrDefault(); - return profileEntity == null ? null : new ProfileDescriptor(module, profileEntity); - } - - public ProfileDescriptor CreateProfile(ProfileModule module, string name) + public ProfileDescriptor CreateProfileDescriptor(ProfileModule module, string name) { var profileEntity = new ProfileEntity {Id = Guid.NewGuid(), Name = name, PluginGuid = module.PluginInfo.Guid}; return new ProfileDescriptor(module, profileEntity); @@ -72,31 +63,59 @@ namespace Artemis.Core.Services.Storage public Profile ActivateProfile(ProfileDescriptor profileDescriptor) { - if (profileDescriptor.ProfileModule.ActiveProfile.EntityId == profileDescriptor.Id) + if (profileDescriptor.ProfileModule.ActiveProfile?.EntityId == profileDescriptor.Id) return profileDescriptor.ProfileModule.ActiveProfile; - var profile = new Profile(profileDescriptor.ProfileModule, profileDescriptor.ProfileEntity); - InitializeLayerProperties(profile); - InstantiateLayers(profile); - InstantiateFolders(profile); + var profileEntity = _profileRepository.Get(profileDescriptor.Id); + var profile = new Profile(profileDescriptor.ProfileModule, profileEntity); + InstantiateProfile(profile); profileDescriptor.ProfileModule.ChangeActiveProfile(profile, _surfaceService.ActiveSurface); return profile; } + public async Task ActivateProfileAnimated(ProfileDescriptor profileDescriptor) + { + if (profileDescriptor.ProfileModule.ActiveProfile?.EntityId == profileDescriptor.Id) + return profileDescriptor.ProfileModule.ActiveProfile; + + var profileEntity = _profileRepository.Get(profileDescriptor.Id); + var profile = new Profile(profileDescriptor.ProfileModule, profileEntity); + InstantiateProfile(profile); + + await profileDescriptor.ProfileModule.ChangeActiveProfileAnimated(profile, _surfaceService.ActiveSurface); + return profile; + } + public void ClearActiveProfile(ProfileModule module) { module.ChangeActiveProfile(null, _surfaceService.ActiveSurface); } + public async Task ClearActiveProfileAnimated(ProfileModule module) + { + await module.ChangeActiveProfileAnimated(null, _surfaceService.ActiveSurface); + } + public void DeleteProfile(Profile profile) { _logger.Debug("Removing profile " + profile); - + // If the given profile is currently active, disable it first (this also disposes it) + if (profile.Module.ActiveProfile == profile) + profile.Module.ChangeActiveProfile(null, _surfaceService.ActiveSurface); + else + profile.Dispose(); + _profileRepository.Remove(profile.ProfileEntity); } + public void DeleteProfile(ProfileDescriptor profileDescriptor) + { + var profileEntity = _profileRepository.Get(profileDescriptor.Id); + _profileRepository.Remove(profileEntity); + } + public void UpdateProfile(Profile profile, bool includeChildren) { _logger.Debug("Updating profile " + profile); @@ -116,46 +135,73 @@ namespace Artemis.Core.Services.Storage _profileRepository.Save(profile.ProfileEntity); } - public bool UndoUpdateProfile(Profile profile, ProfileModule module) + public bool UndoUpdateProfile(Profile profile) { - if (!profile.UndoStack.Any()) + // Keep the profile from being rendered by locking it + lock (profile) { - _logger.Debug("Undo profile update - Failed, undo stack empty"); - return false; - } + if (!profile.UndoStack.Any()) + { + _logger.Debug("Undo profile update - Failed, undo stack empty"); + return false; + } - ActivateProfile(module, null); - var top = profile.UndoStack.Pop(); - var memento = JsonConvert.SerializeObject(profile.ProfileEntity, MementoSettings); - profile.RedoStack.Push(memento); - profile.ProfileEntity = JsonConvert.DeserializeObject(top, MementoSettings); - profile.ApplyToProfile(); - ActivateProfile(module, profile); + var top = profile.UndoStack.Pop(); + var memento = JsonConvert.SerializeObject(profile.ProfileEntity, MementoSettings); + profile.RedoStack.Push(memento); + profile.ProfileEntity = JsonConvert.DeserializeObject(top, MementoSettings); + + profile.ApplyToProfile(); + InstantiateProfile(profile); + } _logger.Debug("Undo profile update - Success"); return true; } - public bool RedoUpdateProfile(Profile profile, ProfileModule module) + public bool RedoUpdateProfile(Profile profile) { - if (!profile.RedoStack.Any()) + // Keep the profile from being rendered by locking it + lock (profile) { - _logger.Debug("Redo profile update - Failed, redo empty"); - return false; + if (!profile.RedoStack.Any()) + { + _logger.Debug("Redo profile update - Failed, redo empty"); + return false; + } + + var top = profile.RedoStack.Pop(); + var memento = JsonConvert.SerializeObject(profile.ProfileEntity, MementoSettings); + profile.UndoStack.Push(memento); + profile.ProfileEntity = JsonConvert.DeserializeObject(top, MementoSettings); + + profile.ApplyToProfile(); + InstantiateProfile(profile); + + _logger.Debug("Redo profile update - Success"); + return true; } - - ActivateProfile(module, null); - var top = profile.RedoStack.Pop(); - var memento = JsonConvert.SerializeObject(profile.ProfileEntity, MementoSettings); - profile.UndoStack.Push(memento); - profile.ProfileEntity = JsonConvert.DeserializeObject(top, MementoSettings); - profile.ApplyToProfile(); - ActivateProfile(module, profile); - - _logger.Debug("Redo profile update - Success"); - return true; } + public ProfileDescriptor GetLastActiveProfile(ProfileModule module) + { + var moduleProfiles = _profileRepository.GetByPluginGuid(module.PluginInfo.Guid); + var profileEntity = moduleProfiles.FirstOrDefault(p => p.IsActive) ?? moduleProfiles.FirstOrDefault(); + return profileEntity == null ? null : new ProfileDescriptor(module, profileEntity); + } + + public void InstantiateProfile(Profile profile) + { + profile.PopulateLeds(_surfaceService.ActiveSurface); + InitializeLayerProperties(profile); + InstantiateLayers(profile); + InstantiateFolders(profile); + } + + /// + /// Initializes the properties on the layers of the given profile + /// + /// private void InitializeLayerProperties(Profile profile) { foreach (var layer in profile.GetAllLayers()) @@ -167,6 +213,9 @@ namespace Artemis.Core.Services.Storage } } + /// + /// Instantiates all plugin-related classes on the folders of the given profile + /// private void InstantiateFolders(Profile profile) { foreach (var folder in profile.GetAllFolders()) @@ -182,6 +231,9 @@ namespace Artemis.Core.Services.Storage } } + /// + /// Instantiates all plugin-related classes on the layers of the given profile + /// private void InstantiateLayers(Profile profile) { foreach (var layer in profile.GetAllLayers()) @@ -204,6 +256,10 @@ namespace Artemis.Core.Services.Storage } } + /// + /// Populates all missing LEDs on all currently active profiles + /// + /// private void ActiveProfilesPopulateLeds(ArtemisSurface surface) { var profileModules = _pluginService.GetPluginsOfType(); @@ -211,6 +267,10 @@ namespace Artemis.Core.Services.Storage profileModule.ActiveProfile.PopulateLeds(surface); } + + /// + /// Instantiates all missing plugin-related classes on the profile trees of all currently active profiles + /// private void ActiveProfilesInstantiatePlugins() { var profileModules = _pluginService.GetPluginsOfType(); @@ -242,8 +302,8 @@ namespace Artemis.Core.Services.Storage ActiveProfilesInstantiatePlugins(); else if (e.PluginInfo.Instance is ProfileModule profileModule) { - var activeProfile = GetActiveProfile(profileModule); - ActivateProfile(profileModule, activeProfile); + var activeProfile = GetLastActiveProfile(profileModule); + ActivateProfile(activeProfile); } } diff --git a/src/Artemis.Core/Utilities/IntroAnimation.cs b/src/Artemis.Core/Utilities/IntroAnimation.cs new file mode 100644 index 000000000..d44860799 --- /dev/null +++ b/src/Artemis.Core/Utilities/IntroAnimation.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Artemis.Core.Extensions; +using Artemis.Core.Models.Profile; +using Artemis.Core.Models.Surface; +using Artemis.Core.Plugins.Abstract; +using Artemis.Core.Plugins.Abstract.ViewModels; +using Artemis.Core.Plugins.LayerBrush; +using Artemis.Core.Services.Interfaces; +using Artemis.Core.Services.Storage.Interfaces; +using Artemis.Storage.Entities.Profile; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Serilog; +using SkiaSharp; + +namespace Artemis.Core.Utilities +{ + internal class IntroAnimation + { + private readonly ILogger _logger; + private readonly IProfileService _profileService; + private readonly ISurfaceService _surfaceService; + + public IntroAnimation(ILogger logger, IProfileService profileService, ISurfaceService surfaceService) + { + _logger = logger; + _profileService = profileService; + _surfaceService = surfaceService; + CreateIntroProfile(); + } + + private void CreateIntroProfile() + { + try + { + // Load the intro profile from JSON into a ProfileEntity + var json = File.ReadAllText(Path.Combine(Constants.ApplicationFolder, "Resources", "intro-profile.json")); + var profileEntity = JsonConvert.DeserializeObject(json); + // Inject every LED on the surface into each layer + foreach (var profileEntityLayer in profileEntity.Layers) + { + profileEntityLayer.Leds.AddRange(_surfaceService.ActiveSurface.Devices.SelectMany(d => d.Leds).Select(l => new LedEntity() + { + DeviceIdentifier = l.Device.RgbDevice.GetDeviceIdentifier(), + LedName = l.RgbLed.Id.ToString(), + })); + } + + var profile = new Profile(new DummyModule(), profileEntity); + profile.Activate(_surfaceService.ActiveSurface); + + _profileService.InstantiateProfile(profile); + AnimationProfile = profile; + } + catch (Exception e) + { + _logger.Warning(e, "Failed to load intro profile"); + } + } + + public Profile AnimationProfile { get; set; } + + public void Render(double deltaTime, SKCanvas canvas, SKImageInfo bitmapInfo) + { + if (AnimationProfile == null) + return; + + AnimationProfile.Update(deltaTime); + AnimationProfile.Render(deltaTime, canvas, bitmapInfo); + } + } + + internal class DummyModule : ProfileModule + { + public override void EnablePlugin() + { + } + + public override void DisablePlugin() + { + } + + public override IEnumerable GetViewModels() + { + return new List(); + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs b/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs index 67f315c49..4c2fa7b65 100644 --- a/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs +++ b/src/Artemis.UI.Shared/Services/Interfaces/IProfileEditorService.cs @@ -24,9 +24,9 @@ namespace Artemis.UI.Shared.Services.Interfaces void ChangeSelectedProfileElement(RenderProfileElement profileElement); void UpdateSelectedProfileElement(); void UpdateProfilePreview(); - bool UndoUpdateProfile(ProfileModule module); - bool RedoUpdateProfile(ProfileModule module); - Module GetCurrentModule(); + bool UndoUpdateProfile(); + bool RedoUpdateProfile(); + ProfileModule GetCurrentModule(); /// /// Occurs when a new profile is selected diff --git a/src/Artemis.UI.Shared/Services/ProfileEditorService.cs b/src/Artemis.UI.Shared/Services/ProfileEditorService.cs index fb4c2bb02..ecbd0a28d 100644 --- a/src/Artemis.UI.Shared/Services/ProfileEditorService.cs +++ b/src/Artemis.UI.Shared/Services/ProfileEditorService.cs @@ -77,8 +77,9 @@ namespace Artemis.UI.Shared.Services var profileElementEvent = new ProfileEventArgs(profile, SelectedProfile); SelectedProfile = profile; - UpdateProfilePreview(); + OnSelectedProfileChanged(profileElementEvent); + UpdateProfilePreview(); } } @@ -88,8 +89,9 @@ namespace Artemis.UI.Shared.Services { _logger.Verbose("UpdateSelectedProfile {profile}", SelectedProfile); _profileService.UpdateProfile(SelectedProfile, true); - UpdateProfilePreview(); + OnSelectedProfileChanged(new ProfileEventArgs(SelectedProfile)); + UpdateProfilePreview(); } } @@ -132,13 +134,19 @@ namespace Artemis.UI.Shared.Services OnProfilePreviewUpdated(); } - public bool UndoUpdateProfile(ProfileModule module) + public bool UndoUpdateProfile() { - var undid = _profileService.UndoUpdateProfile(SelectedProfile, module); + var undid = _profileService.UndoUpdateProfile(SelectedProfile); if (!undid) return false; + if (SelectedProfileElement is Folder folder) + SelectedProfileElement = SelectedProfile.GetAllFolders().FirstOrDefault(f => f.EntityId == folder.EntityId); + else if (SelectedProfileElement is Layer layer) + SelectedProfileElement = SelectedProfile.GetAllLayers().FirstOrDefault(l => l.EntityId == layer.EntityId); + OnSelectedProfileChanged(new ProfileEventArgs(SelectedProfile, SelectedProfile)); + OnSelectedProfileElementChanged(new RenderProfileElementEventArgs(SelectedProfileElement)); if (SelectedProfileElement != null) { @@ -152,9 +160,9 @@ namespace Artemis.UI.Shared.Services return true; } - public bool RedoUpdateProfile(ProfileModule module) + public bool RedoUpdateProfile() { - var redid = _profileService.RedoUpdateProfile(SelectedProfile, module); + var redid = _profileService.RedoUpdateProfile(SelectedProfile); if (!redid) return false; @@ -248,9 +256,9 @@ namespace Artemis.UI.Shared.Services return time; } - public Module GetCurrentModule() + public ProfileModule GetCurrentModule() { - return (Module) SelectedProfile?.PluginInfo.Instance; + return SelectedProfile?.Module; } public event EventHandler ProfileSelected; diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionsView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionsView.xaml index 69087f4ce..b8b30d465 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionsView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionsView.xaml @@ -79,7 +79,7 @@ @@ -96,7 +96,7 @@ @@ -135,7 +135,7 @@ @@ -152,7 +152,7 @@ diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionsViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionsViewModel.cs index 8115186b5..b1195b782 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionsViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/DisplayConditions/DisplayConditionsViewModel.cs @@ -14,6 +14,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions private RenderProfileElement _renderProfileElement; private DisplayConditionGroupViewModel _rootGroup; private int _transitionerIndex; + private bool _displayContinuously; + private bool _alwaysFinishTimeline; public DisplayConditionsViewModel(IProfileEditorService profileEditorService, IDisplayConditionsVmFactory displayConditionsVmFactory) { @@ -39,15 +41,22 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions set => SetAndNotify(ref _renderProfileElement, value); } - public int ConditionBehaviourIndex + public bool DisplayContinuously { - get => RenderProfileElement != null && RenderProfileElement.AlwaysFinishTimeline ? 0 : 1; + get => _displayContinuously; set { - if (RenderProfileElement == null) - return; + if (!SetAndNotify(ref _displayContinuously, value)) return; + _profileEditorService.UpdateSelectedProfileElement(); + } + } - RenderProfileElement.AlwaysFinishTimeline = value == 0; + public bool AlwaysFinishTimeline + { + get => _alwaysFinishTimeline; + set + { + if (!SetAndNotify(ref _alwaysFinishTimeline, value)) return; _profileEditorService.UpdateSelectedProfileElement(); } } @@ -70,9 +79,13 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.DisplayConditions private void ProfileEditorServiceOnProfileElementSelected(object sender, RenderProfileElementEventArgs e) { RenderProfileElement = e.RenderProfileElement; - NotifyOfPropertyChange(nameof(ConditionBehaviourIndex)); NotifyOfPropertyChange(nameof(ConditionBehaviourEnabled)); + _displayContinuously = RenderProfileElement?.DisplayContinuously ?? false; + NotifyOfPropertyChange(nameof(DisplayContinuously)); + _alwaysFinishTimeline = RenderProfileElement?.AlwaysFinishTimeline ?? false; + NotifyOfPropertyChange(nameof(AlwaysFinishTimeline)); + if (e.RenderProfileElement == null) { RootGroup?.Dispose(); diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs index 4f9f1d7f3..0a846301d 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs @@ -209,7 +209,8 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties public List GetAllLayerPropertyGroupViewModels() { var groups = LayerPropertyGroups.ToList(); - groups.AddRange(groups.SelectMany(g => g.Children).Where(g => g is LayerPropertyGroupViewModel).Cast()); + var toAdd = groups.SelectMany(g => g.Children).Where(g => g is LayerPropertyGroupViewModel).Cast().ToList(); + groups.AddRange(toAdd); return groups; } diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyGroupViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyGroupViewModel.cs index c3f85b376..506fb3b80 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyGroupViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertyGroupViewModel.cs @@ -9,6 +9,7 @@ using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Abstract; using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Timeline; using Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.Tree; using Artemis.UI.Shared.Services.Interfaces; +using Humanizer; using Ninject; using Ninject.Parameters; @@ -42,6 +43,16 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties TreePropertyGroupViewModel = _layerPropertyVmFactory.TreePropertyGroupViewModel(this); TimelinePropertyGroupViewModel = _layerPropertyVmFactory.TimelinePropertyGroupViewModel(this); + // Generate a fallback name if the description does not contain one + if (PropertyGroupDescription.Name == null) + { + var propertyInfo = LayerPropertyGroup.Parent?.GetType().GetProperties().FirstOrDefault(p => ReferenceEquals(p.GetValue(LayerPropertyGroup.Parent), LayerPropertyGroup)); + if (propertyInfo != null) + PropertyGroupDescription.Name = propertyInfo.Name.Humanize(); + else + PropertyGroupDescription.Name = "Unknown group"; + } + LayerPropertyGroup.VisibilityChanged += LayerPropertyGroupOnVisibilityChanged; PopulateChildren(); DetermineType(); diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreePropertyGroupView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreePropertyGroupView.xaml index 55d53b651..6bd0b2e1f 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreePropertyGroupView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/Tree/TreePropertyGroupView.xaml @@ -21,7 +21,8 @@ Text="{Binding LayerPropertyGroupViewModel.PropertyGroupDescription.Name}" ToolTip="{Binding LayerPropertyGroupViewModel.PropertyGroupDescription.Description}" VerticalAlignment="Center" - Margin="0 5"> + HorizontalAlignment="Left" + Margin="3 5 0 5">