Tworzenie grafiki SDL

Poprzednie tematy, które należy przerobić przed przystąpieniem do tej lekcji:

01 - Instalacja biblioteki SDL dla Dev-C++
02 - Powierzchnia graficzna w SDL - stawianie punktów
03 - Tworzenie biblioteki graficznej mylib
04 - Stawianie punktów i rysowanie linii poziomych oraz pionowych
05 - Zabawa z pikselami
06 - Rysowanie odcinków - algorytm Bresenhama

Pliki biblioteczne i projektowe do pobrania: Parametry dla konsolidatora
mylib.h
mylib.cpp
main.cpp
-lmingw32
-mwindows
-lSDLmain
-lSDL

Opis funkcji bibliotecznych

Na tej lekcji poznasz praktyczne sposoby pracy z funkcjami bibliotecznymi line(), moveto() oraz lineto().

Zabawa z odcinkami

SDL

Skopiuj do katalogu c:\dev-cpp\include pliki mylib.cpp oraz mylib.h i utwórz nowy projekt SDL. Pobierz szablin main.cpp.

Na poprzedniej lekcji omówiliśmy algorytm Bresenhama, który pozwala rysować odcinki linii prostych na powierzchniach rastrowych. Nie musisz go znać, aby korzystać z jego dobrodziejstw. Wystarczy, że poznasz funkcję line():

 

line(x1,y1,x2,y2,color)

x1,y1 - współrzędne punktu początkowego odcinka
x2,y2 - współrzędne punktu końcowego odcinka
color - kolor rysowanej linii

Punkt początkowy (x1,y1) oraz końcowy (x2,y2) muszą leżeć na powierzchni graficznej, tzn. nie mogą wychodzić poza zdefiniowany obszar okna.

Zacznijmy od narysowania dwóch linii przekątnych. W tym celu musimy określić punkty początkowe i końcowe linii. Przydatny może być poniższy rysunek, na którym zaznaczyliśmy współrzędne narożników okna. Literka w oznacza szerokość okna (screen→w), a h jego wysokość (screen→h):

 

 

Wpisz do szablonu SDL poniższy fragment kodu (w miejscu oznaczonym komentarzem):

 

line(0, 0, screen->w - 1, screen->h - 1, 0xffffff);
line(0, screen->h - 1, screen->w - 1, 0, 0xffffff);

 

W efekcie otrzymasz poniższe okienko:

 

 

Skomplikujmy nieco zadanie. Z punktu (0,0) (lewy górny narożnik okna) poprowadzimy kilka odcinków tak, aby kończyły się na dolnej krawędzi okna w równych od siebie odległościach. W tym celu na spodzie okna musimy wyznaczyć punkty końcowe odcinków.

 

Wszystkie punkty końcowe posiadają współrzędną y równą h-1. Współrzędne x tych punktów są równo odległe od siebie. Załóżmy, iż punktów tych będzie n. Zatem zadanie sprowadza się do wyznaczenia wartości x0, x1, x2, ..., xn-2, xn-1. Przy tym x0 = 0, a xn-1 = w-1. Posłużymy się twierdzeniem Talesa:

 

Otrzymujemy:

Usuń z szablonu poprzednio wpisane funkcje line() i zastąp je poniższym kodem:

 

Uint32 n = 32;

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

 

Po kompilacji i uruchomieniu otrzymasz następującą grafikę:

 

 

Zauważ, iż w górnym narożniku powstaje ciekawy wzorek z pikseli - nazywa się on morą od francuskiego słowa moire. Zobacz do Wikipedi.

Odbijamy grafikę w poziomie - rysujemy linię z prawego górnego narożnika (w-1,0) do punktu (xi, h-1):

 

Uint32 n = 32;

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

 

 

Dodamy teraz kolor. Uzależnimy jego zmianę od wartości zmiennej sterującej pętli i - wykorzystamy ponownie twierdzenie Talesa (dobra rzecz - zapamiętaj to sobie) do wyznaczenia wartości składowych R, G i B:

 

Uint32 n = 32;

for(Uint32 i = 0; i < n; i++)
{
  Uint32 x = i * (screen->w - 1) / (n - 1);

  Uint32 G = i * 255 / (n - 1);
  Uint32 R = 255 - G;
  Uint32 B = G;

  Uint32 color = rgb(R, G, B);

  line(0, 0, x, screen->h - 1, color);
  line(screen->w - 1, 0, x, screen->h - 1, color);
}

 

 

Zmodyfikuj kod tak, aby otrzymać odbicie grafiki w pionie:

 

 

Teraz narysujemy linię nieco inaczej. Początek linii będzie wyprowadzany z kolejnych, równoodległych punktów na boku pionowym. Punkty te policzymy oczywiście z twierdzenia Talesa:

Usuń z szablonu poprzedni kod graficzny i na jego miejscu umieść poniższy kod:

 

Uint32 n = 32;

for(Uint32 i = 0; i < n; i++)
{
  Uint32 x = i * (screen->w - 1) / (n - 1);
  Uint32 y = i * (screen->h - 1) / (n - 1);

  line(0, y, x, screen->h - 1, 0xffffff);
}

 

W wyniku otrzymasz następującą grafikę:

 

 

Następnie utworzymy lustrzane odbicia w pionie i poziomie (figura będzie wyglądała ciekawiej przy większym rozmiarze powierzchni graficznej) :

 

Uint32 n = 32;

for(Uint32 i = 0; i < n; i++)
{
  Uint32 x = i * (screen->w - 1) / (n - 1);
  Uint32 y = i * (screen->h - 1) / (n - 1);

  line(0, y, x, screen->h - 1, 0xffffff);
  line(screen->w - 1, y, screen->w - 1 - x, screen->h - 1, 0xffffff);
  line(0, screen->h - 1 - y, x, 0, 0xffffff);
  line(screen->w - 1, screen->h - 1 - y, screen->w - 1 - x, 0, 0xffffff);
}

 

 

Na koniec dodaj kolor do rysowanych linii - możesz uzależnić składowe od zmiennej i wykorzystując twierdzenie Talesa. Efekt może być przykładowo taki jak poniżej:

 

 

 

Funkcja line() wymaga podania jako parametrów współrzędnych początku i końca rysowanego odcinka. W bibliotece mylib zdefiniowane są dwie pożyteczne funkcje pomocnicze: moveto() oraz lineto(). Pierwsza określa punkt startowy rysowania linii przez lineto(). Punkt końcowy staje się kolejnym punktem startowym dla następnego wywołania lineto(). Pozwala to rysować łamane.

Usuń z szablonu kod graficzny i zastąp go poniższym kodem, który generuje łamaną zbudowaną z 31 odcinków o losowo wygenerowanych współrzędnych wierzchołków. Na początku programu dodaj:

#include <ctime>

 

    const int n = 32;  // liczba wierzchołków łamanej
    
    srand((unsigned)time(NULL));
    
    for(int i = 0; i < n; i++)
    {
      Uint32 x = rand() % screen->w;
      Uint32 y = rand() % screen->h;
      if(i) lineto(x, y, 0xffffff);
      else  moveto(x, y);        
    }

 

 

Przy pomocy łamanych możemy w prosty sposób rysować okręgi oraz wielokąty foremne. W tym celu należy wyznaczyć na obwodzie okręgu odpowiednią liczbę punktów i połączyć je liniami. Współrzędne punktów obliczamy wykorzystując funkcje trygonometryczne:

 

Środek okręgu w punkcie (0,0):

 

Środek okręgu w punkcie (xs,ys):

Jeśli kąt α będzie się zmieniał w równych odstępach w zakresie od 0 do 2π, to otrzymamy równoodległe punkty na obwodzie całego okręgu. Do wyznaczania kąta α znów wykorzystamy twierdzenie Talesa. Aby figura była zamknięta, punkt pierwszy i ostatni łamanej muszą posiadać te same współrzędne. Poniższy kod rysuje okrąg przybliżony wielokątem o 100 wierzchołkach.

 

const int n = 101;          // liczba wierzchołków łamanej

Uint32 xs = screen->w >> 1; // współrzędne środka ekranu
Uint32 ys = screen->h >> 1;
Uint32 r = ys;              // promień okręgu

if(r > xs) r = xs;

for(int i = 0; i < n; i++)
{
  double alpha = 6.283185 * i / (n - 1);

  Uint32 x = (Uint32)(xs + r * cos(alpha));
  Uint32 y = (Uint32)(ys + r * sin(alpha));

  if(i) lineto(x,y,0xffffff);
  else  moveto(x,y); 
}

 

 

Zmieniając w powyższym kodzie liczbę wierzchołków n otrzymamy różne wielokąty foremne:

n = 4 n = 5 n = 6
     
n = 7 n = 8 n = 9
     
n = 10 n = 15 n = 30

 

Zauważ, iż na początku kolejne wielokąty różnią się znacznie od siebie. Jednakże wraz ze wzrostem n różnice te stają się coraz mniejsze a sam wielokąt zaczyna coraz bardziej przypominać okrąg. Po przekroczeniu pewnej graniczonej wartości n kształt figury już praktycznie nie zmienia się.

W naszym kodzie możemy modyfikować wiele parametrów. Powtarzając rysowanie wielokąta m razy dla kolejno rosnących promieni r i operując kolorem linii otrzymamy ciekawe efekty graficzne:

 

const int m = 13;           // liczba figur
const int n = 4;            // liczba wierzchołków łamanej
Uint32 xs = screen->w >> 1; // współrzędne środka ekranu
Uint32 ys = screen->h >> 1;
Uint32 rs = ys;             // maksymalny promień okręgu

if(rs > xs) rs = xs;

for(int j = 1; j < m; j++)  // pętla rysująca m figur
{
  Uint32 r = j * rs / (m - 1);

  Uint32 R = j * 255 / (m - 1);
  Uint32 G = 255 - (R >> 1);
  Uint32 B = 255 - R;
  Uint32 color = rgb(R,G,B);

  for(int i = 0; i < n; i++)  // pętla rysująca figurę
  {
    double alpha = 6.283185 * i / (n - 1);

    Uint32 x = (Uint32)(xs + r * cos(alpha));
    Uint32 y = (Uint32)(ys + r * sin(alpha));

    if(i) lineto(x,y,color);
    else  moveto(x,y); 
  }
}

 

m = 10, n = 4 m = 16, n = 7 m = 21, n = 51

 

A teraz wprowadźmy dodatkowy kąt obrotu figury - β, który będzie sumowany z katem α wewnątrz funkcji trygonometrycznych. Jeśli przed narysowaniem figury kat ten będzie odpowiednio wzrastał od 0 do 2π, to otrzymamy poniższe efekty graficzne:

 

const int m = 33;           // liczba figur
const int n = 4;            // liczba wierzchołków łamanej
Uint32 xs = screen->w >> 1; // współrzędne środka ekranu
Uint32 ys = screen->h >> 1;
Uint32 rs = ys;             // maksymalny promień okręgu

if(rs > xs) rs = xs;

for(int j = 1; j < m; j++)  // pętla rysująca m figur
{
  Uint32 r = j * rs / (m - 1);

  Uint32 R = j * 255 / (m - 1);
  Uint32 G = 255 - (R >> 1);
  Uint32 B = 255 - R;
  Uint32 color = rgb(R,G,B);

  double beta = 6.283185 * j / (m - 1); // kąt obrotu

  for(int i = 0; i < n; i++)  // pętla rysująca figurę
  {
    double alpha = 6.283185 * i / (n - 1);

    Uint32 x = (Uint32)(xs + r * cos(alpha + beta));
    Uint32 y = (Uint32)(ys + r * sin(alpha + beta));

    if(i) lineto(x,y,color);
    else  moveto(x,y); 
  }
}

 

m = 33, n = 4 m = 50, n = 5 m = 25, n = 7

 

Ćwiczenia

Zadanie 1

Zmodyfikuj kod rysowania wielokątów tak, aby rysował spirale (w tym celu zmieniaj promień r wewnątrz procedury rysowania wielokąta). Rysowanie spirali powinno być określone następującymi parametrami:

        rs - wartość początkowa promienia
        rk - wartość końcowa promienia
        k - liczba zwojów spirali
        n - liczba punktów na jeden obieg spirali

Przy pomocy tego kodu utwórz poniższe grafiki

 

 

Zadanie 2

Sprawdź, co się stanie, jeśli we wzorze na współrzędną y wielokąta zastosujemy podwojoną wartość kąta α (a może wyższe wielokrotności?). Otrzymywane figury noszą nazwę krzywych Lissajous. Sprawdź różne kombinację wielokrotności kąta α dla obu współrzędnych x i y.

 

 

Zadanie 3

Oblicz i zapamiętaj w tablicy współrzędne n punktów równo rozłożonych na obwodzie okręgu. Środek okręgu umieść na środku swojego ekranu. Następnie połącz wszystkie pary punktów odcinkami.

Dla 15 punktów powinieneś otrzymać poniższą figurę graficzną:

 

 



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.