mirror of
https://github.com/caelestia-dots/cli.git
synced 2026-06-09 16:59:29 -05:00
scheme: use material for colour gen
Replaces okolors Also boost neutral chroma and all chroma
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
. (dirname (status filename))/util.fish
|
. (dirname (status filename))/util.fish
|
||||||
|
|
||||||
install-deps git hyprland-git hyprpaper-git okolors-git imagemagick wl-clipboard fuzzel-git socat foot jq python xdg-user-dirs python-materialyoucolor-git app2unit-git
|
install-deps git hyprland-git hyprpaper-git imagemagick wl-clipboard fuzzel-git socat foot jq python xdg-user-dirs python-materialyoucolor-git app2unit-git
|
||||||
install-optional-deps 'equibop-bin (discord client)' 'btop (system monitor)' 'wf-recorder (screen recorder)' 'grim (screenshot tool)' 'zen-browser (web browser)' 'spotify-adblock (music player)'
|
install-optional-deps 'equibop-bin (discord client)' 'btop (system monitor)' 'wf-recorder (screen recorder)' 'grim (screenshot tool)' 'zen-browser (web browser)' 'spotify-adblock (music player)'
|
||||||
|
|
||||||
set -l dist $C_DATA/scripts
|
set -l dist $C_DATA/scripts
|
||||||
|
|||||||
+69
-29
@@ -72,6 +72,8 @@ colour_names = [
|
|||||||
"error",
|
"error",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
HLS = tuple[float, float, float]
|
||||||
|
|
||||||
|
|
||||||
def hex_to_rgb(hex: str) -> tuple[float, float, float]:
|
def hex_to_rgb(hex: str) -> tuple[float, float, float]:
|
||||||
"""Convert a hex string to an RGB tuple in the range [0, 1]."""
|
"""Convert a hex string to an RGB tuple in the range [0, 1]."""
|
||||||
@@ -91,23 +93,29 @@ def hls_to_hex(h: str, l: str, s: str) -> str:
|
|||||||
return rgb_to_hex(hls_to_rgb(h, l, s))
|
return rgb_to_hex(hls_to_rgb(h, l, s))
|
||||||
|
|
||||||
|
|
||||||
def adjust(hex: str, light: float = 0, sat: float = 0) -> str:
|
def grayscale(hls: HLS, light: bool) -> HLS:
|
||||||
h, l, s = hex_to_hls(hex)
|
h, l, s = hls
|
||||||
return hls_to_hex(h, max(0, min(1, l + light)), max(0, min(1, s + sat)))
|
return h, 0.5 - l / 2 if light else l / 2 + 0.5, 0
|
||||||
|
|
||||||
|
|
||||||
def grayscale(hex: str, light: bool) -> str:
|
def mix(a: HLS, b: HLS, w: float) -> HLS:
|
||||||
h, l, s = hex_to_hls(hex)
|
h1, l1, s1 = a
|
||||||
return hls_to_hex(h, 0.5 - l / 2 if light else l / 2 + 0.5, 0)
|
h2, l2, s2 = b
|
||||||
|
return h1 * (1 - w) + h2 * w, l1 * (1 - w) + l2 * w, s1 * (1 - w) + s2 * w
|
||||||
|
|
||||||
|
|
||||||
def distance(colour: str, base: str) -> float:
|
def darken(colour: HLS, amount: float) -> HLS:
|
||||||
h1, l1, s1 = hex_to_hls(colour)
|
h, l, s = colour
|
||||||
|
return h, max(0, l - amount), s
|
||||||
|
|
||||||
|
|
||||||
|
def distance(colour: HLS, base: str) -> float:
|
||||||
|
h1, l1, s1 = colour
|
||||||
h2, l2, s2 = hex_to_hls(base)
|
h2, l2, s2 = hex_to_hls(base)
|
||||||
return abs(h1 - h2) * 0.4 + abs(l1 - l2) * 0.3 + abs(s1 - s2) * 0.3
|
return abs(h1 - h2) * 0.4 + abs(l1 - l2) * 0.3 + abs(s1 - s2) * 0.3
|
||||||
|
|
||||||
|
|
||||||
def smart_sort(colours: list[str], base: list[str]) -> list[str]:
|
def smart_sort(colours: list[HLS], base: list[str]) -> dict[str, HLS]:
|
||||||
sorted_colours = [None] * len(colours)
|
sorted_colours = [None] * len(colours)
|
||||||
distances = {}
|
distances = {}
|
||||||
|
|
||||||
@@ -130,15 +138,7 @@ def smart_sort(colours: list[str], base: list[str]) -> list[str]:
|
|||||||
|
|
||||||
distances[colour].pop(0)
|
distances[colour].pop(0)
|
||||||
|
|
||||||
return [i[0] for i in sorted_colours]
|
return {colour_names[i]: c[0] for i, c in enumerate(sorted_colours)}
|
||||||
|
|
||||||
|
|
||||||
def mix(a: str, b: str, w: float) -> str:
|
|
||||||
r1, g1, b1 = hex_to_rgb(a)
|
|
||||||
r2, g2, b2 = hex_to_rgb(b)
|
|
||||||
return rgb_to_hex(
|
|
||||||
(r1 * (1 - w) + r2 * w, g1 * (1 - w) + g2 * w, b1 * (1 - w) + b2 * w)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_scheme(scheme: str) -> DynamicScheme:
|
def get_scheme(scheme: str) -> DynamicScheme:
|
||||||
@@ -167,21 +167,61 @@ if __name__ == "__main__":
|
|||||||
colours_in = sys.argv[3]
|
colours_in = sys.argv[3]
|
||||||
|
|
||||||
base = light_colours if light else dark_colours
|
base = light_colours if light else dark_colours
|
||||||
MatScheme = get_scheme(scheme)
|
chroma_mult = 1.5 if light else 1.2
|
||||||
|
|
||||||
colours = smart_sort(colours_in.split(" "), base)
|
# Convert to HLS
|
||||||
for i, hex in enumerate(colours):
|
colours = [hex_to_hls(c) for c in colours_in.split(" ")]
|
||||||
|
|
||||||
|
# Sort colours and turn into dict
|
||||||
|
colours = smart_sort(colours, base)
|
||||||
|
|
||||||
|
# Adjust colours
|
||||||
|
MatScheme = get_scheme(scheme)
|
||||||
|
for name, hls in colours.items():
|
||||||
if scheme == "monochrome":
|
if scheme == "monochrome":
|
||||||
colours[i] = grayscale(hex, light)
|
colours[name] = grayscale(hls, light)
|
||||||
else:
|
else:
|
||||||
argb = argb_from_rgb(int(hex[:2], 16), int(hex[2:4], 16), int(hex[4:], 16))
|
argb = int(f"0xFF{hls_to_hex(*hls)}", 16)
|
||||||
mat_scheme = MatScheme(Hct.from_int(argb), not light, 0)
|
mat_scheme = MatScheme(Hct.from_int(argb), not light, 0)
|
||||||
primary = MaterialDynamicColors.primary.get_hct(mat_scheme)
|
|
||||||
colours[i] = "{:02X}{:02X}{:02X}".format(*primary.to_rgba()[:3])
|
colour = MaterialDynamicColors.primary.get_hct(mat_scheme)
|
||||||
|
|
||||||
|
# Boost neutral scheme colours
|
||||||
|
if scheme == "neutral":
|
||||||
|
colour.chroma += 10
|
||||||
|
|
||||||
|
colour.chroma *= chroma_mult
|
||||||
|
|
||||||
|
colours[name] = hex_to_hls("{:02X}{:02X}{:02X}".format(*colour.to_rgba()[:3]))
|
||||||
|
|
||||||
# Success and error colours
|
# Success and error colours
|
||||||
colours.append(mix(colours[8], base[8], 0.8)) # Success (green)
|
colours["success"] = mix(colours["green"], hex_to_hls(base[8]), 0.8)
|
||||||
colours.append(mix(colours[4], base[4], 0.8)) # Error (red)
|
colours["error"] = mix(colours["red"], hex_to_hls(base[4]), 0.8)
|
||||||
|
|
||||||
for i, colour in enumerate(colours):
|
# Layers and accents
|
||||||
print(f"{colour_names[i]} {colour}")
|
material = {}
|
||||||
|
primary_scheme = MatScheme(Hct.from_int(int(f"0xFF{colours_in.split(" ")[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)}")
|
||||||
|
|||||||
+2
-10
@@ -10,16 +10,8 @@ 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 variants vibrant tonalspot expressive fidelity fruitsalad rainbow neutral content monochrome
|
||||||
|
|
||||||
# Generate colours
|
# Generate colours
|
||||||
set -l colours (okolors $img -k 14 -w 0)
|
set -l colours ($src/score.py $img)
|
||||||
for variant in $variants
|
for variant in $variants
|
||||||
mkdir -p $src/../data/schemes/dynamic/$variant
|
mkdir -p $src/../data/schemes/dynamic/$variant
|
||||||
$src/autoadjust.py $theme $variant $colours > $src/../data/schemes/dynamic/$variant/$theme.txt
|
$src/autoadjust.py $theme $variant $colours | head -c -1 > $src/../data/schemes/dynamic/$variant/$theme.txt
|
||||||
end
|
end
|
||||||
|
|
||||||
# Generate layers and accents
|
|
||||||
set -l tmp (mktemp)
|
|
||||||
$src/genmaterial.py $img $theme > $tmp
|
|
||||||
for variant in $variants
|
|
||||||
grep -FA 15 $variant $tmp | tail -15 | head -c -1 >> $src/../data/schemes/dynamic/$variant/$theme.txt
|
|
||||||
end
|
|
||||||
rm $tmp
|
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
#!/bin/python
|
|
||||||
|
|
||||||
import math
|
|
||||||
import sys
|
|
||||||
from colorsys import hls_to_rgb, rgb_to_hls
|
|
||||||
|
|
||||||
from materialyoucolor.dynamiccolor.material_dynamic_colors import MaterialDynamicColors
|
|
||||||
from materialyoucolor.hct import Hct
|
|
||||||
from materialyoucolor.quantize import ImageQuantizeCelebi
|
|
||||||
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
|
|
||||||
from materialyoucolor.score.score import Score
|
|
||||||
|
|
||||||
|
|
||||||
def darken(rgb: tuple[int, int, int], amount: float) -> tuple[int, int, int]:
|
|
||||||
h, l, s = rgb_to_hls(*tuple(i / 255 for i in rgb))
|
|
||||||
return tuple(round(i * 255) for i in hls_to_rgb(h, max(0, l - amount), s))
|
|
||||||
|
|
||||||
|
|
||||||
def mix(
|
|
||||||
rgb1: tuple[int, int, int], rgb2: tuple[int, int, int], amount: float
|
|
||||||
) -> tuple[int, int, int]:
|
|
||||||
return tuple(round(rgb1[i] * (1 - amount) + rgb2[i] * amount) for i in range(3))
|
|
||||||
|
|
||||||
|
|
||||||
num_args = len(sys.argv)
|
|
||||||
if num_args < 2:
|
|
||||||
print('Usage: <path/to/image> [ "light" | "dark" ]')
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
img = sys.argv[1]
|
|
||||||
is_dark = num_args < 3 or sys.argv[2] != "light"
|
|
||||||
|
|
||||||
colours = ImageQuantizeCelebi(img, 1, 128)
|
|
||||||
hct = Hct.from_int(Score.score(colours)[0])
|
|
||||||
|
|
||||||
for Scheme in (
|
|
||||||
SchemeFruitSalad,
|
|
||||||
SchemeExpressive,
|
|
||||||
SchemeMonochrome,
|
|
||||||
SchemeRainbow,
|
|
||||||
SchemeTonalSpot,
|
|
||||||
SchemeNeutral,
|
|
||||||
SchemeFidelity,
|
|
||||||
SchemeContent,
|
|
||||||
SchemeVibrant,
|
|
||||||
):
|
|
||||||
print("\n" + Scheme.__name__[6:].lower())
|
|
||||||
scheme = Scheme(hct, is_dark, 0.0)
|
|
||||||
|
|
||||||
colours = {}
|
|
||||||
|
|
||||||
for color in vars(MaterialDynamicColors).keys():
|
|
||||||
color_name = getattr(MaterialDynamicColors, color)
|
|
||||||
if hasattr(color_name, "get_hct"):
|
|
||||||
colours[color] = color_name.get_hct(scheme).to_rgba()[:3]
|
|
||||||
|
|
||||||
colours = {
|
|
||||||
"primary": colours["primary"],
|
|
||||||
"secondary": colours["secondary"],
|
|
||||||
"tertiary": colours["tertiary"],
|
|
||||||
"text": colours["onBackground"],
|
|
||||||
"subtext1": colours["onSurfaceVariant"],
|
|
||||||
"subtext0": colours["outline"],
|
|
||||||
"overlay2": mix(colours["surface"], colours["outline"], 0.86),
|
|
||||||
"overlay1": mix(colours["surface"], colours["outline"], 0.71),
|
|
||||||
"overlay0": mix(colours["surface"], colours["outline"], 0.57),
|
|
||||||
"surface2": mix(colours["surface"], colours["outline"], 0.43),
|
|
||||||
"surface1": mix(colours["surface"], colours["outline"], 0.29),
|
|
||||||
"surface0": mix(colours["surface"], colours["outline"], 0.14),
|
|
||||||
"base": colours["surface"],
|
|
||||||
"mantle": darken(colours["surface"], 0.03),
|
|
||||||
"crust": darken(colours["surface"], 0.05),
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, colour in colours.items():
|
|
||||||
print("{} {:02X}{:02X}{:02X}".format(name, *colour))
|
|
||||||
Executable
+104
@@ -0,0 +1,104 @@
|
|||||||
|
#!/bin/python
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
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) -> list[int]:
|
||||||
|
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
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
chosen_colors = []
|
||||||
|
for difference_degrees_ in range(90, 0, -1):
|
||||||
|
chosen_colors.clear()
|
||||||
|
for hct in [item["hct"] for item in scored_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
|
||||||
|
|
||||||
|
colors = []
|
||||||
|
|
||||||
|
if dislike_filter:
|
||||||
|
for chosen_hct in chosen_colors:
|
||||||
|
chosen_colors[chosen_colors.index(chosen_hct)] = (
|
||||||
|
DislikeAnalyzer.fix_if_disliked(chosen_hct)
|
||||||
|
)
|
||||||
|
for chosen_hct in chosen_colors:
|
||||||
|
colors.append(chosen_hct.to_int())
|
||||||
|
return colors
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
img = sys.argv[1]
|
||||||
|
|
||||||
|
colours = ImageQuantizeCelebi(img, 1, 128)
|
||||||
|
colours = [Hct.from_int(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]))
|
||||||
Reference in New Issue
Block a user