Wymagane jest zapoznanie się z następującymi podrozdziałami:
P019 - Pierwszy program dla Windows
OL031 - instalacja biblioteki SDL w środowisku Dev-C++
OL032 - inicjalizacja biblioteki SDL
OL033 - powierzchnie graficzne w SDL
OL034 - przygotowanie plików własnej biblioteki graficznej
OL035 - rysowanie po powierzchni graficznej
OL036 - algorytm Bresenhama rysowania odcinka
OL037 - obcinanie grafiki do prostokątaOL038 - podstawowe algorytmy wypełniania obszarów
OL039 - algorytm Smitha
OL040 - praca w środowisku sterowanym zdarzeniami
OL041 - czcionka bitmapowa
OL042 - czcionka wektorowa
OL043 - przyciski poleceń
OL050 - macierze - podstawowe operacje na macierzach
UWAGA!!!
Bieżące opracowanie NIE JEST KURSEM programowania biblioteki SDL. Są to materiały przeznaczone do zajęć na kole informatycznym w I LO w Tarnowie.
Przed pracą z tym rozdziałem utwórz w środowisku Dev-C++ nowy projekt SDL i dołącz do niego pliki SDL_gfx.cpp oraz SDL_gfx.h. Jeśli nie przeczytałeś zalecanych rozdziałów, koniecznie zrób to, a wszystko stanie się jasne. W przeciwnym razie odpuść sobie również ten rozdział. Opis funkcji bibliotecznych znajdziesz w podsumowaniu SDL009.
Artykuł nie jest już rozwijany
Na tej lekcji poznamy uniwersalne sposoby geometrycznego przekształcania (ang. geometric transformation) obiektów dwuwymiarowych (w przestrzeni trójwymiarowej będzie bardzo podobnie) na płaszczyźnie. Najpierw określmy sposób reprezentacji obiektów geometrycznych. Umówmy się, iż obiekt będzie zbudowany z wielokątów. Wielokąty składają się z wierzchołków połączonych liniami. Zastosujemy zatem sposób wykorzystywany przez czcionkę wektorową, który opisaliśmy w rozdziale OL042. Takie rozwiązanie umożliwi również stosowanie naszych przekształceń do liter wektorowych.
Obiekt będzie zbudowany z listy łamanych. Zasady tworzenia tej listy są następujące:Każda łamana rozpoczyna się od liczby określającej liczbę wierzchołków. Jeśli liczba ta ma wartość 0, to oznacza to koniec listy łamanych. Za liczbą wierzchołków następują pary współrzędnych określających położenie wierzchołka na płaszczyźnie.
Przykład:
3 12 1 14 5 1 1 2 5 3 4 7 0
Ten obiekt składa się z dwóch łamanych:
3 12 1 14 5 1 1 2 5 3 4 7 0
Pierwsza łamana posiada trzy wierzchołki: (12,1), (14,5) i (1,1).
Druga łamana posiada tylko dwa wierzchołki: (5,3) i (4,7).
Ostatnie zero oznacza koniec listy łamanych.Listę łamanych można utworzyć w tablicy o odpowiedniej wielkości. Na przykład tak:
Sint32 obj[] = {3, 12, 1, 14, 5, 1, 1, 2, 5, 3, 4, 7, 0};
Translacja (ang. translation) polega na przesunięciu współrzędnych wszystkich wierzchołków łamanej o wektor (Tx, Ty).
Oś OY jest skierowana w dół na powierzchni karty graficznej, pamiętasz? Współrzędne nowych punktów obliczamy wg wzorów:
x' = x + Tx
y' = y + TyJeśli w aplikacji stosujemy różne przekształcenia, to takie podejście nie jest wygodne. Lepszym rozwiązaniem będzie zastosowanie rachunku macierzowego, który ułatwi nam składanie przekształceń. Każdy punkt łamanej przekształcamy w macierz P1 x 3 = [x, y, 1] - macierz P możemy potraktować jako współrzędne punktu (x,y) leżącego w przestrzeni trójwymiarowej na płaszczyźnie z=1 - równoległej do osi OX i OY układu współrzędnych. Następnie konstruujemy macierz translacji:
1 | 0 | 0 | |||
T3 x 3 = | 0 | 1 | 0 | ||
Tx | Ty | 1 |
Współrzędne (x',y') punktu przesuniętego o wektor (Tx,Ty) uzyskamy mnożąc macierz P przez macierz T:
1 | 0 | 0 | |||
P' = P x T; [x',y',1] = [x,y,1] x | 0 | 1 | 0 | ||
Tx | Ty | 1 |
Sprawdźmy. Zgodnie z definicją mnożenia macierzy mamy:
x' = x • 1 + y • 0 + 1 • Tx = x + Tx
y' = x • 0 + y • 1 + 1 • Ty = y + Ty
1 = x • 0 + y • 0 + 1 • 1
Jeśli chcemy uzyskać obrót (ang. rotation) punktu P = (x,y) wokół środka układu współrzędnych o kąt φ (podany w radianach), to konstruujemy macierz punktu:
P1 x 3 = [x, y, 1].
Następnie konstruujemy macierz rotacji:
cosφ | sinφ | 0 | |||
R3 x 3 = | -sinφ | cosφ | 0 | ||
0 | 0 | 1 |
Nowe współrzędne otrzymamy mnożąc macierz punktu P przez macierz rotacji R:
cosφ | sinφ | 0 | |||
P' = P x R; [x',y',1] = [x,y,1] x | -sinφ | cosφ | 0 | ||
0 | 0 | 1 |
UWAGA!!! - PUŁAPKA.
Ponowne pomnożenie macierzy P' przez R da nam punkty obrócone o kolejny kąt φ. Mogłoby więc się wydawać, że będzie to dobry sposób do tworzenia animacji. Niestety, musimy uwzględnić błędy zaokrągleń. Wyliczone współrzędne nie będą dokładne, lecz przybliżone. Wielokrotne zastosowanie przekształcenia powoduje wzrost błędów i w efekcie nasza figura może się "numerycznie" rozsypać po powierzchni graficznej. Poprawnie robimy to tak:
W pamięci przechowujemy oryginalne współrzędne obiektu, których nigdy nie zmieniamy.
Przekształcone współrzędne zapamiętujemy w kopii obiektu, która posłuży do narysowania go na powierzchni graficznej.
Modyfikujemy odpowiednio macierze przekształcenia i za ich pomocą wyliczamy nowe współrzędne wykorzystując współrzędne oryginalnego obiektu.. Dzięki temu zawsze utrzymamy błędy zaokrągleń na niskim poziomie - nie będą się kumulowały.
Jeśli chcemy obrócić nasz obiekt o kąt φ wokół dowolnego punktu (xR,yR), to musimy zastosować trzy przekształcenia:
- Sprowadzić punkt (xR,yR) do początku układu współrzędnych - translacja obiektu o wektor (-xR,-yR)
- Obrócić wierzchołki obiektu o kąt φ - rotacja wokół środka układu współrzędnych.
- Powrócić do punktu (xR,yR) - translacja o wektor (xR,yR)
Tworzymy zatem trzy macierze, dwie translacji i jedną rotacji:
|
||||||||||||||||||
|
||||||||||||||||||
|
Obliczamy macierz przekształcenia G = T x R x T-1 (operację można rozbić: G = T x R; G = G x T-1).
|
|
|
|
Wykorzystujemy macierz G do wyznaczenia nowych współrzędnych wszystkich wierzchołków obiektu:
P' = P x G
Tutaj uwidaczniają się zalety rachunku macierzowego - jednorodność operacji. To duże ułatwienie.
Zapis T-1 oznacza macierz odwrotną. Przez analogię do zwykłego rachunku algebraicznego:
a • a-1 = 1
Iloczyn macierzy T i T-1 daje tzw. macierz jednostkową, która na głównej przekątnej posiada same jedynki, a na pozostałych pozycjach same zera. Sprawdźmy:
T-1 = | 1 | 0 | 0 | |||||||||
0 | 1 | 0 | ||||||||||
-Tx | -Ty | 1 | ||||||||||
T= | 1 | 0 | 0 | 1 | 0 | 0 | = 1 | |||||
0 | 1 | 0 | 0 | 1 | 0 | |||||||
Tx | Ty | 1 | 0 | 0 | 1 |
Macierz odwrotną będziemy zawsze interpretować jako przekształcenie odwrotne - np. jeśli macierz R oznacza obrót o kąt φ, to R-1 będzie oznaczać obrót o kąt -φ. Istnieją algorytmy obliczające macierz odwrotną do danej. Jednakże wcale nie musimy z nich korzystać - jak zobaczymy dalej, wystarczy odpowiednio zastosować funkcje tworzące macierze przekształceń.
Skalowanie (ang. scaling) względem osi układu współrzędnych polega na przemnożeniu współrzędnych x i y każdego wierzchołka przez współczynniki skali w osi
OX - sx i w osiOY - sy , które nie muszą być równe - wtedy obiekt zostanie rozciągnięty lub ściśnięty względem wybranej osi.
Macierz skalowania zbudowana jest następująco:
Sx | 0 | 0 | |||
S3 x 3 = | 0 | Sy | 0 | ||
0 | 0 | 1 |
Współrzędne wierzchołków otrzymamy mnożąc macierz punktu P przez macierz skalowania S: P' = P x S.
Jeśli chcemy skalować względem punktu PS = (xS,yS), to postępujemy podobnie jak przy rotacji - przekształcenie składamy z translacji od punktu (xS,yS) do środka układu współrzędnych, skalowania oraz translacji odwrotnej - od środka układu współrzędnych do punktu (xS,yS). W tym celu tworzymy trzy macierze przekształceń:
1 0 0 T = 0 1 0 -xS -yS 1
Sx 0 0 S = 0 Sy 0 0 0 1
1 0 0 T-1 = 0 1 0 xS yS 1 Następnie wyliczamy macierz G złożonego przekształcenia jako iloczyn wyznaczonych macierzy G = T1 x S x T2:
1 0 0 G = 0 1 0 -xS -yS 1
Sx 0 0 x 0 Sy 0 0 0 1
1 0 0 x 0 1 0 xS yS 1
Sx 0 0 = 0 Sy 0 -xS2Sx -yS2Sy 1 Współrzędne punktów otrzymujemy mnożąc ich macierze P przez macierz przekształcenia G: P' = P x G.
Skalować można również wzdłuż dwóch osi prostopadłych do siebie i tworzących kąt φ z osiami układu współrzędnych:
Znalezienie macierzy przekształcenia G proponuję jako zadanie dla czytelnika. Dla ambitnych proponuję zastanowić się nad skalowaniem względem dwóch osi prostopadłych, nachylonych pod zadanym katem względem osi układu współrzędnych, jednakże przesuniętych do punktu PS = (xS.yS).
Jednokładność (ang. homothety, homeothetic transformation) o środku PH = (xH,yH) i skali k różnej od zera jest przekształceniem płaszczyzny, które punkt (x,y) przekształca w punkt o współrzędnych:
x' = xH + k(x - xH)
y' = yH + k(y - yH)Interpretacja geometryczna jest bardzo prosta:
Prowadzimy prostą przechodzącą przez punkt PH = (xH,yH) oraz punkt P = (x,y). Punkt P' = (x',y') odkładamy na tej prostej, tak aby jego odległość do PH była k razy większa od odległości punktu P od PH - czyli:
|PHP'| = k|PHP| Jeśli k jest ujemne, to punkt P' będzie znajdował się po drugiej stronie punktu PH niż punkt P. Dla k = -1 oba punkty P i P' będą w równych odległościach od PH, lecz znajdą się po przeciwnych stronach punktu PH. Otrzymamy symetrię środkową.
Macierz jednokładności jest następująca:
k | 0 | 0 | |||
H 3 x 3 = | 0 | k | 0 | ||
(1 - k)xH | (1 - k)yH | 1 |
Punkt P' otrzymamy standardowo: P' = P x H.
Przy powinowactwie prostokątnym (ang. rectangular affinity) mamy daną prostą o równaniu ax + by + c = 0 oraz stosunek powinowactwa k. Przez punkt P = (x,y) prowadzimy prostopadłą do prostej. Prostopadła przecina prostą
ax + by + c = 0 w punkcie Q, który jest rzutem prostokątnym punktu P na oś powinowactwa. Punkt P' = (x',y') znajdujemy na prostopadłej w odległości |P'Q| równej odległości k|QP|.Jeśli k = -1, otrzymujemy symetrię osiową (ang. axial symmetry) względem osi powinowactwa.
Jeśli oś powinowactwa pokrywa się z osią OX lub OY, to otrzymujemy skalowanie względem osi OX lub OY.
Oznaczmy: f = k - 1 a2 + b2 Macierz powinowactwa prostokątnego względem prostej ax + by + c = 0 i o współczynniku k jest następująca:
1 + fa2 | fab | 0 | |||
AR 3 x 3 = | fab | 1 + fb2 | 0 | ||
fac | fbc | 1 |
Macierze współrzędnych wierzchołków przemnażamy przez macierz AR: P' = P x AR.
Ścinanie (ang. shear) jest przekształceniem, które przesuwa punkt P wzdłuż osi OX lub OY o odległość zależną od drugiej współrzędnej i współczynnika ścinania wzdłuż tej osi:
x' = x + kx • y
y' = y + ky • xMacierz przekształcenia jest następująca:
1 ky 0 SS 3 x 3 = kx 1 0 0 0 1 Jeśli jeden z współczynników kx lub ky jest równy 0, to ścinanie następuje tylko wzdłuż jednej z osi, dla której współczynnik ścinania ma wartość różną od zera. Obraz punktu otrzymujemy przemnażając macierz punktu P przez macierz ścinania SS: P' = P x SS.
Dla każdego przekształcenia utworzymy osobną funkcję biblioteki SDL_gfx, która utworzy odpowiednią macierz przekształcenia na podstawie otrzymanych parametrów. Utwórz projekt SDL, dołącz do niego pliki SDL_gfx.h, SDL_gfx.cpp, których opis znajdziesz w podsumowaniu SDL009. Następnie dołącz pliki SDL_gui.h i SDL_gui.cpp - opis w podsumowaniu GUI004. Będziemy również potrzebowali czcionki vecprop9x12.fnt, którą przekopiuj do katalogu projektu SDL.
Na końcu pliku nagłówkowego SDL_gfx.h dopisz poniższy fragment:
double * gfx2DTrans(double Tx, double Ty); double * gfx2DRot(double phi); double * gfx2DScale(double Sx, double Sy); double * gfx2DHomoth(double xh, double yh, double k); double * gfx2DAffine(double a, double b, double c, double k); double * gfx2DShear(double kx, double ky); void gfx2DDrawPoly(SDL_Surface * s, Sint32 * p, Uint32 color); void gfx2DTransform(Sint32 * p, double * tmx); Sint32 * gfx2DCopyPoly(Sint32 * p);
gfx2DTrans(Tx,Ty) - tworzy macierz translacjigfx2DRot(phi) - tworzy macierz rotacji
Tx - przesunięcie wzdłuż osi OX Ty - przesunięcie wzdłuż osi OY gfx2DScale(Sx,Sy) - tworzy macierz skalowania
phi - kąt obrotu podany w radianach gfx2DHomoth(xh,yh,k) - tworzy macierz jednokładności
Sx - skala na osi OX Sy - skala na osi OY gfx2DAffine(a,b,c,k) - tworzy macierz powinowactwa prostokątnego
xh,yh - współrzędne punktu jednokładności k - współczynnik skali
a,b,c - współczynniki osi powinowactwa o równaniu ax + by + c = 0 k - współczynnik powinowactwa gfx2DShear(kx,ky) - tworzy macierz przekształcenia ścinającego
kx - współczynniki ścinania wzdłuż osi OX ky - współczynniki ścinania wzdłuż osi OY gfx2DDrawPoly(s,p,color) - rysuje obiekt zbudowany z listy łamanych.
s - wskaźnik struktury SDL_Surface p - wskaźnik listy łamanych color - kolor rysowanych linii gfx2DTransform(p,tmx) - przekształca współrzędne wierzchołków obiektu zgodnie z macierzą transformacji. Operację tę wykonujemy zwykle na kopi obiektu.
p - wskaźnik listy łamanych tmx - wskaźnik macierzy przekształcenia gfx2DCopyPoly(p) - tworzy kopię listy łamanych i zwraca jej adres - po wykorzystaniu kopię usuwamy.
p - wskaźnik listy łamanych Na początku pliku SDL_gfx.cpp dopisz dyrektywę preprocesora zaznaczoną na czerwono (bez niej nie będzie funkcji trygonometrycznych):
// I Liceum Ogólnokształcące // w Tarnowie // Koło informatyczne 2007 // // Biblioteka procedur graficznych dla trybów 32 bitowych //------------------------------------------------------- #include <SDL/SDL_gfx.h> #include <stack> #include <fstream> #include <list> #include <cmath> using namespace std;
Następnie na końcu pliku SDL_ghx.cpp dopisz kod nowych funkcji:
// Tworzy macierz translacji // Tx, Ty - współrzędne wektora przesunięcia //------------------------------------------ double * gfx2DTrans(double Tx, double Ty) { double * T = new double[9]; T[1] = T[2] = T[3] = T[5] = 0; T[0] = T[4] = T[8] = 1; T[6] = Tx; T[7] = Ty; return T; } // Tworzy macierz rotacji // phi - kąt obrotu w radianach //----------------------------- double * gfx2DRot(double phi) { double * R = new double[9]; R[2] = R[5] = R[6] = R[7] = 0; R[0] = R[4] = cos(phi); R[1] = sin(phi); R[3] = -R[1]; R[8] = 1; return R; } // Tworzy macierz skalowania // Sx - współczynnik skali na osi OX // Sy - współczynnik skali na osi OY //---------------------------------- double * gfx2DScale(double Sx, double Sy) { double * S = new double[9]; S[1] = S[2] = S[3] = S[5] = S[6] = S[7] = 0; S[0] = Sx; S[4] = Sy; S[8] = 1; return S; } // Tworzy macierz jednokładności // xh,yh - współrzędne punktu jednokładności // k - współczynnik jednokładności //------------------------------------------ double * gfx2DHomoth(double xh, double yh, double k) { double * H = new double[9]; H[1] = H[2] = H[3] = H[5] = 0; H[0] = H[4] = k; H[6] = (1 - k) * xh; H[7] = (1 - k) * yh; H[8] = 1; return H; } // Tworzy macierz powinowactwa prostokątnego // a,b,c - współczynniki osi powinowactwa ax+by+c=0 // k - współczynnik powinowactwa //------------------------------------------------- double * gfx2DAffine(double a, double b, double c, double k) { double * SR = new double[9]; double f = (k - 1) / (a * a + b * b); SR[0] = 1 + f * a * a; SR[1] = SR[3] = f * a * b; SR[2] = SR[5] = 0; SR[4] = 1 + f * b * b; SR[6] = f * a * c; SR[7] = f * b * c; SR[8] = 1; return SR; } // Tworzy macierz ścinania // kx - współczynnik ścinania wzdłuż osi OX // ky - współczynnik ścinania wzdłuż osi OY //----------------------------------------- double * gfx2DShear(double kx, double ky) { double * S = new double[9]; S[0] = S[4] = S[8] = 1; S[1] = ky; S[2] = S[5] = S[6] = S[7] = 0; S[3] = kx; return S; } // Rysuje figurę zbudowaną z listy łamanych // s - wskaźnik struktury SDL_Surface // p - wskaźnik listy łamanych // color - kolor linii //----------------------------------------- void gfx2DDrawPoly(SDL_Surface * s, Sint32 * p, Uint32 color) { Sint32 plen, x, y; while(plen = * p++) { x = * p++; y = * p++; gfxMoveTo(x, y); while(--plen) { x = * p++; y = * p++; gfxClipLineTo(s, x, y, color); } } } // Dokonuje transformacji figury na podstawie // macierzy przekształcenia // p - wskaźnik listy łamanych // tmx - macierz transformacji //------------------------------------------- void gfx2DTransform(Sint32 * p, double * tmx) { double pxy[3], * pw; Sint32 plen; while(plen = * p++) { while(plen--) { pxy[0] = * p++; pxy[1] = * p++; pxy[2] = 1; pw = gfxMMul(1,3,3,pxy,tmx); * (p - 2) = (Sint32)pw[0]; * (p - 1) = (Sint32)pw[1]; delete [] pw; } } } // Tworzy kopię listy łamanych // p - wskaźnik pierwotnej listy //------------------------------ Sint32 * gfx2DCopyPoly(Sint32 * p) { Sint32 plen, * pp = p; while(plen = * pp++) { while(plen--) pp += 2; } plen = pp - p; Sint32 * f = pp = new Sint32[plen]; while(plen--) * pp++ = * p++; return f; }Poniższy program testuje nowe funkcje biblioteczne demonstrując jednocześnie sposób ich używania. Rodzaj transformacji wybieramy przyciskiem akcji.
// I Liceum Ogólnokształcące // w Tarnowie // Koło informatyczne // // P040 - Transformacje geometryczne //---------------------------------- #include <SDL/SDL_gfx.h> #include <SDL/SDL_gui.h> #include <time.h> const int SCRX = 640; // stałe określające szerokość i wysokość const int SCRY = 480; // ekranu w pikselach SDL_Surface * screen; gfxFont * font = gfxOpenFont("vecprop9x12.fnt"); //------------------------------------------ // Funkcje obsługujące poszczególne animacje //------------------------------------------ void (* action)(); // wskaźnik funkcji Sint32 figure[] = {11,0,20,20,0,40,0,40,20,60,20,60,40,50,50,50,60,20,60,0,40,0,20,0}; char * t[] = {"Transformacje geometryczne"," Translacja "," Rotacja "," Skalowanie ", " Jednokładność "," Powinowactwo "," Ścinanie "," Zakończ "}; // Ustala prostokąt obcinania dla animacji // i kasuje ekran - włącza blokadę //---------------------------------------- void SetClip() { SDL_Rect r; r.x = 0; r.y = font->h + 8; r.h = screen->h - font->h - 8; r.w = screen->w; SDL_SetClipRect(screen,&r); if(SDL_MUSTLOCK(screen)) SDL_LockSurface(screen); SDL_FillRect(screen, &(screen->clip_rect), 0); } // Odtwarza prostokąt obcinania //----------------------------- void RestoreClip(int i) { gfxDrawText(screen, font, t[i], (screen->w >> 1) - (gfxTextLength(font, t[i]) >> 1), screen->clip_rect.y + 8, 0x00ff00, -1); SDL_Rect r; if(SDL_MUSTLOCK(screen)) SDL_UnlockSurface(screen); SDL_UpdateRect(screen, screen->clip_rect.x, screen->clip_rect.y, screen->clip_rect.w, screen->clip_rect.h); r.x = 0; r.y = 0; r.h = screen->h; r.w = screen->w; SDL_SetClipRect(screen, &r); } // Translacja //----------- const int MAXF = 25; double Tx[MAXF],Ty[MAXF],dx[MAXF],dy[MAXF]; int si[MAXF]; void InitTransI(int i) { int xp,yp,xk,yk; si[i] = 300 + rand() % 1000; switch(rand() % 4) { case 0: xp = rand() % (screen->w + 120) - 60; xk = rand() % (screen->w + 120) - 60; yp = -60; yk = screen->h + 60; break; case 1: xp = rand() % (screen->w + 120) - 60; xk = rand() % (screen->w + 120) - 60; yk = -60; yp = screen->h + 30; break; case 2: yp = rand() % (screen->h + 120) - 60; yk = rand() % (screen->h + 120) - 60; xp = -60; xk = screen->w + 60; break; case 3: yp = rand() % (screen->h + 120) - 60; yk = rand() % (screen->h + 120) - 60; xk = -60; xp = screen->w + 60; break; } Tx[i] = xp; Ty[i] = yp; dx[i] = (xk - Tx[i]) / si[i]; dy[i] = (yk - Ty[i]) / si[i]; } void InitTrans() { for(int i = 0; i < MAXF; i++) InitTransI(i); } void Trans() { SetClip(); for(int i = 0; i < MAXF; i++) { if(!si[i]) InitTransI(i); si[i]--; Tx[i] += dx[i]; Ty[i] += dy[i]; double * T = gfx2DTrans(Tx[i], Ty[i]); Sint32 * F = gfx2DCopyPoly(figure); gfx2DTransform(F, T); gfx2DDrawPoly(screen, F, 0xffff00); delete [] T; delete [] F; } RestoreClip(1); } // Rotacja //----------- double phi[MAXF], dphi[MAXF]; void InitRot() { for(int i = 0; i < MAXF; i++) { Tx[i] = rand() % (screen->w); Ty[i] = rand() & (screen->h); phi[i] = 0; dphi[i] = 1 / (double)(200 + rand() % 200); } } void Rot() { SetClip(); for(int i = 0; i < MAXF; i++) { phi[i] += dphi[i]; if(phi[i] > 6.2831853) phi[i] -= 6.2831853; double * T1 = gfx2DTrans(Tx[i] - (screen->w >> 1), Ty[i] - (screen->h >> 1)); double * R = gfx2DRot(phi[i]); double * T2 = gfx2DTrans((screen->w >> 1),(screen->h >> 1)); double * E = gfxMMul(3, 3, 3, T1, R); double * G = gfxMMul(3, 3, 3, E, T2); Sint32 * F = gfx2DCopyPoly(figure); gfx2DTransform(F, G); gfx2DDrawPoly(screen, F, 0xffff00); delete [] T1; delete [] R; delete [] T2; delete [] E; delete [] F; delete [] G; } gfxLine(screen, screen->w >> 1, (screen->h >> 1) - 5, screen->w >> 1, (screen->h >> 1) + 5, 0xff0000); gfxLine(screen, (screen->w >> 1) - 5, screen->h >> 1, (screen->w >> 1) + 5, screen->h >> 1, 0xff0000); RestoreClip(2); } // Skalowanie //----------- double Sx[MAXF], Sy[MAXF], dSx[MAXF], dSy[MAXF]; void InitScale() { for(int i = 0; i < MAXF; i++) { Tx[i] = rand() % (screen->w); Ty[i] = rand() & (screen->h); Sx[i] = 0.5; dSx[i] = 1 / (double)(200 + rand() % 200); Sy[i] = 0.5; dSy[i] = 1 / (double)(200 + rand() % 200); } } void Scale() { SetClip(); for(int i = 0; i < MAXF; i++) { if((Sx[i] < -2) || (Sx[i] > 2)) dSx[i] = - dSx[i]; if((Sy[i] < -2) || (Sy[i] > 2)) dSy[i] = - dSy[i]; Sx[i] += dSx[i]; Sy[i] += dSy[i]; double * T1 = gfx2DTrans(-30, -30); double * S = gfx2DScale(Sx[i], Sy[i]); double * T2 = gfx2DTrans(Tx[i] + 30, Ty[i] + 30); double * E = gfxMMul(3, 3, 3, T1, S); double * G = gfxMMul(3, 3, 3, E, T2); Sint32 * F = gfx2DCopyPoly(figure); gfx2DTransform(F, G); gfx2DDrawPoly(screen, F, 0xffff00); delete [] T1; delete [] S; delete [] T2; delete [] E; delete [] F; delete [] G; } RestoreClip(3); } // Jednokładność //----------- double kH[MAXF], dkH[MAXF]; void InitHomoth() { for(int i = 0; i < MAXF; i++) { Tx[i] = rand() % (screen->w); Ty[i] = rand() & (screen->h); kH[i] = 1; dkH[i] = 1 / (double)(200 + rand() % 200); } } void Homoth() { SetClip(); for(int i = 0; i < MAXF; i++) { if((kH[i] < -2) || (kH[i] > 2)) dkH[i] = - dkH[i]; kH[i] += dkH[i]; double * T = gfx2DTrans(Tx[i], Ty[i]); double * H = gfx2DHomoth(screen->w >> 1, screen->h >> 1, kH[i]); double * G = gfxMMul(3, 3, 3, T, H); Sint32 * F = gfx2DCopyPoly(figure); gfx2DTransform(F, G); gfx2DDrawPoly(screen, F, 0xffff00); gfxLine(screen, screen->w >> 1, (screen->h >> 1) - 5, screen->w >> 1, (screen->h >> 1) + 5, 0xff0000); gfxLine(screen, (screen->w >> 1) - 5, screen->h >> 1, (screen->w >> 1) + 5, screen->h >> 1, 0xff0000); delete [] T; delete [] H; delete [] G; delete [] F; } RestoreClip(4); } // Powinowactwo //------------- void Affine() { SetClip(); for(int i = 0; i < MAXF; i++) { if((kH[i] < -2) || (kH[i] > 2)) dkH[i] = - dkH[i]; kH[i] += dkH[i]; double * T = gfx2DTrans(Tx[i], Ty[i]); double * A = gfx2DAffine(-screen->h + 1, screen->w - 1, 0, kH[i]); double * G = gfxMMul(3, 3, 3, T, A); Sint32 * F = gfx2DCopyPoly(figure); gfx2DTransform(F, G); gfx2DDrawPoly(screen, F, 0xffff00); gfxClipLine(screen, 0,0,screen->w - 1, screen->h - 1, 0xff0000); delete [] T; delete [] A; delete [] G; delete [] F; } RestoreClip(5); } // Ścinanie //--------- double kx[MAXF], dkx[MAXF], ky[MAXF], dky[MAXF]; void InitShear() { for(int i = 0; i < MAXF; i++) { Tx[i] = rand() % (screen->w); Ty[i] = rand() % (screen->h); kx[i] = 0; dkx[i] = 1 / (double)(200 + rand() % 200); ky[i] = 0; dky[i] = 1 / (double)(200 + rand() % 200); } } void Shear() { SetClip(); for(int i = 0; i < MAXF; i++) { if((kx[i] < -2) || (kx[i] > 2)) dkx[i] = - dkx[i]; if((ky[i] < -2) || (ky[i] > 2)) dky[i] = - dky[i]; kx[i] += dkx[i]; ky[i] += dky[i]; double * S = gfx2DShear(kx[i], ky[i]); double * T = gfx2DTrans(Tx[i], Ty[i]); double * G = gfxMMul(3, 3, 3, S, T); Sint32 * F = gfx2DCopyPoly(figure); gfx2DTransform(F, G); gfx2DDrawPoly(screen, F, 0xffff00); delete [] T; delete [] S; delete [] G; delete [] F; } RestoreClip(6); } // Funkcja obsługująca przyciski //------------------------------ void bfn(gfxGUIObject * sender) { switch(sender->tag) { case 1: InitTrans(); action = Trans; break; case 2: InitRot(); action = Rot; break; case 3: InitScale(); action = Scale; break; case 4: InitHomoth(); action = Homoth; break; case 5: InitHomoth(); action = Affine; break; case 6: InitShear(); action = Shear; break; case 7: exit(0); } } //*********************** // *** Program główny *** //*********************** int main(int argc, char * argv[]) { // Tablica siedmiu wskaźników do przycisków gfxButton * b[7]; if(SDL_Init(SDL_INIT_VIDEO)) exit(-1); atexit(SDL_Quit); if(!(screen = SDL_SetVideoMode(SCRX, SCRY, 32, SDL_HWSURFACE))) exit(-1); SDL_WM_SetCaption(t[0], ""); // Tworzymy przyciski akcji SDL_Rect r; r.x = r.y = r.w = 0; r.h = font->h + 8; for(int i = 1; i <= 7; i++) { b[i] = new gfxButton(i, true, screen, font, &r, t[i], bfn); r.x += gfxTextLength(font, t[i]) + 4; } // Inicjujemy generator liczb pseudolosowych srand((unsigned)time(NULL)); // Inicjujemy animacje InitTrans(); action = Trans; // Obsługujemy zdarzenia SDL_Event event; bool running = true; while(running) { SDL_Delay(1); (* action)(); // wywołujemy funkcję obsługi animacji while(SDL_PollEvent(&event)) { bool eventfree; for(int i = 1; i <= 7; i++) if(!(eventfree = b[i]->DoEvents(&event))) break; if(eventfree && (event.type == SDL_QUIT)) { running = false; break; } } } // zamykamy czcionkę gfxCloseFont(font); // Usuwamy przyciski for(int i = 1; i <= 7; i++) delete b[i]; return 0; }
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