From 5da660b4c30dc564ee811c860a9c170d13c62854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Fri, 17 Oct 2025 23:57:10 +0200 Subject: [PATCH] barwy kategorii --- app.py | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/app.py b/app.py index 6ca3a99..174116e 100644 --- a/app.py +++ b/app.py @@ -885,29 +885,40 @@ def get_admin_expense_summary(): } -# pip install hsluv -import hashlib -import hsluv # HSLuv - human-friendly, approx. perceptually uniform +import hashlib, colorsys -def category_to_color_hsluv(name: str) -> str: +def category_to_color(name: str, min_hue_gap_deg: int = 18) -> str: + # Stabilny hash -> int hv = int(hashlib.md5(name.encode("utf-8")).hexdigest(), 16) - # Pełne pokrycie hue 0..360 - h = hv % 360 + # Proste, ale skuteczne mieszanie bitów, by uniknąć lokalnych skupień + def rotl(x, r, bits=128): + r %= bits + return ((x << r) | (x >> (bits - r))) & ((1 << bits) - 1) - # Trzymaj stałe L i S (HSLuv: S≈chroma), dobrane dla dobrej widoczności - # L w [50..65] jest uniwersalne na jasnym i ciemnym tle - l = 60.0 - s = 85.0 + mix = hv ^ rotl(hv, 37) ^ rotl(hv, 73) ^ rotl(hv, 91) - # Drobna, ograniczona wariacja (±3) aby rozbić kolizje - s += ((hv >> 17) % 7) - 3 - l += ((hv >> 23) % 7) - 3 + # Pełne pokrycie koła barw 0..360 + hue_deg = mix % 360 - s = min(95.0, max(70.0, s)) - l = min(68.0, max(52.0, l)) + # Odpychanie lokalne: mała losowa korekta hue, aby podobne nazwy nie dawały prawie identycznych kolorów + gap = (rotl(mix, 17) % (2*min_hue_gap_deg)) - min_hue_gap_deg # w zakresie [-gap, +gap] + hue_deg = (hue_deg + gap) % 360 + + # Stałe, „bezpieczne” S i L dla dobrej rozróżnialności + s = 0.78 + l = 0.55 + + # Minimalna wariacja S/L (±0.03..0.04), aby dodatkowo różnicować podobne hue + s_var = ((rotl(mix, 29) % 7) - 3) / 100.0 + l_var = ((rotl(mix, 53) % 7) - 3) / 100.0 + s = min(0.9, max(0.7, s + s_var)) + l = min(0.65, max(0.48, l + l_var)) + + # colorsys.hls_to_rgb używa H,L,S w [0..1], więc hue trzeba przeskalować + h = hue_deg / 360.0 # [0..1] + r, g, b = colorsys.hls_to_rgb(h, l, s) - r, g, b = hsluv.hsluv_to_rgb([h, s, l]) # zwraca RGB w [0..1] return f"#{int(round(r*255)):02x}{int(round(g*255)):02x}{int(round(b*255)):02x}"