Prezentowane materiały są przeznaczone dla uczniów szkół ponadgimnazjalnych. Autor artykułu: mgr Jerzy Wałaszek, wersja1.0 |
©2008 mgr
Jerzy Wałaszek |
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 |
Na tej lekcji poznasz praktyczne sposoby pracy z funkcjami bibliotecznymi line(), moveto() oraz lineto().
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 |
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 |
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
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.
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ą:
I Liceum Ogólnokształcące |
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