From ce6fdf0ab9e113f02fd41744e2176d5c3b538c48 Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Tue, 22 Apr 2025 17:49:44 +1000 Subject: feat: up to 3 options for dynamic scheme Have up to 3 options for base colour for dynamic scheme Variants moved to new command Also remove parallel dependency --- scheme/autoadjust.py | 80 +++++++++++++++++++++++++++++++------------------- scheme/gen-scheme.fish | 37 ++++++++++++----------- scheme/score.py | 59 ++++++++++++++++++++++++------------- 3 files changed, 106 insertions(+), 70 deletions(-) (limited to 'scheme') diff --git a/scheme/autoadjust.py b/scheme/autoadjust.py index dd007ab..7ee28d7 100755 --- a/scheme/autoadjust.py +++ b/scheme/autoadjust.py @@ -2,6 +2,7 @@ import sys from colorsys import hls_to_rgb, rgb_to_hls +from pathlib import Path from materialyoucolor.dynamiccolor.material_dynamic_colors import ( DynamicScheme, @@ -17,7 +18,6 @@ from materialyoucolor.scheme.scheme_neutral import SchemeNeutral from materialyoucolor.scheme.scheme_rainbow import SchemeRainbow from materialyoucolor.scheme.scheme_tonal_spot import SchemeTonalSpot from materialyoucolor.scheme.scheme_vibrant import SchemeVibrant -from materialyoucolor.utils.color_utils import argb_from_rgb light_colours = [ "dc8a78", @@ -101,7 +101,9 @@ def grayscale(hls: HLS, light: bool) -> HLS: def mix(a: HLS, b: HLS, w: float) -> HLS: r1, g1, b1 = hls_to_rgb(*a) r2, g2, b2 = hls_to_rgb(*b) - return rgb_to_hls(r1 * (1 - w) + r2 * w, g1 * (1 - w) + g2 * w, b1 * (1 - w) + b2 * w) + return rgb_to_hls( + r1 * (1 - w) + r2 * w, g1 * (1 - w) + g2 * w, b1 * (1 - w) + b2 * w + ) def darken(colour: HLS, amount: float) -> HLS: @@ -161,16 +163,23 @@ def get_scheme(scheme: str) -> DynamicScheme: return SchemeVibrant +def get_alt(i: int) -> str: + names = ["default", "alt1", "alt2"] + return names[i] + + if __name__ == "__main__": light = sys.argv[1] == "light" scheme = sys.argv[2] - colours_in = sys.argv[3].split(" ") + primaries = sys.argv[3].split(" ") + colours_in = sys.argv[4].split(" ") + out_path = sys.argv[5] base = light_colours if light else dark_colours chroma_mult = 1.5 if light else 1.2 # Convert to HLS - colours = [hex_to_hls(c) for c in colours_in[1:]] + colours = [hex_to_hls(c) for c in colours_in] # Sort colours and turn into dict colours = smart_sort(colours, base) @@ -192,36 +201,45 @@ if __name__ == "__main__": colour.chroma *= chroma_mult - colours[name] = hex_to_hls("{:02X}{:02X}{:02X}".format(*colour.to_rgba()[:3])) + colours[name] = hex_to_hls( + "{:02X}{:02X}{:02X}".format(*colour.to_rgba()[:3]) + ) # Success and error colours colours["success"] = mix(colours["green"], hex_to_hls(base[8]), 0.8) colours["error"] = mix(colours["red"], hex_to_hls(base[4]), 0.8) # Layers and accents - material = {} - primary_scheme = MatScheme(Hct.from_int(int(f"0xFF{colours_in[0]}", 16)), not light, 0) - for colour in vars(MaterialDynamicColors).keys(): - colour_name = getattr(MaterialDynamicColors, colour) - if hasattr(colour_name, "get_hct"): - rgb = colour_name.get_hct(primary_scheme).to_rgba()[:3] - material[colour] = hex_to_hls("{:02X}{:02X}{:02X}".format(*rgb)) - - colours["primary"] = material["primary"] - colours["secondary"] = material["secondary"] - colours["tertiary"] = material["tertiary"] - colours["text"] = material["onBackground"] - colours["subtext1"] = material["onSurfaceVariant"] - colours["subtext0"] = material["outline"] - colours["overlay2"] = mix(material["surface"], material["outline"], 0.86) - colours["overlay1"] = mix(material["surface"], material["outline"], 0.71) - colours["overlay0"] = mix(material["surface"], material["outline"], 0.57) - colours["surface2"] = mix(material["surface"], material["outline"], 0.43) - colours["surface1"] = mix(material["surface"], material["outline"], 0.29) - colours["surface0"] = mix(material["surface"], material["outline"], 0.14) - colours["base"] = material["surface"] - colours["mantle"] = darken(material["surface"], 0.03) - colours["crust"] = darken(material["surface"], 0.05) - - for name, colour in colours.items(): - print(f"{name} {hls_to_hex(*colour)}") + for i, primary in enumerate(primaries): + material = {} + primary_scheme = MatScheme( + Hct.from_int(int(f"0xFF{primary}", 16)), not light, 0 + ) + for colour in vars(MaterialDynamicColors).keys(): + colour_name = getattr(MaterialDynamicColors, colour) + if hasattr(colour_name, "get_hct"): + rgb = colour_name.get_hct(primary_scheme).to_rgba()[:3] + material[colour] = hex_to_hls("{:02X}{:02X}{:02X}".format(*rgb)) + + colours["primary"] = material["primary"] + colours["secondary"] = material["secondary"] + colours["tertiary"] = material["tertiary"] + colours["text"] = material["onBackground"] + colours["subtext1"] = material["onSurfaceVariant"] + colours["subtext0"] = material["outline"] + colours["overlay2"] = mix(material["surface"], material["outline"], 0.86) + colours["overlay1"] = mix(material["surface"], material["outline"], 0.71) + colours["overlay0"] = mix(material["surface"], material["outline"], 0.57) + colours["surface2"] = mix(material["surface"], material["outline"], 0.43) + colours["surface1"] = mix(material["surface"], material["outline"], 0.29) + colours["surface0"] = mix(material["surface"], material["outline"], 0.14) + colours["base"] = material["surface"] + colours["mantle"] = darken(material["surface"], 0.03) + colours["crust"] = darken(material["surface"], 0.05) + + out_file = Path(f"{out_path}/{scheme}/{get_alt(i)}/{sys.argv[1]}.txt") + out_file.parent.mkdir(parents=True, exist_ok=True) + colour_arr = [ + f"{name} {hls_to_hex(*colour)}" for name, colour in colours.items() + ] + out_file.write_text("\n".join(colour_arr)) diff --git a/scheme/gen-scheme.fish b/scheme/gen-scheme.fish index c034980..488ee27 100755 --- a/scheme/gen-scheme.fish +++ b/scheme/gen-scheme.fish @@ -5,30 +5,29 @@ set -l src (dirname (status filename)) . $src/../util.fish test -f "$argv[1]" && set -l img (realpath "$argv[1]") || set -l img $C_STATE/wallpaper/thumbnail.jpg -contains -- "$argv[2]" light dark && set -l theme $argv[2] || set -l theme dark set -l variants vibrant tonalspot expressive fidelity fruitsalad rainbow neutral content monochrome -set -l hash (sha1sum $img | cut -d ' ' -f 1) +contains -- "$argv[2]" $variants && set -l variant $argv[2] || set -l variant (cat $C_STATE/scheme/current-variant.txt 2> /dev/null) +contains -- "$variant" $variants || set -l variant tonalspot -# Cache schemes -mkdir -p $C_CACHE/schemes -set -l dirty_variants -if test -d $C_CACHE/schemes/$hash - for variant in $variants - test -f $C_CACHE/schemes/$hash/$variant/$theme.txt || set -a dirty_variants $variant - end -else - set dirty_variants $variants -end +set -l hash (sha1sum $img | cut -d ' ' -f 1) -if test -n "$dirty_variants" - # Generate schemes for variants that need it +# Cache scheme +if ! test -d $C_CACHE/schemes/$hash/$variant set -l colours ($src/score.py $img) - parallel "mkdir -p $C_CACHE/schemes/$hash/{} && $src/autoadjust.py $theme {} '$colours' | head -c -1 > $C_CACHE/schemes/$hash/{}/$theme.txt" ::: $dirty_variants + $src/autoadjust.py dark $variant $colours $C_CACHE/schemes/$hash + $src/autoadjust.py light $variant $colours $C_CACHE/schemes/$hash end -# Copy schemes from cache -for variant in $variants - mkdir -p $src/../data/schemes/dynamic/$variant - cp $C_CACHE/schemes/$hash/$variant/$theme.txt $src/../data/schemes/dynamic/$variant/$theme.txt +# Copy scheme from cache +rm -rf $src/../data/schemes/dynamic +cp -r $C_CACHE/schemes/$hash/$variant $src/../data/schemes/dynamic + +# Update if current +set -l variant (string match -gr 'dynamic-(.*)' (cat $C_STATE/scheme/current-name.txt 2> /dev/null)) +if test -n "$variant" + # If variant doesn't exist, use default + test -d $src/../data/schemes/dynamic/$variant || set -l variant default + # Apply scheme + $src/main.fish dynamic $variant $MODE > /dev/null end diff --git a/scheme/score.py b/scheme/score.py index 531046f..92ebbb7 100755 --- a/scheme/score.py +++ b/scheme/score.py @@ -2,10 +2,10 @@ import sys -from materialyoucolor.quantize import ImageQuantizeCelebi -from materialyoucolor.hct import Hct -from materialyoucolor.utils.math_utils import sanitize_degrees_int, difference_degrees from materialyoucolor.dislike.dislike_analyzer import DislikeAnalyzer +from materialyoucolor.hct import Hct +from materialyoucolor.quantize import ImageQuantizeCelebi +from materialyoucolor.utils.math_utils import difference_degrees, sanitize_degrees_int class Score: @@ -20,7 +20,7 @@ class Score: pass @staticmethod - def score(colors_to_population: dict) -> list[int]: + def score(colors_to_population: dict) -> tuple[list[Hct], list[Hct]]: desired = 14 filter_enabled = False dislike_filter = True @@ -72,7 +72,8 @@ class Score: chosen_colors = [] for difference_degrees_ in range(90, 0, -1): chosen_colors.clear() - for hct in [item["hct"] for item in scored_hct]: + for item in scored_hct: + hct = item["hct"] duplicate_hue = any( difference_degrees(hct.hue, chosen_hct.hue) < difference_degrees_ for chosen_hct in chosen_colors @@ -85,31 +86,49 @@ class Score: break # Get primary colour - got_primary = False + primary = None for cutoff in range(20, 0, -1): for item in scored_hct: if item["hct"].chroma > cutoff and item["hct"].tone > cutoff * 3: - chosen_colors.insert(0, item["hct"]) - got_primary = True + primary = item["hct"] + break + if primary: + break + + # Choose distinct primaries + chosen_primaries = [primary] + for difference_degrees_ in range(90, 14, -1): + chosen_primaries = [primary] + for item in scored_hct: + hct = item["hct"] + duplicate_hue = any( + difference_degrees(hct.hue, chosen_hct.hue) < difference_degrees_ + for chosen_hct in chosen_primaries + ) + if not duplicate_hue: + chosen_primaries.append(hct) + if len(chosen_primaries) >= 3: break - if got_primary: + if len(chosen_primaries) >= 3: break # Fix disliked colours if dislike_filter: - for chosen_hct in chosen_colors: - chosen_colors[chosen_colors.index(chosen_hct)] = ( - DislikeAnalyzer.fix_if_disliked(chosen_hct) - ) + for i, chosen_hct in enumerate(chosen_primaries): + chosen_primaries[i] = DislikeAnalyzer.fix_if_disliked(chosen_hct) + for i, chosen_hct in enumerate(chosen_colors): + chosen_colors[i] = DislikeAnalyzer.fix_if_disliked(chosen_hct) - return chosen_colors + return chosen_primaries, chosen_colors if __name__ == "__main__": img = sys.argv[1] - - colours = ImageQuantizeCelebi(img, 1, 128) - colours = [c.to_rgba()[:3] for c in Score.score(colours)] - - # print("".join(["\x1b[48;2;{};{};{}m \x1b[0m".format(*colour) for colour in colours])) - print(" ".join(["{:02X}{:02X}{:02X}".format(*colour) for colour in colours])) + mode = sys.argv[2] if len(sys.argv) > 2 else "hex" + + colours = Score.score(ImageQuantizeCelebi(img, 1, 128)) + for l in colours: + if mode != "hex": + print("".join(["\x1b[48;2;{};{};{}m \x1b[0m".format(*c.to_rgba()[:3]) for c in l])) + if mode != "swatch": + print(" ".join(["{:02X}{:02X}{:02X}".format(*c.to_rgba()[:3]) for c in l])) -- cgit v1.2.3-freya