Per Erik Strandberg /cv /kurser /blog

I samband med att jag skaffade en Raspberry Pi och började programmera med barnen i Scratch, se Dagbok 20151215, så började jag fundera på Turtle Graphics i Scratch. När jag letade efter information om det så stötte jag på en paket för det i Python som jag ville undersöka. (Python Turtle Graphics hade något beroende på något tk-paket som jag behövde installera först).

Man kan tänka sig att man har en sköldpadda med en krita på magen. Där den går ritar man ett streck. Sen kan den svänga åt höger, vänster, backa, byta färg på kritan och så vidare. Ett enkelt exempel skulle kunna vara följande mästerverk:

import turtle

turtle.forward(10)
turtle.left(75)
turtle.forward(20)
turtle.right(150)
turtle.forward(40)

for i in range(15):
    turtle.left(150)
    turtle.forward(40 + 3*i)
    turtle.right(150)
    turtle.forward(40 + 4*i)

Ovanstående kod ritar följande spår (man ser sköldpaddan som en liten pil i bilden).
http://www.pererikstrandberg.se/blog/turtle/python-turtle-graphics-hello.png

Ett mönster som ofta återkommer är psykadeliska spiraler. Vi börjar med en enkel vinkelrät spiral. Här kan man se kommandot delay som vi sätter till 0 så att sköldisen rappar på lite.

import turtle as t

t.delay(0)

for n in xrange(240):
    t.forward(5*n)
    t.right(90)

http://www.pererikstrandberg.se/blog/turtle/python-turtle-graphics-spiral-0.png

Men nu kan vi störa den perfekta ordningen lite och få en mycket intressantare bild. Vi roterar inte 90 grader utan 89 och går med ett avstånd som ökar snabbare. Jag beskar den även så att spiralens centrum inte är i centrum av bilden.

    t.forward(n + 1.05**n)
    t.right(89)

http://www.pererikstrandberg.se/blog/turtle/python-turtle-graphics-spiral-1.png

OK, om ni är med så här långt så ska vi ta en kik på fraktaler också. Närmare bestämt en variant av Kochs snöflinga (efter svensken Helge von Koch som beskrev en av de första fraktala kurvorna, se svenska wikipedia: [1]). Skulle jag förklara fraktaler för mina barn så skulle jag säga något i stil med: "Tänk dig en rak linje. Sen gör du några hörn på linjen så att börjar och slutar på samma platser, men så den blir krokig. Nu zoomar du in och stoppar in samma hörn på varje rak del av linjen om och om igen tills du inte kan zooma längre - då har du en fraktal."

Om vi nu ska översätta detta till sköldpaddsgrafik så görs det nog enklast med rekursion. Jag säger åt sköldpaddan att istället för att göra en rak linje så ska vi:

1. Gör en kortare linje än hela

2. Sväng lite vänster

3. Gör lite av en linje

4. Sväng skarpt åt höger

5. Gör lite av en linje

6. Sväng lite vänster

7. Gå resten av linjen

MEN: istället för att göra raka linjer (steg 1, 3, 5 och 7) så ersätter man varje sådant rakt sträck med alla sju instruktioner igen, och igen, och igen, tills man gjort det så många gånger att man blir galen). Python-koden jag använde mig av ser ut så här [*]:

def draw_line(turtle, dist, level):
    """draw a line, perhaps with a twist"""

    if level == 0:
        turtle.forward(dist)
        return

    draw_line(turtle, dist/4.0, level-1)
    turtle.left(45)
    draw_line(turtle, sqrt(2)*dist/4.0, level-1)
    turtle.right(90)
    draw_line(turtle, sqrt(2)*dist/4.0, level-1)
    turtle.left(45)
    draw_line(turtle, dist/4.0, level-1)

    return

Och om jag ritar linjer av nivå 0 till 6 får jag detta:
http://www.pererikstrandberg.se/blog/turtle/python-turtle-graphics-recursion-line.png

Nu lägger jag till lite färger och gör linjen fyra gånger för att få en kvadrat. Jag låter även varje steg bli större än föregående. (Koden kommer under bilden.)

http://www.pererikstrandberg.se/blog/turtle/python-turtle-graphics-snow-flake.png

import turtle as t
from math import sqrt

t.delay(0)


def draw_line(turtle, dist, level):
    """draw a line, perhaps with a twist"""

    if level == 0:
        turtle.forward(dist)
        return

    draw_line(turtle, dist/4.0, level-1)
    turtle.left(45)
    draw_line(turtle, sqrt(2)*dist/4.0, level-1)
    turtle.right(90)
    draw_line(turtle, sqrt(2)*dist/4.0, level-1)
    turtle.left(45)
    draw_line(turtle, dist/4.0, level-1)
    return


def ksquare(turtle, dist, level):
    """make the "koch triangle"."""
    for _ in xrange(4):
        draw_line(turtle, dist, level)
        turtle.right(90)
    return


def get_colors(fifty):
    """Fifty shades of gray"""
    colors = list()
    ff = 0xFF
    for level in range(fifty):
        colors.append(ff - int((level+1.0)/fifty * 0xFF))
    return colors


if __name__ == "__main__":

    # size and size diff between snowflakes
    dist = 800
    levels = 8
    offset = dist / (levels+1)

    # from light to dark
    t.colormode(0xFF)
    colors = get_colors(levels)
    print colors

    # a good starting point
    t.up()
    t.back((dist - levels * offset)/2)
    t.left(90)
    t.forward((dist - levels * offset)/2)
    t.right(90)
    t.down()

    # make the snowflakes
    for level in range(levels):
        t.pencolor((colors[level], colors[level], colors[level]))
        ksquare(t, dist - (levels - level) * offset, level)
        t.up()
        t.back(offset/2)
        t.left(90)
        t.forward(offset/2)
        t.right(90)
        t.down()

    # pause for screenshots
    _ = raw_input("enter")


En kusin har skaffat en mycket geometrisk tatuering på ryggen och jag tänkte att jag skulle försöka rita den med en sköldpadda. Det går säkert, men det är nog ganska mycket jobb. Här kommer ett tafatt försök:

import turtle as t

d = 300

t.delay(0)

for i in range(18):
    t.right(180)
    t.circle(d/8)
    t.right(180)
    for j in range(12):
        t.forward(d)
        t.left(88)

_ = raw_input("enter")

http://www.pererikstrandberg.se/blog/turtle/tatuering.png


Se också Dagbok 20151223


Denna sida tillhör Kategori Programmering
Denna sida tillhör Kategori Plot

[*] Den som vill optimera med sqrt(2) får göra som hen vill.