From cbf2cd17367b6a2afbcf9c09218512fc3b2091ba Mon Sep 17 00:00:00 2001 From: Robert Date: Sun, 30 Oct 2022 18:13:47 +0100 Subject: [PATCH] Nodes - Added color gradient easing node Color ramp node - Fixed 1 and multiples of 1 being treated as 0 --- .../Models/Profile/Colors/ColorGradient.cs | 34 +++++++++ .../Profile/Colors/ColorGradientStop.cs | 6 ++ .../Profile/LayerProperties/LayerProperty.cs | 2 +- .../Nodes/Color/RampSKColorNode.cs | 8 ++- .../Nodes/Easing/ColorGradientEasingNode.cs | 71 +++++++++++++++++++ 5 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 src/Artemis.VisualScripting/Nodes/Easing/ColorGradientEasingNode.cs diff --git a/src/Artemis.Core/Models/Profile/Colors/ColorGradient.cs b/src/Artemis.Core/Models/Profile/Colors/ColorGradient.cs index 18c570c9a..eeb5c3938 100644 --- a/src/Artemis.Core/Models/Profile/Colors/ColorGradient.cs +++ b/src/Artemis.Core/Models/Profile/Colors/ColorGradient.cs @@ -468,6 +468,8 @@ public class ColorGradient : IList, IList, INotifyCollectionC { _stops.Add(item); item.ColorGradient = this; + // Update the position, reapplying the overlap-check in Position's setter + item.Position = item.Position; item.PropertyChanged += ItemOnPropertyChanged; OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, _stops.IndexOf(item))); @@ -624,4 +626,36 @@ public class ColorGradient : IList, IList, INotifyCollectionC } #endregion + + public ColorGradient Interpolate(ColorGradient targetValue, float progress) + { + ColorGradient interpolated = new(this); + + // Add new stops + if (targetValue.Count > interpolated.Count) + { + // Prefer the stops on a vacant position + foreach (ColorGradientStop stop in targetValue.Take(targetValue.Count - interpolated.Count)) + interpolated.Add(new ColorGradientStop(GetColor(stop.Position), stop.Position)); + } + // Interpolate stops + int index = 0; + foreach (ColorGradientStop stop in interpolated.ToList()) + { + if (index < targetValue.Count) + { + ColorGradientStop targetStop = targetValue[index]; + stop.Interpolate(targetStop, progress); + } + // Interpolate stops not on the target gradient + else + { + stop.Color = stop.Color.Interpolate(targetValue.GetColor(stop.Position), progress); + } + + index++; + } + + return interpolated; + } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/Colors/ColorGradientStop.cs b/src/Artemis.Core/Models/Profile/Colors/ColorGradientStop.cs index 96af9f2b6..d38fe4598 100644 --- a/src/Artemis.Core/Models/Profile/Colors/ColorGradientStop.cs +++ b/src/Artemis.Core/Models/Profile/Colors/ColorGradientStop.cs @@ -95,4 +95,10 @@ public class ColorGradientStop : CorePropertyChanged } #endregion + + public void Interpolate(ColorGradientStop targetValue, float progress) + { + Color = Color.Interpolate(targetValue.Color, progress); + Position = Position + ((targetValue.Position - Position) * progress); + } } \ No newline at end of file diff --git a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs index 2772c3821..ff9a7d514 100644 --- a/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs +++ b/src/Artemis.Core/Models/Profile/LayerProperties/LayerProperty.cs @@ -237,7 +237,7 @@ public class LayerProperty : CorePropertyChanged, ILayerProperty } /// - /// Gets or sets the base value of this layer property without any keyframes applied + /// Gets or sets the base value of this layer property without any keyframes or data bindings applied /// public T BaseValue { diff --git a/src/Artemis.VisualScripting/Nodes/Color/RampSKColorNode.cs b/src/Artemis.VisualScripting/Nodes/Color/RampSKColorNode.cs index b1cc9b183..c70e1489d 100644 --- a/src/Artemis.VisualScripting/Nodes/Color/RampSKColorNode.cs +++ b/src/Artemis.VisualScripting/Nodes/Color/RampSKColorNode.cs @@ -22,7 +22,13 @@ public class RampSKColorNode : Node(); + EasingTime = CreateInputPin("delay"); + EasingFunction = CreateInputPin("function"); + + Output = CreateOutputPin(); + } + + public InputPin Input { get; set; } + public InputPin EasingTime { get; set; } + public InputPin EasingFunction { get; set; } + + public OutputPin Output { get; set; } + + public override void Evaluate() + { + DateTime now = DateTime.Now; + + if (Input.Value == null) + return; + + // If the value changed reset progress + if (!Equals(_targetValue, Input.Value)) + { + _sourceValue = _currentValue ?? new ColorGradient(Input.Value); + _targetValue = new ColorGradient(Input.Value); + _progress = 0f; + } + + // Update until finished + if (_progress < 1f) + { + Update(); + Output.Value = _currentValue; + } + // Stop updating past 1 and use the target value + else + { + Output.Value = _targetValue; + } + + _lastEvaluate = now; + } + + private void Update() + { + if (_sourceValue == null || _targetValue == null) + return; + + float easingTime = EasingTime.Value != 0f ? EasingTime.Value : 1f; + TimeSpan delta = DateTime.Now - _lastEvaluate; + + // In case of odd delta's, keep progress between 0f and 1f + _progress = Math.Clamp(_progress + (float) delta.TotalMilliseconds / easingTime, 0f, 1f); + _currentValue = _sourceValue.Interpolate(_targetValue, (float) Easings.Interpolate(_progress, EasingFunction.Value)); + } +} \ No newline at end of file