From 8f53f138d68c5e04cfa34ac236c30ed70709f4ca Mon Sep 17 00:00:00 2001 From: banocean <47253870+banocean@users.noreply.github.com> Date: Fri, 22 Dec 2023 13:32:09 +0100 Subject: [PATCH 01/13] add feature and new dependency to Cargo.toml --- Cargo.toml | 3 +++ src/style/colors.rs | 0 2 files changed, 3 insertions(+) create mode 100644 src/style/colors.rs diff --git a/Cargo.toml b/Cargo.toml index f4e2bbe..411f3c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,8 +16,10 @@ serde = { version = "1.0", features = ["derive"], optional = true } serde_json = { version = "1.0", optional = true } fastsnbt = { git = "https://github.com/owengage/fastnbt", branch = "dev/snbt" } uuid = "1.3.2" +palette = { version = "0.7.3", optional = true } mc_chat_proc = { path = "./mc_chat_proc", optional = true } +clap = { version = "4.4.11", features = ["derive"] } [dev-dependencies] serde_test = "1.0" @@ -26,3 +28,4 @@ serde_test = "1.0" default = [ "serde" ] serde = [ "dep:serde", "serde_json", "uuid/serde"] macros = [ "mc_chat_proc" ] +pallette = [ "palette" ] \ No newline at end of file diff --git a/src/style/colors.rs b/src/style/colors.rs new file mode 100644 index 0000000..e69de29 From 365a82dd871f913a3b8cc9fe9ed11f5cb3fc3d70 Mon Sep 17 00:00:00 2001 From: banocean <47253870+banocean@users.noreply.github.com> Date: Fri, 22 Dec 2023 13:36:21 +0100 Subject: [PATCH 02/13] move color data to separate file --- src/style.rs | 37 +++---------------------------------- src/style/colors.rs | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 34 deletions(-) diff --git a/src/style.rs b/src/style.rs index afcf549..eec64c0 100644 --- a/src/style.rs +++ b/src/style.rs @@ -6,6 +6,9 @@ use serde::{Deserialize, Serialize}; use uuid::Uuid; #[cfg(feature = "serde")] pub(crate) mod serde_support; +mod colors; + +pub use colors::*; /// The style of a [`Chat`] component. /// @@ -118,40 +121,6 @@ impl Style { } } -/// The different colors a [`Chat`] component can have. -/// -/// ## TODO: Automatically find nearest value when serializing [`TextColor::Custom`] for older versions -/// --> feature PR -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub enum TextColor { - Black, - DarkBlue, - DarkGreen, - DarkCyan, - DarkRed, - Purple, - Gold, - Gray, - DarkGray, - Blue, - Green, - Cyan, - Red, - Pink, - Yellow, - White, - /// This field is ignored for versions older than 1.16. - /// - /// See [`TextColor::custom()`]. - Custom(FrozenStr), - Reset, -} - -impl TextColor { - pub fn custom>(color: T) -> TextColor { - TextColor::Custom(color.into()) - } -} /// A ClickEvent useful in a chat message or book. /// TODO: Discuss feature gated `open_file` option diff --git a/src/style/colors.rs b/src/style/colors.rs index e69de29..b1bd7a5 100644 --- a/src/style/colors.rs +++ b/src/style/colors.rs @@ -0,0 +1,38 @@ + + +/// The different colors a [`Chat`] component can have. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum TextColor { + Black, + DarkBlue, + DarkGreen, + DarkCyan, + DarkRed, + Purple, + Gold, + Gray, + DarkGray, + Blue, + Green, + Cyan, + Red, + Pink, + Yellow, + White, + /// This field is ignored for versions older than 1.16. + /// + /// See [`TextColor::custom()`]. + #[cfg(not(feature = "palette"))] + Custom(FrozenStr), + /// This field is ignored for versions older than 1.16. + #[cfg(feature = "palette")] + Custom(Rgb), + Reset, +} + +#[cfg(not(feature = "palette"))] +impl TextColor { + pub fn custom>(color: T) -> TextColor { + TextColor::Custom(color.into()) + } +} \ No newline at end of file From 79eeae43c68397a7855aa8c1526512831e2966f8 Mon Sep 17 00:00:00 2001 From: banocean <47253870+banocean@users.noreply.github.com> Date: Fri, 22 Dec 2023 13:43:56 +0100 Subject: [PATCH 03/13] warpper around Rgb --- src/style/colors.rs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/style/colors.rs b/src/style/colors.rs index b1bd7a5..839690c 100644 --- a/src/style/colors.rs +++ b/src/style/colors.rs @@ -1,4 +1,42 @@ +#[cfg(not(feature = "palette"))] +use crate::freeze::FrozenStr; + +#[cfg(feature = "palette")] +mod rgb { + use std::fmt::Display; + use std::hash::{Hash, Hasher}; + use std::str::FromStr; + use palette::rgb::FromHexError; + use palette::Srgb; + + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub struct Rgb(pub palette::rgb::Rgb); + + impl Hash for Rgb { + fn hash(&self, state: &mut H) { + state.write_u8(self.0.red); + state.write_u8(self.0.green); + state.write_u8(self.0.blue); + } + } + + impl FromStr for Rgb { + type Err = FromHexError; + + fn from_str(s: &str) -> Result { + Ok(Self(s.parse()?)) + } + } + + impl Display for Rgb { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", format!("#{:02x}{:02x}{:02x}", self.0.red, self.0.green, self.0.blue)) + } + } +} +#[cfg(feature = "palette")] +pub use self::rgb::*; /// The different colors a [`Chat`] component can have. #[derive(Clone, Debug, PartialEq, Eq, Hash)] From bd464709c2e5db0e7764d252ae53115c40f92469 Mon Sep 17 00:00:00 2001 From: banocean <47253870+banocean@users.noreply.github.com> Date: Fri, 22 Dec 2023 13:44:14 +0100 Subject: [PATCH 04/13] TextColor::to_string --- src/style/colors.rs | 30 ++++++++++++++++++++++++++++++ src/style/serde_support.rs | 33 ++++++++++----------------------- 2 files changed, 40 insertions(+), 23 deletions(-) diff --git a/src/style/colors.rs b/src/style/colors.rs index 839690c..00972ae 100644 --- a/src/style/colors.rs +++ b/src/style/colors.rs @@ -73,4 +73,34 @@ impl TextColor { pub fn custom>(color: T) -> TextColor { TextColor::Custom(color.into()) } +} + +impl ToString for TextColor { + fn to_string(&self) -> String { + String::from(match self { + TextColor::Black => "black", + TextColor::DarkBlue => "dark_blue", + TextColor::DarkGreen => "dark_green", + TextColor::DarkCyan => "dark_aqua", + TextColor::DarkRed => "dark_red", + TextColor::Purple => "dark_purple", + TextColor::Gold => "gold", + TextColor::Gray => "gray", + TextColor::DarkGray => "dark_gray", + TextColor::Blue => "blue", + TextColor::Green => "green", + TextColor::Cyan => "aqua", + TextColor::Red => "red", + TextColor::Pink => "light_purple", + TextColor::Yellow => "yellow", + TextColor::White => "white", + TextColor::Custom(color) => { + #[cfg(feature = "palette")] + return format!("{color}"); + #[cfg(not(feature = "palette"))] + color + }, + TextColor::Reset => "reset", + }) + } } \ No newline at end of file diff --git a/src/style/serde_support.rs b/src/style/serde_support.rs index 3f45a5e..9b34112 100644 --- a/src/style/serde_support.rs +++ b/src/style/serde_support.rs @@ -11,33 +11,15 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::Value; use uuid::Uuid; -use crate::style::{ClickEvent, HoverEvent, Style, TextColor}; +use crate::style::{ClickEvent, HoverEvent, Style}; +use crate::style::colors::TextColor; impl Serialize for TextColor { fn serialize(&self, serializer: S) -> Result where S: Serializer, { - serializer.serialize_str(match self { - TextColor::Black => "black", - TextColor::DarkBlue => "dark_blue", - TextColor::DarkGreen => "dark_green", - TextColor::DarkCyan => "dark_aqua", - TextColor::DarkRed => "dark_red", - TextColor::Purple => "dark_purple", - TextColor::Gold => "gold", - TextColor::Gray => "gray", - TextColor::DarkGray => "dark_gray", - TextColor::Blue => "blue", - TextColor::Green => "green", - TextColor::Cyan => "aqua", - TextColor::Red => "red", - TextColor::Pink => "light_purple", - TextColor::Yellow => "yellow", - TextColor::White => "white", - TextColor::Custom(color) => color, - TextColor::Reset => "reset", - }) + serializer.serialize_str(&*self.to_string()) } } @@ -71,16 +53,21 @@ impl<'de> Deserialize<'de> for TextColor { Unexpected::Str(custom), &"a 6 digit hex color prefixed by '#'", ); + + #[cfg(not(feature = "palette"))] if custom.len() != 7 || !custom.starts_with('#') { return Err(error); } else { - for c in custom.chars() { - if !"0123456789abcdefABCDEF".contains(c) { + for c in custom.chars().skip(1) { + if c.is_ascii_hexdigit() { return Err(error); } } TextColor::custom(input) } + + #[cfg(feature = "palette")] + TextColor::Custom(custom.parse().map_err(|_| error)?) } }) } From fce9dc12a6ac6dd9f663f8030ffbb8972490aa30 Mon Sep 17 00:00:00 2001 From: banocean <47253870+banocean@users.noreply.github.com> Date: Fri, 22 Dec 2023 13:51:45 +0100 Subject: [PATCH 05/13] refactor: implement `Display` instead of `ToString` --- src/style/colors.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/style/colors.rs b/src/style/colors.rs index 00972ae..4fa5b0e 100644 --- a/src/style/colors.rs +++ b/src/style/colors.rs @@ -1,3 +1,4 @@ +use std::fmt::Display; #[cfg(not(feature = "palette"))] use crate::freeze::FrozenStr; @@ -75,9 +76,9 @@ impl TextColor { } } -impl ToString for TextColor { - fn to_string(&self) -> String { - String::from(match self { +impl Display for TextColor { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let str = String::from(match self { TextColor::Black => "black", TextColor::DarkBlue => "dark_blue", TextColor::DarkGreen => "dark_green", @@ -95,12 +96,12 @@ impl ToString for TextColor { TextColor::Yellow => "yellow", TextColor::White => "white", TextColor::Custom(color) => { - #[cfg(feature = "palette")] - return format!("{color}"); + return write!(f, "{}", format!("{color}")); #[cfg(not(feature = "palette"))] color }, TextColor::Reset => "reset", - }) + }); + write!(f, "{}", str) } } \ No newline at end of file From a8744ba5f20319628789bcf61f48ddf7a34b18a4 Mon Sep 17 00:00:00 2001 From: banocean <47253870+banocean@users.noreply.github.com> Date: Fri, 22 Dec 2023 14:07:19 +0100 Subject: [PATCH 06/13] `TryFrom<&str>` for` TextColor` --- src/style/colors.rs | 44 ++++++++++++++++++++++++++++++++++++ src/style/serde_support.rs | 46 +++++--------------------------------- 2 files changed, 50 insertions(+), 40 deletions(-) diff --git a/src/style/colors.rs b/src/style/colors.rs index 4fa5b0e..f5bb4f5 100644 --- a/src/style/colors.rs +++ b/src/style/colors.rs @@ -1,3 +1,4 @@ +use std::convert::TryFrom; use std::fmt::Display; #[cfg(not(feature = "palette"))] use crate::freeze::FrozenStr; @@ -96,6 +97,7 @@ impl Display for TextColor { TextColor::Yellow => "yellow", TextColor::White => "white", TextColor::Custom(color) => { + #[cfg(feature = "palette")] return write!(f, "{}", format!("{color}")); #[cfg(not(feature = "palette"))] color @@ -104,4 +106,46 @@ impl Display for TextColor { }); write!(f, "{}", str) } +} + +impl TryFrom<&str> for TextColor { + type Error = (); + + fn try_from(value: &str) -> Result { + Ok(match value { + "black" => TextColor::Black, + "dark_blue" => TextColor::DarkBlue, + "dark_green" => TextColor::DarkGreen, + "dark_aqua" => TextColor::DarkCyan, + "dark_red" => TextColor::DarkRed, + "dark_purple" => TextColor::Purple, + "gold" => TextColor::Gold, + "gray" => TextColor::Gray, + "dark_gray" => TextColor::DarkGray, + "blue" => TextColor::Blue, + "green" => TextColor::Green, + "aqua" => TextColor::Cyan, + "red" => TextColor::Red, + "light_purple" => TextColor::Pink, + "yellow" => TextColor::Yellow, + "white" => TextColor::White, + "reset" => TextColor::Reset, + custom => { + #[cfg(not(feature = "palette"))] + if custom.len() != 7 || !custom.starts_with('#') { + return Err(()); + } else { + for c in custom.chars().skip(1) { + if c.is_ascii_hexdigit() { + return Err(()); + } + } + TextColor::custom(FrozenStr::from(custom)) + } + + #[cfg(feature = "palette")] + TextColor::Custom(custom.parse().map_err(|_| ())?) + } + }) + } } \ No newline at end of file diff --git a/src/style/serde_support.rs b/src/style/serde_support.rs index 9b34112..c128d6c 100644 --- a/src/style/serde_support.rs +++ b/src/style/serde_support.rs @@ -30,46 +30,12 @@ impl<'de> Deserialize<'de> for TextColor { D: Deserializer<'de>, { let input = FrozenStr::deserialize(deserializer)?; - Ok(match input.deref() { - "black" => TextColor::Black, - "dark_blue" => TextColor::DarkBlue, - "dark_green" => TextColor::DarkGreen, - "dark_aqua" => TextColor::DarkCyan, - "dark_red" => TextColor::DarkRed, - "dark_purple" => TextColor::Purple, - "gold" => TextColor::Gold, - "gray" => TextColor::Gray, - "dark_gray" => TextColor::DarkGray, - "blue" => TextColor::Blue, - "green" => TextColor::Green, - "aqua" => TextColor::Cyan, - "red" => TextColor::Red, - "light_purple" => TextColor::Pink, - "yellow" => TextColor::Yellow, - "white" => TextColor::White, - "reset" => TextColor::Reset, - custom => { - let error = serde::de::Error::invalid_value( - Unexpected::Str(custom), - &"a 6 digit hex color prefixed by '#'", - ); - - #[cfg(not(feature = "palette"))] - if custom.len() != 7 || !custom.starts_with('#') { - return Err(error); - } else { - for c in custom.chars().skip(1) { - if c.is_ascii_hexdigit() { - return Err(error); - } - } - TextColor::custom(input) - } - - #[cfg(feature = "palette")] - TextColor::Custom(custom.parse().map_err(|_| error)?) - } - }) + TextColor::try_from(input.deref()).map_err(|_| + serde::de::Error::invalid_value( + Unexpected::Str(&*input), + &"a 5 digit hex color prefixed by '#'", + ) + ) } } From f7d5aeb71a2880c4596c5c38eedac02676b188ff Mon Sep 17 00:00:00 2001 From: banocean <47253870+banocean@users.noreply.github.com> Date: Fri, 22 Dec 2023 16:15:21 +0100 Subject: [PATCH 07/13] implement conversions using difference --- src/style/colors.rs | 85 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/src/style/colors.rs b/src/style/colors.rs index f5bb4f5..79cf1ce 100644 --- a/src/style/colors.rs +++ b/src/style/colors.rs @@ -12,7 +12,7 @@ mod rgb { use palette::Srgb; #[derive(Debug, Clone, Copy, PartialEq, Eq)] - pub struct Rgb(pub palette::rgb::Rgb); + pub struct Rgb(pub Srgb); impl Hash for Rgb { fn hash(&self, state: &mut H) { @@ -30,6 +30,12 @@ mod rgb { } } + impl From<(u8, u8, u8)> for Rgb { + fn from(value: (u8, u8, u8)) -> Self { + Self(palette::rgb::Rgb::from([value.0, value.1, value.2])) + } + } + impl Display for Rgb { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", format!("#{:02x}{:02x}{:02x}", self.0.red, self.0.green, self.0.blue)) @@ -148,4 +154,81 @@ impl TryFrom<&str> for TextColor { } }) } +} + +#[cfg(feature = "palette")] +mod custom_colors_to_legacy { + //use palette::color_difference::EuclideanDistance; + use palette::{IntoColor, Lab}; + use palette::white_point::Any; + use crate::{Rgb, TextColor}; + use palette::color_difference::{Ciede2000, EuclideanDistance}; + + pub const RGB_COLORS: [(TextColor, (u8, u8, u8)); 16] = [ + (TextColor::Black, (0, 0, 0)), + (TextColor::DarkBlue, (0, 0, 170)), + (TextColor::DarkGreen, (0, 170, 0)), + (TextColor::DarkCyan, (0, 170, 170)), + (TextColor::DarkRed, (170, 0, 0)), + (TextColor::Purple, (170, 0, 170)), + (TextColor::Gold, (255, 170, 0)), + (TextColor::Gray, (170, 170, 170)), + (TextColor::DarkGray, (85, 85, 85)), + (TextColor::Blue, (85, 85, 255)), + (TextColor::Green, (85, 255, 85)), + (TextColor::Cyan, (85, 255, 255)), + (TextColor::Red, (255, 85, 85)), + (TextColor::Pink, (255, 85, 255)), + (TextColor::Yellow, (255, 255, 85)), + (TextColor::White, (255, 255, 255)) + ]; + + type ColorCompereFn = fn(Rgb, Rgb) -> T; + + impl TextColor { + + fn into_legacy(self, delta_fn: ColorCompereFn) -> Self { + match self { + TextColor::Custom(data) => { + let mut min: Option<(TextColor, T)> = None; + for (color, rgb) in RGB_COLORS { + let result = delta_fn(data, Rgb::from(rgb)); + if let Some((_, value)) = &min { + if result < *value { min = Some((color, result)) } + } else { + min = Some((color, result)) + } + } + + match min { + Some((color, _)) => color, + None => unreachable!() + } + } + color => color + } + } + pub fn to_legacy_ciede2000(self) -> Self { + self.into_legacy(|first, second| { + let first: Lab = first.0.into_linear().into_color(); + let second: Lab = second.0.into_linear().into_color(); + + first.difference(second) + }) + } + } +} + +#[cfg(feature = "palette")] +pub use self::custom_colors_to_legacy::*; + +#[cfg(feature = "palette")] +#[cfg(test)] +mod tests { + use crate::{Rgb, TextColor}; + + #[test] + fn test_ciede200_conversion() { + assert_eq!(TextColor::Custom(Rgb::from((0, 0, 0))).to_legacy_ciede2000(), TextColor::Black) + } } \ No newline at end of file From 292d12977404462199283e6f3c390d08b1313a7a Mon Sep 17 00:00:00 2001 From: banocean <47253870+banocean@users.noreply.github.com> Date: Fri, 22 Dec 2023 16:15:21 +0100 Subject: [PATCH 08/13] implement conversions using ciede2000 --- src/style/colors.rs | 85 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/src/style/colors.rs b/src/style/colors.rs index f5bb4f5..79cf1ce 100644 --- a/src/style/colors.rs +++ b/src/style/colors.rs @@ -12,7 +12,7 @@ mod rgb { use palette::Srgb; #[derive(Debug, Clone, Copy, PartialEq, Eq)] - pub struct Rgb(pub palette::rgb::Rgb); + pub struct Rgb(pub Srgb); impl Hash for Rgb { fn hash(&self, state: &mut H) { @@ -30,6 +30,12 @@ mod rgb { } } + impl From<(u8, u8, u8)> for Rgb { + fn from(value: (u8, u8, u8)) -> Self { + Self(palette::rgb::Rgb::from([value.0, value.1, value.2])) + } + } + impl Display for Rgb { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", format!("#{:02x}{:02x}{:02x}", self.0.red, self.0.green, self.0.blue)) @@ -148,4 +154,81 @@ impl TryFrom<&str> for TextColor { } }) } +} + +#[cfg(feature = "palette")] +mod custom_colors_to_legacy { + //use palette::color_difference::EuclideanDistance; + use palette::{IntoColor, Lab}; + use palette::white_point::Any; + use crate::{Rgb, TextColor}; + use palette::color_difference::{Ciede2000, EuclideanDistance}; + + pub const RGB_COLORS: [(TextColor, (u8, u8, u8)); 16] = [ + (TextColor::Black, (0, 0, 0)), + (TextColor::DarkBlue, (0, 0, 170)), + (TextColor::DarkGreen, (0, 170, 0)), + (TextColor::DarkCyan, (0, 170, 170)), + (TextColor::DarkRed, (170, 0, 0)), + (TextColor::Purple, (170, 0, 170)), + (TextColor::Gold, (255, 170, 0)), + (TextColor::Gray, (170, 170, 170)), + (TextColor::DarkGray, (85, 85, 85)), + (TextColor::Blue, (85, 85, 255)), + (TextColor::Green, (85, 255, 85)), + (TextColor::Cyan, (85, 255, 255)), + (TextColor::Red, (255, 85, 85)), + (TextColor::Pink, (255, 85, 255)), + (TextColor::Yellow, (255, 255, 85)), + (TextColor::White, (255, 255, 255)) + ]; + + type ColorCompereFn = fn(Rgb, Rgb) -> T; + + impl TextColor { + + fn into_legacy(self, delta_fn: ColorCompereFn) -> Self { + match self { + TextColor::Custom(data) => { + let mut min: Option<(TextColor, T)> = None; + for (color, rgb) in RGB_COLORS { + let result = delta_fn(data, Rgb::from(rgb)); + if let Some((_, value)) = &min { + if result < *value { min = Some((color, result)) } + } else { + min = Some((color, result)) + } + } + + match min { + Some((color, _)) => color, + None => unreachable!() + } + } + color => color + } + } + pub fn to_legacy_ciede2000(self) -> Self { + self.into_legacy(|first, second| { + let first: Lab = first.0.into_linear().into_color(); + let second: Lab = second.0.into_linear().into_color(); + + first.difference(second) + }) + } + } +} + +#[cfg(feature = "palette")] +pub use self::custom_colors_to_legacy::*; + +#[cfg(feature = "palette")] +#[cfg(test)] +mod tests { + use crate::{Rgb, TextColor}; + + #[test] + fn test_ciede200_conversion() { + assert_eq!(TextColor::Custom(Rgb::from((0, 0, 0))).to_legacy_ciede2000(), TextColor::Black) + } } \ No newline at end of file From 7a8db09b1eef07d988349125268df5deef71e082 Mon Sep 17 00:00:00 2001 From: banocean <47253870+banocean@users.noreply.github.com> Date: Fri, 22 Dec 2023 17:17:10 +0100 Subject: [PATCH 09/13] renaming stuff --- src/style/colors.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/style/colors.rs b/src/style/colors.rs index 79cf1ce..173e0bf 100644 --- a/src/style/colors.rs +++ b/src/style/colors.rs @@ -158,9 +158,7 @@ impl TryFrom<&str> for TextColor { #[cfg(feature = "palette")] mod custom_colors_to_legacy { - //use palette::color_difference::EuclideanDistance; use palette::{IntoColor, Lab}; - use palette::white_point::Any; use crate::{Rgb, TextColor}; use palette::color_difference::{Ciede2000, EuclideanDistance}; @@ -192,11 +190,11 @@ mod custom_colors_to_legacy { TextColor::Custom(data) => { let mut min: Option<(TextColor, T)> = None; for (color, rgb) in RGB_COLORS { - let result = delta_fn(data, Rgb::from(rgb)); + let delta = delta_fn(data, Rgb::from(rgb)); if let Some((_, value)) = &min { - if result < *value { min = Some((color, result)) } + if value > &delta { min = Some((color, delta)) } } else { - min = Some((color, result)) + min = Some((color, delta)) } } From 5b667723314d396d259586e18d133d5acd635754 Mon Sep 17 00:00:00 2001 From: banocean <47253870+banocean@users.noreply.github.com> Date: Fri, 22 Dec 2023 17:17:41 +0100 Subject: [PATCH 10/13] euclidean conversion --- src/style/colors.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/style/colors.rs b/src/style/colors.rs index 173e0bf..6598816 100644 --- a/src/style/colors.rs +++ b/src/style/colors.rs @@ -214,6 +214,14 @@ mod custom_colors_to_legacy { first.difference(second) }) } + pub fn to_legacy_euclidean(self) -> TextColor { + self.into_legacy(|first, second| { + let first: Lab = first.0.into_linear().into_color(); + let second: Lab = second.0.into_linear().into_color(); + + first.distance(second) + }) + } } } From 754117fa62c5bdcaf13cab39ee5fafeaa16a02e1 Mon Sep 17 00:00:00 2001 From: banocean <47253870+banocean@users.noreply.github.com> Date: Fri, 22 Dec 2023 17:17:52 +0100 Subject: [PATCH 11/13] doc tests --- src/style/colors.rs | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/style/colors.rs b/src/style/colors.rs index 6598816..5ed9a0e 100644 --- a/src/style/colors.rs +++ b/src/style/colors.rs @@ -206,6 +206,16 @@ mod custom_colors_to_legacy { color => color } } + + /// Converts [TextColor::Custom] to legacy [TextColor] values using [palette::color_difference::EuclideanDistance] + /// + /// ```rust + /// use mc_chat::{Rgb, TextColor}; + /// assert_eq!( + /// TextColor::Custom(Rgb::from((0, 0, 0))).to_legacy_ciede2000(), + /// TextColor::Black + /// ) + /// ``` pub fn to_legacy_ciede2000(self) -> Self { self.into_legacy(|first, second| { let first: Lab = first.0.into_linear().into_color(); @@ -214,6 +224,16 @@ mod custom_colors_to_legacy { first.difference(second) }) } + + /// Converts [TextColor::Custom] to legacy [TextColor] values using [palette::color_difference::Ciede2000] + /// + /// ```rust + /// use mc_chat::{Rgb, TextColor}; + /// assert_eq!( + /// TextColor::Custom(Rgb::from((255, 255, 255))).to_legacy_euclidean(), + /// TextColor::White + /// ) + /// ``` pub fn to_legacy_euclidean(self) -> TextColor { self.into_legacy(|first, second| { let first: Lab = first.0.into_linear().into_color(); @@ -227,14 +247,3 @@ mod custom_colors_to_legacy { #[cfg(feature = "palette")] pub use self::custom_colors_to_legacy::*; - -#[cfg(feature = "palette")] -#[cfg(test)] -mod tests { - use crate::{Rgb, TextColor}; - - #[test] - fn test_ciede200_conversion() { - assert_eq!(TextColor::Custom(Rgb::from((0, 0, 0))).to_legacy_ciede2000(), TextColor::Black) - } -} \ No newline at end of file From 6ba8981957b206dd7001d0594c5a638ad89fa199 Mon Sep 17 00:00:00 2001 From: banocean <47253870+banocean@users.noreply.github.com> Date: Fri, 22 Dec 2023 17:27:48 +0100 Subject: [PATCH 12/13] docs --- src/style/colors.rs | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/style/colors.rs b/src/style/colors.rs index 5ed9a0e..a665455 100644 --- a/src/style/colors.rs +++ b/src/style/colors.rs @@ -46,32 +46,48 @@ mod rgb { #[cfg(feature = "palette")] pub use self::rgb::*; -/// The different colors a [`Chat`] component can have. +/// The different colors a [`crate::Chat`] component can have. #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum TextColor { + /// RGB = (0, 0, 0) Black, + /// RGB = (0, 0, 170) DarkBlue, + /// RGB = (0, 170, 0) DarkGreen, + /// RGB = (0, 170, 170) DarkCyan, + /// RGB = (170, 0, 0) DarkRed, + /// RGB = (170, 0, 170) Purple, + /// RGB = (255, 170, 0) Gold, + /// RGB = (170, 170, 170) Gray, + /// RGB = (85, 85, 85) DarkGray, + /// RGB = (85, 85, 255) Blue, + /// RGB = (85, 255, 85) Green, + /// RGB = (85, 255, 255) Cyan, + /// RGB = (255, 85, 85) Red, + /// RGB = (255, 85, 255) Pink, + /// RGB = (255, 255, 85) Yellow, + /// RGB = (255, 255, 255 White, /// This field is ignored for versions older than 1.16. /// /// See [`TextColor::custom()`]. #[cfg(not(feature = "palette"))] Custom(FrozenStr), - /// This field is ignored for versions older than 1.16. #[cfg(feature = "palette")] + /// This field is ignored for versions older than 1.16. Custom(Rgb), Reset, } @@ -184,7 +200,6 @@ mod custom_colors_to_legacy { type ColorCompereFn = fn(Rgb, Rgb) -> T; impl TextColor { - fn into_legacy(self, delta_fn: ColorCompereFn) -> Self { match self { TextColor::Custom(data) => { @@ -207,7 +222,7 @@ mod custom_colors_to_legacy { } } - /// Converts [TextColor::Custom] to legacy [TextColor] values using [palette::color_difference::EuclideanDistance] + /// Converts [`TextColor::Custom`] to legacy [`TextColor`] values using [`EuclideanDistance`] /// /// ```rust /// use mc_chat::{Rgb, TextColor}; @@ -225,7 +240,7 @@ mod custom_colors_to_legacy { }) } - /// Converts [TextColor::Custom] to legacy [TextColor] values using [palette::color_difference::Ciede2000] + /// Converts [`TextColor::Custom`] to legacy [`TextColor`] values using [`Ciede2000`] /// /// ```rust /// use mc_chat::{Rgb, TextColor}; From 5809db30a88f4ed2e4cdbddac932be022234b92e Mon Sep 17 00:00:00 2001 From: banocean <47253870+banocean@users.noreply.github.com> Date: Fri, 22 Dec 2023 18:06:16 +0100 Subject: [PATCH 13/13] make code more idiomatic --- src/style/colors.rs | 63 ++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/src/style/colors.rs b/src/style/colors.rs index a665455..31cc279 100644 --- a/src/style/colors.rs +++ b/src/style/colors.rs @@ -48,6 +48,7 @@ pub use self::rgb::*; /// The different colors a [`crate::Chat`] component can have. #[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "palette", derive(Copy))] pub enum TextColor { /// RGB = (0, 0, 0) Black, @@ -174,10 +175,27 @@ impl TryFrom<&str> for TextColor { #[cfg(feature = "palette")] mod custom_colors_to_legacy { + use std::cmp::Ordering; use palette::{IntoColor, Lab}; use crate::{Rgb, TextColor}; use palette::color_difference::{Ciede2000, EuclideanDistance}; + #[derive(Clone, Copy, PartialOrd, PartialEq)] + struct Float32Wrapper(f32); + + impl Eq for Float32Wrapper {} + impl Ord for Float32Wrapper { + fn cmp(&self, other: &Self) -> Ordering { + if self.0 == other.0 { + Ordering::Equal + } else if self.0 > other.0 { + Ordering::Greater + } else { + Ordering::Less + } + } + } + pub const RGB_COLORS: [(TextColor, (u8, u8, u8)); 16] = [ (TextColor::Black, (0, 0, 0)), (TextColor::DarkBlue, (0, 0, 170)), @@ -200,26 +218,19 @@ mod custom_colors_to_legacy { type ColorCompereFn = fn(Rgb, Rgb) -> T; impl TextColor { - fn into_legacy(self, delta_fn: ColorCompereFn) -> Self { - match self { - TextColor::Custom(data) => { - let mut min: Option<(TextColor, T)> = None; - for (color, rgb) in RGB_COLORS { - let delta = delta_fn(data, Rgb::from(rgb)); - if let Some((_, value)) = &min { - if value > &delta { min = Some((color, delta)) } - } else { - min = Some((color, delta)) - } - } - - match min { - Some((color, _)) => color, - None => unreachable!() - } - } - color => color - } + fn into_legacy(self, delta_fn: ColorCompereFn) -> Self where T: Copy, T: Ord { + if let TextColor::Custom(data) = self { + *RGB_COLORS.iter() + .map(|(color, rgb)| { + let delta = delta_fn(data, Rgb::from(*rgb)); + (color, delta) + }) + .min_by_key(|(_, delta)| *delta) + .map_or_else( + || unreachable!(), // impossible as long as RGB_COLORS.len() != 0 + |(color, _)| color + ) + } else { self } } /// Converts [`TextColor::Custom`] to legacy [`TextColor`] values using [`EuclideanDistance`] @@ -227,16 +238,16 @@ mod custom_colors_to_legacy { /// ```rust /// use mc_chat::{Rgb, TextColor}; /// assert_eq!( - /// TextColor::Custom(Rgb::from((0, 0, 0))).to_legacy_ciede2000(), + /// TextColor::Custom(Rgb::from((0, 0, 0))).into_legacy_ciede2000(), /// TextColor::Black /// ) /// ``` - pub fn to_legacy_ciede2000(self) -> Self { + pub fn into_legacy_ciede2000(self) -> Self { self.into_legacy(|first, second| { let first: Lab = first.0.into_linear().into_color(); let second: Lab = second.0.into_linear().into_color(); - first.difference(second) + Float32Wrapper(first.difference(second)) }) } @@ -245,16 +256,16 @@ mod custom_colors_to_legacy { /// ```rust /// use mc_chat::{Rgb, TextColor}; /// assert_eq!( - /// TextColor::Custom(Rgb::from((255, 255, 255))).to_legacy_euclidean(), + /// TextColor::Custom(Rgb::from((255, 255, 255))).into_legacy_euclidean(), /// TextColor::White /// ) /// ``` - pub fn to_legacy_euclidean(self) -> TextColor { + pub fn into_legacy_euclidean(self) -> TextColor { self.into_legacy(|first, second| { let first: Lab = first.0.into_linear().into_color(); let second: Lab = second.0.into_linear().into_color(); - first.distance(second) + Float32Wrapper(first.distance(second)) }) } }