1use crate::{Color, Radians};
3
4use std::cmp::Ordering;
5
6#[derive(Debug, Clone, Copy, PartialEq)]
7pub enum Gradient {
10 Linear(Linear),
12}
13
14impl Gradient {
15 pub fn scale_alpha(self, factor: f32) -> Self {
17 match self {
18 Gradient::Linear(linear) => Gradient::Linear(linear.scale_alpha(factor)),
19 }
20 }
21}
22
23impl From<Linear> for Gradient {
24 fn from(gradient: Linear) -> Self {
25 Self::Linear(gradient)
26 }
27}
28
29#[derive(Debug, Default, Clone, Copy, PartialEq)]
30pub struct ColorStop {
34 pub offset: f32,
36
37 pub color: Color,
41}
42
43#[derive(Debug, Clone, Copy, PartialEq)]
45pub struct Linear {
46 pub angle: Radians,
48 pub stops: [Option<ColorStop>; 8],
50}
51
52impl Linear {
53 pub fn new(angle: impl Into<Radians>) -> Self {
55 Self {
56 angle: angle.into(),
57 stops: [None; 8],
58 }
59 }
60
61 pub fn add_stop(mut self, offset: f32, color: Color) -> Self {
67 if offset.is_finite() && (0.0..=1.0).contains(&offset) {
68 let (Ok(index) | Err(index)) = self.stops.binary_search_by(|stop| match stop {
69 None => Ordering::Greater,
70 Some(stop) => stop.offset.partial_cmp(&offset).unwrap(),
71 });
72
73 if index < 8 {
74 self.stops[index] = Some(ColorStop { offset, color });
75 }
76 } else {
77 log::warn!("Gradient color stop must be within 0.0..=1.0 range.");
78 };
79
80 self
81 }
82
83 pub fn add_stops(mut self, stops: impl IntoIterator<Item = ColorStop>) -> Self {
87 for stop in stops {
88 self = self.add_stop(stop.offset, stop.color);
89 }
90
91 self
92 }
93
94 pub fn scale_alpha(mut self, factor: f32) -> Self {
97 for stop in self.stops.iter_mut().flatten() {
98 stop.color.a *= factor;
99 }
100
101 self
102 }
103}