// 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; namespace RGB.NET.Brushes.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 ObservableCollection(); 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 += (sender, args) => 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 += (sender, args) => 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 += (sender, args) => 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. /// /// /// protected double ClipOffset(double offset) { double max = GradientStops.Max(n => n.Offset); if (offset > max) return max; double min = GradientStops.Min(n => n.Offset); return offset < min ? min : offset; } /// public abstract Color GetColor(double offset); /// public virtual void Move(double offset) { offset /= 360.0; foreach (GradientStop gradientStop in GradientStops) gradientStop.Offset += offset; while (GradientStops.All(x => x.Offset > 1)) foreach (GradientStop gradientStop in GradientStops) gradientStop.Offset -= 1; while (GradientStops.All(x => x.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, null); 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 } }