use crate::{modulo, norm};
use crate::{Color, InverseGradient, SharpGradient};
use alloc::boxed::Box;
use alloc::vec;
use alloc::vec::Vec;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum BlendMode {
Rgb,
LinearRgb,
Oklab,
#[cfg(feature = "lab")]
Lab,
}
pub trait Gradient: CloneGradient {
fn at(&self, t: f32) -> Color;
fn repeat_at(&self, t: f32) -> Color {
let (dmin, dmax) = self.domain();
let t = norm(t, dmin, dmax);
self.at(dmin + modulo(t, 1.0) * (dmax - dmin))
}
fn reflect_at(&self, t: f32) -> Color {
let (dmin, dmax) = self.domain();
let t = norm(t, dmin, dmax);
self.at(dmin + (modulo(1.0 + t, 2.0) - 1.0).abs() * (dmax - dmin))
}
fn domain(&self) -> (f32, f32) {
(0.0, 1.0)
}
fn colors(&self, n: usize) -> Vec<Color> {
let (dmin, dmax) = self.domain();
if n == 1 {
return vec![self.at(dmin)];
}
(0..n)
.map(|i| self.at(dmin + (i as f32 * (dmax - dmin)) / (n - 1) as f32))
.collect()
}
fn colors_iter(&self, n: usize) -> GradientColors<'_>
where
Self: Sized,
{
GradientColors::new(self, n)
}
#[cfg_attr(
feature = "preset",
doc = r##"
Get new hard-edge gradient
```
let g = colorgrad::preset::rainbow();
```

```
use colorgrad::Gradient;
let g = colorgrad::preset::rainbow().sharp(11, 0.0);
```

"##
)]
fn sharp(&self, segment: u16, smoothness: f32) -> SharpGradient {
let colors = if segment > 1 {
self.colors(segment.into())
} else {
vec![self.at(self.domain().0), self.at(self.domain().0)]
};
SharpGradient::new(&colors, self.domain(), smoothness)
}
#[cfg_attr(
feature = "preset",
doc = r##"
Convert gradient to boxed trait object
This is a convenience function, which is useful when you want to store gradients with
different types in a collection, or when you want to return a gradient from a function but
the type is not known at compile time.
## Examples
```
# fn main() -> Result<(), Box<dyn std::error::Error>> {
# let is_rainbow = true;
# use colorgrad::{BlendMode, LinearGradient, GradientBuilder};
use colorgrad::Gradient;
let g = if is_rainbow {
colorgrad::preset::rainbow().boxed()
} else {
colorgrad::preset::sinebow().boxed()
};
// Vector of different gradient types
let g2: LinearGradient = GradientBuilder::new()
.css("#a52a2a, 35%, #ffd700")
.mode(BlendMode::Oklab)
.build()?;
let gradients = vec![
g2.sharp(7, 0.0).boxed(),
g2.boxed(),
colorgrad::preset::magma().boxed(),
colorgrad::preset::turbo().boxed(),
];
# Ok(())
# }
```
"##
)]
fn boxed<'a>(self) -> Box<dyn Gradient + 'a>
where
Self: Sized + 'a,
{
Box::new(self)
}
fn inverse<'a>(&self) -> InverseGradient<'_>
where
Self: 'a,
{
InverseGradient::new(self.clone_boxed())
}
}
pub trait CloneGradient {
fn clone_boxed<'s>(&self) -> Box<dyn Gradient + 's>
where
Self: 's;
}
impl<T> CloneGradient for T
where
T: Gradient + Clone,
{
fn clone_boxed<'s>(&self) -> Box<dyn Gradient + 's>
where
Self: 's,
{
Box::new(self.clone())
}
}
impl Clone for Box<dyn Gradient + '_> {
fn clone(&self) -> Self {
(**self).clone_boxed()
}
}
impl Gradient for Box<dyn Gradient + '_> {
fn at(&self, t: f32) -> Color {
(**self).at(t)
}
fn repeat_at(&self, t: f32) -> Color {
(**self).repeat_at(t)
}
fn reflect_at(&self, t: f32) -> Color {
(**self).reflect_at(t)
}
fn domain(&self) -> (f32, f32) {
(**self).domain()
}
fn colors(&self, n: usize) -> Vec<Color> {
(**self).colors(n)
}
fn sharp(&self, segment: u16, smoothness: f32) -> SharpGradient {
(**self).sharp(segment, smoothness)
}
fn boxed<'a>(self) -> Box<dyn Gradient + 'a>
where
Self: 'a,
{
Box::new(self)
}
}
#[cfg_attr(
feature = "preset",
doc = r##"
Iterator for evenly spaced colors across gradient
## Examples
```
use colorgrad::Gradient;
let gradient = colorgrad::preset::magma();
for color in gradient.colors_iter(15) {
println!("{:?}", color.to_rgba8());
}
// reverse order
for color in gradient.colors_iter(15).rev() {
println!("{:?}", color.to_rgba8());
}
```
"##
)]
pub struct GradientColors<'a> {
gradient: &'a dyn Gradient,
a_idx: usize,
b_idx: usize,
max: f32,
}
impl<'a> GradientColors<'a> {
pub fn new(gradient: &'a dyn Gradient, total: usize) -> Self {
Self {
gradient,
a_idx: 0,
b_idx: total,
max: if total == 0 { 0.0 } else { (total - 1) as f32 },
}
}
}
impl Iterator for GradientColors<'_> {
type Item = Color;
fn next(&mut self) -> Option<Self::Item> {
if self.a_idx == self.b_idx {
return None;
}
let (dmin, dmax) = self.gradient.domain();
let t = dmin + (self.a_idx as f32 * (dmax - dmin)) / self.max;
self.a_idx += 1;
Some(self.gradient.at(t))
}
}
impl DoubleEndedIterator for GradientColors<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
if self.a_idx == self.b_idx {
return None;
}
let (dmin, dmax) = self.gradient.domain();
self.b_idx -= 1;
let t = dmin + (self.b_idx as f32 * (dmax - dmin)) / self.max;
Some(self.gradient.at(t))
}
}
impl ExactSizeIterator for GradientColors<'_> {
fn len(&self) -> usize {
self.b_idx - self.a_idx
}
}