Source code for pylib.drawblock
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Draw with block characters.
:Date: 2019-11-15
.. module:: drawblock
:platform: *nix, Windows
:synopsis: Draw with block characters.
.. moduleauthor:: Daniel Weschke <daniel.weschke@directbox.de>
"""
[docs]def histogram(f, x=None):
"""Histogram chart with block symbols.
::
dots
,_,
|8|
|7|
|6|
|5|
|4|
|3|
|2|
|1|
```
▁▂▃▄▅▆▇█
12345678
"""
from collections import defaultdict
chars = defaultdict(lambda: defaultdict(int))
# w/o comma no 2d tuple
pixel_map = ((0x2581,), # Lower one eighth block, also used for value 0
(0x2582,), # Lower one quarter block
(0x2583,), # Lower three eighths block
(0x2584,), # Lower half block
(0x2585,), # Lower five eighths block
(0x2586,), # Lower three quarters block
(0x2587,), # Lower seven eighths block
(0x2588,)) # Full block
#print(pixel_map)
pixel_rows = 8
pixel_cols = 1
def normalize(coord):
coord_type = type(coord)
if coord_type == int:
return coord
if coord_type == float:
return int(round(coord))
raise TypeError("Unsupported coordinate type <{0}>".format(type(coord)))
def get_pos(x, y):
"""Convert x, y to cols, rows"""
return x // 1, y // pixel_rows
def set(x, y):
"""Set a pixel of the :class:`Canvas` object.
:param x: x coordinate of the pixel
:param y: y coordinate of the pixel
"""
x = normalize(x)
y = normalize(y)
col, row = get_pos(x, y)
if type(chars[row][col]) != int:
return
chars[row][col] |= pixel_map[y % pixel_rows][x % pixel_cols]
# fill also below the tip
if row > 0:
for i in range(row):
chars[i][col] |= pixel_map[pixel_rows-1][x % pixel_cols]
def rows(min_x=None, min_y=None, max_x=None, max_y=None):
"""Returns a list of the current :class:`Canvas` object lines.
:param min_x: (optional) minimum x coordinate of the canvas
:param min_y: (optional) minimum y coordinate of the canvas
:param max_x: (optional) maximum x coordinate of the canvas
:param max_y: (optional) maximum y coordinate of the canvas
"""
if not chars.keys():
return []
minrow = min_y // pixel_rows if min_y is not None else min(chars.keys())
maxrow = (max_y - 1) // pixel_rows if max_y is not None else max(chars.keys())
mincol = min_x // 1 if min_x is not None else min(min(x.keys()) for x in chars.values())
maxcol = (max_x - 1) // 1 if max_x is not None else max(max(x.keys()) for x in chars.values())
result = []
for rownum in range(minrow, maxrow+1):
if not rownum in chars:
result.append('')
continue
maxcol = (max_x - 1) // 1 if max_x is not None else max(chars[rownum].keys())
row = []
for x in range(mincol, maxcol+1):
char = chars[rownum].get(x)
if not char:
row.append(chr(0x20))
#row.append(chr(0x2588))
elif type(char) != int:
row.append(char)
else:
row.append(chr(char))
result.append(''.join(row))
return result
def frame(min_x=None, min_y=None, max_x=None, max_y=None):
"""String representation of the current :class:`Canvas` object pixels.
:param min_x: (optional) minimum x coordinate of the canvas
:param min_y: (optional) minimum y coordinate of the canvas
:param max_x: (optional) maximum x coordinate of the canvas
:param max_y: (optional) maximum y coordinate of the canvas
"""
import os
result = os.linesep.join(reversed(rows(min_x, min_y, max_x, max_y)))
return result # py2: ret.encode('utf-8')
if x is None:
for xi, y in enumerate(f):
set(xi, y)
else:
for xi, y in zip(x, f):
set(xi, y)
return frame()