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

Merge branch 'feature/gradient-performance' into development

This commit is contained in:
Robert 2022-11-12 22:30:44 +01:00
commit 9681231215
2 changed files with 169 additions and 70 deletions

View File

@ -13,7 +13,7 @@ namespace Artemis.Core;
/// </summary> /// </summary>
public class ColorGradient : IList<ColorGradientStop>, IList, INotifyCollectionChanged public class ColorGradient : IList<ColorGradientStop>, IList, INotifyCollectionChanged
{ {
private static readonly SKColor[] FastLedRainbow = private static readonly SKColor[] FAST_LED_RAINBOW =
{ {
new(0xFFFF0000), // Red new(0xFFFF0000), // Red
new(0xFFFF9900), // Orange new(0xFFFF9900), // Orange
@ -27,8 +27,25 @@ public class ColorGradient : IList<ColorGradientStop>, IList, INotifyCollectionC
}; };
private readonly List<ColorGradientStop> _stops; private readonly List<ColorGradientStop> _stops;
private SKColor[] _colors = Array.Empty<SKColor>();
private float[] _positions = Array.Empty<float>();
private bool _dirty = true;
private bool _updating; private bool _updating;
/// <summary>
/// Gets an array containing the colors of the color gradient.
/// <para/>
/// Note: Making changes to this array will not be reflected on the gradient, is is essentially read-only.
/// </summary>
public SKColor[] Colors => GetColors();
/// <summary>
/// Gets an array containing the positions of colors of the color gradient.
/// <para/>
/// Note: Making changes to this array will not be reflected on the gradient, is is essentially read-only.
/// </summary>
public float[] Positions => GetPositions();
/// <summary> /// <summary>
/// Creates a new instance of the <see cref="ColorGradient" /> class /// Creates a new instance of the <see cref="ColorGradient" /> class
/// </summary> /// </summary>
@ -81,8 +98,12 @@ public class ColorGradient : IList<ColorGradientStop>, IList, INotifyCollectionC
/// last color /// last color
/// </param> /// </param>
/// <returns>An array containing each color in the gradient</returns> /// <returns>An array containing each color in the gradient</returns>
[Obsolete("Use the Colors property instead", true)]
public SKColor[] GetColorsArray(int timesToRepeat = 0, bool seamless = false) public SKColor[] GetColorsArray(int timesToRepeat = 0, bool seamless = false)
{ {
if (timesToRepeat == 0 && !seamless)
return Colors;
List<SKColor> result = new(); List<SKColor> result = new();
if (timesToRepeat == 0) if (timesToRepeat == 0)
result = this.Select(c => c.Color).ToList(); result = this.Select(c => c.Color).ToList();
@ -105,8 +126,12 @@ public class ColorGradient : IList<ColorGradientStop>, IList, INotifyCollectionC
/// last color /// last color
/// </param> /// </param>
/// <returns>An array containing a position for each color between 0.0 and 1.0</returns> /// <returns>An array containing a position for each color between 0.0 and 1.0</returns>
[Obsolete("Use the Positions property instead", true)]
public float[] GetPositionsArray(int timesToRepeat = 0, bool seamless = false) public float[] GetPositionsArray(int timesToRepeat = 0, bool seamless = false)
{ {
if (timesToRepeat == 0 && seamless)
return Positions;
List<float> result = new(); List<float> result = new();
if (timesToRepeat == 0) if (timesToRepeat == 0)
{ {
@ -147,8 +172,12 @@ public class ColorGradient : IList<ColorGradientStop>, IList, INotifyCollectionC
/// A boolean indicating whether to make the gradient seamless by adding the first color behind the /// A boolean indicating whether to make the gradient seamless by adding the first color behind the
/// last color /// last color
/// </param> /// </param>
[Obsolete("Use GetColor(float position) instead.", true)]
public SKColor GetColor(float position, int timesToRepeat = 0, bool seamless = false) public SKColor GetColor(float position, int timesToRepeat = 0, bool seamless = false)
{ {
if (timesToRepeat == 0 && !seamless)
return GetColor(position);
if (!this.Any()) if (!this.Any())
return new SKColor(255, 255, 255); return new SKColor(255, 255, 255);
@ -194,30 +223,38 @@ public class ColorGradient : IList<ColorGradientStop>, IList, INotifyCollectionC
} }
/// <summary> /// <summary>
/// Gets a new ColorGradient with colors looping through the HSV-spectrum /// Gets a color at any position between 0.0 and 1.0 using interpolation
/// </summary> /// </summary>
public static ColorGradient GetUnicornBarf() /// <param name="position">A position between 0.0 and 1.0</param>
public SKColor GetColor(float position)
{ {
ColorGradient gradient = new(); if (_stops.Count == 0)
for (int index = 0; index < FastLedRainbow.Length; index++) return SKColors.Transparent;
{
SKColor skColor = FastLedRainbow[index];
float position = 1f / (FastLedRainbow.Length - 1f) * index;
gradient.Add(new ColorGradientStop(skColor, position));
}
return gradient; if (_stops.Count == 1)
} return _stops[0].Color;
/// <summary> if (position <= 0)
/// Gets a new ColorGradient with random colors from the HSV-spectrum return _stops[0].Color;
/// </summary>
/// <param name="stops">The amount of stops to add</param> if (position >= 1)
public ColorGradient GetRandom(int stops) return _stops[^1].Color;
{
ColorGradient gradient = new(); //find the first stop after the position
gradient.Randomize(stops); int stop2Index = _stops.FindIndex(s => s.Position >= position);
return gradient; //if the position is before the first stop, return that color
if (stop2Index == 0)
return _stops[0].Color;
//interpolate between that one and the one before
int stop1Index = stop2Index - 1;
ColorGradientStop stop1 = _stops[stop1Index];
ColorGradientStop stop2 = _stops[stop2Index];
//calculate how far between the 2 stops we want to interpolate
float positionBetween = (position - stop1.Position) / (stop2.Position - stop1.Position);
return stop1.Color.Interpolate(stop2.Color, positionBetween);
} }
/// <summary> /// <summary>
@ -243,7 +280,7 @@ public class ColorGradient : IList<ColorGradientStop>, IList, INotifyCollectionC
finally finally
{ {
_updating = false; _updating = false;
Sort(); Update();
} }
} }
@ -289,7 +326,7 @@ public class ColorGradient : IList<ColorGradientStop>, IList, INotifyCollectionC
finally finally
{ {
_updating = false; _updating = false;
Sort(); Update();
} }
} }
@ -307,7 +344,7 @@ public class ColorGradient : IList<ColorGradientStop>, IList, INotifyCollectionC
finally finally
{ {
_updating = false; _updating = false;
Sort(); Update();
} }
} }
@ -332,7 +369,7 @@ public class ColorGradient : IList<ColorGradientStop>, IList, INotifyCollectionC
finally finally
{ {
_updating = false; _updating = false;
Sort(); Update();
} }
} }
@ -358,17 +395,96 @@ public class ColorGradient : IList<ColorGradientStop>, IList, INotifyCollectionC
finally finally
{ {
_updating = false; _updating = false;
Sort(); Update();
} }
} }
/// <summary>
/// Interpolates a color gradient between the this gradient and the provided <paramref name="targetValue"/>.
/// </summary>
/// <param name="targetValue">The second color gradient.</param>
/// <param name="progress">A value between 0 and 1.</param>
/// <returns>The interpolated color gradient.</returns>
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;
}
/// <summary>
/// Gets a new ColorGradient with colors looping through the HSV-spectrum
/// </summary>
public static ColorGradient GetUnicornBarf()
{
ColorGradient gradient = new();
for (int index = 0; index < FAST_LED_RAINBOW.Length; index++)
{
SKColor skColor = FAST_LED_RAINBOW[index];
float position = 1f / (FAST_LED_RAINBOW.Length - 1f) * index;
gradient.Add(new ColorGradientStop(skColor, position));
}
return gradient;
}
/// <summary>
/// Gets a new ColorGradient with random colors from the HSV-spectrum
/// </summary>
/// <param name="stops">The amount of stops to add</param>
public static ColorGradient GetRandom(int stops)
{
ColorGradient gradient = new();
gradient.Randomize(stops);
return gradient;
}
/// <summary> /// <summary>
/// Occurs when any of the stops has changed in some way /// Occurs when any of the stops has changed in some way
/// </summary> /// </summary>
public event EventHandler? StopChanged; public event EventHandler? StopChanged;
internal void Sort() private void ItemOnPropertyChanged(object? sender, PropertyChangedEventArgs e)
{ {
Update();
OnStopChanged();
}
private void OnStopChanged()
{
StopChanged?.Invoke(this, EventArgs.Empty);
}
private void Update()
{
_dirty = true;
if (_updating) if (_updating)
return; return;
@ -387,15 +503,24 @@ public class ColorGradient : IList<ColorGradientStop>, IList, INotifyCollectionC
} }
} }
private void ItemOnPropertyChanged(object? sender, PropertyChangedEventArgs e) private float[] GetPositions()
{ {
Sort(); if (!_dirty)
OnStopChanged(); return _positions;
_colors = this.Select(s => s.Color).ToArray();
_positions = this.Select(s => s.Position).ToArray();
return _positions;
} }
private void OnStopChanged() private SKColor[] GetColors()
{ {
StopChanged?.Invoke(this, EventArgs.Empty); if (!_dirty)
return _colors;
_colors = this.Select(s => s.Color).ToArray();
_positions = this.Select(s => s.Position).ToArray();
return _colors;
} }
#region Equality members #region Equality members
@ -473,7 +598,7 @@ public class ColorGradient : IList<ColorGradientStop>, IList, INotifyCollectionC
item.PropertyChanged += ItemOnPropertyChanged; item.PropertyChanged += ItemOnPropertyChanged;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, _stops.IndexOf(item))); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, _stops.IndexOf(item)));
Sort(); Update();
} }
/// <inheritdoc /> /// <inheritdoc />
@ -583,7 +708,7 @@ public class ColorGradient : IList<ColorGradientStop>, IList, INotifyCollectionC
{ {
_stops.Insert(index, item); _stops.Insert(index, item);
item.PropertyChanged += ItemOnPropertyChanged; item.PropertyChanged += ItemOnPropertyChanged;
Sort(); Update();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, _stops.IndexOf(item))); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, _stops.IndexOf(item)));
} }
@ -608,7 +733,7 @@ public class ColorGradient : IList<ColorGradientStop>, IList, INotifyCollectionC
oldValue.PropertyChanged -= ItemOnPropertyChanged; oldValue.PropertyChanged -= ItemOnPropertyChanged;
_stops[index] = value; _stops[index] = value;
_stops[index].PropertyChanged += ItemOnPropertyChanged; _stops[index].PropertyChanged += ItemOnPropertyChanged;
Sort(); Update();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, oldValue)); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, oldValue));
} }
} }
@ -626,36 +751,4 @@ public class ColorGradient : IList<ColorGradientStop>, IList, INotifyCollectionC
} }
#endregion #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;
}
} }

View File

@ -94,11 +94,17 @@ public class ColorGradientStop : CorePropertyChanged
return stopPosition; return stopPosition;
} }
#endregion /// <summary>
/// Interpolates a color gradient stop between the this stop and the provided <paramref name="targetValue"/>.
/// </summary>
/// <param name="targetValue">The second stop.</param>
/// <param name="progress">A value between 0 and 1.</param>
/// <returns>The interpolated color gradient stop.</returns>
public void Interpolate(ColorGradientStop targetValue, float progress) public void Interpolate(ColorGradientStop targetValue, float progress)
{ {
Color = Color.Interpolate(targetValue.Color, progress); Color = Color.Interpolate(targetValue.Color, progress);
Position = Position + ((targetValue.Position - Position) * progress); Position += (targetValue.Position - Position) * progress;
} }
#endregion
} }