// 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 }