From a328617001e25c737ac85272a90797f3f206ccee Mon Sep 17 00:00:00 2001 From: ConnorSkees <39542938+ConnorSkees@users.noreply.github.com> Date: Fri, 14 Feb 2020 17:38:10 -0500 Subject: [PATCH] Implement builtin function `change-color()` --- src/builtin/color/other.rs | 76 ++++++++++++++++++++++++++++++++++++-- src/color/mod.rs | 4 +- tests/color.rs | 15 ++++++++ 3 files changed, 90 insertions(+), 5 deletions(-) diff --git a/src/builtin/color/other.rs b/src/builtin/color/other.rs index 74b54dd..1ec8d98 100644 --- a/src/builtin/color/other.rs +++ b/src/builtin/color/other.rs @@ -1,7 +1,77 @@ use std::collections::BTreeMap; use super::Builtin; +use crate::color::Color; +use crate::units::Unit; +use crate::value::{Number, Value}; -pub(crate) fn register(_f: &mut BTreeMap) { - -} \ No newline at end of file +pub(crate) fn register(f: &mut BTreeMap) { + decl!(f "change-color", |args, _| { + let color = match arg!(args, 0, "color").eval() { + Value::Color(c) => c, + _ => todo!("non-color given to builtin function `change-color()`") + }; + + let alpha = match arg!(args, -1, "alpha"=Value::Null).eval() { + Value::Dimension(n, Unit::None) => Some(n), + Value::Dimension(n, Unit::Percent) => Some(n / Number::from(100)), + Value::Null => None, + _ => todo!("expected either unitless or % number for $alpha") + }; + + let red = match arg!(args, -1, "red"=Value::Null).eval() { + Value::Dimension(n, Unit::None) => Some(n), + Value::Dimension(n, Unit::Percent) => Some(n / Number::from(100)), + Value::Null => None, + _ => todo!("expected either unitless or % number for $red") + }; + let green = match arg!(args, -1, "green"=Value::Null).eval() { + Value::Dimension(n, Unit::None) => Some(n), + Value::Dimension(n, Unit::Percent) => Some(n / Number::from(100)), + Value::Null => None, + _ => todo!("expected either unitless or % number for $green") + }; + let blue = match arg!(args, -1, "blue"=Value::Null).eval() { + Value::Dimension(n, Unit::None) => Some(n), + Value::Dimension(n, Unit::Percent) => Some(n / Number::from(100)), + Value::Null => None, + _ => todo!("expected either unitless or % number for $blue") + }; + + if !red.is_none() || !green.is_none() || !blue.is_none() { + return Some(Value::Color(Color::from_rgba(red.unwrap_or(color.red()), green.unwrap_or(color.green()), blue.unwrap_or(color.blue()), alpha.unwrap_or(color.alpha())))) + } + + let hue = match arg!(args, -1, "hue"=Value::Null).eval() { + Value::Dimension(n, Unit::None) + | Value::Dimension(n, Unit::Percent) + | Value::Dimension(n, Unit::Deg) => Some(n), + Value::Null => None, + _ => todo!("expected either unitless or % number for hue"), + }; + let saturation = match arg!(args, -1, "saturation"=Value::Null).eval() { + Value::Dimension(n, Unit::None) + | Value::Dimension(n, Unit::Percent) => Some(n / Number::from(100)), + Value::Null => None, + _ => todo!("expected either unitless or % number for saturation"), + }; + let luminance = match arg!(args, -1, "lightness"=Value::Null).eval() { + Value::Dimension(n, Unit::None) + | Value::Dimension(n, Unit::Percent) => Some(n / Number::from(100)), + Value::Null => None, + _ => todo!("expected either unitless or % number for luminance"), + }; + + if !hue.is_none() || !saturation.is_none() || !luminance.is_none() { + // Color::as_hsla() returns more exact values than Color::hue(), etc. + let (this_hue, this_saturation, this_luminance, this_alpha) = color.as_hsla(); + return Some(Value::Color(Color::from_hsla(hue.unwrap_or(this_hue), saturation.unwrap_or(this_saturation), luminance.unwrap_or(this_luminance), alpha.unwrap_or(this_alpha)))) + } + + Some(Value::Color(if !alpha.is_none() { + color.with_alpha(alpha.unwrap()) + } else { + color + })) + }); +} diff --git a/src/color/mod.rs b/src/color/mod.rs index e8f368a..ebd23ac 100644 --- a/src/color/mod.rs +++ b/src/color/mod.rs @@ -91,7 +91,7 @@ impl Color { } /// Mix two colors together with weight - /// Algorithm adapted from + /// Algorithm adapted from /// pub fn mix(self, other: Color, weight: Number) -> Self { let weight = clamp!(weight, 0, 100); @@ -179,7 +179,7 @@ impl Color { (((min + max) / Number::from(2)) * Number::from(100)).round() } - fn as_hsla(&self) -> (Number, Number, Number, Number) { + 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); diff --git a/tests/color.rs b/tests/color.rs index 5c8aead..e9c3430 100644 --- a/tests/color.rs +++ b/tests/color.rs @@ -376,3 +376,18 @@ test!( "a {\n color: mix(black, white);\n}\n", "a {\n color: gray;\n}\n" ); +test!( + change_color_blue, + "a {\n color: change-color(#102030, $blue: 5);\n}\n", + "a {\n color: #102005;\n}\n" +); +test!( + change_color_red_blue, + "a {\n color: change-color(#102030, $red: 120, $blue: 5);\n}\n", + "a {\n color: #782005;\n}\n" +); +test!( + change_color_lum_alpha, + "a {\n color: change-color(hsl(25, 100%, 80%), $lightness: 40%, $alpha: 0.8);\n}\n", + "a {\n color: rgba(204, 85, 0, 0.8);\n}\n" +);