barwy kategorii

This commit is contained in:
Mateusz Gruszczyński
2025-10-17 23:57:10 +02:00
parent d439002241
commit 5da660b4c3

43
app.py
View File

@@ -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}"