Skip to content

Commit 21e072d

Browse files
committed
Change hue_pal to HCL color space
closes has2k1/plotnine#937
1 parent 33bb686 commit 21e072d

File tree

3 files changed

+93
-8
lines changed

3 files changed

+93
-8
lines changed

doc/changelog.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
Changelog
22
=========
33

4+
v0.14.0
5+
-------
6+
7+
*not-yet-released*
8+
9+
API Changes
10+
***********
11+
12+
- Changed :class:`~mizani.palettes.hue_pal` to use HCL color space
13+
from HSL (or HSLuv) space. The previous functionality is now available
14+
with :class:`~mizani.palettes.hls_pal`.
15+
16+
417
v0.13.5
518
-------
619

mizani/palettes.py

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,67 @@ def __call__(self, n: int) -> Sequence[RGBHexColor | None]:
357357
@dataclass
358358
class hue_pal(_discrete_pal):
359359
"""
360-
Utility for making hue palettes for color schemes.
360+
Make a hue palette in HCL colour space
361+
362+
Parameters
363+
----------
364+
h : float | tuple[float, float]
365+
If a float, it is the first hue value, in the range `[0, 360]`.
366+
The range of the palette will be `[first, first + 360)`.
367+
368+
If a tuple, it is the range `[first, last)` of the hues.
369+
c : float
370+
The chroma value, in the range [0, 100].
371+
l : float
372+
The lightness value, in the range [0, 100].
373+
direction : 1 | -1
374+
The order of colours in the scale. If -1 the order
375+
of colours is reversed. The default is 1.
376+
377+
Returns
378+
-------
379+
palette : Callable
380+
A function that takes an integer and returns a that number of
381+
colours from the HCL colour space.
382+
383+
Examples
384+
--------
385+
>>> hue_pal()(5)
386+
['#f8766d', '#a3a500', '#00bf7d', '#00b0f6', '#e76bf3']
387+
>>> hue_pal((100, 300))(5)
388+
['#88ac00', '#00be67', '#00becd', '#3fa1ff', '#e36ef6']
389+
"""
390+
391+
h: float | tuple[float, float] = 15
392+
c: float = 100
393+
l: float = 65
394+
direction: Literal[1, -1] = 1
395+
396+
def __post_init__(self):
397+
# Ensure a proper range for the palette
398+
if isinstance(self.h, tuple):
399+
self._hue_range = self.h
400+
else:
401+
self._hue_range = self.h, self.h + 360
402+
403+
def __call__(self, n: int) -> Sequence[RGBHexColor]:
404+
h = self._hue_range
405+
406+
# Make a hue range that is functionally zero wrap around and
407+
# and cover the entire hue space.
408+
# h = (0, 360) == (360, 360) == (720, 360)
409+
# h = (200, 200) == (200, 200+360) == (200, 200+720)
410+
if (h[1] - h[0]) % 360 < 1:
411+
h = h[0] % 360, (h[1] - 360 / n) % 360
412+
413+
hues = np.linspace(h[0], h[1], n)[:: self.direction] % 360
414+
return [hsluv.lch_to_hex((self.l, self.c, h)) for h in hues]
415+
416+
417+
@dataclass
418+
class hls_pal(_discrete_pal):
419+
"""
420+
Make a hue palette in HLS or HSLUV colour space
361421
362422
Parameters
363423
----------
@@ -384,9 +444,9 @@ class hue_pal(_discrete_pal):
384444
385445
Examples
386446
--------
387-
>>> hue_pal()(5)
447+
>>> hls_pal()(5)
388448
['#db5f57', '#b9db57', '#57db94', '#5784db', '#c957db']
389-
>>> hue_pal(color_space='hsluv')(5)
449+
>>> hls_pal(color_space='hsluv')(5)
390450
['#e0697e', '#9b9054', '#569d79', '#5b98ab', '#b675d7']
391451
"""
392452

tests/test_palettes.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
desaturate_pal,
1414
gradient_n_pal,
1515
grey_pal,
16+
hls_pal,
1617
hls_palette,
1718
hsluv_palette,
1819
hue_pal,
@@ -71,24 +72,35 @@ def test_grey_pal():
7172
assert all(s[1:3] * 3 == s[1:] for s in result)
7273

7374

74-
def test_hue_pal():
75-
palette = hue_pal()
75+
def test_hls_pal():
76+
palette = hls_pal()
7677
result = palette(5)
7778
assert all(s[0] == "#" and len(s) == 7 for s in result)
7879

7980
# branches #
8081
with pytest.raises(ValueError):
81-
hue_pal(0.1, 2.3, 3)
82+
hls_pal(0.1, 2.3, 3)
8283

8384
with pytest.raises(ValueError):
84-
hue_pal(color_space="slh") # pyright: ignore
85+
hls_pal(color_space="slh") # pyright: ignore
8586

8687
# Backword compatibility check
87-
palette = hue_pal(color_space="husl") # pyright: ignore
88+
palette = hls_pal(color_space="husl") # pyright: ignore
8889
result = palette(5)
8990
assert all(s[0] == "#" and len(s) == 7 for s in result)
9091

9192

93+
def test_hue_pal():
94+
pal1, pal2 = hue_pal((0, 360)), hue_pal((360, 360))
95+
assert pal1(5) == pal2(5)
96+
97+
pal1, pal2 = hue_pal((100, 100)), hue_pal((100, 100 + 360))
98+
assert pal1(5) == pal2(5)
99+
100+
pal = hue_pal()
101+
assert pal(3) == ["#f8766d", "#00ba38", "#619cff"]
102+
103+
92104
def test_brewer_pal():
93105
result = brewer_pal()(5)
94106
assert all(s[0] == "#" and len(s) == 7 for s in result)

0 commit comments

Comments
 (0)