Želví grafika - dokončení

11. 9. 2007
Doba čtení: 11 minut

Sdílet

V dnešním článku o jazyku Logo dokončíme část věnovanou želví grafice a jejímu použití. Ukážeme si, jak v některých implementacích Loga vykreslovat body, oblouky a elipsy, pracovat s texty i vyplňovat plochy. Nevynecháme ani popis práce s více želvami a využití přerušovacího systému pro obsluhu událostí.

Obsah

1. Pokročilá želví grafika – dokončení
2. Nastavení režimu grafické plochy
3. Vykreslování bodů, oblouků a elips
4. Rozšiřující grafické entity: texty a výplně
5. Práce s více želvami
6. Přerušovací systém – zpracování událostí
7. Odkazy na další informační zdroje
8. Obsah následující části tohoto seriálu

1. Pokročilá želví grafika – dokončení

V předchozích osmi částech tohoto seriálu jsme si vysvětlili a na demonstračních příkladech ukázali mnoho příkazů, pomocí kterých bylo možné pracovat s želví grafikou (turtle graphics). Dnes toto rozsáhlé téma dokončíme. Ukážeme si, jak lze v některých implementacích Loga vykreslovat body, oblouky a elipsy, pracovat s texty i vyplňovat uzavřené plochy. Také další možnosti želví grafiky, zejména možnost souběžné práce s větším množstvím želv a přerušovací systém, jsou dostupné pouze v některých interpreterech Loga (kupodivu se jedná spíše o doménu starších implementací). Jelikož se však jedná o zajímavé a přitom poučné vlastnosti, také se jimi budeme ve dvou kapitolách zabývat, i když se může s poměrně vysokou pravděpodobností stát, že je ve vámi používaném Logu nenaleznete. Pro začátek si jako malé rozcvičení zopakujeme použití vnořených počítaných smyček, aby bylo patrné, že i pro mnohé složité obrazce si vystačíme pouze se základními příkazy želví grafiky:

to spiral3
    clearscreen
    hideturtle
    repeat 3 [
        repeat 99 [
            right 5
            make "d 3*repcount
            repeat 3 [
                forward :d
                left 120
            ]
        ]
    ]
end

(draw 310 310)

spiral3 

logo1201
Obrázek 1: První demonstrační příklad spuštěný v Turtle Tracks

2. Nastavení režimu grafické plochy

Grafická plocha, po které se želva pohybuje, může být programově přepnuta do jednoho ze tří režimů. Režim první se nastavuje příkazem fence. Pokud je grafická plocha nastavena do tohoto režimu (módu) a želva dosáhne jednoho z viditelných okrajů grafické plochy, tj. pokud v grafickém uživatelském rozhraní narazí na okraj okna, je běh programu ukončen a do konzole Loga se vypíše chybové hlášení. Jak si ukážeme v následujících kapitolách, je možné chybovému hlášení předejít pomocí ručního nakreslení „ohrádky“ okolo celého okna a využití přerušovacího systému.

Režim druhý se nastavuje příkazem window a většinou se jedná o režim implicitní, tj. o režim nastavený při spuštění Loga (některé interpretery však implicitně pracují v režimu třetím, který je popsán v dalším odstavci). Pokud želva v tomto režimu dosáhne okraje viditelné části grafické plochy, pokračuje ve svém pohybu dále, tj. chová se tak, jakoby grafická plocha byla nekonečná. Pro mnoho programů je tento režim ideální, protože se želva může kdykoli vrátit do viditelné oblasti a pokračovat v práci bez přerušení programu i bez překreslení již vytvořeného obrazce. Pokud se želva skutečně „ztratí“, postačí zadat příkaz home, který ji vrátí na počátek souřadného systému.

Třetí režim se nastavuje příkazem wrap. V tomto režimu není grafická plocha nekonečná (z hlediska topologie se jedná o torus, nikoli o rovinu), protože se želva při dosažení některého okraje přesune na druhý konec okna (z levého okraje na pravý okraj, z horního okraje na okraj dolní atd.). Kresba je tedy celá viditelná a pomocí tohoto režimu je možné vykreslovat zajímavé obrazce, protože okraj grafické plochy slouží jako další kreslicí pomůcka. Některé interpretery Loga mají skutečnou grafickou plochu větší než viditelné okno, proto je nutné chování režimu wrap předem otestovat.

Chování všech tří režimů je ukázáno na následujícím testovacím příkladu, který byl odladěn pro implementaci Loga Turtle Tracks. Pro jiné implementace Loga se bude lišit nastavení velikosti obrazce tak, aby se při jeho vykreslování přešlo přes okraj viditelné části grafické plochy. Všimněte si, že v režimu fence se vykreslení ukončilo při dosažení spodního okraje (současně došlo k předčasnému ukončení běhu programu), režim window způsobil zobrazení výřezu kresby (zbytek kresby je vykreslen mimo viditelné okno) a v režimu wrap dochází k překryvu motivů.

; procedura pro vykreslení testovacího
; obrázku spirály
to squiral :angle
    make "side 0
    repeat 360 [
        forward :side
        right :angle
        make "side :side + 2
    ]
end

; vykreslení spirály při zapnutém režimu
; grafické plochy "fence"
to test_fence
    ; velikost grafické plochy cca 600x600 pixelů
    ; (platí pro Turtle Tracks)
    (draw 300 314)
    clearscreen
    hideturtle
    fence
    squiral 89
end

; vykreslení spirály při zapnutém režimu
; grafické plochy "window"
to test_window
    ; velikost grafické plochy cca 600x600 pixelů
    ; (platí pro Turtle Tracks)
    (draw 300 314)
    clearscreen
    hideturtle
    window
    squiral 89
end

; vykreslení spirály při zapnutém režimu
; grafické plochy "wrap"
to test_wrap
    ; velikost grafické plochy cca 600x600 pixelů
    ; (platí pro Turtle Tracks)
    (draw 300 314)
    clearscreen
    hideturtle
    wrap
    squiral 89
end

; vhodné je spustit pouze jeden z testů
test_fence
test_window
test_wrap 

logo1202
Obrázek 2: Výsledek běhu procedury test_fence

logo1203
Obrázek 3: Výsledek běhu procedury test_window

logo1204
Obrázek 4: Výsledek běhu procedury test_wrap

V režimu wrap je možné zcela změnit vzhled některých objektů, což je patrné i z následujícího obrázku. Pokud by byl zvolen větší výřez nebo by byl zapnutý režim window, skládal by se obrázek ze sady kružnic procházejících středem obrázku. Díky použitému režimu wrap došlo k rozdělení kružnic a jejich vzájemnému překrytí, což obrázek činí zajímavějším.

logo1205
Obrázek 5: Původně sada kružnic při zobrazení v režimu wrap

3. Vykreslování bodů, oblouků a elips

S pomocí klasicky pojaté želví grafiky je možné vytvářet obrazce, které se skládají pouze z úseček. Pro překvapivě velké množství objektů použití úseček plně dostačuje, avšak pro ulehčení práce se složitějšími motivy obsahují některé implementace Loga i rozšiřující příkazy sloužící k vykreslování dalších typů grafických objektů. Jedná se o příkazy pro vykreslení bodů, oblouků, elips, textů a o příkaz, který je možné použít k vyplnění uzavřených ploch. Tyto příkazy nejsou standardizované, proto se může stát, že v některé implementaci Loga nebudou dostupné, budou mít jiné pojmenování nebo budou akceptovat odlišnou strukturu parametrů. Následující text je tedy nutné chápat jako souhrn příkazů z různých interpreterů Loga, který není ani úplný ani univerzální.

Nejjednodušším rozšiřujícím příkazem je příkaz pro vykreslení bodu. Jméno tohoto příkazu (samozřejmě pokud je vůbec v dané implementaci Loga dostupný) je většinou dot, parametrem je seznam představující dvojici souřadnic x a y. Na tomto příkazu je zajímavé, že nemění pozici ani orientaci želvy. Další rozšiřující příkaz slouží pro vykreslení kruhového oblouku. Jméno tohoto příkazu je arc a dvojice parametrů, které se tomuto příkazu předávají, představují úhel oblouku zadaný ve stupních a jeho poloměr. Počátek oblouku i jeho orientace je určena pozicí a orientací želvy, ovšem želva není po vykreslení oblouku posunuta ani se nezmění její orientace. Použití příkazu arc je ukázáno na následujícím demonstračním příkladu:

; ukázka použití příkazu arc
to arc_demo
    clearscreen
    hideturtle
    ; budou se vykreslovat tři segmenty oblouků
    repeat 3 [
        ; změna poloměru oblouků v každém segmentu
        repeat 30 [
            arc 60 10*repcount
        ]
        left 120
    ]
end

; vykreslení sady kruhových oblouků
arc_demo 

logo1206
Obrázek 6: Obrázek vytvořený pouze z kruhových oblouků

Elipsy se mohou vytvářet pomocí procedury ellipse. Této proceduře se většinou předávají dva parametry, kterými se nastavují poloměry obou poloos elipsy. Střed elipsy leží v místě aktivní želvy, natočení většinou není možné specifikovat – vykreslí se tedy vždy elipsa orientovaná podle souřadných os. Pro tvorbu obecnějších elips je zapotřebí si vhodnou proceduru naprogramovat. Ovšem například LXlogo, které je k dispozici i pro Linux (http://lxlogo­.sourceforge.net/wel­come.html), obsahuje příkaz circrotate, kterým je možné elipsu do požadovaného směru natočit.

4. Rozšiřující grafické entity: texty a výplně

Mnohé implementace Loga rozšiřují grafické příkazy i o možnost výpisu textů do grafické plochy. Potřebný příkaz má jméno buď text nebo label. Řetězec znaků je vypsán na aktuální pozici želvy a orientován je ve směru jejího natočení. Následující demonstrační příklad byl odladěn v MSW Logu, podobným způsobem však bude pracovat i v UCB Logu a dalších interpreterech (dokonce by měly být zachovány i barvy). V Turtle Tracks bude řetězec zapsán na stejné místo obrazovky, protože v tomto interpreteru není možné měnit orientaci textu.

; ukázka použití příkazu label
to text_demo
    clearscreen
    hideturtle
    repeat 8 [
        setpencolor repcount-1
        label (list "Hello "world)
        left 360/8
    ]
end

; vykreslení osmi řetězců
text_demo 

logo1207
Obrázek 7: Osm různobarevných řetězců

Posledním rozšiřujícím grafickým příkazem, který může být v mnoha případech užitečný, je procedura sloužící pro vyplnění uzavřené plochy. Tento příkaz se jmenuje fill a není mu nutné předávat žádné parametry. Po zavolání příkazu fill se od pozice želvy začne s vyplňováním obrazce až do hranice, kterou musí tvořit úsečky nebo oblouky vytvořené dříve. Tato hranice musí být uzavřená, v opačném případě by mohlo dojít k vyplnění celé grafické plochy. Pro vyplňování je většinou použit algoritmus semínkového vyplňování (seed fill), který je v mnoha aplikacích nebo programovacích jazycích známý pod jménem flood fill. Podpora pro tento příkaz existuje například v UCB Logu nebo MSW Logu.

5. Práce s více želvami

Velmi zajímavá může být práce s více želvami současně. Některé interpretery programovacího jazyka Logo mají implementován příkaz setturtle, jemuž se předá číslo želvy, se kterou má program dále pracovat. Implicitní želva má číslo 0, maximální počet želv je závislý na konkrétní implementaci (Atari Logo má čtyři želvy, MSW Logo 1024 želv atd.). Želva vybraná příkazem setturtle provádí všechny kreslicí příkazy (forward, left, home…), zatímco ostatní želvy se zastaví – nejedná se tedy o skutečně současné provádění příkazů více želvami. Nastavení Loga do počáteční podoby, tj. do konfigurace, ve které je aktivní pouze želva číslo 0, zajistí mimo jiné i příkaz clearscreen. Jednoduché použití příkazu setturtle ukazuje následující program odladěný v MSW Logu:

; nastavení počáteční souřadnice
; vybrané želvy (bez kreslení)
to setup_turtle :number
    setturtle :number
    penup
    setpos (list -400+50*:number 0)
    pendown
end

; jeden krok při kreslení kružnice,
; který je proveden pro každou
; z osmi želv
to one_step :step :turn
    repeat 8 [
        setturtle repcount-1
        forward :step+repcount
        left :turn
    ]
end

; vykreslení kružnice, přičemž každá želva
; vytvoří kružnici o jiném poloměru
to circle :steps :step :turn
    repeat :steps [
        one_step :step :turn
    ]
end

; procedura, která otestuje
; kreslení všemi osmi želvami
; (platí pro MSW Logo)
to multi_turtles
    repeat 8 [
        setup_turtle repcount-1
    ]
    circle 180 1 2
end

; spuštění testu
multi_turtles 

logo1208
Obrázek 8: Osm želv vykreslilo kružnici v MSW Logu (želvy zůstávají zobrazeny)

K provádění skutečné paralelní práce více želv (ta je velmi efektní, a to i pro pokročilejší programátory) slouží v některých implementacích příkazy ask, each a tell. Procedura tell je vlastně zobecněním výše uvedené procedury setturtle. Místo čísla jedné želvy je však možné předat i celý seznam s čísly želv. Například pomocí tell [0 3 4 5] se pro provádění dalších kreslicích příkazů vyberou želvy číslo 0, 3, 4 a 5. Tyto želvy pracují paralelně, takže například příkaz forward 10 všechny želvy posune o 10 kroků nezávisle na tom, kde se nachází a jak jsou orientovány. V některých případech není vhodné vybírat želvy „natrvalo“, ale pouze za účelem provedení několika příkazů. Zde pomůže procedura ask, které se předají dva seznamy – seznam želv a seznam příkazů, které tyto želvy mají provést. Příklad:

ask [0 3 4 5] [left 45 forward 100] 

Příkaz each akceptuje jeden parametr, kterým je seznam příkazů (opět uzavřený v hranatých závorkách). Tyto příkazy budou provedeny všemi želvami současně – jedná se tedy o zkrácenou formu příkazu ask.

V případě, že nějaký interpreter Loga nabízí pouze příkaz setturtle nebo jeho ekvivalent, a ostatní příkazy pro paralelní ovládání želv (ask, each, tell) nám chybí, je možné tyto příkazy doprogramovat v samotném Logu. Článek na toto téma vydal Erich Neuwirth: Turtle Ballet: Simulating Parallel Turtles in a Nonparallel LOGO Version.

6. Přerušovací systém – zpracování událostí

Některé implementace programovacího jazyka Logo obsahují i přerušovací systém, na nějž jsou navázány metody pro zpracovávání událostí. Řízení běhu programů na základě událostí je velmi mocná programátorská technika, která nachází své uplatnění například při programování grafických uživatelských rozhraní, paralelních procesů, asynchronních přenosů dat atd. Mnoho programovacích jazyků tento způsob programování většinou vůbec nepodporuje – vše je ponecháno na programových knihovnách nebo API operačních systémů. V Logu existuje několik takzvaných zdrojů přerušení, například se jedná o stisk klávesy, přesun kurzoru myši nebo vypršení časového úseku běžícího časovače. S těmito typy přerušení se můžeme setkat i v jiných programovacích jazycích.

Kromě toho však mohou v Logu nastat přerušení i při práci se želví grafikou. Například se může jednat o střet želvy s okrajem grafické plochy, střet želvy s nakresleným objektem (většinou úsečkou) nebo dokonce o střet želv mezi sebou. Tento typ přerušení je možné využít například při programování her nebo některých typů algoritmů (procházení bludištěm). Při vytváření her se využijí také další (výše zmíněné) zdroje přerušení – stisk klávesy, tik časovače apod. Jakým způsobem se zadefinuje zpracování události? V Logu je to velmi jednoduché, postačuje použít proceduru when, které se předají dva parametry. Prvním parametrem je identifikace události (typicky jde o celočíselnou konstantu), druhým parametrem je seznam příkazů, které se mají provést v případě, kdy daná událost nastane (jde o takzvanou obsluhu události).

Čísla událostí mohou být v některých implementacích Loga nahrazeny symbolickými konstantami, konkrétní hodnoty a jejich význam je nutné hledat v referenční příručce. Pokud například konstanta 5 značí událost, jež nastane při nárazu želvy do okraje grafické plochy, můžeme pomocí obsluhy události zajistit, aby se želva otočila o 180° a posunula se v tomto směru o 10 kroků. Tím je zajištěno, že želva nikdy neopustí kreslicí plochu a její chování bude vzdáleně připomínat režim wrap.

when 5 [right 180 forward 10] 

Také je možné celou grafickou plochu obkreslit čtyřmi úsečkami a zaregistrovat obsluhu události pro střet želvy s nakresleným objektem. Obsluha událostí je od dané události „odregistrována“ například při provedení příkazu clearscreen.

logo1209

bitcoin_skoleni

7. Odkazy na další informační zdroje

  1. Stránky StarLoga na MIT,
    http://el.www­.media.mit.edu/grou­ps/el/Projects/star­logo/
  2. Stránky StarLoga na Northwestern University,
    http://ccl.nor­thwestern.edu/cm/star­logoT/
  3. Stránky NETLoga,
    http://ccl.nor­thwestern.edu/ne­tlogo/
  4. MSWLogo,
    http://www.sof­tronix.com/lo­go.html
  5. Erich Neuwirth: Multiturtle Logo code,
    http://sunsite­.univie.ac.at/mul­titurtle/
  6. Erich Neuwirth: Turtle Ballet: Simulating Parallel Turtles in a Nonparallel LOGO Version,
    University of Vienna, Dept. of Statistics and Decision Support Systems
  7. Brian Harvey: Computer Science Logo Style,
    MIT Press 2000

8. Obsah následující části tohoto seriálu

Seznamy patří mezi nejdůležitější datové struktury, se kterými se můžeme při programování setkat. Práce s nimi je na jednu stranu jednoduchá, na stranu druhou však umožňuje elegantní řešení mnohdy složitých algoritmů. V některých programovacích jazycích, jakými jsou Lisp, Logo či Python, seznam slouží jako základ pro tvorbu složitějších (lineárních i nelineárních) datových struktur, například front, stromů či obecných grafů. V těchto jazycích je práce se seznamy podporovaná už na nejzákladnější úrovni – jedná se například o programové smyčky for-each, „seznamové“ funkce typu apply, map či reduce apod. Z tohoto důvodu se v následující části seriálu o programovacím jazyce Logo budeme seznamy podrobněji zabývat.

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.