///|
/// Converts a color to HSV components: hue in radians, saturation/value in [0, 1].
pub fn Color::to_hsv(self : Color) -> (Double, Double, Double) {
let maximum = self.r.max(self.g).max(self.b)
let minimum = self.r.min(self.g).min(self.b)
let delta = maximum - minimum
let hue = if delta == 0.0 {
0.0
} else if maximum == self.r {
2.0 * @math.PI * ((self.g - self.b) / delta / 6.0)
} else if maximum == self.g {
2.0 * @math.PI * (1.0 / 3.0 + (self.b - self.r) / delta / 6.0)
} else {
2.0 * @math.PI * (2.0 / 3.0 + (self.r - self.g) / delta / 6.0)
}
let saturation = if maximum == 0.0 { 0.0 } else { delta / maximum }
(hue, saturation, maximum)
}
///|
pub fn Color::hsv(
hue : Double,
saturation : Double,
value : Double,
alpha? : Double = 1.0,
) -> Color {
let h = (hue / (2.0 * @math.PI)).abs()
let sector = (h * 6.0).to_int()
let fraction = h * 6.0 - sector.to_double()
let p = value * (1.0 - saturation)
let q = value * (1.0 - fraction * saturation)
let t = value * (1.0 - (1.0 - fraction) * saturation)
let rgb = match sector % 6 {
0 => (value, t, p)
1 => (q, value, p)
2 => (p, value, t)
3 => (p, q, value)
4 => (t, p, value)
_ => (value, p, q)
}
Color::rgba(rgb.0, rgb.1, rgb.2, a=alpha)
}
///|
pub fn Color::grayscale(self : Color) -> Color {
let value = self.luminance()
Color::rgba(value, value, value, a=self.a)
}
///|
pub fn Color::saturate(self : Color, amount : Double) -> Color {
let (hue, saturation, value) = self.to_hsv()
Color::hsv(hue, (saturation + amount).max(0.0).min(1.0), value, alpha=self.a)
}
///|
pub fn Color::brighten(self : Color, amount : Double) -> Color {
let (hue, saturation, value) = self.to_hsv()
Color::hsv(hue, saturation, (value + amount).max(0.0).min(1.0), alpha=self.a)
}
///|
pub fn Color::invert(self : Color) -> Color {
Color::rgba(1.0 - self.r, 1.0 - self.g, 1.0 - self.b, a=self.a)
}
///|
pub fn triadic_palette(base : Color) -> Palette {
let (hue, saturation, value) = base.to_hsv()
Palette::new("triadic", [
base,
Color::hsv(hue + 2.0 * @math.PI / 3.0, saturation, value),
Color::hsv(hue + 4.0 * @math.PI / 3.0, saturation, value),
])
}
///|
pub fn split_complementary_palette(base : Color) -> Palette {
let (hue, saturation, value) = base.to_hsv()
Palette::new("split-complementary", [
base,
Color::hsv(hue + 5.0 * @math.PI / 6.0, saturation, value),
Color::hsv(hue + 7.0 * @math.PI / 6.0, saturation, value),
])
}
///|
pub fn thermal_palette(samples : Int) -> Palette {
let colors = []
for i in 0..