Zanim przystąpisz do tej lekcji, zapoznaj się z poprzednimi lekcjami:

Wstęp do grafiki - biblioteki SDL i newgfx
Opis biblioteki newgfx


 

Grafika SLD - rysowanie linii i wielokątów

Dzisiaj zajmiemy się rysowaniem linii, z których będziemy stopniowo tworzyć różne figury geometryczne. Uruchom środowisko Code::Blocks i utwórz w nim projekt SDL z szablonu - jak to zrobić, dokładnie opisaliśmy w poprzedniej lekcji i nie będziemy tych wiadomości więcej powtarzać.

Na początek stworzymy taki oto rysunek:

 

obrazek

 

Rysunek ten tworzy pęk linii, które wychodzą z lewego górnego narożnika ekranu i biegną do punktów leżących na dolnej krawędzi. Punktów tych jest N i są one w równych odległościach od siebie. Pierwszym problemem będzie wyznaczenie współrzędnych tych punktów. Posłużymy się w tym celu prostą proporcją:

 

obrazek

 

gdzie:

n - liczba wszystkich punktów
i - numer punktu, i  = 0,1,...,n-1
w - szerokość okna graficznego w pikselach
xi - współrzędna x  i-tego punktu

 

Zatem i-ta linia biegnie od punktu 0,0 (lewy górny narożnik okna graficznego) do punktu xi,h-1, gdzie h  to wysokość okna graficznego w pikselach.

Wpisz do szablonu następujący fragment programu (pod komentarzem):

 

// tutaj wstawiamy program graficzny

    const Sint32 N = 20;  // liczba punktów

    Sint32 i, x;

    for(i = 0; i < N; i++)
    {
        x = i * (screen->w - 1) / (N - 1);
        gfxLine(screen, 0, 0, x, screen->h - 1, 0xffffff);
    }

 

Zmodyfikuj program, aby otrzymać następującą grafikę:

 

obrazek

 

A teraz tak:

 

obrazek

 

Poeksperymentuj z kolorem (składowe koloru można zmieniać w zależności od i  - tutaj również przydaje się proporcja).

 

obrazek

 

// tutaj wstawiamy program graficzny

    const Sint32 N = 20;  // liczba punktów

    Sint32 i, x, r, g, b, c;

    for(i = 0; i < N; i++)
    {
        x = i * (screen->w - 1) / (N - 1);  // współrzędna xi

        r = i * 255 / (N - 1);              // składowa czerwona
        g = 255 - i * 255 / (N - 1);        // składowa zielona
        b = 255;                            // składowa niebieska

        c = (r << 16) | (g << 8) | b;       // kolor linii

        gfxLine(screen, 0, 0, x, screen->h - 1, c);
        gfxLine(screen, screen->w-1, 0, x, screen->h - 1, c);
        gfxLine(screen, 0, screen->h-1, x, 0, c);
        gfxLine(screen, screen->w-1, screen->h-1, x, 0, c);
    }

 

Zamiast funkcji gfxLine() wykorzystaj funkcję gfxWuLine(). Działa ona identycznie. Różnica polega na tym, iż gfxWuLine() używa algorytmu wygładzania opracowanego przez profesora Xiaolin Wu. Linie wyglądają dużo lepiej.

 


 

Kolejna grafika powinna wyglądać następująco:

 

obrazek

 

Na pierwszy rzut oka wygląda to dosyć skomplikowanie. A robimy to tak:

 

obrazek

 

Wzdłuż lewej i dolnej krawędzi ekranu wyznaczamy n  punktów. Wykorzystujemy do tego znaną nam proporcję:

 

obrazek

gdzie:

n - liczba wszystkich punktów
i - numer punktu, i  = 0,1,...,n-1
w - szerokość okna graficznego w pikselach
h - wysokość okna graficznego w pikselach
xi - współrzędna x  i-tego punktu
yi - współrzędna y i-tego punktu

 

Następnie łączymy odcinkami kolejno pierwszy punkt na lewej krawędzi z pierwszym punktem na krawędzi dolnej, drugi punkt na lewej krawędzi z drugim punktem na krawędzi dolnej itd:

 

obrazek

 

// tutaj wstawiamy program graficzny

  const Sint32 N = 20; // liczba punktów

  Sint32 i, x, y;

  for(i = 0; i < N; i++)
  {
    x = i * (screen->w - 1) / (N - 1);
    y = i * (screen->h - 1) / (N - 1);
    gfxLine(screen, 0, y, x, screen->h - 1, 0xffffff);
  }

 

Zmodyfikuj program, tak aby otrzymać kolejno poniższe grafiki:

 

obrazek obrazek obrazek

 


 

Problem rysowania kwadratów i prostokątów rozwiązuje nasza biblioteka, w której mamy odpowiednią do tego celu funkcję:

 

gfxRect(s, r, c)

s - wskaźnik do struktury SDL_Surface
r - wskaźnik do struktury SDL_Rect
c - kolor prostokąta

 

Struktura SDL_Rect definiuje prostokąt i ma następującą postać:

 

typedef struct
{
  Sint16 x, y; // współrzędne lewego, górnego narożnika prostokąta
  Uint16 w, h; // szerokość i wysokość prostokąta w pikselach
} SDL_Rect;

 

Poniższy program rysuje ramkę o rozmiarze okna graficznego:

 

 // tutaj wstawiamy program graficzny

  SDL_Rect * r = new SDL_Rect;

  r->x = r->y = 0;
  r->w = screen->w;
  r->h = screen->h;

  gfxRect(screen, r, 0xffffff);

 

obrazek

 

Przez prostą modyfikację tego programu można uzyskać ciekawy efekt:

 

// tutaj wstawiamy program graficzny

    SDL_Rect * r = new SDL_Rect;

    Sint32 c,i;

    r->x = r->y = 0;
    r->w = screen->w;
    r->h = screen->h;

    for(i = 1; i < screen->h; i += 2)
    {
        c = 255 - i * 255 / (screen->h - 1);
        c = (c << 16) | (c << 8) | c;

        gfxRect(screen, r, c);

        r->x++;
        r->y++;
        r->w -= 2;
        r->h -= 2;
    }

 

obrazek

 

Napisz samodzielnie programy, które rysują następujące grafiki:

 

obrazek obrazek

 


 

Rysowanie wielokątów rozwiązujemy następująco za pomocą funkcji gfxMoveTo() i gfxLineTo():

  1. Wyznaczamy współrzędne kolejnych wierzchołków.

  2. Pierwszy wierzchołek jest wierzchołkiem startowym. Przekazujemy go do funkcji gfxMoveTo(), która zapamiętuje jego współrzędne.

  3. Kolejne wierzchołki przekazujemy do funkcji gfxLineTo(), która rysuje linie od poprzedniego punktu do punktu otrzymanego jako parametr. Poprzedni punkt jest punktem przekazanym do gfxMoveTo() lub do gfxLineTo().

  4. Od ostatniego wierzchołka rysujemy linię do wierzchołka pierwszego.

Poniższy program rysuje trójkąt:

 

// tutaj wstawiamy program graficzny

  gfxMoveTo(0,screen->h-1);                           // pierwszy wierzchołek
  gfxLineTo(screen,screen->w/2,0,0xffffff);           // drugi wierzchołek
  gfxLineTo(screen,screen->w-1,screen->h-1,0xffffff); // trzeci wierzchołek
  gfxLineTo(screen,0,screen->h-1,0xffffff);           // pierwszy wierzchołek

 

obrazek

 

Wielokąty foremne otrzymujemy w sposób następujący:

 

obrazek

 

Bierzemy okrąg o promieniu r  i środku w punkcie xs,ys.

 

obrazek

 

Na obwodzie okręgu wyznaczamy w równych od siebie odległościach n  punktów.

 

obrazek

 

Punkty łączymy liniami wg zasad opisanych powyżej. Podstawowym problemem jest tutaj wyznaczenie współrzędnych tych punktów. Zadanie to rozwiążemy prosto wykorzystując podstawowe wiadomości z geometrii. Na początek załóżmy, że środek okręgu jest w początku układu współrzędnych.

 

obrazek

 

Dla każdego punktu leżącego na takim okręgu zachodzą następujące zależności:

 

obrazek

gdzie

αP - kąt pomiędzy osią OX, a promieniem r  poprowadzonym do punktu P
r - promień okręgu
xP - współrzędna x  punktu P
yP - współrzędna y  punktu P

 

Znając kąt αP, możemy bez problemów wyznaczyć współrzędne punktu:

 

obrazek

 

Jeśli punkty są umieszczone na obwodzie okręgu w równych odległościach, to kąty pomiędzy ich promieniami są również równe i wynoszą:

 

obrazek

 

Mając dany kąt αP  dla pierwszego punktu, pozostałe kąty obliczymy dodając kolejno nasze α. Zatem wzory na współrzędne kolejnych punktów są następujące:

 

obrazek

gdzie i  = 0,1,...,n-1

 

Współrzędne te odnoszą się do przypadku, gdy środek okręgu znajduje się w środku układu współrzędnych. Jeśli środek okręgu jest w innym punkcie, to po prostu do wyliczonych współrzędnych dodajemy współrzędne środka okręgu. Nasze wzory przyjmują ostateczną postać:

 

obrazek

 

W poniższym programie należy dołączyć na początku plik nagłówkowy cmath (dla funkcji trygonometrycznych). Program rysuje wielokąty foremne z punktów na obwodzie okręgu o promieniu równym połowie wysokości okna graficznego. Środek okręgu jest umieszczony w środku tego okna. Kat αP  przyjmujemy zerowy i nie uwzględniamy go we wzorach (zrobimy to za chwilę w innym programie).

 

#include <cmath>

...

// tutaj wstawiamy program graficzny

    const Sint32 N = 3;  // określa liczbę wierzchołków wielokąta

    Sint32 i,x,y,xp,yp,r;

    xp = screen->w / 2;
    yp = screen->h / 2;
    r  = screen->h / 2;

    for(i = 0; i <= N; i++)
    {
        x = xp + r * cos(i * 6.283185 / N);
        y = yp + r * sin(i * 6.283185 / N);
        if(!i) gfxMoveTo(x,y);  // pierwszy punkt
        else   gfxLineTo(screen,x,y,0xffffff);
    }

 

Zmieniając stałą N otrzymujemy różne wielokąty foremne. Zwróć uwagę, iż ze wzrostem N figura coraz bardziej przypomina okrąg.

 

N = 3 N = 4 N = 5
obrazek obrazek obrazek
N = 6 N = 8 N = 12
obrazek obrazek obrazek

 

Manipulując promieniem r  możemy otrzymywać ciekawe efekty graficzne:

 

#include <cmath>

...

// tutaj wstawiamy program graficzny

    const Sint32 N = 3;  // określa liczbę wierzchołków wielokąta

    Sint32 i,x,y,xp,yp,r;

    xp = screen->w / 2;
    yp = screen->h / 2;

    for(r = 8; r <= screen->h / 2; r += 8)
      for(i = 0; i <= N; i++)
      {
          x = xp + r * cos(i * 6.283185 / N);
          y = yp + r * sin(i * 6.283185 / N);
          if(!i) gfxMoveTo(x,y);  // pierwszy punkt
          else   gfxLineTo(screen,x,y,0xffffff);
      }

 

N = 3 N = 6 N = 15
obrazek obrazek obrazek

 

Jeśli w trakcie rysowania kolejnych figur będziemy dodatkowo zmieniali kat pierwszego punktu, to otrzymamy bardzo ciekawy efekt graficzny:

 

#include <cmath>

...

// tutaj wstawiamy program graficzny

    const Sint32 N = 3;  // określa liczbę wierzchołków wielokąta

    Sint32 i,x,y,xp,yp,r;
    double alpha;

    xp = screen->w / 2;
    yp = screen->h / 2;

    for(r = 8; r <= screen->h / 2; r += 8)
    {
      alpha = 3.1415 * r / (screen->h / 2);

      for(i = 0; i <= N; i++)
      {
          x = xp + r * cos(alpha + i * 6.283185 / N);
          y = yp + r * sin(alpha + i * 6.283185 / N);
          if(!i) gfxMoveTo(x,y);  // pierwszy punkt
          else   gfxLineTo(screen,x,y,0xffffff);
      }
    }

 

N = 3 N = 4 N = 5
obrazek obrazek obrazek

 

Kolejna możliwość to cykliczne wyznaczanie punktów wielokąta, jednakże przy każdym punkcie zwiększamy (zmniejszamy) promień r. W rezultacie otrzymujemy spiralę.

 

#include <cmath>

...

// tutaj wstawiamy program graficzny

    const Sint32 N = 3;  // okresla liczbę wierzchołków wielokata

    Sint32 i,x,y,xp,yp,r;

    xp = screen->w / 2;
    yp = screen->h / 2;

    i = 0;
    for(r = 1; r <= screen->h / 2; r += 4)
    {
        x = xp + r * cos(i * 6.283185 / N);
        y = yp + r * sin(i * 6.283185 / N);
        if(!i) gfxMoveTo(x,y);  // pierwszy punkt
        else   gfxLineTo(screen,x,y,0xffffff);
        i++;
    }

 

N = 3 N = 4 N = 9
obrazek obrazek obrazek

 

Skoki tych spiral zależą od ilości wierzchołków w wielokącie - im jest ich więcej, tym szybciej rośnie promień r i skok się zwiększa. Zastanów się nad takim przerobieniem algorytmu, aby spirale posiadały zadaną liczbę zwoi. Sprawdź również wygląd figur, gdy funkcję gfxLineTo() zamieni się na gfxWuLineTo(), która rysuje linie wygładzone.

 


   I Liceum Ogólnokształcące   
im. Kazimierza Brodzińskiego
w Tarnowie

©2024 mgr Jerzy Wałaszek

Dokument ten rozpowszechniany jest zgodnie z zasadami licencji
GNU Free Documentation License.

Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl

W artykułach serwisu są używane cookies. Jeśli nie chcesz ich otrzymywać,
zablokuj je w swojej przeglądarce.
Informacje dodatkowe