mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-11 19:36:28 +00:00
Profiles - Abstracted property elements and effect elements
Folders - Added properties to folders Layer effects - Expanded to folders Layer effects - Added shape clipping
This commit is contained in:
1 parent
34bcd22f8c
commit
75b0ee8151
42 files changed
+638
-347
No files matched your search
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Artemis.Core.Annotations;
|
||||
using Artemis.Core.Plugins.LayerEffect.Abstract;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core.Models.Profile
|
||||
{
|
||||
public abstract class EffectProfileElement : PropertiesProfileElement
|
||||
{
|
||||
internal abstract EffectsEntity EffectsEntity { get; }
|
||||
|
||||
protected List<BaseLayerEffect> _layerEffects;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only collection of the layer effects on this entity
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<BaseLayerEffect> LayerEffects => _layerEffects.AsReadOnly();
|
||||
|
||||
internal void RemoveLayerEffect([NotNull] BaseLayerEffect effect)
|
||||
{
|
||||
if (effect == null) throw new ArgumentNullException(nameof(effect));
|
||||
|
||||
DeactivateLayerEffect(effect);
|
||||
|
||||
// Update the order on the remaining effects
|
||||
var index = 0;
|
||||
foreach (var baseLayerEffect in LayerEffects.OrderBy(e => e.Order))
|
||||
{
|
||||
baseLayerEffect.Order = Order = index + 1;
|
||||
index++;
|
||||
}
|
||||
|
||||
OnLayerEffectsUpdated();
|
||||
}
|
||||
|
||||
internal void AddLayerEffect([NotNull] BaseLayerEffect effect)
|
||||
{
|
||||
if (effect == null) throw new ArgumentNullException(nameof(effect));
|
||||
_layerEffects.Add(effect);
|
||||
OnLayerEffectsUpdated();
|
||||
}
|
||||
|
||||
internal void DeactivateLayerEffect([NotNull] BaseLayerEffect effect)
|
||||
{
|
||||
if (effect == null) throw new ArgumentNullException(nameof(effect));
|
||||
|
||||
// Remove the effect from the layer and dispose it
|
||||
_layerEffects.Remove(effect);
|
||||
effect.Dispose();
|
||||
}
|
||||
|
||||
internal SKPath CreateShapeClip()
|
||||
{
|
||||
var shapeClip = new SKPath();
|
||||
if (Path == null)
|
||||
return shapeClip;
|
||||
|
||||
if (Parent is EffectProfileElement effectParent)
|
||||
shapeClip = shapeClip.Op(effectParent.CreateShapeClip(), SKPathOp.Union);
|
||||
|
||||
foreach (var baseLayerEffect in LayerEffects)
|
||||
{
|
||||
var effectClip = baseLayerEffect.InternalCreateShapeClip(Path);
|
||||
shapeClip = shapeClip.Op(effectClip, SKPathOp.Union);
|
||||
}
|
||||
|
||||
return shapeClip;
|
||||
}
|
||||
|
||||
protected void ApplyLayerEffectsToEntity()
|
||||
{
|
||||
EffectsEntity.LayerEffects.Clear();
|
||||
foreach (var layerEffect in LayerEffects)
|
||||
{
|
||||
var layerEffectEntity = new LayerEffectEntity
|
||||
{
|
||||
Id = layerEffect.EntityId,
|
||||
PluginGuid = layerEffect.PluginInfo.Guid,
|
||||
EffectType = layerEffect.GetType().Name,
|
||||
Name = layerEffect.Name,
|
||||
HasBeenRenamed = layerEffect.HasBeenRenamed,
|
||||
Order = layerEffect.Order
|
||||
};
|
||||
EffectsEntity.LayerEffects.Add(layerEffectEntity);
|
||||
layerEffect.BaseProperties.ApplyToEntity();
|
||||
}
|
||||
}
|
||||
|
||||
#region Events
|
||||
|
||||
public event EventHandler LayerEffectsUpdated;
|
||||
|
||||
internal void OnLayerEffectsUpdated()
|
||||
{
|
||||
LayerEffectsUpdated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Artemis.Core.Plugins.LayerEffect.Abstract;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
@@ -8,10 +7,8 @@ using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core.Models.Profile
|
||||
{
|
||||
public sealed class Folder : ProfileElement
|
||||
public sealed class Folder : EffectProfileElement
|
||||
{
|
||||
private readonly List<BaseLayerEffect> _layerEffects;
|
||||
|
||||
public Folder(Profile profile, ProfileElement parent, string name)
|
||||
{
|
||||
FolderEntity = new FolderEntity();
|
||||
@@ -20,19 +17,25 @@ namespace Artemis.Core.Models.Profile
|
||||
Profile = profile;
|
||||
Parent = parent;
|
||||
Name = name;
|
||||
|
||||
_layerEffects = new List<BaseLayerEffect>();
|
||||
_expandedPropertyGroups = new List<string>();
|
||||
}
|
||||
|
||||
internal Folder(Profile profile, ProfileElement parent, FolderEntity folderEntity)
|
||||
{
|
||||
FolderEntity = folderEntity;
|
||||
|
||||
EntityId = folderEntity.Id;
|
||||
|
||||
Profile = profile;
|
||||
Parent = parent;
|
||||
Name = folderEntity.Name;
|
||||
Order = folderEntity.Order;
|
||||
|
||||
_layerEffects = new List<BaseLayerEffect>();
|
||||
_expandedPropertyGroups = new List<string>();
|
||||
_expandedPropertyGroups.AddRange(folderEntity.ExpandedPropertyGroups);
|
||||
|
||||
// TODO: Load conditions
|
||||
|
||||
@@ -50,14 +53,14 @@ namespace Artemis.Core.Models.Profile
|
||||
}
|
||||
|
||||
internal FolderEntity FolderEntity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only collection of the layer effects on this layer
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<BaseLayerEffect> LayerEffects => _layerEffects.AsReadOnly();
|
||||
internal override PropertiesEntity PropertiesEntity => FolderEntity;
|
||||
internal override EffectsEntity EffectsEntity => FolderEntity;
|
||||
|
||||
public override void Update(double deltaTime)
|
||||
{
|
||||
foreach (var baseLayerEffect in LayerEffects)
|
||||
baseLayerEffect.Update(deltaTime);
|
||||
|
||||
// Iterate the children in reverse because that's how they must be rendered too
|
||||
for (var index = Children.Count - 1; index > -1; index--)
|
||||
{
|
||||
@@ -66,14 +69,32 @@ namespace Artemis.Core.Models.Profile
|
||||
}
|
||||
}
|
||||
|
||||
public override void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo)
|
||||
public override void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo, SKPaint paint)
|
||||
{
|
||||
canvas.Save();
|
||||
canvas.ClipPath(Path);
|
||||
|
||||
// Clone the paint so that any changes are confined to the current group
|
||||
var groupPaint = paint.Clone();
|
||||
|
||||
// Pre-processing only affects other pre-processors and the brushes
|
||||
canvas.Save();
|
||||
foreach (var baseLayerEffect in LayerEffects)
|
||||
baseLayerEffect.InternalPreProcess(canvas, canvasInfo, new SKPath(Path), groupPaint);
|
||||
|
||||
// Iterate the children in reverse because the first layer must be rendered last to end up on top
|
||||
for (var index = Children.Count - 1; index > -1; index--)
|
||||
{
|
||||
var profileElement = Children[index];
|
||||
profileElement.Render(deltaTime, canvas, canvasInfo);
|
||||
profileElement.Render(deltaTime, canvas, canvasInfo, groupPaint);
|
||||
}
|
||||
|
||||
// Restore the canvas as to not be affected by pre-processors
|
||||
canvas.Restore();
|
||||
foreach (var baseLayerEffect in LayerEffects)
|
||||
baseLayerEffect.InternalPostProcess(canvas, canvasInfo, new SKPath(Path), groupPaint);
|
||||
|
||||
canvas.Restore();
|
||||
}
|
||||
|
||||
public Folder AddFolder(string name)
|
||||
@@ -88,6 +109,24 @@ namespace Artemis.Core.Models.Profile
|
||||
return $"[Folder] {nameof(Name)}: {Name}, {nameof(Order)}: {Order}";
|
||||
}
|
||||
|
||||
public void CalculateRenderProperties()
|
||||
{
|
||||
var path = new SKPath {FillType = SKPathFillType.Winding};
|
||||
foreach (var child in Children)
|
||||
{
|
||||
if (child is EffectProfileElement effectChild && effectChild.Path != null)
|
||||
path.AddPath(effectChild.Path);
|
||||
}
|
||||
|
||||
Path = path;
|
||||
|
||||
// Folder render properties are based on child paths and thus require an update
|
||||
if (Parent is Folder folder)
|
||||
folder.CalculateRenderProperties();
|
||||
|
||||
OnRenderPropertiesUpdated();
|
||||
}
|
||||
|
||||
internal override void ApplyToEntity()
|
||||
{
|
||||
FolderEntity.Id = EntityId;
|
||||
@@ -98,7 +137,20 @@ namespace Artemis.Core.Models.Profile
|
||||
|
||||
FolderEntity.ProfileId = Profile.EntityId;
|
||||
|
||||
ApplyLayerEffectsToEntity();
|
||||
|
||||
// TODO: conditions
|
||||
}
|
||||
|
||||
#region Events
|
||||
|
||||
public event EventHandler RenderPropertiesUpdated;
|
||||
|
||||
private void OnRenderPropertiesUpdated()
|
||||
{
|
||||
RenderPropertiesUpdated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -21,13 +21,10 @@ namespace Artemis.Core.Models.Profile
|
||||
/// 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 : EffectProfileElement
|
||||
{
|
||||
private readonly List<string> _expandedPropertyGroups;
|
||||
private readonly List<BaseLayerEffect> _layerEffects;
|
||||
private LayerShape _layerShape;
|
||||
private List<ArtemisLed> _leds;
|
||||
private SKPath _path;
|
||||
|
||||
internal Layer(Profile profile, ProfileElement parent, string name)
|
||||
{
|
||||
@@ -68,38 +65,14 @@ namespace Artemis.Core.Models.Profile
|
||||
}
|
||||
|
||||
internal LayerEntity LayerEntity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a read-only collection of the layer effects on this layer
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<BaseLayerEffect> LayerEffects => _layerEffects.AsReadOnly();
|
||||
internal override PropertiesEntity PropertiesEntity => LayerEntity;
|
||||
internal override EffectsEntity EffectsEntity => LayerEntity;
|
||||
|
||||
/// <summary>
|
||||
/// A collection of all the LEDs this layer is assigned to.
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<ArtemisLed> Leds => _leds.AsReadOnly();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a copy of the path containing all the LEDs this layer is applied to, any rendering outside the layer Path is
|
||||
/// clipped.
|
||||
/// </summary>
|
||||
public SKPath Path
|
||||
{
|
||||
get => _path != null ? new SKPath(_path) : null;
|
||||
private set
|
||||
{
|
||||
_path = value;
|
||||
// I can't really be sure about the performance impact of calling Bounds often but
|
||||
// SkiaSharp calls SkiaApi.sk_path_get_bounds (Handle, &rect); which sounds expensive
|
||||
Bounds = value?.Bounds ?? SKRect.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The bounds of this layer
|
||||
/// </summary>
|
||||
public SKRect Bounds { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines the shape that is rendered by the <see cref="LayerBrush" />.
|
||||
/// </summary>
|
||||
@@ -130,19 +103,6 @@ namespace Artemis.Core.Models.Profile
|
||||
return $"[Layer] {nameof(Name)}: {Name}, {nameof(Order)}: {Order}";
|
||||
}
|
||||
|
||||
public bool IsPropertyGroupExpanded(LayerPropertyGroup layerPropertyGroup)
|
||||
{
|
||||
return _expandedPropertyGroups.Contains(layerPropertyGroup.Path);
|
||||
}
|
||||
|
||||
public void SetPropertyGroupExpanded(LayerPropertyGroup layerPropertyGroup, bool expanded)
|
||||
{
|
||||
if (!expanded && IsPropertyGroupExpanded(layerPropertyGroup))
|
||||
_expandedPropertyGroups.Remove(layerPropertyGroup.Path);
|
||||
else if (expanded && !IsPropertyGroupExpanded(layerPropertyGroup))
|
||||
_expandedPropertyGroups.Add(layerPropertyGroup.Path);
|
||||
}
|
||||
|
||||
#region Storage
|
||||
|
||||
internal override void ApplyToEntity()
|
||||
@@ -161,21 +121,7 @@ namespace Artemis.Core.Models.Profile
|
||||
LayerBrush?.BaseProperties.ApplyToEntity();
|
||||
|
||||
// Effects
|
||||
LayerEntity.LayerEffects.Clear();
|
||||
foreach (var layerEffect in LayerEffects)
|
||||
{
|
||||
var layerEffectEntity = new LayerEffectEntity
|
||||
{
|
||||
Id = layerEffect.EntityId,
|
||||
PluginGuid = layerEffect.PluginInfo.Guid,
|
||||
EffectType = layerEffect.GetType().Name,
|
||||
Name = layerEffect.Name,
|
||||
HasBeenRenamed = layerEffect.HasBeenRenamed,
|
||||
Order = layerEffect.Order
|
||||
};
|
||||
LayerEntity.LayerEffects.Add(layerEffectEntity);
|
||||
layerEffect.BaseProperties.ApplyToEntity();
|
||||
}
|
||||
ApplyLayerEffectsToEntity();
|
||||
|
||||
// LEDs
|
||||
LayerEntity.Leds.Clear();
|
||||
@@ -190,7 +136,7 @@ namespace Artemis.Core.Models.Profile
|
||||
}
|
||||
|
||||
// Conditions TODO
|
||||
LayerEntity.Condition.Clear();
|
||||
LayerEntity.Conditions.Clear();
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -273,7 +219,7 @@ namespace Artemis.Core.Models.Profile
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo)
|
||||
public override void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo, SKPaint paint)
|
||||
{
|
||||
// Ensure the layer is ready
|
||||
if (Path == null || LayerShape?.Path == null || !General.PropertiesInitialized || !Transform.PropertiesInitialized)
|
||||
@@ -284,36 +230,42 @@ namespace Artemis.Core.Models.Profile
|
||||
|
||||
canvas.Save();
|
||||
canvas.ClipPath(Path);
|
||||
|
||||
paint.BlendMode = General.BlendMode.CurrentValue;
|
||||
paint.Color = new SKColor(0, 0, 0, (byte) (Transform.Opacity.CurrentValue * 2.55f));
|
||||
|
||||
using (var paint = new SKPaint())
|
||||
{
|
||||
paint.FilterQuality = SKFilterQuality.Low;
|
||||
paint.BlendMode = General.BlendMode.CurrentValue;
|
||||
paint.Color = new SKColor(0, 0, 0, (byte) (Transform.Opacity.CurrentValue * 2.55f));
|
||||
// Pre-processing only affects other pre-processors and the brushes
|
||||
canvas.Save();
|
||||
foreach (var baseLayerEffect in LayerEffects)
|
||||
baseLayerEffect.InternalPreProcess(canvas, canvasInfo, new SKPath(Path), paint);
|
||||
|
||||
foreach (var baseLayerEffect in LayerEffects)
|
||||
baseLayerEffect.PreProcess(canvas, canvasInfo, Path, paint);
|
||||
// Shape clip must be determined before commiting to any rendering
|
||||
var shapeClip = CreateShapeClip();
|
||||
if (!shapeClip.IsEmpty)
|
||||
ExcludePathFromTranslation(shapeClip);
|
||||
|
||||
if (!LayerBrush.SupportsTransformation)
|
||||
SimpleRender(canvas, canvasInfo, paint);
|
||||
else if (General.FillType.CurrentValue == LayerFillType.Stretch)
|
||||
StretchRender(canvas, canvasInfo, paint);
|
||||
else if (General.FillType.CurrentValue == LayerFillType.Clip)
|
||||
ClipRender(canvas, canvasInfo, paint);
|
||||
|
||||
foreach (var baseLayerEffect in LayerEffects)
|
||||
baseLayerEffect.PostProcess(canvas, canvasInfo, Path, paint);
|
||||
}
|
||||
if (!LayerBrush.SupportsTransformation)
|
||||
SimpleRender(canvas, canvasInfo, paint, shapeClip);
|
||||
else if (General.FillType.CurrentValue == LayerFillType.Stretch)
|
||||
StretchRender(canvas, canvasInfo, paint, shapeClip);
|
||||
else if (General.FillType.CurrentValue == LayerFillType.Clip)
|
||||
ClipRender(canvas, canvasInfo, paint, shapeClip);
|
||||
|
||||
// Restore the canvas as to not be affected by pre-processors
|
||||
canvas.Restore();
|
||||
foreach (var baseLayerEffect in LayerEffects)
|
||||
baseLayerEffect.InternalPostProcess(canvas, canvasInfo, new SKPath(Path), paint);
|
||||
|
||||
canvas.Restore();
|
||||
}
|
||||
|
||||
private void SimpleRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPaint paint)
|
||||
private void SimpleRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPaint paint, SKPath shapeClip)
|
||||
{
|
||||
LayerBrush.InternalRender(canvas, canvasInfo, new SKPath(LayerShape.Path), paint);
|
||||
var path = LayerShape.Path.Op(shapeClip, SKPathOp.Difference);
|
||||
LayerBrush.InternalRender(canvas, canvasInfo, path, paint);
|
||||
}
|
||||
|
||||
private void StretchRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPaint paint)
|
||||
private void StretchRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPaint paint, SKPath shapeClip)
|
||||
{
|
||||
// Apply transformations
|
||||
var sizeProperty = Transform.Scale.CurrentValue;
|
||||
@@ -331,10 +283,11 @@ namespace Artemis.Core.Models.Profile
|
||||
canvas.Scale(sizeProperty.Width / 100f, sizeProperty.Height / 100f, anchorPosition.X, anchorPosition.Y);
|
||||
canvas.Translate(x, y);
|
||||
|
||||
LayerBrush.InternalRender(canvas, canvasInfo, new SKPath(LayerShape.Path), paint);
|
||||
var path = LayerShape.Path.Op(shapeClip, SKPathOp.Difference);
|
||||
LayerBrush.InternalRender(canvas, canvasInfo, path, paint);
|
||||
}
|
||||
|
||||
private void ClipRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPaint paint)
|
||||
private void ClipRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPaint paint, SKPath shapeClip)
|
||||
{
|
||||
// Apply transformation
|
||||
var sizeProperty = Transform.Scale.CurrentValue;
|
||||
@@ -367,29 +320,31 @@ namespace Artemis.Core.Models.Profile
|
||||
var renderPath = new SKPath();
|
||||
renderPath.AddRect(boundsRect);
|
||||
|
||||
renderPath = renderPath.Op(shapeClip, SKPathOp.Difference);
|
||||
LayerBrush.InternalRender(canvas, canvasInfo, renderPath, paint);
|
||||
}
|
||||
|
||||
internal void CalculateRenderProperties()
|
||||
{
|
||||
if (!Leds.Any())
|
||||
{
|
||||
Path = new SKPath();
|
||||
else
|
||||
{
|
||||
var path = new SKPath {FillType = SKPathFillType.Winding};
|
||||
foreach (var artemisLed in Leds)
|
||||
path.AddRect(artemisLed.AbsoluteRenderRectangle);
|
||||
|
||||
LayerShape?.CalculateRenderProperties();
|
||||
OnRenderPropertiesUpdated();
|
||||
return;
|
||||
Path = path;
|
||||
}
|
||||
|
||||
var path = new SKPath {FillType = SKPathFillType.Winding};
|
||||
foreach (var artemisLed in Leds)
|
||||
path.AddRect(artemisLed.AbsoluteRenderRectangle);
|
||||
|
||||
Path = path;
|
||||
|
||||
// This is called here so that the shape's render properties are up to date when other code
|
||||
// responds to OnRenderPropertiesUpdated
|
||||
LayerShape?.CalculateRenderProperties();
|
||||
|
||||
// Folder render properties are based on child paths and thus require an update
|
||||
if (Parent is Folder folder)
|
||||
folder.CalculateRenderProperties();
|
||||
|
||||
OnRenderPropertiesUpdated();
|
||||
}
|
||||
|
||||
@@ -407,9 +362,39 @@ namespace Artemis.Core.Models.Profile
|
||||
return position;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Effect management
|
||||
/// <summary>
|
||||
/// Excludes the provided path from the translations applied to the layer by applying translations that cancel the
|
||||
/// layer translations out
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
public void ExcludePathFromTranslation(SKPath path)
|
||||
{
|
||||
var sizeProperty = Transform.Scale.CurrentValue;
|
||||
var rotationProperty = Transform.Rotation.CurrentValue;
|
||||
|
||||
var anchorPosition = GetLayerAnchorPosition();
|
||||
var anchorProperty = Transform.AnchorPoint.CurrentValue;
|
||||
|
||||
// Translation originates from the unscaled center of the shape and is tied to the anchor
|
||||
var x = anchorPosition.X - Bounds.MidX - anchorProperty.X * Bounds.Width;
|
||||
var y = anchorPosition.Y - Bounds.MidY - anchorProperty.Y * Bounds.Height;
|
||||
|
||||
var reversedXScale = 1f / (sizeProperty.Width / 100f);
|
||||
var reversedYScale = 1f / (sizeProperty.Height / 100f);
|
||||
|
||||
if (General.FillType == LayerFillType.Stretch)
|
||||
{
|
||||
path.Transform(SKMatrix.MakeRotationDegrees(rotationProperty * -1, anchorPosition.X, anchorPosition.Y));
|
||||
path.Transform(SKMatrix.MakeScale(reversedXScale, reversedYScale, anchorPosition.X, anchorPosition.Y));
|
||||
path.Transform(SKMatrix.MakeTranslation(x * -1, y * -1));
|
||||
}
|
||||
else
|
||||
{
|
||||
path.Transform(SKMatrix.MakeRotationDegrees(rotationProperty * -1, anchorPosition.X, anchorPosition.Y));
|
||||
path.Transform(SKMatrix.MakeTranslation(x * -1, y * -1));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -494,15 +479,6 @@ namespace Artemis.Core.Models.Profile
|
||||
brush.Dispose();
|
||||
}
|
||||
|
||||
private void DeactivateLayerEffect([NotNull] BaseLayerEffect effect)
|
||||
{
|
||||
if (effect == null) throw new ArgumentNullException(nameof(effect));
|
||||
|
||||
// Remove the effect from the layer and dispose it
|
||||
_layerEffects.Remove(effect);
|
||||
effect.Dispose();
|
||||
}
|
||||
|
||||
internal void RemoveLayerBrush()
|
||||
{
|
||||
if (LayerBrush == null)
|
||||
@@ -513,62 +489,23 @@ namespace Artemis.Core.Models.Profile
|
||||
LayerEntity.PropertyEntities.RemoveAll(p => p.PluginGuid == brush.PluginInfo.Guid && p.Path.StartsWith("LayerBrush."));
|
||||
}
|
||||
|
||||
internal void RemoveLayerEffect([NotNull] BaseLayerEffect effect)
|
||||
{
|
||||
if (effect == null) throw new ArgumentNullException(nameof(effect));
|
||||
|
||||
DeactivateLayerEffect(effect);
|
||||
|
||||
// Clean up properties
|
||||
LayerEntity.PropertyEntities.RemoveAll(p => p.PluginGuid == effect.PluginInfo.Guid && p.Path.StartsWith(effect.PropertyRootPath));
|
||||
|
||||
// Update the order on the remaining effects
|
||||
var index = 0;
|
||||
foreach (var baseLayerEffect in LayerEffects.OrderBy(e => e.Order))
|
||||
{
|
||||
baseLayerEffect.Order = Order = index + 1;
|
||||
index++;
|
||||
}
|
||||
|
||||
OnLayerEffectsUpdated();
|
||||
}
|
||||
|
||||
internal void AddLayerEffect([NotNull] BaseLayerEffect effect)
|
||||
{
|
||||
if (effect == null) throw new ArgumentNullException(nameof(effect));
|
||||
_layerEffects.Add(effect);
|
||||
OnLayerEffectsUpdated();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
public event EventHandler RenderPropertiesUpdated;
|
||||
public event EventHandler ShapePropertiesUpdated;
|
||||
public event EventHandler LayerBrushUpdated;
|
||||
public event EventHandler LayerEffectsUpdated;
|
||||
|
||||
private void OnRenderPropertiesUpdated()
|
||||
{
|
||||
RenderPropertiesUpdated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void OnShapePropertiesUpdated()
|
||||
{
|
||||
ShapePropertiesUpdated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
internal void OnLayerBrushUpdated()
|
||||
{
|
||||
LayerBrushUpdated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
internal void OnLayerEffectsUpdated()
|
||||
{
|
||||
LayerEffectsUpdated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ namespace Artemis.Core.Models.Profile.LayerProperties
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The layer this property applies to
|
||||
/// Gets the profile element (such as layer or folder) this effect is applied to
|
||||
/// </summary>
|
||||
public Layer Layer { get; internal set; }
|
||||
public PropertiesProfileElement ProfileElement { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// The parent group of this layer property, set after construction
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Artemis.Core.Extensions;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core.Models.Profile.LayerProperties.Types
|
||||
@@ -17,17 +18,7 @@ namespace Artemis.Core.Models.Profile.LayerProperties.Types
|
||||
|
||||
protected override void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased)
|
||||
{
|
||||
var redDiff = NextKeyframe.Value.Red - CurrentKeyframe.Value.Red;
|
||||
var greenDiff = NextKeyframe.Value.Green - CurrentKeyframe.Value.Green;
|
||||
var blueDiff = NextKeyframe.Value.Blue - CurrentKeyframe.Value.Blue;
|
||||
var alphaDiff = NextKeyframe.Value.Alpha - CurrentKeyframe.Value.Alpha;
|
||||
|
||||
CurrentValue = new SKColor(
|
||||
ClampToByte(CurrentKeyframe.Value.Red + redDiff * keyframeProgressEased),
|
||||
ClampToByte(CurrentKeyframe.Value.Green + greenDiff * keyframeProgressEased),
|
||||
ClampToByte(CurrentKeyframe.Value.Blue + blueDiff * keyframeProgressEased),
|
||||
ClampToByte(CurrentKeyframe.Value.Alpha + alphaDiff * keyframeProgressEased)
|
||||
);
|
||||
CurrentValue = CurrentKeyframe.Value.Interpolate(NextKeyframe.Value, keyframeProgressEased);
|
||||
}
|
||||
|
||||
private static byte ClampToByte(float value)
|
||||
|
||||
@@ -29,9 +29,9 @@ namespace Artemis.Core.Models.Profile
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The layer this property group applies to
|
||||
/// Gets the profile element (such as layer or folder) this effect is applied to
|
||||
/// </summary>
|
||||
public Layer Layer { get; internal set; }
|
||||
public PropertiesProfileElement ProfileElement { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// The path of this property group
|
||||
@@ -129,7 +129,7 @@ namespace Artemis.Core.Models.Profile
|
||||
PropertyGroupInitialized?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
internal void InitializeProperties(ILayerService layerService, Layer layer, [NotNull] string path)
|
||||
internal void InitializeProperties(ILayerService layerService, PropertiesProfileElement profileElement, [NotNull] string path)
|
||||
{
|
||||
if (path == null)
|
||||
throw new ArgumentNullException(nameof(path));
|
||||
@@ -137,7 +137,7 @@ namespace Artemis.Core.Models.Profile
|
||||
if (PropertiesInitialized)
|
||||
throw new ArtemisCoreException("Layer property group already initialized, wut");
|
||||
|
||||
Layer = layer;
|
||||
ProfileElement = profileElement;
|
||||
Path = path.TrimEnd('.');
|
||||
|
||||
// Get all properties with a PropertyDescriptionAttribute
|
||||
@@ -153,10 +153,10 @@ namespace Artemis.Core.Models.Profile
|
||||
if (instance == null)
|
||||
throw new ArtemisPluginException($"Failed to create instance of layer property at {path + propertyInfo.Name}");
|
||||
|
||||
instance.Layer = layer;
|
||||
instance.ProfileElement = profileElement;
|
||||
instance.Parent = this;
|
||||
instance.PropertyDescription = (PropertyDescriptionAttribute) propertyDescription;
|
||||
InitializeProperty(layer, path + propertyInfo.Name, instance);
|
||||
InitializeProperty(profileElement, path + propertyInfo.Name, instance);
|
||||
|
||||
propertyInfo.SetValue(this, instance);
|
||||
_layerProperties.Add(instance);
|
||||
@@ -177,7 +177,7 @@ namespace Artemis.Core.Models.Profile
|
||||
instance.GroupDescription = (PropertyGroupDescriptionAttribute) propertyGroupDescription;
|
||||
instance.LayerBrush = LayerBrush;
|
||||
instance.LayerEffect = LayerEffect;
|
||||
instance.InitializeProperties(layerService, layer, $"{path}{propertyInfo.Name}.");
|
||||
instance.InitializeProperties(layerService, profileElement, $"{path}{propertyInfo.Name}.");
|
||||
|
||||
propertyInfo.SetValue(this, instance);
|
||||
_layerPropertyGroups.Add(instance);
|
||||
@@ -236,7 +236,7 @@ namespace Artemis.Core.Models.Profile
|
||||
OnPropertyGroupOverriding(new PropertyGroupUpdatingEventArgs(overrideTime));
|
||||
}
|
||||
|
||||
private void InitializeProperty(Layer layer, string path, BaseLayerProperty instance)
|
||||
private void InitializeProperty(PropertiesProfileElement profileElement, string path, BaseLayerProperty instance)
|
||||
{
|
||||
Guid pluginGuid;
|
||||
if (IsCorePropertyGroup || instance.IsCoreProperty)
|
||||
@@ -246,13 +246,13 @@ namespace Artemis.Core.Models.Profile
|
||||
else
|
||||
pluginGuid = instance.Parent.LayerEffect.PluginInfo.Guid;
|
||||
|
||||
var entity = layer.LayerEntity.PropertyEntities.FirstOrDefault(p => p.PluginGuid == pluginGuid && p.Path == path);
|
||||
var entity = profileElement.PropertiesEntity.PropertyEntities.FirstOrDefault(p => p.PluginGuid == pluginGuid && p.Path == path);
|
||||
var fromStorage = true;
|
||||
if (entity == null)
|
||||
{
|
||||
fromStorage = false;
|
||||
entity = new PropertyEntity {PluginGuid = pluginGuid, Path = path};
|
||||
layer.LayerEntity.PropertyEntities.Add(entity);
|
||||
profileElement.PropertiesEntity.PropertyEntities.Add(entity);
|
||||
}
|
||||
|
||||
instance.ApplyToLayerProperty(entity, this, fromStorage);
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace Artemis.Core.Models.Profile
|
||||
}
|
||||
}
|
||||
|
||||
public override void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo)
|
||||
public override void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo, SKPaint paint)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
@@ -65,7 +65,7 @@ namespace Artemis.Core.Models.Profile
|
||||
throw new ArtemisCoreException($"Cannot render inactive profile: {this}");
|
||||
|
||||
foreach (var profileElement in Children)
|
||||
profileElement.Render(deltaTime, canvas, canvasInfo);
|
||||
profileElement.Render(deltaTime, canvas, canvasInfo, paint);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Artemis.Core.Models.Profile
|
||||
/// <summary>
|
||||
/// Renders the element
|
||||
/// </summary>
|
||||
public abstract void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo);
|
||||
public abstract void Render(double deltaTime, SKCanvas canvas, SKImageInfo canvasInfo, SKPaint paint);
|
||||
|
||||
public List<Folder> GetAllFolders()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.Collections.Generic;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core.Models.Profile
|
||||
{
|
||||
public abstract class PropertiesProfileElement : ProfileElement
|
||||
{
|
||||
internal abstract PropertiesEntity PropertiesEntity { get; }
|
||||
|
||||
private SKPath _path;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a copy of the path containing all the LEDs this entity is applied to, any rendering outside the entity Path is
|
||||
/// clipped.
|
||||
/// </summary>
|
||||
public SKPath Path
|
||||
{
|
||||
get => _path != null ? new SKPath(_path) : null;
|
||||
protected set
|
||||
{
|
||||
_path = value;
|
||||
// I can't really be sure about the performance impact of calling Bounds often but
|
||||
// SkiaSharp calls SkiaApi.sk_path_get_bounds (Handle, &rect); which sounds expensive
|
||||
Bounds = value?.Bounds ?? SKRect.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The bounds of this entity
|
||||
/// </summary>
|
||||
public SKRect Bounds { get; private set; }
|
||||
|
||||
#region Property group expansion
|
||||
|
||||
protected List<string> _expandedPropertyGroups;
|
||||
|
||||
public bool IsPropertyGroupExpanded(LayerPropertyGroup layerPropertyGroup)
|
||||
{
|
||||
return _expandedPropertyGroups.Contains(layerPropertyGroup.Path);
|
||||
}
|
||||
|
||||
public void SetPropertyGroupExpanded(LayerPropertyGroup layerPropertyGroup, bool expanded)
|
||||
{
|
||||
if (!expanded && IsPropertyGroupExpanded(layerPropertyGroup))
|
||||
_expandedPropertyGroups.Remove(layerPropertyGroup.Path);
|
||||
else if (expanded && !IsPropertyGroupExpanded(layerPropertyGroup))
|
||||
_expandedPropertyGroups.Add(layerPropertyGroup.Path);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user