1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-31 09:43:46 +00:00

Added layer properties and keyframes to persistent storage

This commit is contained in:
SpoinkyNL 2020-01-08 21:54:58 +01:00
parent d1e0267709
commit e8570a6dd9
5 changed files with 143 additions and 63 deletions

View File

@ -264,22 +264,76 @@ namespace Artemis.Core.Models.Profile
CalculateRenderProperties(); CalculateRenderProperties();
} }
#region Properties internal void CalculateRenderProperties()
public void AddLayerProperty<T>(LayerProperty<T> layerProperty)
{ {
if (_properties.ContainsKey(layerProperty.Id)) if (!Leds.Any())
throw new ArtemisCoreException($"Duplicate property ID detected. Layer already contains a property with ID {layerProperty.Id}."); {
Rectangle = SKRect.Empty;
_properties.Add(layerProperty.Id, layerProperty); AbsoluteRectangle = SKRect.Empty;
Path = new SKPath();
OnRenderPropertiesUpdated();
return;
} }
public void AddLayerProperty(BaseLayerProperty layerProperty) // Determine to top-left and bottom-right
var minX = Leds.Min(l => l.AbsoluteRenderRectangle.Left);
var minY = Leds.Min(l => l.AbsoluteRenderRectangle.Top);
var maxX = Leds.Max(l => l.AbsoluteRenderRectangle.Right);
var maxY = Leds.Max(l => l.AbsoluteRenderRectangle.Bottom);
Rectangle = SKRect.Create(minX, minY, maxX - minX, maxY - minY);
AbsoluteRectangle = SKRect.Create(0, 0, maxX - minX, maxY - minY);
var path = new SKPath {FillType = SKPathFillType.Winding};
foreach (var artemisLed in Leds)
path.AddRect(artemisLed.AbsoluteRenderRectangle);
Path = path;
// This is called here so that the shape's render properties are up to date when other code
// responds to OnRenderPropertiesUpdated
LayerShape?.CalculateRenderProperties(PositionProperty.GetCurrentValue(), SizeProperty.GetCurrentValue());
OnRenderPropertiesUpdated();
}
public override string ToString()
{
return $"[Layer] {nameof(Name)}: {Name}, {nameof(Order)}: {Order}";
}
#region Properties
/// <summary>
/// Adds the provided layer property to the layer.
/// If found, the last stored base value and keyframes will be applied to the provided property.
/// </summary>
/// <typeparam name="T">The type of value of the layer property</typeparam>
/// <param name="layerProperty">The property to apply to the layer</param>
/// <returns>True if an existing value was found and applied, otherwise false.</returns>
public bool AddLayerProperty<T>(LayerProperty<T> layerProperty)
{
return AddLayerProperty((BaseLayerProperty) layerProperty);
}
/// <summary>
/// Adds the provided layer property to the layer.
/// If found, the last stored base value and keyframes will be applied to the provided property.
/// </summary>
/// <param name="layerProperty">The property to apply to the layer</param>
/// <returns>True if an existing value was found and applied, otherwise false.</returns>
public bool AddLayerProperty(BaseLayerProperty layerProperty)
{ {
if (_properties.ContainsKey(layerProperty.Id)) if (_properties.ContainsKey(layerProperty.Id))
throw new ArtemisCoreException($"Duplicate property ID detected. Layer already contains a property with ID {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); _properties.Add(layerProperty.Id, layerProperty);
return propertyEntity != null;
} }
/// <summary> /// <summary>
@ -320,44 +374,6 @@ namespace Artemis.Core.Models.Profile
#endregion #endregion
internal void CalculateRenderProperties()
{
if (!Leds.Any())
{
Rectangle = SKRect.Empty;
AbsoluteRectangle = SKRect.Empty;
Path = new SKPath();
OnRenderPropertiesUpdated();
return;
}
// Determine to top-left and bottom-right
var minX = Leds.Min(l => l.AbsoluteRenderRectangle.Left);
var minY = Leds.Min(l => l.AbsoluteRenderRectangle.Top);
var maxX = Leds.Max(l => l.AbsoluteRenderRectangle.Right);
var maxY = Leds.Max(l => l.AbsoluteRenderRectangle.Bottom);
Rectangle = SKRect.Create(minX, minY, maxX - minX, maxY - minY);
AbsoluteRectangle = SKRect.Create(0, 0, maxX - minX, maxY - minY);
var path = new SKPath {FillType = SKPathFillType.Winding};
foreach (var artemisLed in Leds)
path.AddRect(artemisLed.AbsoluteRenderRectangle);
Path = path;
// This is called here so that the shape's render properties are up to date when other code
// responds to OnRenderPropertiesUpdated
LayerShape?.CalculateRenderProperties(PositionProperty.GetCurrentValue(), SizeProperty.GetCurrentValue());
OnRenderPropertiesUpdated();
}
public override string ToString()
{
return $"[Layer] {nameof(Name)}: {Name}, {nameof(Order)}: {Order}";
}
#region Events #region Events
public event EventHandler RenderPropertiesUpdated; public event EventHandler RenderPropertiesUpdated;

View File

@ -4,7 +4,7 @@ namespace Artemis.Core.Models.Profile.LayerProperties
{ {
public class BaseKeyframe public class BaseKeyframe
{ {
protected BaseKeyframe(Layer layer, BaseLayerProperty property) protected internal BaseKeyframe(Layer layer, BaseLayerProperty property)
{ {
Layer = layer; Layer = layer;
BaseProperty = property; BaseProperty = property;
@ -14,6 +14,6 @@ namespace Artemis.Core.Models.Profile.LayerProperties
public TimeSpan Position { get; set; } public TimeSpan Position { get; set; }
protected BaseLayerProperty BaseProperty { get; } protected BaseLayerProperty BaseProperty { get; }
protected object BaseValue { get; set; } protected internal object BaseValue { get; set; }
} }
} }

View File

@ -1,7 +1,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using Artemis.Core.Exceptions; using Artemis.Core.Exceptions;
using Artemis.Core.Models.Profile.KeyframeEngines; using Artemis.Core.Models.Profile.KeyframeEngines;
using Artemis.Storage.Entities.Profile;
using Newtonsoft.Json;
namespace Artemis.Core.Models.Profile.LayerProperties namespace Artemis.Core.Models.Profile.LayerProperties
{ {
@ -9,7 +12,7 @@ namespace Artemis.Core.Models.Profile.LayerProperties
{ {
private object _baseValue; 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; Layer = layer;
Parent = parent; Parent = parent;
@ -78,7 +81,7 @@ namespace Artemis.Core.Models.Profile.LayerProperties
protected List<BaseKeyframe> BaseKeyframes { get; set; } protected List<BaseKeyframe> BaseKeyframes { get; set; }
protected object BaseValue protected internal object BaseValue
{ {
get => _baseValue; get => _baseValue;
set 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() public override string ToString()
@ -103,6 +133,13 @@ namespace Artemis.Core.Models.Profile.LayerProperties
return $"{nameof(Id)}: {Id}, {nameof(Name)}: {Name}, {nameof(Description)}: {Description}"; 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 #region Events
public event EventHandler<EventArgs> ValueChanged; public event EventHandler<EventArgs> ValueChanged;

View File

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing;
using LiteDB; using LiteDB;
namespace Artemis.Storage.Entities.Profile namespace Artemis.Storage.Entities.Profile
@ -10,6 +9,7 @@ namespace Artemis.Storage.Entities.Profile
public LayerEntity() public LayerEntity()
{ {
Leds = new List<LedEntity>(); Leds = new List<LedEntity>();
PropertyEntities = new List<PropertyEntity>();
Condition = new List<ProfileConditionEntity>(); Condition = new List<ProfileConditionEntity>();
} }
@ -20,6 +20,7 @@ namespace Artemis.Storage.Entities.Profile
public string Name { get; set; } public string Name { get; set; }
public List<LedEntity> Leds { get; set; } public List<LedEntity> Leds { get; set; }
public List<PropertyEntity> PropertyEntities { get; set; }
public List<ProfileConditionEntity> Condition { get; set; } public List<ProfileConditionEntity> Condition { get; set; }
public ShapeEntity ShapeEntity { get; set; } public ShapeEntity ShapeEntity { get; set; }
@ -27,6 +28,7 @@ namespace Artemis.Storage.Entities.Profile
[BsonRef("ProfileEntity")] [BsonRef("ProfileEntity")]
public ProfileEntity Profile { get; set; } public ProfileEntity Profile { get; set; }
public Guid ProfileId { get; set; } public Guid ProfileId { get; set; }
} }
} }

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
namespace Artemis.Storage.Entities.Profile
{
public class PropertyEntity
{
public PropertyEntity()
{
KeyframeEntities = new List<KeyframeEntity>();
}
public string Id { get; set; }
public string ValueType { get; set; }
public string Value { get; set; }
public List<KeyframeEntity> KeyframeEntities { get; set; }
}
public class KeyframeEntity
{
public TimeSpan Position { get; set; }
public string Value { get; set; }
}
}