using System.Collections; using System.Collections.Generic; using System.Drawing; using System.Numerics; namespace Dashboard { /// /// Enumeration of the kinds of gradients available. /// public enum GradientType { /// /// A gradient which transitions over a set axis. /// Axial, /// /// A gradient which transitions along elliptical curves. /// Radial, } /// /// A single gradient stop. /// /// The position of the gradient stop. Must be [0,1]. /// The color value for the stop. public record struct GradientStop(float Position, Color Color); /// /// Represents a linear gradient. /// public struct Gradient : ICollection, ICloneable { private readonly List _stops = new List(); /// /// Gradient type. /// public GradientType Type { get; set; } = GradientType.Axial; /// /// First gradient control point. /// public Vector2 C0 { get; set; } = Vector2.Zero; /// /// Second gradient control point. /// public Vector2 C1 { get; set; } = Vector2.One; /// /// Number of stops in a gradient. /// public int Count => _stops.Count; public bool IsReadOnly => false; /// /// Get a gradient control point. /// /// The index to get the control point for. public GradientStop this[int index] { get => _stops[index]; set { RemoveAt(index); Add(value); } } public Gradient() { } public Gradient(Color a, Color b) { Add(new GradientStop(0, a)); Add(new GradientStop(1, b)); } public Gradient(IEnumerable stops) { _stops.AddRange(stops); if (_stops.Any(x => x.Position < 0 || x.Position > 1)) throw new Exception("Gradient stop positions must be in the range [0, 1]."); _stops.Sort((a, b) => a.Position.CompareTo(b.Position)); } public Color GetColor(float position) { if (Count == 0) return Color.Black; else if (Count == 1) return _stops[0].Color; int pivot = _stops.FindIndex(x => x.Position < position); GradientStop left, right; if (pivot == -1) { left = right = _stops[^1]; } else if (pivot == 0) { left = right = _stops[0]; } else { left = _stops[pivot-1]; right = _stops[pivot]; } float weight = (position - left.Position) / (right.Position - left.Position); Vector4 lcolor = new Vector4(left.Color.R, left.Color.G, left.Color.B, left.Color.A) * (1-weight); Vector4 rcolor = new Vector4(right.Color.R, right.Color.G, right.Color.B, right.Color.A) * weight; Vector4 color = lcolor + rcolor; return Color.FromArgb((byte)color.W, (byte)color.X, (byte)color.Y, (byte)color.Z); } public Gradient Clone() { Gradient gradient = new Gradient() { Type = Type, C0 = C0, C1 = C1, }; foreach (GradientStop stop in _stops) { gradient.Add(stop); } return gradient; } object ICloneable.Clone() { return Clone(); } public IEnumerator GetEnumerator() { return _stops.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)_stops).GetEnumerator(); } public void Add(GradientStop item) { if (item.Position < 0 || item.Position > 1) throw new Exception("Gradient stop positions must be in the range [0, 1]."); int index = _stops.FindIndex(x => x.Position > item.Position); if (index == -1) index = _stops.Count; _stops.Insert(index, item); } public void Clear() { _stops.Clear(); } public bool Contains(GradientStop item) { return _stops.Contains(item); } public void CopyTo(GradientStop[] array, int arrayIndex) { _stops.CopyTo(array, arrayIndex); } public bool Remove(GradientStop item) { return _stops.Remove(item); } public void RemoveAt(int index) { _stops.RemoveAt(index); } public override int GetHashCode() { HashCode code = new HashCode(); code.Add(Count); foreach (GradientStop item in this) code.Add(item.GetHashCode()); return code.ToHashCode(); } } }