Implement builtin functions hue()
, saturation()
, and lightness()
This commit is contained in:
parent
835fe61bb4
commit
8638e2f251
@ -1,7 +1,5 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use num_bigint::BigInt;
|
||||
|
||||
use super::Builtin;
|
||||
use crate::color::Color;
|
||||
use crate::common::QuoteKind;
|
||||
@ -104,23 +102,41 @@ pub(crate) fn register(f: &mut BTreeMap<String, Builtin>) {
|
||||
Some(Value::Color(Color::from_hsla(hue, saturation, luminance, alpha)))
|
||||
});
|
||||
decl!(f "red", |args, _| {
|
||||
match arg!(args, 0, "red") {
|
||||
Value::Color(c) => Some(Value::Dimension(Number::from(BigInt::from(c.red())), Unit::None)),
|
||||
match arg!(args, 0, "color") {
|
||||
Value::Color(c) => Some(Value::Dimension(Number::from(c.red()), Unit::None)),
|
||||
_ => todo!("non-color given to builtin function `red()`")
|
||||
}
|
||||
});
|
||||
decl!(f "green", |args, _| {
|
||||
match arg!(args, 0, "green") {
|
||||
Value::Color(c) => Some(Value::Dimension(Number::from(BigInt::from(c.green())), Unit::None)),
|
||||
match arg!(args, 0, "color") {
|
||||
Value::Color(c) => Some(Value::Dimension(Number::from(c.green()), Unit::None)),
|
||||
_ => todo!("non-color given to builtin function `green()`")
|
||||
}
|
||||
});
|
||||
decl!(f "blue", |args, _| {
|
||||
match arg!(args, 0, "blue") {
|
||||
Value::Color(c) => Some(Value::Dimension(Number::from(BigInt::from(c.blue())), Unit::None)),
|
||||
match arg!(args, 0, "color") {
|
||||
Value::Color(c) => Some(Value::Dimension(Number::from(c.blue()), Unit::None)),
|
||||
_ => todo!("non-color given to builtin function `blue()`")
|
||||
}
|
||||
});
|
||||
decl!(f "hue", |args, _| {
|
||||
match arg!(args, 0, "color") {
|
||||
Value::Color(c) => Some(Value::Dimension(c.hue(), Unit::Deg)),
|
||||
_ => todo!("non-color given to builtin function `hue()`")
|
||||
}
|
||||
});
|
||||
decl!(f "saturation", |args, _| {
|
||||
match arg!(args, 0, "color") {
|
||||
Value::Color(c) => Some(Value::Dimension(c.saturation(), Unit::Percent)),
|
||||
_ => todo!("non-color given to builtin function `saturation()`")
|
||||
}
|
||||
});
|
||||
decl!(f "lightness", |args, _| {
|
||||
match arg!(args, 0, "color") {
|
||||
Value::Color(c) => Some(Value::Dimension(c.lightness(), Unit::Percent)),
|
||||
_ => todo!("non-color given to builtin function `lightness()`")
|
||||
}
|
||||
});
|
||||
decl!(f "opacity", |args, _| {
|
||||
match arg!(args, 0, "color") {
|
||||
Value::Color(c) => Some(Value::Dimension(c.alpha() / Number::from(255), Unit::None)),
|
||||
|
@ -40,6 +40,68 @@ impl Color {
|
||||
self.green
|
||||
}
|
||||
|
||||
/// Calculate hue from RGBA values
|
||||
/// Algorithm adapted from http://www.niwa.nu/2013/05/math-behind-colorspace-conversions-rgb-hsl/
|
||||
pub fn hue(&self) -> Number {
|
||||
let red = Number::ratio(self.red, 255);
|
||||
let green = Number::ratio(self.green, 255);
|
||||
let blue = Number::ratio(self.blue, 255);
|
||||
let min = red.clone().min(green.clone().min(blue.clone()));
|
||||
let max = red.clone().max(green.clone().max(blue.clone()));
|
||||
if &min == &max {
|
||||
return Number::from(0);
|
||||
}
|
||||
|
||||
let mut hue = 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)).round()
|
||||
}
|
||||
|
||||
/// Calculate saturation from RGBA values
|
||||
/// Algorithm adapted from http://www.niwa.nu/2013/05/math-behind-colorspace-conversions-rgb-hsl/
|
||||
pub fn saturation(&self) -> Number {
|
||||
let red = Number::ratio(self.red, 255);
|
||||
let green = Number::ratio(self.green, 255);
|
||||
let blue = Number::ratio(self.blue, 255);
|
||||
let mut min = red.clone().min(green.clone().min(blue.clone()));
|
||||
min = Number::ratio((min * Number::from(100)).to_integer(), 100);
|
||||
let mut max = red.max(green.max(blue));
|
||||
max = Number::ratio((max * Number::from(100)).to_integer(), 100);
|
||||
let luminance = (min.clone() + max.clone()) / Number::from(2);
|
||||
if &min == &max {
|
||||
return Number::from(0);
|
||||
}
|
||||
|
||||
let saturation = if luminance < Number::ratio(1, 2) {
|
||||
(max.clone() - min.clone()) / (max + min)
|
||||
} else {
|
||||
(max.clone() - min.clone()) / (Number::from(2) - max - min)
|
||||
} * Number::from(100);
|
||||
|
||||
saturation.round()
|
||||
}
|
||||
|
||||
/// Calculate luminance from RGBA values
|
||||
/// Algorithm adapted from http://www.niwa.nu/2013/05/math-behind-colorspace-conversions-rgb-hsl/
|
||||
pub fn lightness(&self) -> Number {
|
||||
let red = Number::ratio(self.red, 255);
|
||||
let green = Number::ratio(self.green, 255);
|
||||
let blue = Number::ratio(self.blue, 255);
|
||||
let min = red.clone().min(green.clone().min(blue.clone()));
|
||||
let max = red.max(green.max(blue));
|
||||
(((min + max) / Number::from(2)) * Number::from(100)).round()
|
||||
}
|
||||
|
||||
pub fn alpha(&self) -> Number {
|
||||
self.alpha.clone()
|
||||
}
|
||||
@ -77,7 +139,7 @@ impl Color {
|
||||
luminance.clone() + saturation.clone() - luminance.clone() * saturation.clone()
|
||||
};
|
||||
let temporary_2 = Number::from(2) * luminance.clone() - temporary_1.clone();
|
||||
hue = hue / Number::from(360);
|
||||
hue /= Number::from(360);
|
||||
let mut temporary_r = hue.clone() + Number::ratio(1, 3);
|
||||
let mut temporary_g = hue.clone();
|
||||
let mut temporary_b = hue.clone() - Number::ratio(1, 3);
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::convert::From;
|
||||
use std::fmt::{self, Display, Write};
|
||||
use std::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign};
|
||||
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
|
||||
|
||||
use num_bigint::BigInt;
|
||||
use num_rational::BigRational;
|
||||
@ -132,6 +132,12 @@ impl Mul for Number {
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign for Number {
|
||||
fn mul_assign(&mut self, other: Self) {
|
||||
self.val *= other.val
|
||||
}
|
||||
}
|
||||
|
||||
impl Div for Number {
|
||||
type Output = Self;
|
||||
|
||||
@ -141,3 +147,9 @@ impl Div for Number {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DivAssign for Number {
|
||||
fn div_assign(&mut self, other: Self) {
|
||||
self.val /= other.val
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ impl Add for Value {
|
||||
Self::Null => Value::Ident(c.to_string(), QuoteKind::None),
|
||||
Self::Color(..) => todo!("figure out if it's possible to add colors"),
|
||||
_ => Value::Ident(format!("{}{}", c, other), QuoteKind::None),
|
||||
}
|
||||
},
|
||||
// Self::BinaryOp(..) => todo!(),
|
||||
// Self::Paren(..) => todo!(),
|
||||
Self::Ident(s1, quotes1) => match other {
|
||||
@ -97,7 +97,7 @@ impl Sub for Value {
|
||||
}
|
||||
Self::Null => Value::Ident(format!("{}-", c), QuoteKind::None),
|
||||
Self::Dimension(..) => todo!("investigate adding numbers and colors"),
|
||||
_ => Value::Ident(format!("{}-{}", c, other), QuoteKind::None)
|
||||
_ => Value::Ident(format!("{}-{}", c, other), QuoteKind::None),
|
||||
},
|
||||
// Self::BinaryOp(..) => todo!(),
|
||||
// Self::Paren(..) => todo!(),
|
||||
@ -116,7 +116,11 @@ impl Sub for Value {
|
||||
QuoteKind::None,
|
||||
)
|
||||
}
|
||||
Self::Important | Self::True | Self::False | Self::Dimension(..) | Self::Color(..) => {
|
||||
Self::Important
|
||||
| Self::True
|
||||
| Self::False
|
||||
| Self::Dimension(..)
|
||||
| Self::Color(..) => {
|
||||
let quotes = match quotes1 {
|
||||
QuoteKind::Double | QuoteKind::Single => QuoteKind::Double,
|
||||
QuoteKind::None => QuoteKind::None,
|
||||
|
@ -156,3 +156,18 @@ test!(
|
||||
"a {\n color: foo - red;\n}\n",
|
||||
"a {\n color: foo-red;\n}\n"
|
||||
);
|
||||
test!(
|
||||
hue,
|
||||
"$a: hsl(193, 67%, 28%);\n\na {\n color: hue($a);\n}\n",
|
||||
"a {\n color: 193deg;\n}\n"
|
||||
);
|
||||
test!(
|
||||
saturation,
|
||||
"$a: hsl(193, 67%, 28%);\n\na {\n color: saturation($a);\n}\n",
|
||||
"a {\n color: 67%;\n}\n"
|
||||
);
|
||||
test!(
|
||||
lightness,
|
||||
"$a: hsl(193, 67%, 28%);\n\na {\n color: lightness($a);\n}\n",
|
||||
"a {\n color: 28%;\n}\n"
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user