Take a range of contrasts for generating colours

このコミットが含まれているのは:
n9k 2022-06-14 05:58:57 +00:00
コミット 47ee5fe607
3個のファイルの変更44行の追加32行の削除

ファイルの表示

@ -38,7 +38,8 @@ def generate_tripcode(password):
background_colour = generate_colour(
seed='tripcode-background\0' + digest,
bg=CONFIG['CHAT_BACKGROUND_COLOUR'],
contrast=5.0,
min_contrast=5.0,
max_contrast=5.0,
)
foreground_colour = generate_maximum_contrast_colour(
seed='tripcode-foreground\0' + digest,

ファイルの表示

@ -26,7 +26,7 @@ def generate_user(timestamp, token, broadcaster, presence):
colour = generate_colour(
seed='name\0' + token,
bg=CONFIG['CHAT_BACKGROUND_COLOUR'],
contrast=4.53,
min_contrast=4.53,
)
token_hash, tag = generate_token_hash_and_tag(token)
return {

ファイルの表示

@ -3,6 +3,7 @@
import re
import random
from math import inf
class NotAColor(Exception):
pass
@ -47,7 +48,7 @@ def _tc_to_sc(tc):
Almost-inverse of _sc_to_tc.
The function _sc_to_tc is not injective (because of the discontinuity at
sc=0.03928), thus it has no true inverse. In this implementation, whenever
sc=0.03928), thus it has no true inverse. In this implementation, whenever
for a given `tc` there are two distinct values of `sc` such that
sc_to_tc(`sc`)=`tc`, the smaller sc is chosen. (The smaller one is less
expensive to compute).
@ -89,22 +90,23 @@ def get_contrast(bg, fg):
)
return (max(lumas) + 0.05) / (min(lumas) + 0.05)
def generate_colour(seed, bg, contrast=4.5, lighter=True):
def generate_colour(seed, bg, min_contrast=4.5, max_contrast=inf, lighter=True):
'''
Generate a random colour with given contrast to `bg`.
Generate a random colour with a contrast to `bg` in a given interval.
Channels of `t` are uniformly distributed. No characteristics of the
returned colour are guaranteed to be chosen uniformly from the space of
possible values.
This works by generating an intermediate 3-tuple `t` and transforming it
into the returned colour. Channels of `t` are uniformly distributed, but no
characteristics of the returned colour are guaranteed to be chosen uniformly
from the space of possible values.
If `lighter` is true, the returned colour is forced to have a higher
relative luminance than `bg`. This is fine if `bg` is dark; if `bg` is
not dark, the space of possible returned colours will be a lot smaller
(and might be empty). If `lighter` is false, the returned colour is
forced to have a lower relative luminance than `bg`.
relative luminance than `bg`. This is fine if `bg` is dark; if `bg` is not
dark, the space of possible returned colours will be a lot smaller (and
might be empty). If `lighter` is false, the returned colour is forced to
have a lower relative luminance than `bg`.
It's simple to calculate the maximum possible contrast between `bg` and
any other colour. (The minimum contrast is always 1.)
It's simple to calculate the maximum possible contrast between `bg` and any
other colour. (The minimum contrast is always 1.)
>>> bg = (0x23, 0x23, 0x27)
>>> luma = get_relative_luminance(bg)
@ -113,11 +115,13 @@ def generate_colour(seed, bg, contrast=4.5, lighter=True):
>>> 1.05 / (luma + 0.05) # maximum contrast for colours with greater luma
15.657919499763137
There are values of `contrast` for which the space of possible returned
colours is empty. For example a `contrast` greater than 21 is always
impossible, but the exact upper bound depends on `bg`. The desired
relative luminance of the returned colour must exist in the interval [0,1].
The formula for desired luma is given below.
There are contrast intervals for which the space of possible returned
colours is empty. For example a contrast greater than 21 is always
impossible, but the exact upper bound depends on `bg`. The desired relative
luminance of the returned colour must exist in the interval [0,1]. The
formula for desired luma is given below. This is for one particular
contrast but the same formula can be used twice (once with `min_contrast` and
once with `max_contrast`) to get a range of desired lumas.
>>> bg_luma = get_relative_luminance(bg)
>>> desired_luma = (
@ -131,29 +135,34 @@ def generate_colour(seed, bg, contrast=4.5, lighter=True):
r = random.Random(seed)
if lighter:
desired_luma = contrast * (get_relative_luminance(bg) + 0.05) - 0.05
min_desired_luma = min_contrast * (get_relative_luminance(bg) + 0.05) - 0.05
max_desired_luma = max_contrast * (get_relative_luminance(bg) + 0.05) - 0.05
else:
desired_luma = (get_relative_luminance(bg) + 0.05) / contrast - 0.05
min_desired_luma = (get_relative_luminance(bg) + 0.05) / max_contrast - 0.05
max_desired_luma = (get_relative_luminance(bg) + 0.05) / min_contrast - 0.05
V = (0.2126, 0.7152, 0.0722)
indices = [0, 1, 2]
r.shuffle(indices)
i, j, k = indices
# V[i] * ci + V[j] * 0 + V[k] * 0 <= desired_luma
# V[i] * ci + V[j] * 1 + V[k] * 1 >= desired_luma
ci_upper = (desired_luma - V[j] * 0 - V[k] * 0) / V[i]
ci_lower = (desired_luma - V[j] * 1 - V[k] * 1) / V[i]
# V[i] * ci + V[j] * 0 + V[k] * 0 <= max_desired_luma
# V[i] * ci + V[j] * 1 + V[k] * 1 >= min_desired_luma
ci_upper = (max_desired_luma - V[j] * 0 - V[k] * 0) / V[i]
ci_lower = (min_desired_luma - V[j] * 1 - V[k] * 1) / V[i]
ci = r.uniform(max(0, ci_lower), min(1, ci_upper))
# V[i] * ci + V[j] * cj + V[k] * 0 <= desired_luma
# V[i] * ci + V[j] * cj + V[k] * 1 >= desired_luma
cj_upper = (desired_luma - V[i] * ci - V[k] * 0) / V[j]
cj_lower = (desired_luma - V[i] * ci - V[k] * 1) / V[j]
# V[i] * ci + V[j] * cj + V[k] * 0 <= max_desired_luma
# V[i] * ci + V[j] * cj + V[k] * 1 >= min_desired_luma
cj_upper = (max_desired_luma - V[i] * ci - V[k] * 0) / V[j]
cj_lower = (min_desired_luma - V[i] * ci - V[k] * 1) / V[j]
cj = r.uniform(max(0, cj_lower), min(1, cj_upper))
# V[i] * ci + V[j] * cj + V[k] * ck = desired_luma
ck = (desired_luma - V[i] * ci - V[j] * cj) / V[k]
# V[i] * ci + V[j] * cj + V[k] * ck <= max_desired_luma
# V[i] * ci + V[j] * cj + V[k] * ck >= min_desired_luma
ck_upper = (max_desired_luma - V[i] * ci - V[j] * cj) / V[k]
ck_lower = (min_desired_luma - V[i] * ci - V[j] * cj) / V[k]
ck = r.uniform(max(0, ck_lower), min(1, ck_upper))
t = [None, None, None]
t[i], t[j], t[k] = ci, cj, ck
@ -185,10 +194,12 @@ def generate_maximum_contrast_colour(seed, bg, proportion_of_max=31/32):
max_darker_contrast = get_maximum_contrast(bg, lighter=False)
max_contrast = max(max_lighter_contrast, max_darker_contrast)
practical_max_contrast = max_contrast * proportion_of_max
colour = generate_colour(
seed,
bg,
contrast=max_contrast * proportion_of_max,
min_contrast=practical_max_contrast,
max_contrast=practical_max_contrast,
lighter=max_lighter_contrast > max_darker_contrast,
)