5 Algoritmy vyplňování 2D oblastí Studijní cíl Tento blok je věnován základním algoritmům pro vyplňování plošných objektů. V textu bude vysvětlen rozdíl mezi vyplňováním oblastí, které jsou definovány rastrovým obrazem a oblastí, které jsou definovány vektorově. Dále budou představeny modifikace pro vyplnění jednolitou barvou, barevným přechodem, šrafováním a vlastním vzorem, který je definován pomocí matice vzoru. Pro vybrané algoritmy bude ukázána i jejich základní implementace. Doba nutná k nastudování 3-4 hodiny Průvodce studiem Při studiu tohoto bloku se předpokládá, že student je seznámen se základy analytické geometrie, dokáže použít vztahy pro nalezení průsečíků dvou přímek, zvládá základy práce s vektory, dokáže určit směrový a normálový vektor přímky, chápe význam skalárního a vektorového součinu a dokáže pracovat s jednotlivými pixely rastrového obrazu. 5.1 Definice oblasti a její rasterizace Pro potřeby dalšího výkladu lze definovat oblast (area) jako uzavřenou část plochy, která je charakterizována především svým okrajem (hranicí). Oblast může, ale také nemusí být konvexní. Součástí hranice oblasti pro potřeby vyplňování se mohou stát i okraje zobrazované plochy (okna). Úplná definice oblasti zahrnuje samotnou definici hranice, způsob vykreslení hranice, způsob vykreslení vnitřních bodů oblasti, vztažný bod pro počátek výplně, který bude použit při složitějším způsobu vyplňování, např.: vzorem, šrafováním nebo texturou. KST/IPOGR 1-1 Petr Veselý
Rasterizace oblasti řeší nalezení všech vnitřních zobrazovaných bodů (pixelů) dané oblasti a definování jejich barvy. K rasterizaci oblasti nelze použít již dříve představené rasterizační algoritmy liniových objektů, neboť ty jsou schopny nalézt pouze obrazové body příslušné hranice dané oblasti. Hranice je možno všeobecně chápat jako objekt, který vzájemně odděluje vnitřní plochu (body) oblasti od jejího okolí. Při specifikování oblasti může být použito obecně dvou způsobů definice hranice: Hranice určená geometricky (vektorově) Je tvořena posloupností bodů (definujících mnohoúhelník), případně posloupností geometricky popsaných liniových grafických primitiv (úsečky, křivky). Ve druhém případě je pro jednoduchost vhodné provést aproximaci křivek na lomenou čáru, neboť některé algoritmy požadují na vstupu právě posloupnost bodů definujících mnohoúhelník. Předpokladem vektorově definované hranice je uzavřenost oblasti (poslední bod = první bod, popřípadě automatické propojení posledního a prvního bodu). Hranice určená rastrovým obrazem Tato oblast je uřčena pouze obsahem (barvou) jednotlivých pixelů ve vybraném rastrovém podkladu. Za rastrový podklad může být považován i obsah obrazové paměti. Podstatná pro určení vnitřních bodů oblasti je specifikace barvy bodů tvořících hranici, nebo specifikace barvy vnitřních bodů oblasti. Při tomto způsobu definice hranice musí být uživatelem (nebo programem) určen jeden obrazový bod (pixel), který patří mezi vnitřní body (tzv. semínko). Rasterizační algoritmy se obecně dělí na dvě skupiny podle toho, se kterou definicí hranice dokáží pracovat. Dále budou představeny vybrané algoritmy, které jsou schopny vyplnit vektorově nebo rastrově definovanou oblast. 5.2 Způsob vyplnění Pro většinu algoritmů platí, že jsou schopny provést výplň oblasti (nastavení barvy vnitřních bodů oblasti) jedním z následujících způsobů (případně jejich kombinací): KST/IPOGR 1-2 Petr Veselý
Výplň jednou určenou barvou, tzv. jednolité souvislé vyplnění (Solid Fill) Aplikování barevného přechodu dvou nebo více barev (Gradient Fill, Multi- Color Gradient Fill) Šrafování oblasti (Hatch Fill) pravidelným nebo nepravidelným opakováním dvou nebo více barev Opakovaným nanášením zadaného vzoru (Pattern Fill) Texturové vyplnění, kdy je pro nastavení bravy na vnitřních bodů použit vybraný rastrový obrázek (Texture/Picture Fill) 5.3 Vyplňování vektorově zadané oblasti 5.3.1 Řádkové vyplňování Tento algoritmus je založen na postupném nalezení průsečíků jednotlivých řádků obrazového rastru se všemi hraničními úsečkami, které tvoří hranici dané oblasti. Nalezené průsečíky na každém řádku jsou využity k určení (a následnému zabarvení) té části řádku, která tvoří vnitřní body oblasti. KST/IPOGR 1-3 Petr Veselý
Obrázek 1: Základní princip řádkového vyplňování Samotný algoritmus se skládá z několika kroků, které jsou popsány a vysvětleny na následujícím ukázkovém příkladu, který je znázorněn na obrázku 1. Je dána vektorově definovaná uzavřená oblast, která je ohraničena nepravidelným osmiúhelníkem, který je určen posloupností vrcholů A-B-C-D-E-F-G-H. Krok 1 Odstranění vodorovných hran Vzhledem k tomu, že budou následně vyhledávány průsečíky jednotlivých řádků, které lze analyticky popsat jako vodorovné přímky, s jednotlivými hranami mnohoúhelníku (tvořící hranici), generovaly by vodorovné hrany nekonečně mnoho průsečíků a proto musí být z dalšího zpracování vypuštěny Krok 2 Orientace a zkrácení zbývajících hran Této krok slouží jako příprava před hledáním průsečíků. Je totiž nutné rozpojit hrany, které mají společný bod. Pokud by se toto rozpojení neprovedlo, došlo by při následném hledání průsečíků k nalezení násobných průsečíků, což by způsobilo problémy při jejich dalším zpracování. Všechny zbývající hrany je třeba orientovat stejným směrem (shora dolů nebo zdola nahoru) a poté se rozpojení provede zkrácením úseček, tvořící jednotlivé KST/IPOGR 1-4 Petr Veselý
hrany. Důležité je zkracovat všechny hrany na stejné straně, buď na jejich začátku, nebo na konci. Krok 3 Průchod přes všechny řádky V tomto kroku je určen rozsah řádků rastru, kde mohou být hledány průsečíky s jednotlivými hranami a následně je spuštěn cyklus, který provede pro každý řádek krok 3a a 3b. Kroky 3a + 3b Nalezení průsečíků a zobrazení vnitřních bodů na řádku V tomto kroku jsou nalezeny všechny průsečíky daného řádku rastru se všemi upravenými úsečkami, tvořící hranici (mimo úseček, vyloučených v kroku 1). Těchto průsečíků musí být sudý počet. Jednotlivé nalezené průsečíky jsou následně seřazeny dle x-ové souřadnice průsečíku. Vnitřní body oblasti, ležící na daném řádku rastru se nachází vždy mezi lichým a následujícím sudým průsečíkem v seznamu (indexovaném od 1) nalezených a seřazených průsečíků z daného řádku. Jinými slovy vnitřní body leží mezi 1. a 2. průsečíkem, dále mezi 3. a 4. průsečíkem atd. Jejich změnu barvy lze provést buď jednotlivě pixel po pixelu, nebo podstatně rychlejším způsobem pomocí metody pro kreslení úsečky. KST/IPOGR 1-5 Petr Veselý
Následující kód obsahuje implementaci hledání průsečíků na jednom konkrétním řádku rastru. Parematry metody urcenipruseciku jsou hranice oblasti (mnohoúhelník, předaný jako instance třídy Polygon) a y-ová souřadnice představující konkrétní řádek rastru. Návratovou hodnotu tvoří seznam x-ových souřadnic nalezených průsečíků. static ArrayList <Float> urcenipruseciku(polygon p, int y) { ArrayList <Float> seznam = new ArrayList <Float> (); int zx, zy, kx, ky; float smernice; int dy, dx; float x; Point zacatek = new Point(p.xpoints[p.npoints-1], p.ypoints[p.npoints-1]); Point konec; for (int index = 0; index < p.npoints; index++) { konec = new Point(p.xpoints[index], p.ypoints[index]); //logika je v SSZ (pixely, y roste dolu) if (zacatek.y<konec.y) { zx = zacatek.x; zy = zacatek.y; kx = konec.x; ky = konec.y; } else { zx = konec.x; zy = konec.y; kx = zacatek.x; ky = zacatek.y; } dy = ky - zy; dx = kx - zx; // ignorovani vodorovneho useku if (dy!=0) { // osetreni svisleho useku neni treba, // smernice = +/- Infinity smernice = (float) dy / dx; KST/IPOGR 1-6 Petr Veselý
// zkraceni useku na jeho konci // pro svisle useky neni treba pocitat // nove kx, zustava stejne ky = ky - 1; if (dy!=0) kx = Math.round(kx - 1/smernice); if ((zy<=y) && (ky>=y)) { if (dy!=0) x = zx + (y-zy)/smernice; else x = zx; } seznam.add(x); } } zacatek = konec; } return seznam; Příklad 2: Nalezení průsečíků v algoritmu řádkového vyplňování 5.4 Vyplňování rastrově zadané oblasti 5.4.1 Semínkové vyplňování Základem je semínko, které určí uživatel jako začátek vyplňování. Tento pixel se vybarví požadovanou barvou a postup se opakuje na všechny jeho sousedy, pokud patří do vnitřní oblasti. Implementace je možná pomocí rekurzivní varianty, nebo pomocí vlastního zásobníku. Tento způsob jednoduchého rastrového vyplňování nevykazuje příliš velkou efektivitu (opakované vyplňování již vyplněných pixelů) a při použití zásobníku je omezen jeho velikostí a nehodí se k reálnému vyplnění větších oblastí. 5.4.2 Určení hranice Při rastrovém vyplňování může být hranice určena dvojím způsobem: KST/IPOGR 1-7 Petr Veselý
Hraniční vyplňování - Určuje se barva hranice a bod patří do oblasti, pokud má jinou barvu než hranice. Záplavové vyplňování - Vnitřní body jsou ty, které mají stejnou barvu jako původní semínko. 5.4.3 Spojitost rastrově definované oblasti Obdobně jako u rasterizace liniových objektů je možno i při rasterizaci plošných objektů hovořit o spojitosti. Pro rasterizaci oblasti platí: 4-spojitá oblast je tvořena jednotlivými vnitřními body, přičemž mezi libovolnými dvěmi existuje cesta složená z vodorovných a svislých kroků po vnitřních bodech této oblasti. Je ohraničena 8 spojitou hranicí. KST/IPOGR 1-8 Petr Veselý
8-spojitá oblast je tvořena jednotlivými vnitřními body, přičemž mezi libovolnými dvěmi existuje cesta složená z vodorovných a svislých a diagonálních kroků po vnitřních bodech této oblasti. Je ohraničena 4 spojitou hranicí. 5.4.4 Semínkové vyplňování řádková varianta Efektivnější, menší velikost zásobníku, rychlejší (kreslení vodorovných čar, méně opakovaného testování). Semínko obsahuje atributy všech čtyř směrů pro další prohledávání. Pro každou položku vybranou ze zásobníku určujeme okraje oblasti na daném řádku. Postup: (1) Zadané semínko vložíme do zásobníku s atributy (true) všech čtyř směrů pro další prohledávání. (2) Dokud není zásobník prázdný, vybereme jednu položku a provedeme s ní následující operace: (2.a) Pokud obsahuje směr NAHORU a pixel [x, y-1] je vnitřní, vložíme tento pixel do zásobníku s atributy,,. (2.b) Pokud obsahuje směr DOLU a pixel [x, y+1] je vnitřní, vložíme tento pixel do zásobníku s atributy,,. KST/IPOGR 1-9 Petr Veselý
(2.c) Pokud obsahuje směr VLEVO, zmenšujeme x a hledáme v tomto směru poslední vnitřní bod. (2.d) Pokud obsahuje směr VPRAVO zvětšujeme x a hledáme v tomto směru poslední vnitřní bod. (2.e) Vykreslíme zadanou barvou pixely mezi nalezenými krajními body. (2.c.1, 2.d.1) Při hledání minima a maxima na daném řádku je třeba kontrolovat i změnu barvy u bodů [x, y-1] a [x, y+1] (podle směru ) a pokud nastala mezi [x, y-1] a [x-1, y-1], respektive mezi ([x, y+1] a [x-1, y+1]) změna z hraniční na překreslovanou barvu, je třeba tyto body zanést s příslušnými atributy směru ( ) (resp. ) do zásobníku. Pojmy k zapamatování Rasterizace, rasterizační algoritmus, grafická primitiva, rekurze, záplavové a hraniční vyplňování, semínko, čtyř a osmispojitá oblast, obrazová paměť KST/IPOGR 1-10 Petr Veselý
Otázky na procvičení 1. Jaké jsou základní 2D plošné grafické primitivy? 2. Jak je definována oblast? 3. Jaké jsou možnosti vyplnění oblasti? 4. Jaký je rozdíl mezi rastrově a vektorově definovanou oblastí? 5. Jak je ohraničená čtyřspojitá a ospispojitá oblast? 6. Co je to rekurzivní algoritmus? 7. Jaké jsou způsoby pro vyplnění vektorově definované oblasti? 8. Jak se řeší vyplnění pomocí šrafování? 9. Jaká je úprava algoritmu pro šrafování pod libovolným úhlem? 10. Jaký je algoritmus inverzního vyplňování? Odkazy a další studijní prameny Žára, J., Beneš, B., Felkel, P. Moderní počítačová grafika. Computer Press, Brno, 1998. ISBN 80-7226-049-9. Foley, Van D. Computer Graphics. Principles and Practice. Addison-Wesley,1991. KST/IPOGR 1-11 Petr Veselý