diff --git a/catppuccin/colour.py b/catppuccin/colour.py index 193eea7..62be77a 100644 --- a/catppuccin/colour.py +++ b/catppuccin/colour.py @@ -7,6 +7,8 @@ import re from dataclasses import dataclass from typing import Any, Tuple +from catppuccin.hsl import rgb_to_hsl + @dataclass(frozen=True) class Colour: @@ -27,6 +29,16 @@ class Colour: """Get the colour as a 4-tuple of red, green, blue, and alpha.""" return self.red, self.green, self.blue, self.alpha + @property + def hsl(self) -> Tuple[float, float, float]: + """Get the colour as a 3-tuple of hue, saturation, and lightness.""" + return rgb_to_hsl(*self.rgb) + + @property + def hsla(self) -> Tuple[float, float, float, float]: + """Get the colour as a 4-tuple of hue, saturation, lightness, and alpha.""" + return (*self.hsl, self.alpha) + @property def hex(self) -> str: """Get the colour as a lowercase hex string.""" diff --git a/catppuccin/hsl.py b/catppuccin/hsl.py new file mode 100644 index 0000000..839d202 --- /dev/null +++ b/catppuccin/hsl.py @@ -0,0 +1,40 @@ +""" +Utilities for working with HSL colours. +""" +from __future__ import annotations + +from typing import Tuple + + +# pylint: disable=invalid-name +def rgb_to_hsl(r: int, g: int, b: int) -> Tuple[int, float, float]: + """Convert RGB to HSL.""" + r1 = r / 255 + g1 = g / 255 + b1 = b / 255 + + cmax = max(r1, g1, b1) + cmin = min(r1, g1, b1) + delta = cmax - cmin + + if delta == 0: + h = 0.0 + elif cmax == r1: + h = ((g1 - b1) / delta) % 6 + elif cmax == g1: + h = (b1 - r1) / delta + 2 + else: + h = (r1 - g1) / delta + 4 + + h = round(h * 60) + if h < 0: + h += 360 + + l = (cmax + cmin) / 2 + + if delta == 0: + s = 0.0 + else: + s = delta / (1 - abs(2 * l - 1)) + + return h, round(s, 2), round(l, 2) diff --git a/tests/test_colour.py b/tests/test_colour.py index 3c47806..daaeac6 100644 --- a/tests/test_colour.py +++ b/tests/test_colour.py @@ -15,6 +15,18 @@ def test_colour_to_rgba_default(): assert Colour(12, 123, 234).rgba == (12, 123, 234, 255) +def test_colour_to_hsl(): + assert Colour(12, 123, 234).hsl == (210, 0.90, 0.48) + + +def test_colour_to_hsla(): + assert Colour(12, 123, 234, 35).hsla == (210, 0.90, 0.48, 35) + + +def test_colour_to_hsla_default(): + assert Colour(12, 123, 234).hsla == (210, 0.90, 0.48, 255) + + def test_rgb_colour_to_hex(): assert Colour(0x12, 0xEB, 0x77).hex == "12eb77" diff --git a/tests/test_hsl.py b/tests/test_hsl.py new file mode 100644 index 0000000..4cf6d78 --- /dev/null +++ b/tests/test_hsl.py @@ -0,0 +1,13 @@ +from catppuccin import Flavour +from catppuccin.hsl import rgb_to_hsl + +mocha = Flavour.mocha() +latte = Flavour.latte() + + +def test_rgb_to_hsl(): + hsl = rgb_to_hsl(*mocha.mauve.rgb) + assert hsl == (267, 0.84, 0.81) + + hsl = rgb_to_hsl(*latte.text.rgb) + assert hsl == (234, 0.16, 0.35)