diff --git a/flake-modules/colors.json b/flake-modules/colors.json new file mode 100644 index 0000000..7421d63 --- /dev/null +++ b/flake-modules/colors.json @@ -0,0 +1,13 @@ +{ + "base": { + "foreground": "#eedce2", + "background": "#221016" + }, + "to_alter": { + "accent": "#ac5271", + "secondary": "#f20c5b", + "tertiary": "#d5a287", + "special": "#51ac8d", + "weird": "#0cf2a3" + } +} \ No newline at end of file diff --git a/flake-modules/rice.nix b/flake-modules/rice.nix index cfc907b..1be0f5f 100644 --- a/flake-modules/rice.nix +++ b/flake-modules/rice.nix @@ -12,30 +12,34 @@ pkgs: rec { bright = (0.5); alter-set = let f = lib.alter-luminace-hex; in color-name: color-value: { base = color-value; dark = f color-value dark; bright = f color-value bright; }; alter = let f = lib.alter-luminace-hex; in color-value: { base = color-value; dark = f color-value dark; bright = f color-value bright; }; + # ccolor = builtins.mapAttrs alter-set (builtins.fromJSON (builtins.readFile ./colors.json)); ccolor = builtins.mapAttrs alter-set { - black = "#000022"; # "#111111" "#001100"; - white = "#ddddff"; # "#dddddd" "#66dd66"; - blue = "#4422ff"; # "#3333dd" "#003300"; - cyan = "#11dddd"; # "#11dddd" "#00dd55"; - green = "#11dd44"; # "#11dd11" "#11dd11"; - magenta = "#6622dd"; # "#dd11dd" "#005500"; - red = "#dd1166"; # "#dd1111" "#00aa00"; - yellow = "#dd6611"; # "#dddd11" "#ffff00"; + black = "#111111"; # "#111111" "#001100"; + white = "#dddddd"; # "#dddddd" "#66dd66"; + blue = "#4444dd"; # "#3333dd" "#003300"; + cyan = "#44dddd"; # "#11dddd" "#00dd55"; + green = "#44dd44"; # "#11dd11" "#11dd11"; + magenta = "#dd44dd"; # "#dd11dd" "#005500"; + red = "#dd4444"; # "#dd1111" "#00aa00"; + yellow = "#dd4444"; # "#dddd11" "#ffff00"; }; - xcolor = with ccolor; rec { - background = black.dark; - foreground = white.base; - border = red.base; - border2 = blue.base; - accent = blue; - secondary = cyan; - tertiary = magenta; - special = yellow; - weird = green; + facolor = builtins.mapAttrs alter-set (builtins.fromJSON (builtins.readFile ./colors.json)).to_alter; + fbcolor = (builtins.fromJSON (builtins.readFile ./colors.json)).base; + fcolor = facolor // fbcolor; + xcolor = with ccolor; with fcolor; rec { + background = "#000000"; + # foreground = "#dddddd"; + # accent = blue; + # secondary = cyan; + # tertiary = magenta; + # special = yellow; + # weird = green; positive = alter "#00dd00"; negative = alter "#dd0000"; + border = accent.base; + border2 = secondary.base; }; - in ccolor // xcolor; + in ccolor // fcolor // xcolor; font = { code = { name = "JetBrainsMono-NF"; diff --git a/home-modules/hyprland.nix b/home-modules/hyprland.nix index 25556c4..b3f36e3 100755 --- a/home-modules/hyprland.nix +++ b/home-modules/hyprland.nix @@ -334,7 +334,7 @@ lib.mkIf (user != "tv") "SUPER SHIFT, F5, exec, nx_gcal_event reauthenticate" # "SUPER, F6," ''SUPER, F8, exec, find ~/Pictures/wallpapers/* -type f -not -path "~/Pictures/wallpapers/.git/*" | sort -R | head -n 1 | xargs swww img --transition-type wipe --transition-angle 60 --transition-step 120 --transition-fps 120'' - # "SUPER, F9, hyprload,reload" + "SUPER, F9, exec, change_colors_json $(swww query | sed -n 2p | sed -e 's-.*image: --g') && kitty -e sh -c 'nh home switch'" # "SUPER, F10, hyprload,update" "SUPER, F11, exec, waybar" "SUPER SHIFT, F11, exec, pkill waybar " diff --git a/home-modules/wallpaper-to-colors.nix b/home-modules/wallpaper-to-colors.nix new file mode 100644 index 0000000..8c597f3 --- /dev/null +++ b/home-modules/wallpaper-to-colors.nix @@ -0,0 +1,152 @@ +{ pkgs-unstable, ... }: +{ + home.packages = with pkgs-unstable; [ + (writers.writePython3Bin "change_colors_json" { + libraries = with python3Packages; [ numpy pillow scikit-learn ]; + flakeIgnore = [ "E302" "E305" "E226" "E501" ]; + } /*python */ '' +from colorsys import hls_to_rgb, rgb_to_hls +import json +import sys +from typing import Literal, cast +from numpy.typing import NDArray +from sklearn.cluster import KMeans +import numpy as np +from PIL import Image + +def fc(c: int) -> str: + assert c < 256 + s = str(hex(c))[2:] + if c < 16: + return "0" + s + elif len(s) == 1: + return s + s + else: + return s + +class Color(object): + def __init__(self, rgb: tuple[int, ...], frequency: float): + assert len(rgb) == 3, "RGB values must be a tuple of length 3" + self.rgb = cast(tuple[int, int, int], rgb) + self.freq: float = frequency + + def __lt__(self, other: "Color") -> bool: + return self.freq < other.freq + + @property + def hls(self) -> tuple[float, float, float]: + return rgb_to_hls(r=self.rgb[0] / 255, g=self.rgb[1] / 255, b=self.rgb[2] / 255) + + @property + def luminance(self) -> float: + return np.dot(np.array([0.2126, 0.7152, 0.0722]), self.rgb) + +def k_means_extraction(arr: NDArray[float], height: int, width: int, palette_size: int) -> list[Color]: + arr = np.reshape(arr, (width * height, -1)) + model = KMeans(n_clusters=palette_size, n_init="auto", init="k-means++", random_state=2024) + labels = model.fit_predict(arr) + palette = np.array(model.cluster_centers_, dtype=int) + color_count = np.bincount(labels) + color_frequency = color_count / float(np.sum(color_count)) + colors = [] + for color, freq in zip(palette, color_frequency): + colors.append(Color(color, freq)) + return colors + + +class Palette: + def __init__(self, colors: list[Color]): + self.colors = colors + self.frequencies = [c.freq for c in colors] + + def __getitem__(self, item: int) -> Color: + return self.colors[item] + + def __len__(self) -> int: + return self.number_of_colors + +def ensure_color(c: Color, alter_sat: bool) -> list[int]: + hue, lum, sat = c.hls + if alter_sat: + new_sat = min(max(sat, 0.6) + 0.3, 1) + else: + new_sat = sat + new_lum = max(lum, 0.5) + r, g, b = hls_to_rgb(h=hue, l=new_lum, s=new_sat) + return [int(r*255), int(g*255), int(b*255)] + +def list_to_hex(ilist: list[int]) -> str: + return f"#{fc(ilist[0])}{fc(ilist[1])}{fc(ilist[2])}" + +def alter_hue(ilist: list[int], hue: int) -> list[int]: + assert hue >= 0 and hue <= 360 + r, g, b = ilist + h, l, s = rgb_to_hls((r/255), (g/255), (b/255)) + new_hue = (((h*360) + hue) % 360) / 360 + r, g, b = hls_to_rgb(h=new_hue, l=l, s=s) + return [int(r*255), int(g*255), int(b*255)] + +def alter_l(ilist: list[int], l_in_1_0: float) -> list[int]: + assert l_in_1_0 >= 0 and l_in_1_0 <= 1 + r, g, b = ilist + h, _, s = rgb_to_hls((r/255), (g/255), (b/255)) + r, g, b = hls_to_rgb(h=h, l=l_in_1_0, s=s) + return [int(r*255), int(g*255), int(b*255)] + +def extract_colors( + image: str, + palette_size: int = 5, + resize: bool = True, + sort_mode: Literal["luminance", "frequency"] | None = None, +) -> Palette: + + img = Image.open(image).convert("RGB") + + # open the image + img = img.resize((256, 256)) + width, height = img.size + arr = np.asarray(img) + + colors = k_means_extraction(arr, height, width, palette_size) + + if sort_mode == "luminance": + colors.sort(key=lambda c: c.luminance, reverse=False) + else: + colors.sort(reverse=True) + + return Palette(colors) +if __name__ == "__main__": + try: + img = sys.argv[1] + except IndexError: + img = "/home/nx2/Pictures/wallpapers/absolute-cinema-acid.png" + + palette = extract_colors(image=img, palette_size=3) + + accent = ensure_color(c=palette[0], alter_sat=False) + secondary = ensure_color(c=palette[1], alter_sat=True) + tertiary = ensure_color(c=palette[2], alter_sat=False) + weird = alter_hue(ilist=secondary, hue=180) + special = alter_hue(ilist=accent, hue=180) + foreground = alter_l(accent, 0.9) + background = alter_l(accent, 0.1) + + d = { + "base": { + "foreground": list_to_hex(foreground), + "background": list_to_hex(background) + }, + "to_alter": { + "accent": list_to_hex(accent), + "secondary": list_to_hex(secondary), + "tertiary": list_to_hex(tertiary), + "special": list_to_hex(special), + "weird": list_to_hex(weird) + } + } + + with open("/home/nx2/nix-dots/flake-modules/colors.json", "w") as f: + f.write(json.dumps(d, indent=4)) +'') + ]; +} diff --git a/home.nix b/home.nix index 065667d..de2f250 100755 --- a/home.nix +++ b/home.nix @@ -44,6 +44,7 @@ ./home-modules/vale.nix ./home-modules/virt-manager.nix ./home-modules/vscode.nix + ./home-modules/wallpaper-to-colors.nix ./home-modules/waybar.nix ./home-modules/wlogout.nix ./home-modules/yazi.nix