From 194826efaa95480bfe1799f889ca5c02571b3e36 Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Thu, 12 Jun 2025 15:48:02 +1000 Subject: feat: generate dynamic schemes --- src/caelestia/utils/material/__init__.py | 52 ++++++++ src/caelestia/utils/material/generator.py | 192 ++++++++++++++++++++++++++++++ src/caelestia/utils/material/score.py | 129 ++++++++++++++++++++ 3 files changed, 373 insertions(+) create mode 100644 src/caelestia/utils/material/__init__.py create mode 100755 src/caelestia/utils/material/generator.py create mode 100755 src/caelestia/utils/material/score.py (limited to 'src/caelestia/utils/material') diff --git a/src/caelestia/utils/material/__init__.py b/src/caelestia/utils/material/__init__.py new file mode 100644 index 0000000..8adab1f --- /dev/null +++ b/src/caelestia/utils/material/__init__.py @@ -0,0 +1,52 @@ +import json +from pathlib import Path + +from materialyoucolor.hct import Hct + +from caelestia.utils.material.generator import gen_scheme +from caelestia.utils.material.score import score +from caelestia.utils.paths import compute_hash, scheme_cache_dir, wallpaper_thumbnail_path + + +def get_score_for_image(image: str, cache_base: Path) -> tuple[list[Hct], list[Hct]]: + cache = cache_base / "score.json" + + try: + with cache.open("r") as f: + return [[Hct.from_int(c) for c in li] for li in json.load(f)] + except (IOError, json.JSONDecodeError): + pass + + s = score(image) + + cache.parent.mkdir(parents=True, exist_ok=True) + with cache.open("w") as f: + json.dump([[c.to_int() for c in li] for li in s], f) + + return s + + +def get_colours_for_image(image: str = str(wallpaper_thumbnail_path), scheme=None) -> dict[str, str]: + if scheme is None: + from caelestia.utils.scheme import get_scheme + + scheme = get_scheme() + + cache_base = scheme_cache_dir / compute_hash(image) + cache = (cache_base / scheme.variant / scheme.flavour / scheme.mode).with_suffix(".json") + + try: + with cache.open("r") as f: + return json.load(f) + except (IOError, json.JSONDecodeError): + pass + + primaries, colours = get_score_for_image(image, cache_base) + i = ["default", "alt1", "alt2"].index(scheme.flavour) + scheme = gen_scheme(scheme, primaries[i], colours) + + cache.parent.mkdir(parents=True, exist_ok=True) + with cache.open("w") as f: + json.dump(scheme, f) + + return scheme diff --git a/src/caelestia/utils/material/generator.py b/src/caelestia/utils/material/generator.py new file mode 100755 index 0000000..33ff0e8 --- /dev/null +++ b/src/caelestia/utils/material/generator.py @@ -0,0 +1,192 @@ +from materialyoucolor.blend import Blend +from materialyoucolor.dynamiccolor.material_dynamic_colors import ( + DynamicScheme, + MaterialDynamicColors, +) +from materialyoucolor.hct import Hct +from materialyoucolor.hct.cam16 import Cam16 +from materialyoucolor.scheme.scheme_content import SchemeContent +from materialyoucolor.scheme.scheme_expressive import SchemeExpressive +from materialyoucolor.scheme.scheme_fidelity import SchemeFidelity +from materialyoucolor.scheme.scheme_fruit_salad import SchemeFruitSalad +from materialyoucolor.scheme.scheme_monochrome import SchemeMonochrome +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 + + +def hex_to_hct(hex: str) -> Hct: + return Hct.from_int(int(f"0xFF{hex}", 16)) + + +light_colours = [ + hex_to_hct("dc8a78"), + hex_to_hct("dd7878"), + hex_to_hct("ea76cb"), + hex_to_hct("8839ef"), + hex_to_hct("d20f39"), + hex_to_hct("e64553"), + hex_to_hct("fe640b"), + hex_to_hct("df8e1d"), + hex_to_hct("40a02b"), + hex_to_hct("179299"), + hex_to_hct("04a5e5"), + hex_to_hct("209fb5"), + hex_to_hct("1e66f5"), + hex_to_hct("7287fd"), +] + +dark_colours = [ + hex_to_hct("f5e0dc"), + hex_to_hct("f2cdcd"), + hex_to_hct("f5c2e7"), + hex_to_hct("cba6f7"), + hex_to_hct("f38ba8"), + hex_to_hct("eba0ac"), + hex_to_hct("fab387"), + hex_to_hct("f9e2af"), + hex_to_hct("a6e3a1"), + hex_to_hct("94e2d5"), + hex_to_hct("89dceb"), + hex_to_hct("74c7ec"), + hex_to_hct("89b4fa"), + hex_to_hct("b4befe"), +] + +colour_names = [ + "rosewater", + "flamingo", + "pink", + "mauve", + "red", + "maroon", + "peach", + "yellow", + "green", + "teal", + "sky", + "sapphire", + "blue", + "lavender", + "success", + "error", +] + + +def grayscale(colour: Hct, light: bool) -> None: + colour.chroma = 0 + + +def mix(a: Hct, b: Hct, w: float) -> Hct: + return Hct.from_int(Blend.cam16_ucs(a.to_int(), b.to_int(), w)) + + +def harmonize(a: Hct, b: Hct) -> Hct: + return Hct.from_int(Blend.harmonize(a.to_int(), b.to_int())) + + +def lighten(colour: Hct, amount: float) -> Hct: + diff = (100 - colour.tone) * amount + return Hct.from_hct(colour.hue, colour.chroma + diff / 2, colour.tone + diff) + + +def darken(colour: Hct, amount: float) -> Hct: + diff = colour.tone * amount + return Hct.from_hct(colour.hue, colour.chroma + diff / 2, colour.tone - diff) + + +def distance(colour: Cam16, base: Cam16) -> float: + return colour.distance(base) + + +def smart_sort(colours: list[Hct], base: list[Hct]) -> dict[str, Hct]: + sorted_colours = [None] * len(colours) + distances = {} + + cams = [(c, Cam16.from_int(c.to_int())) for c in colours] + base_cams = [Cam16.from_int(c.to_int()) for c in base] + + for colour, cam in cams: + dist = [(i, distance(cam, b)) for i, b in enumerate(base_cams)] + dist.sort(key=lambda x: x[1]) + distances[colour] = dist + + for colour in colours: + while len(distances[colour]) > 0: + i, dist = distances[colour][0] + + if sorted_colours[i] is None: + sorted_colours[i] = colour, dist + break + elif sorted_colours[i][1] > dist: + old = sorted_colours[i][0] + sorted_colours[i] = colour, dist + colour = old + + distances[colour].pop(0) + + return {colour_names[i]: c[0] for i, c in enumerate(sorted_colours)} + + +def get_scheme(scheme: str) -> DynamicScheme: + if scheme == "content": + return SchemeContent + if scheme == "expressive": + return SchemeExpressive + if scheme == "fidelity": + return SchemeFidelity + if scheme == "fruitsalad": + return SchemeFruitSalad + if scheme == "monochrome": + return SchemeMonochrome + if scheme == "neutral": + return SchemeNeutral + if scheme == "rainbow": + return SchemeRainbow + if scheme == "tonalspot": + return SchemeTonalSpot + return SchemeVibrant + + +def gen_scheme(scheme, primary: Hct, colours: list[Hct]) -> dict[str, str]: + light = scheme.mode == "light" + base = light_colours if light else dark_colours + + # Sort colours and turn into dict + colours = smart_sort(colours, base) + + # Harmonize colours + for name, hct in colours.items(): + if scheme.variant == "monochrome": + grayscale(hct, light) + else: + harmonized = harmonize(hct, primary) + colours[name] = darken(harmonized, 0.35) if light else lighten(harmonized, 0.65) + + # Material colours + primary_scheme = get_scheme(scheme.variant)(primary, not light, 0) + for colour in vars(MaterialDynamicColors).keys(): + colour_name = getattr(MaterialDynamicColors, colour) + if hasattr(colour_name, "get_hct"): + colours[colour] = colour_name.get_hct(primary_scheme) + + # FIXME: deprecated stuff + colours["text"] = colours["onBackground"] + colours["subtext1"] = colours["onSurfaceVariant"] + colours["subtext0"] = colours["outline"] + colours["overlay2"] = mix(colours["surface"], colours["outline"], 0.86) + colours["overlay1"] = mix(colours["surface"], colours["outline"], 0.71) + colours["overlay0"] = mix(colours["surface"], colours["outline"], 0.57) + colours["surface2"] = mix(colours["surface"], colours["outline"], 0.43) + colours["surface1"] = mix(colours["surface"], colours["outline"], 0.29) + colours["surface0"] = mix(colours["surface"], colours["outline"], 0.14) + colours["base"] = colours["surface"] + colours["mantle"] = darken(colours["surface"], 0.03) + colours["crust"] = darken(colours["surface"], 0.05) + colours["success"] = harmonize(base[8], primary) + + # For debugging + print("\n".join(["{}: \x1b[48;2;{};{};{}m \x1b[0m".format(n, *c.to_rgba()[:3]) for n, c in colours.items()])) + + return {k: hex(v.to_int())[4:] for k, v in colours.items()} diff --git a/src/caelestia/utils/material/score.py b/src/caelestia/utils/material/score.py new file mode 100755 index 0000000..da8b062 --- /dev/null +++ b/src/caelestia/utils/material/score.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python + +import sys + +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: + TARGET_CHROMA = 48.0 + WEIGHT_PROPORTION = 0.7 + WEIGHT_CHROMA_ABOVE = 0.3 + WEIGHT_CHROMA_BELOW = 0.1 + CUTOFF_CHROMA = 5.0 + CUTOFF_EXCITED_PROPORTION = 0.01 + + def __init__(self): + pass + + @staticmethod + def score(colors_to_population: dict) -> tuple[list[Hct], list[Hct]]: + desired = 14 + filter_enabled = True + dislike_filter = True + + colors_hct = [] + hue_population = [0] * 360 + population_sum = 0 + + for rgb, population in colors_to_population.items(): + hct = Hct.from_int(rgb) + colors_hct.append(hct) + hue = int(hct.hue) + hue_population[hue] += population + population_sum += population + + hue_excited_proportions = [0.0] * 360 + + for hue in range(360): + proportion = hue_population[hue] / population_sum + for i in range(hue - 14, hue + 16): + neighbor_hue = int(sanitize_degrees_int(i)) + hue_excited_proportions[neighbor_hue] += proportion + + # Score colours + scored_hct = [] + for hct in colors_hct: + hue = int(sanitize_degrees_int(round(hct.hue))) + proportion = hue_excited_proportions[hue] + + if filter_enabled and (hct.chroma < Score.CUTOFF_CHROMA or proportion <= Score.CUTOFF_EXCITED_PROPORTION): + continue + + proportion_score = proportion * 100.0 * Score.WEIGHT_PROPORTION + chroma_weight = Score.WEIGHT_CHROMA_BELOW if hct.chroma < Score.TARGET_CHROMA else Score.WEIGHT_CHROMA_ABOVE + chroma_score = (hct.chroma - Score.TARGET_CHROMA) * chroma_weight + score = proportion_score + chroma_score + scored_hct.append({"hct": hct, "score": score}) + + scored_hct.sort(key=lambda x: x["score"], reverse=True) + + # Choose distinct colours + chosen_colors = [] + for difference_degrees_ in range(90, 0, -1): + chosen_colors.clear() + 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 + ) + if not duplicate_hue: + chosen_colors.append(hct) + if len(chosen_colors) >= desired: + break + if len(chosen_colors) >= desired: + break + + # Get primary colour + 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: + 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 len(chosen_primaries) >= 3: + break + + # Fix disliked colours + if dislike_filter: + 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_primaries, chosen_colors + + +def score(image: str) -> tuple[list[Hct], list[Hct]]: + return Score.score(ImageQuantizeCelebi(image, 1, 128)) + + +if __name__ == "__main__": + img = sys.argv[1] + mode = sys.argv[2] if len(sys.argv) > 2 else "hex" + + colours = Score.score(ImageQuantizeCelebi(img, 1, 128)) + for t in colours: + if mode != "hex": + print("".join(["\x1b[48;2;{};{};{}m \x1b[0m".format(*c.to_rgba()[:3]) for c in t])) + if mode != "swatch": + print(" ".join(["{:02X}{:02X}{:02X}".format(*c.to_rgba()[:3]) for c in t])) -- cgit v1.2.3-freya From 672ef4a2d9291fb4333e6d6aa807826d6860259a Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Thu, 12 Jun 2025 16:00:43 +1000 Subject: scheme: impl random + fix single schemes --- src/caelestia/data/schemes/oldworld/dark.txt | 81 ---------------------- .../data/schemes/oldworld/default/dark.txt | 81 ++++++++++++++++++++++ src/caelestia/data/schemes/onedark/dark.txt | 81 ---------------------- .../data/schemes/onedark/default/dark.txt | 81 ++++++++++++++++++++++ src/caelestia/data/schemes/shadotheme/dark.txt | 81 ---------------------- .../data/schemes/shadotheme/default/dark.txt | 81 ++++++++++++++++++++++ src/caelestia/subcommands/scheme.py | 1 + src/caelestia/utils/material/generator.py | 2 +- src/caelestia/utils/scheme.py | 8 +++ 9 files changed, 253 insertions(+), 244 deletions(-) delete mode 100644 src/caelestia/data/schemes/oldworld/dark.txt create mode 100644 src/caelestia/data/schemes/oldworld/default/dark.txt delete mode 100644 src/caelestia/data/schemes/onedark/dark.txt create mode 100644 src/caelestia/data/schemes/onedark/default/dark.txt delete mode 100644 src/caelestia/data/schemes/shadotheme/dark.txt create mode 100644 src/caelestia/data/schemes/shadotheme/default/dark.txt (limited to 'src/caelestia/utils/material') diff --git a/src/caelestia/data/schemes/oldworld/dark.txt b/src/caelestia/data/schemes/oldworld/dark.txt deleted file mode 100644 index 846dc18..0000000 --- a/src/caelestia/data/schemes/oldworld/dark.txt +++ /dev/null @@ -1,81 +0,0 @@ -rosewater f4d3d3 -flamingo edbab5 -pink e29eca -mauve c99ee2 -red ea83a5 -maroon f49eba -peach f5a191 -yellow e6b99d -green 90b99f -teal 85b5ba -sky 69b6e0 -sapphire 5b9fba -blue 92a2d5 -lavender aca1cf -text c9c7cd -subtext1 9998a8 -subtext0 757581 -overlay2 57575f -overlay1 3e3e43 -overlay0 353539 -surface2 2a2a2d -surface1 27272a -surface0 1b1b1d -base 161617 -mantle 131314 -crust 101011 -success 90b99f -primary_paletteKeyColor 5A77AB -secondary_paletteKeyColor 6E778A -tertiary_paletteKeyColor 8A6E8E -neutral_paletteKeyColor 76777D -neutral_variant_paletteKeyColor 74777F -background 111318 -onBackground E2E2E9 -surface 111318 -surfaceDim 111318 -surfaceBright 37393E -surfaceContainerLowest 0C0E13 -surfaceContainerLow 191C20 -surfaceContainer 1E2025 -surfaceContainerHigh 282A2F -surfaceContainerHighest 33353A -onSurface E2E2E9 -surfaceVariant 44474E -onSurfaceVariant C4C6D0 -inverseSurface E2E2E9 -inverseOnSurface 2E3036 -outline 8E9099 -outlineVariant 44474E -shadow 000000 -scrim 000000 -surfaceTint ABC7FF -primary ABC7FF -onPrimary 0B305F -primaryContainer 284777 -onPrimaryContainer D7E3FF -inversePrimary 415E91 -secondary BEC6DC -onSecondary 283141 -secondaryContainer 3E4759 -onSecondaryContainer DAE2F9 -tertiary DDBCE0 -onTertiary 3F2844 -tertiaryContainer A587A9 -onTertiaryContainer 000000 -error FFB4AB -onError 690005 -errorContainer 93000A -onErrorContainer FFDAD6 -primaryFixed D7E3FF -primaryFixedDim ABC7FF -onPrimaryFixed 001B3F -onPrimaryFixedVariant 284777 -secondaryFixed DAE2F9 -secondaryFixedDim BEC6DC -onSecondaryFixed 131C2B -onSecondaryFixedVariant 3E4759 -tertiaryFixed FAD8FD -tertiaryFixedDim DDBCE0 -onTertiaryFixed 28132E -onTertiaryFixedVariant 573E5C \ No newline at end of file diff --git a/src/caelestia/data/schemes/oldworld/default/dark.txt b/src/caelestia/data/schemes/oldworld/default/dark.txt new file mode 100644 index 0000000..846dc18 --- /dev/null +++ b/src/caelestia/data/schemes/oldworld/default/dark.txt @@ -0,0 +1,81 @@ +rosewater f4d3d3 +flamingo edbab5 +pink e29eca +mauve c99ee2 +red ea83a5 +maroon f49eba +peach f5a191 +yellow e6b99d +green 90b99f +teal 85b5ba +sky 69b6e0 +sapphire 5b9fba +blue 92a2d5 +lavender aca1cf +text c9c7cd +subtext1 9998a8 +subtext0 757581 +overlay2 57575f +overlay1 3e3e43 +overlay0 353539 +surface2 2a2a2d +surface1 27272a +surface0 1b1b1d +base 161617 +mantle 131314 +crust 101011 +success 90b99f +primary_paletteKeyColor 5A77AB +secondary_paletteKeyColor 6E778A +tertiary_paletteKeyColor 8A6E8E +neutral_paletteKeyColor 76777D +neutral_variant_paletteKeyColor 74777F +background 111318 +onBackground E2E2E9 +surface 111318 +surfaceDim 111318 +surfaceBright 37393E +surfaceContainerLowest 0C0E13 +surfaceContainerLow 191C20 +surfaceContainer 1E2025 +surfaceContainerHigh 282A2F +surfaceContainerHighest 33353A +onSurface E2E2E9 +surfaceVariant 44474E +onSurfaceVariant C4C6D0 +inverseSurface E2E2E9 +inverseOnSurface 2E3036 +outline 8E9099 +outlineVariant 44474E +shadow 000000 +scrim 000000 +surfaceTint ABC7FF +primary ABC7FF +onPrimary 0B305F +primaryContainer 284777 +onPrimaryContainer D7E3FF +inversePrimary 415E91 +secondary BEC6DC +onSecondary 283141 +secondaryContainer 3E4759 +onSecondaryContainer DAE2F9 +tertiary DDBCE0 +onTertiary 3F2844 +tertiaryContainer A587A9 +onTertiaryContainer 000000 +error FFB4AB +onError 690005 +errorContainer 93000A +onErrorContainer FFDAD6 +primaryFixed D7E3FF +primaryFixedDim ABC7FF +onPrimaryFixed 001B3F +onPrimaryFixedVariant 284777 +secondaryFixed DAE2F9 +secondaryFixedDim BEC6DC +onSecondaryFixed 131C2B +onSecondaryFixedVariant 3E4759 +tertiaryFixed FAD8FD +tertiaryFixedDim DDBCE0 +onTertiaryFixed 28132E +onTertiaryFixedVariant 573E5C \ No newline at end of file diff --git a/src/caelestia/data/schemes/onedark/dark.txt b/src/caelestia/data/schemes/onedark/dark.txt deleted file mode 100644 index 269096e..0000000 --- a/src/caelestia/data/schemes/onedark/dark.txt +++ /dev/null @@ -1,81 +0,0 @@ -rosewater edcbc5 -flamingo d3a4a4 -pink d792c6 -mauve c678dd -red be5046 -maroon e06c75 -peach d19a66 -yellow e5c07b -green 98c379 -teal 56b6c2 -sky 90ccd7 -sapphire 389dcc -blue 61afef -lavender 8e98d9 -text abb2bf -subtext1 95a0b5 -subtext0 838b9c -overlay2 767f8f -overlay1 666e7c -overlay0 5c6370 -surface2 4b5263 -surface1 3c414f -surface0 30343e -base 282c34 -mantle 21242b -crust 1e2126 -success 98c379 -primary_paletteKeyColor 5878AB -secondary_paletteKeyColor 6E778A -tertiary_paletteKeyColor 896E8F -neutral_paletteKeyColor 75777D -neutral_variant_paletteKeyColor 74777F -background 111318 -onBackground E1E2E9 -surface 111318 -surfaceDim 111318 -surfaceBright 37393E -surfaceContainerLowest 0C0E13 -surfaceContainerLow 191C20 -surfaceContainer 1D2024 -surfaceContainerHigh 282A2F -surfaceContainerHighest 33353A -onSurface E1E2E9 -surfaceVariant 43474E -onSurfaceVariant C4C6CF -inverseSurface E1E2E9 -inverseOnSurface 2E3035 -outline 8E9099 -outlineVariant 43474E -shadow 000000 -scrim 000000 -surfaceTint A8C8FF -primary A8C8FF -onPrimary 06305F -primaryContainer 254777 -onPrimaryContainer D5E3FF -inversePrimary 3F5F90 -secondary BDC7DC -onSecondary 273141 -secondaryContainer 40495B -onSecondaryContainer D9E3F8 -tertiary DBBCE1 -onTertiary 3E2845 -tertiaryContainer A387AA -onTertiaryContainer 000000 -error FFB4AB -onError 690005 -errorContainer 93000A -onErrorContainer FFDAD6 -primaryFixed D5E3FF -primaryFixedDim A8C8FF -onPrimaryFixed 001B3C -onPrimaryFixedVariant 254777 -secondaryFixed D9E3F8 -secondaryFixedDim BDC7DC -onSecondaryFixed 121C2B -onSecondaryFixedVariant 3E4758 -tertiaryFixed F8D8FE -tertiaryFixedDim DBBCE1 -onTertiaryFixed 28132F -onTertiaryFixedVariant 563E5D \ No newline at end of file diff --git a/src/caelestia/data/schemes/onedark/default/dark.txt b/src/caelestia/data/schemes/onedark/default/dark.txt new file mode 100644 index 0000000..269096e --- /dev/null +++ b/src/caelestia/data/schemes/onedark/default/dark.txt @@ -0,0 +1,81 @@ +rosewater edcbc5 +flamingo d3a4a4 +pink d792c6 +mauve c678dd +red be5046 +maroon e06c75 +peach d19a66 +yellow e5c07b +green 98c379 +teal 56b6c2 +sky 90ccd7 +sapphire 389dcc +blue 61afef +lavender 8e98d9 +text abb2bf +subtext1 95a0b5 +subtext0 838b9c +overlay2 767f8f +overlay1 666e7c +overlay0 5c6370 +surface2 4b5263 +surface1 3c414f +surface0 30343e +base 282c34 +mantle 21242b +crust 1e2126 +success 98c379 +primary_paletteKeyColor 5878AB +secondary_paletteKeyColor 6E778A +tertiary_paletteKeyColor 896E8F +neutral_paletteKeyColor 75777D +neutral_variant_paletteKeyColor 74777F +background 111318 +onBackground E1E2E9 +surface 111318 +surfaceDim 111318 +surfaceBright 37393E +surfaceContainerLowest 0C0E13 +surfaceContainerLow 191C20 +surfaceContainer 1D2024 +surfaceContainerHigh 282A2F +surfaceContainerHighest 33353A +onSurface E1E2E9 +surfaceVariant 43474E +onSurfaceVariant C4C6CF +inverseSurface E1E2E9 +inverseOnSurface 2E3035 +outline 8E9099 +outlineVariant 43474E +shadow 000000 +scrim 000000 +surfaceTint A8C8FF +primary A8C8FF +onPrimary 06305F +primaryContainer 254777 +onPrimaryContainer D5E3FF +inversePrimary 3F5F90 +secondary BDC7DC +onSecondary 273141 +secondaryContainer 40495B +onSecondaryContainer D9E3F8 +tertiary DBBCE1 +onTertiary 3E2845 +tertiaryContainer A387AA +onTertiaryContainer 000000 +error FFB4AB +onError 690005 +errorContainer 93000A +onErrorContainer FFDAD6 +primaryFixed D5E3FF +primaryFixedDim A8C8FF +onPrimaryFixed 001B3C +onPrimaryFixedVariant 254777 +secondaryFixed D9E3F8 +secondaryFixedDim BDC7DC +onSecondaryFixed 121C2B +onSecondaryFixedVariant 3E4758 +tertiaryFixed F8D8FE +tertiaryFixedDim DBBCE1 +onTertiaryFixed 28132F +onTertiaryFixedVariant 563E5D \ No newline at end of file diff --git a/src/caelestia/data/schemes/shadotheme/dark.txt b/src/caelestia/data/schemes/shadotheme/dark.txt deleted file mode 100644 index e178804..0000000 --- a/src/caelestia/data/schemes/shadotheme/dark.txt +++ /dev/null @@ -1,81 +0,0 @@ -rosewater f1c4e0 -flamingo F18FB0 -pink a8899c -mauve E9729D -red B52A5B -maroon FF4971 -peach ff79c6 -yellow 8897F4 -green 6a5acd -teal F18FB0 -sky 4484d1 -sapphire 2f77a1 -blue bd93f9 -lavender 849BE0 -text e3c7fc -subtext1 CBB2E1 -subtext0 B39DC7 -overlay2 9A88AC -overlay1 827392 -overlay0 6A5D77 -surface2 52485D -surface1 393342 -surface0 211E28 -base 09090d -mantle 060608 -crust 030304 -success 37d4a7 -primary_paletteKeyColor 6F72AC -secondary_paletteKeyColor 75758B -tertiary_paletteKeyColor 936B83 -neutral_paletteKeyColor 78767D -neutral_variant_paletteKeyColor 777680 -background 131318 -onBackground E4E1E9 -surface 131318 -surfaceDim 131318 -surfaceBright 39383F -surfaceContainerLowest 0E0E13 -surfaceContainerLow 1B1B21 -surfaceContainer 1F1F25 -surfaceContainerHigh 2A292F -surfaceContainerHighest 35343A -onSurface E4E1E9 -surfaceVariant 46464F -onSurfaceVariant C7C5D0 -inverseSurface E4E1E9 -inverseOnSurface 303036 -outline 918F9A -outlineVariant 46464F -shadow 000000 -scrim 000000 -surfaceTint BFC1FF -primary BFC1FF -onPrimary 282B60 -primaryContainer 3F4178 -onPrimaryContainer E1E0FF -inversePrimary 565992 -secondary C5C4DD -onSecondary 2E2F42 -secondaryContainer 47475B -onSecondaryContainer E2E0F9 -tertiary E8B9D4 -onTertiary 46263B -tertiaryContainer AF849D -onTertiaryContainer 000000 -error FFB4AB -onError 690005 -errorContainer 93000A -onErrorContainer FFDAD6 -primaryFixed E1E0FF -primaryFixedDim BFC1FF -onPrimaryFixed 12144B -onPrimaryFixedVariant 3F4178 -secondaryFixed E2E0F9 -secondaryFixedDim C5C4DD -onSecondaryFixed 191A2C -onSecondaryFixedVariant 454559 -tertiaryFixed FFD8ED -tertiaryFixedDim E8B9D4 -onTertiaryFixed 2E1125 -onTertiaryFixedVariant 5F3C52 \ No newline at end of file diff --git a/src/caelestia/data/schemes/shadotheme/default/dark.txt b/src/caelestia/data/schemes/shadotheme/default/dark.txt new file mode 100644 index 0000000..e178804 --- /dev/null +++ b/src/caelestia/data/schemes/shadotheme/default/dark.txt @@ -0,0 +1,81 @@ +rosewater f1c4e0 +flamingo F18FB0 +pink a8899c +mauve E9729D +red B52A5B +maroon FF4971 +peach ff79c6 +yellow 8897F4 +green 6a5acd +teal F18FB0 +sky 4484d1 +sapphire 2f77a1 +blue bd93f9 +lavender 849BE0 +text e3c7fc +subtext1 CBB2E1 +subtext0 B39DC7 +overlay2 9A88AC +overlay1 827392 +overlay0 6A5D77 +surface2 52485D +surface1 393342 +surface0 211E28 +base 09090d +mantle 060608 +crust 030304 +success 37d4a7 +primary_paletteKeyColor 6F72AC +secondary_paletteKeyColor 75758B +tertiary_paletteKeyColor 936B83 +neutral_paletteKeyColor 78767D +neutral_variant_paletteKeyColor 777680 +background 131318 +onBackground E4E1E9 +surface 131318 +surfaceDim 131318 +surfaceBright 39383F +surfaceContainerLowest 0E0E13 +surfaceContainerLow 1B1B21 +surfaceContainer 1F1F25 +surfaceContainerHigh 2A292F +surfaceContainerHighest 35343A +onSurface E4E1E9 +surfaceVariant 46464F +onSurfaceVariant C7C5D0 +inverseSurface E4E1E9 +inverseOnSurface 303036 +outline 918F9A +outlineVariant 46464F +shadow 000000 +scrim 000000 +surfaceTint BFC1FF +primary BFC1FF +onPrimary 282B60 +primaryContainer 3F4178 +onPrimaryContainer E1E0FF +inversePrimary 565992 +secondary C5C4DD +onSecondary 2E2F42 +secondaryContainer 47475B +onSecondaryContainer E2E0F9 +tertiary E8B9D4 +onTertiary 46263B +tertiaryContainer AF849D +onTertiaryContainer 000000 +error FFB4AB +onError 690005 +errorContainer 93000A +onErrorContainer FFDAD6 +primaryFixed E1E0FF +primaryFixedDim BFC1FF +onPrimaryFixed 12144B +onPrimaryFixedVariant 3F4178 +secondaryFixed E2E0F9 +secondaryFixedDim C5C4DD +onSecondaryFixed 191A2C +onSecondaryFixedVariant 454559 +tertiaryFixed FFD8ED +tertiaryFixedDim E8B9D4 +onTertiaryFixed 2E1125 +onTertiaryFixedVariant 5F3C52 \ No newline at end of file diff --git a/src/caelestia/subcommands/scheme.py b/src/caelestia/subcommands/scheme.py index e149d13..973cfce 100644 --- a/src/caelestia/subcommands/scheme.py +++ b/src/caelestia/subcommands/scheme.py @@ -15,6 +15,7 @@ class Command: if self.args.random: scheme.set_random() + apply_colours(scheme.colours, scheme.mode) elif self.args.name or self.args.flavour or self.args.mode: if self.args.name: scheme.name = self.args.name diff --git a/src/caelestia/utils/material/generator.py b/src/caelestia/utils/material/generator.py index 33ff0e8..235b2ce 100755 --- a/src/caelestia/utils/material/generator.py +++ b/src/caelestia/utils/material/generator.py @@ -187,6 +187,6 @@ def gen_scheme(scheme, primary: Hct, colours: list[Hct]) -> dict[str, str]: colours["success"] = harmonize(base[8], primary) # For debugging - print("\n".join(["{}: \x1b[48;2;{};{};{}m \x1b[0m".format(n, *c.to_rgba()[:3]) for n, c in colours.items()])) + # print("\n".join(["{}: \x1b[48;2;{};{};{}m \x1b[0m".format(n, *c.to_rgba()[:3]) for n, c in colours.items()])) return {k: hex(v.to_int())[4:] for k, v in colours.items()} diff --git a/src/caelestia/utils/scheme.py b/src/caelestia/utils/scheme.py index 66cd697..ce8d6fe 100644 --- a/src/caelestia/utils/scheme.py +++ b/src/caelestia/utils/scheme.py @@ -1,4 +1,5 @@ import json +import random from pathlib import Path from caelestia.utils.material import get_colours_for_image @@ -106,6 +107,13 @@ class Scheme: f, ) + def set_random(self) -> None: + self._name = random.choice(get_scheme_names()) + self._flavour = random.choice(get_scheme_flavours()) + self._mode = random.choice(get_scheme_modes()) + self._update_colours() + self.save() + def _check_flavour(self) -> None: global scheme_flavours scheme_flavours = None -- cgit v1.2.3-freya From bb2eec1d67beba47587e5d36170231bad2487bee Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Thu, 12 Jun 2025 16:02:08 +1000 Subject: material: tone down chroma boost --- src/caelestia/utils/material/generator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/caelestia/utils/material') diff --git a/src/caelestia/utils/material/generator.py b/src/caelestia/utils/material/generator.py index 235b2ce..656a5af 100755 --- a/src/caelestia/utils/material/generator.py +++ b/src/caelestia/utils/material/generator.py @@ -88,12 +88,12 @@ def harmonize(a: Hct, b: Hct) -> Hct: def lighten(colour: Hct, amount: float) -> Hct: diff = (100 - colour.tone) * amount - return Hct.from_hct(colour.hue, colour.chroma + diff / 2, colour.tone + diff) + return Hct.from_hct(colour.hue, colour.chroma + diff / 5, colour.tone + diff) def darken(colour: Hct, amount: float) -> Hct: diff = colour.tone * amount - return Hct.from_hct(colour.hue, colour.chroma + diff / 2, colour.tone - diff) + return Hct.from_hct(colour.hue, colour.chroma + diff / 5, colour.tone - diff) def distance(colour: Cam16, base: Cam16) -> float: -- cgit v1.2.3-freya From dc4b6733bfbab1fc9e9f5001d800b549d3f6f98f Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Thu, 12 Jun 2025 16:28:12 +1000 Subject: material: better mono scheme --- src/caelestia/utils/material/generator.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/caelestia/utils/material') diff --git a/src/caelestia/utils/material/generator.py b/src/caelestia/utils/material/generator.py index 656a5af..584d375 100755 --- a/src/caelestia/utils/material/generator.py +++ b/src/caelestia/utils/material/generator.py @@ -74,8 +74,10 @@ colour_names = [ ] -def grayscale(colour: Hct, light: bool) -> None: +def grayscale(colour: Hct, light: bool) -> Hct: + colour = darken(colour, 0.35) if light else lighten(colour, 0.65) colour.chroma = 0 + return colour def mix(a: Hct, b: Hct, w: float) -> Hct: @@ -159,7 +161,7 @@ def gen_scheme(scheme, primary: Hct, colours: list[Hct]) -> dict[str, str]: # Harmonize colours for name, hct in colours.items(): if scheme.variant == "monochrome": - grayscale(hct, light) + colours[name] = grayscale(hct, light) else: harmonized = harmonize(hct, primary) colours[name] = darken(harmonized, 0.35) if light else lighten(harmonized, 0.65) -- cgit v1.2.3-freya From 558a086bcd5a17ea92139bcafeceaf5da5d5be30 Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Thu, 12 Jun 2025 21:57:09 +1000 Subject: scheme: ensure enough colours --- src/caelestia/utils/material/score.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/caelestia/utils/material') diff --git a/src/caelestia/utils/material/score.py b/src/caelestia/utils/material/score.py index da8b062..7765050 100755 --- a/src/caelestia/utils/material/score.py +++ b/src/caelestia/utils/material/score.py @@ -20,9 +20,8 @@ class Score: pass @staticmethod - def score(colors_to_population: dict) -> tuple[list[Hct], list[Hct]]: + def score(colors_to_population: dict, filter_enabled: bool = True) -> tuple[list[Hct], list[Hct]]: desired = 14 - filter_enabled = True dislike_filter = True colors_hct = [] @@ -110,6 +109,10 @@ class Score: for i, chosen_hct in enumerate(chosen_colors): chosen_colors[i] = DislikeAnalyzer.fix_if_disliked(chosen_hct) + # Ensure enough colours + if len(chosen_colors) < desired: + return Score.score(colors_to_population, False) + return chosen_primaries, chosen_colors -- cgit v1.2.3-freya From ec4bd7826a8cfef14f231fdca87bf6cd5f0fd4cf Mon Sep 17 00:00:00 2001 From: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> Date: Fri, 13 Jun 2025 14:39:33 +1000 Subject: internal: fix file perms --- src/caelestia/utils/material/generator.py | 0 src/caelestia/utils/material/score.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 src/caelestia/utils/material/generator.py mode change 100755 => 100644 src/caelestia/utils/material/score.py (limited to 'src/caelestia/utils/material') diff --git a/src/caelestia/utils/material/generator.py b/src/caelestia/utils/material/generator.py old mode 100755 new mode 100644 diff --git a/src/caelestia/utils/material/score.py b/src/caelestia/utils/material/score.py old mode 100755 new mode 100644 -- cgit v1.2.3-freya