1//! Colors that transition progressively.
2use crate::{Color, Radians};
34use std::cmp::Ordering;
56#[derive(Debug, Clone, Copy, PartialEq)]
7/// A fill which transitions colors progressively along a direction, either linearly, radially (TBD),
8/// or conically (TBD).
9pub enum Gradient {
10/// A linear gradient interpolates colors along a direction at a specific angle.
11Linear(Linear),
12}
1314impl Gradient {
15/// Scales the alpha channel of the [`Gradient`] by the given factor.
16pub fn scale_alpha(self, factor: f32) -> Self {
17match self {
18 Gradient::Linear(linear) => {
19 Gradient::Linear(linear.scale_alpha(factor))
20 }
21 }
22 }
23}
2425impl From<Linear> for Gradient {
26fn from(gradient: Linear) -> Self {
27Self::Linear(gradient)
28 }
29}
3031#[derive(Debug, Default, Clone, Copy, PartialEq)]
32/// A point along the gradient vector where the specified [`color`] is unmixed.
33///
34/// [`color`]: Self::color
35pub struct ColorStop {
36/// Offset along the gradient vector.
37pub offset: f32,
3839/// The color of the gradient at the specified [`offset`].
40 ///
41 /// [`offset`]: Self::offset
42pub color: Color,
43}
4445/// A linear gradient.
46#[derive(Debug, Clone, Copy, PartialEq)]
47pub struct Linear {
48/// How the [`Gradient`] is angled within its bounds.
49pub angle: Radians,
50/// [`ColorStop`]s along the linear gradient path.
51pub stops: [Option<ColorStop>; 8],
52}
5354impl Linear {
55/// Creates a new [`Linear`] gradient with the given angle in [`Radians`].
56pub fn new(angle: impl Into<Radians>) -> Self {
57Self {
58 angle: angle.into(),
59 stops: [None; 8],
60 }
61 }
6263/// Adds a new [`ColorStop`], defined by an offset and a color, to the gradient.
64 ///
65 /// Any `offset` that is not within `0.0..=1.0` will be silently ignored.
66 ///
67 /// Any stop added after the 8th will be silently ignored.
68pub fn add_stop(mut self, offset: f32, color: Color) -> Self {
69if offset.is_finite() && (0.0..=1.0).contains(&offset) {
70let (Ok(index) | Err(index)) =
71self.stops.binary_search_by(|stop| match stop {
72None => Ordering::Greater,
73Some(stop) => stop.offset.partial_cmp(&offset).unwrap(),
74 });
7576if index < 8 {
77self.stops[index] = Some(ColorStop { offset, color });
78 }
79 } else {
80log::warn!("Gradient color stop must be within 0.0..=1.0 range.");
81 };
8283self
84}
8586/// Adds multiple [`ColorStop`]s to the gradient.
87 ///
88 /// Any stop added after the 8th will be silently ignored.
89pub fn add_stops(
90mut self,
91 stops: impl IntoIterator<Item = ColorStop>,
92 ) -> Self {
93for stop in stops {
94self = self.add_stop(stop.offset, stop.color);
95 }
9697self
98}
99100/// Scales the alpha channel of the [`Linear`] gradient by the given
101 /// factor.
102pub fn scale_alpha(mut self, factor: f32) -> Self {
103for stop in self.stops.iter_mut().flatten() {
104 stop.color.a *= factor;
105 }
106107self
108}
109}