1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Data binding modifiers - Finished color saturate/desaturate

Data binding modifiers - Added color invert
Layer properties - Added FloatRange and IntRange layer properties
This commit is contained in:
SpoinkyNL 2020-11-06 23:40:25 +01:00
parent 925bedc0a6
commit 0795069af2
15 changed files with 460 additions and 9 deletions

View File

@ -1,4 +1,5 @@
using SkiaSharp;
using System;
using SkiaSharp;
namespace Artemis.Core.DefaultTypes
{
@ -10,10 +11,11 @@ namespace Artemis.Core.DefaultTypes
public override SKColor Apply(SKColor currentValue, float parameterValue)
{
// TODO: Not so straightforward ^^
currentValue.ToHsl(out float h, out float s, out float l);
s *= (parameterValue * -1 + 100f) / 100f;
return SKColor.FromHsl(h, s, l);
s -= parameterValue;
s = Math.Clamp(s, 0, 100);
return SKColor.FromHsl(h, s, l, currentValue.Alpha);
}
}
}

View File

@ -0,0 +1,18 @@
using SkiaSharp;
namespace Artemis.Core.DefaultTypes
{
internal class SKColorInvertModifierType : DataBindingModifierType<SKColor>
{
public override string Name => "Invert color";
public override string Icon => "InvertColors";
public override string Description => "Inverts the color by rotating its hue by a 180°";
public override SKColor Apply(SKColor currentValue)
{
currentValue.ToHsl(out float h, out float s, out float l);
h += 180;
return SKColor.FromHsl(h % 360, s, l);
}
}
}

View File

@ -1,4 +1,5 @@
using SkiaSharp;
using System;
using SkiaSharp;
namespace Artemis.Core.DefaultTypes
{
@ -10,9 +11,11 @@ namespace Artemis.Core.DefaultTypes
public override SKColor Apply(SKColor currentValue, float parameterValue)
{
// TODO: Not so straightforward ^^
currentValue.ToHsl(out float h, out float s, out float l);
return SKColor.FromHsl(h, parameterValue, l);
currentValue.ToHsv(out float h, out float s, out float v);
s += parameterValue;
s = Math.Clamp(s, 0, 100);
return SKColor.FromHsv(h, s, v, currentValue.Alpha);
}
}
}

View File

@ -1,4 +1,6 @@
namespace Artemis.Core.DefaultTypes
using System;
namespace Artemis.Core.DefaultTypes
{
/// <inheritdoc />
public class FloatLayerProperty : LayerProperty<float>

View File

@ -0,0 +1,31 @@
namespace Artemis.Core.DefaultTypes
{
/// <inheritdoc />
public class FloatRangeLayerProperty : LayerProperty<FloatRange>
{
internal FloatRangeLayerProperty()
{
RegisterDataBindingProperty(range => range.Start, new FloatDataBindingConverter<FloatRange>());
RegisterDataBindingProperty(range => range.End, new FloatDataBindingConverter<FloatRange>());
CurrentValueSet += OnCurrentValueSet;
}
/// <inheritdoc />
protected override void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased)
{
float startDiff = NextKeyframe.Value.Start - CurrentKeyframe.Value.Start;
float endDiff = NextKeyframe.Value.End - CurrentKeyframe.Value.End;
CurrentValue = new FloatRange(
(int) (CurrentKeyframe.Value.Start + startDiff * keyframeProgressEased),
(int) (CurrentKeyframe.Value.End + endDiff * keyframeProgressEased)
);
}
private void OnCurrentValueSet(object sender, LayerPropertyEventArgs<FloatRange> e)
{
// Don't allow the int range to be null
BaseValue ??= DefaultValue ?? new FloatRange(0, 0);
}
}
}

View File

@ -0,0 +1,33 @@
using System;
namespace Artemis.Core.DefaultTypes
{
/// <inheritdoc />
public class IntRangeLayerProperty : LayerProperty<IntRange>
{
internal IntRangeLayerProperty()
{
RegisterDataBindingProperty(range => range.Start, new IntDataBindingConverter<IntRange>());
RegisterDataBindingProperty(range => range.End, new IntDataBindingConverter<IntRange>());
CurrentValueSet += OnCurrentValueSet;
}
/// <inheritdoc />
protected override void UpdateCurrentValue(float keyframeProgress, float keyframeProgressEased)
{
float startDiff = NextKeyframe.Value.Start - CurrentKeyframe.Value.Start;
float endDiff = NextKeyframe.Value.End - CurrentKeyframe.Value.End;
CurrentValue = new IntRange(
(int) (CurrentKeyframe.Value.Start + startDiff * keyframeProgressEased),
(int) (CurrentKeyframe.Value.End + endDiff * keyframeProgressEased)
);
}
private void OnCurrentValueSet(object sender, LayerPropertyEventArgs<IntRange> e)
{
// Don't allow the int range to be null
BaseValue ??= DefaultValue ?? new IntRange(0, 0);
}
}
}

View File

@ -0,0 +1,63 @@
using System;
namespace Artemis.Core
{
/// <summary>
/// Represents a range between two single-precision floating point numbers
/// </summary>
public class FloatRange
{
private readonly Random _rand;
/// <summary>
/// Creates a new instance of the <see cref="FloatRange" /> class
/// </summary>
/// <param name="start">The start value of the range</param>
/// <param name="end">The end value of the range</param>
public FloatRange(float start, float end)
{
Start = start;
End = end;
_rand = new Random();
}
/// <summary>
/// Gets or sets the start value of the range
/// </summary>
public float Start { get; set; }
/// <summary>
/// Gets or sets the end value of the range
/// </summary>
public float End { get; set; }
/// <summary>
/// Determines whether the given value is in this range
/// </summary>
/// <param name="value">The value to check</param>
/// <param name="inclusive">
/// Whether the value may be equal to <see cref="Start" /> or <see cref="End" />
/// <para>Defaults to <see langword="true" /></para>
/// </param>
/// <returns></returns>
public bool IsInRange(float value, bool inclusive = true)
{
if (inclusive)
return value >= Start && value <= End;
return value > Start && value < End;
}
/// <summary>
/// Returns a pseudo-random value between <see cref="Start" /> and <see cref="End" />
/// </summary>
/// <param name="inclusive">Whether the value may be equal to <see cref="Start" /></param>
/// <returns>The pseudo-random value</returns>
public float GetRandomValue(bool inclusive = true)
{
if (inclusive)
return _rand.Next((int) (Start * 100), (int) (End * 100)) / 100f;
return _rand.Next((int) (Start * 100) + 1, (int) (End * 100)) / 100f;
}
}
}

View File

@ -0,0 +1,63 @@
using System;
namespace Artemis.Core
{
/// <summary>
/// Represents a range between two signed integers
/// </summary>
public class IntRange
{
private readonly Random _rand;
/// <summary>
/// Creates a new instance of the <see cref="IntRange" /> class
/// </summary>
/// <param name="start">The start value of the range</param>
/// <param name="end">The end value of the range</param>
public IntRange(int start, int end)
{
Start = start;
End = end;
_rand = new Random();
}
/// <summary>
/// Gets or sets the start value of the range
/// </summary>
public int Start { get; set; }
/// <summary>
/// Gets or sets the end value of the range
/// </summary>
public int End { get; set; }
/// <summary>
/// Determines whether the given value is in this range
/// </summary>
/// <param name="value">The value to check</param>
/// <param name="inclusive">
/// Whether the value may be equal to <see cref="Start" /> or <see cref="End" />
/// <para>Defaults to <see langword="true" /></para>
/// </param>
/// <returns></returns>
public bool IsInRange(int value, bool inclusive = true)
{
if (inclusive)
return value >= Start && value <= End;
return value > Start && value < End;
}
/// <summary>
/// Returns a pseudo-random value between <see cref="Start" /> and <see cref="End" />
/// </summary>
/// <param name="inclusive">Whether the value may be equal to <see cref="Start" /></param>
/// <returns>The pseudo-random value</returns>
public int GetRandomValue(bool inclusive = true)
{
if (inclusive)
return _rand.Next(Start, End + 1);
return _rand.Next(Start + 1, End);
}
}
}

View File

@ -77,6 +77,7 @@ namespace Artemis.Core.Services
RegisterModifierType(Constants.CorePluginInfo, new SKColorBrightenModifierType());
RegisterModifierType(Constants.CorePluginInfo, new SKColorDarkenModifierType());
RegisterModifierType(Constants.CorePluginInfo, new SKColorRotateHueModifierType());
RegisterModifierType(Constants.CorePluginInfo, new SKColorInvertModifierType());
}
}
}

View File

@ -332,5 +332,8 @@
<Page Update="DefaultTypes\DataModel\Input\StringDataModelInputView.xaml">
<SubType>Designer</SubType>
</Page>
<Page Update="DefaultTypes\PropertyInput\FloatRangePropertyInputView.xaml">
<SubType>Designer</SubType>
</Page>
</ItemGroup>
</Project>

View File

@ -0,0 +1,33 @@
<UserControl x:Class="Artemis.UI.PropertyInput.FloatRangePropertyInputView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Artemis.UI.PropertyInput"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:FloatRangePropertyInputViewModel}">
<StackPanel Orientation="Horizontal">
<TextBlock Width="10" Text="{Binding LayerProperty.PropertyDescription.InputPrefix}" />
<shared:DraggableFloat ToolTip="Start"
Value="{Binding Start}"
StepSize="{Binding LayerProperty.PropertyDescription.InputStepSize}"
Max="{Binding End}"
Min="{Binding LayerProperty.PropertyDescription.MinInputValue}"
DragStarted="{s:Action InputDragStarted}"
DragEnded="{s:Action InputDragEnded}"
IsEnabled="{Binding IsStartEnabled}"/>
<TextBlock Margin="5 0" VerticalAlignment="Bottom">-</TextBlock>
<shared:DraggableFloat ToolTip="End"
Value="{Binding End}"
StepSize="{Binding LayerProperty.PropertyDescription.InputStepSize}"
Max="{Binding LayerProperty.PropertyDescription.MaxInputValue}"
Min="{Binding Start}"
DragStarted="{s:Action InputDragStarted}"
DragEnded="{s:Action InputDragEnded}"
IsEnabled="{Binding IsEndEnabled}"/>
<TextBlock Width="10" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" />
</StackPanel>
</UserControl>

View File

@ -0,0 +1,82 @@
using System;
using Artemis.Core;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using FluentValidation;
using Stylet;
namespace Artemis.UI.PropertyInput
{
public class FloatRangePropertyInputViewModel : PropertyInputViewModel<FloatRange>
{
private readonly DataBindingRegistration<FloatRange, float> _startRegistration;
private readonly DataBindingRegistration<FloatRange, float> _endRegistration;
public FloatRangePropertyInputViewModel(LayerProperty<FloatRange> layerProperty,
IProfileEditorService profileEditorService,
IModelValidator<FloatRangePropertyInputViewModel> validator) : base(layerProperty, profileEditorService, validator)
{
_startRegistration = layerProperty.GetDataBindingRegistration(range => range.Start);
_endRegistration = layerProperty.GetDataBindingRegistration(range => range.End);
}
public float Start
{
get => InputValue?.Start ?? 0f;
set
{
if (InputValue == null)
InputValue = new FloatRange(value, value + 1f);
else
InputValue.Start = value;
NotifyOfPropertyChange(nameof(Start));
}
}
public float End
{
get => InputValue?.End ?? 0f;
set
{
if (InputValue == null)
InputValue = new FloatRange(value - 1f, value);
else
InputValue.End = value;
NotifyOfPropertyChange(nameof(End));
}
}
public bool IsStartEnabled => _startRegistration.DataBinding == null;
public bool IsEndEnabled => _endRegistration.DataBinding == null;
protected override void OnInputValueChanged()
{
NotifyOfPropertyChange(nameof(Start));
NotifyOfPropertyChange(nameof(End));
}
protected override void OnDataBindingsChanged()
{
NotifyOfPropertyChange(nameof(IsStartEnabled));
NotifyOfPropertyChange(nameof(IsEndEnabled));
}
}
public class FloatRangePropertyInputViewModelValidator : AbstractValidator<FloatRangePropertyInputViewModel>
{
public FloatRangePropertyInputViewModelValidator()
{
RuleFor(vm => vm.Start).LessThanOrEqualTo(vm => vm.End);
RuleFor(vm => vm.End).GreaterThanOrEqualTo(vm => vm.Start);
RuleFor(vm => vm.Start)
.GreaterThanOrEqualTo(vm => Convert.ToSingle(vm.LayerProperty.PropertyDescription.MinInputValue))
.When(vm => vm.LayerProperty.PropertyDescription.MinInputValue.IsNumber());
RuleFor(vm => vm.End)
.LessThanOrEqualTo(vm => Convert.ToSingle(vm.LayerProperty.PropertyDescription.MaxInputValue))
.When(vm => vm.LayerProperty.PropertyDescription.MaxInputValue.IsNumber());
}
}
}

View File

@ -0,0 +1,33 @@
<UserControl x:Class="Artemis.UI.PropertyInput.IntRangePropertyInputView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Artemis.UI.PropertyInput"
xmlns:s="https://github.com/canton7/Stylet"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
d:DataContext="{d:DesignInstance local:IntRangePropertyInputViewModel}">
<StackPanel Orientation="Horizontal">
<TextBlock Width="10" Text="{Binding LayerProperty.PropertyDescription.InputPrefix}" />
<shared:DraggableFloat ToolTip="Start"
Value="{Binding Start}"
StepSize="{Binding LayerProperty.PropertyDescription.InputStepSize}"
Max="{Binding End}"
Min="{Binding LayerProperty.PropertyDescription.MinInputValue}"
DragStarted="{s:Action InputDragStarted}"
DragEnded="{s:Action InputDragEnded}"
IsEnabled="{Binding IsStartEnabled}"/>
<TextBlock Margin="5 0" VerticalAlignment="Bottom">-</TextBlock>
<shared:DraggableFloat ToolTip="End"
Value="{Binding End}"
StepSize="{Binding LayerProperty.PropertyDescription.InputStepSize}"
Max="{Binding LayerProperty.PropertyDescription.MaxInputValue}"
Min="{Binding Start}"
DragStarted="{s:Action InputDragStarted}"
DragEnded="{s:Action InputDragEnded}"
IsEnabled="{Binding IsEndEnabled}"/>
<TextBlock Width="10" Text="{Binding LayerProperty.PropertyDescription.InputAffix}" />
</StackPanel>
</UserControl>

View File

@ -0,0 +1,82 @@
using System;
using Artemis.Core;
using Artemis.UI.Shared;
using Artemis.UI.Shared.Services;
using FluentValidation;
using Stylet;
namespace Artemis.UI.PropertyInput
{
public class IntRangePropertyInputViewModel : PropertyInputViewModel<IntRange>
{
private readonly DataBindingRegistration<IntRange, int> _startRegistration;
private readonly DataBindingRegistration<IntRange, int> _endRegistration;
public IntRangePropertyInputViewModel(LayerProperty<IntRange> layerProperty,
IProfileEditorService profileEditorService,
IModelValidator<IntRangePropertyInputViewModel> validator) : base(layerProperty, profileEditorService, validator)
{
_startRegistration = layerProperty.GetDataBindingRegistration(range => range.Start);
_endRegistration = layerProperty.GetDataBindingRegistration(range => range.End);
}
public int Start
{
get => InputValue?.Start ?? 0;
set
{
if (InputValue == null)
InputValue = new IntRange(value, value + 1);
else
InputValue.Start = value;
NotifyOfPropertyChange(nameof(Start));
}
}
public int End
{
get => InputValue?.End ?? 0;
set
{
if (InputValue == null)
InputValue = new IntRange(value - 1, value);
else
InputValue.End = value;
NotifyOfPropertyChange(nameof(End));
}
}
public bool IsStartEnabled => _startRegistration.DataBinding == null;
public bool IsEndEnabled => _endRegistration.DataBinding == null;
protected override void OnInputValueChanged()
{
NotifyOfPropertyChange(nameof(Start));
NotifyOfPropertyChange(nameof(End));
}
protected override void OnDataBindingsChanged()
{
NotifyOfPropertyChange(nameof(IsStartEnabled));
NotifyOfPropertyChange(nameof(IsEndEnabled));
}
}
public class IntRangePropertyInputViewModelValidator : AbstractValidator<IntRangePropertyInputViewModel>
{
public IntRangePropertyInputViewModelValidator()
{
RuleFor(vm => vm.Start).LessThanOrEqualTo(vm => vm.End);
RuleFor(vm => vm.End).GreaterThanOrEqualTo(vm => vm.Start);
RuleFor(vm => vm.Start)
.GreaterThanOrEqualTo(vm => Convert.ToInt32(vm.LayerProperty.PropertyDescription.MinInputValue))
.When(vm => vm.LayerProperty.PropertyDescription.MinInputValue.IsNumber());
RuleFor(vm => vm.End)
.LessThanOrEqualTo(vm => Convert.ToInt32(vm.LayerProperty.PropertyDescription.MaxInputValue))
.When(vm => vm.LayerProperty.PropertyDescription.MaxInputValue.IsNumber());
}
}
}

View File

@ -67,6 +67,8 @@ namespace Artemis.UI.Services
_profileEditorService.RegisterPropertyInput<SKSizePropertyInputViewModel>(Constants.CorePluginInfo);
_profileEditorService.RegisterPropertyInput(typeof(EnumPropertyInputViewModel<>), Constants.CorePluginInfo);
_profileEditorService.RegisterPropertyInput<BoolPropertyInputViewModel>(Constants.CorePluginInfo);
_profileEditorService.RegisterPropertyInput<FloatRangePropertyInputViewModel>(Constants.CorePluginInfo);
_profileEditorService.RegisterPropertyInput<IntRangePropertyInputViewModel>(Constants.CorePluginInfo);
_registeredBuiltInPropertyEditors = true;
}