// ReSharper disable MemberCanBeProtected.Global
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Global
using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using RGB.NET.Core;
using RGB.NET.Presets.Decorators;
namespace RGB.NET.Presets.Textures.Gradients
{
///
///
///
/// Represents a basic gradient.
///
public abstract class AbstractGradient : AbstractDecoratable, IGradient
{
#region Properties & Fields
///
/// Gets a list of the stops used by this .
///
public ObservableCollection GradientStops { get; } = new();
private bool _wrapGradient;
///
/// Gets or sets if the Gradient wraps around if there isn't a second stop to take.
/// Example: There is a stop at offset 0.0, 0.5 and 0.75.
/// Without wrapping offset 1.0 will be calculated the same as 0.75; with wrapping it would be the same as 0.0.
///
public bool WrapGradient
{
get => _wrapGradient;
set => SetProperty(ref _wrapGradient, value);
}
#endregion
#region Events
///
public event EventHandler? GradientChanged;
#endregion
#region Constructors
///
/// Initializes a new instance of the class.
///
protected AbstractGradient()
{
GradientStops.CollectionChanged += GradientCollectionChanged;
PropertyChanged += (_, _) => OnGradientChanged();
}
///
/// Initializes a new instance of the class.
///
/// The stops with which the gradient should be initialized.
protected AbstractGradient(params GradientStop[] gradientStops)
{
GradientStops.CollectionChanged += GradientCollectionChanged;
PropertyChanged += (_, _) => OnGradientChanged();
foreach (GradientStop gradientStop in gradientStops)
GradientStops.Add(gradientStop);
}
///
/// Initializes a new instance of the class.
///
/// Specifies whether the gradient should wrapp or not (see for an example of what this means).
/// The stops with which the gradient should be initialized.
protected AbstractGradient(bool wrapGradient, params GradientStop[] gradientStops)
{
this.WrapGradient = wrapGradient;
GradientStops.CollectionChanged += GradientCollectionChanged;
PropertyChanged += (_, _) => OnGradientChanged();
foreach (GradientStop gradientStop in gradientStops)
GradientStops.Add(gradientStop);
}
#endregion
#region Methods
///
/// Clips the offset and ensures, that it is inside the bounds of the stop list.
///
/// The offset to clip.
/// The clipped offset.
protected float ClipOffset(float offset)
{
float max = GradientStops.Max(stop => stop.Offset);
if (offset > max)
return max;
float min = GradientStops.Min(stop => stop.Offset);
return offset < min ? min : offset;
}
///
public abstract Color GetColor(float offset);
///
public virtual void Move(float offset)
{
offset /= 360.0f;
foreach (GradientStop gradientStop in GradientStops)
gradientStop.Offset += offset;
while (GradientStops.All(stop => stop.Offset > 1))
foreach (GradientStop gradientStop in GradientStops)
gradientStop.Offset -= 1;
while (GradientStops.All(stop => stop.Offset < 0))
foreach (GradientStop gradientStop in GradientStops)
gradientStop.Offset += 1;
}
///
/// Should be called to indicate that the gradient was changed.
///
protected void OnGradientChanged() => GradientChanged?.Invoke(this, EventArgs.Empty);
private void GradientCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
if (e.OldItems != null)
foreach (GradientStop gradientStop in e.OldItems)
gradientStop.PropertyChanged -= GradientStopChanged;
if (e.NewItems != null)
foreach (GradientStop gradientStop in e.NewItems)
gradientStop.PropertyChanged += GradientStopChanged;
OnGradientChanged();
}
private void GradientStopChanged(object? sender, PropertyChangedEventArgs propertyChangedEventArgs) => OnGradientChanged();
#endregion
}
}