ascii arts

Run Settings
LanguagePython
Language Version
Run Command
#!/usr/bin/env python # -*- coding: utf-8 -*- """ The program accept inputs of the form (x,y) – (x,y), (x,y) – (x,y), … where (x,y) are the coordinates of the line segments. The program accept an arbitrary number of line segments. The coordinate range is between 0 and 19 on x and 0 to 9 on y, and the output is a 20x10 array of ASCII characters. The program be able to handle any number of lines from any coordinates. """ import re import argparse POINT_PATTERN = r'\((\d+) *, *(\d+)\)' LINE_PATTERN = r'{point} *- *{point}'.format(point=POINT_PATTERN) def parse(input_string, pattern): """ parse start of the string using a pattern and return the remainings :param input_string: a sting :param pattern: a regex pattern :return: the matched part and the remaining :raises: ValueError """ if not pattern.startswith('^'): pattern = '^' + pattern match = re.search(pattern, input_string) if not match: raise ValueError('"{}" is invalid.'.format(input_string)) return match, input_string[len(match.group()):] def parse_to_integers(input_string): """ parse input :param input_string: input string :return: a list of (x0, y0, x1, y1) :raises: ValueError """ result = [] matched, remaining = parse(input_string, LINE_PATTERN) result.append(tuple(map(int, matched.groups()))) while remaining: matched, remaining = parse( remaining, ' *, *{}'.format(LINE_PATTERN)) result.append(tuple(map(int, matched.groups()))) return result def validate_coordinates(canvas_width, canvas_height, x0, y0, x1, y1): """ check if the points are not out of the canvas """ conditions = [ (0 <= x0 < canvas_width), (0 <= x1 < canvas_width), (0 <= y0 < canvas_height), (0 <= y1 < canvas_height), ] if not all(conditions): raise ValueError('({}, {}) - ({}, {}) is out of canvas'.format( x0, y0, x1, y1 )) return x0, y0, x1, y1 def find_dots(x0, y0, x1, y1): """ find dots for for a line on a canvas """ adj = x0 - x1 opp = y0 - y1 if adj == 0: return {y: [x0] for y in range(min(y0, y1), max(y0, y1) + 1)} if opp == 0: return {y0: range(min(x0, x1), max(x0, x1) + 1)} tan = opp / float(adj) result = {} if abs(tan) < 1: points = sorted([(x0, y0), (x1, y1)], key=lambda pt: pt[0]) for x in range(points[0][0], points[1][0] + 1): y = int(round(points[0][1] + tan * (x - points[0][0]))) result.setdefault(y, []).append(x) return result else: points = sorted([(x0, y0), (x1, y1)], key=lambda pt: pt[1]) for y in range(points[0][1], points[1][1] + 1): x = int(round(points[0][0] + (y - points[0][1]) / tan)) result.setdefault(y, []).append(x) return result def merge_by_row(dots_list): """ merge a list of dots by row :return: a dictionay mapping row to columns """ def _merge(x, y): for k, v in x.items(): x[k] = sorted(list(set(v + y.pop(k, [])))) x.update(y) return x return reduce(_merge, dots_list, {}) def render_row(row, canvas_width, symbol, blank): """ get the string representation of a row """ chars = [symbol if i in row else blank for i in range(canvas_width)] return ''.join(chars) def argument_parser(): """ argumment parser """ parser = argparse.ArgumentParser( description='ascii line art generator') description = ( 'input represeting lines in the format of' ' (x,y) – (x,y), (x,y) – (x,y), …') parser.add_argument('input', help=description) parser.add_argument( '--width', type=int, default=20, help='width of the canvas[default=20].') parser.add_argument( '--height', type=int, default=10, help='height of the canvas[default=10].') parser.add_argument( '--blank', default=' ', help='symbol representing blank pixel[default=" "].') parser.add_argument( '--symbol', default='x', help='symbol representing a pixel[default="x"].') return parser def main(argv=None): args = argument_parser().parse_args(argv) print('draw on a {} X {} canvas'.format(args.width, args.height)) try: lines = [ validate_coordinates(args.width, args.height, *ints) for ints in parse_to_integers(args.input) ] except ValueError as exc: print(exc) return dots_list = [find_dots(*line) for line in lines] rows = merge_by_row(dots_list) rendered = [ render_row(rows.get(y, []), args.width, args.symbol, args.blank) for y in range(args.height) ] print('\n'.join(rendered)) if __name__ == '__main__': main()
Editor Settings
Theme
Key bindings
Full width
Lines