Fák rajzolása

Czirkos Zoltán, Rakitai Balázs · 2019.02.27.

Különféle fafajtákat kirajzoló program. Éljen a rekurzió!

Ha fogunk egy függőleges szakaszt (fekete), aztán abból oldalra kiindulva rajzolunk két újabb, rövidebb szakaszt, meg fölfelé is egyet (kék)… Aztán az így kapott szakaszokból kiindulva megint rajzolunk szakaszokat (piros), amelyek ugyanolyan szögben állnak az előzőekhez képest, mint az az elsőhöz; aztán meg az ekkor kapottakból ugyanúgy (zöld) és így tovább… Akkor kapunk egy fát. Itt a jobb oldali rajzon egy kék–piros–zöld mini fa pont úgy néz ki, mint a tőle eggyel balra lévő piros–kék–fekete fa, csak kisebb.

Ha mindezt úgy csináljuk, hogy a kiinduló vonal még vastagabb legyen, utána egyre vékonyabb vonalakat rajzoljunk; a kiinduló vonal még barna legyen, utána egyre zöldebb vonalakat rajzolunk, akkor már kész is van a fánk. Csak tovább kell vinni a rajzolást, nem 3 lépésben rajzolni az ágakat, hanem mondjuk 8-10 lépésben, és akkor tényleg fának néz majd ki, nem pedig antennának.

A fát rajzoló függvény nagyon egyszerű. Megkapja, hányadik szinten van, és milyen hosszú szakaszokat kell rajzolnia, továbbá az ágak adatait. Megrajzolja a törzset egy szakasszal. Utána pedig fogja az ágak listáját, és meghívja újra saját magát: de eltérő pozíciókban (mert a törzs valamely pontjáról indul ki az ág), eltérő szöggel (mert valamerre nő az ág a törzshöz képest), és kisebb hosszal (mert az ágak egyre rövidebbek). A rekurziót az n paraméter csökkenése állítja meg:

FÜGGVÉNY fát_rajzol(n, ágak, hossz)
    HA n = 0, VÉGE
    
    szakasz(hossz)
    CIKLUS végig az ágakon
        elmozdulás/fordulás az ághoz
        fát_rajzol(n - 1, ágak, hossz - rövidülés)
        vissza az eredeti helyre
    CIKLUS vége
FÜGGVÉNY vége

A fák változatosságát az ágak paraméterei adják. Ha nagy szögben hajlanak el, terebélyes fát kapunk, ha kicsiben, inkább magasat. Ha az ágak aljáról indítjuk az elágazásokat, bokrokat rajzol a program. Ha gyorsan rövidülnek az ágak (a „szülő” águkhoz képest), akkor facsemetéket. Az egyes ágak tulajdonságait ezért eltároljuk egy objektumban:

class AgAdat:
    def __init__(self, doles, helyzet, rovidules):
        # merre dől az eredeti ághoz képest (fok)
        self.doles = doles
        # honnan indul ki (0 = legalul, 1 = legfelül)
        self.helyzet = helyzet
        # mennyit rövidül (0 = semennyit, 1 = lenullázódik)
        self.rovidules = rovidules

Minden ágból három másikat indítunk ki, egyet balra, egyet felfelé, egyet pedig jobbra (tehát a szögeket így kell majd beállítani). A rajzoló függvény az ágak listáját fogja átvenni. További változatosságot lehet bevinni a fák színének beállításával, az egyes ágak dőlésszögének kisebb véletlenszerű módosításaival és így tovább.

Hogy a sok véletlenszámot és lehetséges számtartományt könnyen tudjuk kezelni, érdemes egy intervallumot megadó típust is létrehozni, és egy ilyet átvevő véletlenszám-generátor függvényt írni:

class Intervallum:
    def __init__(self, min, max):
        self.min = min
        self.max = max

def itv_rand(itv):
    return random.uniform(itv.min, itv.max)

A fa rajzolása Python nyelven alig bonyolultabb a fenti pszeudokódnál. Közben a törzs barnájától a levelek zöldjéig egy színátmenetet kell létrehozni. Ezt két szín típusú objektum segítségével érjük el: minden lépésben csökken a szín vörös(-es barnás) komponense, és erősödik a zöld komponense. Ezt állítják be az RGB típusú objektumok összetevői.

def fat_rajzol(n, agak, hossz, dolesrand, szin, szin_valtozas):
    if n == 0:
        return
    
    szakasz(n, hossz, szin)
    for ag in agak:
        # beállítjuk, honnan indul majd a következő ág
        szog = ag.doles + itv_rand(dolesrand)
        turtle.forward(hossz * (1 - ag.helyzet)) 
        turtle.left(szog)
        fat_rajzol(n - 1, agak, hossz * (1 - ag.rovidules), dolesrand,
                   szin + szin_valtozas, szin_valtozas)
        # visszamegyünk az eredeti pozícióba
        turtle.right(szog)
        turtle.backward(hossz * (1 - ag.helyzet)) 

A paraméterek hatása a fa alakjára:

dőlés = 10°, 20°, 35°
helyzet = 0.15, 0.5, 0.85
rövidülés = 0.2, 0.35, 0.5
dőlésrand = 0, ±5°, ±10°

A teljes program letölthető innen: fa.py. A működés egy-két további részletével kapcsolatban lásd a forráskód kommentjeit!