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()