From 84088247b479e495a8d32b618d005679de5088e4 Mon Sep 17 00:00:00 2001 From: Sunpy Date: Wed, 12 Feb 2020 12:01:12 +0100 Subject: [PATCH] First commit --- main.py | 73 +++++++++++++++++++++++++++++ pixel.py | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 213 insertions(+) create mode 100644 main.py create mode 100644 pixel.py diff --git a/main.py b/main.py new file mode 100644 index 0000000..fcd6fad --- /dev/null +++ b/main.py @@ -0,0 +1,73 @@ +# import shutil # shutil.get_terminal_size(fallback=(0, 0)) +import os +import math + +from pixel import Pixel, Screen + +WIDTH, HEIGHT = 100, 30 +STD_HANDLE = 1 # Would want this to be 0, but it can cause issues + +def shad_background(pixel: Pixel, x: int, y: int, time: float): + uv = (x / WIDTH, y / HEIGHT) + + h = math.sin(uv[0] * math.pi + uv[1] * 10.0 + time + math.cos(uv[0] * uv[1] + uv[1] * 7.1823) * math.sin(uv[0] * 14.2)) + pixel.background_color.set(h, h, h) + +def shad_wave(pixel: Pixel, x: int, y: int, time: float): # https://www.shadertoy.com/view/Wd23W3 + uv = (x / WIDTH, y / HEIGHT) + + def hsv2rgb(h, s, v) -> tuple: + r,g,b = 0,0,0 + + i = int(h * 6) + f = h * 6 - i + p = v * (1 - s) + q = v * (1 - f * s) + t = v * (1 - (1 - f) * s) + + o = i % 6 + + if o == 0: + r,g,b = v,t,p + elif o == 1: + r,g,b = q,v,p + elif o == 2: + r,g,b = p,v,t + elif o == 3: + r,g,b = p,q,v + elif o == 4: + r,g,b = t,p,v + else: + r,g,b = v,p,q + + return r, g, b + + def step(edge: float, value: float) -> float: + return 1.0 if value > edge else 0.0 + + def strip(h: float, thickness: float, y: float) -> float: + return step(y + h - thickness, step(1., y + h + thickness)) + + col = [0.0, 0.0, 0.0] + + f = 0.0 + while f < math.pi * 2: + a = strip(.5 + math.cos(uv[0] * 2. + time * 3.) * .15, + .005, + uv[1] + math.cos(uv[0] * 3. + time * 2. + f) * .25) + + _col = hsv2rgb(f / math.pi / 2., 1., 1.) + for i in range(3): + col[i] += a * _col[i] + + f += math.pi / 4. + + pixel.text_color.set(*col) + + +screen = Screen(WIDTH, HEIGHT, STD_HANDLE, [ + shad_background + #shad_wave +]) + +screen.mainloop() \ No newline at end of file diff --git a/pixel.py b/pixel.py new file mode 100644 index 0000000..17accde --- /dev/null +++ b/pixel.py @@ -0,0 +1,140 @@ +import os +import datetime + +from time import sleep # debug + +class Color: + cache_closest_printable_color = 0 + + def __init__(self, r: float = 0.0, g: float = 0.0, b: float = 0.0): + self.set(r, g, b) + self.update() + + def set(self, r, g, b): + self.r, self.g, self.b = r, g, b + + def update(self): + self._r, self._g, self._b = self.r, self.g, self.b + + def hasChange(self) -> bool: + return any(x != y for x, y in {self._r: self.r, self._g: self.g, self._b: self.b}.items()) + + def distance(self, to) -> float: + dr = self.r - to.r + dg = self.g - to.g + db = self.b - to.b + return (dr * dr + dg * dg + db * db) ** .5 + + def getClosestPrintableColor(self) -> int: + if not self.hasChange(): + return self.cache_closest_printable_color + + closest_distance = float("inf") + closest_value = 0 + for k, v in COLORS.items(): + distance = self.distance(k) + if closest_distance > distance: + closest_distance = distance + closest_value = v + + self.cache_closest_printable_color = closest_value + self.update() + + return closest_value + +class Pixel: + char = " " + text_color = Color() + background_color = Color() + + cached_text_color_value = -1 + cached_background_color_value = -1 + + def __str__(self) -> str: + color = "" + + text_color = self.text_color.getClosestPrintableColor() + if text_color != self.cached_text_color_value: + color += COLOR_FORMAT % (text_color + COLOR_TEXT) + self.cached_text_color_value = text_color + + background_color = self.background_color.getClosestPrintableColor() + if background_color != self.cached_background_color_value: + color += COLOR_FORMAT % (background_color + COLOR_BACKGROUND) + self.cached_background_color_value = background_color + + + return color + self.char + + def reset(self): + self.cached_text_color_value = -1 + self.cached_background_color_value = -1 + +class Screen: + def __init__(self, width: int, height: int, std_handle: int = 0, render_funcs = []): + self.width = width + self.height = height + self.std_handle = std_handle + self.render_funcs = render_funcs + + self.time_start = datetime.datetime.now() + self.buffer = "" + + def get_time(self): + return (datetime.datetime.now() - self.time_start).total_seconds() + + def write(self, string): + self.buffer += string + + def flush(self): + print(self.buffer, end="", flush=True) + self.buffer = "" + + def mainloop(self): + pixel = Pixel() + + while True: + width, height = os.get_terminal_size(self.std_handle) + if width < self.width or height < self.height: + Screen.clear() + print("Unable to print the screen, a terminal with %dx%d (and above) chars are required to continue" % (self.width, self.height)) + while width < self.width or height < self.height: + width, height = os.get_terminal_size(self.std_handle) + + time = self.get_time() + + for y in range(self.height): + for x in range(self.width): + for render_func in self.render_funcs: + render_func(pixel, x, y, time) + self.write(str(pixel)) + pixel.reset() + self.write("\x1b[0m\n") # newline + + #Screen.clear() + #self.flush() + self.new_flush() + #sleep(.01) + + def new_flush(self): + self.buffer = "\x1b[0;0H" + self.buffer + self.flush() + + @staticmethod + def clear(): + print("\x1b[2J", end="", flush=True) + +COLOR_RESET = 0 +COLOR_TEXT = 30 +COLOR_BACKGROUND = 40 +COLOR_FORMAT = "\x1b[%dm" +COLORS = { + Color(0.0, 0.0, 0.0): 0, + Color(1.0, 0.0, 0.0): 1, + Color(0.0, 1.0, 0.0): 2, + Color(1.0, 1.0, 0.0): 3, + Color(0.0, 0.0, 1.0): 4, + Color(1.0, 0.0, 1.0): 5, + Color(0.0, 1.0, 1.0): 6, + Color(1.0, 1.0, 1.0): 7 +} \ No newline at end of file