Bresenhamov stredový elipsový algoritmus (Midpoint Ellipse Algorithm)
Princíp algoritmu
Bresenhamov stredový elipsový algoritmus je založený na rovnakom princípe ako Bresenhamov kružnicový algoritmus. Dané sú parametre $r_x,r_y$ a stred elipsy $(x_C,y_C)$. Vyčísľujeme body elipsy v základnej pozícii a to so stredom v bode $(0,0)$. Následne sa všetky body elipsy posunú tak, že bod $(x_C,y_C)$ je jej stredom.
Ak chceme vykresliť elipsu v inej ako základnej polohe (ak hlavná a vedľajšia os elipsy nie sú rovnobežné so súradnicovými osami), môžeme elipsu otočiť okolo tredu otáčania (viď Kapitola - rotácia).
V nasledujúcej časti ukážeme rasterizáciu elipsy so stredom v bode $(0,0)$ a základnej pozícii. Algoritmus budeme aplikovať v I. kvadrante v dvoch oblastiach (Obr. 1). Tieto oblasti vzniknú rozdelením I. kvadrantu na dva v bode elipsy, v ktorom dotyčnica k elipse má smernicu $m=-1$.
Hranicou medzi oblasťami je priamka spájajúca stred elipsy s dotykovým bodom.
Začíname v bode elipsy $(0,r_y)$ a postupujeme v smere chodu hodinových ručičiek. Najprv postupujeme jednotkovým krokom v smere osi $x$ v prvej oblasti, až pokým sa nedostaneme na hranicu medzi oblasťami. Vtedy prejdeme na jednotkový krok v smere osi $y$, až kým neprejdeme celý I. kvadrant.Tento postup je potrebný, pretože ak by sme postupovali jednotkovým krokom iba v smere jednej zo súradnicových osí, pri rasterizácii by vznikali medzery medzi zobrazenými pixlami a výsledná elipsa by bola nespojitá.
Pre smernicu $m$ dotyčnice v bode elipsy $X=(x,y)$ na hranici oblasti platí $m=-1 \Rightarrow \frac{dy}{dx}=\frac{2r_y^2x}{2r_x^2y} \Rightarrow 2r_y^2x=2r_x^2y$. Teda pre smernicu $m$ dotyčnice v bode elipsy $X=(x,y)$ v prvej oblasti I. kvadrantu platí $m>-1$ a v druhom $m \lt -1$. Preto môžeme povedať, že bod elipsy $X=(x,y)$ sa nachádza v prvej oblasti, ak platí $2r_y^2x \geq 2r_x^2y$, t.j. vzorkujeme jednotkovým krokom v smere osi $x$, inak v smere osi $y$.
Samozrejme, modifikáciou algoritmu je možné postupovať zo začiatočného bodu $(r_x,0)$ proti smeru hodinových ručičiek.
Zapíšeme rovnicu elipsy vyjadrenú v rovnici elipsy stredom $(x_C,y_C)=(0,0)$ ako $f(x,y)=r_y^2x^2+r_x^2y^2-r_x^2r_y^2$.
Je zrejmé, že platí:
- Ak $f(x,y) \lt 0$ tak bod $X=(x,y)$ je vnútorný bod elipsy
- Ak $f(x,y)=0$ tak bod $X=(x,y)$ je bodom elipsy
- Ak $f(x,y) \gt 0$ tak bod $X=(x,y)$ je vonkajším bodom elipsy
Napr. nachádzame sa v bode elipsy $(x_i,y_i)$ a rozhodujeme sa, ktorý z nasledujúcich bodov pozdĺž elipsy vykreslíme. Môžu nastať dve situácie:
-
Nachádzame sa v prvej oblasti. Teda platí $2r_y^{2}x_i\leq 2r_x^{2}y_i$ a kandidáti na nasledujúci bod sú body na mieste $E=(x_i+1,y_i)$ a $SE=(x_i+1,y_i-1)$ (Obr. 2).
Ako v oboch Midpoint algoritmoch, použijeme bod $M=stred(E,SE)=(x_i+1,y_i-\frac{1}{2})$. Vyjadríme si rozhodovací parameter pre prvú oblasť ako $p1_i=f(M)=f(x_i+1,y_i-\frac{1}{2})=r_y^2(x_i+1)^2+r_x^2(y_i-\frac{1}{2})^2-r_x^2r_y^2$ a odvodíme rekurentný vzorec. $p1_{i+1}=f(x_{i+1}+1,y_{i+1}-\frac{1}{2})=r_y^2\left(x_{i+1}+1 \right)^2+r_x^2\left(y_{i+1}-\frac{1}{2} \right)^2 -r_x^2r_y^2 $.
Z toho $p1_{i+1}=p1_i+2r_y^2(x_i+1)+r_y^2$ +$r_x^2\left((y_{i+1}-\frac{1}{2})^2-(y_i-\frac{1}{2})^2\right)$.
Zrejme platí:
- Ak $p1_i \lt 0$, tak vyberáme za nasledujúci pixel $(x_{i+1},y_{i+1})=E=(x_i+1,y_i)$ a $p1_{i+1}=p1_i+2r_y^2x_{i+1}+r_y^2$.
- Ak $p1_i\geq 0$, tak vyberáme za nasledujúci pixel $(x_{i+1},y_{i+1})=SE=(x_i+1,y_i-1)$ a $p1_{i+1}=p1_i+2r_y^2x_{i+1}+r_y^2-2r_x^2y_{i+1}$.
Potrebujeme ešte vyčísliť hodnotu začiatočného parametra v začiatočnej pozícii pre $(x_0,y_0)=(0,r_y)$, teda $p1_0=f(1,r_y-\frac{1}{2})=r_y^2-r_x^2r_y+\frac{1}{4} r_x^2$
-
Ak $2r_y^{2}x_i\geq 2r_x^{2}y_i$ nachádzame sa v druhej oblasti. Teda kandidáti na nasledujúci bod sú body na mieste $S=(x_i,y_i-1)$ a $SE=(x_i+1,y_i-1)$ (Obr. 3).
V tomto prípade použijeme bod $M=stred(S,SE)=(x_i+\frac{1}{2},y_i-1)$.
Vyjadríme si rozhodovací parameter pre druhú oblasť ako $p2_i=f(M)=f(x_i+\frac{1}{2},y_i-1)=r_y^2(x_i+\frac{1}{2})+r_x^2(y_i-1)-r_x^2r_y^2$ a odvodíme rekurentný vzorec. $p2_{i+1}=f(x_{i+1}+\frac{1}{2},y_{i+1}-1)=r_y^2(x_{i+1}+\frac{1}{2})^2+r_x^2\left(y_i-1 \right)^2 -r_x^2r_y^2 $. Z toho $p2_{i+1}=p2_i+2r_x^2(y_i-1)+r_x^2+r_y^2\left((x_{i+1}+\frac{1}{2})^2-(x_i+\frac{1}{2})^2\right)$.
Zrejme platí:
- Ak $p2_i \lt 0$, tak vyberáme za nasledujúci pixel $(x_{i+1},y_{i+1})=S=(x_i,y_i-1)$ a $p2_{i+1}=p2_i-2r_x^{2}y_{i+1}+r_x^{2}$.
- Ak $p2_i\geq 0$, tak vyberáme za nasledujúci pixel $(x_{i+1},y_{i+1})=SE=(x_i+1,y_i-1)$ a $p2_{i+1}=p2_i+2r_y^{2}x_{i+1}-2r_x^{2}y_{i+1}+r_x^{2}$.
Potrebujeme ešte vyčísliť hodnotu začiatočného parametra v poslednom bode z prvej oblasti, ktorý je vlastne bod $(x_0,y_0)$ v oblasti dva. Teda $p2_0=f(x_0+\frac{1}{2},y_0-1)=r_y^2(x_0+\frac{1}{2})^2+r_x^2(y_0-1)^2-r_x^2r_y^2$.
Pre zjednodušenie môžeme pixely v druhej oblasti I kvadrantu vyčísľovať taktiež z bodu $(r_x,0)$ proti smeru chodu hodinových ručičiek, až kým sa nedostaneme do posledného vyčísleného bodu prvej oblasti.
Tak ako v kružnicovom algoritme, pri výpočte $p1_{i+1}$ a $p2_{i+1}$ používame iba operácie sčitovania a odčitovania. Hodnoty $2r_y^{2}x_{i+1}$ a $2r_x^{2}y_{i+1}$ je možno počítať s pomocou konštantného prírastku. Pre hodnotu začiatočného parametra v bode $(x_0,y_0)=(0, r_y)$ platí $2r_y^{2}x_0=0$ a $2r_x^{2}y_0=2r_x^{2}r_y$. Ako sa budú hodnoty $x_i$ a $y_i$ zvyšovať, budú sa tieto parametre meniť v I. kvadrante takto:
- Ak $x_{i+1} \gt x{i}$ platí $2r_y^{2}x_{i+1}=2r_y^{2}x_i+2r_y^{2}$, inak $2r_y^{2}x_{i+1}=2r_y^{2}x_i$.
- Ak $y_{i+1} \lt y{i}$ platí $2r_x^{2}y_{i+1}=2r_x^{2}x_i-2r_x^{2}$, inak $2r_x^{2}y_{i+1}=2r_x^{2}y_i$.
Potom pre začiatočnú hodnotu $p_0$ v bode $(x_C,y_C)=(0,r)$ dostaneme $p_0=3-2r$. Hoci pri výpočte parametra sa používa násobenie, príslušný násobok je druhou mocninou a je možná implementácia cez logické operácie. Ostatné operácie sú celočíselné sčítanie a odčítanie.
Postup výpočtu
Kroky algoritmu vieme zhrnúť nasledovne:
- Zadaj $r_x$ a $r_y$ a stred elipsy $(x_C,y_C)$ a urči prvý bod na elipse so stredom v začiatku $(x_0 ,y_0)=(0, r_y)$.
- Vypočítaj začiatočnú hodnotu rozhodovacieho parametra v prvej oblasti ako $p1_0=r_y^{2}-r_x^{2}r_y+\frac{1}{4}r_x^{2}$, urči $2r_y^2x_0 a 2r_x^2y_0$.
-
V každej pozícii $x_i$ v prvej oblasti, štartujúc v $i=0$, vykonaj nasledujúci test:
- Ak $p1_i \lt 0$: nasledujúci bod pozdĺž elipsy so stredom $(0,0)$ bude $(x_{i+1}, y_{i+1})=(x_i+1,y_i)$ a $p1_{i+1}=p1_i+2r_y^{2}x_{i+1}+r_y^{2}$.
- Inak je nasledujúci bod $(x_{i+1}, y_{i+1})=(x_i+1,y_i-1)$ a $p1_{i+1}=p1_i+2r_y^{2}x_{i+1}-2r_x^{2}y_{i+1}+r_y^{2}$.
- Opakuj krok 3. až pokým $2r_y^{2}x\leq 2r_x^{2}y$.
- Urči začiatočnú hodnotu rozhodovacieho parametra v druhej oblasti využitím posledného bodu z prvej oblasti ako $p2_0=r_y^{2}(x_0+\frac{1}{2})^{2}+r_x^{2}(y_0-1)^{2}-r_x^{2}r_y^{2}$
- V každej pozícii $x_i$ v druhej oblasti, štartujúc v $i=0$, vykonaj nasledujúci test:
- Ak $p2_i \gt 0$: nasledujúci bod pozdĺž elipsy so stredom $(0,0)$ bude $(x_{i+1}, y_{i+1})=(x_i,y_i-1)$ a $p2_{i+1}=p2_i-2r_x^{2}y_{i+1}+r_x^{2}$.
- Inak je nasledujúci bod $(x_{i+1}, y_{i+1})=(x_i+1,y_i-1)$ a $p2_{i+1}=p2_i+2r_y^{2}x_{i+1}-2r_x^{2}y_{i+1}+r_x^{2}$.
- Urči súmerné body v ostatných troch kvadrantoch.
- Posuň každú vypočítanú pixlovú pozíciu $(x, y)$ na elipsu so stredom $(x_C ,y_C )$ a zobraz bod so súradnicami: $x=x+x_C$,$y=y+y_C$.
Príklad
Na ilustráciu algoritmu si ukážeme rasterizáciu elipsy s parametrami $r_x=8$ a $r_y=6$ a stredom elipsy v bode $(0,7)$
- $(x_0,y_0)=(0,r_y)=(0,6)$.
- $p1_0=-332$, $2r_y^2x_0=0$ a $2r_x^2y_0=2.8^2.6=768$. Výraz $2r_y^2x$ sa bude zvyšovať o hodnotu $2r_y^2=72$ a výraz $2r_x^2y$ o hodnotu $-2r_x^2=-128$.
-
- $i=0:$ $p1_0 \lt 0 \Rightarrow (x_1,y_1)=(x_0+1,y_0)=(1,6)$, $p1_1=p1_0+2r_y^2x_1+r_y^2=-224$, $2r_y^2x_1=72$ a $2r_x^2y_1=768$.
- $i=1:$ $p1_1 \lt 0 \Rightarrow (x_2,y_2)=(x_1+1,y_1)=(2,6)$, $p1_2=p1_1+2r_y^2x_2+r_y^2=-44$, $2r_y^2x_2=72+72=144$ a $2r_x^2y_2=768$.
- $i=2:$ $p1_2 \lt 0 \Rightarrow (x_3,y_3)=(x_2+1,y_2)=(3,6)$, $p1_3=p1_2+2r_y^2x_3+r_y^2=208$, $2r_y^2x_3=144+72=216$ a $2r_x^2y_3=768$.
- $i=3:$ $p1_3 \gt 0 \Rightarrow (x_4,y_4)=(x_3+1,y_3-1)=(4,5)$, $p1_4=p1_3+2r_y^2x_4-2r_x^2y_4+r_y^2=-108$, $2r_y^2x_4=216+72=288$ a $2r_x^2y_4=768-128=640$.
- $i=4:$ $p1_4 \lt 0 \Rightarrow (x_5,y_5)=(x_4+1,y_4)=(5,5)$, $p1_5=p1_4+2r_y^2x_5+r_y^2=288$, $2r_y^2x_5=288+72=360$ a $2r_x^2y_5=640$.
- $i=5:$ $p1_5 \gt 0 \Rightarrow (x_6,y_6)=(x_5+1,y_5-1)=(6,4)$, $p1_6=p1_5+2r_y^2x_6-2r_x^2y_6+r_y^2=244$, $2r_y^2x_6=360+72=432$ a $2r_x^2y_6=640-128=512$.
- $i=6:$ $p1_6 \gt 0 \Rightarrow (x_7,y_7)=(x_6+1,y_6-1)=(7,3)$, $2r_y^2x_7=432+72=504$ a $2r_x^2y_7=512-128=384$. Platí $2r_y^2x_7 \geq 2r_x^2y_7$, preto prechádzame do druhej oblasti.
- $(x_0,y_0)=(7,3)$ a teda $p2_0=r_y^{2}(x_0+\frac{1}{2})^{2}+r_x^{2}(y_0-1)^{2}-r_x^{2}r_y^{2}=-23$.
-
- $i=0:$ $p2_0 \lt 0 \Rightarrow (x_1,y_1)=(x_0+1,y_0-1)=(8,2)$, $p2_1=p2_0+2r_y^2x_1-2r_x^2y_1+r_x^2=233$, $2r_y^2x_1=504+72=576$ a $2r_x^2y_1=384-128=256$.
- $i=1:$ $p2_1 \gt 0 \Rightarrow (x_2,y_2)=(x_1,y_1-1)=(8,1)$, $p2_2=p2_1+2r_x^2y_2+r_x^2=169$, $2r_y^2x_2=576$ a $2r_x^2y_2=256-128=128$.
- $i=2:$ $p2_2 \gt 0 \Rightarrow (x_3,y_3)=(x_2,y_2-1)=(8,0)$, algoritmus končí, pretože sme sa dostali na hranicu I. oktantu. Výsledné body možno vidieť na Obr. 4.
- Určíme súmerné body v ostatných oktantoch. Výslednú elipsu možno vidieť na Obr. 4.
- Posunieme každú hodnotu pixla do stredu $(0,7)$. Teda dostaneme body: $(0,13),(1,13),(2,13),(3,13),(4,12), ...$
Interaktívny applet
Bressenhamov stredový elipsový algoritmus
Zobrazený raster sa nachádza v prvom kvadrante, teda v ľavom dolnom rohu sa nachádza bod (0,0).
Urči dĺžku hlavnej a vedľajšej poloosi a kliknutím do rastra vlož stred elipsy.
ellipseMidpoint(int xC, int yC, int rx, int ry){
int x = 0, y = ry;
int px = 0;
int py = 2*rx*rx*y;
void vykresliBodElipsy(int, int, int, int);
vykresliBodElipsy(xC, yC, x, y);
p=ROUND(ry*ry - (rx*rx*ry) + (0.25*rx*rx));
while(px < py) {
x++;
px=px+2*ry*ry;
if(p < 0) p = p + ry*ry+px;
else {
y--;
py = p - 2*rx*rx;
p = p + ry*ry+px-py;
}
vykresliBodElipsy(xC, yC, x, y); }
}
p=ROUND(ry*ry*(x+0.5)*(x+0.5)+rx*rx*(y-1)*(y-1)-rx*rx*ry*ry);
while(y > 0) {
y--;
py=px-2*rx*rx;
if(p > 0) p = p + rx*rx-py;
else {
x++;
px = p + 2*ry*ry;
p = p + rx*rx-py+px;
}
vykresliBodElipsy(xC, yC, x, y); }
}
void vykresliBodElipsy(int xC, int yC, int x, int y){
vykresliPixel(xC + x, yC + y);
vykresliPixel(xC - x, yC + y);
vykresliPixel(xC + x, yC - y);
vykresliPixel(xC - x, yC - y);
}
Pseudokód
Implementácia Bresenhamovho stredového elipsového algoritmu je zhrnutá v nasledujúcom pseudokóde. Na vstupe sú parametre $r_x$, $r_y$ a stred elipsy$(x_C,y_C)$.
ellipseMidpoint(int $x_C$, int $y_C$, int $r_x$, int $r_y$) $\lbrace$
int p, x = 0, y = $r_y$, $p_x$ = 0, $p_y$ = 2 * $r_x$ * $r_x$ * y
void vykresliBodElipsy(int, int, int, int);
// Vykreslí prvý bod elipsy
vykresliBodElipsy($x_C$, $y_C$, x, y);
// Región 1
p=ROUND($r_y$ * $r_y$ -($r_x$ * $r_x$ * $r_y$) + (0.25 * $r_x$ * $r_x$));
while($p_x \lt p_y$) $\lbrace$
x++;
$p_x$ += 2 * $r_y$ * $r_y$;
if(p \lt 0) p += $r_y$ * $r_y$ + $p_x$;
else $\lbrace$
y$--$;
$p_y$ -= 2 * $r_x$ * $r_x$;
p += $r_y$ * $r_y$ + $p_x$ - $p_y$; $\rbrace$
vykresliBodElipsy($x_C$, $y_C$, x, y);
$\rbrace$
// Región
p=ROUND($r_y$ * $r_y$ * (x+0.5) * (x+0.5) + $r_x$ * $r_x$ * (y - 1) * (y - 1) - $r_x$ * $r_x$ * $r_y$ * $r_y$);
while(y \gt 0) $\lbrace$
y--;
$p_x$ += 2 * $r_y$ * $r_y$;
if(p \gt 0) p += $r_x$ * $r_x$ + $p_y$;
else $\lbrace$
x++;
$p_x$ -= 2 * $r_y$ * $r_y$
p += $r_x$ * $r_x$ + $p_x$; $\rbrace$
vykresliBodElipsy($x_C$, $y_C$, x, y);
$\rbrace$
$\rbrace$
void vykresliBodElipsy(int $x_C$, int $y_C$, int x, int y); $\lbrace$
vykresliPixel($x_C$ + x, $y_C$ + y);
vykresliPixel($x_C$ - x, $y_C$ + y);
vykresliPixel($x_C$ + x, $y_C$ - y);
vykresliPixel($x_C$ - x, $y_C$ - y);
$\rbrace$
Literatúra
Bresenhamov stredový elipsový algoritmus je podrobne spracovaný v [3] od strany 102. Je uvedený aj v [2] od strany 88, avšak je uvedený iba výsledný vzorec na výpočet bodov pozdĺž elipsy, bez odvodenia.