From 072d816aac939a05f3576d570ff3e1dbc2bb3d68 Mon Sep 17 00:00:00 2001 From: ninya9k Date: Wed, 18 Aug 2021 11:10:19 +0000 Subject: [PATCH] fix badly implemented loop, loop less, cache common results; this makes requests by new people >10x faster when there are a lot of existing people --- website/utils/colour.py | 46 ++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/website/utils/colour.py b/website/utils/colour.py index 0e9e9a9..e2d4e97 100644 --- a/website/utils/colour.py +++ b/website/utils/colour.py @@ -1,12 +1,25 @@ import hashlib from website.constants import BACKGROUND_COLOUR +from functools import lru_cache + +# functions for calculating constrast between two colours +# https://www2.cs.sfu.ca/CourseCentral/820/li/material/RGB-YUV.pdf +# https://www.accessibility-developer-guide.com/knowledge/colours-and-contrast/how-to-calculate/ + +def _contrast_numerator(c1): + l1 = (0.299 * c1[0] + 0.587 * c1[1] + 0.114 * c1[2]) / 256 + return l1 + 0.05 + +# this is being cached because it is always called with the colours of existing +# users, and we don't need to do the same float division thousands of times +# just for gen_colour to return a single colour +@lru_cache(maxsize=256) +def _contrast_denominator(c2): + l2 = (0.299 * c2[0] + 0.587 * c2[1] + 0.114 * c2[2]) / 256 + return 1 / (l2 + 0.05) def _contrast(c1, c2): - # https://www2.cs.sfu.ca/CourseCentral/820/li/material/RGB-YUV.pdf - l1 = (0.299 * c1[0] + 0.587 * c1[1] + 0.114 * c1[2]) / 256 - l2 = (0.299 * c2[0] + 0.587 * c2[1] + 0.114 * c2[2]) / 256 - # https://www.accessibility-developer-guide.com/knowledge/colours-and-contrast/how-to-calculate/ - return (l1 + 0.05) / (l2 + 0.05) + return _contrast_numerator(c1) * _contrast_denominator(c2) def _distance_sq(c1, c2): return sum((i / 256 - j / 256) ** 2 for i, j in zip(c1, c2)) @@ -15,12 +28,13 @@ def _gen_colour(seed, background=BACKGROUND_COLOUR): ''' Returns a colour with sufficient contrast to the background colour ''' - while True: + for _ in range(16): # this loop exits on the first iteration 99.99% of the time (literally) seed = hashlib.sha256(seed).digest() for i in range(0, len(seed) - len(seed) % 3, 3): colour = seed[i:i+3] if 1.5 < _contrast(colour, background) < 3: - return colour + break + return colour, seed def gen_colour(seed, background=BACKGROUND_COLOUR, *avoid): ''' @@ -29,22 +43,20 @@ def gen_colour(seed, background=BACKGROUND_COLOUR, *avoid): This function hasn't been analysed for efficiency or anything ''' best_colour, best_score = None, None - for _ in range(16384): - colour = _gen_colour(seed, background) - score = float('inf') if len(avoid) == 0 else sum(_contrast(colour, c) for c in avoid) / len(avoid) - if colour in avoid: + for _ in range(1024): + colour, seed = _gen_colour(seed, background) + if len(avoid) == 0: + score = float('inf') + elif colour in avoid: score = float('-inf') + else: + score = sum(_contrast(colour, c) for c in avoid) / len(avoid) if 2.5 < score: return colour if best_score == None or score > best_score: - best_colour = colour + best_colour, best_score = colour, score return best_colour -# old-style tag generation: similar colours make similar tags -#def tag(colour): -# tag = ((colour[2] & 0xf0) >> 4) | (colour[1] & 0xf0) | ((colour[0] & 0xf0) << 4) -# return f'#{tag:03x}' - def tag(token, length=3): ''' Generates a deterministic pseudorandom tag from a given token