From 9b1d28840cf168d0eaba309d2252125253cd1098 Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 11 Feb 2020 19:10:31 +0100 Subject: [PATCH] Refactored shape brushes to use the properties system --- src/Artemis.Core/Artemis.Core.csproj | 4 +- src/Artemis.Core/Constants.cs | 2 + .../KeyframeEngines/SKColorKeyframeEngine.cs | 31 ++ src/Artemis.Core/Models/Profile/Layer.cs | 276 ++++++++++-------- .../Models/Profile/LayerBrushReference.cs | 21 ++ .../LayerProperties/BaseLayerProperty.cs | 15 +- .../Profile/LayerProperties/LayerProperty.cs | 63 +++- .../Plugins/LayerBrush/LayerBrush.cs | 44 ++- .../Plugins/LayerBrush/LayerBrushSettings.cs | 18 -- .../Plugins/LayerBrush/LayerBrushViewModel.cs | 14 - src/Artemis.Core/RGB.NET/BitmapBrush.cs | 5 - .../Services/Interfaces/ILayerService.cs | 4 +- src/Artemis.Core/Services/LayerService.cs | 67 ++--- src/Artemis.Core/Services/SettingsService.cs | 3 +- .../Services/Storage/ProfileService.cs | 15 +- .../Artemis.Plugins.LayerBrushes.Color.csproj | 8 - .../ColorBrush.cs | 44 ++- .../ColorBrushSettings.cs | 46 --- .../ColorBrushView.xaml | 134 --------- .../ColorBrushViewModel.cs | 17 -- .../Artemis.Plugins.LayerBrushes.Noise.csproj | 10 +- .../NoiseBrush.cs | 31 +- .../NoiseBrushSettings.cs | 44 --- .../NoiseBrushView.xaml | 140 --------- .../NoiseBrushViewModel.cs | 18 -- .../Entities/Profile/LayerEntity.cs | 2 - src/Artemis.UI.Shared/ColorPicker.xaml | 15 +- src/Artemis.UI/Artemis.UI.csproj | 16 + .../LayerPropertiesView.xaml.cs | 6 +- .../LayerPropertiesViewModel.cs | 1 + .../PropertyInput/BrushPropertyInputView.xaml | 28 ++ .../BrushPropertyInputView.xaml.cs | 28 ++ .../BrushPropertyInputViewModel.cs | 68 +++++ .../PropertyInput/EnumPropertyInputView.xaml | 5 - .../EnumPropertyInputView.xaml.cs | 5 - .../SKColorPropertyInputView.xaml | 25 ++ .../SKColorPropertyInputView.xaml.cs | 28 ++ .../SKColorPropertyInputViewModel.cs | 27 ++ .../PropertyTreeChildViewModel.cs | 4 +- .../Visualization/ProfileView.xaml | 4 +- 40 files changed, 650 insertions(+), 686 deletions(-) create mode 100644 src/Artemis.Core/Models/Profile/KeyframeEngines/SKColorKeyframeEngine.cs create mode 100644 src/Artemis.Core/Models/Profile/LayerBrushReference.cs delete mode 100644 src/Artemis.Core/Plugins/LayerBrush/LayerBrushSettings.cs delete mode 100644 src/Artemis.Core/Plugins/LayerBrush/LayerBrushViewModel.cs delete mode 100644 src/Artemis.Plugins.LayerBrushes.Color/ColorBrushSettings.cs delete mode 100644 src/Artemis.Plugins.LayerBrushes.Color/ColorBrushView.xaml delete mode 100644 src/Artemis.Plugins.LayerBrushes.Color/ColorBrushViewModel.cs delete mode 100644 src/Artemis.Plugins.LayerBrushes.Noise/NoiseBrushSettings.cs delete mode 100644 src/Artemis.Plugins.LayerBrushes.Noise/NoiseBrushView.xaml delete mode 100644 src/Artemis.Plugins.LayerBrushes.Noise/NoiseBrushViewModel.cs create mode 100644 src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/BrushPropertyInputView.xaml create mode 100644 src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/BrushPropertyInputView.xaml.cs create mode 100644 src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/BrushPropertyInputViewModel.cs create mode 100644 src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKColorPropertyInputView.xaml create mode 100644 src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKColorPropertyInputView.xaml.cs create mode 100644 src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKColorPropertyInputViewModel.cs diff --git a/src/Artemis.Core/Artemis.Core.csproj b/src/Artemis.Core/Artemis.Core.csproj index def2607a6..9a4159779 100644 --- a/src/Artemis.Core/Artemis.Core.csproj +++ b/src/Artemis.Core/Artemis.Core.csproj @@ -152,8 +152,10 @@ + + @@ -178,8 +180,6 @@ - - diff --git a/src/Artemis.Core/Constants.cs b/src/Artemis.Core/Constants.cs index 6f55fa4d1..6429f74ed 100644 --- a/src/Artemis.Core/Constants.cs +++ b/src/Artemis.Core/Constants.cs @@ -1,4 +1,5 @@ using System; +using Artemis.Core.Plugins.Models; namespace Artemis.Core { @@ -6,5 +7,6 @@ namespace Artemis.Core { public static readonly string DataFolder = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "\\Artemis\\"; public static readonly string ConnectionString = $"FileName={DataFolder}\\database.db;Mode=Exclusive"; + public static readonly PluginInfo CorePluginInfo = new PluginInfo {Guid = Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"), Name = "Artemis Core"}; } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/KeyframeEngines/SKColorKeyframeEngine.cs b/src/Artemis.Core/Models/Profile/KeyframeEngines/SKColorKeyframeEngine.cs new file mode 100644 index 000000000..9e427d237 --- /dev/null +++ b/src/Artemis.Core/Models/Profile/KeyframeEngines/SKColorKeyframeEngine.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using Artemis.Core.Models.Profile.LayerProperties; +using SkiaSharp; + +namespace Artemis.Core.Models.Profile.KeyframeEngines +{ + /// + public class SKColorKeyframeEngine : KeyframeEngine + { + public sealed override List CompatibleTypes { get; } = new List {typeof(SKColor)}; + + protected override object GetInterpolatedValue() + { + var currentKeyframe = (Keyframe) CurrentKeyframe; + var nextKeyframe = (Keyframe) NextKeyframe; + + var redDiff = nextKeyframe.Value.Red - currentKeyframe.Value.Red; + var greenDiff = nextKeyframe.Value.Green - currentKeyframe.Value.Green; + var blueDiff = nextKeyframe.Value.Blue - currentKeyframe.Value.Blue; + var alphaDiff = nextKeyframe.Value.Alpha - currentKeyframe.Value.Alpha; + + return new SKColor( + (byte) (currentKeyframe.Value.Red + redDiff * KeyframeProgressEased), + (byte) (currentKeyframe.Value.Green + greenDiff * KeyframeProgressEased), + (byte) (currentKeyframe.Value.Blue + blueDiff * KeyframeProgressEased), + (byte)(currentKeyframe.Value.Alpha + alphaDiff * KeyframeProgressEased) + ); + } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs index 64487e9a0..81b2d5b8b 100644 --- a/src/Artemis.Core/Models/Profile/Layer.cs +++ b/src/Artemis.Core/Models/Profile/Layer.cs @@ -8,15 +8,18 @@ using Artemis.Core.Models.Profile.LayerProperties; using Artemis.Core.Models.Profile.LayerShapes; using Artemis.Core.Models.Surface; using Artemis.Core.Plugins.LayerBrush; +using Artemis.Core.Plugins.Models; using Artemis.Storage.Entities.Profile; using Newtonsoft.Json; +using Ninject; +using Ninject.Parameters; using SkiaSharp; namespace Artemis.Core.Models.Profile { public sealed class Layer : ProfileElement { - private readonly Dictionary _properties; + private readonly Dictionary<(Guid, string), BaseLayerProperty> _properties; private LayerShape _layerShape; private List _leds; private SKPath _path; @@ -31,12 +34,12 @@ namespace Artemis.Core.Models.Profile Name = name; _leds = new List(); - _properties = new Dictionary(); + _properties = new Dictionary<(Guid, string), BaseLayerProperty>(); CreateDefaultProperties(); - CreateShapeType(); + ApplyShapeType(); - ShapeTypeProperty.ValueChanged += (sender, args) => CreateShapeType(); + ShapeTypeProperty.ValueChanged += (sender, args) => ApplyShapeType(); } internal Layer(Profile profile, ProfileElement parent, LayerEntity layerEntity) @@ -50,27 +53,12 @@ namespace Artemis.Core.Models.Profile Order = layerEntity.Order; _leds = new List(); - _properties = new Dictionary(); + _properties = new Dictionary<(Guid, string), BaseLayerProperty>(); CreateDefaultProperties(); - CreateShapeType(); + ApplyShapeType(); - ShapeTypeProperty.ValueChanged += (sender, args) => CreateShapeType(); - } - - private void CreateShapeType() - { - switch (ShapeTypeProperty.CurrentValue) - { - case LayerShapeType.Ellipse: - LayerShape = new Ellipse(this); - break; - case LayerShapeType.Rectangle: - LayerShape = new Rectangle(this); - break; - default: - throw new ArgumentOutOfRangeException(); - } + ShapeTypeProperty.ValueChanged += (sender, args) => ApplyShapeType(); } internal LayerEntity LayerEntity { get; set; } @@ -126,6 +114,8 @@ namespace Artemis.Core.Models.Profile public LayerProperty BlendModeProperty { get; set; } + public LayerProperty BrushReferenceProperty { get; set; } + /// /// The anchor point property of this layer, also found in /// @@ -156,6 +146,39 @@ namespace Artemis.Core.Models.Profile /// public LayerBrush LayerBrush { get; internal set; } + #region Storage + + internal override void ApplyToEntity() + { + // Properties + LayerEntity.Id = EntityId; + LayerEntity.ParentId = Parent?.EntityId ?? new Guid(); + LayerEntity.Order = Order; + LayerEntity.Name = Name; + LayerEntity.ProfileId = Profile.EntityId; + foreach (var layerProperty in Properties) + layerProperty.ApplyToEntity(); + + // LEDs + LayerEntity.Leds.Clear(); + foreach (var artemisLed in Leds) + { + var ledEntity = new LedEntity + { + DeviceHash = artemisLed.Device.RgbDevice.GetDeviceHashCode(), + LedName = artemisLed.RgbLed.Id.ToString() + }; + LayerEntity.Leds.Add(ledEntity); + } + + // Conditions TODO + LayerEntity.Condition.Clear(); + } + + #endregion + + #region Rendering + public override void Update(double deltaTime) { foreach (var property in Properties) @@ -276,6 +299,29 @@ namespace Artemis.Core.Models.Profile LayerBrush?.Render(canvas, LayerShape.Path, paint); } + internal void CalculateRenderProperties() + { + if (!Leds.Any()) + { + Path = new SKPath(); + + LayerShape?.CalculateRenderProperties(); + OnRenderPropertiesUpdated(); + return; + } + + 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(); + OnRenderPropertiesUpdated(); + } + private SKPoint GetLayerAnchorPosition() { var positionProperty = PositionProperty.CurrentValue; @@ -290,43 +336,9 @@ namespace Artemis.Core.Models.Profile return position; } - internal override void ApplyToEntity() - { - // Properties - LayerEntity.Id = EntityId; - LayerEntity.ParentId = Parent?.EntityId ?? new Guid(); - LayerEntity.Order = Order; - LayerEntity.Name = Name; - LayerEntity.ProfileId = Profile.EntityId; - foreach (var layerProperty in Properties) - layerProperty.ApplyToEntity(); + #endregion - // LEDs - LayerEntity.Leds.Clear(); - foreach (var artemisLed in Leds) - { - var ledEntity = new LedEntity - { - DeviceHash = artemisLed.Device.RgbDevice.GetDeviceHashCode(), - LedName = artemisLed.RgbLed.Id.ToString() - }; - LayerEntity.Leds.Add(ledEntity); - } - - // Conditions TODO - LayerEntity.Condition.Clear(); - - // Brush - if (LayerBrush != null) - { - LayerEntity.BrushEntity = new BrushEntity - { - BrushPluginGuid = LayerBrush.Descriptor.LayerBrushProvider.PluginInfo.Guid, - BrushType = LayerBrush.GetType().Name, - Configuration = JsonConvert.SerializeObject(LayerBrush.Settings) - }; - } - } + #region LED management /// /// Adds a new to the layer and updates the render properties. @@ -385,47 +397,39 @@ namespace Artemis.Core.Models.Profile CalculateRenderProperties(); } - internal void CalculateRenderProperties() + #endregion + + #region Shape management + + private void ApplyShapeType() { - if (!Leds.Any()) + switch (ShapeTypeProperty.CurrentValue) { - Path = new SKPath(); - - LayerShape?.CalculateRenderProperties(); - OnRenderPropertiesUpdated(); - return; + case LayerShapeType.Ellipse: + LayerShape = new Ellipse(this); + break; + case LayerShapeType.Rectangle: + LayerShape = new Rectangle(this); + break; + default: + throw new ArgumentOutOfRangeException(); } - - 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(); - OnRenderPropertiesUpdated(); } - - public override string ToString() - { - return $"[Layer] {nameof(Name)}: {Name}, {nameof(Order)}: {Order}"; - } + #endregion #region Properties /// - /// Adds the provided layer property to the layer. + /// Adds the provided layer property and its children 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) + public bool RegisterLayerProperty(LayerProperty layerProperty) { - return AddLayerProperty((BaseLayerProperty) layerProperty); + return RegisterLayerProperty((BaseLayerProperty) layerProperty); } /// @@ -434,9 +438,9 @@ namespace Artemis.Core.Models.Profile /// /// The property to apply to the layer /// True if an existing value was found and applied, otherwise false. - public bool AddLayerProperty(BaseLayerProperty layerProperty) + public bool RegisterLayerProperty(BaseLayerProperty layerProperty) { - if (_properties.ContainsKey(layerProperty.Id)) + if (_properties.ContainsKey((layerProperty.PluginInfo.Guid, 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); @@ -444,66 +448,91 @@ namespace Artemis.Core.Models.Profile if (propertyEntity != null) layerProperty.ApplyToProperty(propertyEntity); - _properties.Add(layerProperty.Id, layerProperty); + _properties.Add((layerProperty.PluginInfo.Guid, layerProperty.Id), layerProperty); + OnLayerPropertyRegistered(); return propertyEntity != null; } + /// + /// Removes the provided layer property from the layer. + /// + /// The type of value of the layer property + /// The property to remove from the layer + public void RemoveLayerProperty(LayerProperty layerProperty) + { + RemoveLayerProperty((BaseLayerProperty) layerProperty); + } + + /// + /// Removes the provided layer property from the layer. + /// + /// The property to remove from the layer + public void RemoveLayerProperty(BaseLayerProperty layerProperty) + { + if (!_properties.ContainsKey((layerProperty.PluginInfo.Guid, layerProperty.Id))) + throw new ArtemisCoreException($"Could not find a property with ID {layerProperty.Id}."); + + var property = _properties[(layerProperty.PluginInfo.Guid, layerProperty.Id)]; + property.Parent?.Children.Remove(property); + _properties.Remove((layerProperty.PluginInfo.Guid, layerProperty.Id)); + + OnLayerPropertyRemoved(); + } + /// /// If found, returns the matching the provided ID /// /// The type of the layer property + /// The plugin this property belongs to /// /// - public LayerProperty GetLayerPropertyById(string id) + public LayerProperty GetLayerPropertyById(PluginInfo pluginInfo, string id) { - if (!_properties.ContainsKey(id)) + if (!_properties.ContainsKey((pluginInfo.Guid, id))) return null; - var property = _properties[id]; + var property = _properties[(pluginInfo.Guid, 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]; + return (LayerProperty) _properties[(pluginInfo.Guid, id)]; } private void CreateDefaultProperties() { - var shape = new LayerProperty(this, null, "Core.Shape", "Shape", "A collection of basic shape properties."); + // Shape + var shape = new LayerProperty(this, "Core.Shape", "Shape", "A collection of basic shape properties."); ShapeTypeProperty = new LayerProperty(this, shape, "Core.ShapeType", "Shape type", "The type of shape to draw in this layer.") {CanUseKeyframes = false}; FillTypeProperty = new LayerProperty(this, shape, "Core.FillType", "Fill type", "How to make the shape adjust to scale changes.") {CanUseKeyframes = false}; BlendModeProperty = new LayerProperty(this, shape, "Core.BlendMode", "Blend mode", "How to blend this layer into the resulting image.") {CanUseKeyframes = false}; - shape.Children.Add(ShapeTypeProperty); - shape.Children.Add(FillTypeProperty); - shape.Children.Add(BlendModeProperty); + ShapeTypeProperty.Value = LayerShapeType.Rectangle; + FillTypeProperty.Value = LayerFillType.Stretch; + BlendModeProperty.Value = SKBlendMode.SrcOver; - var transform = new LayerProperty(this, null, "Core.Transform", "Transform", "A collection of transformation properties.") {ExpandByDefault = true}; + RegisterLayerProperty(shape); + foreach (var shapeProperty in shape.Children) + RegisterLayerProperty(shapeProperty); + + // Brush + var brush = new LayerProperty(this, "Core.Brush", "Brush", "A collection of properties that configure the selected brush."); + BrushReferenceProperty = new LayerProperty(this, brush, "Core.BrushReference", "Brush type", "The type of brush to use for this layer.") {CanUseKeyframes = false}; + + RegisterLayerProperty(brush); + foreach (var brushProperty in brush.Children) + RegisterLayerProperty(brushProperty); + + // Transform + var transform = new LayerProperty(this, "Core.Transform", "Transform", "A collection of transformation properties.") {ExpandByDefault = true}; AnchorPointProperty = new LayerProperty(this, transform, "Core.AnchorPoint", "Anchor Point", "The point at which the shape is attached to its position."); PositionProperty = new LayerProperty(this, transform, "Core.Position", "Position", "The position of the shape."); ScaleProperty = new LayerProperty(this, transform, "Core.Scale", "Scale", "The scale of the shape.") {InputAffix = "%"}; RotationProperty = new LayerProperty(this, transform, "Core.Rotation", "Rotation", "The rotation of the shape in degrees.") {InputAffix = "°"}; OpacityProperty = new LayerProperty(this, transform, "Core.Opacity", "Opacity", "The opacity of the shape.") {InputAffix = "%"}; - transform.Children.Add(AnchorPointProperty); - transform.Children.Add(PositionProperty); - transform.Children.Add(ScaleProperty); - transform.Children.Add(RotationProperty); - - // Set default values - ShapeTypeProperty.Value = LayerShapeType.Rectangle; - FillTypeProperty.Value = LayerFillType.Stretch; - BlendModeProperty.Value = SKBlendMode.SrcOver; - ScaleProperty.Value = new SKSize(100, 100); OpacityProperty.Value = 100; - - transform.Children.Add(OpacityProperty); - - AddLayerProperty(shape); - foreach (var shapeProperty in shape.Children) - AddLayerProperty(shapeProperty); - - AddLayerProperty(transform); + RegisterLayerProperty(transform); foreach (var transformProperty in transform.Children) - AddLayerProperty(transformProperty); + RegisterLayerProperty(transformProperty); } #endregion @@ -512,6 +541,8 @@ namespace Artemis.Core.Models.Profile public event EventHandler RenderPropertiesUpdated; public event EventHandler ShapePropertiesUpdated; + public event EventHandler LayerPropertyRegistered; + public event EventHandler LayerPropertyRemoved; private void OnRenderPropertiesUpdated() { @@ -524,6 +555,21 @@ namespace Artemis.Core.Models.Profile } #endregion + + public override string ToString() + { + return $"[Layer] {nameof(Name)}: {Name}, {nameof(Order)}: {Order}"; + } + + private void OnLayerPropertyRegistered() + { + LayerPropertyRegistered?.Invoke(this, EventArgs.Empty); + } + + private void OnLayerPropertyRemoved() + { + LayerPropertyRemoved?.Invoke(this, EventArgs.Empty); + } } public enum LayerShapeType diff --git a/src/Artemis.Core/Models/Profile/LayerBrushReference.cs b/src/Artemis.Core/Models/Profile/LayerBrushReference.cs new file mode 100644 index 000000000..2e1930f0f --- /dev/null +++ b/src/Artemis.Core/Models/Profile/LayerBrushReference.cs @@ -0,0 +1,21 @@ +using System; +using Artemis.Core.Plugins.LayerBrush; + +namespace Artemis.Core.Models.Profile +{ + /// + /// A reference to a + /// + public class LayerBrushReference + { + /// + /// The GUID of the plugin the brush descriptor resides in + /// + public Guid BrushPluginGuid { get; set; } + + /// + /// The full type name of the brush descriptor + /// + public string BrushType { 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 7e279c61b..1dabc6f97 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/BaseLayerProperty.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/BaseLayerProperty.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Artemis.Core.Exceptions; using Artemis.Core.Models.Profile.KeyframeEngines; +using Artemis.Core.Plugins.Models; using Artemis.Core.Utilities; using Artemis.Storage.Entities.Profile; using Newtonsoft.Json; @@ -13,9 +14,10 @@ namespace Artemis.Core.Models.Profile.LayerProperties { private object _baseValue; - protected BaseLayerProperty(Layer layer, BaseLayerProperty parent, string id, string name, string description, Type type) + protected BaseLayerProperty(Layer layer, PluginInfo pluginInfo, BaseLayerProperty parent, string id, string name, string description, Type type) { Layer = layer; + PluginInfo = pluginInfo; Parent = parent; Id = id; Name = name; @@ -23,8 +25,14 @@ namespace Artemis.Core.Models.Profile.LayerProperties Type = type; CanUseKeyframes = true; + // This can only be null if accessed internally + if (PluginInfo == null) + PluginInfo = Constants.CorePluginInfo; + Children = new List(); BaseKeyframes = new List(); + + parent?.Children.Add(this); } /// @@ -32,6 +40,11 @@ namespace Artemis.Core.Models.Profile.LayerProperties /// public Layer Layer { get; } + /// + /// Info of the plugin associated with this property + /// + public PluginInfo PluginInfo { get; } + /// /// Gets the parent property of this property. /// diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs index 8eb2c16a9..9745b53ad 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs @@ -1,14 +1,73 @@ -using System.Collections.ObjectModel; +using System; +using System.Collections.ObjectModel; using System.Linq; +using Artemis.Core.Plugins.Models; namespace Artemis.Core.Models.Profile.LayerProperties { + /// + /// Represents a property on the layer. This property is visible in the profile editor and can be key-framed (unless + /// opted out) + /// + /// public class LayerProperty : BaseLayerProperty { - public LayerProperty(Layer layer, BaseLayerProperty parent, string id, string name, string description) : base(layer, parent, id, name, description, typeof(T)) + internal LayerProperty(Layer layer, BaseLayerProperty parent, string id, string name, string description) : base(layer, null, parent, id, name, description, typeof(T)) { } + internal LayerProperty(Layer layer, string id, string name, string description) : base(layer, null, null, id, name, description, typeof(T)) + { + } + + /// + /// Represents a property on the layer. This property is visible in the profile editor and can be key-framed (unless + /// opted out) + /// + /// Note: The value and keyframes of the property are stored using the ID, after adding the property to the layer + /// these are restored. + /// + /// + /// The layer the property is applied to + /// The plugin to create this property for + /// The parent of this property, use this to create a tree-hierarchy in the editor + /// A and ID identifying your property, must be unique within your plugin + /// A name for your property, this is visible in the editor + /// A description for your property, this is visible in the editor + public LayerProperty(Layer layer, PluginInfo pluginInfo, BaseLayerProperty parent, string id, string name, string description) : base(layer, pluginInfo, parent, id, name, description, + typeof(T)) + { + if (layer == null) + throw new ArgumentNullException(nameof(layer)); + if (pluginInfo == null) + throw new ArgumentNullException(nameof(pluginInfo)); + if (id == null) + throw new ArgumentNullException(nameof(id)); + } + + /// + /// Represents a property on the layer. This property is visible in the profile editor and can be key-framed (unless + /// opted out) + /// + /// Note: The value and keyframes of the property are stored using the ID, after adding the property to the layer + /// these are restored. + /// + /// + /// The layer the property is applied to + /// The plugin to create this property for + /// A and ID identifying your property, must be unique within your plugin + /// A name for your property, this is visible in the editor + /// A description for your property, this is visible in the editor + public LayerProperty(Layer layer, PluginInfo pluginInfo, string id, string name, string description) : base(layer, pluginInfo, null, id, name, description, typeof(T)) + { + if (layer == null) + throw new ArgumentNullException(nameof(layer)); + if (pluginInfo == null) + throw new ArgumentNullException(nameof(pluginInfo)); + if (id == null) + throw new ArgumentNullException(nameof(id)); + } + /// /// Gets or sets the value of the property without any keyframes applied /// diff --git a/src/Artemis.Core/Plugins/LayerBrush/LayerBrush.cs b/src/Artemis.Core/Plugins/LayerBrush/LayerBrush.cs index f4bdd6a2e..cf1d32cf5 100644 --- a/src/Artemis.Core/Plugins/LayerBrush/LayerBrush.cs +++ b/src/Artemis.Core/Plugins/LayerBrush/LayerBrush.cs @@ -1,32 +1,25 @@ using System; using Artemis.Core.Models.Profile; +using Artemis.Core.Models.Profile.LayerProperties; using SkiaSharp; namespace Artemis.Core.Plugins.LayerBrush { public abstract class LayerBrush : IDisposable { - protected LayerBrush(Layer layer, LayerBrushSettings settings, LayerBrushDescriptor descriptor) + protected LayerBrush(Layer layer, LayerBrushDescriptor descriptor) { Layer = layer; - Settings = settings; Descriptor = descriptor; } public Layer Layer { get; } - public LayerBrushSettings Settings { get; } public LayerBrushDescriptor Descriptor { get; } public virtual void Dispose() { } - /// - /// Called by the profile editor to populate the brush properties panel - /// - /// - public abstract LayerBrushViewModel GetViewModel(); - /// /// Called before rendering every frame, write your update logic here /// @@ -46,5 +39,38 @@ namespace Artemis.Core.Plugins.LayerBrush public virtual void Render(SKCanvas canvas, SKPath path, SKPaint paint) { } + + /// + /// Provides an easy way to add your own properties to the layer, for more info see . + /// Note: If found, the last value and keyframes are loaded and set when calling this method. + /// + /// + /// The parent of this property, use this to create a tree-hierarchy in the editor + /// A and ID identifying your property, must be unique within your plugin + /// A name for your property, this is visible in the editor + /// A description for your property, this is visible in the editor + /// The layer property + protected LayerProperty RegisterLayerProperty(BaseLayerProperty parent, string id, string name, string description) + { + var property = new LayerProperty(Layer, Descriptor.LayerBrushProvider.PluginInfo, parent, id, name, description); + Layer.RegisterLayerProperty(property); + return property; + } + + /// + /// Provides an easy way to add your own properties to the layer, for more info see . + /// Note: If found, the last value and keyframes are loaded and set when calling this method. + /// + /// + /// A and ID identifying your property, must be unique within your plugin + /// A name for your property, this is visible in the editor + /// A description for your property, this is visible in the editor + /// The layer property + protected LayerProperty RegisterLayerProperty(string id, string name, string description) + { + var property = new LayerProperty(Layer, Descriptor.LayerBrushProvider.PluginInfo, Layer.BrushReferenceProperty.Parent, id, name, description); + Layer.RegisterLayerProperty(property); + return property; + } } } \ No newline at end of file diff --git a/src/Artemis.Core/Plugins/LayerBrush/LayerBrushSettings.cs b/src/Artemis.Core/Plugins/LayerBrush/LayerBrushSettings.cs deleted file mode 100644 index 8ba064593..000000000 --- a/src/Artemis.Core/Plugins/LayerBrush/LayerBrushSettings.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Xml.Serialization; -using Newtonsoft.Json; -using Stylet; - -namespace Artemis.Core.Plugins.LayerBrush -{ - public abstract class LayerBrushSettings : PropertyChangedBase - { - /// - /// Gets or sets the dispatcher to use to dispatch PropertyChanged events. Defaults to - /// Execute.DefaultPropertyChangedDispatcher - /// - [XmlIgnore] - [JsonIgnore] - public override Action PropertyChangedDispatcher { get; set; } = Execute.DefaultPropertyChangedDispatcher; - } -} \ No newline at end of file diff --git a/src/Artemis.Core/Plugins/LayerBrush/LayerBrushViewModel.cs b/src/Artemis.Core/Plugins/LayerBrush/LayerBrushViewModel.cs deleted file mode 100644 index 21eee433c..000000000 --- a/src/Artemis.Core/Plugins/LayerBrush/LayerBrushViewModel.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Stylet; - -namespace Artemis.Core.Plugins.LayerBrush -{ - public abstract class LayerBrushViewModel : PropertyChangedBase - { - protected LayerBrushViewModel(LayerBrush brush) - { - Brush = brush; - } - - public LayerBrush Brush { get; } - } -} \ No newline at end of file diff --git a/src/Artemis.Core/RGB.NET/BitmapBrush.cs b/src/Artemis.Core/RGB.NET/BitmapBrush.cs index 9f03294a2..3e37bba20 100644 --- a/src/Artemis.Core/RGB.NET/BitmapBrush.cs +++ b/src/Artemis.Core/RGB.NET/BitmapBrush.cs @@ -75,14 +75,9 @@ namespace Artemis.Core.RGB.NET { foreach (var renderTarget in renderTargets) { - if (renderTarget.Led.Id == LedId.Keyboard_W) - Console.WriteLine(); var scaledLocation = renderTarget.Point * Scale; if (scaledLocation.X < Bitmap.Width && scaledLocation.Y < Bitmap.Height) - { - var test = Bitmap.GetPixel(scaledLocation.X.RoundToInt(), scaledLocation.Y.RoundToInt()); RenderedTargets[renderTarget] = Bitmap.GetPixel(scaledLocation.X.RoundToInt(), scaledLocation.Y.RoundToInt()).ToRgbColor(); - } } } diff --git a/src/Artemis.Core/Services/Interfaces/ILayerService.cs b/src/Artemis.Core/Services/Interfaces/ILayerService.cs index 77e0cba6b..a6f1d3682 100644 --- a/src/Artemis.Core/Services/Interfaces/ILayerService.cs +++ b/src/Artemis.Core/Services/Interfaces/ILayerService.cs @@ -12,10 +12,8 @@ namespace Artemis.Core.Services.Interfaces /// to the provided . /// /// The layer to add the new layer element to - /// The descriptor of the new layer brush - /// JSON settings to be deserialized and injected into the layer brush /// - LayerBrush InstantiateLayerBrush(Layer layer, LayerBrushDescriptor brushDescriptor, string settings = null); + LayerBrush InstantiateLayerBrush(Layer layer); /// /// Instantiates and adds a compatible to the provided diff --git a/src/Artemis.Core/Services/LayerService.cs b/src/Artemis.Core/Services/LayerService.cs index 55a68ecec..2771a8c0e 100644 --- a/src/Artemis.Core/Services/LayerService.cs +++ b/src/Artemis.Core/Services/LayerService.cs @@ -1,13 +1,10 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Artemis.Core.Models.Profile; using Artemis.Core.Models.Profile.KeyframeEngines; using Artemis.Core.Models.Profile.LayerProperties; -using Artemis.Core.Plugins.Exceptions; using Artemis.Core.Plugins.LayerBrush; using Artemis.Core.Services.Interfaces; -using Newtonsoft.Json; using Ninject; using Ninject.Parameters; using Serilog; @@ -18,57 +15,38 @@ namespace Artemis.Core.Services { private readonly IKernel _kernel; private readonly ILogger _logger; + private readonly IPluginService _pluginService; - public LayerService(IKernel kernel, ILogger logger) + public LayerService(IKernel kernel, ILogger logger, IPluginService pluginService) { _kernel = kernel; _logger = logger; + _pluginService = pluginService; } - public LayerBrush InstantiateLayerBrush(Layer layer, LayerBrushDescriptor brushDescriptor, string settings) + public LayerBrush InstantiateLayerBrush(Layer layer) { - // Determine the settings type declared by the layer element - object settingsInstance = null; - var properties = brushDescriptor.LayerBrushType.GetProperties(); - var settingsType = properties.FirstOrDefault(p => p.Name == "Settings" && - p.DeclaringType == brushDescriptor.LayerBrushType)?.PropertyType; + RemoveLayerBrush(layer); - // Deserialize the settings if provided, check for null in JSON as well - if (settings != null && settings != "null") - { - // Setting where provided but no settings type was found, something is wrong - if (settingsType == null) - { - throw new ArtemisPluginException( - brushDescriptor.LayerBrushProvider.PluginInfo, - $"Settings where provided but layer element of type {brushDescriptor.LayerBrushType.Name} has no Settings property." - ); - } + var descriptorReference = layer.BrushReferenceProperty.CurrentValue; + if (descriptorReference == null) + return null; - try - { - settingsInstance = JsonConvert.DeserializeObject(settings, settingsType); - } - catch (JsonSerializationException e) - { - _logger.Warning(e, "Failed to deserialize settings for layer type {type}, resetting element settings - Plugin info: {pluginInfo}", - brushDescriptor.LayerBrushType.Name, - brushDescriptor.LayerBrushProvider.PluginInfo); + // Get a matching descriptor + var layerBrushProviders = _pluginService.GetPluginsOfType(); + var descriptors = layerBrushProviders.SelectMany(l => l.LayerBrushDescriptors).ToList(); + var descriptor = descriptors.FirstOrDefault(d => d.LayerBrushProvider.PluginInfo.Guid == descriptorReference.BrushPluginGuid && + d.LayerBrushType.Name == descriptorReference.BrushType); - settingsInstance = Activator.CreateInstance(settingsType); - } - } - // If no settings found, provide a fresh instance of the settings type - else if (settingsType != null) - settingsInstance = Activator.CreateInstance(settingsType); + if (descriptor == null) + return null; var arguments = new IParameter[] { new ConstructorArgument("layer", layer), - new ConstructorArgument("settings", settingsInstance), - new ConstructorArgument("descriptor", brushDescriptor) + new ConstructorArgument("descriptor", descriptor) }; - var layerElement = (LayerBrush) _kernel.Get(brushDescriptor.LayerBrushType, arguments); + var layerElement = (LayerBrush) _kernel.Get(descriptor.LayerBrushType, arguments); layer.LayerBrush = layerElement; return layerElement; @@ -92,10 +70,17 @@ namespace Artemis.Core.Services return keyframeEngine; } - public void RemoveLayerBrush(Layer layer, LayerBrush layerElement) + public void RemoveLayerBrush(Layer layer) { + if (layer.LayerBrush == null) + return; + var brush = layer.LayerBrush; layer.LayerBrush = null; + + var propertiesToRemove = layer.Properties.Where(l => l.PluginInfo == brush.Descriptor.LayerBrushProvider.PluginInfo).ToList(); + foreach (var layerProperty in propertiesToRemove) + layer.RemoveLayerProperty(layerProperty); brush.Dispose(); } } diff --git a/src/Artemis.Core/Services/SettingsService.cs b/src/Artemis.Core/Services/SettingsService.cs index d276b035f..2f235a9b5 100644 --- a/src/Artemis.Core/Services/SettingsService.cs +++ b/src/Artemis.Core/Services/SettingsService.cs @@ -12,8 +12,7 @@ namespace Artemis.Core.Services internal SettingsService(IPluginSettingRepository pluginSettingRepository) { - var pluginInfo = new PluginInfo {Guid = Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"), Name = "Artemis Core"}; - _pluginSettings = new PluginSettings(pluginInfo, pluginSettingRepository); + _pluginSettings = new PluginSettings(Constants.CorePluginInfo, pluginSettingRepository); } public PluginSetting GetSetting(string name, T defaultValue = default) diff --git a/src/Artemis.Core/Services/Storage/ProfileService.cs b/src/Artemis.Core/Services/Storage/ProfileService.cs index 11ef0a265..15ba6c18e 100644 --- a/src/Artemis.Core/Services/Storage/ProfileService.cs +++ b/src/Artemis.Core/Services/Storage/ProfileService.cs @@ -148,20 +148,9 @@ namespace Artemis.Core.Services.Storage private void InstantiateProfileLayerBrushes(Profile profile) { - var layerBrushProviders = _pluginService.GetPluginsOfType(); - var descriptors = layerBrushProviders.SelectMany(l => l.LayerBrushDescriptors).ToList(); - // Only instantiate brushes for layers without an existing brush instance - foreach (var layer in profile.GetAllLayers().Where(l => l.LayerBrush == null && l.LayerEntity.BrushEntity != null)) - { - // Get a matching descriptor - var descriptor = descriptors.FirstOrDefault(d => d.LayerBrushProvider.PluginInfo.Guid == layer.LayerEntity.BrushEntity.BrushPluginGuid && - d.LayerBrushType.Name == layer.LayerEntity.BrushEntity.BrushType); - - // If a descriptor that matches if found, instantiate it with the GUID of the element entity - if (descriptor != null) - _layerService.InstantiateLayerBrush(layer, descriptor, layer.LayerEntity.BrushEntity.Configuration); - } + foreach (var layer in profile.GetAllLayers().Where(l => l.LayerBrush == null)) + _layerService.InstantiateLayerBrush(layer); } private void InstantiateProfileKeyframeEngines(Profile profile) diff --git a/src/Artemis.Plugins.LayerBrushes.Color/Artemis.Plugins.LayerBrushes.Color.csproj b/src/Artemis.Plugins.LayerBrushes.Color/Artemis.Plugins.LayerBrushes.Color.csproj index 1fbedfc38..506742723 100644 --- a/src/Artemis.Plugins.LayerBrushes.Color/Artemis.Plugins.LayerBrushes.Color.csproj +++ b/src/Artemis.Plugins.LayerBrushes.Color/Artemis.Plugins.LayerBrushes.Color.csproj @@ -90,8 +90,6 @@ - - @@ -114,12 +112,6 @@ PreserveNewest - - - Designer - MSBuild:Compile - - diff --git a/src/Artemis.Plugins.LayerBrushes.Color/ColorBrush.cs b/src/Artemis.Plugins.LayerBrushes.Color/ColorBrush.cs index b7475ca56..88a058b69 100644 --- a/src/Artemis.Plugins.LayerBrushes.Color/ColorBrush.cs +++ b/src/Artemis.Plugins.LayerBrushes.Color/ColorBrush.cs @@ -1,7 +1,8 @@ using System; using System.Collections.Generic; -using System.Linq; +using System.ComponentModel; using Artemis.Core.Models.Profile; +using Artemis.Core.Models.Profile.LayerProperties; using Artemis.Core.Plugins.LayerBrush; using SkiaSharp; @@ -10,12 +11,15 @@ namespace Artemis.Plugins.LayerBrushes.Color public class ColorBrush : LayerBrush { private readonly List _testColors; + private SKColor _color; private SKPaint _paint; private SKShader _shader; - public ColorBrush(Layer layer, ColorBrushSettings settings, LayerBrushDescriptor descriptor) : base(layer, settings, descriptor) + public ColorBrush(Layer layer, LayerBrushDescriptor descriptor) : base(layer, descriptor) { - Settings = settings; + ColorProperty = RegisterLayerProperty("Brush.Color", "Main color", "The color of the brush."); + GradientTypeProperty = RegisterLayerProperty("Brush.GradientType", "Gradient type", "The scale of the noise."); + GradientTypeProperty.CanUseKeyframes = false; _testColors = new List(); for (var i = 0; i < 9; i++) @@ -28,19 +32,19 @@ namespace Artemis.Plugins.LayerBrushes.Color CreateShader(); Layer.RenderPropertiesUpdated += (sender, args) => CreateShader(); - Settings.PropertyChanged += (sender, args) => CreateShader(); } - public new ColorBrushSettings Settings { get; } + public LayerProperty ColorProperty { get; set; } + public LayerProperty GradientTypeProperty { get; set; } private void CreateShader() { var center = new SKPoint(Layer.Bounds.MidX, Layer.Bounds.MidY); SKShader shader; - switch (Settings.GradientType) + switch (GradientTypeProperty.CurrentValue) { case GradientType.Solid: - shader = SKShader.CreateColor(_testColors.First()); + shader = SKShader.CreateColor(_color); break; case GradientType.LinearGradient: shader = SKShader.CreateLinearGradient(new SKPoint(0, 0), new SKPoint(Layer.Bounds.Width, 0), _testColors.ToArray(), SKShaderTileMode.Repeat); @@ -63,9 +67,16 @@ namespace Artemis.Plugins.LayerBrushes.Color oldPaint?.Dispose(); } - public override LayerBrushViewModel GetViewModel() + public override void Update(double deltaTime) { - return new ColorBrushViewModel(this); + // Only recreate the shader if the color changed + if (_color != ColorProperty.CurrentValue) + { + _color = ColorProperty.CurrentValue; + CreateShader(); + } + + base.Update(deltaTime); } public override void Render(SKCanvas canvas, SKPath path, SKPaint paint) @@ -74,4 +85,19 @@ namespace Artemis.Plugins.LayerBrushes.Color canvas.DrawPath(path, paint); } } + + public enum GradientType + { + [Description("Solid")] + Solid, + + [Description("Linear Gradient")] + LinearGradient, + + [Description("Radial Gradient")] + RadialGradient, + + [Description("Sweep Gradient")] + SweepGradient + } } \ No newline at end of file diff --git a/src/Artemis.Plugins.LayerBrushes.Color/ColorBrushSettings.cs b/src/Artemis.Plugins.LayerBrushes.Color/ColorBrushSettings.cs deleted file mode 100644 index 78d46cc17..000000000 --- a/src/Artemis.Plugins.LayerBrushes.Color/ColorBrushSettings.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Collections.Generic; -using System.ComponentModel; -using Artemis.Core.Plugins.LayerBrush; -using SkiaSharp; - -namespace Artemis.Plugins.LayerBrushes.Color -{ - public class ColorBrushSettings : LayerBrushSettings - { - private List _colors; - private GradientType _gradientType; - - public ColorBrushSettings() - { - GradientType = GradientType.Solid; - Colors = new List(); - } - - public GradientType GradientType - { - get => _gradientType; - set => SetAndNotify(ref _gradientType, value); - } - - public List Colors - { - get => _colors; - set => SetAndNotify(ref _colors, value); - } - } - - public enum GradientType - { - [Description("Solid")] - Solid, - - [Description("Linear Gradient")] - LinearGradient, - - [Description("Radial Gradient")] - RadialGradient, - - [Description("Sweep Gradient")] - SweepGradient - } -} \ No newline at end of file diff --git a/src/Artemis.Plugins.LayerBrushes.Color/ColorBrushView.xaml b/src/Artemis.Plugins.LayerBrushes.Color/ColorBrushView.xaml deleted file mode 100644 index 2a038da7b..000000000 --- a/src/Artemis.Plugins.LayerBrushes.Color/ColorBrushView.xaml +++ /dev/null @@ -1,134 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Brush type - - - - - - - - - - - - - - - - - - - Setting title - Setting subtitle - - - - - - - - - - - - - - - - - - - Setting title - Setting subtitle - - - - - - - - - - - - - - - - - - - Setting title - Setting subtitle - - - - - - - - - - - - - - - - - - - Setting title - Setting subtitle - - - - - - - - \ No newline at end of file diff --git a/src/Artemis.Plugins.LayerBrushes.Color/ColorBrushViewModel.cs b/src/Artemis.Plugins.LayerBrushes.Color/ColorBrushViewModel.cs deleted file mode 100644 index 54ba5429e..000000000 --- a/src/Artemis.Plugins.LayerBrushes.Color/ColorBrushViewModel.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Generic; -using Artemis.Core.Plugins.LayerBrush; -using Artemis.UI.Shared.Utilities; - -namespace Artemis.Plugins.LayerBrushes.Color -{ - public class ColorBrushViewModel : LayerBrushViewModel - { - public ColorBrushViewModel(ColorBrush element) : base(element) - { - ColorBrush = element; - } - - public ColorBrush ColorBrush { get; } - public IEnumerable BrushTypes => EnumUtilities.GetAllValuesAndDescriptions(typeof(GradientType)); - } -} \ No newline at end of file diff --git a/src/Artemis.Plugins.LayerBrushes.Noise/Artemis.Plugins.LayerBrushes.Noise.csproj b/src/Artemis.Plugins.LayerBrushes.Noise/Artemis.Plugins.LayerBrushes.Noise.csproj index 7dbd14a31..533b471b0 100644 --- a/src/Artemis.Plugins.LayerBrushes.Noise/Artemis.Plugins.LayerBrushes.Noise.csproj +++ b/src/Artemis.Plugins.LayerBrushes.Noise/Artemis.Plugins.LayerBrushes.Noise.csproj @@ -12,7 +12,7 @@ v4.7.2 512 true - false + false @@ -90,8 +90,6 @@ - - @@ -115,12 +113,6 @@ PreserveNewest - - - Designer - MSBuild:Compile - - diff --git a/src/Artemis.Plugins.LayerBrushes.Noise/NoiseBrush.cs b/src/Artemis.Plugins.LayerBrushes.Noise/NoiseBrush.cs index 70a8939d0..d66caa98e 100644 --- a/src/Artemis.Plugins.LayerBrushes.Noise/NoiseBrush.cs +++ b/src/Artemis.Plugins.LayerBrushes.Noise/NoiseBrush.cs @@ -1,5 +1,6 @@ using System; using Artemis.Core.Models.Profile; +using Artemis.Core.Models.Profile.LayerProperties; using Artemis.Core.Plugins.LayerBrush; using Artemis.Plugins.LayerBrushes.Noise.Utilities; using SkiaSharp; @@ -13,20 +14,25 @@ namespace Artemis.Plugins.LayerBrushes.Noise private readonly OpenSimplexNoise _noise; private float _z; - public NoiseBrush(Layer layer, NoiseBrushSettings settings, LayerBrushDescriptor descriptor) : base(layer, settings, descriptor) + public NoiseBrush(Layer layer, LayerBrushDescriptor descriptor) : base(layer, descriptor) { - Settings = settings; + MainColorProperty = RegisterLayerProperty("Brush.MainColor", "Main color", "The main color of the noise."); + ScaleProperty = RegisterLayerProperty("Brush.Scale", "Scale", "The scale of the noise."); + AnimationSpeedProperty = RegisterLayerProperty("Brush.AnimationSpeed", "Animation speed", "The speed at which the noise moves."); + ScaleProperty.InputAffix = "%"; _z = Rand.Next(0, 4096); _noise = new OpenSimplexNoise(Rand.Next(0, 4096)); } - - public new NoiseBrushSettings Settings { get; } + + public LayerProperty MainColorProperty { get; set; } + public LayerProperty ScaleProperty { get; set; } + public LayerProperty AnimationSpeedProperty { get; set; } public override void Update(double deltaTime) { // TODO: Come up with a better way to use deltaTime - _z += Settings.AnimationSpeed / 500f / 0.04f * (float) deltaTime; + _z += AnimationSpeedProperty.CurrentValue / 500f / 0.04f * (float) deltaTime; if (_z >= float.MaxValue) _z = 0; @@ -34,17 +40,15 @@ namespace Artemis.Plugins.LayerBrushes.Noise base.Update(deltaTime); } - public override LayerBrushViewModel GetViewModel() - { - return new NoiseBrushViewModel(this); - } - public override void Render(SKCanvas canvas, SKPath path, SKPaint paint) { + var mainColor = MainColorProperty.CurrentValue; + var scale = ScaleProperty.CurrentValue; + // Scale down the render path to avoid computing a value for every pixel var width = (int) (Math.Max(path.Bounds.Width, path.Bounds.Height) / Scale); var height = (int) (Math.Max(path.Bounds.Width, path.Bounds.Height) / Scale); - var opacity = (float) Math.Round(Settings.Color.Alpha / 255.0, 2, MidpointRounding.AwayFromZero); + var opacity = (float) Math.Round(mainColor.Alpha / 255.0, 2, MidpointRounding.AwayFromZero); using (var bitmap = new SKBitmap(new SKImageInfo(width, height))) { bitmap.Erase(new SKColor(0, 0, 0, 0)); @@ -60,15 +64,14 @@ namespace Artemis.Plugins.LayerBrushes.Noise { for (var y = yStart; y < yEnd; y++) { - var v = _noise.Evaluate(Settings.XScale * x / width, Settings.YScale * y / height, _z); + var v = _noise.Evaluate(scale.Width * x / width, scale.Height * y / height, _z); var alpha = (byte) ((v + 1) * 127 * opacity); // There's some fun stuff we can do here, like creating hard lines // if (alpha > 128) // alpha = 255; // else // alpha = 0; - var color = new SKColor(Settings.Color.Red, Settings.Color.Green, Settings.Color.Blue, alpha); - bitmap.SetPixel((int) x, (int) y, color); + bitmap.SetPixel((int) x, (int) y, new SKColor(mainColor.Red, mainColor.Green, mainColor.Blue, alpha)); } } } diff --git a/src/Artemis.Plugins.LayerBrushes.Noise/NoiseBrushSettings.cs b/src/Artemis.Plugins.LayerBrushes.Noise/NoiseBrushSettings.cs deleted file mode 100644 index 7bc7b1265..000000000 --- a/src/Artemis.Plugins.LayerBrushes.Noise/NoiseBrushSettings.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Artemis.Core.Plugins.LayerBrush; -using SkiaSharp; - -namespace Artemis.Plugins.LayerBrushes.Noise -{ - public class NoiseBrushSettings : LayerBrushSettings - { - private float _animationSpeed; - private SKBlendMode _blendMode; - private SKColor _color; - private float _xScale; - private float _yScale; - - public SKColor Color - { - get => _color; - set => SetAndNotify(ref _color, value); - } - - public SKBlendMode BlendMode - { - get => _blendMode; - set => SetAndNotify(ref _blendMode, value); - } - - public float XScale - { - get => _xScale; - set => SetAndNotify(ref _xScale, value); - } - - public float YScale - { - get => _yScale; - set => SetAndNotify(ref _yScale, value); - } - - public float AnimationSpeed - { - get => _animationSpeed; - set => SetAndNotify(ref _animationSpeed, value); - } - } -} \ No newline at end of file diff --git a/src/Artemis.Plugins.LayerBrushes.Noise/NoiseBrushView.xaml b/src/Artemis.Plugins.LayerBrushes.Noise/NoiseBrushView.xaml deleted file mode 100644 index d3fd894c1..000000000 --- a/src/Artemis.Plugins.LayerBrushes.Noise/NoiseBrushView.xaml +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Artemis.Plugins.LayerBrushes.Noise/NoiseBrushViewModel.cs b/src/Artemis.Plugins.LayerBrushes.Noise/NoiseBrushViewModel.cs deleted file mode 100644 index 69c7a98c2..000000000 --- a/src/Artemis.Plugins.LayerBrushes.Noise/NoiseBrushViewModel.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Collections.Generic; -using Artemis.Core.Plugins.LayerBrush; -using Artemis.UI.Shared.Utilities; -using SkiaSharp; - -namespace Artemis.Plugins.LayerBrushes.Noise -{ - public class NoiseBrushViewModel : LayerBrushViewModel - { - public NoiseBrushViewModel(NoiseBrush brush) : base(brush) - { - Brush = brush; - } - - public new NoiseBrush Brush { get; } - public IEnumerable BlendModes => EnumUtilities.GetAllValuesAndDescriptions(typeof(SKBlendMode)); - } -} \ No newline at end of file diff --git a/src/Artemis.Storage/Entities/Profile/LayerEntity.cs b/src/Artemis.Storage/Entities/Profile/LayerEntity.cs index 65bcf2b64..614e575c3 100644 --- a/src/Artemis.Storage/Entities/Profile/LayerEntity.cs +++ b/src/Artemis.Storage/Entities/Profile/LayerEntity.cs @@ -23,8 +23,6 @@ namespace Artemis.Storage.Entities.Profile public List PropertyEntities { get; set; } public List Condition { get; set; } - public BrushEntity BrushEntity { get; set; } - [BsonRef("ProfileEntity")] public ProfileEntity Profile { get; set; } diff --git a/src/Artemis.UI.Shared/ColorPicker.xaml b/src/Artemis.UI.Shared/ColorPicker.xaml index 05e8a582a..255eebad7 100644 --- a/src/Artemis.UI.Shared/ColorPicker.xaml +++ b/src/Artemis.UI.Shared/ColorPicker.xaml @@ -89,15 +89,24 @@ + + - + diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj index 07ef904df..cb8eb2c40 100644 --- a/src/Artemis.UI/Artemis.UI.csproj +++ b/src/Artemis.UI/Artemis.UI.csproj @@ -169,6 +169,10 @@ + + BrushPropertyInputView.xaml + + EnumPropertyInputView.xaml @@ -176,6 +180,10 @@ + + SKColorPropertyInputView.xaml + + @@ -302,6 +310,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile @@ -314,6 +326,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesView.xaml.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesView.xaml.cs index a8fff5257..499528790 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesView.xaml.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesView.xaml.cs @@ -15,11 +15,11 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties // Keeping the scroll viewers in sync is up to the view, not a viewmodel concern private void TimelineScrollChanged(object sender, ScrollChangedEventArgs e) { - if (sender == TimelineHeaderScrollViewer) + if (e.OriginalSource == TimelineHeaderScrollViewer) TimelineRailsScrollViewer.ScrollToHorizontalOffset(e.HorizontalOffset); - else if (sender == PropertyTreeScrollViewer) + else if (e.OriginalSource == PropertyTreeScrollViewer) TimelineRailsScrollViewer.ScrollToVerticalOffset(e.VerticalOffset); - else if (sender == TimelineRailsScrollViewer) + else if (e.OriginalSource == TimelineRailsScrollViewer) { TimelineHeaderScrollViewer.ScrollToHorizontalOffset(e.HorizontalOffset); PropertyTreeScrollViewer.ScrollToVerticalOffset(e.VerticalOffset); diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs index 66e6f7c60..39e5e5f3f 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/LayerPropertiesViewModel.cs @@ -41,6 +41,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties PopulateProperties(); _profileEditorService.SelectedProfileElementChanged += (sender, args) => PopulateProperties(); + _profileEditorService.SelectedProfileChanged += (sender, args) => PopulateProperties(); _profileEditorService.CurrentTimeChanged += ProfileEditorServiceOnCurrentTimeChanged; } diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/BrushPropertyInputView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/BrushPropertyInputView.xaml new file mode 100644 index 000000000..d47b18aad --- /dev/null +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/BrushPropertyInputView.xaml @@ -0,0 +1,28 @@ + + + + + + + + diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/BrushPropertyInputView.xaml.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/BrushPropertyInputView.xaml.cs new file mode 100644 index 000000000..b536c8ee1 --- /dev/null +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/BrushPropertyInputView.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput +{ + /// + /// Interaction logic for BrushPropertyInputView.xaml + /// + public partial class BrushPropertyInputView : UserControl + { + public BrushPropertyInputView() + { + InitializeComponent(); + } + } +} diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/BrushPropertyInputViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/BrushPropertyInputViewModel.cs new file mode 100644 index 000000000..78ca1ae94 --- /dev/null +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/BrushPropertyInputViewModel.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Artemis.Core.Models.Profile; +using Artemis.Core.Plugins.LayerBrush; +using Artemis.Core.Services.Interfaces; +using Artemis.UI.Services.Interfaces; +using Artemis.UI.Shared.Utilities; +using Stylet; + +namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput +{ + public class BrushPropertyInputViewModel : PropertyInputViewModel + { + private readonly ILayerService _layerService; + private readonly IPluginService _pluginService; + + public BrushPropertyInputViewModel(IProfileEditorService profileEditorService, ILayerService layerService, IPluginService pluginService) : base(profileEditorService) + { + _layerService = layerService; + _pluginService = pluginService; + EnumValues = new BindableCollection(); + + _pluginService.PluginLoaded += (sender, args) => UpdateEnumValues(); + } + + public BindableCollection EnumValues { get; } + + public sealed override List CompatibleTypes { get; } = new List {typeof(LayerBrushReference)}; + + public LayerBrushReference BrushInputValue + { + get => (LayerBrushReference) InputValue; + set + { + InputValue = value; + _layerService.InstantiateLayerBrush(LayerPropertyViewModel.LayerProperty.Layer); + } + } + + protected override void OnInitialized() + { + UpdateEnumValues(); + base.OnInitialized(); + } + + public void UpdateEnumValues() + { + var layerBrushProviders = _pluginService.GetPluginsOfType(); + var descriptors = layerBrushProviders.SelectMany(l => l.LayerBrushDescriptors).ToList(); + EnumValues.Clear(); + foreach (var layerBrushDescriptor in descriptors) + { + var brushName = layerBrushDescriptor.LayerBrushType.Name; + var brushGuid = layerBrushDescriptor.LayerBrushProvider.PluginInfo.Guid; + if (BrushInputValue != null && BrushInputValue.BrushType == brushName && BrushInputValue.BrushPluginGuid == brushGuid) + EnumValues.Add(new ValueDescription {Description = layerBrushDescriptor.DisplayName, Value = BrushInputValue}); + else + EnumValues.Add(new ValueDescription {Description = layerBrushDescriptor.DisplayName, Value = new LayerBrushReference {BrushType = brushName, BrushPluginGuid = brushGuid}}); + } + } + + public override void Update() + { + NotifyOfPropertyChange(() => BrushInputValue); + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/EnumPropertyInputView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/EnumPropertyInputView.xaml index 416b66bf6..1b29af406 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/EnumPropertyInputView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/EnumPropertyInputView.xaml @@ -23,11 +23,6 @@ SelectedValue="{Binding Path=EnumInputValue}" RequestBringIntoView="{s:Action OnRequestBringIntoView}" materialDesign:ComboBoxAssist.ClassicMode="True"> - - - diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/EnumPropertyInputView.xaml.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/EnumPropertyInputView.xaml.cs index ecbd7ab17..251517ae7 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/EnumPropertyInputView.xaml.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/EnumPropertyInputView.xaml.cs @@ -24,10 +24,5 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.P { InitializeComponent(); } - - private void OnRequestBringIntoView(object sender, RequestBringIntoViewEventArgs e) - { - e.Handled = true; - } } } diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKColorPropertyInputView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKColorPropertyInputView.xaml new file mode 100644 index 000000000..057e78127 --- /dev/null +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKColorPropertyInputView.xaml @@ -0,0 +1,25 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKColorPropertyInputView.xaml.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKColorPropertyInputView.xaml.cs new file mode 100644 index 000000000..e78144cec --- /dev/null +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKColorPropertyInputView.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput +{ + /// + /// Interaction logic for SKColorPropertyInputView.xaml + /// + public partial class SKColorPropertyInputView : UserControl + { + public SKColorPropertyInputView() + { + InitializeComponent(); + } + } +} diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKColorPropertyInputViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKColorPropertyInputViewModel.cs new file mode 100644 index 000000000..ed5ca0332 --- /dev/null +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyInput/SKColorPropertyInputViewModel.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using Artemis.UI.Services.Interfaces; +using SkiaSharp; + +namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree.PropertyInput +{ + public class SKColorPropertyInputViewModel : PropertyInputViewModel + { + public SKColorPropertyInputViewModel(IProfileEditorService profileEditorService) : base(profileEditorService) + { + } + + public sealed override List CompatibleTypes { get; } = new List {typeof(SKColor)}; + + public SKColor SKColorInputValue + { + get => (SKColor?) InputValue ?? new SKColor(); + set => InputValue = value; + } + + public override void Update() + { + NotifyOfPropertyChange(() => SKColorInputValue); + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyTreeChildViewModel.cs b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyTreeChildViewModel.cs index c0a8cf009..61b373af3 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyTreeChildViewModel.cs +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/LayerProperties/PropertyTree/PropertyTreeChildViewModel.cs @@ -16,12 +16,12 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties.PropertyTree public override void Update(bool forceUpdate) { if (forceUpdate) - PropertyInputViewModel.Update(); + PropertyInputViewModel?.Update(); else { // Only update if visible and if keyframes are enabled if (LayerPropertyViewModel.Parent.IsExpanded && LayerPropertyViewModel.KeyframesEnabled) - PropertyInputViewModel.Update(); + PropertyInputViewModel?.Update(); } } } diff --git a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileView.xaml b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileView.xaml index f2fa17dc6..14b3e840b 100644 --- a/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileView.xaml +++ b/src/Artemis.UI/Screens/Module/ProfileEditor/Visualization/ProfileView.xaml @@ -91,10 +91,10 @@ - + - +