[+] Random custom coloring

This commit is contained in:
Azalea (on HyDEV-Daisy) 2022-06-19 20:50:09 -04:00
parent 7bd96e422f
commit a1d687d3bd
3 changed files with 184 additions and 45 deletions

View file

@ -2,20 +2,19 @@
from __future__ import annotations
import argparse
import importlib
import json
import os
import random
from dataclasses import dataclass
from pathlib import Path
from itertools import permutations
from typing import Iterable
from typing_extensions import Literal
from . import constants
from .color_util import AnsiMode, printc, color, clear_screen, RGB
from .color_util import AnsiMode, printc, color, clear_screen
from .constants import CONFIG_PATH, VERSION, TERM_LEN, TEST_ASCII_WIDTH, TEST_ASCII
from .neofetch_util import run_neofetch, replace_colors, get_custom_distro_ascii
from .presets import PRESETS, ColorProfile
from .neofetch_util import run_neofetch, get_distro_ascii, ColorAlignment, ascii_size
from .presets import PRESETS
from .serializer import json_stringify
@ -25,6 +24,7 @@ class Config:
mode: AnsiMode
light_dark: Literal['light', 'dark'] = 'dark'
lightness: float | None = None
color_align: ColorAlignment = ColorAlignment('horizontal')
def save(self):
CONFIG_PATH.parent.mkdir(exist_ok=True, parents=True)
@ -160,8 +160,8 @@ def create_config() -> Config:
num_cols = TERM_LEN // (TEST_ASCII_WIDTH + 2)
ratios = [col / (num_cols - 1) for col in range(num_cols)]
ratios = [r * 0.6 + 0.2 for r in ratios]
lines = [replace_colors(TEST_ASCII.replace('{txt}', f'{r * 100:.0f}%'.center(5)),
_prs.set_light(r))[0].split('\n') for r in ratios]
lines = [ColorAlignment('horizontal').recolor_ascii(TEST_ASCII.replace(
'{txt}', f'{r * 100:.0f}%'.center(5)), _prs.set_light(r)).split('\n') for r in ratios]
[printc(' '.join(line)) for line in zip(*lines)]
while True:
@ -182,8 +182,66 @@ def create_config() -> Config:
except Exception:
printc('&cUnable to parse lightness value, please input it as a decimal or percentage (e.g. 0.5 or 50%)')
if lightness:
_prs = _prs.set_light(lightness)
title += f'\n&e4. Brightness: &r{f"{lightness:.2f}" if lightness else "unset"}'
#############################
# 5. Color arrangement
while True:
clear_screen(title)
printc(f'&a5. Let\'s choose a color arrangement!')
printc(f'You can choose standard horizontal or vertical alignment, or use one of the random color schemes, or assign colors yourself (TODO).')
print()
asc = get_distro_ascii()
asc_width = ascii_size(asc)[0]
asciis = [
['Horizontal'.center(asc_width), *ColorAlignment('horizontal').recolor_ascii(asc, _prs).split('\n')],
['Vertical'.center(asc_width), *ColorAlignment('vertical').recolor_ascii(asc, _prs).split('\n')],
]
# Random color schemes
# ascii_indices =
pis = list(range(len(_prs.unique_colors().colors)))
while len(pis) < 6:
pis += pis
perm = list(permutations(pis))
choices = random.sample(perm, 4)
choices = [{i: n for i, n in enumerate(c)} for c in choices]
asciis += [[f'Random {i}'.center(asc_width), *ColorAlignment('custom', r).recolor_ascii(asc, _prs).split('\n')]
for i, r in enumerate(choices)]
ascii_per_row = TERM_LEN // (asc_width + 2)
while asciis:
current = asciis[:ascii_per_row]
asciis = asciis[ascii_per_row:]
# Print by row
[printc(' '.join(line)) for line in zip(*current)]
print()
print('You can type "roll" to randomize again.')
print()
choice = literal_input(f'Your choice?', ['horizontal', 'vertical', 'roll', 'random1', 'random2', 'random3', 'random4'], 'horizontal')
if choice == 'roll':
continue
if choice in ['horizontal', 'vertical']:
color_alignment = ColorAlignment(choice)
elif choice.startswith('random'):
color_alignment = ColorAlignment('custom', choices[int(choice[6]) - 1])
else:
raise NotImplementedError()
break
title += f'\n&e5. Color Alignment: &r{color_alignment}'
# Create config
c = Config(preset, color_system, light_dark, lightness)
clear_screen(title)
c = Config(preset, color_system, light_dark, lightness, color_alignment)
# Save config
print()
@ -238,13 +296,15 @@ def run():
preset = preset.lighten(args.scale)
if args.light:
preset = preset.set_light(args.light)
if config.lightness:
preset = preset.set_light(config.lightness)
# Test distro ascii art
if args.test_distro:
asc = get_custom_distro_ascii(args.test_distro)
asc = get_distro_ascii(args.test_distro)
print(asc)
print(replace_colors(asc, preset, config.mode)[0])
print(ColorAlignment('horizontal').recolor_ascii(asc, preset))
return
# Run
run_neofetch(preset, config.mode)
run_neofetch(preset, config.color_align)

View file

@ -4,17 +4,94 @@ import os
import platform
import re
import subprocess
from dataclasses import dataclass
from pathlib import Path
from subprocess import check_output
from tempfile import TemporaryDirectory
import pkg_resources
from hyfetch.color_util import color
from typing_extensions import Literal
from .color_util import AnsiMode
from .constants import COLOR_MODE
from .presets import ColorProfile
RE_NEOFETCH_COLOR = re.compile('\\${c[0-9]}')
def ascii_size(asc: str) -> tuple[int, int]:
"""
Get distro ascii width, height ignoring color code
:param asc: Distro ascii
:return: Width, Height
"""
return max(len(line) for line in re.sub(RE_NEOFETCH_COLOR, '', asc).split('\n')), len(asc.split('\n'))
def normalize_ascii(asc: str) -> str:
"""
Make sure every line are the same width
"""
w = ascii_size(asc)[0]
return '\n'.join(line + ' ' * (w - ascii_size(line)[0]) for line in asc.split('\n'))
@dataclass
class ColorAlignment:
mode: Literal['horizontal', 'vertical', 'custom']
# custom_colors[ascii color index] = unique color index in preset
custom_colors: dict[int, int] = ()
def recolor_ascii(self, asc: str, preset: ColorProfile) -> str:
"""
Use the color alignment to recolor an ascii art
:return Colored ascii, Uncolored lines
"""
if self.mode in ['horizontal', 'vertical']:
# Remove existing colors
asc = re.sub(RE_NEOFETCH_COLOR, '', asc)
lines = asc.split('\n')
# Add new colors
if self.mode == 'horizontal':
colors = preset.with_length(len(lines))
asc = '\n'.join([colors[i].to_ansi() + l + color('&r') for i, l in enumerate(lines)])
else:
asc = '\n'.join(preset.color_text(line) + color('&r') for line in lines)
else:
preset = preset.unique_colors()
# Apply colors
new = []
start_color = None
color_map = {ai: preset.colors[pi].to_ansi() for ai, pi in self.custom_colors.items()}
for line in asc.split('\n'):
# Line has color placeholders
if len(RE_NEOFETCH_COLOR.findall(line)) > 0:
# Get the last placeholder for the next line
last = int(RE_NEOFETCH_COLOR.findall(line)[-1][3])
# Replace placeholders
for ascii_i, c in color_map.items():
line = line.replace(f'${{c{ascii_i}}}', c)
# Add to new ascii
new.append(f'{start_color or ""}{line}')
# Change next start color
start_color = color_map[last]
else:
new.append(f'{start_color or ""}{line}')
asc = '\n'.join(new)
return asc
def get_command_path() -> str:
"""
Get the absolute path of the neofetch command
@ -24,39 +101,25 @@ def get_command_path() -> str:
return pkg_resources.resource_filename(__name__, 'scripts/neofetch_mod.sh')
def get_distro_ascii() -> str:
def get_distro_ascii(distro: str | None = None) -> str:
"""
Get the distro ascii
Get the distro ascii of the current distro. Or if distro is specified, get the specific distro's
ascii art instead.
:return: Distro ascii
"""
return check_output([get_command_path(), "print_ascii"]).decode().strip()
cmd = 'print_ascii'
if distro:
os.environ['CUSTOM_DISTRO'] = distro
cmd = 'print_custom_ascii'
return normalize_ascii(check_output([get_command_path(), cmd]).decode().strip())
def get_custom_distro_ascii(distro: str) -> str:
"""
Get the distro ascii of a specific distro
:return: Distro ascii
"""
os.environ['CUSTOM_DISTRO'] = distro
return check_output([get_command_path(), "print_custom_ascii"]).decode().strip()
def replace_colors(asc: str, preset: ColorProfile, mode: AnsiMode = COLOR_MODE):
# Remove existing colors
asc = re.sub('\\${.*?}', '', asc)
# Add new colors
lines = asc.split('\n')
colors = preset.with_length(len(lines))
asc = '\n'.join([colors[i].to_ansi(mode) + l for i, l in enumerate(lines)])
return asc, lines
def run_neofetch(preset: ColorProfile, mode: AnsiMode):
asc, lines = replace_colors(get_distro_ascii(), preset, mode)
def run_neofetch(preset: ColorProfile, alignment: ColorAlignment):
asc = get_distro_ascii()
w, h = ascii_size(asc)
asc = alignment.recolor_ascii(asc, preset)
# Write temp file
with TemporaryDirectory() as tmp_dir:
@ -65,8 +128,8 @@ def run_neofetch(preset: ColorProfile, mode: AnsiMode):
path.write_text(asc)
# Call neofetch with the temp file
os.environ['ascii_len'] = str(max(len(l) for l in lines))
os.environ['ascii_lines'] = str(len(lines))
os.environ['ascii_len'] = str(w)
os.environ['ascii_lines'] = str(h)
if platform.system() != 'Windows':
os.system(f'{get_command_path()} --ascii --source {path.absolute()} --ascii-colors')
@ -75,8 +138,7 @@ def run_neofetch(preset: ColorProfile, mode: AnsiMode):
cmd = get_command_path().replace("\\", "/").replace("C:/", "/c/")
path_str = str(path.absolute()).replace('\\', '/').replace('C:/', '/c/')
cmd = f'ascii_len={max(len(l) for l in lines)} ascii_lines={len(lines)} ' \
f'{cmd} --ascii --source {path_str} --ascii-colors'
cmd = f'ascii_len={w} ascii_lines={h} {cmd} --ascii --source {path_str} --ascii-colors'
full_cmd = ['C:\\Program Files\\Git\\bin\\bash.exe', '-c', cmd]
# print(full_cmd)

View file

@ -1,10 +1,21 @@
from __future__ import annotations
from typing import Iterable
from typing_extensions import Literal
from .color_util import RGB
def remove_duplicates(seq: Iterable) -> list:
"""
Remove duplicate items from a sequence while preserving the order
"""
seen = set()
seen_add = seen.add
return [x for x in seq if not (x in seen or seen_add(x))]
class ColorProfile:
raw: list[str]
colors: list[RGB]
@ -98,6 +109,12 @@ class ColorProfile:
"""
return ColorProfile([c.set_light(light) for c in self.colors])
def unique_colors(self) -> ColorProfile:
"""
Create another color profile with only the unique colors
"""
return ColorProfile(remove_duplicates(self.colors))
PRESETS: dict[str, ColorProfile] = {
'rainbow': ColorProfile([