____                          __                    __
      / __ \                        / /                   /_/\
     / /L/ /,_____  _______  _,__  / /_  ____,__,_,__  __ \_\/_  __
    / __  / __/ _ \/ __/ _ \/ __ \/ __ \/ __  / _  _ \/ / / / / / /\
   / /L/ / /\/ ___L\ \/ ___/ /\/ / / / / /L/ / // // / /_/ /\ \/ / /
  /_____/_/ /\___/___/\___/_/ /_/_/ /_/\__._/_//_//_/\____/ /\__/ /
  \_____\_\/  \__\___\/\__\_\/\_\_\/\_\/\_\_\_\\_\\_\/\___\/  \_\/
                                   / /\              /_/ /\
                            ____,_/ /_/_,_____  _,___\/ __/,_,__  __  ______
                           / __  / / __  / __ \/ __/ / /\/ _  _ \/ / / / __/\
                          / /L/ / / /L/ / /L/ / /\/ / /_/ // // / /_/ L\ \_\/
                          \__._/_/\__  /\____/_/ /_/\__/_// // /\____/___/\
                           \_\_\_/____/ /\___\_\/\_\/\_\_\\_\\_\/\___\___\/
                                 \____\/

     Když   jsem   pročítal   minulá   čísla   Výhně,   konkrétně  rubriku
     programování, objevil jsem jeden malý nedostatek všech těch návodů na
     vektorovou grafiku.  Všichni totiž asi předpokládají, že znáte nějaký
     použitelný   algoritmus  na  kreslení  čar   a  lineární  interpolaci
     čehokoliv vůbec.  Pro ty z vás,  kteří umí  kreslit čáry  zatím jenom
     knihovními funkcemi,  ReDoxím tetragonem a případně nějakou šíleností
     se tedy chci pokusit popsat jeden naprosto geniální a přitom kupodivu
     pochopitelný algoritmus, který se podle autora nazývá bresenhamův.

     ČISTĚ MATEMATICKÝ POSTUP

     Každou přímku  v rovině můžeme  matematicky popsat jako množinu bodů,
     které vyhovují rovnici

               a x + b y + c = 0  (a, b, c ε R).                       (1)

     Z této rovnice můžeme vyjádřit  jednu proměnnou v závislosti na druhé

               y = k x + q  (k, q ε R)                                 (2)

     a   postupným  dosazováním   získat  souřadnice   jednotlivých  bodů.
     Doporučuje  se při tom  vyjadřovat tu  proměnnou,  která  roste/klesá
     pomaleji  (má větší  absolutní hodnotu  koeficientu),  abychom  mohli
     dosazovat pouze celočíselné hodnoty řídící proměnné a hodnoty závislé
     proměnné se nikdy nelišily o více než 1 (jinak vzniknou v čáře díry!)
     Tenhle postup má jednu kolosální vadu.  Násobení reálnou konstantou k
     v (2) je hrozně pómálé.

     PRINCIP BRESENHAMOVA ALGORITMU

               o-------                                 +
               P1      --------                         |
                               --------                 | deltaY
                                       --------      P2 |
                                               -------o +
               +--------------------------------------+
                                deltaX

     Pro každou  úsečku však platí,  že když se  X změní deltaX-krát, Y se
     změní deltaY-krát.  Takže vytvoříme  proměnou d (d jako rozhodovací),
     která se při změně x  o jedna změní o deltaY  (jenom x*deltaY).  No a
     až se tahle proměnná změní o deltaX, je čas změnit také Y. Já vím, že
     to vysvětlení  je zmatené,  ale když si to zkusíte,  tak to tak vážně
     funguje.

     ZÁKLADNÍ POSTUP

     Takže tady je popis postupu:
     1) Inicializace proměnných: D = 0, X = X1, Y = Y1, deltaX = |X2-X1|,
          deltaY = |Y2 - Y1| (zatím předpokládáme, že deltaY < deltaX a
          deltaX i deltaY jsou kladné)
     2) Nakreslíme bod na souřadnicích [X,Y]
     3) Pokud X == X2 skončíme (na Y == Y2 se můžeme spolehnout)
     4) Zvýšíme X (X++)
     5) K D přidáme deltaY
     6) Když D >= deltaX, D -= deltaY, Y++
     7) Skok na bod 2

     Ještě poznámka - takhle  nakreslená čára bude nesymetrická.  Proto je
     výhodnější nainicializovat proměnnou D na hodnotu deltaX/2. A protože
     testovat  D >= deltaX  je  složitější,  než  testovat  přetečení  při
     přičítání,  je zvlášť při kódování  v asembleru lepší inicializovat D
     na -deltaX/2 a testovat přetečení při přičítání proměnné D.

     ODSTRANĚNÍ OMEZENÍ PRO deltaX A deltaY

     1) První možností je napsat osm variant pro případy:
        X roste, Y roste, |deltaX| > |deltaY|                          (a)
        X klesá, Y klesá, |deltaX| > |deltaY|                          (b)
        X roste, Y klesá, |deltaX| > |deltaY|                          (c)
        X klesá, Y roste, |deltaX| > |deltaY|                          (d)
        Y roste, X roste, |deltaX| < |deltaY|                          (e)
        Y klesá, X klesá, |deltaX| < |deltaY|                          (f)
        Y roste, X klesá, |deltaX| < |deltaY|                          (g)
        Y klesá, X roste, |deltaX| < |deltaY|                          (h)
     2) Zkrátit  na polovinu  můžeme tak,  že případy (b,d,f,h)  převedeme
        na případy  (a,c,e,h) tím,  že setřídíme  počáteční  a koncový bod
        podle  X resp.  Y souřadnice  (případy (e,f,g,h)  používají řídící
        proměnou Y).  Jinak řečeno  počáteční  bod je ten,  který má menší
        hodnotu řídící proměnné. Takže se rutina zkrátí na čtyři případy:
        Y roste, |deltaX| > |deltaY|                                   (i)
        Y klesá, |deltaX| > |deltaY|                                   (j)
        X roste, |deltaX| < |deltaY|                                   (k)
        X klesá, |deltaX| < |deltaY|                                   (l)
     3) Případy (j,l) lze převést  na případy (i,k) tak, že si pamatujeme,
        jestli  Y respektive  X má růst  nebo klesat  a podle  toho  místo
        pevného INC  nebo DEC  přičítáme proměnnou  obsahující ±1. Bohužel
        pak už bývá docela nouze o registry. Je však možné využít fintu se
        sebe  přepisujícím  kódem.   Někam  sranou  si   necháme  přeložit
        instrukce INC a DEC s příslušnými registry a potom, když dostaneme
        souřadnice bodů,  vybereme si příslušnou instrukci a MOVneme ji na
        příslušné místo kódu. (Tuto možnost doporučuji hlavně pro šílence)
     4) Můžeme  stáhnout řešení  na jediný  případ tak,  že když dojde při
        přičítání proměnné  D k přetečení,  zvyšujeme Y  než opět  dojde k
        přetečení (tentokrát opačným směrem).  Problémem této  varianty je
        správně  zvolit  pořadí  jednotlivých  operací  cyklu  a koncových
        podmínek  tak,  aby  rutina  fungovala  i v případě  svislé  čáry.
        Bohužel  jsem  to  nezkoušel,  takže  vám  neřeknu  jak na to.  Na
        obyčejné čáry jsou ty ostatní metody lepší.  Výhoda této metody se
        však projeví při texture mappingu nebo shadingu,  kdy bodů textury
        nebo  barev  potřebujeme  jenom  tolik,  kolik  bodů  kreslíme  na
        obrazovku  a jejich vzdálenosti  mohou být větší  než jedna (každý
        druhý pixel textury, barva skákající po 2)
     5) Poslední možnost je převést řešení přímky na parametrický problém.
        Abych pravdu řekl, je to pěkná vopičárna, ale může se vám to hodit
        při   texturování   nebo   nějakých   jiných   relativně  šílených
        interpolacích  čehosi.  Princip  je  asi  takovýhle.  Spočítáme si
        absolutní  hodnoty  deltaX  a  deltaY  a také  přičítací  proměnné
        (znaménka deltaX a deltaY).  Potom si zavedeme deltaT,  které musí
        být největší z trojice deltaX, deltaY, deltaT  (doporučuje se vzít
        větší  z deltaX, deltaY),  jako rozsah  řídící proměnné.  Potom se
        chováme,  jako kdybychom kreslili dvě čáry,  jednu se souřadnicemi
        T a X a jednu se souřadnicemi T a Y.  Musíme je samozřejmě počítat
        v jedné smyčce.  Potřebujeme však více proměnných,  které se těžko
        cpou do registrů.

     Všech 5 postupů  je prakticky  aplikovatelných.  1., 4. a 5.  možnost
     jsou však spíše opičárny než cokoliv jiného a jejich použití se proto
     nedoporučuje  (i když  v nějakém  speciálním případě  mohou mít svoje
     výhody  -  zatím nevím  jaké,  ale třeba někdo  na nějaké narazíte. V
     takovém případě bych  o nich rád slyšel.  Možná se to  bude hodit pro
     antialiasing  (takovej pseudo  -  prostě průměrování barev z textury)
     Stejně tak se  nedoporučuje použití  finty z bodu 3.  Jedná se sice o
     velice elegantní řešení (prasárna na kvadrát:-)), ale mohou s tím být
     problémy v P-módu. Bohužel nevím, jak to dopadne, pokud to napíšete v
     real-módu a pustíte  pod nějakým DPMI ovladačem (např. Wokna:-().  Na
     druhou stranu CRT0  (zaváděcí kód) některých  P-módových  kompilátorů
     (DJGPP - D.J.'s GNU Programming Package (GNU C pro DOS)) se postarají
     o to,  aby se  DataSegment  a CodeSegment kryly,  takže stejné labely
     jsou platné v obou, a potom to  zapíšte přes  DS a spustíte přes CS a
     ono  to  chodí.  (V DJGPP to někdo zkoušel  (myslim self-rewriting) -
     fakt to fachá)

     Pro lineární  texture  mapping,  gouraud nebo  phong shading  a další
     techniky  je také  velmi  výhodné  používat  Bresenhamův  algoritmus.
     Vzhledem k většímu počtu počítaných souřadnic  (od 3 - gouroud - přes
     4 - mapping -  až po 5 - phong -  a třeba  i víc (kombinované metody,
     bůhvíco:-)))  je nutné  algoritmus  mírně upravit.  První možností je
     využití parametrického přístupu. Ten má tu výhodu, že máme jnom jednu
     obecnou  rutinu  i pro  velké  počty  souřadnic  a nevýhodu,  že když
     mapujeme velikou texturu na malinkatej trojúhelníček někde kdesi :-),
     kreslíme zbytečně  moc bodů.  Druhá metoda,  která v  takovém případě
     připadá v úvahu, je,  že na X a Y použijeme klasickou metodu 2 nebo 3
     s variantami  pro  2  nebo  4  samostatné  případy  a  pro  přičítání
     souřadnic  v textuře  použijeme  metodu  4 s vnitřní  smyčkou,  která
     umožňuje  v jednom kroku  změnit souřadnici  o více než ±1.  V každém
     případě  nedoporučuji pro  velká množství  souřadnic psát  rutiny pro
     jednotlivé případy zvlášť (počet případů roste geometrickou řadou).

     PŘÍKLADY IMPLEMENTACE

                            viz Druhá stránka

                                                                        - Bulb -



            výheň