Take a range of contrasts for generating colours
このコミットが含まれているのは:
コミット
47ee5fe607
|
@ -38,7 +38,8 @@ def generate_tripcode(password):
|
||||||
background_colour = generate_colour(
|
background_colour = generate_colour(
|
||||||
seed='tripcode-background\0' + digest,
|
seed='tripcode-background\0' + digest,
|
||||||
bg=CONFIG['CHAT_BACKGROUND_COLOUR'],
|
bg=CONFIG['CHAT_BACKGROUND_COLOUR'],
|
||||||
contrast=5.0,
|
min_contrast=5.0,
|
||||||
|
max_contrast=5.0,
|
||||||
)
|
)
|
||||||
foreground_colour = generate_maximum_contrast_colour(
|
foreground_colour = generate_maximum_contrast_colour(
|
||||||
seed='tripcode-foreground\0' + digest,
|
seed='tripcode-foreground\0' + digest,
|
||||||
|
|
|
@ -26,7 +26,7 @@ def generate_user(timestamp, token, broadcaster, presence):
|
||||||
colour = generate_colour(
|
colour = generate_colour(
|
||||||
seed='name\0' + token,
|
seed='name\0' + token,
|
||||||
bg=CONFIG['CHAT_BACKGROUND_COLOUR'],
|
bg=CONFIG['CHAT_BACKGROUND_COLOUR'],
|
||||||
contrast=4.53,
|
min_contrast=4.53,
|
||||||
)
|
)
|
||||||
token_hash, tag = generate_token_hash_and_tag(token)
|
token_hash, tag = generate_token_hash_and_tag(token)
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import random
|
import random
|
||||||
|
from math import inf
|
||||||
|
|
||||||
class NotAColor(Exception):
|
class NotAColor(Exception):
|
||||||
pass
|
pass
|
||||||
|
@ -47,7 +48,7 @@ def _tc_to_sc(tc):
|
||||||
Almost-inverse of _sc_to_tc.
|
Almost-inverse of _sc_to_tc.
|
||||||
|
|
||||||
The function _sc_to_tc is not injective (because of the discontinuity at
|
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
|
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
|
sc_to_tc(`sc`)=`tc`, the smaller sc is chosen. (The smaller one is less
|
||||||
expensive to compute).
|
expensive to compute).
|
||||||
|
@ -89,22 +90,23 @@ def get_contrast(bg, fg):
|
||||||
)
|
)
|
||||||
return (max(lumas) + 0.05) / (min(lumas) + 0.05)
|
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
|
This works by generating an intermediate 3-tuple `t` and transforming it
|
||||||
returned colour are guaranteed to be chosen uniformly from the space of
|
into the returned colour. Channels of `t` are uniformly distributed, but no
|
||||||
possible values.
|
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
|
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
|
relative luminance than `bg`. This is fine if `bg` is dark; if `bg` is not
|
||||||
not dark, the space of possible returned colours will be a lot smaller
|
dark, the space of possible returned colours will be a lot smaller (and
|
||||||
(and might be empty). If `lighter` is false, the returned colour is
|
might be empty). If `lighter` is false, the returned colour is forced to
|
||||||
forced to have a lower relative luminance than `bg`.
|
have a lower relative luminance than `bg`.
|
||||||
|
|
||||||
It's simple to calculate the maximum possible contrast between `bg` and
|
It's simple to calculate the maximum possible contrast between `bg` and any
|
||||||
any other colour. (The minimum contrast is always 1.)
|
other colour. (The minimum contrast is always 1.)
|
||||||
|
|
||||||
>>> bg = (0x23, 0x23, 0x27)
|
>>> bg = (0x23, 0x23, 0x27)
|
||||||
>>> luma = get_relative_luminance(bg)
|
>>> 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
|
>>> 1.05 / (luma + 0.05) # maximum contrast for colours with greater luma
|
||||||
15.657919499763137
|
15.657919499763137
|
||||||
|
|
||||||
There are values of `contrast` for which the space of possible returned
|
There are contrast intervals for which the space of possible returned
|
||||||
colours is empty. For example a `contrast` greater than 21 is always
|
colours is empty. For example a contrast greater than 21 is always
|
||||||
impossible, but the exact upper bound depends on `bg`. The desired
|
impossible, but the exact upper bound depends on `bg`. The desired relative
|
||||||
relative luminance of the returned colour must exist in the interval [0,1].
|
luminance of the returned colour must exist in the interval [0,1]. The
|
||||||
The formula for desired luma is given below.
|
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)
|
>>> bg_luma = get_relative_luminance(bg)
|
||||||
>>> desired_luma = (
|
>>> desired_luma = (
|
||||||
|
@ -131,29 +135,34 @@ def generate_colour(seed, bg, contrast=4.5, lighter=True):
|
||||||
r = random.Random(seed)
|
r = random.Random(seed)
|
||||||
|
|
||||||
if lighter:
|
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:
|
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)
|
V = (0.2126, 0.7152, 0.0722)
|
||||||
indices = [0, 1, 2]
|
indices = [0, 1, 2]
|
||||||
r.shuffle(indices)
|
r.shuffle(indices)
|
||||||
i, j, k = indices
|
i, j, k = indices
|
||||||
|
|
||||||
# V[i] * ci + V[j] * 0 + V[k] * 0 <= desired_luma
|
# V[i] * ci + V[j] * 0 + V[k] * 0 <= max_desired_luma
|
||||||
# V[i] * ci + V[j] * 1 + V[k] * 1 >= desired_luma
|
# V[i] * ci + V[j] * 1 + V[k] * 1 >= min_desired_luma
|
||||||
ci_upper = (desired_luma - V[j] * 0 - V[k] * 0) / V[i]
|
ci_upper = (max_desired_luma - V[j] * 0 - V[k] * 0) / V[i]
|
||||||
ci_lower = (desired_luma - V[j] * 1 - V[k] * 1) / 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))
|
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] * 0 <= max_desired_luma
|
||||||
# V[i] * ci + V[j] * cj + V[k] * 1 >= desired_luma
|
# V[i] * ci + V[j] * cj + V[k] * 1 >= min_desired_luma
|
||||||
cj_upper = (desired_luma - V[i] * ci - V[k] * 0) / V[j]
|
cj_upper = (max_desired_luma - V[i] * ci - V[k] * 0) / V[j]
|
||||||
cj_lower = (desired_luma - V[i] * ci - V[k] * 1) / 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))
|
cj = r.uniform(max(0, cj_lower), min(1, cj_upper))
|
||||||
|
|
||||||
# V[i] * ci + V[j] * cj + V[k] * ck = desired_luma
|
# V[i] * ci + V[j] * cj + V[k] * ck <= max_desired_luma
|
||||||
ck = (desired_luma - V[i] * ci - V[j] * cj) / V[k]
|
# 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 = [None, None, None]
|
||||||
t[i], t[j], t[k] = ci, cj, ck
|
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_darker_contrast = get_maximum_contrast(bg, lighter=False)
|
||||||
|
|
||||||
max_contrast = max(max_lighter_contrast, max_darker_contrast)
|
max_contrast = max(max_lighter_contrast, max_darker_contrast)
|
||||||
|
practical_max_contrast = max_contrast * proportion_of_max
|
||||||
colour = generate_colour(
|
colour = generate_colour(
|
||||||
seed,
|
seed,
|
||||||
bg,
|
bg,
|
||||||
contrast=max_contrast * proportion_of_max,
|
min_contrast=practical_max_contrast,
|
||||||
|
max_contrast=practical_max_contrast,
|
||||||
lighter=max_lighter_contrast > max_darker_contrast,
|
lighter=max_lighter_contrast > max_darker_contrast,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
読み込み中…
新しいイシューから参照