File: aa.py

package info (click to toggle)
python-aafigure 0.6-3
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 428 kB
  • sloc: python: 1,942; makefile: 112
file content (111 lines) | stat: -rw-r--r-- 3,463 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#!python
#
# This file is part of aafigure. https://github.com/aafigure/aafigure
# (C) 2006 Chris Liechti <cliechti@gmx.net>
#
# SPDX-License-Identifier:    BSD-3-Clause
"""\
Simple ASCII output of the rendered image.  Think of it as a low resolution
black and white image.
"""

import sys


class AsciiOutputVisitor:
    """\
    Render a list of shapes as ASCII art.
    Scaled, think of it as a low resolution black and white image.
    """

    def __init__(self, options):
        self.options = options
        self.image = {}
        self.scale = options['scale']

    def visit_image(self, aa_image):
        self.visit_shapes(aa_image.shapes)
        self.options['file_like'].write(self.create_image())

    def visit_shapes(self, shapes):
        for shape in shapes:
            shape_name = shape.__class__.__name__.lower()
            visitor_name = 'visit_{}'.format(shape_name)
            function = getattr(self, visitor_name, None)
            if function is not None:
                function(shape)
            else:
                sys.stderr.write("WARNING: don't know how to handle shape {!r}\n".format(shape))

    def visit_group(self, group):
        self.visit_shapes(group.shapes)

    def visit_point(self, point):
        self.image[point.x * self.scale, point.y * self.scale] = '#'

    def visit_line(self, line):
        x1, x2 = line.start.x * self.scale, line.end.x * self.scale
        y1, y2 = line.start.y * self.scale, line.end.y * self.scale
        if x1 > x2:
            x1, x2 = x2, x1
        if y1 > y2:
            y1, y2 = y2, y1
        dx = x2 - x1
        dy = y2 - y1
        if dx > dy:
            y = y1
            if dx:
                m = float(dy) / dx
            else:
                m = 0
            for x in range(int(x1), int(x2 + 1)):
                self.image[x, int(y)] = '#'
            y += m
        else:
            x = x1
            if dy:
                m = float(dx) / dy
            else:
                m = 0
            for y in range(int(y1), int(y2+1)):
                self.image[int(x), y] = '#'
            x += m

    def visit_rectangle(self, rectangle):
        x1, x2 = rectangle.p1.x * self.scale, rectangle.p2.x * self.scale
        y1, y2 = rectangle.p1.y * self.scale, rectangle.p2.y * self.scale
        if x1 > x2:
            x1, x2 = x2, x1
        if y1 > y2:
            y1, y2 = y2, y1
        for y in range(int(y1), int(y2)):
            for x in range(int(x1), int(x2)):
                self.image[x, y] = '#'

    def visit_label(self, label):
        x, y = int(label.position.x * self.scale), int(label.position.y * self.scale)
        for character in label.text:
            self.image[x, y] = character
            x += 1

    def __str__(self):
        return self.create_image()

    def create_image(self):
        """return a cropped image"""
        # find out size
        min_x = min_y = 1000000
        max_x = max_y = -1000000
        for x, y in self.image:
            min_x = min(min_x, x)
            max_x = max(max_x, x)
            min_y = min(min_y, y)
            max_y = max(max_y, y)
        # render image to lines of text, fill unused fields with a dot
        result = []
        for y in range(min_y, max_y+1):
            line = []
            for x in range(min_x, max_x+1):
                line.append(self.image.get((x, y), '.'))
            result.append(''.join(line))
        return u'{}\n'.format('\n'.join(result))