// ReSharper disable UnusedMember.Global using System.Collections.Generic; using System.Linq; using RGB.NET.Core; namespace RGB.NET.Brushes.Gradients { /// /// Represents a linear interpolated gradient with n stops. /// public class LinearGradient : AbstractGradient { #region Constructors /// /// Initializes a new instance of the class. /// public LinearGradient() { } /// /// Initializes a new instance of the class. /// /// The stops with which the gradient should be initialized. public LinearGradient(params GradientStop[] gradientStops) : base(gradientStops) { } /// /// 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. public LinearGradient(bool wrapGradient, params GradientStop[] gradientStops) : base(wrapGradient, gradientStops) { } #endregion #region Methods /// /// Gets the linear interpolated at the given offset. /// /// The percentage offset to take the color from. /// The at the specific offset. public override Color GetColor(double offset) { if (GradientStops.Count == 0) return Color.Transparent; if (GradientStops.Count == 1) return new Color(GradientStops[0].Color); (GradientStop gsBefore, GradientStop gsAfter) = GetEnclosingGradientStops(offset, GradientStops, WrapGradient); double blendFactor = 0; if (!gsBefore.Offset.Equals(gsAfter.Offset)) blendFactor = ((offset - gsBefore.Offset) / (gsAfter.Offset - gsBefore.Offset)); byte colA = (byte)(((gsAfter.Color.A - gsBefore.Color.A) * blendFactor) + gsBefore.Color.A); byte colR = (byte)(((gsAfter.Color.R - gsBefore.Color.R) * blendFactor) + gsBefore.Color.R); byte colG = (byte)(((gsAfter.Color.G - gsBefore.Color.G) * blendFactor) + gsBefore.Color.G); byte colB = (byte)(((gsAfter.Color.B - gsBefore.Color.B) * blendFactor) + gsBefore.Color.B); return new Color(colA, colR, colG, colB); } /// /// Get the two s encapsulating the given offset. /// /// The reference offset. /// The to choose from. /// Bool indicating if the gradient should be wrapped or not. /// protected virtual (GradientStop gsBefore, GradientStop gsAfter) GetEnclosingGradientStops(double offset, IEnumerable stops, bool wrap) { LinkedList orderedStops = new LinkedList(stops.OrderBy(x => x.Offset)); if (wrap) { GradientStop gsBefore, gsAfter; do { gsBefore = orderedStops.LastOrDefault(n => n.Offset <= offset); if (gsBefore == null) { GradientStop lastStop = orderedStops.Last.Value; orderedStops.AddFirst(new GradientStop(lastStop.Offset - 1, lastStop.Color)); orderedStops.RemoveLast(); } gsAfter = orderedStops.FirstOrDefault(n => n.Offset >= offset); if (gsAfter == null) { GradientStop firstStop = orderedStops.First.Value; orderedStops.AddLast(new GradientStop(firstStop.Offset + 1, firstStop.Color)); orderedStops.RemoveFirst(); } } while ((gsBefore == null) || (gsAfter == null)); return (gsBefore, gsAfter); } offset = ClipOffset(offset); return (orderedStops.Last(n => n.Offset <= offset), orderedStops.First(n => n.Offset >= offset)); } #endregion } }