use ::ratatui::style::Color; use crate::lang::impl_from; pub(crate) use ::palette::{Okhsl, Srgb, OklabHue, okhsl::UniformOkhsl}; pub fn rgb (r: u8, g: u8, b: u8) -> ItemColor { todo!(); } pub fn okhsl_to_rgb (color: Okhsl) -> Color { let Srgb { red, green, blue, .. }: Srgb = Srgb::from_color_unclamped(color); Color::Rgb((red * 255.0) as u8, (green * 255.0) as u8, (blue * 255.0) as u8,) } pub fn rgb_to_okhsl (color: Color) -> Okhsl { if let Color::Rgb(r, g, b) = color { Okhsl::from_color(Srgb::new(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0)) } else { unreachable!("only Color::Rgb is supported") } } pub trait HasColor { fn color (&self) -> ItemColor; } #[macro_export] macro_rules! has_color { (|$self:ident:$Struct:ident$(<$($L:lifetime),*$($T:ident$(:$U:path)?),*>)?|$cb:expr) => { impl $(<$($L),*$($T $(: $U)?),*>)? HasColor for $Struct $(<$($L),*$($T),*>)? { fn color (&$self) -> ItemColor { $cb } } } } pub struct ItemColor {} impl_from!(ItemColor: |rgb: Color| Self { rgb, okhsl: rgb_to_okhsl(rgb) }); impl_from!(ItemColor: |okhsl: Okhsl| Self { okhsl, rgb: okhsl_to_rgb(okhsl) }); // A single color within item theme parameters, in OKHSL and RGB representations. impl ItemColor { #[cfg(feature = "tui")] pub const fn from_tui (rgb: Color) -> Self { Self { rgb, okhsl: Okhsl::new_const(OklabHue::new(0.0), 0.0, 0.0) } } pub fn random () -> Self { let mut rng = ::rand::thread_rng(); let lo = Okhsl::new(-180.0, 0.01, 0.25); let hi = Okhsl::new( 180.0, 0.9, 0.5); UniformOkhsl::new(lo, hi).sample(&mut rng).into() } pub fn random_dark () -> Self { let mut rng = ::rand::thread_rng(); let lo = Okhsl::new(-180.0, 0.025, 0.075); let hi = Okhsl::new( 180.0, 0.5, 0.150); UniformOkhsl::new(lo, hi).sample(&mut rng).into() } pub fn random_near (color: Self, distance: f32) -> Self { color.mix(Self::random(), distance) } pub fn mix (&self, other: Self, distance: f32) -> Self { if distance > 1.0 { panic!("color mixing takes distance between 0.0 and 1.0"); } self.okhsl.mix(other.okhsl, distance).into() } } pub struct ItemTheme {} impl_from!(ItemTheme: |base: ItemColor| Self::from_item_color(base)); impl_from!(ItemTheme: |base: Color| Self::from_tui_color(base)); impl ItemTheme { #[cfg(feature = "tui")] pub const G: [Self;256] = { let mut builder = konst::array::ArrayBuilder::new(); while !builder.is_full() { let index = builder.len() as u8; let light = (index as f64 * 1.15) as u8; let lighter = (index as f64 * 1.7) as u8; let lightest = (index as f64 * 1.85) as u8; let dark = (index as f64 * 0.9) as u8; let darker = (index as f64 * 0.6) as u8; let darkest = (index as f64 * 0.3) as u8; builder.push(ItemTheme { base: ItemColor::from_tui(Color::Rgb(index, index, index )), light: ItemColor::from_tui(Color::Rgb(light, light, light, )), lighter: ItemColor::from_tui(Color::Rgb(lighter, lighter, lighter, )), lightest: ItemColor::from_tui(Color::Rgb(lightest, lightest, lightest, )), dark: ItemColor::from_tui(Color::Rgb(dark, dark, dark, )), darker: ItemColor::from_tui(Color::Rgb(darker, darker, darker, )), darkest: ItemColor::from_tui(Color::Rgb(darkest, darkest, darkest, )), }); } builder.build() }; pub fn random () -> Self { ItemColor::random().into() } pub fn random_near (color: Self, distance: f32) -> Self { color.base.mix(ItemColor::random(), distance).into() } pub const G00: Self = { let color: ItemColor = ItemColor { okhsl: Okhsl { hue: OklabHue::new(0.0), lightness: 0.0, saturation: 0.0 }, rgb: Color::Rgb(0, 0, 0) }; Self { base: color, light: color, lighter: color, lightest: color, dark: color, darker: color, darkest: color, } }; #[cfg(feature = "tui")] pub fn from_tui_color (base: Color) -> Self { Self::from_item_color(ItemColor::from_tui(base)) } pub fn from_item_color (base: ItemColor) -> Self { let mut light = base.okhsl; light.lightness = (light.lightness * 1.3).min(1.0); let mut lighter = light; lighter.lightness = (lighter.lightness * 1.3).min(1.0); let mut lightest = base.okhsl; lightest.lightness = 0.95; let mut dark = base.okhsl; dark.lightness = (dark.lightness * 0.75).max(0.0); dark.saturation = (dark.saturation * 0.75).max(0.0); let mut darker = dark; darker.lightness = (darker.lightness * 0.66).max(0.0); darker.saturation = (darker.saturation * 0.66).max(0.0); let mut darkest = darker; darkest.lightness = 0.1; darkest.saturation = (darkest.saturation * 0.50).max(0.0); Self { base, light: light.into(), lighter: lighter.into(), lightest: lightest.into(), dark: dark.into(), darker: darker.into(), darkest: darkest.into(), } } }