diff --git a/src/Artemis.Core/DefaultTypes/DataBindings/Modifiers/Colors/SKColorDesaturateModifierType.cs b/src/Artemis.Core/DefaultTypes/DataBindings/Modifiers/Colors/SKColorDesaturateModifierType.cs index 2c27d58d1..14c9d8790 100644 --- a/src/Artemis.Core/DefaultTypes/DataBindings/Modifiers/Colors/SKColorDesaturateModifierType.cs +++ b/src/Artemis.Core/DefaultTypes/DataBindings/Modifiers/Colors/SKColorDesaturateModifierType.cs @@ -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); } } } \ No newline at end of file diff --git a/src/Artemis.Core/DefaultTypes/DataBindings/Modifiers/Colors/SKColorInvertModifierType.cs b/src/Artemis.Core/DefaultTypes/DataBindings/Modifiers/Colors/SKColorInvertModifierType.cs new file mode 100644 index 000000000..804c551fc --- /dev/null +++ b/src/Artemis.Core/DefaultTypes/DataBindings/Modifiers/Colors/SKColorInvertModifierType.cs @@ -0,0 +1,18 @@ +using SkiaSharp; + +namespace Artemis.Core.DefaultTypes +{ + internal class SKColorInvertModifierType : DataBindingModifierType + { + 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); + } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/DefaultTypes/DataBindings/Modifiers/Colors/SKColorSaturateModifierType.cs b/src/Artemis.Core/DefaultTypes/DataBindings/Modifiers/Colors/SKColorSaturateModifierType.cs index daa5b0fc7..750915da6 100644 --- a/src/Artemis.Core/DefaultTypes/DataBindings/Modifiers/Colors/SKColorSaturateModifierType.cs +++ b/src/Artemis.Core/DefaultTypes/DataBindings/Modifiers/Colors/SKColorSaturateModifierType.cs @@ -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); } } } \ No newline at end of file diff --git a/src/Artemis.Core/DefaultTypes/Properties/FloatLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/FloatLayerProperty.cs index 4eb435574..4ac6959c0 100644 --- a/src/Artemis.Core/DefaultTypes/Properties/FloatLayerProperty.cs +++ b/src/Artemis.Core/DefaultTypes/Properties/FloatLayerProperty.cs @@ -1,4 +1,6 @@ -namespace Artemis.Core.DefaultTypes +using System; + +namespace Artemis.Core.DefaultTypes { /// public class FloatLayerProperty : LayerProperty diff --git a/src/Artemis.Core/DefaultTypes/Properties/FloatRangeLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/FloatRangeLayerProperty.cs new file mode 100644 index 000000000..64ff24c0a --- /dev/null +++ b/src/Artemis.Core/DefaultTypes/Properties/FloatRangeLayerProperty.cs @@ -0,0 +1,31 @@ +namespace Artemis.Core.DefaultTypes +{ + /// + public class FloatRangeLayerProperty : LayerProperty + { + internal FloatRangeLayerProperty() + { + RegisterDataBindingProperty(range => range.Start, new FloatDataBindingConverter()); + RegisterDataBindingProperty(range => range.End, new FloatDataBindingConverter()); + + CurrentValueSet += OnCurrentValueSet; + } + + /// + 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 e) + { + // Don't allow the int range to be null + BaseValue ??= DefaultValue ?? new FloatRange(0, 0); + } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/DefaultTypes/Properties/IntRangeLayerProperty.cs b/src/Artemis.Core/DefaultTypes/Properties/IntRangeLayerProperty.cs new file mode 100644 index 000000000..4aa74cbd0 --- /dev/null +++ b/src/Artemis.Core/DefaultTypes/Properties/IntRangeLayerProperty.cs @@ -0,0 +1,33 @@ +using System; + +namespace Artemis.Core.DefaultTypes +{ + /// + public class IntRangeLayerProperty : LayerProperty + { + internal IntRangeLayerProperty() + { + RegisterDataBindingProperty(range => range.Start, new IntDataBindingConverter()); + RegisterDataBindingProperty(range => range.End, new IntDataBindingConverter()); + + CurrentValueSet += OnCurrentValueSet; + } + + /// + 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 e) + { + // Don't allow the int range to be null + BaseValue ??= DefaultValue ?? new IntRange(0, 0); + } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/FloatRange.cs b/src/Artemis.Core/Models/Profile/LayerProperties/FloatRange.cs new file mode 100644 index 000000000..3df43ae4d --- /dev/null +++ b/src/Artemis.Core/Models/Profile/LayerProperties/FloatRange.cs @@ -0,0 +1,63 @@ +using System; + +namespace Artemis.Core +{ + /// + /// Represents a range between two single-precision floating point numbers + /// + public class FloatRange + { + private readonly Random _rand; + + /// + /// Creates a new instance of the class + /// + /// The start value of the range + /// The end value of the range + public FloatRange(float start, float end) + { + Start = start; + End = end; + + _rand = new Random(); + } + + /// + /// Gets or sets the start value of the range + /// + public float Start { get; set; } + + /// + /// Gets or sets the end value of the range + /// + public float End { get; set; } + + /// + /// Determines whether the given value is in this range + /// + /// The value to check + /// + /// Whether the value may be equal to or + /// Defaults to + /// + /// + public bool IsInRange(float value, bool inclusive = true) + { + if (inclusive) + return value >= Start && value <= End; + return value > Start && value < End; + } + + /// + /// Returns a pseudo-random value between and + /// + /// Whether the value may be equal to + /// The pseudo-random value + 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; + } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/IntRange.cs b/src/Artemis.Core/Models/Profile/LayerProperties/IntRange.cs new file mode 100644 index 000000000..ebe6a4d80 --- /dev/null +++ b/src/Artemis.Core/Models/Profile/LayerProperties/IntRange.cs @@ -0,0 +1,63 @@ +using System; + +namespace Artemis.Core +{ + /// + /// Represents a range between two signed integers + /// + public class IntRange + { + private readonly Random _rand; + + /// + /// Creates a new instance of the class + /// + /// The start value of the range + /// The end value of the range + public IntRange(int start, int end) + { + Start = start; + End = end; + + _rand = new Random(); + } + + /// + /// Gets or sets the start value of the range + /// + public int Start { get; set; } + + /// + /// Gets or sets the end value of the range + /// + public int End { get; set; } + + /// + /// Determines whether the given value is in this range + /// + /// The value to check + /// + /// Whether the value may be equal to or + /// Defaults to + /// + /// + public bool IsInRange(int value, bool inclusive = true) + { + if (inclusive) + return value >= Start && value <= End; + return value > Start && value < End; + } + + /// + /// Returns a pseudo-random value between and + /// + /// Whether the value may be equal to + /// The pseudo-random value + public int GetRandomValue(bool inclusive = true) + { + if (inclusive) + return _rand.Next(Start, End + 1); + return _rand.Next(Start + 1, End); + } + } +} \ No newline at end of file diff --git a/src/Artemis.Core/Services/Registration/DataBindingService.cs b/src/Artemis.Core/Services/Registration/DataBindingService.cs index 96dedfe50..48f853641 100644 --- a/src/Artemis.Core/Services/Registration/DataBindingService.cs +++ b/src/Artemis.Core/Services/Registration/DataBindingService.cs @@ -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()); } } } \ No newline at end of file diff --git a/src/Artemis.UI/Artemis.UI.csproj b/src/Artemis.UI/Artemis.UI.csproj index 22186756d..6e8ced594 100644 --- a/src/Artemis.UI/Artemis.UI.csproj +++ b/src/Artemis.UI/Artemis.UI.csproj @@ -332,5 +332,8 @@ Designer + + Designer + \ No newline at end of file diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/FloatRangePropertyInputView.xaml b/src/Artemis.UI/DefaultTypes/PropertyInput/FloatRangePropertyInputView.xaml new file mode 100644 index 000000000..8efeaa4cd --- /dev/null +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/FloatRangePropertyInputView.xaml @@ -0,0 +1,33 @@ + + + + + - + + + + \ No newline at end of file diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/FloatRangePropertyInputViewModel.cs b/src/Artemis.UI/DefaultTypes/PropertyInput/FloatRangePropertyInputViewModel.cs new file mode 100644 index 000000000..afdd82d35 --- /dev/null +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/FloatRangePropertyInputViewModel.cs @@ -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 + { + private readonly DataBindingRegistration _startRegistration; + private readonly DataBindingRegistration _endRegistration; + + public FloatRangePropertyInputViewModel(LayerProperty layerProperty, + IProfileEditorService profileEditorService, + IModelValidator 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 + { + 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()); + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/IntRangePropertyInputView.xaml b/src/Artemis.UI/DefaultTypes/PropertyInput/IntRangePropertyInputView.xaml new file mode 100644 index 000000000..2cef1a896 --- /dev/null +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/IntRangePropertyInputView.xaml @@ -0,0 +1,33 @@ + + + + + - + + + + \ No newline at end of file diff --git a/src/Artemis.UI/DefaultTypes/PropertyInput/IntRangePropertyInputViewModel.cs b/src/Artemis.UI/DefaultTypes/PropertyInput/IntRangePropertyInputViewModel.cs new file mode 100644 index 000000000..072cea046 --- /dev/null +++ b/src/Artemis.UI/DefaultTypes/PropertyInput/IntRangePropertyInputViewModel.cs @@ -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 + { + private readonly DataBindingRegistration _startRegistration; + private readonly DataBindingRegistration _endRegistration; + + public IntRangePropertyInputViewModel(LayerProperty layerProperty, + IProfileEditorService profileEditorService, + IModelValidator 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 + { + 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()); + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Services/RegistrationService.cs b/src/Artemis.UI/Services/RegistrationService.cs index 707222791..1413abbca 100644 --- a/src/Artemis.UI/Services/RegistrationService.cs +++ b/src/Artemis.UI/Services/RegistrationService.cs @@ -67,6 +67,8 @@ namespace Artemis.UI.Services _profileEditorService.RegisterPropertyInput(Constants.CorePluginInfo); _profileEditorService.RegisterPropertyInput(typeof(EnumPropertyInputViewModel<>), Constants.CorePluginInfo); _profileEditorService.RegisterPropertyInput(Constants.CorePluginInfo); + _profileEditorService.RegisterPropertyInput(Constants.CorePluginInfo); + _profileEditorService.RegisterPropertyInput(Constants.CorePluginInfo); _registeredBuiltInPropertyEditors = true; }