![]() |
![]() ![]()
Autor artykułu: mgr Jerzy Wałaszek, wersja1.0 |
©2011 mgr
Jerzy Wałaszek
|
Obiekt na płaszczyźnie składa się z ciągu wierzchołków połączonych liniami - tworzy tzw. łamaną. Wierzchołki są obiektami typu TPoint. Cała łamana jest tablicą typu TPoint, której elementy są poszczególnymi wierzchołkami. Na początek zdefiniujmy odpowiednią figurę:
Figura składa się z następujących wierzchołków (wierzchołek v7 jest równy wierzchołkowi v0, aby otrzymać łamaną zamkniętą):
v0 | (50,0) |
v1 | (50,50) |
v2 | (0,50) |
v3 | (0,0) |
v4 | (-50,0) |
v5 | (-50,-50) |
v6 | (0,-50) |
v7 | (50,0) |
Przy przekształceniach punkty będziemy reprezentować wektorem:
[x,y,1]
Jest to macierz jednowierszowa o trzech kolumnach.
W ten sposób wierzchołek P(x,y) przechodzi w wierzchołek P'(x',y'), gdzie:
Gdyby nam chodziło jedynie o translację, to właściwie sprawę przekształcenia mamy już załatwioną. Jednakże będziemy chcieli składać ze sobą kilka przekształceń, a tutaj bardzo wygodny staje się rachunek macierzowy. Najpierw tworzymy tzw. macierz translacji o postaci:
1 | 0 | 0 | |||
T3 x 3 = | 0 | 1 | 0 | ||
Tx | Ty | 1 |
Następnie z każdego wierzchołka figury tworzymy wektor [x,y,1] i mnożymy go przez macierz przekształcenia:
1 | 0 | 0 | |||
[x,y,1] x | 0 | 1 | 0 | ||
Tx | Ty | 1 |
Wynikiem tego mnożenia jest wektor [x',y',1], który oznacza wierzchołek przesunięty o Tx i Ty wzdłuż odpowiednich osi. Otrzymane wyniki zapamiętujemy w tablicy wierzchołków docelowych, która posłuży do narysowania figury w oknie. Zasada tworzenia animacji jest następująca:
Utwórz projekt w C++ Builder. Na oknie umieść Timer.
Form1
BorderStyle =
bsSingle
Użytkownik nie będzie mógł zmieniać rozmiarów okna przez przeciąganie
myszką jego krawędzi.
Caption = Przekształcenia 2D
Tytuł okna.
ClientHeight = 512
Wysokość obszaru graficznego okna. Taką wysokość będzie miała nasza
powierzchnia rysunkowa Canvas.
ClientWidth = 512
Szerokość obszaru graficznego okna.
Color = clBlack
Kolor okna.
Name = frm2D
Pod tą nazwą będzie dostępna w programie zmienna klasy naszego okna.
Position = poScreenCenter
Okienko aplikacji będzie się pojawiało na środku ekranu.
Timer1
Name = tmrAnimate
Teraz tworzymy zmienne globalne. Definicje umieszczamy na początku programu przed wszystkimi funkcjami.
//Zmienne globalne // Definicja obiektu TPoint V[] = {Point(50,0), Point(50,50), Point(0,50), Point(0,0), Point(-50,0), Point(-50,-50), Point(0,-50), Point(50,0)}; // Obiekt do rysowania na ekranie TPoint DV[8]; // Macierz przekształcenia double G[3][3]; // Parametry ruchu int Tx = 0; int Ty = 0; int dx = 2; int dy = 3;
Teraz kilka funkcji pomocniczych:
void Identity() // Funkcja ustawia macierz jednostkową w macierzy przekształcenia. // Służy ona do wyzerowania poprzednich przekształceń i stworzenia // przekształcenia tożsamosciowego. { G[0][0] = G[1][1] = G[2][2] = 1; G[0][1] = G[0][2] = G[1][0] = G[1][2] = G[2][0] = G[2][1] = 0; } void Multiply(double X[][3]) // Mnoży macierz G przez macierz X. Słuzy do składania przekształceń { double C[3][3]; // macierz pomocnicza int i,j; for(i = 0; i < 3; i++) for(j = 0; j < 3; j++) C[i][j] = G[i][0] * X[0][j] + G[i][1] * X[1][j] + G[i][2] * X[2][j]; for(i = 0; i < 3; i++) for(j = 0; j < 3; j++) G[i][j] = C[i][j]; } void Translate(double Tx, double Ty) // Tworzy macierz translacji T, a następnie mnoży G przez T. // W ten sposób translacja składa się z przekształceniem w G. { double T[3][3]; T[0][0] = T[1][1] = T[2][2] = 1; T[0][1] = T[0][2] = T[1][0] = T[1][2] = 0; T[2][0] = Tx; T[2][1] = Ty; Multiply(T); } void SetDV() // Tworzy DV na podstawie V i macierzy przekształcenia G { int x,y,i; for(i = 0; i < 8; i++) { x = V[i].x; y = V[i].y; DV[i].x = x * G[0][0] + y * G[1][0] + G[2][0]; DV[i].y = x * G[0][1] + y * G[1][1] + G[2][1]; } }
Utwórz funkcję obsługi zdarzenia onTimer dla tmrAnimate i wpisz kod:
void __fastcall Tfrm2D::tmrAnimateTimer(TObject *Sender) { int t; Identity(); // zerujemy przekształcenie Translate(Tx,Ty); // translacja SetDV(); // ustawiamy obiekt do wyświetlenia t = Tx + dx; // modyfikujemy parametry if(t < 0 || t > ClientWidth) dx = -dx; Tx += dx; t = Ty + dy; if(t < 0 || t > ClientHeight) dy = -dy; Ty += dy; Invalidate(); // okno wymaga przerysowania }
Utwórz funkcję obsługi zdarzenia onCreate dla frm2D i wpisz kod:
void __fastcall Tfrm2D::FormCreate(TObject *Sender) { DoubleBuffered = true; tmrAnimateTimer(Sender); }
Utwórz funkcję obsługi zdarzenia onPaint dla frm2D i wpisz kod:
void __fastcall Tfrm2D::FormPaint(TObject *Sender) { Canvas->Pen->Color = clYellow; Canvas->Brush->Color = clRed; Canvas->Polygon(DV,7); }
Aplikację możesz skompilować i uruchomić. Będzie wykonywała prostą animację
Zadanie to realizujemy prosto mnożąc wektory [x,y,1] przez macierz rotacji:
cosφ | sinφ | 0 | |||
R3 x 3 = | -sinφ | cosφ | 0 | ||
0 | 0 | 1 |
Jeśli chcemy złożyć ze sobą kilka przekształceń, to macierz całkowitego przekształcenia jest iloczynem macierzy przekształceń. Na przykład, mnożąc macierz rotacji przez macierz translacji uzyskamy przekształcenie, które obraca obiekt o zadany kat, a następnie przemieszcza go w wybrane miejsce płaszczyzny graficznej.
Na początku programu wpisz
#include <math.h>
Do zmiennych globalnych dodaj
double fi = 0; double dfi = 0.05;
Dodaj funkcję tworzenia rotacji:
void Rotate(double fi) // Tworzy macierz rotacji R, a następnie mnoży G przez R. // W ten sposób rotacja składa się z przekształceniem w G. { double R[3][3]; R[0][0] = R[1][1] = cos(fi); R[0][1] = sin(fi); R[1][0] = -R[0][1]; R[2][2] = 1; R[0][2] = R[1][2] = R[2][0] = R[2][1] = 0; Multiply(R); }
Zmień funkcję obsługi zdarzenia onTimer:
void __fastcall Tfrm2D::tmrAnimateTimer(TObject *Sender) { int t; Identity(); // zerujemy przekształcenie Rotate(fi); // rotacja Translate(Tx,Ty); // translacja SetDV(); // ustawiamy obiekt do wyświetlenia t = Tx + dx; // modyfikujemy parametry if(t < 0 || t > ClientWidth) dx = -dx; Tx += dx; t = Ty + dy; if(t < 0 || t > ClientHeight) dy = -dy; Ty += dy; fi += dfi; if(fi > 6.28) fi = 0; Invalidate(); // okno wymaga przerysowania }
Skompiluj i uruchom aplikację. Oprócz ruchu po okienku nasz obiekt wykonuje dodatkowo obroty. Sprawdź, co się stanie, gdy zamienisz miejscami wywołanie Rotate() z Translate(). Czy potrafisz to wytłumaczyć? Jak widzisz, kolejność składania przekształceń ma znaczenie.
Jeśli Sx = Sy, to obiekt rośnie równomiernie. Inaczej zostaje ścieśniony lub rozciągnięty wzdłuż określonej osi. Do skalowania służy macierz:
Sx | 0 | 0 | |||
S3 x 3 = | 0 | Sy | 0 | ||
0 | 0 | 1 |
Skalowanie można łączyć z pozostałymi przekształceniami przez proste mnożenie macierzy.
Do zmiennych globalnych dodaj
double Sx = 1; double dSx = 0.02; double Sy = 1; double dSy = 0.03;
Dodaj funkcję tworzenia skalowania:
void Scale(double sx, double sy) // Tworzy macierz skalowania S, a następnie mnoży G przez S. // W ten sposób skalowanie składa się z przekształceniem w G { double S[3][3]; S[0][0] = sx; S[1][1] = sy; S[2][2] = 1; S[0][1] = S[0][2] = S[1][0] = S[1][2] = S[2][0] = S[2][1] = 0; Multiply(S); }
Zmień funkcję obsługi zdarzenia onTimer:
void __fastcall Tfrm2D::tmrAnimateTimer(TObject *Sender) { int t; Identity(); // zerujemy przekształcenie Scale(Sx,Sy); // skalowanie Rotate(fi); // rotacja Translate(Tx,Ty); // translacja SetDV(); // ustawiamy obiekt do wyświetlenia t = Tx + dx; // modyfikujemy parametry if(t < 0 || t > ClientWidth) dx = -dx; Tx += dx; t = Ty + dy; if(t < 0 || t > ClientHeight) dy = -dy; y += dy; fi += dfi; if(fi > 6.28) fi = 0; if(Sx < 0.1 || Sx > 5) dSx = -dSx; if(Sy < 0.1 || Sy > 5) dSy = -dSy; Sx += dSx; Sy += dSy; Invalidate(); // okno wymaga przerysowania }
Skompiluj i uruchom aplikację. Pozmieniaj kolejność przekształceń i sprawdź wynik.
Ś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 • x
Macierz 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.
Zaprojektuj odpowiednią funkcję dla ścinania i dołącz ją do programu. Współczynniki kx i ky mogą się zmieniać wg podobnych zasad jak pozostałe.
![]() | 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