Serwis Edukacyjny Nauczycieli w I-LO w Tarnowie ![]() Materiały dla uczniów liceum |
Wyjście Spis treści Wstecz Dalej
Autor artykułu: mgr Jerzy Wałaszek |
©2025 mgr Jerzy Wałaszek
|
SPIS TREŚCI |
Podrozdziały |
Odcinek (ang. line) składa się z ciągu pikseli leżących na ekranie obok siebie w taki sposób, iż dają wrażenie prostej linii ciągłej. Nie jest to definicja akademicka. Linie tak naprawdę będą odcinkami o końcach określonych współrzędnymi. W SDL2 mamy zadanie ułatwione przy kreśleniu prostych linii, ponieważ istnieje do tego celu odpowiednia funkcja:
SDL_RenderDrawLine(r,x1,y1,x2,y2)
r – wskaźnik struktury SDL_Renderer z kontekstem graficznym
x1,y1 – współrzędne punktu startowego
odcinka
x2,y2 – współrzędne punktu końcowego
Proponuję od razu wypróbować tę funkcję.
Utwórz w CodeBlocks projekt SDL2, wpisz do edytora poniższy tekst programu, skompiluj go i uruchom:
C++// Linie 1 //--------- #include <SDL.h> #include <iostream> using namespace std; // Rozmiar okienka const int W_W = 640; const int W_H = 480; int main(int argc, char* args[]) { // Inicjujemy SDL if(SDL_Init(SDL_INIT_VIDEO)) { cout << "SDL_Init Error: " << SDL_GetError() << endl; return 1; } // Tworzymy okno SDL_Window * w = SDL_CreateWindow("Linie", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0); if(!w) { cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl; SDL_Quit(); return 2; } // Tworzymy kontekst graficzny SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC); if(!r) { cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl; SDL_DestroyWindow(w); SDL_Quit(); return 3; } // Rysujemy linie przekątne SDL_SetRenderDrawColor(r,255,0,0,255); // kolor czerwony if(SDL_RenderDrawLine(r,0,0,W_W-1,W_H-1)) cout << SDL_GetError() << endl;; SDL_SetRenderDrawColor(r,0,255,0,255); // kolor zielony SDL_RenderDrawLine(r,W_W-1,0,0,W_H-1); // Uaktualniamy obraz SDL_RenderPresent(r); // Czekamy na zdarzenie SDL_QUIT SDL_Event e; while(1) { SDL_WaitEvent(&e); if(e.type == SDL_QUIT) { cout << "Program closed after " << e.quit.timestamp << " miliseconds" << endl << endl; break; } } // Usuwamy kontekst graficzny SDL_DestroyRenderer(r); // Usuwamy okno SDL_DestroyWindow(w); // Kończymy pracę z SDL2 SDL_Quit(); return 0; } |
Program nie powinien sprawiać kłopotów w zrozumieniu, gdyż jest podobny do programów przedstawionych w poprzednim rozdziale. Rysowane są tutaj dwie linie przekątne, jedna w kolorze czerwonym, druga zielona, po czym program czeka na zamknięcie przez użytkownika okna roboczego.
Kolejny program tworzy nieco ciekawszy efekt. Na całej szerokości okna wyznaczamy zadaną liczbę równoodległych punktów na dolnej i górnej krawędzi, po czym rysujemy odcinki z narożników okna do tych punktów.
C++// Linie 2 //--------- #include <SDL.h> #include <iostream> using namespace std; // Rozmiar okienka const int W_W = 640; const int W_H = 480; // Liczba punktów const int N = 20; int main(int argc, char* args[]) { // Inicjujemy SDL if(SDL_Init(SDL_INIT_VIDEO)) { cout << "SDL_Init Error: " << SDL_GetError() << endl; return 1; } // Tworzymy okno SDL_Window * w = SDL_CreateWindow("Linie", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0); if(!w) { cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl; SDL_Quit(); return 2; } // Tworzymy kontekst graficzny SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC); if(!r) { cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl; SDL_DestroyWindow(w); SDL_Quit(); return 3; } // Rysujemy linie z narożników do wyznaczonych punktów int i,x; for(i = 0; i < N; i++) { x = (i * W_W) / N; SDL_SetRenderDrawColor(r,255,0,0,255); // Czerwony SDL_RenderDrawLine(r,0,0,x,W_H-1); SDL_SetRenderDrawColor(r,0,0,255,255); // Niebieski SDL_RenderDrawLine(r,W_W-1,0,x,W_H-1); SDL_SetRenderDrawColor(r,0,255,0,255); // Zielony SDL_RenderDrawLine(r,0,W_H-1,x,0); SDL_SetRenderDrawColor(r,255,255,255,255); // Biały SDL_RenderDrawLine(r,W_W-1,W_H-1,x,0); } // Uaktualniamy obraz SDL_RenderPresent(r); // Czekamy na zdarzenie SDL_QUIT SDL_Event e; while(1) { SDL_WaitEvent(&e); if(e.type == SDL_QUIT) { cout << "Program closed after " << e.quit.timestamp << " miliseconds" << endl << endl; break; } } // Usuwamy kontekst graficzny SDL_DestroyRenderer(r); // Usuwamy okno SDL_DestroyWindow(w); // Kończymy pracę z SDL2 SDL_Quit(); return 0; } |
Wykorzystując pomysł z programu powyżej można uzyskać jeszcze ciekawszy efekt graficzny. W tym celu wyznaczamy równoodległe punkty na krawędziach poziomych i pionowych okna:
Łączymy odcinkiem pierwszy punkt na boku pionowym z pierwszym punktem na boku poziomym:
Następnie łączymy drugi punkt na boku pionowym z drugim punktem na boku poziomym:
Teraz trzeci punkt pionowy z trzecim punktem poziomym:
Operację kontynuujemy, aż połączymy ze sobą wszystkie punkty pionowe z poziomymi:
Poprzez symetrię taką samą operację wykonujemy na pozostałych parach boków pionowych i poziomych:
Gdy liczba punktów będzie odpowiednio duża, efekt stanie się ciekawy. A oto program:
C++// Linie 3 //--------- #include <SDL.h> #include <iostream> using namespace std; // Rozmiar okienka const int W_W = 640; const int W_H = 480; // Liczba punktów const int N = 30; int main(int argc, char* args[]) { // Inicjujemy SDL if(SDL_Init(SDL_INIT_VIDEO)) { cout << "SDL_Init Error: " << SDL_GetError() << endl; return 1; } // Tworzymy okno SDL_Window * w = SDL_CreateWindow("Linie", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0); if(!w) { cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl; SDL_Quit(); return 2; } // Tworzymy kontekst graficzny SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC); if(!r) { cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl; SDL_DestroyWindow(w); SDL_Quit(); return 3; } // Łączymy liniami punkty pionowe z poziomymi int i,x,y; for(i = 0; i < N; i++) { x = (i * (W_W - 1)) / (N - 1); // Punkt w poziomie y = (i * (W_H - 1)) / (N - 1); // Punkt w pionie SDL_SetRenderDrawColor(r,255,255,255,255); // Kolor biały SDL_RenderDrawLine(r,0,y,x,W_H-1); SDL_RenderDrawLine(r,W_W-1,y,W_W-1-x,W_H-1); SDL_RenderDrawLine(r,0,W_H-1-y,x,0); SDL_RenderDrawLine(r,W_W-1,W_H-y,W_W-x,0); } // Uaktualniamy obraz SDL_RenderPresent(r); // Czekamy na zdarzenie SDL_QUIT SDL_Event e; while(1) { SDL_WaitEvent(&e); if(e.type == SDL_QUIT) { cout << "Program closed after " << e.quit.timestamp << " miliseconds" << endl << endl; break; } } // Usuwamy kontekst graficzny SDL_DestroyRenderer(r); // Usuwamy okno SDL_DestroyWindow(w); // Kończymy pracę z SDL2 SDL_Quit(); return 0; } |
Kolejny program tworzy ciekawą grafikę, łącząc ze sobą punkty leżące na obwodzie elipsy wpisanej w ekran. Pokażę krok po kroku, jak należy podejść do tego od strony matematycznej.
Mamy okno o szerokości W_W (ang. window width) i wysokości W_H (ang. window height):
Na rysunku zaznaczone są również współrzędne punktów narożnych. W okno wpisujemy elipsę, tak aby stykała się z jego krawędziami:
Środek elipsy znajduje się w środku okna.
Na obwodzie elipsy wybieramy punkt P i prowadzimy od niego pomocniczy promień r do środka elipsy. Promień ten tworzy kąt α ze średnicą poziomą:
Parametry a i b są odpowiednio równe połowom średnic poziomej i pionowej. Punkt S jest środkiem elipsy. Do wyznaczenia współrzędnych punktu P wykorzystujemy równanie parametryczne elipsy:
Jeśli chcemy wyznaczyć ciąg n równoodległych punktów na obwodzie elipsy, to musimy w tym wzorze zmieniać kąt α z równym krokiem w zakresie od 0 do 2π. Obliczone współrzędne zapamiętujemy w odpowiednich tablicach:
Gdy wyznaczymy wszystkie punkty, łączymy je odcinkami. Wybieramy punkt pierwszy i łączymy go ze wszystkimi następnymi:
Przechodzimy do punktu drugiego i wykonujemy tę samą operację. Zwróć jednak uwagę, że nie musisz łączyć go z punktem pierwszym, ponieważ połączenie to już istnieje. Zatem wystarczy punkt drugi połączyć z trzecim, czwartym, itd. Zawsze punkt i-ty łączymy z punktami o numerach większych od i:
Gdy połączymy wszystkie punkty, otrzymamy następującą figurę:
Program jest następujący:
C++// Linie 4 //--------- #include <SDL.h> #include <iostream> #include <cmath> using namespace std; // Rozmiar okienka const int W_W = 640; const int W_H = 480; // Liczba punktów const int N = 24; int main(int argc, char* args[]) { // Inicjujemy SDL if(SDL_Init(SDL_INIT_VIDEO)) { cout << "SDL_Init Error: " << SDL_GetError() << endl; return 1; } // Tworzymy okno SDL_Window * w = SDL_CreateWindow("Linie", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0); if(!w) { cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl; SDL_Quit(); return 2; } // Tworzymy kontekst graficzny SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC); if(!r) { cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl; SDL_DestroyWindow(w); SDL_Quit(); return 3; } // Tablice współrzędnych int x[N],y[N],i,j; // Współrzędne środka okna int sx = W_W / 2 - 1; int sy = W_H / 2 - 1; // Promienie elipsy int a = sx + 1; int b = sy + 1; // Obliczamy współrzędne punktów i zapamiętujemy je w tablicach for(i = 0; i < N; i++) { x[i] = sx + a * cos(i * 2 * M_PI / N); y[i] = sy + b * sin(i * 2 * M_PI / N); } // Rysujemy odcinki łączące punkty SDL_SetRenderDrawColor(r,255,255,255,255); // Kolor biały for(i = 0; i < N - 1; i++) for(j = i+1; j < N; j++) SDL_RenderDrawLine(r,x[i],y[i],x[j],y[j]); // Uaktualniamy obraz SDL_RenderPresent(r); // Czekamy na zdarzenie SDL_QUIT SDL_Event e; while(1) { SDL_WaitEvent(&e); if(e.type == SDL_QUIT) { cout << "Program closed after " << e.quit.timestamp << " miliseconds" << endl << endl; break; } } // Usuwamy kontekst graficzny SDL_DestroyRenderer(r); // Usuwamy okno SDL_DestroyWindow(w); // Kończymy pracę z SDL2 SDL_Quit(); return 0; } |
Teraz pobawimy się liniami kolorowymi.
Pierwszy program rysuje odcinki w przypadkowych kolorach. Współrzędne początków i końców odcinków również są losowane, tak aby wypadały w obszarze okna. Program działa do momentu aż zostanie zamknięty.
C++// Linie 5 //--------- #include <SDL.h> #include <iostream> #include <cstdlib> #include <ctime> using namespace std; // Rozmiar okienka const int W_W = 640; const int W_H = 480; int main(int argc, char* args[]) { // Inicjujemy SDL if(SDL_Init(SDL_INIT_VIDEO)) { cout << "SDL_Init Error: " << SDL_GetError() << endl; return 1; } // Tworzymy okno SDL_Window * w = SDL_CreateWindow("Linie", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0); if(!w) { cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl; SDL_Quit(); return 2; } // Tworzymy kontekst graficzny SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC); if(!r) { cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl; SDL_DestroyWindow(w); SDL_Quit(); return 3; } // Inicjujemy generator pseudolosowy srand(time(NULL)); // Animacja SDL_Event e; int c = 0; // Licznik linii while(1) { // Losujemy kolor linii SDL_SetRenderDrawColor(r,rand(),rand(),rand(),255); // Rysujemy linię SDL_RenderDrawLine(r,rand()%W_W,rand()%W_H,rand()%W_W,rand()%W_H); // Uaktualniamy okno po narysowaniu 100 linii if(++c == 100) { c = 0; SDL_RenderPresent(r); } // Sprawdzamy, czy użytkownik nie zamyka programu if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break; } // Usuwamy kontekst graficzny SDL_DestroyRenderer(r); // Usuwamy okno SDL_DestroyWindow(w); // Kończymy pracę z SDL2 SDL_Quit(); return 0; } |
Ciekawszy będzie następny program. Rysuje on animowane odcinki, które stopniowo wypełniają okno. Wykorzystujemy tutaj ruch końców odcinka w ten sam sposób, co przy odbijanych punktach z poprzedniego rozdziału. Kolor odcinka zmienia się płynnie w trakcie rysowania.
C++// Linie 6 //--------- #include <SDL.h> #include <iostream> #include <cstdlib> #include <ctime> using namespace std; // Rozmiar okienka const int W_W = 640; const int W_H = 480; int main(int argc, char* args[]) { // Inicjujemy SDL if(SDL_Init(SDL_INIT_VIDEO)) { cout << "SDL_Init Error: " << SDL_GetError() << endl; return 1; } // Tworzymy okno SDL_Window * w = SDL_CreateWindow("Linie", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0); if(!w) { cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl; SDL_Quit(); return 2; } // Tworzymy kontekst graficzny SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC); if(!r) { cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl; SDL_DestroyWindow(w); SDL_Quit(); return 3; } // Inicjujemy generator pseudolosowy srand(time(NULL)); // Inicjujemy zmienne int x1 = rand() % W_W; // Punkt początkowy linii int y1 = rand() % W_H; int x2 = rand() % W_W; // Punkt końcowy linii int y2 = rand() % W_H; int dx1,dy1,dx2,dy2; // Przyrosty współrzędnych do dx1 = -3 + rand() %7; while(!dx1); do dy1 = -3 + rand() %7; while(!dy1); do dx2 = -3 + rand() %7; while(!dx2); do dy2 = -3 + rand() %7; while(!dy2); int cr = rand() % 256; // Składowa czerwona koloru linii int cg = rand() % 256; // Składowa zielona koloru linii int cb = rand() % 256; // Składowa niebieska koloru linii int dr,dg,db; // Przyrosty składowych koloru do dr = -3 + rand() %7; while(!dr); do dg = -3 + rand() %7; while(!dg); do db = -3 + rand() %7; while(!db); // Animacja SDL_Event e; while(1) { // Ustawiamy kolor linii SDL_SetRenderDrawColor(r,cr,cg,cb,255); // Modyfikujemy składowe koloru if((cr+dr > 255) || (cr+dr < 0)) dr = -dr; if((cg+dg > 255) || (cg+dg < 0)) dg = -dg; if((cb+db > 255) || (cb+db < 0)) db = -db; cr += dr; cg += dg; cb += db; // Rysujemy linię SDL_RenderDrawLine(r,x1,y1,x2,y2); // Modyfikujemy współrzędne linii if((x1+dx1 >= W_W) || (x1+dx1 < 0)) dx1 = -dx1; if((y1+dy1 >= W_H) || (y1+dy1 < 0)) dy1 = -dy1; if((x2+dx2 >= W_W) || (x2+dx2 < 0)) dx2 = -dx2; if((y2+dy2 >= W_H) || (y2+dy2 < 0)) dy2 = -dy2; x1 += dx1; y1 += dy1; x2 += dx2; y2 += dy2; // Uaktualniamy okno SDL_RenderPresent(r); // Sprawdzamy, czy użytkownik nie zamyka programu if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break; } // Usuwamy kontekst graficzny SDL_DestroyRenderer(r); // Usuwamy okno SDL_DestroyWindow(w); // Kończymy pracę z SDL2 SDL_Quit(); return 0; } |
Szybkość działania tego programu ogranicza uaktualnianie treści okna przez funkcję SDL_RenderPresent(). Jeśli chcesz, aby odcinki były rysowane szybciej, zastosuj licznik wywołań jak w poprzednim programie. Same odcinki rysowane są sprzętowo bardzo szybko (kilkadziesiąt tysięcy na sekundę).
Odcinki mogą być wykorzystane do wypełniania obszarów gradientem, czyli wypełnieniem, które płynnie zmienia barwę z jednego koloru w drugi. Należy tu rozwiązać problem zmiany składowych koloru.
Mamy dwa punkty A i B leżące na linii poziomej (te same rozważania można przeprowadzić dla linii pionowej):
W punkcie A składowa czerwona r ma wartość rA, a w punkcie B ma ona wartość rB (to samo dotyczy dwóch pozostałych składowych koloru):
Chcemy wyznaczyć wartość tej składowej w poszczególnych pikselach odcinka AB. Najpierw wyznaczamy odległość w pikselach od punktu A do punktu B:
Punkt A ma współrzędną xA. Punkt B ma współrzędną xB (dla wariantu pionowego będą to współrzędne yA i yB). Zatem:
Odległość ta określa liczbę pikseli zawartą pomiędzy punktami A i B (z nimi włącznie), a więc również liczbę kroków zmiany składowej r. Będziemy przechodzić zmienną i przez poszczególne piksele od xA do xB. Zapiszmy:
Podobnie będzie się zmieniała składowa czerwona, lecz przyrosty mogą nie być równe 1 jak w przypadku współrzędnej x. Musimy zastosować wzór:
Jak ten wzór rozumieć? Gdy jesteśmy w punkcie xA,
to i = 0. W takim przypadku składowa r przyjmuje wartość rA.
Gdy jesteśmy w punkcie xB, to i ma wartość
Mamy wzór przeliczeniowy, możemy napisać program, który będzie go wykorzystywał do rysowania prostokątów wypełnionych poziomym gradientem.
C++// Linie 7 //--------- #include <SDL.h> #include <iostream> #include <cstdlib> #include <ctime> using namespace std; // Rozmiar okienka const int W_W = 640; const int W_H = 480; int main(int argc, char* args[]) { // Inicjujemy SDL if(SDL_Init(SDL_INIT_VIDEO)) { cout << "SDL_Init Error: " << SDL_GetError() << endl; return 1; } // Tworzymy okno SDL_Window * w = SDL_CreateWindow("Linie", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0); if(!w) { cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl; SDL_Quit(); return 2; } // Tworzymy kontekst graficzny SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC); if(!r) { cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl; SDL_DestroyWindow(w); SDL_Quit(); return 3; } // Inicjujemy generator pseudolosowy srand(time(NULL)); // Zmienne int xA,yA,xB,yB,rA,rB,gA,gB,bA,bB,i,x,cr,cg,cb; // Animacja SDL_Event e; while(1) { // Losujemy lewy górny narożnik prostokąta xA = rand() % W_W; yA = rand() % W_H; // Losujemy prawy dolny narożnik prostokąta xB = rand() % W_W; yB = rand() % W_H; // Korygujemy współrzędne: if(xA > xB) swap(xA,xB); if(yA > yB) swap(yA,yB); if(xA == xB) xB++; // Losujemy kolory narożników rA = rand() % 256; gA = rand() % 256; bA = rand() % 256; rB = rand() % 256; gB = rand() % 256; bB = rand() % 256; // Rysujemy prostokąt liniami pionowymi for(i = xB - xA; i >= 0; i--) { x = xA + i; cr = rA + i * (rB - rA) / (xB - xA); cg = gA + i * (gB - gA) / (xB - xA); cb = bA + i * (bB - bA) / (xB - xA); SDL_SetRenderDrawColor(r,cr,cg,cb,255); SDL_RenderDrawLine(r,x,yA,x,yB); } // Uaktualniamy okno SDL_RenderPresent(r); // Krótkie opóźnienie SDL_Delay(50); // Sprawdzamy, czy użytkownik nie zamyka programu if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break; } // Usuwamy kontekst graficzny SDL_DestroyRenderer(r); // Usuwamy okno SDL_DestroyWindow(w); // Kończymy pracę z SDL2 SDL_Quit(); return 0; } |
Jako ćwiczenie zmodyfikuj program, tak aby :
Jeśli koniec ostatniego odcinka łączy się z początkiem pierwszego, otrzymujemy łamaną zamkniętą (ang. closed polyline):
Łamaną definiujemy za pomocą ciągu kolejnych punktów, wierzchołków łamanej, które są początkami i końcami jej odcinków. Łamaną można rysować za pomocą serii wywołań funkcji SDL_RenderDrawLine() lub, bardziej efektywnie, za pomocą pojedynczej funkcji SDL_RenderDrawLines(). Funkcja ta posiada następujące parametry:
SDL_RenderDrawLines(r,p,n)
r – wskaźnik struktury SDL_Renderer, która definiuje kontekst
graficzny,
p – tablica struktur typu SDL_Point, która definiuje
współrzędne kolejnych wierzchołków łamanej,
n – liczba wierzchołków w tablicy p, które zostaną
połączone odcinkami.
Poniższy program rysuje łamaną zbudowaną z N wierzchołków, które są animowane w oknie. Do animacji wykorzystujemy odbijanie punktów od krawędzi okna, co opisaliśmy dokładnie w poprzednich programach.
C++// Łamane 1 //--------- #include <SDL.h> #include <iostream> #include <cstdlib> #include <ctime> using namespace std; // Rozmiar okienka const int W_W = 640; const int W_H = 480; // Liczba punktów łamanej const int N = 40; int main(int argc, char* args[]) { // Inicjujemy SDL if(SDL_Init(SDL_INIT_VIDEO)) { cout << "SDL_Init Error: " << SDL_GetError() << endl; return 1; } // Tworzymy okno SDL_Window * w = SDL_CreateWindow("Linie", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0); if(!w) { cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl; SDL_Quit(); return 2; } // Tworzymy kontekst graficzny SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC); if(!r) { cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl; SDL_DestroyWindow(w); SDL_Quit(); return 3; } // Inicjujemy generator pseudolosowy srand(time(NULL)); // Tworzymy zmienne i inicjujemy je SDL_Point p[N+1]; int dx[N],dy[N],i; // Losujemy współrzędne punktów i przyrosty for(i = 0; i < N; i++) { p[i].x = rand() % W_W; p[i].y = rand() % W_H; do dx[i] = -3 + rand() % 7; while(!dx[i]); do dy[i] = -3 + rand() % 7; while(!dy[i]); } // Ostatni punkt jest pierwszym, aby powstała łamana zamknięta p[N] = p[0]; // Animacja SDL_Event e; while(1) { // Usuwamy treść poprzedniego okna SDL_SetRenderDrawColor(r,0,0,0,255); SDL_RenderClear(r); SDL_SetRenderDrawColor(r,255,255,255,255); // Kolor biały // Rysujemy łamaną SDL_RenderDrawLines(r,p,N+1); // Modyfikujemy współrzędne wierzchołków for(i = 0; i < N; i++) { if((p[i].x + dx[i] >= W_W) || (p[i].x + dx[i] < 0)) dx[i] = -dx[i]; if((p[i].y + dy[i] >= W_H) || (p[i].y + dy[i] < 0)) dy[i] = -dy[i]; p[i].x += dx[i]; p[i].y += dy[i]; } p[N] = p[0]; // Zamknięcie łamanej // Uaktualniamy okno SDL_RenderPresent(r); // Sprawdzamy, czy użytkownik nie zamyka programu if(SDL_PollEvent(&e) && e.type == SDL_QUIT) break; } // Usuwamy kontekst graficzny SDL_DestroyRenderer(r); // Usuwamy okno SDL_DestroyWindow(w); // Kończymy pracę z SDL2 SDL_Quit(); return 0; } |
Jeśli linie poruszają się zbyt szybko, dodaj opóźnienie za pomocą funkcji SDL_Delay().
Ciekawym zastosowaniem łamanych jest tworzenie wykresów funkcji matematycznych. Napiszemy program tworzący wykres dowolnej funkcji ciągłej w zadanym przedziale <xp,xk>. Wykres będzie uproszczony, niemniej użyteczny.
Oto procedura tworzenia wykresu:
Mamy daną funkcję y = f(x):
Chcemy utworzyć wykres tej funkcji w przedziale argumentów <xp,xk>:
Dzielimy przedział na N równoodległych punktów:
Przez dx oznaczamy tutaj odległość między dwoma sąsiednimi punktami. Jak policzyć wartość dx? Wystarczy zauważyć, że n punktów dzieli przedział na n-1 segmentów o długości dx:
n | dx |
2 | ![]()
|
3 | ![]()
|
4 | ![]()
|
... | |
N | ![]() |
Gdy mamy obliczone dx, możemy wyznaczyć każdy z punktów podziałowych:
Dla każdego z tych punktów obliczamy i zapamiętujemy wartość funkcji:
Otrzymujemy w ten sposób pary współrzędnych punktów wykresu:
Aby narysować wykres punkty te należy połączyć liniami, czyli utworzyć z nich łamaną. Ale najpierw musimy przeliczyć współrzędne wykresu na współrzędne okna graficznego. Zawrzyjmy cały wykres w prostokącie:
Lewy górny narożnik prostokąta ma współrzędne (xp, max y), gdzie przez max y rozumiemy największą wartość współrzędnej y punktów wykresy. Podobnie prawy dolny narożnik prostokąta ma współrzędne (xk,min y), gdzie min y oznacza najmniejszą wartość współrzędnych y punktów wykresu. Wartości max y i min y możemy obliczać w trakcie wyznaczania kolejnych yi.
Prostokąt odwzorujemy w obszar graficzny okna:
Co to znaczy odwzorować? Znaczy to: znaleźć wzór przeliczeniowy, który punkt P o współrzędnych (x,y) w obszarze prostokąta przetwarza w punkt P' o współrzędnych (xe,ye) w obszarze graficznym okna, przy czym narożniki prostokąta wykresu mają przechodzić w narożniki okna graficznego. Tworzymy proporcje:
Z proporcji wyliczamy:
Wzory te umożliwią nam przeliczenie współrzędnych punktów wykresu na współrzędne punktów wierzchołkowych łamanej w obszarze graficznym okna.
Pozostaje jeszcze problem narysowania osi OX i OY. Oś OX narysujemy odcinkiem poziomym. Obliczamy współrzędną pionową tego odcinka wg wzoru:
Jeśli współrzędna ta wpada w obszar okna, to rysujemy odcinek poziomy od punktu (0,yOX) do punktu (W_W-1,yOX).
Podobnie postępujemy z osią OY. Wyliczamy jej współrzędną poziomą:
Jeśli wpada w obszar okna, to rysujemy odcinek pionowy od punktu (xOY,0) do punktu (xOY,W_H-1).
Mamy wszystko. Program jest następujący:
C++// Łamane 2 //--------- #include <SDL.h> #include <iostream> #include <cmath> using namespace std; // Rozmiar okienka const int W_W = 640; const int W_H = 480; // Liczba punktów wykresu const int N = 500; // Tutaj wpisujesz wzór funkcji, której wykres program ma wykonać //--------------------------------------------------------------- double f(double x) { return x*sin(x*x)*cos(x*x*x); } // Tutaj określasz przedział dla wykresu //-------------------------------------- double xp = -1; // Początek double xk = 2; // Koniec int main(int argc, char* args[]) { // Inicjujemy SDL if(SDL_Init(SDL_INIT_VIDEO)) { cout << "SDL_Init Error: " << SDL_GetError() << endl; return 1; } // Tworzymy okno SDL_Window * w = SDL_CreateWindow("Linie", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W_W, W_H, 0); if(!w) { cout << "SDL_CreateWindow Error: " << SDL_GetError() << endl; SDL_Quit(); return 2; } // Tworzymy kontekst graficzny SDL_Renderer * r = SDL_CreateRenderer(w,0,SDL_RENDERER_PRESENTVSYNC); if(!r) { cout << "SDL_CreateRenderer Error: " << SDL_GetError() << endl; SDL_DestroyWindow(w); SDL_Quit(); return 3; } // Tworzymy zmienne double x[N],y[N],dx,maxy,miny; SDL_Point p[N]; int i,xOY,yOX; // Obliczamy odległość między punktami podziałowymi dx = (xk - xp) / (N - 1); // Obliczamy kolejne punkty podziałowe, wartości funkcji w tych punktach oraz maxy i miny for(i = 0; i < N; i++) { x[i] = xp + i * dx; y[i] = f(x[i]); if(!i) maxy = miny = y[0]; if(y[i] > maxy) maxy = y[i]; if(y[i] < miny) miny = y[i]; } // Przeliczamy współrzędne wykresu na współrzędne okna graficznego for(i = 0; i < N; i++) { p[i].x = (W_W - 1) * (x[i] - xp) / (xk - xp); p[i].y = (W_H - 1) * (maxy - y[i]) / (maxy - miny); } // Rysujemy osie wykresu SDL_SetRenderDrawColor(r,0,128,255,255); // Kolor niebieski xOY = - (W_W - 1) * xp / (xk - xp); if((xOY >= 0) && (xOY < W_W)) SDL_RenderDrawLine(r,xOY,0,xOY,W_H-1); yOX = (W_H - 1) * maxy / (maxy - miny); if((yOX >= 0) && (yOX < W_H)) SDL_RenderDrawLine(r,0,yOX,W_W-1,yOX); // Rysujemy linię wykresu SDL_SetRenderDrawColor(r,255,255,255,255); // Kolor biały SDL_RenderDrawLines(r,p,N); // Uaktualniamy treść okna SDL_RenderPresent(r); // Czekamy na zdarzenie SDL_QUIT SDL_Event e; while(1) { SDL_WaitEvent(&e); if(e.type == SDL_QUIT) { cout << "Program closed after " << e.quit.timestamp << " miliseconds" << endl << endl; break; } } // Usuwamy kontekst graficzny SDL_DestroyRenderer(r); // Usuwamy okno SDL_DestroyWindow(w); // Kończymy pracę z SDL2 SDL_Quit(); return 0; } |
r – wskaźnik struktury SDL_Renderer
cr – składowa czerwona
cg – składowa zielona
cb – składowa niebieska
ca – przezroczystość koloru
SDL_RenderDrawLine(r,x1,y1,x2,y2) – rysuje odcinek
r – wskaźnik struktury SDL_Renderer
x1,y1 – współrzędne punktu początkowego odcinka
x2,y2 – współrzędne punktu końcowego odcinka
SDL_RenderDrawLines(r,p,n) – rysuje łamaną
r – wskaźnik struktury SDL_Renderer
p – tablica struktur typu SDL_Point, która definiuje
współrzędne kolejnych wierzchołków łamanej
n – liczba wierzchołków w tablicy p.
![]() |
Zespół Przedmiotowy Chemii-Fizyki-Informatyki w I Liceum Ogólnokształcącym im. Kazimierza Brodzińskiego w Tarnowie ul. Piłsudskiego 4 ©2025 mgr Jerzy Wałaszek |
Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone pod warunkiem podania źródła oraz niepobierania za to pieniędzy.
Pytania proszę przesyłać na adres email:
Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.
Informacje dodatkowe.