use super::{Color, Extend};
use kurbo::Point;
use smallvec::SmallVec;
use core::{
cmp,
hash::{Hash, Hasher},
};
#[derive(Copy, Clone, PartialOrd, Default, Debug)]
pub struct ColorStop {
pub offset: f32,
pub color: Color,
}
impl Hash for ColorStop {
fn hash<H: Hasher>(&self, state: &mut H) {
self.offset.to_bits().hash(state);
self.color.hash(state);
}
}
impl cmp::PartialEq for ColorStop {
fn eq(&self, other: &Self) -> bool {
self.offset.to_bits() == other.offset.to_bits() && self.color == other.color
}
}
impl cmp::Eq for ColorStop {}
impl ColorStop {
pub fn with_alpha_factor(self, alpha: f32) -> Self {
Self {
offset: self.offset,
color: self.color.with_alpha_factor(alpha),
}
}
}
impl From<(f32, Color)> for ColorStop {
fn from(pair: (f32, Color)) -> Self {
Self {
offset: pair.0,
color: pair.1,
}
}
}
pub type ColorStops = SmallVec<[ColorStop; 4]>;
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum GradientKind {
Linear {
start: Point,
end: Point,
},
Radial {
start_center: Point,
start_radius: f32,
end_center: Point,
end_radius: f32,
},
Sweep {
center: Point,
start_angle: f32,
end_angle: f32,
},
}
#[derive(Clone, PartialEq, Debug)]
pub struct Gradient {
pub kind: GradientKind,
pub extend: Extend,
pub stops: ColorStops,
}
impl Default for Gradient {
fn default() -> Self {
Self {
kind: GradientKind::Linear {
start: Point::default(),
end: Point::default(),
},
extend: Default::default(),
stops: Default::default(),
}
}
}
impl Gradient {
pub fn new_linear(start: impl Into<Point>, end: impl Into<Point>) -> Self {
Self {
kind: GradientKind::Linear {
start: start.into(),
end: end.into(),
},
extend: Default::default(),
stops: Default::default(),
}
}
pub fn new_radial(center: impl Into<Point>, radius: f32) -> Self {
let center = center.into();
Self {
kind: GradientKind::Radial {
start_center: center,
start_radius: 0.0,
end_center: center,
end_radius: radius,
},
extend: Default::default(),
stops: Default::default(),
}
}
pub fn new_two_point_radial(
start_center: impl Into<Point>,
start_radius: f32,
end_center: impl Into<Point>,
end_radius: f32,
) -> Self {
Self {
kind: GradientKind::Radial {
start_center: start_center.into(),
start_radius,
end_center: end_center.into(),
end_radius,
},
extend: Default::default(),
stops: Default::default(),
}
}
pub fn new_sweep(center: impl Into<Point>, start_angle: f32, end_angle: f32) -> Self {
Self {
kind: GradientKind::Sweep {
center: center.into(),
start_angle,
end_angle,
},
extend: Default::default(),
stops: Default::default(),
}
}
pub fn with_extend(mut self, mode: Extend) -> Self {
self.extend = mode;
self
}
pub fn with_stops(mut self, stops: impl ColorStopsSource) -> Self {
self.stops.clear();
stops.collect_stops(&mut self.stops);
self
}
}
pub trait ColorStopsSource {
fn collect_stops(&self, vec: &mut SmallVec<[ColorStop; 4]>);
}
impl<T> ColorStopsSource for &'_ [T]
where
T: Into<ColorStop> + Copy,
{
fn collect_stops(&self, vec: &mut SmallVec<[ColorStop; 4]>) {
for &stop in *self {
vec.push(stop.into());
}
}
}
impl<T, const N: usize> ColorStopsSource for [T; N]
where
T: Into<ColorStop> + Copy,
{
fn collect_stops(&self, vec: &mut SmallVec<[ColorStop; 4]>) {
for stop in *self {
vec.push(stop.into());
}
}
}
impl ColorStopsSource for &'_ [Color] {
fn collect_stops(&self, vec: &mut SmallVec<[ColorStop; 4]>) {
if !self.is_empty() {
let denom = (self.len() - 1).max(1) as f32;
vec.extend(self.iter().enumerate().map(|(i, c)| ColorStop {
offset: (i as f32) / denom,
color: *c,
}));
}
}
}
impl<const N: usize> ColorStopsSource for [Color; N] {
fn collect_stops(&self, vec: &mut SmallVec<[ColorStop; 4]>) {
(&self[..]).collect_stops(vec);
}
}