mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Conditions - Refactor layer properties WIP (needed for consistency..)
This commit is contained in:
parent
302ba10caa
commit
d9bba8cb54
@ -22,6 +22,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Ben.Demystifier" Version="0.1.6" />
|
<PackageReference Include="Ben.Demystifier" Version="0.1.6" />
|
||||||
<PackageReference Include="Castle.Core" Version="4.4.0" />
|
<PackageReference Include="Castle.Core" Version="4.4.0" />
|
||||||
|
<PackageReference Include="FastMember" Version="1.5.0" />
|
||||||
<PackageReference Include="HidSharp" Version="2.1.0" />
|
<PackageReference Include="HidSharp" Version="2.1.0" />
|
||||||
<PackageReference Include="LiteDB" Version="5.0.7" />
|
<PackageReference Include="LiteDB" Version="5.0.7" />
|
||||||
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.2.0" />
|
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.2.0" />
|
||||||
|
|||||||
@ -5,11 +5,11 @@ namespace Artemis.Core.Events
|
|||||||
{
|
{
|
||||||
public class LayerPropertyEventArgs : EventArgs
|
public class LayerPropertyEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public LayerPropertyEventArgs(BaseLayerProperty layerProperty)
|
public LayerPropertyEventArgs(LayerProperty layerProperty)
|
||||||
{
|
{
|
||||||
LayerProperty = layerProperty;
|
LayerProperty = layerProperty;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BaseLayerProperty LayerProperty { get; }
|
public LayerProperty LayerProperty { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -17,7 +17,7 @@ namespace Artemis.Core.Models.Profile.KeyframeEngines
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The layer property this keyframe engine applies to.
|
/// The layer property this keyframe engine applies to.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public BaseLayerProperty LayerProperty { get; private set; }
|
public LayerProperty LayerProperty { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The total progress
|
/// The total progress
|
||||||
@ -55,7 +55,7 @@ namespace Artemis.Core.Models.Profile.KeyframeEngines
|
|||||||
/// Associates the keyframe engine with the provided layer property.
|
/// Associates the keyframe engine with the provided layer property.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="layerProperty"></param>
|
/// <param name="layerProperty"></param>
|
||||||
public void Initialize(BaseLayerProperty layerProperty)
|
public void Initialize(LayerProperty layerProperty)
|
||||||
{
|
{
|
||||||
if (Initialized)
|
if (Initialized)
|
||||||
throw new ArtemisCoreException("Cannot initialize the same keyframe engine twice");
|
throw new ArtemisCoreException("Cannot initialize the same keyframe engine twice");
|
||||||
|
|||||||
@ -3,22 +3,28 @@ using System.Collections.Generic;
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Artemis.Core.Extensions;
|
using Artemis.Core.Extensions;
|
||||||
using Artemis.Core.Models.Profile.LayerProperties;
|
using Artemis.Core.Models.Profile.LayerProperties.Attributes;
|
||||||
using Artemis.Core.Models.Profile.LayerShapes;
|
using Artemis.Core.Models.Profile.LayerShapes;
|
||||||
using Artemis.Core.Models.Surface;
|
using Artemis.Core.Models.Surface;
|
||||||
using Artemis.Core.Plugins.LayerBrush;
|
using Artemis.Core.Plugins.LayerBrush;
|
||||||
|
using Artemis.Core.Services;
|
||||||
|
using Artemis.Core.Services.Interfaces;
|
||||||
using Artemis.Storage.Entities.Profile;
|
using Artemis.Storage.Entities.Profile;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
|
|
||||||
namespace Artemis.Core.Models.Profile
|
namespace Artemis.Core.Models.Profile
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a layer on a profile. To create new layers use the <see cref="LayerService" /> by injecting
|
||||||
|
/// <see cref="ILayerService" /> into your code
|
||||||
|
/// </summary>
|
||||||
public sealed class Layer : ProfileElement
|
public sealed class Layer : ProfileElement
|
||||||
{
|
{
|
||||||
private LayerShape _layerShape;
|
private LayerShape _layerShape;
|
||||||
private List<ArtemisLed> _leds;
|
private List<ArtemisLed> _leds;
|
||||||
private SKPath _path;
|
private SKPath _path;
|
||||||
|
|
||||||
public Layer(Profile profile, ProfileElement parent, string name)
|
internal Layer(Profile profile, ProfileElement parent, string name)
|
||||||
{
|
{
|
||||||
LayerEntity = new LayerEntity();
|
LayerEntity = new LayerEntity();
|
||||||
EntityId = Guid.NewGuid();
|
EntityId = Guid.NewGuid();
|
||||||
@ -26,12 +32,10 @@ namespace Artemis.Core.Models.Profile
|
|||||||
Profile = profile;
|
Profile = profile;
|
||||||
Parent = parent;
|
Parent = parent;
|
||||||
Name = name;
|
Name = name;
|
||||||
Properties = new LayerPropertyCollection(this);
|
General = new LayerGeneralProperties();
|
||||||
|
Transform = new LayerTransformProperties();
|
||||||
|
|
||||||
_leds = new List<ArtemisLed>();
|
_leds = new List<ArtemisLed>();
|
||||||
|
|
||||||
ApplyShapeType();
|
|
||||||
Properties.ShapeType.ValueChanged += (sender, args) => ApplyShapeType();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal Layer(Profile profile, ProfileElement parent, LayerEntity layerEntity)
|
internal Layer(Profile profile, ProfileElement parent, LayerEntity layerEntity)
|
||||||
@ -43,12 +47,10 @@ namespace Artemis.Core.Models.Profile
|
|||||||
Parent = parent;
|
Parent = parent;
|
||||||
Name = layerEntity.Name;
|
Name = layerEntity.Name;
|
||||||
Order = layerEntity.Order;
|
Order = layerEntity.Order;
|
||||||
Properties = new LayerPropertyCollection(this);
|
General = new LayerGeneralProperties();
|
||||||
|
Transform = new LayerTransformProperties();
|
||||||
|
|
||||||
_leds = new List<ArtemisLed>();
|
_leds = new List<ArtemisLed>();
|
||||||
|
|
||||||
ApplyShapeType();
|
|
||||||
Properties.ShapeType.ValueChanged += (sender, args) => ApplyShapeType();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal LayerEntity LayerEntity { get; set; }
|
internal LayerEntity LayerEntity { get; set; }
|
||||||
@ -93,15 +95,16 @@ namespace Artemis.Core.Models.Profile
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
[PropertyGroupDescription(Name = "General", Description = "A collection of general properties", ExpandByDefault = true)]
|
||||||
/// The properties of this layer
|
public LayerGeneralProperties General { get; set; }
|
||||||
/// </summary>
|
|
||||||
public LayerPropertyCollection Properties { get; set; }
|
[PropertyGroupDescription(Name = "Transform", Description = "A collection of transformation properties", ExpandByDefault = true)]
|
||||||
|
public LayerTransformProperties Transform { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The brush that will fill the <see cref="LayerShape" />.
|
/// The brush that will fill the <see cref="LayerShape" />.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public LayerBrush LayerBrush { get; internal set; }
|
public ILayerBrush LayerBrush { get; internal set; }
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
@ -158,6 +161,19 @@ namespace Artemis.Core.Models.Profile
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
|
||||||
|
internal void InitializeProperties(ILayerService layerService)
|
||||||
|
{
|
||||||
|
PropertiesInitialized = true;
|
||||||
|
|
||||||
|
ApplyShapeType();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool PropertiesInitialized { get; private set; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Rendering
|
#region Rendering
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
35
src/Artemis.Core/Models/Profile/LayerGeneralProperties.cs
Normal file
35
src/Artemis.Core/Models/Profile/LayerGeneralProperties.cs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
using Artemis.Core.Models.Profile.LayerProperties;
|
||||||
|
using Artemis.Core.Models.Profile.LayerProperties.Attributes;
|
||||||
|
using Artemis.Core.Models.Profile.LayerProperties.Types;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Models.Profile
|
||||||
|
{
|
||||||
|
public class LayerGeneralProperties : LayerPropertyGroup
|
||||||
|
{
|
||||||
|
[PropertyDescription(Name = "Shape type", Description = "The type of shape to draw in this layer")]
|
||||||
|
public EnumLayerProperty<LayerShapeType> ShapeType { get; set; }
|
||||||
|
|
||||||
|
[PropertyDescription(Name = "Fill type", Description = "How to make the shape adjust to scale changes")]
|
||||||
|
public EnumLayerProperty<LayerFillType> FillType { get; set; }
|
||||||
|
|
||||||
|
[PropertyDescription(Name = "Blend mode", Description = "How to blend this layer into the resulting image")]
|
||||||
|
public EnumLayerProperty<SKBlendMode> BlendMode { get; set; }
|
||||||
|
|
||||||
|
[PropertyDescription(Name = "Brush type", Description = "The type of brush to use for this layer")]
|
||||||
|
public LayerBrushReferenceLayerProperty BrushReference { get; set; }
|
||||||
|
|
||||||
|
protected override void OnPropertiesInitialized()
|
||||||
|
{
|
||||||
|
// Populate defaults
|
||||||
|
if (!ShapeType.IsLoadedFromStorage)
|
||||||
|
ShapeType.BaseValue = LayerShapeType.Rectangle;
|
||||||
|
if (!FillType.IsLoadedFromStorage)
|
||||||
|
FillType.BaseValue = LayerFillType.Stretch;
|
||||||
|
if (!BlendMode.IsLoadedFromStorage)
|
||||||
|
BlendMode.BaseValue = SKBlendMode.SrcOver;
|
||||||
|
|
||||||
|
// TODO: SpoinkyNL 28-4-2020: Select preferred default brush type with a fallback to the first available
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,80 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Artemis.Core.Utilities;
|
|
||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.LayerProperties.Abstract
|
|
||||||
{
|
|
||||||
public abstract class LayerProperty<T>
|
|
||||||
{
|
|
||||||
private List<LayerPropertyKeyFrame<T>> _keyframes;
|
|
||||||
|
|
||||||
protected LayerProperty()
|
|
||||||
{
|
|
||||||
_keyframes = new List<LayerPropertyKeyFrame<T>>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public T BaseValue { get; set; }
|
|
||||||
public T CurrentValue { get; set; }
|
|
||||||
public IReadOnlyList<LayerPropertyKeyFrame<T>> Keyframes => _keyframes.AsReadOnly();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The total progress on the timeline
|
|
||||||
/// </summary>
|
|
||||||
public TimeSpan TimelineProgress { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The current keyframe in the timeline
|
|
||||||
/// </summary>
|
|
||||||
public LayerPropertyKeyFrame<T> CurrentKeyframe { get; protected set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The next keyframe in the timeline
|
|
||||||
/// </summary>
|
|
||||||
public LayerPropertyKeyFrame<T> NextKeyframe { get; protected set; }
|
|
||||||
|
|
||||||
public void Update(double deltaTime)
|
|
||||||
{
|
|
||||||
float keyframeProgress;
|
|
||||||
float keyframeProgressEased;
|
|
||||||
|
|
||||||
TimelineProgress = TimelineProgress.Add(TimeSpan.FromSeconds(deltaTime));
|
|
||||||
// The current keyframe is the last keyframe before the current time
|
|
||||||
CurrentKeyframe = _keyframes.LastOrDefault(k => k.Position <= TimelineProgress);
|
|
||||||
// The next keyframe is the first keyframe that's after the current time
|
|
||||||
NextKeyframe = _keyframes.FirstOrDefault(k => k.Position > TimelineProgress);
|
|
||||||
|
|
||||||
if (CurrentKeyframe == null)
|
|
||||||
{
|
|
||||||
keyframeProgress = 0;
|
|
||||||
keyframeProgressEased = 0;
|
|
||||||
}
|
|
||||||
else if (NextKeyframe == null)
|
|
||||||
{
|
|
||||||
keyframeProgress = 1;
|
|
||||||
keyframeProgressEased = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var timeDiff = NextKeyframe.Position - CurrentKeyframe.Position;
|
|
||||||
keyframeProgress = (float) ((TimelineProgress - CurrentKeyframe.Position).TotalMilliseconds / timeDiff.TotalMilliseconds);
|
|
||||||
keyframeProgressEased = (float) Easings.Interpolate(keyframeProgress, CurrentKeyframe.EasingFunction);
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateCurrentValue(keyframeProgress, keyframeProgressEased);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased);
|
|
||||||
|
|
||||||
public void OverrideProgress(TimeSpan progress)
|
|
||||||
{
|
|
||||||
TimelineProgress = TimeSpan.Zero;
|
|
||||||
Update(progress.TotalSeconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void SortKeyframes()
|
|
||||||
{
|
|
||||||
_keyframes = _keyframes.OrderBy(k => k.Position).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,10 +1,42 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.LayerProperties.Attributes
|
namespace Artemis.Core.Models.Profile.LayerProperties.Attributes
|
||||||
{
|
{
|
||||||
public class PropertyDescriptionAttribute : Attribute
|
public class PropertyDescriptionAttribute : Attribute
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The user-friendly name for this property, shown in the UI
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The user-friendly description for this property, shown in the UI
|
||||||
|
/// </summary>
|
||||||
|
public string Description { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Input prefix to show before input elements in the UI
|
||||||
|
/// </summary>
|
||||||
|
public string InputPrefix { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Input affix to show behind input elements in the UI
|
||||||
|
/// </summary>
|
||||||
|
public string InputAffix { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The input drag step size, used in the UI
|
||||||
|
/// </summary>
|
||||||
|
public float InputStepSize { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Minimum input value, only enforced in the UI
|
||||||
|
/// </summary>
|
||||||
|
public object MinInputValue { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum input value, only enforced in the UI
|
||||||
|
/// </summary>
|
||||||
|
public object MaxInputValue { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Models.Profile.LayerProperties.Attributes
|
||||||
|
{
|
||||||
|
public class PropertyGroupDescriptionAttribute : Attribute
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The user-friendly name for this property, shown in the UI.
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The user-friendly description for this property, shown in the UI.
|
||||||
|
/// </summary>
|
||||||
|
public string Description { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to expand this property by default, this is useful for important parent properties.
|
||||||
|
/// </summary>
|
||||||
|
public bool ExpandByDefault { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,33 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Artemis.Core.Utilities;
|
|
||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.LayerProperties
|
|
||||||
{
|
|
||||||
public class BaseKeyframe
|
|
||||||
{
|
|
||||||
private TimeSpan _position;
|
|
||||||
|
|
||||||
protected BaseKeyframe(Layer layer, BaseLayerProperty property)
|
|
||||||
{
|
|
||||||
Layer = layer;
|
|
||||||
BaseProperty = property;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Layer Layer { get; set; }
|
|
||||||
|
|
||||||
public TimeSpan Position
|
|
||||||
{
|
|
||||||
get => _position;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == _position) return;
|
|
||||||
_position = value;
|
|
||||||
BaseProperty.SortKeyframes();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected BaseLayerProperty BaseProperty { get; }
|
|
||||||
public object BaseValue { get; internal set; }
|
|
||||||
public Easings.Functions EasingFunction { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,375 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Artemis.Core.Exceptions;
|
|
||||||
using Artemis.Core.Models.Profile.KeyframeEngines;
|
|
||||||
using Artemis.Core.Models.Profile.LayerProperties.Abstract;
|
|
||||||
using Artemis.Core.Plugins.Models;
|
|
||||||
using Artemis.Core.Utilities;
|
|
||||||
using Artemis.Storage.Entities.Profile;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Stylet;
|
|
||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.LayerProperties
|
|
||||||
{
|
|
||||||
public abstract class BaseLayerProperty : PropertyChangedBase
|
|
||||||
{
|
|
||||||
private object _baseValue;
|
|
||||||
private bool _isHidden;
|
|
||||||
|
|
||||||
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;
|
|
||||||
Description = description;
|
|
||||||
Type = type;
|
|
||||||
CanUseKeyframes = true;
|
|
||||||
InputStepSize = 1;
|
|
||||||
|
|
||||||
// This can only be null if accessed internally, all public ways of creating enforce a plugin info
|
|
||||||
if (PluginInfo == null)
|
|
||||||
PluginInfo = Constants.CorePluginInfo;
|
|
||||||
|
|
||||||
Children = new List<BaseLayerProperty>();
|
|
||||||
BaseKeyframes = new List<BaseKeyframe>();
|
|
||||||
|
|
||||||
|
|
||||||
parent?.Children.Add(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the layer this property applies to
|
|
||||||
/// </summary>
|
|
||||||
public Layer Layer { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Info of the plugin associated with this property
|
|
||||||
/// </summary>
|
|
||||||
public PluginInfo PluginInfo { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the parent property of this property.
|
|
||||||
/// </summary>
|
|
||||||
public BaseLayerProperty Parent { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the child properties of this property.
|
|
||||||
/// <remarks>If the layer has children it cannot contain a value or keyframes.</remarks>
|
|
||||||
/// </summary>
|
|
||||||
public List<BaseLayerProperty> Children { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a unique identifier for this property, a layer may not contain two properties with the same ID.
|
|
||||||
/// </summary>
|
|
||||||
public string Id { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the user-friendly name for this property, shown in the UI.
|
|
||||||
/// </summary>
|
|
||||||
public string Name { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the user-friendly description for this property, shown in the UI.
|
|
||||||
/// </summary>
|
|
||||||
public string Description { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets whether to expand this property by default, this is useful for important parent properties.
|
|
||||||
/// </summary>
|
|
||||||
public bool ExpandByDefault { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the an optional input prefix to show before input elements in the UI.
|
|
||||||
/// </summary>
|
|
||||||
public string InputPrefix { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets an optional input affix to show behind input elements in the UI.
|
|
||||||
/// </summary>
|
|
||||||
public string InputAffix { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets an optional maximum input value, only enforced in the UI.
|
|
||||||
/// </summary>
|
|
||||||
public object MaxInputValue { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the input drag step size, used in the UI.
|
|
||||||
/// </summary>
|
|
||||||
public float InputStepSize { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets an optional minimum input value, only enforced in the UI.
|
|
||||||
/// </summary>
|
|
||||||
public object MinInputValue { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets whether this property can use keyframes, True by default.
|
|
||||||
/// </summary>
|
|
||||||
public bool CanUseKeyframes { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets whether this property is using keyframes.
|
|
||||||
/// </summary>
|
|
||||||
public bool IsUsingKeyframes { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the type of value this layer property contains.
|
|
||||||
/// </summary>
|
|
||||||
public Type Type { get; protected set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets whether this property is hidden in the UI.
|
|
||||||
/// </summary>
|
|
||||||
public bool IsHidden
|
|
||||||
{
|
|
||||||
get => _isHidden;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_isHidden = value;
|
|
||||||
OnVisibilityChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a list of keyframes defining different values of the property in time, this list contains the untyped
|
|
||||||
/// <see cref="BaseKeyframe" />.
|
|
||||||
/// </summary>
|
|
||||||
public IReadOnlyCollection<BaseKeyframe> UntypedKeyframes => BaseKeyframes.AsReadOnly();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the keyframe engine instance of this property
|
|
||||||
/// </summary>
|
|
||||||
public KeyframeEngine KeyframeEngine { get; internal set; }
|
|
||||||
|
|
||||||
protected List<BaseKeyframe> BaseKeyframes { get; set; }
|
|
||||||
|
|
||||||
public object BaseValue
|
|
||||||
{
|
|
||||||
get => _baseValue;
|
|
||||||
internal set
|
|
||||||
{
|
|
||||||
if (value != null && value.GetType() != Type)
|
|
||||||
throw new ArtemisCoreException($"Cannot set value of type {value.GetType()} on property {this}, expected type is {Type}.");
|
|
||||||
if (!Equals(_baseValue, value))
|
|
||||||
{
|
|
||||||
_baseValue = value;
|
|
||||||
OnValueChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new keyframe for this base property without knowing the type
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public BaseKeyframe CreateNewKeyframe(TimeSpan position, object value)
|
|
||||||
{
|
|
||||||
// Create a strongly typed keyframe or else it cannot be cast later on
|
|
||||||
var keyframeType = typeof(Keyframe<>);
|
|
||||||
var keyframe = (BaseKeyframe) Activator.CreateInstance(keyframeType.MakeGenericType(Type), Layer, this);
|
|
||||||
keyframe.Position = position;
|
|
||||||
keyframe.BaseValue = value;
|
|
||||||
BaseKeyframes.Add(keyframe);
|
|
||||||
SortKeyframes();
|
|
||||||
|
|
||||||
return keyframe;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes all keyframes from the property and sets the base value to the current value.
|
|
||||||
/// </summary>
|
|
||||||
public void ClearKeyframes()
|
|
||||||
{
|
|
||||||
if (KeyframeEngine != null)
|
|
||||||
BaseValue = KeyframeEngine.GetCurrentValue();
|
|
||||||
|
|
||||||
BaseKeyframes.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current value using the regular value or if present, keyframes
|
|
||||||
/// </summary>
|
|
||||||
public object GetCurrentValue()
|
|
||||||
{
|
|
||||||
if (KeyframeEngine == null || !UntypedKeyframes.Any())
|
|
||||||
return BaseValue;
|
|
||||||
|
|
||||||
return KeyframeEngine.GetCurrentValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the current value using the regular value or keyframes.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">The value to set.</param>
|
|
||||||
/// <param name="time">
|
|
||||||
/// An optional time to set the value add, if provided and property is using keyframes the value will be set to an new
|
|
||||||
/// or existing keyframe.
|
|
||||||
/// </param>
|
|
||||||
public void SetCurrentValue(object value, TimeSpan? time)
|
|
||||||
{
|
|
||||||
if (value != null && value.GetType() != Type)
|
|
||||||
throw new ArtemisCoreException($"Cannot set value of type {value.GetType()} on property {this}, expected type is {Type}.");
|
|
||||||
|
|
||||||
if (time == null || !CanUseKeyframes || !IsUsingKeyframes)
|
|
||||||
BaseValue = value;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// If on a keyframe, update the keyframe
|
|
||||||
var currentKeyframe = UntypedKeyframes.FirstOrDefault(k => k.Position == time.Value);
|
|
||||||
// Create a new keyframe if none found
|
|
||||||
if (currentKeyframe == null)
|
|
||||||
currentKeyframe = CreateNewKeyframe(time.Value, value);
|
|
||||||
|
|
||||||
currentKeyframe.BaseValue = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
OnValueChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a keyframe to the property.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="keyframe">The keyframe to remove</param>
|
|
||||||
public void AddKeyframe(BaseKeyframe keyframe)
|
|
||||||
{
|
|
||||||
BaseKeyframes.Add(keyframe);
|
|
||||||
SortKeyframes();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes a keyframe from the property.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="keyframe">The keyframe to remove</param>
|
|
||||||
public void RemoveKeyframe(BaseKeyframe keyframe)
|
|
||||||
{
|
|
||||||
BaseKeyframes.Remove(keyframe);
|
|
||||||
SortKeyframes();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the flattened index of this property on the layer
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public int GetFlattenedIndex()
|
|
||||||
{
|
|
||||||
if (Parent == null)
|
|
||||||
return Layer.Properties.ToList().IndexOf(this);
|
|
||||||
|
|
||||||
// Create a flattened list of all properties in their order as defined by the parent/child hierarchy
|
|
||||||
var properties = new List<BaseLayerProperty>();
|
|
||||||
// Iterate root properties (those with children)
|
|
||||||
foreach (var baseLayerProperty in Layer.Properties)
|
|
||||||
{
|
|
||||||
// First add self, then add all children
|
|
||||||
if (baseLayerProperty.Children.Any())
|
|
||||||
{
|
|
||||||
properties.Add(baseLayerProperty);
|
|
||||||
properties.AddRange(baseLayerProperty.GetAllChildren());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return properties.IndexOf(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return $"{nameof(Id)}: {Id}, {nameof(Name)}: {Name}, {nameof(Description)}: {Description}";
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ApplyToEntity()
|
|
||||||
{
|
|
||||||
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.IsUsingKeyframes = IsUsingKeyframes;
|
|
||||||
|
|
||||||
propertyEntity.KeyframeEntities.Clear();
|
|
||||||
foreach (var baseKeyframe in BaseKeyframes)
|
|
||||||
{
|
|
||||||
propertyEntity.KeyframeEntities.Add(new KeyframeEntity
|
|
||||||
{
|
|
||||||
Position = baseKeyframe.Position,
|
|
||||||
Value = JsonConvert.SerializeObject(baseKeyframe.BaseValue),
|
|
||||||
EasingFunction = (int) baseKeyframe.EasingFunction
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ApplyToProperty(PropertyEntity propertyEntity)
|
|
||||||
{
|
|
||||||
BaseValue = DeserializePropertyValue(propertyEntity.Value);
|
|
||||||
IsUsingKeyframes = propertyEntity.IsUsingKeyframes;
|
|
||||||
|
|
||||||
BaseKeyframes.Clear();
|
|
||||||
foreach (var keyframeEntity in propertyEntity.KeyframeEntities.OrderBy(e => e.Position))
|
|
||||||
{
|
|
||||||
// Create a strongly typed keyframe or else it cannot be cast later on
|
|
||||||
var keyframeType = typeof(Keyframe<>);
|
|
||||||
var keyframe = (BaseKeyframe) Activator.CreateInstance(keyframeType.MakeGenericType(Type), Layer, this);
|
|
||||||
keyframe.Position = keyframeEntity.Position;
|
|
||||||
keyframe.BaseValue = DeserializePropertyValue(keyframeEntity.Value);
|
|
||||||
keyframe.EasingFunction = (Easings.Functions) keyframeEntity.EasingFunction;
|
|
||||||
|
|
||||||
BaseKeyframes.Add(keyframe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void SortKeyframes()
|
|
||||||
{
|
|
||||||
BaseKeyframes = BaseKeyframes.OrderBy(k => k.Position).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal IEnumerable<BaseLayerProperty> GetAllChildren()
|
|
||||||
{
|
|
||||||
var children = new List<BaseLayerProperty>();
|
|
||||||
children.AddRange(Children);
|
|
||||||
foreach (var layerPropertyViewModel in Children)
|
|
||||||
children.AddRange(layerPropertyViewModel.GetAllChildren());
|
|
||||||
|
|
||||||
return children;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object DeserializePropertyValue(string value)
|
|
||||||
{
|
|
||||||
if (value == "null")
|
|
||||||
return Type.IsValueType ? Activator.CreateInstance(Type) : null;
|
|
||||||
return JsonConvert.DeserializeObject(value, Type);
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Events
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when this property's value was changed outside regular keyframe updates
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<EventArgs> ValueChanged;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when this property or any of it's ancestors visibility is changed
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<EventArgs> VisibilityChanged;
|
|
||||||
|
|
||||||
protected virtual void OnValueChanged()
|
|
||||||
{
|
|
||||||
ValueChanged?.Invoke(this, EventArgs.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnVisibilityChanged()
|
|
||||||
{
|
|
||||||
VisibilityChanged?.Invoke(this, EventArgs.Empty);
|
|
||||||
foreach (var baseLayerProperty in Children)
|
|
||||||
baseLayerProperty.OnVisibilityChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,223 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Artemis.Core.Utilities;
|
||||||
|
using Artemis.Storage.Entities.Profile;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Models.Profile.LayerProperties
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a property on a layer. Properties are saved in storage and can optionally be modified from the UI.
|
||||||
|
/// <para>
|
||||||
|
/// Note: You cannot initialize layer properties yourself. If properly placed, the Artemis core will initialize
|
||||||
|
/// these for you.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of property encapsulated in this layer property</typeparam>
|
||||||
|
public abstract class GenericLayerProperty<T> : LayerProperty
|
||||||
|
{
|
||||||
|
private T _currentValue;
|
||||||
|
private List<LayerPropertyKeyframe<T>> _keyframes;
|
||||||
|
private T _baseValue;
|
||||||
|
|
||||||
|
protected GenericLayerProperty()
|
||||||
|
{
|
||||||
|
_keyframes = new List<LayerPropertyKeyframe<T>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the base value of this layer property without any keyframes applied
|
||||||
|
/// </summary>
|
||||||
|
public T BaseValue
|
||||||
|
{
|
||||||
|
get => _baseValue;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_baseValue != null && !_baseValue.Equals(value) || _baseValue == null && value != null)
|
||||||
|
{
|
||||||
|
_baseValue = value;
|
||||||
|
OnBaseValueChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the current value of this property as it is affected by it's keyframes, updated once every frame
|
||||||
|
/// </summary>
|
||||||
|
public T CurrentValue
|
||||||
|
{
|
||||||
|
get => !KeyframesEnabled || !KeyframesSupported ? BaseValue : _currentValue;
|
||||||
|
internal set => _currentValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets whether keyframes are supported on this property
|
||||||
|
/// </summary>
|
||||||
|
public bool KeyframesSupported { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether keyframes are enabled on this property, has no effect if <see cref="KeyframesSupported" /> is
|
||||||
|
/// False
|
||||||
|
/// </summary>
|
||||||
|
public bool KeyframesEnabled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether the property is hidden in the UI
|
||||||
|
/// </summary>
|
||||||
|
public bool IsHidden { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the BaseValue was loaded from storage, useful to check whether a default value must be applied
|
||||||
|
/// </summary>
|
||||||
|
public bool IsLoadedFromStorage { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a read-only list of all the keyframes on this layer property
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyList<LayerPropertyKeyframe<T>> Keyframes => _keyframes.AsReadOnly();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the total progress on the timeline
|
||||||
|
/// </summary>
|
||||||
|
public TimeSpan TimelineProgress { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the current keyframe in the timeline according to the current progress
|
||||||
|
/// </summary>
|
||||||
|
public LayerPropertyKeyframe<T> CurrentKeyframe { get; protected set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the next keyframe in the timeline according to the current progress
|
||||||
|
/// </summary>
|
||||||
|
public LayerPropertyKeyframe<T> NextKeyframe { get; protected set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the property, moving the timeline forwards by the provided <paramref name="deltaTime" />
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="deltaTime">The amount of time to move the timeline forwards</param>
|
||||||
|
public void Update(double deltaTime)
|
||||||
|
{
|
||||||
|
TimelineProgress = TimelineProgress.Add(TimeSpan.FromSeconds(deltaTime));
|
||||||
|
if (!KeyframesSupported || !KeyframesEnabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// The current keyframe is the last keyframe before the current time
|
||||||
|
CurrentKeyframe = _keyframes.LastOrDefault(k => k.Position <= TimelineProgress);
|
||||||
|
// The next keyframe is the first keyframe that's after the current time
|
||||||
|
NextKeyframe = _keyframes.FirstOrDefault(k => k.Position > TimelineProgress);
|
||||||
|
|
||||||
|
// No need to update the current value if either of the keyframes are null
|
||||||
|
if (CurrentKeyframe == null)
|
||||||
|
CurrentValue = BaseValue;
|
||||||
|
else if (NextKeyframe == null)
|
||||||
|
CurrentValue = CurrentKeyframe.Value;
|
||||||
|
// Only determine progress and current value if both keyframes are present
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var timeDiff = NextKeyframe.Position - CurrentKeyframe.Position;
|
||||||
|
var keyframeProgress = (float) ((TimelineProgress - CurrentKeyframe.Position).TotalMilliseconds / timeDiff.TotalMilliseconds);
|
||||||
|
var keyframeProgressEased = (float) Easings.Interpolate(keyframeProgress, CurrentKeyframe.EasingFunction);
|
||||||
|
UpdateCurrentValue(keyframeProgress, keyframeProgressEased);
|
||||||
|
}
|
||||||
|
|
||||||
|
OnUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Overrides the timeline progress to match the provided <paramref name="progress" />
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="progress">The new progress to set the layer property timeline to.</param>
|
||||||
|
public void OverrideProgress(TimeSpan progress)
|
||||||
|
{
|
||||||
|
TimelineProgress = TimeSpan.Zero;
|
||||||
|
Update(progress.TotalSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a keyframe to the layer property
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="keyframe">The keyframe to add</param>
|
||||||
|
public void AddKeyframe(LayerPropertyKeyframe<T> keyframe)
|
||||||
|
{
|
||||||
|
keyframe.LayerProperty = this;
|
||||||
|
_keyframes.Add(keyframe);
|
||||||
|
SortKeyframes();
|
||||||
|
OnKeyframeAdded();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a keyframe from the layer property
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="keyframe">The keyframe to remove</param>
|
||||||
|
public void RemoveKeyframe(LayerPropertyKeyframe<T> keyframe)
|
||||||
|
{
|
||||||
|
_keyframes.Remove(keyframe);
|
||||||
|
keyframe.LayerProperty = null;
|
||||||
|
SortKeyframes();
|
||||||
|
OnKeyframeRemoved();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called every update (if keyframes are both supported and enabled) to determine the new <see cref="CurrentValue" />
|
||||||
|
/// based on the provided progress
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="keyframeProgress">The linear current keyframe progress</param>
|
||||||
|
/// <param name="keyframeProgressEased">The current keyframe progress, eased with the current easing function</param>
|
||||||
|
protected abstract void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased);
|
||||||
|
|
||||||
|
internal void SortKeyframes()
|
||||||
|
{
|
||||||
|
_keyframes = _keyframes.OrderBy(k => k.Position).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal override void LoadFromEntity(PropertyEntity entity)
|
||||||
|
{
|
||||||
|
BaseValue = JsonConvert.DeserializeObject<T>(entity.Value);
|
||||||
|
CurrentValue = BaseValue;
|
||||||
|
|
||||||
|
_keyframes.Clear();
|
||||||
|
foreach (var keyframeEntity in entity.KeyframeEntities)
|
||||||
|
{
|
||||||
|
var value = JsonConvert.DeserializeObject<T>(keyframeEntity.Value);
|
||||||
|
var keyframe = new LayerPropertyKeyframe<T>(value, keyframeEntity.Position, (Easings.Functions) keyframeEntity.EasingFunction);
|
||||||
|
_keyframes.Add(keyframe);
|
||||||
|
}
|
||||||
|
SortKeyframes();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Events
|
||||||
|
|
||||||
|
public event EventHandler Updated;
|
||||||
|
public event EventHandler BaseValueChanged;
|
||||||
|
public event EventHandler KeyframeAdded;
|
||||||
|
public event EventHandler KeyframeRemoved;
|
||||||
|
|
||||||
|
protected virtual void OnUpdated()
|
||||||
|
{
|
||||||
|
Updated?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnBaseValueChanged()
|
||||||
|
{
|
||||||
|
BaseValueChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnKeyframeAdded()
|
||||||
|
{
|
||||||
|
KeyframeAdded?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void OnKeyframeRemoved()
|
||||||
|
{
|
||||||
|
KeyframeRemoved?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class LayerProperty
|
||||||
|
{
|
||||||
|
internal abstract void LoadFromEntity(PropertyEntity entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,18 +0,0 @@
|
|||||||
namespace Artemis.Core.Models.Profile.LayerProperties
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public class Keyframe<T> : BaseKeyframe
|
|
||||||
{
|
|
||||||
public Keyframe(Layer layer, LayerProperty<T> propertyBase) : base(layer, propertyBase)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public LayerProperty<T> Property => (LayerProperty<T>) BaseProperty;
|
|
||||||
|
|
||||||
public T Value
|
|
||||||
{
|
|
||||||
get => BaseValue != null ? (T) BaseValue : default;
|
|
||||||
set => BaseValue = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,76 +0,0 @@
|
|||||||
using System.Collections.ObjectModel;
|
|
||||||
using System.Linq;
|
|
||||||
using Artemis.Core.Plugins.LayerBrush;
|
|
||||||
using Artemis.Core.Plugins.Models;
|
|
||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.LayerProperties
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a property on the layer. This property is visible in the profile editor and can be key-framed (unless
|
|
||||||
/// opted out).
|
|
||||||
/// <para>To create and register a new LayerProperty use <see cref="LayerBrush.RegisterLayerProperty" /></para>
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
public class LayerProperty<T> : BaseLayerProperty
|
|
||||||
{
|
|
||||||
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))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
internal LayerProperty(Layer layer, PluginInfo pluginInfo, BaseLayerProperty parent, string id, string name, string description)
|
|
||||||
: base(layer, pluginInfo, parent, id, name, description, typeof(T))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the value of the property without any keyframes applied
|
|
||||||
/// </summary>
|
|
||||||
public T Value
|
|
||||||
{
|
|
||||||
get => BaseValue != null ? (T) BaseValue : default;
|
|
||||||
set => BaseValue = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the value of the property with keyframes applied
|
|
||||||
/// </summary>
|
|
||||||
public T CurrentValue
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var currentValue = GetCurrentValue();
|
|
||||||
return currentValue == null ? default : (T) currentValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a list of keyframes defining different values of the property in time, this list contains the strongly typed
|
|
||||||
/// <see cref="Keyframe{T}" />
|
|
||||||
/// </summary>
|
|
||||||
public ReadOnlyCollection<Keyframe<T>> Keyframes => BaseKeyframes.Cast<Keyframe<T>>().ToList().AsReadOnly();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a keyframe to the property.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="keyframe">The keyframe to remove</param>
|
|
||||||
public void AddKeyframe(Keyframe<T> keyframe)
|
|
||||||
{
|
|
||||||
base.AddKeyframe(keyframe);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes a keyframe from the property.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="keyframe">The keyframe to remove</param>
|
|
||||||
public void RemoveKeyframe(Keyframe<T> keyframe)
|
|
||||||
{
|
|
||||||
base.RemoveKeyframe(keyframe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,225 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Artemis.Core.Events;
|
|
||||||
using Artemis.Core.Exceptions;
|
|
||||||
using Artemis.Core.Plugins.Models;
|
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.LayerProperties
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Contains all the properties of the layer and provides easy access to the default properties.
|
|
||||||
/// </summary>
|
|
||||||
public class LayerPropertyCollection : IEnumerable<BaseLayerProperty>
|
|
||||||
{
|
|
||||||
private readonly Dictionary<(Guid, string), BaseLayerProperty> _properties;
|
|
||||||
|
|
||||||
internal LayerPropertyCollection(Layer layer)
|
|
||||||
{
|
|
||||||
_properties = new Dictionary<(Guid, string), BaseLayerProperty>();
|
|
||||||
|
|
||||||
Layer = layer;
|
|
||||||
CreateDefaultProperties();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the layer these properties are applied on
|
|
||||||
/// </summary>
|
|
||||||
public Layer Layer { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IEnumerator<BaseLayerProperty> GetEnumerator()
|
|
||||||
{
|
|
||||||
return _properties.Values.GetEnumerator();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
|
||||||
{
|
|
||||||
return GetEnumerator();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If found, returns the <see cref="LayerProperty{T}" /> matching the provided ID
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of the layer property</typeparam>
|
|
||||||
/// <param name="pluginInfo">The plugin this property belongs to</param>
|
|
||||||
/// <param name="id"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public LayerProperty<T> GetLayerPropertyById<T>(PluginInfo pluginInfo, string id)
|
|
||||||
{
|
|
||||||
if (!_properties.ContainsKey((pluginInfo.Guid, id)))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
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<T>)_properties[(pluginInfo.Guid, id)];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes the provided layer property from the layer.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">The type of value of the layer property</typeparam>
|
|
||||||
/// <param name="layerProperty">The property to remove from the layer</param>
|
|
||||||
internal void RemoveLayerProperty<T>(LayerProperty<T> layerProperty)
|
|
||||||
{
|
|
||||||
RemoveLayerProperty((BaseLayerProperty) layerProperty);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes the provided layer property from the layer.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="layerProperty">The property to remove from the layer</param>
|
|
||||||
internal 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(new LayerPropertyEventArgs(property));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 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.
|
|
||||||
/// </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>
|
|
||||||
internal bool RegisterLayerProperty<T>(LayerProperty<T> layerProperty)
|
|
||||||
{
|
|
||||||
return RegisterLayerProperty((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>
|
|
||||||
internal bool RegisterLayerProperty(BaseLayerProperty layerProperty)
|
|
||||||
{
|
|
||||||
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 entity = Layer.LayerEntity.PropertyEntities.FirstOrDefault(p => p.Id == layerProperty.Id && p.ValueType == layerProperty.Type.Name);
|
|
||||||
// TODO: Catch serialization exceptions and log them
|
|
||||||
if (entity != null)
|
|
||||||
layerProperty.ApplyToProperty(entity);
|
|
||||||
|
|
||||||
_properties.Add((layerProperty.PluginInfo.Guid, layerProperty.Id), layerProperty);
|
|
||||||
OnLayerPropertyRegistered(new LayerPropertyEventArgs(layerProperty));
|
|
||||||
return entity != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Default properties
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the shape type property of the layer
|
|
||||||
/// </summary>
|
|
||||||
public LayerProperty<LayerShapeType> ShapeType { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the fill type property of the layer
|
|
||||||
/// </summary>
|
|
||||||
public LayerProperty<LayerFillType> FillType { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the blend mode property of the layer
|
|
||||||
/// </summary>
|
|
||||||
public LayerProperty<SKBlendMode> BlendMode { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the brush reference property of the layer
|
|
||||||
/// </summary>
|
|
||||||
public LayerProperty<LayerBrushReference> BrushReference { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the anchor point property of the layer
|
|
||||||
/// </summary>
|
|
||||||
public LayerProperty<SKPoint> AnchorPoint { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the position of the layer
|
|
||||||
/// </summary>
|
|
||||||
public LayerProperty<SKPoint> Position { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the size property of the layer
|
|
||||||
/// </summary>
|
|
||||||
public LayerProperty<SKSize> Scale { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the rotation property of the layer range 0 - 360
|
|
||||||
/// </summary>
|
|
||||||
public LayerProperty<float> Rotation { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the opacity property of the layer range 0 - 100
|
|
||||||
/// </summary>
|
|
||||||
public LayerProperty<float> Opacity { get; private set; }
|
|
||||||
|
|
||||||
private void CreateDefaultProperties()
|
|
||||||
{
|
|
||||||
// Shape
|
|
||||||
var shape = new LayerProperty<object>(Layer, "Core.Shape", "Shape", "A collection of basic shape properties");
|
|
||||||
ShapeType = new LayerProperty<LayerShapeType>(Layer, shape, "Core.ShapeType", "Shape type", "The type of shape to draw in this layer") {CanUseKeyframes = false};
|
|
||||||
FillType = new LayerProperty<LayerFillType>(Layer, shape, "Core.FillType", "Fill type", "How to make the shape adjust to scale changes") {CanUseKeyframes = false};
|
|
||||||
BlendMode = new LayerProperty<SKBlendMode>(Layer, shape, "Core.BlendMode", "Blend mode", "How to blend this layer into the resulting image") {CanUseKeyframes = false};
|
|
||||||
ShapeType.Value = LayerShapeType.Rectangle;
|
|
||||||
FillType.Value = LayerFillType.Stretch;
|
|
||||||
BlendMode.Value = SKBlendMode.SrcOver;
|
|
||||||
|
|
||||||
RegisterLayerProperty(shape);
|
|
||||||
foreach (var shapeProperty in shape.Children)
|
|
||||||
RegisterLayerProperty(shapeProperty);
|
|
||||||
|
|
||||||
// Brush
|
|
||||||
var brush = new LayerProperty<object>(Layer, "Core.Brush", "Brush", "A collection of properties that configure the selected brush");
|
|
||||||
BrushReference = new LayerProperty<LayerBrushReference>(Layer, 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<object>(Layer, "Core.Transform", "Transform", "A collection of transformation properties") {ExpandByDefault = true};
|
|
||||||
AnchorPoint = new LayerProperty<SKPoint>(Layer, transform, "Core.AnchorPoint", "Anchor Point", "The point at which the shape is attached to its position") {InputStepSize = 0.001f};
|
|
||||||
Position = new LayerProperty<SKPoint>(Layer, transform, "Core.Position", "Position", "The position of the shape") {InputStepSize = 0.001f};
|
|
||||||
Scale = new LayerProperty<SKSize>(Layer, transform, "Core.Scale", "Scale", "The scale of the shape") {InputAffix = "%", MinInputValue = 0f};
|
|
||||||
Rotation = new LayerProperty<float>(Layer, transform, "Core.Rotation", "Rotation", "The rotation of the shape in degrees") {InputAffix = "°"};
|
|
||||||
Opacity = new LayerProperty<float>(Layer, transform, "Core.Opacity", "Opacity", "The opacity of the shape") {InputAffix = "%", MinInputValue = 0f, MaxInputValue = 100f};
|
|
||||||
Scale.Value = new SKSize(100, 100);
|
|
||||||
Opacity.Value = 100;
|
|
||||||
|
|
||||||
RegisterLayerProperty(transform);
|
|
||||||
foreach (var transformProperty in transform.Children)
|
|
||||||
RegisterLayerProperty(transformProperty);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Events
|
|
||||||
|
|
||||||
public event EventHandler<LayerPropertyEventArgs> LayerPropertyRegistered;
|
|
||||||
public event EventHandler<LayerPropertyEventArgs> LayerPropertyRemoved;
|
|
||||||
|
|
||||||
private void OnLayerPropertyRegistered(LayerPropertyEventArgs e)
|
|
||||||
{
|
|
||||||
LayerPropertyRegistered?.Invoke(this, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnLayerPropertyRemoved(LayerPropertyEventArgs e)
|
|
||||||
{
|
|
||||||
LayerPropertyRemoved?.Invoke(this, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -3,19 +3,18 @@ using Artemis.Core.Utilities;
|
|||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.LayerProperties
|
namespace Artemis.Core.Models.Profile.LayerProperties
|
||||||
{
|
{
|
||||||
public class LayerPropertyKeyFrame<T>
|
public class LayerPropertyKeyframe<T>
|
||||||
{
|
{
|
||||||
private TimeSpan _position;
|
private TimeSpan _position;
|
||||||
|
|
||||||
public LayerPropertyKeyFrame(LayerProperty<T> layerProperty, T value, TimeSpan position, Easings.Functions easingFunction)
|
public LayerPropertyKeyframe(T value, TimeSpan position, Easings.Functions easingFunction)
|
||||||
{
|
{
|
||||||
_position = position;
|
_position = position;
|
||||||
Value = value;
|
Value = value;
|
||||||
LayerProperty = layerProperty;
|
|
||||||
EasingFunction = easingFunction;
|
EasingFunction = easingFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LayerProperty<T> LayerProperty { get; set; }
|
public GenericLayerProperty<T> LayerProperty { get; internal set; }
|
||||||
public T Value { get; set; }
|
public T Value { get; set; }
|
||||||
|
|
||||||
public TimeSpan Position
|
public TimeSpan Position
|
||||||
|
|||||||
@ -0,0 +1,19 @@
|
|||||||
|
using Artemis.Core.Exceptions;
|
||||||
|
using Artemis.Core.Models.Profile.Colors;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Models.Profile.LayerProperties.Types
|
||||||
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public class ColorGradientLayerProperty : GenericLayerProperty<ColorGradient>
|
||||||
|
{
|
||||||
|
internal ColorGradientLayerProperty()
|
||||||
|
{
|
||||||
|
KeyframesSupported = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased)
|
||||||
|
{
|
||||||
|
throw new ArtemisCoreException("Color Gradients do not support keyframes.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
using Artemis.Core.Exceptions;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Models.Profile.LayerProperties.Types
|
||||||
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public class EnumLayerProperty<T> : GenericLayerProperty<T> where T : System.Enum
|
||||||
|
{
|
||||||
|
public EnumLayerProperty()
|
||||||
|
{
|
||||||
|
KeyframesSupported = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased)
|
||||||
|
{
|
||||||
|
throw new ArtemisCoreException("Enum properties do not support keyframes.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,12 @@
|
|||||||
using Artemis.Core.Models.Profile.LayerProperties.Abstract;
|
namespace Artemis.Core.Models.Profile.LayerProperties.Types
|
||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.LayerProperties
|
|
||||||
{
|
{
|
||||||
public class FloatLayerProperty : LayerProperty<float>
|
/// <inheritdoc/>
|
||||||
|
public class FloatLayerProperty : GenericLayerProperty<float>
|
||||||
{
|
{
|
||||||
|
internal FloatLayerProperty()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
protected override void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased)
|
protected override void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased)
|
||||||
{
|
{
|
||||||
var diff = NextKeyframe.Value - CurrentKeyframe.Value;
|
var diff = NextKeyframe.Value - CurrentKeyframe.Value;
|
||||||
@ -1,10 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using Artemis.Core.Models.Profile.LayerProperties.Abstract;
|
|
||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.LayerProperties
|
namespace Artemis.Core.Models.Profile.LayerProperties.Types
|
||||||
{
|
{
|
||||||
public class IntLayerProperty : LayerProperty<int>
|
/// <inheritdoc/>
|
||||||
|
public class IntLayerProperty : GenericLayerProperty<int>
|
||||||
{
|
{
|
||||||
|
internal IntLayerProperty()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
protected override void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased)
|
protected override void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased)
|
||||||
{
|
{
|
||||||
var diff = NextKeyframe.Value - CurrentKeyframe.Value;
|
var diff = NextKeyframe.Value - CurrentKeyframe.Value;
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
using Artemis.Core.Exceptions;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Models.Profile.LayerProperties.Types
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A special layer property used to configure the selected layer brush
|
||||||
|
/// </summary>
|
||||||
|
public class LayerBrushReferenceLayerProperty : GenericLayerProperty<LayerBrushReference>
|
||||||
|
{
|
||||||
|
internal LayerBrushReferenceLayerProperty()
|
||||||
|
{
|
||||||
|
KeyframesSupported = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased)
|
||||||
|
{
|
||||||
|
throw new ArtemisCoreException("Layer brush references do not support keyframes.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,11 +1,15 @@
|
|||||||
using System;
|
using System;
|
||||||
using Artemis.Core.Models.Profile.LayerProperties.Abstract;
|
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.LayerProperties
|
namespace Artemis.Core.Models.Profile.LayerProperties.Types
|
||||||
{
|
{
|
||||||
public class SKColorLayerProperty : LayerProperty<SKColor>
|
/// <inheritdoc/>
|
||||||
|
public class SKColorLayerProperty : GenericLayerProperty<SKColor>
|
||||||
{
|
{
|
||||||
|
internal SKColorLayerProperty()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
protected override void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased)
|
protected override void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased)
|
||||||
{
|
{
|
||||||
var redDiff = NextKeyframe.Value.Red - CurrentKeyframe.Value.Red;
|
var redDiff = NextKeyframe.Value.Red - CurrentKeyframe.Value.Red;
|
||||||
@ -1,10 +1,14 @@
|
|||||||
using Artemis.Core.Models.Profile.LayerProperties.Abstract;
|
using SkiaSharp;
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.LayerProperties
|
namespace Artemis.Core.Models.Profile.LayerProperties.Types
|
||||||
{
|
{
|
||||||
public class SKPointLayerProperty : LayerProperty<SKPoint>
|
/// <inheritdoc/>
|
||||||
|
public class SKPointLayerProperty : GenericLayerProperty<SKPoint>
|
||||||
{
|
{
|
||||||
|
internal SKPointLayerProperty()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
protected override void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased)
|
protected override void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased)
|
||||||
{
|
{
|
||||||
var xDiff = NextKeyframe.Value.X - CurrentKeyframe.Value.X;
|
var xDiff = NextKeyframe.Value.X - CurrentKeyframe.Value.X;
|
||||||
@ -1,10 +1,14 @@
|
|||||||
using Artemis.Core.Models.Profile.LayerProperties.Abstract;
|
using SkiaSharp;
|
||||||
using SkiaSharp;
|
|
||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.LayerProperties
|
namespace Artemis.Core.Models.Profile.LayerProperties.Types
|
||||||
{
|
{
|
||||||
public class SKSizeLayerProperty : LayerProperty<SKSize>
|
/// <inheritdoc/>
|
||||||
|
public class SKSizeLayerProperty : GenericLayerProperty<SKSize>
|
||||||
{
|
{
|
||||||
|
internal SKSizeLayerProperty()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
protected override void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased)
|
protected override void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased)
|
||||||
{
|
{
|
||||||
var widthDiff = NextKeyframe.Value.Width - CurrentKeyframe.Value.Width;
|
var widthDiff = NextKeyframe.Value.Width - CurrentKeyframe.Value.Width;
|
||||||
62
src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs
Normal file
62
src/Artemis.Core/Models/Profile/LayerPropertyGroup.cs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Artemis.Core.Models.Profile.LayerProperties;
|
||||||
|
using Artemis.Core.Models.Profile.LayerProperties.Attributes;
|
||||||
|
using Artemis.Core.Plugins.Exceptions;
|
||||||
|
using Artemis.Core.Services.Interfaces;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Models.Profile
|
||||||
|
{
|
||||||
|
public class LayerPropertyGroup
|
||||||
|
{
|
||||||
|
public bool PropertiesInitialized { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when all layer properties in this property group have been initialized
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void OnPropertiesInitialized()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void InitializeProperties(ILayerService layerService, Layer layer, string path)
|
||||||
|
{
|
||||||
|
// Get all properties with a PropertyDescriptionAttribute
|
||||||
|
foreach (var propertyInfo in GetType().GetProperties())
|
||||||
|
{
|
||||||
|
var propertyDescription = Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyDescriptionAttribute));
|
||||||
|
if (propertyDescription != null)
|
||||||
|
{
|
||||||
|
if (!typeof(GenericLayerProperty<>).IsAssignableFrom(propertyInfo.PropertyType))
|
||||||
|
throw new ArtemisPluginException("Layer property with PropertyDescription attribute must be of type LayerProperty");
|
||||||
|
|
||||||
|
var instance = (LayerProperty) Activator.CreateInstance(propertyInfo.PropertyType);
|
||||||
|
InitializeProperty(layer, path, instance);
|
||||||
|
propertyInfo.SetValue(this, instance);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var propertyGroupDescription = Attribute.GetCustomAttribute(propertyInfo, typeof(PropertyGroupDescriptionAttribute));
|
||||||
|
if (propertyGroupDescription != null)
|
||||||
|
{
|
||||||
|
if (!typeof(LayerPropertyGroup).IsAssignableFrom(propertyInfo.PropertyType))
|
||||||
|
throw new ArtemisPluginException("Layer property with PropertyGroupDescription attribute must be of type LayerPropertyGroup");
|
||||||
|
|
||||||
|
var instance = (LayerPropertyGroup) Activator.CreateInstance(propertyInfo.PropertyType);
|
||||||
|
instance.InitializeProperties(layerService, layer, $"{path}{propertyInfo.Name}.");
|
||||||
|
propertyInfo.SetValue(this, instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OnPropertiesInitialized();
|
||||||
|
PropertiesInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeProperty(Layer layer, string path, LayerProperty instance)
|
||||||
|
{
|
||||||
|
var entity = layer.LayerEntity.PropertyEntities.FirstOrDefault(p => p.Id == path);
|
||||||
|
if (entity != null)
|
||||||
|
instance.LoadFromEntity(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/Artemis.Core/Models/Profile/LayerTransformProperties.cs
Normal file
34
src/Artemis.Core/Models/Profile/LayerTransformProperties.cs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
using Artemis.Core.Models.Profile.LayerProperties;
|
||||||
|
using Artemis.Core.Models.Profile.LayerProperties.Attributes;
|
||||||
|
using Artemis.Core.Models.Profile.LayerProperties.Types;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Models.Profile
|
||||||
|
{
|
||||||
|
public class LayerTransformProperties : LayerPropertyGroup
|
||||||
|
{
|
||||||
|
[PropertyDescription(Description = "The point at which the shape is attached to its position", InputStepSize = 0.001f)]
|
||||||
|
public SKPointLayerProperty AnchorPoint { get; set; }
|
||||||
|
|
||||||
|
[PropertyDescription(Description = "The position of the shape", InputStepSize = 0.001f)]
|
||||||
|
public SKPointLayerProperty Position { get; set; }
|
||||||
|
|
||||||
|
[PropertyDescription(Description = "The scale of the shape", InputAffix = "%", MinInputValue = 0f)]
|
||||||
|
public SKSizeLayerProperty Scale { get; set; }
|
||||||
|
|
||||||
|
[PropertyDescription(Description = "The rotation of the shape in degrees", InputAffix = "°")]
|
||||||
|
public FloatLayerProperty Rotation { get; set; }
|
||||||
|
|
||||||
|
[PropertyDescription(Description = "The opacity of the shape", InputAffix = "%", MinInputValue = 0f, MaxInputValue = 100f)]
|
||||||
|
public FloatLayerProperty Opacity { get; set; }
|
||||||
|
|
||||||
|
protected override void OnPropertiesInitialized()
|
||||||
|
{
|
||||||
|
// Populate defaults
|
||||||
|
if (!Scale.IsLoadedFromStorage)
|
||||||
|
Scale.BaseValue = new SKSize(100, 100);
|
||||||
|
if (!Opacity.IsLoadedFromStorage)
|
||||||
|
Opacity.BaseValue = 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/Artemis.Core/Plugins/LayerBrush/ILayerBrush.cs
Normal file
39
src/Artemis.Core/Plugins/LayerBrush/ILayerBrush.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using Artemis.Core.Models.Profile;
|
||||||
|
using Artemis.Core.Services.Interfaces;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Plugins.LayerBrush
|
||||||
|
{
|
||||||
|
public interface ILayerBrush : IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the layer this brush is applied to
|
||||||
|
/// </summary>
|
||||||
|
Layer Layer { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the descriptor of this brush
|
||||||
|
/// </summary>
|
||||||
|
LayerBrushDescriptor Descriptor { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called before rendering every frame, write your update logic here
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="deltaTime"></param>
|
||||||
|
void Update(double deltaTime);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The main method of rendering anything to the layer. The provided <see cref="SKCanvas" /> is specific to the layer
|
||||||
|
/// and matches it's width and height.
|
||||||
|
/// <para>Called during rendering or layer preview, in the order configured on the layer</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="canvas">The layer canvas</param>
|
||||||
|
/// <param name="canvasInfo"></param>
|
||||||
|
/// <param name="path">The path to be filled, represents the shape</param>
|
||||||
|
/// <param name="paint">The paint to be used to fill the shape</param>
|
||||||
|
void Render(SKCanvas canvas, SKImageInfo canvasInfo, SKPath path, SKPaint paint);
|
||||||
|
|
||||||
|
public void InitializeProperties(ILayerService layerService, string path);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,16 +1,13 @@
|
|||||||
using System;
|
using Artemis.Core.Models.Profile;
|
||||||
using System.Linq;
|
|
||||||
using Artemis.Core.Models.Profile;
|
|
||||||
using Artemis.Core.Models.Profile.LayerProperties;
|
|
||||||
using Artemis.Core.Plugins.Exceptions;
|
using Artemis.Core.Plugins.Exceptions;
|
||||||
using Artemis.Core.Services.Interfaces;
|
using Artemis.Core.Services.Interfaces;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
|
|
||||||
namespace Artemis.Core.Plugins.LayerBrush
|
namespace Artemis.Core.Plugins.LayerBrush
|
||||||
{
|
{
|
||||||
public abstract class LayerBrush : IDisposable
|
public abstract class LayerBrush<T> : ILayerBrush where T : LayerPropertyGroup
|
||||||
{
|
{
|
||||||
private ILayerService _layerService;
|
private T _properties;
|
||||||
|
|
||||||
protected LayerBrush(Layer layer, LayerBrushDescriptor descriptor)
|
protected LayerBrush(Layer layer, LayerBrushDescriptor descriptor)
|
||||||
{
|
{
|
||||||
@ -18,112 +15,63 @@ namespace Artemis.Core.Plugins.LayerBrush
|
|||||||
Descriptor = descriptor;
|
Descriptor = descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public Layer Layer { get; }
|
public Layer Layer { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public LayerBrushDescriptor Descriptor { get; }
|
public LayerBrushDescriptor Descriptor { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public virtual void Dispose()
|
public virtual void Dispose()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Called before rendering every frame, write your update logic here
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="deltaTime"></param>
|
|
||||||
public virtual void Update(double deltaTime)
|
public virtual void Update(double deltaTime)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// The main method of rendering anything to the layer. The provided <see cref="SKCanvas" /> is specific to the layer
|
|
||||||
/// and matches it's width and height.
|
|
||||||
/// <para>Called during rendering or layer preview, in the order configured on the layer</para>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="canvas">The layer canvas</param>
|
|
||||||
/// <param name="canvasInfo"></param>
|
|
||||||
/// <param name="path">The path to be filled, represents the shape</param>
|
|
||||||
/// <param name="paint">The paint to be used to fill the shape</param>
|
|
||||||
public virtual void Render(SKCanvas canvas, SKImageInfo canvasInfo, SKPath path, SKPaint paint)
|
public virtual void Render(SKCanvas canvas, SKImageInfo canvasInfo, SKPath path, SKPaint paint)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Properties
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides an easy way to add your own properties to the layer, for more info see <see cref="LayerProperty{T}" />.
|
/// Gets the properties of this brush.
|
||||||
/// <para>Note: If found, the last value and keyframes are loaded and set when calling this method.</para>
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T"></typeparam>
|
public T Properties
|
||||||
/// <param name="parent">The parent of this property, use this to create a tree-hierarchy in the editor</param>
|
|
||||||
/// <param name="id">A and ID identifying your property, must be unique within your plugin</param>
|
|
||||||
/// <param name="name">A name for your property, this is visible in the editor</param>
|
|
||||||
/// <param name="description">A description for your property, this is visible in the editor</param>
|
|
||||||
/// <param name="defaultValue">The default value of the property, if not configured by the user</param>
|
|
||||||
/// <returns>The layer property</returns>
|
|
||||||
protected LayerProperty<T> RegisterLayerProperty<T>(BaseLayerProperty parent, string id, string name, string description, T defaultValue = default)
|
|
||||||
{
|
{
|
||||||
var property = new LayerProperty<T>(Layer, Descriptor.LayerBrushProvider.PluginInfo, parent, id, name, description) {Value = defaultValue};
|
get
|
||||||
Layer.Properties.RegisterLayerProperty(property);
|
{
|
||||||
// It's fine if this is null, it'll be picked up by SetLayerService later
|
// I imagine a null reference here can be confusing, so lets throw an exception explaining what to do
|
||||||
_layerService?.InstantiateKeyframeEngine(property);
|
if (_properties == null)
|
||||||
return property;
|
throw new ArtemisPluginException("Cannot access brush properties until OnPropertiesInitialized has been called");
|
||||||
|
return _properties;
|
||||||
|
}
|
||||||
|
internal set => _properties = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides an easy way to add your own properties to the layer, for more info see <see cref="LayerProperty{T}" />.
|
/// Gets whether all properties on this brush are initialized
|
||||||
/// <para>Note: If found, the last value and keyframes are loaded and set when calling this method.</para>
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T"></typeparam>
|
public bool PropertiesInitialized { get; private set; }
|
||||||
/// <param name="id">A and ID identifying your property, must be unique within your plugin</param>
|
|
||||||
/// <param name="name">A name for your property, this is visible in the editor</param>
|
|
||||||
/// <param name="description">A description for your property, this is visible in the editor</param>
|
|
||||||
/// <param name="defaultValue">The default value of the property, if not configured by the user</param>
|
|
||||||
/// <returns>The layer property</returns>
|
|
||||||
protected LayerProperty<T> RegisterLayerProperty<T>(string id, string name, string description, T defaultValue = default)
|
|
||||||
{
|
|
||||||
// Check if the property already exists
|
|
||||||
var existing = Layer.Properties.FirstOrDefault(p =>
|
|
||||||
p.PluginInfo.Guid == Descriptor.LayerBrushProvider.PluginInfo.Guid &&
|
|
||||||
p.Id == id &&
|
|
||||||
p.Name == name &&
|
|
||||||
p.Description == description);
|
|
||||||
|
|
||||||
if (existing != null)
|
|
||||||
{
|
|
||||||
// If it exists and the types match, return the existing property
|
|
||||||
if (existing.Type == typeof(T))
|
|
||||||
return (LayerProperty<T>) existing;
|
|
||||||
// If it exists and the types are different, something is wrong
|
|
||||||
throw new ArtemisPluginException($"Cannot register the property {id} with different types twice.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var property = new LayerProperty<T>(Layer, Descriptor.LayerBrushProvider.PluginInfo, Layer.Properties.BrushReference.Parent, id, name, description)
|
|
||||||
{
|
|
||||||
Value = defaultValue
|
|
||||||
};
|
|
||||||
|
|
||||||
Layer.Properties.RegisterLayerProperty(property);
|
|
||||||
// It's fine if this is null, it'll be picked up by SetLayerService later
|
|
||||||
_layerService?.InstantiateKeyframeEngine(property);
|
|
||||||
return property;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Allows you to remove layer properties previously added by using <see cref="RegisterLayerProperty{T}(BaseLayerProperty,string,string,string,T)" />.
|
/// Called when all layer properties in this brush have been initialized
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T"></typeparam>
|
protected virtual void OnPropertiesInitialized()
|
||||||
/// <param name="layerProperty"></param>
|
|
||||||
protected void UnRegisterLayerProperty<T>(LayerProperty<T> layerProperty)
|
|
||||||
{
|
{
|
||||||
if (layerProperty == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (Layer.Properties.Any(p => p == layerProperty))
|
|
||||||
Layer.Properties.RemoveLayerProperty(layerProperty);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void SetLayerService(ILayerService layerService)
|
public void InitializeProperties(ILayerService layerService, string path)
|
||||||
{
|
{
|
||||||
_layerService = layerService;
|
Properties.InitializeProperties(layerService, Descriptor.LayerBrushProvider.PluginInfo, path);
|
||||||
foreach (var baseLayerProperty in Layer.Properties)
|
OnPropertiesInitialized();
|
||||||
_layerService.InstantiateKeyframeEngine(baseLayerProperty);
|
PropertiesInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -20,7 +20,7 @@ namespace Artemis.Core.Plugins.LayerBrush
|
|||||||
|
|
||||||
public ReadOnlyCollection<LayerBrushDescriptor> LayerBrushDescriptors => _layerBrushDescriptors.AsReadOnly();
|
public ReadOnlyCollection<LayerBrushDescriptor> LayerBrushDescriptors => _layerBrushDescriptors.AsReadOnly();
|
||||||
|
|
||||||
protected void AddLayerBrushDescriptor<T>(string displayName, string description, string icon) where T : LayerBrush
|
protected void AddLayerBrushDescriptor<T>(string displayName, string description, string icon) where T : ILayerBrush
|
||||||
{
|
{
|
||||||
_layerBrushDescriptors.Add(new LayerBrushDescriptor(displayName, description, icon, typeof(T), this));
|
_layerBrushDescriptors.Add(new LayerBrushDescriptor(displayName, description, icon, typeof(T), this));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,22 +13,9 @@ namespace Artemis.Core.Services.Interfaces
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="layer">The layer to instantiate the brush for</param>
|
/// <param name="layer">The layer to instantiate the brush for</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
LayerBrush InstantiateLayerBrush(Layer layer);
|
ILayerBrush InstantiateLayerBrush(Layer layer);
|
||||||
|
|
||||||
/// <summary>
|
void LoadPropertyBaseValue(Layer layer, string path, object layerProperty);
|
||||||
/// Instantiates and adds a compatible <see cref="KeyframeEngine" /> to the provided <see cref="LayerProperty{T}" />.
|
void LoadPropertyKeyframes(Layer layer, string path, object layerProperty);
|
||||||
/// If the property already has a compatible keyframe engine, nothing happens.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="layerProperty">The layer property to apply the keyframe engine to.</param>
|
|
||||||
/// <returns>The resulting keyframe engine, if a compatible engine was found.</returns>
|
|
||||||
KeyframeEngine InstantiateKeyframeEngine<T>(LayerProperty<T> layerProperty);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Instantiates and adds a compatible <see cref="KeyframeEngine" /> to the provided <see cref="BaseLayerProperty" />.
|
|
||||||
/// If the property already has a compatible keyframe engine, nothing happens.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="layerProperty">The layer property to apply the keyframe engine to.</param>
|
|
||||||
/// <returns>The resulting keyframe engine, if a compatible engine was found.</returns>
|
|
||||||
KeyframeEngine InstantiateKeyframeEngine(BaseLayerProperty layerProperty);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4,7 +4,9 @@ using Artemis.Core.Models.Profile;
|
|||||||
using Artemis.Core.Models.Profile.KeyframeEngines;
|
using Artemis.Core.Models.Profile.KeyframeEngines;
|
||||||
using Artemis.Core.Models.Profile.LayerProperties;
|
using Artemis.Core.Models.Profile.LayerProperties;
|
||||||
using Artemis.Core.Plugins.LayerBrush;
|
using Artemis.Core.Plugins.LayerBrush;
|
||||||
|
using Artemis.Core.Plugins.Models;
|
||||||
using Artemis.Core.Services.Interfaces;
|
using Artemis.Core.Services.Interfaces;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
using Ninject.Parameters;
|
using Ninject.Parameters;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
@ -24,11 +26,25 @@ namespace Artemis.Core.Services
|
|||||||
_pluginService = pluginService;
|
_pluginService = pluginService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LayerBrush InstantiateLayerBrush(Layer layer)
|
public Layer CreateLayer(Profile profile, ProfileElement parent, string name)
|
||||||
|
{
|
||||||
|
var layer = new Layer(profile, parent, name);
|
||||||
|
|
||||||
|
// Layers have two hardcoded property groups, instantiate them
|
||||||
|
layer.General.InitializeProperties(this, layer, null, null);
|
||||||
|
layer.Transform.InitializeProperties(this, layer, null, null);
|
||||||
|
|
||||||
|
// With the properties loaded, the layer brush can be instantiated
|
||||||
|
InstantiateLayerBrush(layer);
|
||||||
|
|
||||||
|
return layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ILayerBrush InstantiateLayerBrush(Layer layer)
|
||||||
{
|
{
|
||||||
RemoveLayerBrush(layer);
|
RemoveLayerBrush(layer);
|
||||||
|
|
||||||
var descriptorReference = layer.Properties.BrushReference.CurrentValue;
|
var descriptorReference = layer.General.BrushReference.CurrentValue;
|
||||||
if (descriptorReference == null)
|
if (descriptorReference == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@ -46,36 +62,13 @@ namespace Artemis.Core.Services
|
|||||||
new ConstructorArgument("layer", layer),
|
new ConstructorArgument("layer", layer),
|
||||||
new ConstructorArgument("descriptor", descriptor)
|
new ConstructorArgument("descriptor", descriptor)
|
||||||
};
|
};
|
||||||
var layerBrush = (LayerBrush) _kernel.Get(descriptor.LayerBrushType, arguments);
|
var layerBrush = (ILayerBrush) _kernel.Get(descriptor.LayerBrushType, arguments);
|
||||||
// Set the layer service after the brush was created to avoid constructor clutter, SetLayerService will play catch-up for us.
|
layerBrush.InitializeProperties(this, null);
|
||||||
// If layer brush implementations need the LayerService they can inject it themselves, but don't require it by default
|
|
||||||
layerBrush.SetLayerService(this);
|
|
||||||
layer.LayerBrush = layerBrush;
|
layer.LayerBrush = layerBrush;
|
||||||
|
|
||||||
return layerBrush;
|
return layerBrush;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeyframeEngine InstantiateKeyframeEngine<T>(LayerProperty<T> layerProperty)
|
|
||||||
{
|
|
||||||
return InstantiateKeyframeEngine((BaseLayerProperty) layerProperty);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KeyframeEngine InstantiateKeyframeEngine(BaseLayerProperty layerProperty)
|
|
||||||
{
|
|
||||||
if (layerProperty.KeyframeEngine != null && layerProperty.KeyframeEngine.CompatibleTypes.Contains(layerProperty.Type))
|
|
||||||
return layerProperty.KeyframeEngine;
|
|
||||||
|
|
||||||
// This creates an instance of each keyframe engine, which is pretty cheap since all the expensive stuff is done during
|
|
||||||
// Initialize() call but it's not ideal
|
|
||||||
var keyframeEngines = _kernel.Get<List<KeyframeEngine>>();
|
|
||||||
var keyframeEngine = keyframeEngines.FirstOrDefault(k => k.CompatibleTypes.Contains(layerProperty.Type));
|
|
||||||
if (keyframeEngine == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
keyframeEngine.Initialize(layerProperty);
|
|
||||||
return keyframeEngine;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemoveLayerBrush(Layer layer)
|
public void RemoveLayerBrush(Layer layer)
|
||||||
{
|
{
|
||||||
if (layer.LayerBrush == null)
|
if (layer.LayerBrush == null)
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
using Artemis.Core.Models.Profile;
|
using Artemis.Core.Models.Profile;
|
||||||
using Artemis.Core.Models.Profile.LayerProperties;
|
using Artemis.Core.Models.Profile.LayerProperties;
|
||||||
using Artemis.Core.Models.Profile.LayerProperties.Abstract;
|
|
||||||
using Artemis.Core.Models.Surface;
|
using Artemis.Core.Models.Surface;
|
||||||
using Artemis.Core.Plugins.Abstract;
|
using Artemis.Core.Plugins.Abstract;
|
||||||
using Artemis.UI.Screens.Module;
|
using Artemis.UI.Screens.Module;
|
||||||
@ -51,7 +50,7 @@ namespace Artemis.UI.Ninject.Factories
|
|||||||
|
|
||||||
public interface ILayerPropertyVmFactory : IVmFactory
|
public interface ILayerPropertyVmFactory : IVmFactory
|
||||||
{
|
{
|
||||||
LayerPropertyViewModel Create(BaseLayerProperty layerProperty, LayerPropertyViewModel parent);
|
LayerPropertyViewModel Create(LayerProperty layerProperty, LayerPropertyViewModel parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IPropertyTreeVmFactory : IVmFactory
|
public interface IPropertyTreeVmFactory : IVmFactory
|
||||||
|
|||||||
@ -169,7 +169,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
|
|||||||
PopulateProperties(e.LayerProperty.Layer);
|
PopulateProperties(e.LayerProperty.Layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LayerPropertyViewModel CreatePropertyViewModel(BaseLayerProperty layerProperty)
|
private LayerPropertyViewModel CreatePropertyViewModel(LayerProperty layerProperty)
|
||||||
{
|
{
|
||||||
LayerPropertyViewModel parent = null;
|
LayerPropertyViewModel parent = null;
|
||||||
// If the property has a parent, find it's VM
|
// If the property has a parent, find it's VM
|
||||||
|
|||||||
@ -16,7 +16,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
|
|||||||
private bool _keyframesEnabled;
|
private bool _keyframesEnabled;
|
||||||
private bool _isExpanded;
|
private bool _isExpanded;
|
||||||
|
|
||||||
public LayerPropertyViewModel(BaseLayerProperty layerProperty, LayerPropertyViewModel parent, IKernel kernel, IProfileEditorService profileEditorService)
|
public LayerPropertyViewModel(LayerProperty layerProperty, LayerPropertyViewModel parent, IKernel kernel, IProfileEditorService profileEditorService)
|
||||||
{
|
{
|
||||||
_kernel = kernel;
|
_kernel = kernel;
|
||||||
_profileEditorService = profileEditorService;
|
_profileEditorService = profileEditorService;
|
||||||
@ -30,7 +30,7 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.LayerProperties
|
|||||||
Parent?.Children.Add(this);
|
Parent?.Children.Add(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BaseLayerProperty LayerProperty { get; }
|
public LayerProperty LayerProperty { get; }
|
||||||
|
|
||||||
public LayerPropertyViewModel Parent { get; }
|
public LayerPropertyViewModel Parent { get; }
|
||||||
public List<LayerPropertyViewModel> Children { get; }
|
public List<LayerPropertyViewModel> Children { get; }
|
||||||
|
|||||||
@ -4,7 +4,6 @@ using System.Linq;
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using Artemis.Core.Models.Profile.LayerProperties;
|
using Artemis.Core.Models.Profile.LayerProperties;
|
||||||
using Artemis.Core.Models.Profile.LayerProperties.Abstract;
|
|
||||||
using Artemis.Core.Utilities;
|
using Artemis.Core.Utilities;
|
||||||
using Artemis.UI.Services.Interfaces;
|
using Artemis.UI.Services.Interfaces;
|
||||||
using Stylet;
|
using Stylet;
|
||||||
|
|||||||
@ -1,15 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Linq;
|
|
||||||
using Artemis.Core.Models.Profile;
|
using Artemis.Core.Models.Profile;
|
||||||
using Artemis.Core.Models.Profile.Colors;
|
|
||||||
using Artemis.Core.Models.Profile.LayerProperties;
|
|
||||||
using Artemis.Core.Plugins.LayerBrush;
|
using Artemis.Core.Plugins.LayerBrush;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
|
|
||||||
namespace Artemis.Plugins.LayerBrushes.Color
|
namespace Artemis.Plugins.LayerBrushes.Color
|
||||||
{
|
{
|
||||||
public class ColorBrush : LayerBrush
|
public class ColorBrush : LayerBrush<ColorBrushProperties>
|
||||||
{
|
{
|
||||||
private SKColor _color;
|
private SKColor _color;
|
||||||
private SKPaint _paint;
|
private SKPaint _paint;
|
||||||
@ -18,33 +14,16 @@ namespace Artemis.Plugins.LayerBrushes.Color
|
|||||||
|
|
||||||
public ColorBrush(Layer layer, LayerBrushDescriptor descriptor) : base(layer, descriptor)
|
public ColorBrush(Layer layer, LayerBrushDescriptor descriptor) : base(layer, descriptor)
|
||||||
{
|
{
|
||||||
GradientTypeProperty = RegisterLayerProperty("Brush.GradientType", "Gradient type", "The type of color brush to draw", GradientType.Solid);
|
|
||||||
GradientTypeProperty.CanUseKeyframes = false;
|
|
||||||
ColorProperty = RegisterLayerProperty("Brush.Color", "Color", "The color of the brush", new SKColor(255, 0, 0));
|
|
||||||
GradientProperty = RegisterLayerProperty("Brush.Gradient", "Gradient", "The gradient of the brush", new ColorGradient());
|
|
||||||
GradientProperty.CanUseKeyframes = false;
|
|
||||||
if (!GradientProperty.Value.Stops.Any())
|
|
||||||
GradientProperty.Value.MakeFabulous();
|
|
||||||
|
|
||||||
UpdateColorProperties();
|
|
||||||
|
|
||||||
Layer.RenderPropertiesUpdated += (sender, args) => CreateShader();
|
Layer.RenderPropertiesUpdated += (sender, args) => CreateShader();
|
||||||
GradientTypeProperty.ValueChanged += (sender, args) => UpdateColorProperties();
|
|
||||||
ColorProperty.ValueChanged += (sender, args) => CreateShader();
|
|
||||||
GradientProperty.Value.PropertyChanged += (sender, args) => CreateShader();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public LayerProperty<SKColor> ColorProperty { get; set; }
|
|
||||||
public LayerProperty<ColorGradient> GradientProperty { get; set; }
|
|
||||||
public LayerProperty<GradientType> GradientTypeProperty { get; set; }
|
|
||||||
|
|
||||||
public override void Update(double deltaTime)
|
public override void Update(double deltaTime)
|
||||||
{
|
{
|
||||||
// Only check if a solid is being drawn, because that can be changed by keyframes
|
// Only check if a solid is being drawn, because that can be changed by keyframes
|
||||||
if (GradientTypeProperty.Value == GradientType.Solid && _color != ColorProperty.CurrentValue)
|
if (Properties.GradientType.BaseValue == GradientType.Solid && _color != Properties.Color.CurrentValue)
|
||||||
{
|
{
|
||||||
// If the color was changed since the last frame, recreate the shader
|
// If the color was changed since the last frame, recreate the shader
|
||||||
_color = ColorProperty.CurrentValue;
|
_color = Properties.Color.CurrentValue;
|
||||||
CreateShader();
|
CreateShader();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,36 +42,35 @@ namespace Artemis.Plugins.LayerBrushes.Color
|
|||||||
canvas.DrawPath(path, paint);
|
canvas.DrawPath(path, paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateColorProperties()
|
protected override void OnPropertiesInitialized()
|
||||||
{
|
{
|
||||||
ColorProperty.IsHidden = GradientTypeProperty.Value != GradientType.Solid;
|
Properties.GradientType.BaseValueChanged += (sender, args) => CreateShader();
|
||||||
GradientProperty.IsHidden = GradientTypeProperty.Value == GradientType.Solid;
|
Properties.Color.BaseValueChanged += (sender, args) => CreateShader();
|
||||||
|
Properties.Gradient.BaseValue.PropertyChanged += (sender, args) => CreateShader();
|
||||||
CreateShader();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateShader()
|
private void CreateShader()
|
||||||
{
|
{
|
||||||
var center = new SKPoint(_shaderBounds.MidX, _shaderBounds.MidY);
|
var center = new SKPoint(_shaderBounds.MidX, _shaderBounds.MidY);
|
||||||
var shader = GradientTypeProperty.CurrentValue switch
|
var shader = Properties.GradientType.CurrentValue switch
|
||||||
{
|
{
|
||||||
GradientType.Solid => SKShader.CreateColor(_color),
|
GradientType.Solid => SKShader.CreateColor(_color),
|
||||||
GradientType.LinearGradient => SKShader.CreateLinearGradient(
|
GradientType.LinearGradient => SKShader.CreateLinearGradient(
|
||||||
new SKPoint(_shaderBounds.Left, _shaderBounds.Top),
|
new SKPoint(_shaderBounds.Left, _shaderBounds.Top),
|
||||||
new SKPoint(_shaderBounds.Right, _shaderBounds.Top),
|
new SKPoint(_shaderBounds.Right, _shaderBounds.Top),
|
||||||
GradientProperty.Value.GetColorsArray(),
|
Properties.Gradient.BaseValue.GetColorsArray(),
|
||||||
GradientProperty.Value.GetPositionsArray(),
|
Properties.Gradient.BaseValue.GetPositionsArray(),
|
||||||
SKShaderTileMode.Repeat),
|
SKShaderTileMode.Repeat),
|
||||||
GradientType.RadialGradient => SKShader.CreateRadialGradient(
|
GradientType.RadialGradient => SKShader.CreateRadialGradient(
|
||||||
center,
|
center,
|
||||||
Math.Min(_shaderBounds.Width, _shaderBounds.Height),
|
Math.Min(_shaderBounds.Width, _shaderBounds.Height),
|
||||||
GradientProperty.Value.GetColorsArray(),
|
Properties.Gradient.BaseValue.GetColorsArray(),
|
||||||
GradientProperty.Value.GetPositionsArray(),
|
Properties.Gradient.BaseValue.GetPositionsArray(),
|
||||||
SKShaderTileMode.Repeat),
|
SKShaderTileMode.Repeat),
|
||||||
GradientType.SweepGradient => SKShader.CreateSweepGradient(
|
GradientType.SweepGradient => SKShader.CreateSweepGradient(
|
||||||
center,
|
center,
|
||||||
GradientProperty.Value.GetColorsArray(),
|
Properties.Gradient.BaseValue.GetColorsArray(),
|
||||||
GradientProperty.Value.GetPositionsArray(),
|
Properties.Gradient.BaseValue.GetPositionsArray(),
|
||||||
SKShaderTileMode.Clamp,
|
SKShaderTileMode.Clamp,
|
||||||
0,
|
0,
|
||||||
360),
|
360),
|
||||||
@ -107,19 +85,4 @@ namespace Artemis.Plugins.LayerBrushes.Color
|
|||||||
oldPaint?.Dispose();
|
oldPaint?.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum GradientType
|
|
||||||
{
|
|
||||||
[Description("Solid")]
|
|
||||||
Solid,
|
|
||||||
|
|
||||||
[Description("Linear Gradient")]
|
|
||||||
LinearGradient,
|
|
||||||
|
|
||||||
[Description("Radial Gradient")]
|
|
||||||
RadialGradient,
|
|
||||||
|
|
||||||
[Description("Sweep Gradient")]
|
|
||||||
SweepGradient
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using Artemis.Core.Models.Profile;
|
||||||
|
using Artemis.Core.Models.Profile.Colors;
|
||||||
|
using Artemis.Core.Models.Profile.LayerProperties.Attributes;
|
||||||
|
using Artemis.Core.Models.Profile.LayerProperties.Types;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Artemis.Plugins.LayerBrushes.Color
|
||||||
|
{
|
||||||
|
public class ColorBrushProperties : LayerPropertyGroup
|
||||||
|
{
|
||||||
|
[PropertyDescription(Description = "The type of color brush to draw")]
|
||||||
|
public EnumLayerProperty<GradientType> GradientType { get; set; }
|
||||||
|
|
||||||
|
[PropertyDescription(Description = "The color of the brush")]
|
||||||
|
public SKColorLayerProperty Color { get; set; }
|
||||||
|
|
||||||
|
[PropertyDescription(Description = "The gradient of the brush")]
|
||||||
|
public ColorGradientLayerProperty Gradient { get; set; }
|
||||||
|
|
||||||
|
protected override void OnPropertiesInitialized()
|
||||||
|
{
|
||||||
|
// Populate defaults
|
||||||
|
if (!GradientType.IsLoadedFromStorage)
|
||||||
|
GradientType.BaseValue = LayerBrushes.Color.GradientType.Solid;
|
||||||
|
if (!Color.IsLoadedFromStorage)
|
||||||
|
Color.BaseValue = new SKColor(255, 0, 0);
|
||||||
|
if (!Gradient.IsLoadedFromStorage)
|
||||||
|
{
|
||||||
|
Gradient.BaseValue = new ColorGradient();
|
||||||
|
Gradient.BaseValue.MakeFabulous();
|
||||||
|
}
|
||||||
|
|
||||||
|
GradientType.BaseValueChanged += GradientTypeOnBaseValueChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GradientTypeOnBaseValueChanged(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
Color.IsHidden = GradientType.BaseValue != LayerBrushes.Color.GradientType.Solid;
|
||||||
|
Gradient.IsHidden = GradientType.BaseValue == LayerBrushes.Color.GradientType.Solid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum GradientType
|
||||||
|
{
|
||||||
|
[Description("Solid")]
|
||||||
|
Solid,
|
||||||
|
|
||||||
|
[Description("Linear Gradient")]
|
||||||
|
LinearGradient,
|
||||||
|
|
||||||
|
[Description("Radial Gradient")]
|
||||||
|
RadialGradient,
|
||||||
|
|
||||||
|
[Description("Sweep Gradient")]
|
||||||
|
SweepGradient
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
|
||||||
using Artemis.Core.Models.Profile;
|
using Artemis.Core.Models.Profile;
|
||||||
using Artemis.Core.Models.Profile.Colors;
|
|
||||||
using Artemis.Core.Models.Profile.LayerProperties;
|
|
||||||
using Artemis.Core.Plugins.LayerBrush;
|
using Artemis.Core.Plugins.LayerBrush;
|
||||||
using Artemis.Core.Services.Interfaces;
|
using Artemis.Core.Services.Interfaces;
|
||||||
using Artemis.Plugins.LayerBrushes.Noise.Utilities;
|
using Artemis.Plugins.LayerBrushes.Noise.Utilities;
|
||||||
@ -11,18 +8,18 @@ using SkiaSharp;
|
|||||||
|
|
||||||
namespace Artemis.Plugins.LayerBrushes.Noise
|
namespace Artemis.Plugins.LayerBrushes.Noise
|
||||||
{
|
{
|
||||||
public class NoiseBrush : LayerBrush
|
public class NoiseBrush : LayerBrush<NoiseBrushProperties>
|
||||||
{
|
{
|
||||||
private static readonly Random Rand = new Random();
|
private static readonly Random Rand = new Random();
|
||||||
private readonly OpenSimplexNoise _noise;
|
private readonly OpenSimplexNoise _noise;
|
||||||
private readonly IRgbService _rgbService;
|
private readonly IRgbService _rgbService;
|
||||||
private SKBitmap _bitmap;
|
private SKBitmap _bitmap;
|
||||||
|
private SKColor[] _colorMap;
|
||||||
|
|
||||||
private float _renderScale;
|
private float _renderScale;
|
||||||
private float _x;
|
private float _x;
|
||||||
private float _y;
|
private float _y;
|
||||||
private float _z;
|
private float _z;
|
||||||
private SKColor[] _colorMap;
|
|
||||||
|
|
||||||
public NoiseBrush(Layer layer, LayerBrushDescriptor descriptor, IRgbService rgbService) : base(layer, descriptor)
|
public NoiseBrush(Layer layer, LayerBrushDescriptor descriptor, IRgbService rgbService) : base(layer, descriptor)
|
||||||
{
|
{
|
||||||
@ -31,58 +28,15 @@ namespace Artemis.Plugins.LayerBrushes.Noise
|
|||||||
_y = Rand.Next(0, 4096);
|
_y = Rand.Next(0, 4096);
|
||||||
_z = Rand.Next(0, 4096);
|
_z = Rand.Next(0, 4096);
|
||||||
_noise = new OpenSimplexNoise(Rand.Next(0, 4096));
|
_noise = new OpenSimplexNoise(Rand.Next(0, 4096));
|
||||||
|
|
||||||
DetermineRenderScale();
|
DetermineRenderScale();
|
||||||
|
|
||||||
ColorTypeProperty = RegisterLayerProperty("Brush.ColorType", "Color mapping type", "The way the noise is converted to colors", ColorMappingType.Simple);
|
|
||||||
ColorTypeProperty.CanUseKeyframes = false;
|
|
||||||
ColorTypeProperty.ValueChanged += (sender, args) => UpdateColorProperties();
|
|
||||||
ScaleProperty = RegisterLayerProperty("Brush.Scale", "Scale", "The scale of the noise.", new SKSize(100, 100));
|
|
||||||
ScaleProperty.MinInputValue = 0f;
|
|
||||||
HardnessProperty = RegisterLayerProperty("Brush.Hardness", "Hardness", "The hardness of the noise, lower means there are gradients in the noise, higher means hard lines", 500f);
|
|
||||||
HardnessProperty.MinInputValue = 0f;
|
|
||||||
HardnessProperty.MaxInputValue = 2048f;
|
|
||||||
ScrollSpeedProperty = RegisterLayerProperty<SKPoint>("Brush.ScrollSpeed", "Movement speed", "The speed at which the noise moves vertically and horizontally");
|
|
||||||
ScrollSpeedProperty.MinInputValue = -64f;
|
|
||||||
ScrollSpeedProperty.MaxInputValue = 64f;
|
|
||||||
AnimationSpeedProperty = RegisterLayerProperty("Brush.AnimationSpeed", "Animation speed", "The speed at which the noise moves", 25f);
|
|
||||||
AnimationSpeedProperty.MinInputValue = 0f;
|
|
||||||
AnimationSpeedProperty.MaxInputValue = 64f;
|
|
||||||
ScaleProperty.InputAffix = "%";
|
|
||||||
MainColorProperty = RegisterLayerProperty("Brush.MainColor", "Main color", "The main color of the noise", new SKColor(255, 0, 0));
|
|
||||||
SecondaryColorProperty = RegisterLayerProperty("Brush.SecondaryColor", "Secondary color", "The secondary color of the noise", new SKColor(0, 0, 255));
|
|
||||||
GradientColorProperty = RegisterLayerProperty("Brush.GradientColor", "Noise gradient map", "The gradient the noise will map it's value to", new ColorGradient());
|
|
||||||
GradientColorProperty.CanUseKeyframes = false;
|
|
||||||
if (!GradientColorProperty.Value.Stops.Any())
|
|
||||||
GradientColorProperty.Value.MakeFabulous();
|
|
||||||
GradientColorProperty.Value.PropertyChanged += CreateColorMap;
|
|
||||||
|
|
||||||
UpdateColorProperties();
|
|
||||||
CreateColorMap(null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public LayerProperty<ColorMappingType> ColorTypeProperty { get; set; }
|
|
||||||
public LayerProperty<SKColor> MainColorProperty { get; set; }
|
|
||||||
public LayerProperty<SKColor> SecondaryColorProperty { get; set; }
|
|
||||||
public LayerProperty<ColorGradient> GradientColorProperty { get; set; }
|
|
||||||
|
|
||||||
public LayerProperty<SKSize> ScaleProperty { get; set; }
|
|
||||||
public LayerProperty<float> HardnessProperty { get; set; }
|
|
||||||
public LayerProperty<SKPoint> ScrollSpeedProperty { get; set; }
|
|
||||||
public LayerProperty<float> AnimationSpeedProperty { get; set; }
|
|
||||||
|
|
||||||
private void UpdateColorProperties()
|
|
||||||
{
|
|
||||||
GradientColorProperty.IsHidden = ColorTypeProperty.Value != ColorMappingType.Gradient;
|
|
||||||
MainColorProperty.IsHidden = ColorTypeProperty.Value != ColorMappingType.Simple;
|
|
||||||
SecondaryColorProperty.IsHidden = ColorTypeProperty.Value != ColorMappingType.Simple;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Update(double deltaTime)
|
public override void Update(double deltaTime)
|
||||||
{
|
{
|
||||||
_x += ScrollSpeedProperty.CurrentValue.X / 500f / (float) deltaTime;
|
_x += Properties.ScrollSpeed.CurrentValue.X / 500f / (float) deltaTime;
|
||||||
_y += ScrollSpeedProperty.CurrentValue.Y / 500f / (float) deltaTime;
|
_y += Properties.ScrollSpeed.CurrentValue.Y / 500f / (float) deltaTime;
|
||||||
_z += AnimationSpeedProperty.CurrentValue / 500f / 0.04f * (float) deltaTime;
|
_z += Properties.AnimationSpeed.CurrentValue / 500f / 0.04f * (float) deltaTime;
|
||||||
|
|
||||||
// A telltale sign of someone who can't do math very well
|
// A telltale sign of someone who can't do math very well
|
||||||
if (float.IsPositiveInfinity(_x) || float.IsNegativeInfinity(_x) || float.IsNaN(_x))
|
if (float.IsPositiveInfinity(_x) || float.IsNegativeInfinity(_x) || float.IsNaN(_x))
|
||||||
@ -98,11 +52,11 @@ namespace Artemis.Plugins.LayerBrushes.Noise
|
|||||||
|
|
||||||
public override void Render(SKCanvas canvas, SKImageInfo canvasInfo, SKPath path, SKPaint paint)
|
public override void Render(SKCanvas canvas, SKImageInfo canvasInfo, SKPath path, SKPaint paint)
|
||||||
{
|
{
|
||||||
var mainColor = MainColorProperty?.CurrentValue;
|
var mainColor = Properties.MainColor?.CurrentValue;
|
||||||
var gradientColor = GradientColorProperty?.CurrentValue;
|
var gradientColor = Properties.GradientColor?.CurrentValue;
|
||||||
var scale = ScaleProperty.CurrentValue;
|
var scale = Properties.Scale.CurrentValue;
|
||||||
var opacity = mainColor != null ? (float) Math.Round(mainColor.Value.Alpha / 255.0, 2, MidpointRounding.AwayFromZero) : 0;
|
var opacity = mainColor != null ? (float) Math.Round(mainColor.Value.Alpha / 255.0, 2, MidpointRounding.AwayFromZero) : 0;
|
||||||
var hardness = 127 + HardnessProperty.CurrentValue;
|
var hardness = 127 + Properties.Hardness.CurrentValue;
|
||||||
|
|
||||||
// Scale down the render path to avoid computing a value for every pixel
|
// Scale down the render path to avoid computing a value for every pixel
|
||||||
var width = (int) Math.Floor(path.Bounds.Width * _renderScale);
|
var width = (int) Math.Floor(path.Bounds.Width * _renderScale);
|
||||||
@ -122,10 +76,8 @@ namespace Artemis.Plugins.LayerBrushes.Noise
|
|||||||
|
|
||||||
var v = _noise.Evaluate(evalX, evalY, _z);
|
var v = _noise.Evaluate(evalX, evalY, _z);
|
||||||
var alpha = (byte) Math.Max(0, Math.Min(255, v * hardness));
|
var alpha = (byte) Math.Max(0, Math.Min(255, v * hardness));
|
||||||
if (ColorTypeProperty.Value == ColorMappingType.Simple && mainColor != null)
|
if (Properties.ColorType.BaseValue == ColorMappingType.Simple && mainColor != null)
|
||||||
{
|
|
||||||
_bitmap.SetPixel(x, y, new SKColor(mainColor.Value.Red, mainColor.Value.Green, mainColor.Value.Blue, (byte) (alpha * opacity)));
|
_bitmap.SetPixel(x, y, new SKColor(mainColor.Value.Red, mainColor.Value.Green, mainColor.Value.Blue, (byte) (alpha * opacity)));
|
||||||
}
|
|
||||||
else if (gradientColor != null && _colorMap.Length == 101)
|
else if (gradientColor != null && _colorMap.Length == 101)
|
||||||
{
|
{
|
||||||
var color = _colorMap[(int) Math.Round(alpha / 255f * 100, MidpointRounding.AwayFromZero)];
|
var color = _colorMap[(int) Math.Round(alpha / 255f * 100, MidpointRounding.AwayFromZero)];
|
||||||
@ -141,9 +93,9 @@ namespace Artemis.Plugins.LayerBrushes.Noise
|
|||||||
);
|
);
|
||||||
|
|
||||||
canvas.ClipPath(path);
|
canvas.ClipPath(path);
|
||||||
if (ColorTypeProperty.Value == ColorMappingType.Simple)
|
if (Properties.ColorType.BaseValue == ColorMappingType.Simple)
|
||||||
{
|
{
|
||||||
using var backgroundShader = SKShader.CreateColor(SecondaryColorProperty.CurrentValue);
|
using var backgroundShader = SKShader.CreateColor(Properties.SecondaryColor.CurrentValue);
|
||||||
paint.Shader = backgroundShader;
|
paint.Shader = backgroundShader;
|
||||||
canvas.DrawRect(path.Bounds, paint);
|
canvas.DrawRect(path.Bounds, paint);
|
||||||
}
|
}
|
||||||
@ -153,6 +105,17 @@ namespace Artemis.Plugins.LayerBrushes.Noise
|
|||||||
canvas.DrawRect(path.Bounds, paint);
|
canvas.DrawRect(path.Bounds, paint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnPropertiesInitialized()
|
||||||
|
{
|
||||||
|
Properties.GradientColor.BaseValue.PropertyChanged += GradientColorChanged;
|
||||||
|
CreateColorMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GradientColorChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
CreateColorMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void DetermineRenderScale()
|
private void DetermineRenderScale()
|
||||||
{
|
{
|
||||||
@ -170,11 +133,11 @@ namespace Artemis.Plugins.LayerBrushes.Noise
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateColorMap(object sender, EventArgs e)
|
private void CreateColorMap()
|
||||||
{
|
{
|
||||||
var colorMap = new SKColor[101];
|
var colorMap = new SKColor[101];
|
||||||
for (var i = 0; i < 101; i++)
|
for (var i = 0; i < 101; i++)
|
||||||
colorMap[i] = GradientColorProperty.Value.GetColor(i / 100f);
|
colorMap[i] = Properties.GradientColor.BaseValue.GetColor(i / 100f);
|
||||||
|
|
||||||
_colorMap = colorMap;
|
_colorMap = colorMap;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,67 @@
|
|||||||
|
using System;
|
||||||
|
using Artemis.Core.Models.Profile;
|
||||||
|
using Artemis.Core.Models.Profile.Colors;
|
||||||
|
using Artemis.Core.Models.Profile.LayerProperties.Attributes;
|
||||||
|
using Artemis.Core.Models.Profile.LayerProperties.Types;
|
||||||
|
using SkiaSharp;
|
||||||
|
|
||||||
|
namespace Artemis.Plugins.LayerBrushes.Noise
|
||||||
|
{
|
||||||
|
public class NoiseBrushProperties : LayerPropertyGroup
|
||||||
|
{
|
||||||
|
[PropertyDescription(Name = "Color mapping type", Description = "The way the noise is converted to colors")]
|
||||||
|
public EnumLayerProperty<ColorMappingType> ColorType { get; set; }
|
||||||
|
|
||||||
|
[PropertyDescription(Description = "The main color of the noise")]
|
||||||
|
public SKColorLayerProperty MainColor { get; set; }
|
||||||
|
|
||||||
|
[PropertyDescription(Description = "The secondary color of the noise")]
|
||||||
|
public SKColorLayerProperty SecondaryColor { get; set; }
|
||||||
|
|
||||||
|
[PropertyDescription(Name = "Noise gradient map", Description = "The gradient the noise will map it's value to")]
|
||||||
|
public ColorGradientLayerProperty GradientColor { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
[PropertyDescription(Description = "The scale of the noise", MinInputValue = 0f, InputAffix = "%")]
|
||||||
|
public SKSizeLayerProperty Scale { get; set; }
|
||||||
|
|
||||||
|
[PropertyDescription(Description = "The hardness of the noise, lower means there are gradients in the noise, higher means hard lines", MinInputValue = 0f, MaxInputValue = 2048f)]
|
||||||
|
public FloatLayerProperty Hardness { get; set; }
|
||||||
|
|
||||||
|
[PropertyDescription(Description = "The speed at which the noise moves vertically and horizontally", MinInputValue = -64f, MaxInputValue = 64f)]
|
||||||
|
public SKPointLayerProperty ScrollSpeed { get; set; }
|
||||||
|
|
||||||
|
[PropertyDescription(Description = "The speed at which the noise moves", MinInputValue = 0f, MaxInputValue = 64f)]
|
||||||
|
public FloatLayerProperty AnimationSpeed { get; set; }
|
||||||
|
|
||||||
|
protected override void OnPropertiesInitialized()
|
||||||
|
{
|
||||||
|
// Populate defaults
|
||||||
|
if (!MainColor.IsLoadedFromStorage)
|
||||||
|
MainColor.BaseValue = new SKColor(255, 0, 0);
|
||||||
|
if (!SecondaryColor.IsLoadedFromStorage)
|
||||||
|
SecondaryColor.BaseValue = new SKColor(0, 0, 255);
|
||||||
|
if (!GradientColor.IsLoadedFromStorage)
|
||||||
|
{
|
||||||
|
GradientColor.BaseValue = new ColorGradient();
|
||||||
|
GradientColor.BaseValue.MakeFabulous();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Scale.IsLoadedFromStorage)
|
||||||
|
Scale.BaseValue = new SKSize(100, 100);
|
||||||
|
if (!Hardness.IsLoadedFromStorage)
|
||||||
|
Hardness.BaseValue = 500f;
|
||||||
|
if (!AnimationSpeed.IsLoadedFromStorage)
|
||||||
|
AnimationSpeed.BaseValue = 25f;
|
||||||
|
|
||||||
|
ColorType.BaseValueChanged += ColorTypeOnBaseValueChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ColorTypeOnBaseValueChanged(object? sender, EventArgs e)
|
||||||
|
{
|
||||||
|
GradientColor.IsHidden = ColorType.BaseValue != ColorMappingType.Gradient;
|
||||||
|
MainColor.IsHidden = ColorType.BaseValue != ColorMappingType.Simple;
|
||||||
|
SecondaryColor.IsHidden = ColorType.BaseValue != ColorMappingType.Simple;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user