Last active
November 14, 2021 17:12
-
-
Save iliakonnov/6dc535f8d4227a7fc604aca1cc73a76b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from dataclasses import dataclass, replace | |
from functools import wraps | |
from math import sin | |
SYMBOL_HEIGHT, SYMBOL_WIDTH, USE_COLORS = { | |
'1x1': (1, 1, True), | |
'2x2': (2, 2, True), | |
'braille': (4, 2, False), | |
}['2x2'] | |
FONT_SCALE = 14 / 7 | |
X_SCALE = (SYMBOL_WIDTH / SYMBOL_HEIGHT) * FONT_SCALE | |
def get_char(bits): | |
bits = [1 if int(i) else 0 for i in bits] | |
if len(bits) == 1: | |
return '█' if bits[0] else ' ' | |
if len(bits) == 4: | |
''' | |
1 2 | |
3 4 | |
''' | |
char = bits[0] + 2*bits[1] + 4*bits[2] + 8*bits[3] | |
return [ | |
' ', # 0000 | |
'▘', # 0001 | |
'▝', # 0010 | |
'▀', # 0011 | |
'▖', # 0100 | |
'▌', # 0101 | |
'▞', # 0110 | |
'▛', # 0111 | |
'▗', # 1000 | |
'▚', # 1001 | |
'▐', # 1010 | |
'▜', # 1011 | |
'▄', # 1100 | |
'▙', # 1101 | |
'▟', # 1110 | |
'█', # 1111 | |
][char] | |
if len(bits) == 8: | |
''' | |
1(0) 4(1) | |
2(2) 5(3) | |
3(4) 6(5) | |
7(6) 8(7) | |
''' | |
char = ( 1 * bits[0] | |
+ 2 * bits[2] | |
+ 4 * bits[4] | |
+ 8 * bits[1] | |
+ 16 * bits[3] | |
+ 32 * bits[5] | |
+ 64 * bits[6] | |
+ 128* bits[7]) | |
return chr(10240 + char) | |
return '□' | |
def get_color(color): | |
colors = { | |
0b000: (0, (0, 0, 0 )), # black | |
0b100: (1, (255, 0, 0 )), # red | |
0b010: (2, (0, 255, 0 )), # green | |
0b110: (3, (255, 255, 0 )), # yellow | |
0b001: (4, (0, 0, 255)), # blue | |
0b101: (5, (255, 0, 255)), # magenta | |
0b011: (6, (0, 255, 255)), # cyan | |
0b111: (7, (255, 255, 255)), # white | |
} | |
code, *_ = colors[color] | |
return f'\x1b[3{code}m' | |
@dataclass | |
class Cell: | |
is_set: int = 0 | |
color: int = 0 | |
def __post_init__(self): | |
self.is_set = int(self.is_set) | |
def __int__(self): | |
return self.is_set | |
@property | |
def effective_color(self): | |
return self.color * self.is_set | |
def __or__(self, other): | |
return Cell( | |
is_set = self.is_set | other.is_set, | |
color = self.effective_color | other.effective_color | |
) | |
class Matrix: | |
@classmethod | |
def empty(cls, width, height): | |
data = [[Cell() for i in range(width)] for j in range(height)] | |
return Matrix(data) | |
def __init__(self, data): | |
if data and data[0] and not isinstance(data[0][0], Cell): | |
data = [[Cell(int(i)) for i in row] for row in data] | |
height = len(data) | |
width = max(len(row) for row in data) if height else 0 | |
self._size = (width, height) | |
self._data = data | |
def size(self): | |
return self._size | |
def __setitem__(self, idx, cell): | |
x, y = idx | |
self._data[y][x] |= cell | |
def __getitem__(self, idx): | |
x, y = idx | |
return self._data[y][x] | |
def __call__(self, x, y): | |
if y >= len(self._data): | |
return Cell() | |
row = self._data[y] | |
if x >= len(row): | |
return Cell() | |
return row[x] | |
def plot_matrix(matrix): | |
width, height = matrix.size() | |
y = 0 | |
while y <= height: | |
x = 0 | |
while x <= width: | |
char = [] | |
color = 0 | |
for dy in range(SYMBOL_HEIGHT): | |
for dx in range(SYMBOL_WIDTH): | |
cell = matrix(x+dx, y+dy) | |
char.append(cell.is_set) | |
color |= cell.effective_color | |
char = get_char(char) | |
if USE_COLORS: | |
color = get_color(color) | |
print(f'{color}{char}\x1b[0m', end='') | |
else: | |
print(char, end='') | |
x += SYMBOL_WIDTH | |
print(flush=False) | |
y += SYMBOL_HEIGHT | |
def centered(matrix, func): | |
width, height = matrix.size() | |
result = lambda x, y: func((x - width // 2) / X_SCALE, (height // 2 - y)) | |
return wraps(func)(result) | |
def plot_bool(matrix, func, **kwargs): | |
kwargs['color'] = kwargs.get('color', 0b111) | |
template = Cell(**kwargs) | |
func = centered(matrix, func) | |
width, height = matrix.size() | |
cnt = 0 | |
print(f' - Rendering {func.__name__}...') | |
for y in range(height): | |
for x in range(width): | |
cell = replace(template, is_set=func(x, y)) | |
matrix[x, y] = cell | |
if cnt == 0: | |
print(f'\x1b[F({x:03}, {y:03})') | |
cnt = 300 | |
cnt -= 1 | |
def plot_border(matrix, func, **kwargs): | |
@wraps(func) | |
def check_corners(x, y): | |
corners = [ | |
func(x - 0.5, y - 0.5), | |
func(x - 0.5, y + 0.5), | |
func(x + 0.5, y - 0.5), | |
func(x + 0.5, y + 0.5), | |
] | |
return any(corners) and not all(corners) | |
return plot_bool(matrix, check_corners, **kwargs) | |
def function(code): | |
result = eval(f'lambda x, y: {code}') | |
result.__name__ = f'{{{code}}}' | |
return result | |
matrix = Matrix.empty(int(300 * X_SCALE), 300) | |
plot_border(matrix, function('y <= 0')) | |
plot_border(matrix, function('0 <= x')) | |
# circle: | |
plot_bool(matrix, function('(x+12)**2 + (y+12)**2 <= 15**2'), color=0b100) | |
# parabola: | |
#plot_border(matrix, lambda x, y: y <= x*x) | |
# sin: | |
plot_border(matrix, function('y <= 10*sin(x/5)'), color=0b001) | |
# hyperbola: | |
plot_border(matrix, function('y <= 270/x if x != 0 else False'), color=0b010) | |
plot_matrix(matrix) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment