aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Touhey <thomas@touhey.fr>2019-05-09 11:38:20 +0200
committerThomas Touhey <thomas@touhey.fr>2019-05-09 11:38:20 +0200
commit7d914122185bac74146963bb8e378dee2e10f4fb (patch)
tree749d1db728dbef17701c185b356990c2c2ffb0f8
parent3768451a4b36e689c92d3ae993d23d62b4512d02 (diff)
Added lighter() and darker() and corrected some methods.
-rwxr-xr-xtests/test_rgba.py32
-rwxr-xr-xtests/test_text.py42
-rwxr-xr-xthcolor/_angle.py17
-rwxr-xr-xthcolor/_builtin/_default.py33
-rwxr-xr-xthcolor/_color.py161
-rwxr-xr-xthcolor/_ref.py7
-rwxr-xr-xthcolor/_sys.py37
7 files changed, 273 insertions, 56 deletions
diff --git a/tests/test_rgba.py b/tests/test_rgba.py
deleted file mode 100755
index 07614e2..0000000
--- a/tests/test_rgba.py
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env python3
-#******************************************************************************
-# Copyright (C) 2019 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr>
-# This file is part of the thcolor project, which is MIT-licensed.
-#******************************************************************************
-""" Unit tests for the thcolor color decoding and management module. """
-
-import pytest
-from thcolor import Color
-
-@pytest.mark.parametrize('test_input,expected', (
- ('blue', ( 0, 0, 255, 1.0)),
- ('#12345F', ( 18, 52, 95, 1.0)),
- ('#123', ( 17, 34, 51, 1.0)),
- ('123', ( 1, 2, 3, 1.0)),
- ('123.0', ( 18, 48, 0, 1.0)),
- ('chucknorris', (192, 0, 0, 1.0)),
- ('rgb(1, 22,242)', ( 1, 22, 242, 1.0)),
- (' rgb (1,22, 242 , 50.0% )', ( 1, 22, 242, 0.5)),
- ('rgba(1,22,242,0.500)', ( 1, 22, 242, 0.5)),
- ('rbga(5, 7)', ( 5, 0, 7, 1.0)),
- ('hsl(0, 1,50.0%)', (255, 0, 0, 1.0)),
- ('hls(0 / 1 0.5 , 0.2)', (255, 0, 0, 0.2)),
- ('hwb(0 0% 0)', (255, 0, 0, 1.0)),
- ('hbw(127 .5)', (128, 255, 142, 1.0)),
- ('gray(100)', (100, 100, 100, 1.0)),
- ('gray(100 / 55 %)', (100, 100, 100, 0.55)),
-))
-def test_rgba(test_input, expected):
- assert Color.from_text(test_input).rgba() == expected
-
-# End of file.
diff --git a/tests/test_text.py b/tests/test_text.py
new file mode 100755
index 0000000..e5a7d4c
--- /dev/null
+++ b/tests/test_text.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+#******************************************************************************
+# Copyright (C) 2019 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr>
+# This file is part of the thcolor project, which is MIT-licensed.
+#******************************************************************************
+""" Unit tests for the thcolor color decoding and management module. """
+
+import pytest
+from thcolor import Color, Angle
+
+def _deg(value):
+ return Angle(Angle.Type.DEG, value)
+
+@pytest.mark.parametrize('test_input,expected', (
+ ('blue', ( 0, 0, 255, 1.00)),
+ ('#12345F', ( 18, 52, 95, 1.00)),
+ ('#123', ( 17, 34, 51, 1.00)),
+ ('123', ( 1, 2, 3, 1.00)),
+ ('123.0', ( 18, 48, 0, 1.00)),
+ ('chucknorris', (192, 0, 0, 1.00)),
+ ('rgb(1, 22,242)', ( 1, 22, 242, 1.00)),
+ (' rgb (1,22, 242 , 50.0% )', ( 1, 22, 242, 0.50)),
+ ('rgba(1,22,242,0.500)', ( 1, 22, 242, 0.50)),
+ ('rbga(5, 7)', ( 5, 0, 7, 1.00)),
+ ('hsl(0, 1,50.0%)', (255, 0, 0, 1.00)),
+ ('hls(0 / 1 0.5 , 0.2)', (255, 0, 0, 0.20)),
+ ('hwb(0 0% 0)', (255, 0, 0, 1.00)),
+ ('hbw(127 .5)', (128, 255, 142, 1.00)),
+ ('gray(100)', (100, 100, 100, 1.00)),
+ ('gray(100 / 55 %)', (100, 100, 100, 0.55)),
+))
+def test_rgba(test_input, expected):
+ assert Color.from_text(test_input).rgba() == expected
+
+@pytest.mark.parametrize('test_input,expected', (
+ ('darker(10%, hsl(0, 1, 50.0%))', (_deg(0), 1.0, 0.4, 1.00)),
+ ('lighter(50%, hsl(0, 1, 60.0%))', (_deg(0), 1.0, 1.0, 1.00)),
+))
+def test_hsla(test_input, expected):
+ assert Color.from_text(test_input).hsla() == expected
+
+# End of file.
diff --git a/thcolor/_angle.py b/thcolor/_angle.py
index 0a0b1bd..c402197 100755
--- a/thcolor/_angle.py
+++ b/thcolor/_angle.py
@@ -62,6 +62,23 @@ class Angle:
self._type = Angle.Type.INVALID
self.set(*args, **kwargs)
+ def __eq__(self, other):
+ if not isinstance(other, Angle):
+ return super().__eq__(other)
+
+ if self._type == Angle.Type.INVALID:
+ return other.type == Angle.Type.INVALID
+ elif self._type == Angle.Type.DEG:
+ return other.degrees == self._value
+ elif self._type == Angle.Type.GRAD:
+ return other.gradiants == self._value
+ elif self._type == Angle.Type.RAD:
+ return other.radiants == self._value
+ elif self._type == Angle.Type.TURN:
+ return other.turns == self._value
+
+ return False
+
def __repr__(self):
args = (('type', self._type),)
if self._type in (Angle.Type.DEG, Angle.Type.GRAD, Angle.Type.RAD,
diff --git a/thcolor/_builtin/_default.py b/thcolor/_builtin/_default.py
index c34243b..8ae5768 100755
--- a/thcolor/_builtin/_default.py
+++ b/thcolor/_builtin/_default.py
@@ -6,6 +6,7 @@
""" Named colors and function definitions. Color names are case-insensitive.
Extends the CSS references. """
+from .._color import Color as _Color
from .._ref import Reference as _Reference
from ._css import CSS4Reference as _CSS4Reference
@@ -94,4 +95,36 @@ class DefaultReference(_CSS4Reference):
alpha: number | percentage = number(1.0)):
return self._hwb((h, w, b, alpha), (0, 2, 1))
+ def darker(self, by: number | percentage, col: color) -> color:
+ try:
+ by = by.to_factor()
+ except ValueError as e:
+ raise _InvalidArgumentValueError(0, str(e))
+
+ try:
+ col = col.to_color()
+ except ValueError as e:
+ raise _InvalidArgumentValueError(0, str(e))
+
+ h, l, s, a = col.hlsa()
+ l = max(l - by, 0.0)
+
+ return _Reference.color(_Color(_Color.Type.HSL, h, s, l, a))
+
+ def lighter(self, by: number | percentage, col: color) -> color:
+ try:
+ by = by.to_factor()
+ except ValueError as e:
+ raise _InvalidArgumentValueError(0, str(e))
+
+ try:
+ col = col.to_color()
+ except ValueError as e:
+ raise _InvalidArgumentValueError(0, str(e))
+
+ h, l, s, a = col.hlsa()
+ l = min(l + by, 1.0)
+
+ return _Reference.color(_Color(_Color.Type.HSL, h, s, l, a))
+
# End of file.
diff --git a/thcolor/_color.py b/thcolor/_color.py
index 301d001..7f69d90 100755
--- a/thcolor/_color.py
+++ b/thcolor/_color.py
@@ -18,7 +18,9 @@ except ImportError:
from ._ref import Reference as _Reference
from ._angle import Angle as _Angle
-from ._sys import (hls_to_rgb as _hls_to_rgb, hwb_to_rgb as _hwb_to_rgb,
+from ._sys import (hls_to_rgb as _hls_to_rgb, rgb_to_hls as _rgb_to_hls,
+ hls_to_hwb as _hls_to_hwb, hwb_to_hls as _hwb_to_hls,
+ rgb_to_hwb as _rgb_to_hwb, hwb_to_rgb as _hwb_to_rgb,
netscape_color as _netscape_color)
from ._exc import (\
ColorExpressionDecodingError as _ColorExpressionDecodingError,
@@ -69,7 +71,7 @@ def _percentage(name, value):
raise ValueError(f"{name} should be a proportion between 0 " \
"and 1.0") from None
- return value
+ return round(value, 4)
def _hue(name, value):
if isinstance(value, _Angle):
@@ -254,12 +256,6 @@ class Color:
return self._alpha
@property
- def a(self):
- """ Alias for the `alpha` property. """
-
- return self.alpha
-
- @property
def red(self):
""" Get the red component. """
@@ -267,12 +263,6 @@ class Color:
return r
@property
- def r(self):
- """ Alias for the `red` property. """
-
- return self.r
-
- @property
def green(self):
""" Get the green component. """
@@ -280,12 +270,6 @@ class Color:
return g
@property
- def g(self):
- """ Alias for the `green` property. """
-
- return self.green
-
- @property
def blue(self):
""" Get the blue component. """
@@ -293,11 +277,101 @@ class Color:
return b
@property
+ def hue(self):
+ """ Hue for the HSL or HWB. """
+
+ if self._type == Color.Type.HWB:
+ return self._hue
+
+ h, _, _ = self.hsl()
+ return h
+
+ @property
+ def saturation(self):
+ """ Saturation for the HSL notation. """
+
+ _, s, _ = self.hsl()
+ return s
+
+ @property
+ def lightness(self):
+ """ Lightness for the HSL notation. """
+
+ _, _, l = self.hsl()
+ return l
+
+ @property
+ def whiteness(self):
+ """ Whiteness for the HWB notation. """
+
+ _, w, _ = self.hwb()
+ return w
+
+ @property
+ def blackness(self):
+ """ Blackness for the HWB notation. """
+
+ _, _, b = self.hwb()
+ return b
+
+ # ---
+ # Short properties.
+ # ---
+
+ @property
+ def a(self):
+ """ Alias for the `alpha` property. """
+
+ return self.alpha
+
+ @property
+ def r(self):
+ """ Alias for the `red` property. """
+
+ return self.r
+
+ @property
+ def g(self):
+ """ Alias for the `green` property. """
+
+ return self.green
+
+ @property
def b(self):
""" Alias for the `blue` property. """
return self.blue
+ @property
+ def h(self):
+ """ Alias for the `hue` property. """
+
+ return self.hue
+
+ @property
+ def s(self):
+ """ Alias for the `saturation` property. """
+
+ return self.saturation
+
+ @property
+ def l(self):
+ """ Alias for the `lightness` property. """
+
+ return self.lightness
+
+ @property
+ def wh(self):
+ """ Alias for the `whiteness` property. """
+
+ return self.whiteness
+
+ @property
+ def bl(self):
+ """ Alias for the `blackness` property. """
+
+ return self.blackness
+
# ---
# Conversion methods.
# ---
@@ -318,6 +392,32 @@ class Color:
raise ValueError(f"color type {self._type} doesn't translate to rgb")
+ def hsl(self):
+ """ Get the (hue, saturation, lightness) components of the color. """
+
+ if self._type == Color.Type.RGB:
+ h, s, l = _rgb_to_hsl(self._r, self._g, self._b)
+ return (_Angle(_Angle.Type.TURN, h), s, l)
+ elif self._type == Color.Type.HSL:
+ return (self._hue, self._sat, self._lgt)
+ elif self._type == Color.Type.HWB:
+ h, s, l = _hwb_to_hsl(self._hue.turns % 1, self._wht, self._blk)
+ return (_Angle(_Angle.Type.TURN, h), s, l)
+
+ raise ValueError(f"color type {self._type} doesn't translate to hsl")
+
+ def hwb(self):
+ """ Get the (hue, whiteness, blackness) components of the color. """
+
+ if self._type == Color.Type.RGB:
+ h, w, b = _rgb_to_hwb(self._r, self._g, self._b)
+ return (_Angle(_Angle.Type.TURN, h), w, b)
+ elif self._type == Color.Type.HSL:
+ h, w, b = _hls_to_hwb(self._hue.turns % 1, self._lgt, self._sat)
+ return (_Angle(_Angle.Type.TURN, h), w, b)
+ elif self._type == Color.Type.HWB:
+ return (self._hue, self._wht, self._blk)
+
def rgba(self):
""" Get the (red, green, blue, alpha) components of the color. """
@@ -326,6 +426,27 @@ class Color:
return (r, g, b, alpha)
+ def hsla(self):
+ """ Get the (hue, saturation, lightness) components of the color. """
+
+ h, s, l = self.hsl()
+ alpha = self._alpha
+
+ return (h, s, l, alpha)
+
+ def hls(self):
+ """ Get the (hue, lightness, saturation) components of the color. """
+
+ h, s, l = self.hsl()
+ return (h, l, s)
+
+ def hlsa(self):
+ """ Get the (hue, lightness, saturation, alpha) components of
+ the color. """
+
+ h, s, l, a = self.hsla()
+ return (h, l, s, a)
+
def css(self):
""" Get the CSS declarations (with compatibility management). """
diff --git a/thcolor/_ref.py b/thcolor/_ref.py
index 9c8a017..83cf9b6 100755
--- a/thcolor/_ref.py
+++ b/thcolor/_ref.py
@@ -237,9 +237,10 @@ class Reference:
return None
self._args = list(map(annotate, spec.args[1:]))
- self._optargs = self._args[-len(spec.defaults):] \
- if spec.defaults else []
- self._args = self._args[:-len(self._optargs)]
+ self._optargs = []
+ if spec.defaults:
+ self._optargs = self._args[-len(spec.defaults):]
+ self._args = self._args[:-len(self._optargs)]
def __call__(self, *args):
if len(args) < len(self._args):
diff --git a/thcolor/_sys.py b/thcolor/_sys.py
index ee36dec..e40d885 100755
--- a/thcolor/_sys.py
+++ b/thcolor/_sys.py
@@ -8,7 +8,38 @@
from math import ceil as _ceil
from colorsys import hls_to_rgb
-__all__ = ["hls_to_rgb", "hwb_to_rgb", "netscape_color"]
+__all__ = ["hls_to_hwb", "hwb_to_hls", "hls_to_rgb", "rgb_to_hls",
+ "rgb_to_hwb", "hwb_to_rgb", "netscape_color"]
+
+# ---
+# Color systems conversion utilities.
+# ---
+
+def hls_to_hwb(hue, s, l):
+ """ Convert HWB to HSL. """
+
+ # TODO
+ pass
+
+def hwb_to_hls(hue, l, s):
+ """ Convert HWB to HLS. """
+
+ # TODO
+ pass
+
+# `hls_to_rgb` is imported from the standard `colorsys` module.
+
+def rgb_to_hls(r, g, b):
+ """ Convert RGB to HLS. """
+
+ # TODO
+ pass
+
+def rgb_to_hwb(r, g, b):
+ """ Convert RGB to HWB. """
+
+ # TODO
+ pass
def hwb_to_rgb(hue, w, b):
""" Convert HWB to RGB color.
@@ -20,6 +51,10 @@ def hwb_to_rgb(hue, w, b):
return r, g, b
+# ---
+# Other utilities.
+# ---
+
def netscape_color(name):
""" Produce a color from a name (which can be all-text, all-digits or
both), using the Netscape behaviour. """