Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 534f2423e9 | |||
| b5b9b12174 | |||
| 4ee9e23c5c | |||
| a5256aaa62 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -17,7 +17,6 @@ docs/_build/
|
||||
# local
|
||||
/Makefile
|
||||
|
||||
data/
|
||||
archive/
|
||||
|
||||
notebooks/color_spaces_manyview.ipynb
|
||||
|
||||
11
README.md
11
README.md
@@ -32,11 +32,11 @@ smoothly as a function of lightness within sRGB gamut bounds.
|
||||
|
||||
| Chroma curves | Color trajectories |
|
||||
|----------------------------------------------------------|------------------------------------------|
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
| Palette |
|
||||
|----------------------------------------------|
|
||||
|  |
|
||||
|  |
|
||||
|
||||
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.
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
version = "1.5.1"
|
||||
version = "1.5.2"
|
||||
|
||||
[alpine]
|
||||
l10 = "#030303"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version = "1.5.1"
|
||||
version = "1.5.2"
|
||||
|
||||
[alpine]
|
||||
l10 = "oklch(10.0% 0.0000 0.0)"
|
||||
|
||||
BIN
images/release/1.5.2/chroma-bounds.png
Normal file
BIN
images/release/1.5.2/chroma-bounds.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 80 KiB |
BIN
images/release/1.5.2/chroma-curves.png
Normal file
BIN
images/release/1.5.2/chroma-curves.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 100 KiB |
BIN
images/release/1.5.2/palette-bare.png
Normal file
BIN
images/release/1.5.2/palette-bare.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
BIN
images/release/1.5.2/palette.png
Normal file
BIN
images/release/1.5.2/palette.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
65
monobiome/data/parameters.toml
Normal file
65
monobiome/data/parameters.toml
Normal 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
|
||||
@@ -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})"
|
||||
|
||||
@@ -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,13 +73,12 @@ 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()
|
||||
#colors = set(["red", "orange", "yellow", "green", "blue"])
|
||||
# uncomment to preview just the 5 core term colors
|
||||
# colors = set(["red", "orange", "yellow", "green", "blue"])
|
||||
|
||||
for h_str, _ in max_Cstar_Horder:
|
||||
Lpoints_Cstar = Lpoints_Cstar_Hmap[h_str]
|
||||
@@ -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))]
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "monobiome"
|
||||
version = "1.5.1"
|
||||
version = "1.5.3"
|
||||
description = "Monobiome color palette"
|
||||
requires-python = ">=3.12"
|
||||
authors = [
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user