[Python-de] Re: "Statistik" graphisch in ASCII darstellen

Peter Otten __peter__ at web.de
Mit Jul 28 21:33:16 CEST 2004


Frank Immich wrote:

> jetzt hätte ich gerne das nicht Interpoliert wird, sondern
> bei einem Sprung der x-wert beibehalten wird...:
> also so..
> +
> |
> |   ..
> |  .  .
> |  .   .
> | ..   .
> | .     .
> |.       ..
> |          
> +-------------+
> 
> 
> leider fehlt mir hier die Idee wie ich das in mein Script einbauen
> kann...wahrscheinlich muss es völlig neu aufgebaut werden ?!
> Kann mir jemand einen Tip geben ?

Wäre es keine "ASCII-Kunst", würdest du die Kurve als Streckenzug
darstellen: (x1, y1)-(x2, y2)- ... -(xN, yN), bei dem die Punkte durch
gerade Linien verbunden sind. In Deinem Fall ist (xi, yi) == (i, l[i]).
Bresenham ist ein bekannter Algorithmus für solche Pixel-Strecken. Jetzt
muss nur noch die Zeichenfläche so verpackt werden, dass sie wie ein
Framebuffer aussieht, also beliebige "Pixel" durch ihre Koordinaten
bestimmt und eingefärbt werden können. Als "Farbe" lässt sich der jeweilige
Buchstabe verwenden. Ich habe das - Du ahnst es - im folgenden
durchexerziert.

Peter

def bresenham(x1, y1, x2, y2):
    # uebersetzt aus C
    # http://www.edepot.com/linebresenham.html
    if x2 >= x1:
        dx = x2 - x1
        incx = 1
    else:
        dx = x1 - x2
        incx = -1
    if y2 >= y1:
        dy = y2 - y1
        incy = 1
    else:
        dy = y1 - y2
        incy = -1

    x = x1
    y = y1

    if dx >= dy:
        dy <<= 1
        balance = dy - dx
        dx <<= 1

        while x != x2:
            yield  x, y
            if balance >= 0:
                y += incy
                balance -= dx
            balance += dy
            x += incx
        yield x, y
    else:
        dx <<= 1
        balance = dx - dy
        dy <<= 1

        while y != y2:
                yield x, y
                if balance >= 0:
                        x += incx
                        balance -= dy
                balance += dx
                y += incy
        yield x, y


class FrameBuffer:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.fill(" ")
        self.x = self.y = 0
    def fill(self, color):
        self._grid = [[color] * self.width for _ in range(self.height)]
    def moveto(self, x, y):
        self.x = x
        self.y = y
    def line(self, x1, y1, x2, y2, color):
        for x, y in bresenham(x1, y1, x2, y2):
            self.draw(x, y, color)
    def lineto(self, x, y, color):
        self.line(self.x, self.y, x, y, color)
        self.x = x
        self.y = y
    def draw(self, x, y, color):
        if 0 <= x < self.width and 0 <= y < self.height:
            self._grid[y][x] = color
    def show(self):
        grid = self._grid
        grid.reverse()
        for line in grid:
            print "".join(line)
        grid.reverse()


def main():
    yvalues = [1,3,3,6,6,5,4,2,1,1]
    width = len(yvalues) + 3
    height = max(yvalues) + 3
    f = FrameBuffer(width, height)

    # Koordinatenkreuz
    f.line(0, 0, width-1, 0, "-")
    f.line(0, 0, 0, height-1, "|")
    f.draw(0, 0, "+")
    f.draw(0, height-1, "+")
    f.draw(width-1, 0, "+")

    # Graph
    f.moveto(1, yvalues[0]+1)
    for x, y in enumerate(yvalues):
        f.lineto(x+1, y+1, "o")

    f.show()

if __name__ == "__main__":
    main()