Algorytm Bresenhama

Z testwiki
Wersja z dnia 02:01, 5 maj 2024 autorstwa imported>Arkosz (growthexperiments-addlink-summary-summary:2|0|0)
(różn.) ← poprzednia wersja | przejdź do aktualnej wersji (różn.) | następna wersja → (różn.)
Przejdź do nawigacji Przejdź do wyszukiwania

Algorytm Bresenhama służy do rasteryzacji krzywych płaskich, czyli do jak najlepszego ich obrazowania na siatce pikseli. Jack Bresenham w 1965 roku opracował metodę rasteryzacji odcinków, którą następnie przystosowano do rysowania obiektów innego rodzaju (okręgów czy elips).

Siła algorytmu tkwi w prostocie; koszt postawienia jednego piksela to porównanie i jedno dodawanie (dla odcinków) lub porównanie i dwa dodawania (dla okręgów i elips), ponadto algorytm wykonuje obliczenia na liczbach całkowitych, które są bardzo szybkie na wszystkich mikroprocesorach.

Metoda pozwala w bardzo prosty sposób wybierać, które piksele leżą najbliżej rasteryzowanego obiektu (np. odcinka). Zakładając, że poprzednio algorytm zapisał piksel o współrzędnych (x,y), w kolejnym kroku algorytm może zapisać piksel albo (x+1,y) (ruch poziomy), albo (x+1,y+1) (ruch ukośny) – wybór determinuje znak tzw. zmiennej decyzyjnej, której wartość jest po każdym kroku aktualizowana. Aktualizacja polega na dodaniu pewnych wartości, będących w przypadku odcinka stałymi, zaś dla okręgu i elipsy wartościami zmieniającymi się z każdym krokiem:

  • D = zmienna decyzyjna
  • DL = przyrost D przy ruchu w lewo
  • DU = przyrost D przy ruchu ukośnym
  • DDLL = przyrost DL przy ruchu w lewo (dla odcinka = 0)
  • DDUL = przyrost DU przy ruchu w lewo (dla odcinka = 0)
  • DDLU = przyrost DL przy ruchu ukośnym (dla odcinka = 0)
  • DDUU = przyrost DU przy ruchu ukośnym (dla odcinka = 0)
  • powtarzaj
    • zapisz piksel (x,y)
    • jeśli D>0
      • x:=x+1 – ruch w lewo
      • D:=D+DL – aktualizacja zmiennej decyzyjnej
      • DL:=DL+DDLL – aktualizacja parametrów pomocniczych
      • DU:=DU+DDUL – aktualizacja parametrów pomocniczych
    • w przeciwnym razie
      • x:=x+1 – ruch ukośny
      • y:=y+1
      • D:=D+DU – aktualizacja zmiennej decyzyjnej
      • DL:=DL+DDLU – aktualizacja parametrów pomocniczych
      • DU:=DU+DDUU – aktualizacja parametrów pomocniczych

Algorytm konwersji odcinka

Założenia

Algorytm i jego działanie

Załóżmy, że krzywa w przedziale [xi,xk] spełnia ww. założenia

Pierwszy piksel stawiamy w punkcie P(xi,yi). Drugi natomiast ogranicza się jedynie do dwóch możliwości: Si+1=(xi+1,yi) lub Ti+1=(xi+1,yi+1). Przyrost w kierunku osi OX (osi wiodącej) jest stały – jeden piksel. Korzystając z równania kierunkowego prostej

y=dydx(xxo)+yo

policzymy w jakiej odległości znajdują się powyższe piksele od punktu przecięcia łączącego je odcinka z prostą przebiegającą w rzeczywistym układzie współrzędnych

s=dydx(xi+1xo)(yiyo)
t=(yi+1yo)dydx(xi+1xo)
di=dx(st)=2dy(xixo)2dx(yiyo)+2dydx

Ponieważ dx > 0, di określa, która z odległości s i t jest większa. Jeżeli di>0, to s > t za punkt Pi+1 przyjmujemy piksel Ti+1, jeżeli di<0, wybieramy piksel Si+1. Wartość di=0 oznacza, że oba piksele leżą w tej samej odległości i arbitralnie przyjmujemy, że następny piksel to Ti+1. Policzmy jeszcze wartość di+1

di+1=2dy(xi+1x0)2dx(yi+1y0)+2dydx

oraz różnicę di+1di

di+1di=2dy(xi+1xi)2dx(yi+1yi)

czyli

di+1=di+2dy2dx(yi+1yi)

gdyż xi+1=xi+1.

Jeżeli di=0, to yi+1=yi+1 (wybieramy piksel Ti+1), co pozwala na uproszczenie obliczeń di+1

di+1=di+2(dydx)

Analogicznie, gdy di<0 mamy yi+1=yi (wybieramy piksel Si+1), i wzór na di+1 ma postać

di+1=di+2dy

Z uwagi na rekurencyjną postać wzoru na obliczanie współczynnika di, nazywanego także zmienna decyzyjna, należy jeszcze policzyć wartość początkową d0. Podstawiając xi=x0 oraz yi=y0 do równania

di=2dy(xix0)2dx(yiy0)+2dydx

otrzymujemy wzór na d0

d0=2dydx

Implementacja

Implementacja algorytmu Bresenhama musi oczywiście uwzględniać inne możliwe położenia odcinka względem osi OX. Jednak w każdej sytuacji można zastosować opisany wyżej schemat, w razie potrzeby traktując oś OY jako oś wiodącą

Rysowanie odcinka algorytmem Bresenhama

Procedura w języku C:

 // x1 , y1 - współrzędne początku odcinka
 // x2 , y2 - współrzędne końca odcinka
 void BresenhamLine(const int x1, const int y1, const int x2, const int y2)
 {
     // zmienne pomocnicze
     int d, dx, dy, ai, bi, xi, yi;
     int x = x1, y = y1;
     // ustalenie kierunku rysowania
     if (x1 < x2)
     {
         xi = 1;
         dx = x2 - x1;
     }
     else
     {
         xi = -1;
         dx = x1 - x2;
     }
     // ustalenie kierunku rysowania
     if (y1 < y2)
     {
         yi = 1;
         dy = y2 - y1;
     }
     else
     {
         yi = -1;
         dy = y1 - y2;
     }
     // pierwszy piksel
     glVertex2i(x, y);
     // oś wiodąca OX
     if (dx > dy)
     {
         ai = (dy - dx) * 2;
         bi = dy * 2;
         d = bi - dx;
         // pętla po kolejnych x
         while (x != x2)
         {
             // test współczynnika
             if (d >= 0)
             {
                 x += xi;
                 y += yi;
                 d += ai;
             }
             else
             {
                 d += bi;
                 x += xi;
             }
             glVertex2i(x, y);
         }
     }
     // oś wiodąca OY
     else
     {
         ai = ( dx - dy ) * 2;
         bi = dx * 2;
         d = bi - dy;
         // pętla po kolejnych y
         while (y != y2)
         {
             // test współczynnika
             if (d >= 0)
             {
                 x += xi;
                 y += yi;
                 d += ai;
             }
             else
             {
                 d += bi;
                 y += yi;
             }
             glVertex2i(x, y);
         }
     }
 }

Algorytm Bresenhama dla elipsy

Założenia

  • Elipsa ma osie zgodne z osiami układu współrzędnych,
  • Półosie elipsy mają długości a (wzdłuż osi OX) i b (wzdłuż OY),
  • Rozważamy elipsę w I ćwiartce układu współrzędnych,
  • Środkiem symetrii elipsy jest środek układu współrzędnych,
  • Rysowanie elipsy zaczynamy od punktu (0, b),
  • W każdym kroku stawiamy symetrycznie 4 punkty elipsy,
  • Początkową osią wiodacą jest oś OX,
  • W punkcie zmiany osi wiodącej, współczynnik nachylenia stycznej do elipsy wynosi -1 (tg 135°)

Algorytm i jego działanie

Przybliżana elipsa ma równanie:

x2a2+y2b21=0

O wyborze piksela decydować będzie wartość funkcji

F(x,y)=a2b2(x2a2+y2b21)=b2x2+a2y2a2b2

w punkcie środkowym M położonym pomiędzy alternatywnymi pikselami. Gdy osią wiodąca jest OX oblicza się

F(M)=F(xi+1,yi12)

Jeżeli F (M) > 0, to punkt M leży na zewnątrz elipsy i wybieramy piksel Pi+1=SE. Natomiast, gdy F (M)⇐ 0, to punkt M leży wewnątrz elipsy lub na jej brzegu i wybieramy piksel Pi+1=E. Gdy osią wiodąca jest OY oblicza się

F(M)=F(xi+12,yi1)

Jeżeli F(M)>0, to punkt M leży na zewnątrz elipsy i wybieramy piksel Pi+1=S. Natomiast, gdy F(M)0, to punkt M leży wewnątrz elipsy lub na jej brzegu i wybieramy piksel Pi+1=SE. Algorytm nie wymaga jednak wyliczania każdorazowo wartości funkcji F(M). Jego siła leży w możliwości wyliczania wartości tej funkcji (czyli zmiennej decyzyjnej) w kolejnym kroku (di+1) mniej obliczeń.

Zgodnie z przyjętymi założeniami elipsę zaczynamy rysować w punkcie (0,b). Ponieważ osią wiodącą jest wówczas OX policzymy wartość zmiennej decyzyjnej d dla piksela startowego P0=(x0,y0)=(0,b)

d0=F(1,b12)=b2+a2(b+14)

Jeżeli następnym pikselem jest Pi+1=E, czyli xi+1=xi+1,yi+1=yi, to wartość zmiennej decyzyjnej wynosi:

di+1=F(xi+1+1,yi+112)=b2(xi+1+1)2+a2(yi+112)2a2b2=b2(xi+1+1)2+a2(yi12)2a2b2=F(xi+1,yi12)+2b2xi+1+b2=di+2b2xi+1+b2

Jeżeli następnym pikselem jest Pi+1=SE, czyli xi+1=xi+1,yi+1=yi1, to wartość zmiennej decyzyjnej wynosi:

di+1=F(xi+1+1,yi+112)=b2(xi+1+1)2+a2(yi+112)2a2b2=b2(xi+1+1)2+a2(yi112)a2b2=F(xi+1,yi12)+2b2xi+1+b22a2(yi12)+a2=di+2b2xi+12a2yi+1+b2

Przy zmianie osi wiodącej na OY należy także zmienić wartość zmiennej decyzyjnej. Różnica między „nową” i „starą” zmienną wynosi:

F(xi+12,yi1)F(xi+1,yi12)=b2(xi+12)2+a2(yi1)2a2b2b2(xi+1)2a2(yi12)2+a2b2=b2(xi2+xi+14)+a2(yi22yi+1)b2(xi2+2xi+1)a2(yi2yi+14)=b2(xi2xi+141)+a2(2yi+yi+114)=b2(xi34)+a2(yi+34)

Teraz wyliczymy rekurencyjne równania opisujące zmienną decyzyjną, gdy osią wiodącą jest OY. Jeżeli następnym pikselem jest Pi+1=SE, czyli xi+1=xi+1,yi+1=yi1, to wartość zmiennej decyzyjnej wynosi:

di+1=F(xi+1+12,yi+11)=b2(xi+1+12)2+a2(yi+11)2a2b2=b2(xi+1+12)2+a2(yi11)2a2b2=F(xi+12,yi1)+2b2(xi+12)2a2(yi1)+a2+b2=di+2b2xi+12a2yi+1+a2

Przy wyborze następnego piksela Pi+1=S, czyli xi+1=xi,yi+1=yi1, wartość zmiennej decyzyjnej wynosi:

di+1=F(xi+1+12,yi+11)=b2(xi+1+12)2+a2(yi+11)2a2b2=b2(xi+12)2+a2(yi11)2a2b2=F(xi+12,yi1)2a2(yi1)+a2=di2a2yi+1+a2

Bibliografia

Linki zewnętrzne