From e8570a6dd9aaa49cf0a16bfee4ac789c1bede606 Mon Sep 17 00:00:00 2001 From: SpoinkyNL Date: Wed, 8 Jan 2020 21:54:58 +0100 Subject: [PATCH] Added layer properties and keyframes to persistent storage --- src/Artemis.Core/Models/Profile/Layer.cs | 128 ++++++++++-------- .../Profile/LayerProperties/BaseKeyframe.cs | 4 +- .../LayerProperties/BaseLayerProperty.cs | 45 +++++- .../Entities/Profile/LayerEntity.cs | 4 +- .../Entities/Profile/PropertyEntity.cs | 25 ++++ 5 files changed, 143 insertions(+), 63 deletions(-) create mode 100644 src/Artemis.Storage/Entities/Profile/PropertyEntity.cs diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs index 37346bcef..8139b97a9 100644 --- a/src/Artemis.Core/Models/Profile/Layer.cs +++ b/src/Artemis.Core/Models/Profile/Layer.cs @@ -264,62 +264,6 @@ namespace Artemis.Core.Models.Profile CalculateRenderProperties(); } - #region Properties - - public void AddLayerProperty(LayerProperty layerProperty) - { - if (_properties.ContainsKey(layerProperty.Id)) - throw new ArtemisCoreException($"Duplicate property ID detected. Layer already contains a property with ID {layerProperty.Id}."); - - _properties.Add(layerProperty.Id, layerProperty); - } - - public void AddLayerProperty(BaseLayerProperty layerProperty) - { - if (_properties.ContainsKey(layerProperty.Id)) - throw new ArtemisCoreException($"Duplicate property ID detected. Layer already contains a property with ID {layerProperty.Id}."); - - _properties.Add(layerProperty.Id, layerProperty); - } - - /// - /// If found, returns the matching the provided ID - /// - /// The type of the layer property - /// - /// - public LayerProperty GetLayerPropertyById(string id) - { - if (!_properties.ContainsKey(id)) - return null; - - var property = _properties[id]; - if (property.Type != typeof(T)) - throw new ArtemisCoreException($"Property type mismatch. Expected property {property} to have type {typeof(T)} but it has {property.Type} instead."); - return (LayerProperty) _properties[id]; - } - - private void CreateDefaultProperties() - { - var transformProperty = new LayerProperty(this, null, "Core.Transform", "Transform", "The default properties collection every layer has, allows you to transform the shape."); - 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 = "°"}; - OpacityProperty = new LayerProperty(this, transformProperty, "Core.Opacity", "Opacity", "The opacity of the shape.") {InputAffix = "%"}; - transformProperty.Children.Add(AnchorPointProperty); - transformProperty.Children.Add(PositionProperty); - transformProperty.Children.Add(SizeProperty); - transformProperty.Children.Add(RotationProperty); - transformProperty.Children.Add(OpacityProperty); - - AddLayerProperty(transformProperty); - foreach (var transformPropertyChild in transformProperty.Children) - AddLayerProperty(transformPropertyChild); - } - - #endregion - internal void CalculateRenderProperties() { if (!Leds.Any()) @@ -358,6 +302,78 @@ namespace Artemis.Core.Models.Profile return $"[Layer] {nameof(Name)}: {Name}, {nameof(Order)}: {Order}"; } + #region Properties + + /// + /// Adds the provided layer property to the layer. + /// If found, the last stored base value and keyframes will be applied to the provided property. + /// + /// The type of value of the layer property + /// The property to apply to the layer + /// True if an existing value was found and applied, otherwise false. + public bool AddLayerProperty(LayerProperty layerProperty) + { + return AddLayerProperty((BaseLayerProperty) layerProperty); + } + + /// + /// Adds the provided layer property to the layer. + /// If found, the last stored base value and keyframes will be applied to the provided property. + /// + /// The property to apply to the layer + /// True if an existing value was found and applied, otherwise false. + public bool AddLayerProperty(BaseLayerProperty layerProperty) + { + if (_properties.ContainsKey(layerProperty.Id)) + throw new ArtemisCoreException($"Duplicate property ID detected. Layer already contains a property with ID {layerProperty.Id}."); + + var propertyEntity = LayerEntity.PropertyEntities.FirstOrDefault(p => p.Id == layerProperty.Id && p.ValueType == layerProperty.Type.Name); + // TODO: Catch serialization exceptions and log them + if (propertyEntity != null) + layerProperty.ApplyToProperty(propertyEntity); + + _properties.Add(layerProperty.Id, layerProperty); + return propertyEntity != null; + } + + /// + /// If found, returns the matching the provided ID + /// + /// The type of the layer property + /// + /// + public LayerProperty GetLayerPropertyById(string id) + { + if (!_properties.ContainsKey(id)) + return null; + + var property = _properties[id]; + if (property.Type != typeof(T)) + throw new ArtemisCoreException($"Property type mismatch. Expected property {property} to have type {typeof(T)} but it has {property.Type} instead."); + return (LayerProperty) _properties[id]; + } + + private void CreateDefaultProperties() + { + var transformProperty = new LayerProperty(this, null, "Core.Transform", "Transform", "The default properties collection every layer has, allows you to transform the shape."); + 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 = "°"}; + OpacityProperty = new LayerProperty(this, transformProperty, "Core.Opacity", "Opacity", "The opacity of the shape.") {InputAffix = "%"}; + transformProperty.Children.Add(AnchorPointProperty); + transformProperty.Children.Add(PositionProperty); + transformProperty.Children.Add(SizeProperty); + transformProperty.Children.Add(RotationProperty); + transformProperty.Children.Add(OpacityProperty); + + AddLayerProperty(transformProperty); + foreach (var transformPropertyChild in transformProperty.Children) + AddLayerProperty(transformPropertyChild); + } + + #endregion + #region Events public event EventHandler RenderPropertiesUpdated; diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/BaseKeyframe.cs b/src/Artemis.Core/Models/Profile/LayerProperties/BaseKeyframe.cs index 59639f372..ddb571051 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/BaseKeyframe.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/BaseKeyframe.cs @@ -4,7 +4,7 @@ namespace Artemis.Core.Models.Profile.LayerProperties { public class BaseKeyframe { - protected BaseKeyframe(Layer layer, BaseLayerProperty property) + protected internal BaseKeyframe(Layer layer, BaseLayerProperty property) { Layer = layer; BaseProperty = property; @@ -14,6 +14,6 @@ namespace Artemis.Core.Models.Profile.LayerProperties public TimeSpan Position { get; set; } protected BaseLayerProperty BaseProperty { get; } - protected object BaseValue { get; set; } + protected internal object BaseValue { get; set; } } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/BaseLayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/BaseLayerProperty.cs index b48bdbf60..d5a3fc074 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/BaseLayerProperty.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/BaseLayerProperty.cs @@ -1,7 +1,10 @@ using System; using System.Collections.Generic; +using System.Linq; using Artemis.Core.Exceptions; using Artemis.Core.Models.Profile.KeyframeEngines; +using Artemis.Storage.Entities.Profile; +using Newtonsoft.Json; namespace Artemis.Core.Models.Profile.LayerProperties { @@ -9,7 +12,7 @@ namespace Artemis.Core.Models.Profile.LayerProperties { private object _baseValue; - protected BaseLayerProperty(Layer layer, BaseLayerProperty parent, string id, string name, string description, Type type) + protected internal BaseLayerProperty(Layer layer, BaseLayerProperty parent, string id, string name, string description, Type type) { Layer = layer; Parent = parent; @@ -78,7 +81,7 @@ namespace Artemis.Core.Models.Profile.LayerProperties protected List BaseKeyframes { get; set; } - protected object BaseValue + protected internal object BaseValue { get => _baseValue; set @@ -93,9 +96,36 @@ namespace Artemis.Core.Models.Profile.LayerProperties } } - public void ApplyToEntity() + internal void ApplyToEntity() { - // Big o' TODO + var propertyEntity = Layer.LayerEntity.PropertyEntities.FirstOrDefault(p => p.Id == Id); + if (propertyEntity == null) + { + propertyEntity = new PropertyEntity {Id = Id}; + Layer.LayerEntity.PropertyEntities.Add(propertyEntity); + } + + propertyEntity.ValueType = Type.Name; + propertyEntity.Value = JsonConvert.SerializeObject(BaseValue); + + propertyEntity.KeyframeEntities.Clear(); + foreach (var baseKeyframe in BaseKeyframes) + { + propertyEntity.KeyframeEntities.Add(new KeyframeEntity + { + Position = baseKeyframe.Position, + Value = JsonConvert.SerializeObject(baseKeyframe.BaseValue) + }); + } + } + + internal void ApplyToProperty(PropertyEntity propertyEntity) + { + BaseValue = DeserializePropertyValue(propertyEntity.Value); + + BaseKeyframes.Clear(); + foreach (var keyframeEntity in propertyEntity.KeyframeEntities) + BaseKeyframes.Add(new BaseKeyframe(Layer, this) {BaseValue = DeserializePropertyValue(keyframeEntity.Value)}); } public override string ToString() @@ -103,6 +133,13 @@ namespace Artemis.Core.Models.Profile.LayerProperties return $"{nameof(Id)}: {Id}, {nameof(Name)}: {Name}, {nameof(Description)}: {Description}"; } + private object DeserializePropertyValue(string value) + { + if (value == "null") + return Type.IsValueType ? Activator.CreateInstance(Type) : null; + return JsonConvert.DeserializeObject(value, Type); + } + #region Events public event EventHandler ValueChanged; diff --git a/src/Artemis.Storage/Entities/Profile/LayerEntity.cs b/src/Artemis.Storage/Entities/Profile/LayerEntity.cs index 5b49bb2a0..a67b54b44 100644 --- a/src/Artemis.Storage/Entities/Profile/LayerEntity.cs +++ b/src/Artemis.Storage/Entities/Profile/LayerEntity.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Drawing; using LiteDB; namespace Artemis.Storage.Entities.Profile @@ -10,6 +9,7 @@ namespace Artemis.Storage.Entities.Profile public LayerEntity() { Leds = new List(); + PropertyEntities = new List(); Condition = new List(); } @@ -20,6 +20,7 @@ namespace Artemis.Storage.Entities.Profile public string Name { get; set; } public List Leds { get; set; } + public List PropertyEntities { get; set; } public List Condition { get; set; } public ShapeEntity ShapeEntity { get; set; } @@ -27,6 +28,7 @@ namespace Artemis.Storage.Entities.Profile [BsonRef("ProfileEntity")] public ProfileEntity Profile { get; set; } + public Guid ProfileId { get; set; } } } \ No newline at end of file diff --git a/src/Artemis.Storage/Entities/Profile/PropertyEntity.cs b/src/Artemis.Storage/Entities/Profile/PropertyEntity.cs new file mode 100644 index 000000000..90e56a3c2 --- /dev/null +++ b/src/Artemis.Storage/Entities/Profile/PropertyEntity.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; + +namespace Artemis.Storage.Entities.Profile +{ + public class PropertyEntity + { + public PropertyEntity() + { + KeyframeEntities = new List(); + } + + public string Id { get; set; } + public string ValueType { get; set; } + public string Value { get; set; } + + public List KeyframeEntities { get; set; } + } + + public class KeyframeEntity + { + public TimeSpan Position { get; set; } + public string Value { get; set; } + } +} \ No newline at end of file