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:

 

 

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ą:

 

 

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ę:

 

 

A teraz tak:

 

 

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

 

 

// 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:

 

 

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

 

 

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

 

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:

 

 

// 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:

 

 


 

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);

 

 

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;
    }

 

 

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

 

 


 

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

 

 

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

 

 

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

 

 

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

 

 

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.

 

 

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

 

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:

 

 

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ą:

 

 

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:

 

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ć:

 

 

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
N = 6 N = 8 N = 12

 

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

 

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

 

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

 

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.

 



List do administratora Serwisu Edukacyjnego Nauczycieli I LO

Twój email: (jeśli chcesz otrzymać odpowiedź)
Temat:
Uwaga: ← tutaj wpisz wyraz  ilo , inaczej list zostanie zignorowany

Poniżej wpisz swoje uwagi lub pytania dotyczące tego rozdziału (max. 2048 znaków).

Liczba znaków do wykorzystania: 2048

 

W związku z dużą liczbą listów do naszego serwisu edukacyjnego nie będziemy udzielać odpowiedzi na prośby rozwiązywania zadań, pisania programów zaliczeniowych, przesyłania materiałów czy też tłumaczenia zagadnień szeroko opisywanych w podręcznikach.



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

©2017 mgr Jerzy Wałaszek

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