6 Commits
1.5.1 ... 1.5.4

51 changed files with 113 additions and 41 deletions

1
.gitignore vendored
View File

@@ -17,7 +17,6 @@ docs/_build/
# local
/Makefile
data/
archive/
notebooks/color_spaces_manyview.ipynb

View File

@@ -32,11 +32,11 @@ smoothly as a function of lightness within sRGB gamut bounds.
| Chroma curves | Color trajectories |
|----------------------------------------------------------|------------------------------------------|
| ![Chroma curves](images/release/1.5.1/chroma-curves.png) | ![Trajectories](images/trajectories.gif) |
| ![Chroma curves](images/release/1.5.4/chroma-curves.png) | ![Trajectories](images/trajectories.gif) |
| Palette |
|----------------------------------------------|
| ![Palette](images/release/1.5.1/palette.png) |
| ![Palette](images/release/1.5.4/palette.png) |
Chroma curves are designed specifically to establish a distinct role for each
accent and are non-intersecting over the lightness domain (hence the distinct
@@ -143,6 +143,13 @@ using the `monobiome` CLI:
(`grassland`). Every part of this process can be customized: the scheme
parameters, the scheme definitions/file, the app template.
The separation of duties here facilitates robustness: the palette colors can be
tweaked (across versions, say) without changing how those colors get used in
app themes. Scheme files don't hardcode color values, and app templates don't
refer to palette variables at all. Scheme files bridge palette colors with
*intended uses*, and templates need only align those uses under an app's
expected config syntax.
Running these commands in sequence from the repo root should work
out-of-the-box after having installed the CLI tool.

View File

@@ -1,4 +1,4 @@
version = "1.5.1"
version = "1.5.4"
[alpine]
l10 = "#030303"

View File

@@ -1,4 +1,4 @@
version = "1.5.1"
version = "1.5.4"
[alpine]
l10 = "oklch(10.0% 0.0000 0.0)"

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@@ -0,0 +1,65 @@
L_min = 10
L_max = 98
L_step = 5
[monotone_C_map]
alpine = 0
badlands = 0.011
chaparral = 0.011
savanna = 0.011
grassland = 0.011
reef = 0.011
tundra = 0.011
heathland = 0.011
moorland = 0.011
[h_weights]
red = 3.0
orange = 3.8
yellow = 3.8
green = 3.8
cyan = 3.8
blue = 3.6
violet = 3.0
magenta = 3.6
[h_L_offsets]
red = 0
orange = -5.5
yellow = -13.5
green = -12.5
cyan = -10.5
blue = 9
violet = 7
magenta = 2
[h_C_offsets]
red = 0
orange = -0.015
yellow = -0.052
green = -0.08
cyan = -0.009
blue = -0.01
violet = -0.047
magenta = -0.1
[monotone_h_map]
alpine = 0
badlands = 29
chaparral = 62.5
savanna = 104
grassland = 148
reef = 205
tundra = 262
heathland = 306
moorland = 350
[accent_h_map]
red = 29
orange = 62.5
yellow = 104
green = 148
cyan = 205
blue = 262
violet = 306
magenta = 350

View File

@@ -5,6 +5,10 @@ from importlib.metadata import version
from coloraide import Color
from monobiome.util import (
hex_from_rgb8,
srgb8_from_color,
)
from monobiome.constants import (
h_map,
L_points,
@@ -24,8 +28,8 @@ def compute_hlc_map(notation: str) -> dict[str, Any]:
oklch = Color('oklch', [_l/100, _c, _h])
if notation == "hex":
srgb = oklch.convert('srgb')
c_str = srgb.to_string(hex=True)
rgb8 = srgb8_from_color(oklch)
c_str = hex_from_rgb8(rgb8)
elif notation == "oklch":
ol, oc, oh = oklch.convert('oklch').coords()
c_str = f"oklch({ol*100:.1f}% {oc:.4f} {oh:.1f})"

View File

@@ -5,6 +5,7 @@ import matplotlib.pyplot as plt
from coloraide import Color
from matplotlib.collections import LineCollection
from monobiome.util import srgb8_from_color
from monobiome.palette import compute_hlc_map
from monobiome.constants import (
h_map,
@@ -72,12 +73,11 @@ def plot_hue_chroma_bounds() -> tuple[plt.Figure, plt.Axes]:
return fig, axes
def plot_hue_chroma_star() -> tuple[plt.Figure, plt.Axes]:
fig, ax = plt.subplots(1, 1, figsize=(8, 6))
# uncomment to preview 5 core term colors
colors = accent_h_map.keys()
# uncomment to preview just the 5 core term colors
# colors = set(["red", "orange", "yellow", "green", "blue"])
for h_str, _ in max_Cstar_Horder:
@@ -88,34 +88,22 @@ def plot_hue_chroma_star() -> tuple[plt.Figure, plt.Axes]:
_h = h_map[h_str]
h_colors = [
Color('oklch', [_l/100, _c, _h]).convert("srgb")
Color(
'oklch', [_l/100, _c, _h]
).convert("srgb").fit(method="oklch-chroma")
for _l, _c in zip(L_points, Lpoints_Cstar, strict=True)
]
# # ax.fill_between(
# ax.scatter(
# L_points,
# Lpoints_Cstar,
# alpha=0.7,
# c=h_colors,
# #alpha=0.2,
# #color='grey',
# label=h_str
# )
# x, y = L_points, Lpoints_Cstar_Hmap[h_str]
# n = int(0.45*len(x))
# ax.text(x[n], y[n]-0.01, h_str, rotation=10, va='center', ha='left')
x = np.asarray(L_points)
y = np.asarray(Lpoints_Cstar)
pts = np.column_stack([x, y]).reshape(-1, 1, 2)
segs = np.concatenate([pts[:-1], pts[1:]], axis=1)
rgb = np.asarray(h_colors)
seg_colors = (rgb[:-1] + rgb[1:]) / 2
lc = LineCollection(segs, colors=seg_colors, linewidth=3,
capstyle="round", joinstyle="round",
label=h_str)
ax.add_collection(lc)
ax.autoscale_view()
@@ -126,12 +114,11 @@ def plot_hue_chroma_star() -> tuple[plt.Figure, plt.Axes]:
return fig, ax
def palette_image(
palette: dict[str, dict[int, str]],
cell_size: int = 40,
keys: list[str] | None = None
) -> tuple[np.ndarray, list[str], list[list[int]], int, int]:
) -> tuple[np.ndarray, list[str], list[list[int]]]:
names = list(palette.keys()) if keys is None else keys
row_count = len(names)
@@ -140,7 +127,7 @@ def palette_image(
h = row_count * cell_size
w = max_cols * cell_size
img = np.ones((h, w, 3), float)
img = np.ones((h, w, 3), int)
lightness_keys_per_row = []
@@ -149,14 +136,12 @@ def palette_image(
lkeys = sorted(shades.keys())
lightness_keys_per_row.append(lkeys)
for c, k in enumerate(lkeys):
col = Color(shades[k]).convert("srgb").fit(method="clip")
rgb = [col["r"], col["g"], col["b"]]
rgb = srgb8_from_color(shades[k])
r0, r1 = r * cell_size, (r + 1) * cell_size
c0, c1 = c * cell_size, (c + 1) * cell_size
img[r0:r1, c0:c1, :] = rgb
return img, names, lightness_keys_per_row, cell_size, max_cols
return img, names, lightness_keys_per_row
def show_palette(
palette: dict[str, dict[int, str]],
@@ -165,9 +150,7 @@ def show_palette(
show_labels: bool = True,
dpi: int = 100,
) -> tuple[plt.Figure, plt.Axes]:
img, names, keys, cell_size, max_cols = palette_image(
palette, cell_size, keys=keys
)
img, names, _ = palette_image(palette, cell_size, keys=keys)
fig_w = img.shape[1] / 100
fig_h = img.shape[0] / 100
@@ -175,7 +158,7 @@ def show_palette(
if show_labels:
fig, ax = plt.subplots(figsize=(fig_w, fig_h), dpi=dpi)
ax.imshow(img, interpolation="none", origin="upper")
ax.imshow(img, interpolation="nearest", origin="upper")
ax.set_xticks([])
ytick_pos = [(i + 0.5) * cell_size for i in range(len(names))]

View File

@@ -2,6 +2,7 @@ import math
from types import GenericAlias
from argparse import ArgumentParser, _SubParsersAction
import numpy as np
from coloraide import Color
_SubParsersAction.__class_getitem__ = classmethod(GenericAlias)
@@ -33,3 +34,13 @@ def oklch_distance(xc: Color, yc: Color) -> float:
dz = l1 - l2
return (dx**2 + dy**2 + dz**2)**0.5
def srgb8_from_color(c: str | Color) -> np.ndarray:
c = Color(c).convert("srgb").fit(method="oklch-chroma")
rgb = np.array([c["r"], c["g"], c["b"]], dtype=float)
rgb8 = np.clip(np.round(rgb * 255), 0, 255).astype(np.uint8)
return rgb8
def hex_from_rgb8(rgb8: np.ndarray) -> str:
return f"#{int(rgb8[0]):02x}{int(rgb8[1]):02x}{int(rgb8[2]):02x}"

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "monobiome"
version = "1.5.1"
version = "1.5.4"
description = "Monobiome color palette"
requires-python = ">=3.12"
authors = [

View File

@@ -17,8 +17,8 @@ fig.savefig(Path(figure_dir, "chroma-curves.png"))
# "oklch" causes some slight hex drift when later using an eyedropper
hlc_map = compute_hlc_map("hex") # ("oklch")
fig, ax = plotting.show_palette(hlc_map)
fig.savefig(Path(figure_dir, "palette.png"))
fig, ax = plotting.show_palette(hlc_map, cell_size=25)
fig.savefig(Path(figure_dir, "palette.png"), pad_inches=0)
fig, ax = plotting.show_palette(hlc_map, show_labels=False)
fig.savefig(Path(figure_dir, "palette-bare.png"), pad_inches=0)

View File

@@ -11,3 +11,6 @@ uv run monobiome palette -n oklch -f toml -o colors/oklch-palette.toml
# generate provided app config
"$script_dir/generate.sh"
# generate release plots
uv run "$script_dir/plots.py"

2
uv.lock generated
View File

@@ -882,7 +882,7 @@ wheels = [
[[package]]
name = "monobiome"
version = "1.5.1"
version = "1.5.4"
source = { editable = "." }
dependencies = [
{ name = "coloraide" },