From db8e8eaebbc1292ae07447eff4958edbd88cd78a Mon Sep 17 00:00:00 2001 From: ConnorSkees <39542938+ConnorSkees@users.noreply.github.com> Date: Fri, 14 Feb 2020 08:43:10 -0500 Subject: [PATCH] Implement builtin function `lighten()` --- src/builtin/color.rs | 14 +++++++++++- src/color/mod.rs | 51 ++++++++++++++++++++++++++++++++++++++++---- tests/color.rs | 17 +++++++++++++++ 3 files changed, 77 insertions(+), 5 deletions(-) diff --git a/src/builtin/color.rs b/src/builtin/color.rs index bfbcf94..728f686 100644 --- a/src/builtin/color.rs +++ b/src/builtin/color.rs @@ -175,8 +175,20 @@ pub(crate) fn register(f: &mut BTreeMap) { Value::Dimension(n, Unit::None) | Value::Dimension(n, Unit::Percent) | Value::Dimension(n, Unit::Deg) => n, - _ => todo!("expected either unitless or % number for alpha"), + _ => todo!("expected either unitless or % number for degrees"), }; Some(Value::Color(color.adjust_hue(degrees))) }); + decl!(f "lighten", |args, _| { + let color = match arg!(args, 0, "color").eval() { + Value::Color(c) => c, + _ => todo!("non-color given to builtin function `lighten()`") + }; + let amount = match arg!(args, 1, "amount").eval() { + Value::Dimension(n, Unit::None) => n, + Value::Dimension(n, Unit::Percent) => n / Number::from(100), + _ => todo!("expected either unitless or % number for amount"), + }; + Some(Value::Color(color.lighten(amount))) + }); } diff --git a/src/color/mod.rs b/src/color/mod.rs index b7c94f8..e247aa5 100644 --- a/src/color/mod.rs +++ b/src/color/mod.rs @@ -102,11 +102,54 @@ impl Color { (((min + max) / Number::from(2)) * Number::from(100)).round() } + pub fn as_hsla(&self) -> (Number, Number, Number, Number) { + let red = self.red.clone() / Number::from(255); + let green = self.green.clone() / Number::from(255); + let blue = self.blue.clone() / Number::from(255); + let min = red.clone().min(green.clone().min(blue.clone())); + let max = red.clone().max(green.clone().max(blue.clone())); + + let lightness = (min.clone() + max.clone()) / Number::from(2); + + let saturation = if &min == &max { + Number::from(0) + } else { + let d = max.clone() - min.clone(); + let mm = max.clone() + min.clone(); + d / if mm > Number::from(1) { + Number::from(2) - mm + } else { + mm + } + }; + + let mut hue = if min == max { + Number::from(0) + } else if red == max { + (green - blue) / (max - min) + } else if green == max { + Number::from(2) + (blue - red) / (max - min) + } else { + Number::from(4) + (red - green) / (max - min) + }; + + if hue < Number::from(0) { + hue += Number::from(360); + } + + hue *= Number::from(60); + + (hue, saturation, lightness, self.alpha()) + } + pub fn adjust_hue(&self, degrees: Number) -> Self { - let hue = self.hue(); - let saturation = Number::ratio(self.saturation(), 100); - let luminance = Number::ratio(self.lightness(), 100); - Color::from_hsla(hue + degrees, saturation, luminance, self.alpha()) + let (hue, saturation, luminance, alpha) = self.as_hsla(); + Color::from_hsla(hue + degrees, saturation, luminance, alpha) + } + + pub fn lighten(&self, amount: Number) -> Self { + let (hue, saturation, luminance, alpha) = self.as_hsla(); + Color::from_hsla(hue, saturation, luminance + amount, alpha) } pub fn alpha(&self) -> Number { diff --git a/tests/color.rs b/tests/color.rs index a969061..26f1cb4 100644 --- a/tests/color.rs +++ b/tests/color.rs @@ -217,3 +217,20 @@ test!( // "a {\n color: adjust-hue($color: hsl(120, 30%, 90%), $degrees: 60deg);\n}\n", // "a {\n color: #deeded;\n}\n" // ); +// test!( +// lighten_named_args, +// "a {\n color: lighten($color: hsl(0, 0%, 0%), $amount: 30%);\n}\n", +// "a {\n color: #deeded;\n}\n" +// ); +test!( + lighten_basic, + "a {\n color: lighten(hsl(0, 0%, 0%), 30%);\n}\n", + "a {\n color: #4d4d4d;\n}\n" +); +test!( + lighten_3_hex, + "a {\n color: lighten(#800, 20%);\n}\n", + // eventually, this should become `#e00` + // blocked on recognizing when to use 3-hex over 6-hex + "a {\n color: #ee0000;\n}\n" +);