Geometrické praktikum Jan Laštovička, Martin Kauer 15. března 2017 1 Kreslení objektů v rovině Nejdříve si představme základní objekty a metody se kterými budeme pracovat, jsou to body a vektory. Funkce point vytvoří bod na daných souřadnicích. Podobně pracuje fuknce vect vytvářející vektory. Následující funkce pracují jak s vektory tak body. Funkce coordinates vrací souřadnice bodu nebo vektoru v seznamu. Funkce x, y a z zjišt ují první, druhou a třetí souřadnici. Dimenzi prostoru, ve kterém se bod nebo vektor nachází, zjistíme funkcí dimension. Pro získání normy vektoru lze použít funkci norm. Náš souřadný systém je nastaven takovým způsobem, že počátek, tj. bod o souřadnicích (0, 0), je v levém dolním rohu, osa x směřuje doprava a osa y nahoru. Úkol 1. Otevřete si soubor lgl.lisp a dopište chybějící těla následujících metod. Násobení skaláru vektorem - mult. Sčítání dvou vektorů - plus. Přičtení vektoru k bodu - plus. Vektor vedoucí od jednoho bodu k druhému vrátí funkce minus. U vektoru, který je z prostoru dimenze dva, zjišt uje funkce phi jeho odchylku od kladné poloosy x. Funkce rotate otočí vektor dimenze dva o zadaný úhel. Po dokončení prvního úkolu si můžete vyzkoušet následující příklad. Nahrajte knihovnu lisp-gl načtením (například z nabídky File > Load...) souboru load.lisp a vyhodnot te následující kód: (opengl () (color (color-point :red)) (polygon (vertex (point 0 0)) (vertex (point 500 0)) (vertex (point 0 200)))) Pokud jste první úkol naimplementovali správně, otevře se nové okno s červeným trojúhelníkem. 1
OpenGL okno s jedním trojúhelníkem. Postupně si rozebereme právě použitý kód. Začneme makrem opengl, které vykresluje obsah do nově vytvořeného OpenGL okna. Toto makro může být použito pouze v top-levelu, tzn. nelze ho použít v tělech funkcí. Zjednodušená syntax makra je následující: (opengl (module ) form ) Moduly zajišt ují dodatečnou funkčnost plátna a lze je i kombinovat. Modul 2d nakreslí v počátku šipky naznačující osy souřadného systému a umožní posouvat počátek souřadného systému pomocí kláves w, a, s, d. Další moduly si představíme až dle jejich potřeby. Smyslem výrazů uvnitř makra je kreslit obsah okna. Při potřebě překreslit okno jsou tyto výrazy postupně vyhodnocovány. Funkce color mění aktuální barvu. Do další změny jsou všechny objekty kresleny nastavenou barvou. Funkce jako svůj argument bere bod z jednotkové krychle určující barvu v RGB modelu. Funkce color-point vrací bod v krychli reprezentující barvu zadanou jejím názvem. Tedy (color (color-point :red)) nastaví aktuální barvu na červenou. Makro polygon kreslí konvexní polygon. Jeho argumenty jsou výrazy, které jsou postupně vyhodnocovány. Jejich významem je zadávání vrcholů polygonu. Funkce vertex bere dvourozměrný bod, který je poslán do grafické karty jako další vrchol polygonu. Funkce point vytvoří bod o zadaných souřadnicích. Úkol 2. Napište funkci regular-polygon, která bere střed (bod), poloměr a počet vrcholů. Funkce nakreslí pravidelný mnohoúhelník vepsaný do kružnice. Vrchol mnohoúhelníku směřuje nahoru. Úkol 3. Naprogramujte funkci circle, která bere střed (bod) a poloměr, kreslící kruh. 2
Úkol 4. Napište funkci star kreslící hvězdu. Funkce bere střed (bod), dva poloměry (r 1 a r 2 ) a počet cípů. Hvězdě je opsaná kružnice o poloměru r 1 a do hvězdy je vepsaná kružnice o poloměru r 2. Cíp hvězdy směřuje nahoru. Výsledek volání (star (point 400 300) 150 50 5). 2 Konvexní polygony Makro opengl neumožňuje ukládání vlastností kreslení (např. barva objektu) a ani definici obsluh uživatelských akcí (např. kliknutí myši). Tuto funkcionalitu poskytuje obecnější makro opengl-canvas jehož zjednodušená syntax je následující: (opengl-canvas (module ) ({var (var [init-form])} ) (function-name lambda-list form ) ) Druhý argument makra je definice proměnných kreslícího plátna, které se definují stejně jako u makra let. Takto definované proměnné jsou dostupné v celém těle makra opengl-canvas. Za proměnnými následuje definice lokálních funkcí podobná jako u makra labels. Následuje výčet funkcí, které lze takto definovat. Funkce display nebere žádný argument a stará se o kreslení obsahu na plátno. Funkce mouse-press-primary bere bod. Je volána v případě, že došlo ke zmáčknutí hlavního (primárního) tlačítka myši. Do argumentu je dána pozice myši. Funkce mouse-press-secondary funguje stejně jako mouse-press-primary s tím rozdílem, že je volána v případě stisku sekundárního tlačítka. V těle funkcí je možno přistupovat k proměnným plátna (číst jejich hodnotu a nastavovat ji makrem setf). Funkce key-press očekává znak. Po stisku klávesy je funkce zavolána a znak klávesy je jí předán jako argument. Po zpracování kliku myši či stisku klávesy je okno automaticky překresleno. 3
Poznámka 1. Objekt reprezentující znak c získáme vyhodnocením #\c. Např znak z je reprezentován jako #\z a znak klávesy backspace jako #\Backspace apod. Následující příklad zobrazí okno s trojúhelníkem jehož barva se mění po stisku tlačítka myši. (opengl-canvas () ((color :red)) (display () (color (color-point color)) (polygon (vertex (point 0 0)) (vertex (point 500 0)) (vertex (point 0 100)))) (mouse-press-primary (point) (if (eql color :red) (setf color :blue) (setf color :red)))) Před zadáním úkolů si představíme funkci cross-product, která vrátí vektorový součin dvou vektorů dimenze tři, jehož výsledkem je vektor kolmý k oběma zadaným vektorům směřující do poloprostoru určeného pravidlem pravé ruky. Vektorový součin je formálně definován následovně v r = v r sinθ n, kde v, r jsou vektory, θ je úhel, který svírají v rovině jimi určené a n je jednotkový vektor, kolmý na rovinu určenou vektory v, r, směřující do poloprostoru určeného pravidlem pravé ruky. S použitím představené funkce splňte následující úkoly. Úkol 1. Napište funkci, která rozhodne, zda je polygon zadaný jako seznam vrcholů (bodů) konvexní. Následuje nápověda, číst jen v krajní nouzi. Představte si, že projdete po stranách celý obvod polygonu. Pokud jste při změně strany vždy zatočili na stejnou stranu, tak polygon je konvexní. Stranu na kterou zatáčíte můžete určit pomocí vektorového součinu. Úkol 2. Napište funkci rozhodující, zda je bod v konvexním polygonu. Následuje nápověda, číst jen v krajní nouzi. Platí to samé, jako u předchozího úkol, jen si kontrolujete, na jaké straně leží zadaný bod. Úkol 3. Vytvořte okno s následujícím chováním. Po stisku hlavního tlačítka se přidá vrchol do konvexního polygonu. Po prvních dvou stiscích, kdy se zadávají první dva vrcholy polygonu, zůstane okno prázdné. Po třetím stisku se zobrazí trojúhelník. Další body se budou přidávat k polygonu, ale pouze v případě, že polygon zůstane konvexní. V opačném případě se zobrazí (pomocí funkce capi:display-message) chybová hláška. Pokud se do takto vznikajícího polygonu klikne sekundárním tlačítkem, dojde k změně jeho barvy. Po stisku klávesy Backspace se z polygonu odebere poslední vrchol. 4
3 Souřadný systém modelu Vrcholy polygonů jsou zadávané v souřadném systému modelu. Jeho výchozí nastavení je následující. Počátek se nalézá v levém dolním rohu. Osa x směřuje vpravo a osa y nahoru. Měřítko os je nastaveno tak, aby jednotka měla velikost jednoho pixelu. Následující tři funkce slouží k transformaci souřadného systému modelu. Funkce c-s-translate (c-s je zkratkou za coordinate system) bere vektor zadaný v souřadném systému modelu. Funkce posune počátek o vektor. Funkce c-s-scale bere dva koeficienty (čísla). Funkce změní měřítka os podle zadaných koeficientů (první koeficient je použit na osu x). Nakonec funkce c-s-rotate otočí osy o zadaný úhel proti směru hodinových ručiček. Před kreslením obsahu okna je souřadný systém vrácen do své výchozí polohy. Poznámka 2. Vyzkoušejte si, že na pořadí transformací záleží. Opravdu, pokud uděláte jeden krok dopředu a pak se otočíte doprava, skončíte na jiné pozici než když se napřed otočíte doprava a pak uděláte krok dopředu. Při kreslení je k dispozici zásobník, na který je možné ukládat souřadné systémy modelu makrem with-c-s-pushed. Makro bere libovolný počet výrazů. Nejdříve vloží souřadný systém modelu na zásobník, poté postupně provede výrazy a na závěr odebere souřadný systém ze zásobníku a učiní jej aktuálním. Neformálně, transformace souřadného systému uvnitř tohoto makra jsou platné od jejich aplikace do konce rozsahu makra. Po vyhodnocení celého makra, je souřadný systém vrácen do původního stavu, ve kterém byl před začátkem vyhodnocení tohoto makra. Klasické použití tedy vypadá následovně. (with-c-s-pushed (c-s-translate (vect 25 25)) (c-s-scale 40 10)...draw-something...) (with-c-s-pushed (c-s-rotate (/pi 4)) (c-s-translate (vect 50 100))...draw-something-else...) Úkol 1. Vytvořte okno s robotickou paží zachycené na následujícím obrázku. Paže se bude natahovat a ohýbat stiskem tlačítek + a -. Ke kreslení paže můžete použít pouze čtverec o hraně délky jedna. 5
Robotická paže se může natahovat a ohýbat. Zápočtový úkol 1. Vytvořte funkci tree jednoho argumentu kreslící strom. Argumentem je nezáporné číslo určující hloubku větvení stromu. Funkci vyzkoušejte (pomocí makra opengl-canvas). Výsledek by se měl co nejvíce blížit následujícím obrázkům zachycujím popořadě testy fce tree s argumenty 2, 3 a 6. Ke kreslení stromu můžete použít pouze čtverec o straně délky jedna. Výsledek volání (tree 2). 6
Výsledek volání (tree 3). 4 Třetí dimenze Výsledek volání (tree 6). Plátno pracující s trojrozměrným prostorem se vytvoří makrem opengl-canvas nastavením proměnné dimension na 3. Tímto se posune počátek souřadného systému do středu okna a navíc bude obsahovat osu z, která je ve výchozí pozici kolmá na osy x a y a směřuje směrem k uživateli. Funkce pracující s trojrozměrným prostorem očekávají jako argumenty trojrozměrné body a vektory. Například funkce vertex očekává trojrozměrný bod. Následující kód nakreslí dva do sebe zakleslé trojúhelníky. 7
(opengl-canvas () ((dimension 3)) (display () (color (color-point :red)) (polygon (vertex (point 0 0 100)) (vertex (point 500 0 100)) (vertex (point 0 100 100))) (color (color-point :blue)) (polygon (vertex (point 0 0 0)) (vertex (point 200 0 200)) (vertex (point 200 100 200))))) Abychom mohli jednoduše pracovat se třetím rozměrem, představíme si modul 3d, který nastaví dimenzi plátna na 3 a postupně rotuje kolem kladných částí os x, y a z o úhly, které je možné měnit stisky následujících kláves. Osu x rotují klávesy w a s, osu y klávesy a a d a osu z klávesy q a e. Následující kód nakreslí trojúhelník, se kterým je možné stisky kláves rotovat. (opengl (3d) (color (color-point :red)) (polygon (vertex (point 0 0 0)) (vertex (point 200 0 0)) (vertex (point 0 100 0)))) Další užitečný modul se jmenuje level, který přidá plátnu číselnou proměnnou level. Ve výchozím nastavení má hodnotu 0 a stiskem kláves +, - se hodnota inkrementuje a dekrementuje (ale ne do záporných hodnot). Abychom tuto proměnnou mohli používat, představme si ještě následující makro, které je možné použít ve všech vnitřních funkcích plátna. (with-canvas-variables (variable ) form ) Ve výrazech makra je možné číst a nastavovat vyjmenované proměnné. (opengl (3d level) (with-canvas-variables (level)...draw something with detail lvl = level...)) Poznámka 3. Nezapomeňte, že moduly se dají kombinovat. Můžete tedy použít zároveň modul 3d i level. 8
V trojrozměrném prostoru mají rotující funkce (rotate a c-s-rotate) nepovinný argument rotation-vector, což je trojrozměrný vektor, kolem kterého se bude točit. Například příkaz (c-s-rotate pi (vect 1 0 0)) otočí soustavu kolem kladné části osy x o 180. Implicitně je tento argument vektor o souřadnicích (0, 0, 1). Rozšíříme si repertoár funkcí pracující s body a vektory. Funkce normalize vrátí vektor velikosti jedna stejného směru jako zadaný vektor. Funkce minus-origin bere bod a vrátí vektor vedoucí z počátku (bod o nulových souřadnicích) k zadanému bodu. Naopak funkce plus-origin vrátí k zadanému vektoru bod, který vznikne sečtením počátku a vektoru. Poznámka 4. Všechny úkoly z této kapitoly budeme používat ve zbytku seminářů. Pokud je nestihnete naprogramovat na semináři, doprogramujte si je do začátku dalšího semináře. Poznámka 5. Následující úkoly pracují s jednotkovou koulí, před vykreslováním výsledků úkolů si nezapomeňte změnit měřítka os pomocí funkce c-s-scale. Úkol 1. Nakreslete pravidelný osmistěn(octahedron) vepsaný do jednotkové koule se středem v počátku. Pootočený osmistěn. Úkol 2. Nakreslete subdivizi trojúhelníku, nově vzniklé trojúhelníky nakreslete rozdílnými barvami. 9
Subdivize rozdělí vstupní trojúhelník na čtyři menší trojúhelníky. Úkol 3. Naprogramujte rekurzivní fci subdivision, která přijme dva argumenty: trojúhelník tria a nezáporné číslo level. Funkce nakreslí subdivizi trojúhelníku tria s vrcholy ležícími na jednotkové kouli se středem v počátku. Všechny vrcholy včetně nově vzniklých musí ležet na jednotkové kouli. Argument level určuje úroveň zanoření subdivize. Úroveň jedna znamená jednu subdivizi, úroveň dva znamená udělat první subdivizi a na výsledné trojúhelníky aplikovat znovu subdivizi atd. Výsledek volání (subdivision (list (point 1 0 0) (point 0 1 0) (point 0 0 1)) 2). Úkol 4. Pomocí subdivize osmistěnu naprogramujte funkci sphere, která kreslí aproximaci jednotkové koule. Funkce přijme nezáporné číslo, které určí úroveň subdivize. Výsledek volání fce (sphere 2). 10