Files
monobiome/monobiome/constants.py

142 lines
3.6 KiB
Python

import tomllib
from importlib.resources import files
import numpy as np
from monobiome.curve import (
l_maxC_h,
bezier_y_at_x,
)
parameters_file = files("monobiome.data") / "parameters.toml"
parameters = tomllib.load(parameters_file.open("rb"))
L_min: int = parameters.get("L_min", 10)
L_max: int = parameters.get("L_max", 98)
L_step: int = parameters.get("L_step", 5)
L_points: list[int] = list(range(L_min, L_max+1))
# L-space just affects accuracy of chroma max
L_space = np.arange(0, 100 + L_step, L_step)
monotone_C_map = parameters.get("monotone_C_map", {})
h_weights = parameters.get("h_weights", {})
h_L_offsets = parameters.get("h_L_offsets", {})
h_C_offsets = parameters.get("h_C_offsets", {})
monotone_h_map = parameters.get("monotone_h_map", {})
accent_h_map = parameters.get("accent_h_map", {})
h_map = {**monotone_h_map, **accent_h_map}
"""
Compute chroma maxima at provided lightness levels across hues.
A map with max chroma values for each hue across lightness space
{
"red": [ Cmax@L=10, Cmax@L=11, Cmax@L=12, ... ],
"orange": [ Cmax@L=10, Cmax@L=11, Cmax@L=12, ... ],
...
}
"""
Lspace_Cmax_Hmap = {
h_str: [l_maxC_h(_L, _h) for _L in L_space]
for h_str, _h in h_map.items()
}
"""
Set QBR curves, *unbounded* chroma curves for all hues
1. Raw bezier chroma values for each hue across the lightness space
Lpoints_Cqbr_Hmap = {
"red": [ Bezier@L=10, Bezier@L=11, Bezier@L=12, ... ],
...
}
2. Three bezier control points for each hue's chroma curve
QBR_ctrl_Hmap = {
"red": np.array([
[ x1, y1 ],
[ x2, y2 ],
[ x3, y3 ]
]),
...
}
"""
Lpoints_Cqbr_Hmap = {}
QBR_ctrl_Hmap = {}
for h_str, _h in monotone_h_map.items():
Lpoints_Cqbr_Hmap[h_str] = np.array(
[monotone_C_map[h_str]]*len(L_points)
)
for h_str, _h in accent_h_map.items():
Lspace_Cmax = Lspace_Cmax_Hmap[h_str]
# get L value of max chroma; will be a bezier control
L_Cmax_idx = np.argmax(Lspace_Cmax)
L_Cmax = L_space[L_Cmax_idx]
# offset control point by any preset x-shift
L_Cmax += h_L_offsets[h_str]
# and get max C at the L offset
Cmax = l_maxC_h(L_Cmax, _h)
# set 3 control points; shift by any global linear offest
C_offset = h_C_offsets.get(h_str, 0)
p_0 = np.array([0, 0])
p_Cmax = np.array([L_Cmax, Cmax + C_offset])
p_100 = np.array([100, 0])
B_L_points = bezier_y_at_x(
p_0, p_Cmax, p_100,
h_weights.get(h_str, 1),
L_points
)
Lpoints_Cqbr_Hmap[h_str] = B_L_points
QBR_ctrl_Hmap[h_str] = np.vstack([p_0, p_Cmax, p_100])
"""
Bezier chroma values, but bounded to attainable gamut colors (bezier fit
can produce invalid chroma values)
h_L_points_Cstar = {
"red": [ bounded-bezier@L=10, bounded-bezier@L=11, ... ],
...
}
"""
Lpoints_Cstar_Hmap = {}
for h_str, L_points_C in Lpoints_Cqbr_Hmap.items():
_h = h_map[h_str]
Lpoints_Cstar_Hmap[h_str] = [
max(0, min(_C, l_maxC_h(_L, _h)))
for _L, _C in zip(L_points, L_points_C, strict=True)
]
# strictly enforce curve bounds s.t. there are no intersections
# order is determined by the max attained chromap
max_Cstar_Horder = [
(h_str, max(Lpoints_Cstar))
for h_str, Lpoints_Cstar in Lpoints_Cstar_Hmap.items()
]
max_Cstar_Horder = sorted(max_Cstar_Horder, key=lambda t: t[1], reverse=True)
for i in range(len(max_Cstar_Horder)-1):
outer_h, _ = max_Cstar_Horder[i]
inner_h, _ = max_Cstar_Horder[i+1]
Lpoints_Cstar_Hmap[inner_h] = [
min(inner_c, Lpoints_Cstar_Hmap[outer_h][ci])
for ci, inner_c in enumerate(Lpoints_Cstar_Hmap[inner_h])
]