j p e d e f
Transkrypt
j p e d e f
Przykład implementacji przekształcenia współrzednych ˛ jednorodnych Bogdan Kreczmer ZPCiR IIAiR PWr pokój 307 budynek C3 [email protected] c 2003–2008 Bogdan Kreczmer⋆ Copyright ⋆ Niniejszy dokument zawiera materiały do wykładu na temat programowania obiektowego. Jest on udostepiony ˛ pod warun- kiem wykorzystania wyłacznie ˛ do własnych prywatnych potrzeb i może on być kopiowany wyłacznie ˛ w całości, razem z niniejsza˛ strona˛ tytułowa. ˛ Definicja struktur danych (implementacja w C) typedef struct Wektor 3f { float wspol[3]; } Wektor3D; 0 1 2 x y z 1 typedef struct Macierz J3f { float elem[3][4]; } Macierz3DJ; 0 1 2 ... 0 1 2 3 R11 R21 R31 R12 R22 R32 R13 R23 R33 Tx Ty Tz 0 0 0 1 Organizacja tablicy dwuwymiarowej w pamieci: ˛ [0][0] [0][1] [0][2] [0][3] [1][0] R11 R21 R31 Tx R12 R22 −→ R32 [2][0] Ty R13 R23 −→ R33 Tz 1 Mnożenie wektora przez macierz (implementacja w C) ... Wektor 3f MacierzRazWektor( Macierz J3f M, Wektor 3f V ) { int i, j; Wektor 3f V wyn; for ( i = 0; i < 3; i++ ) { V wyn.wspol[ i ] = elem[ 3 ][ i ]; for ( j = 0; j < 3; j++ ) V wyn.wspol[ i ] += M.elem[ i ][ j ] ∗ V.wspol[ j ]; } return V wyn; } ... R11 R21 R31 R12 R22 R32 R13 R23 R33 Tx Ty Tz x y z 0 0 0 1 1 x Tx = x += R11 ∗ x x += R12 ∗ y x += R13 ∗ z 2 Definicja struktur danych (implementacja w C) ... 0 1 2 typedef struct Macierz J3f { float elem[3][4]; } Macierz J3f ; 0 1 2 3 Tx Ty Tz R11 R21 R31 R12 R22 R32 R13 R23 R33 0 0 0 1 R13 −→ R23 ... Organizacja tablicy dwuwymiarowej w pamieci: ˛ [0][0] [0][1] [0][2] [0][3] [1][0] Tx R11 R21 R31 Ty R12 −→ R22 [2][0] R32 Tz R33 3 Mnożenie wektora przez macierz (implementacja w C) ... Wektor 3f MacierzRazWektor( Macierz J3f { int i, j; Wektor 3f V wyn, float ∗wsk Melem = M.elem[0]; float ∗wsk Vwyn = V wyn.wspol, float ∗wsk Varg = V.wspol; M, Wektor 3f V ) for (i = 0; i < 3; ++i, ++wsk Vwyn) { /∗Tu uwzgledniamy ˛ współrz˛edna˛ wektora translacji ∗/ ∗wsk Vwyn = ∗wsk Melem++; for (j = 0, wsk Varg = V.wspol; j < 3; ++j) /∗ Tu zajmujemy sie˛ rotacja˛ ∗/ ∗wsk Vwyn += ∗wsk Melem++ ∗ ∗wsk Varg++; } return V wyn; } ... Wykorzystanie wskaźników do przemieszczania sie˛ po tablicy daje zdecydowanie szybsze działanie funkcji. Jest to szczególnie ważne w przypadku dużych tablic. 4 Mnożenie wektora przez macierz (implementacja w C) ... Wektor 3f MacierzRazWektor( Macierz J3f M, Wektor 3f V ) { Wektor 3f V wyn; V wyn.wspol[0] = M.elem[0][0] + M.elem[0][1]∗V.wspol[0] + M.elem[0][2]∗V.wspol[1] + M.elem[0][3]∗V.wspol[2]; V wyn.wspol[1] = M.elem[1][0] + M.elem[1][1]∗V.wspol[0] + M.elem[1][2]∗V.wspol[1] + M.elem[1][3]∗V.wspol[2]; V wyn.wspol[2] = M.elem[2][0] + M.elem[2][1]∗V.wspol[0] + M.elem[2][2]∗V.wspol[1] + M.elem[2][3]∗V.wspol[2]; return V wyn; } ... Zapis explicite całego wyrażenia gwarantuje najwieksz ˛ a˛ efektywność kodu. Jednak nie zawsze jest to możliwe (np. ze wzgledu ˛ na zmiany rozmiaru tablic lub ich wielkość). W takim przypadku optymalizacja kodu poprzez posługiwanie sie˛ wskaźnikami jest istotna i bardzo wskazana. Należy pamietać, ˛ że przedstawiona powyżej postać wyrażenia jest bardziej narażona na przypadkowe błedy ˛ jego zapisu przez programiste˛ i z tego powodu jest mniej preferowana. 5 Mnożenie macierzy przez macierz (implementacja w C) ... Macierz J3f MnozenieMacierzy( Macierz J3f M1, Macierz J3f M2 ) { Macierz J3f M wyn; int i, j, k; for ( i=0; i < 3; ++i ) { for ( j = 0; j < 3; ++j ) { /∗ . . . . . . . . . . . . . . . . . Obliczanie elementów podmacierzy rotacji ∗/ M wyn.elem[ i ][ j ] = 0; for ( k = 0; k < 3; ++k ) M wyn.elem[ i ][ j ] += M1.elem[ i ][ k ] ∗ M2.elem[ k ][ j ]; } /∗ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ∗ / M wyn.elem[ i ][ 3 ] = M1.elem[ i ][ 3 ]; /∗ . . . . . . . . . . . . . . . . Wyznaczanie wektora translacji ∗/ for ( k = 0; k < 3; ++k ) M wyn.elem[ i ][ 3 ] += M1.elem[ i ][ k ] ∗ M2.elem[ k ][ 3 ]; } return M wyn; } ... R11 R12 R13 Tx R11 R12 R13 Tx R21 R22 R23 Ty R21 R22 R23 Ty R31 R32 R33 Tz 0 0 0 1 R31 0 R32 0 R33 0 Tz 1 ∗ 6 Organizacja struktur danych R 11 R 12 R 13 Tx R 21 R 22 R 23 Ty R 31 R 32 R 33 Tz −→ R 11 R 12 R 13 Tx R 21 R 22 R 23 Ty R 31 R 32 R 33 Tz Wydzielenie w strukturze macierzy zbioru wektorów pozwala uprościć zapis operacji takich jak mnożenie przez wektor lub macierz. Jest to możliwe dzieki ˛ wykorzystaniu przeciażenia ˛ operatora jednego z operatorów implementujacego ˛ iloczyn skalarny. 7 Definicja struktur danych (implementacja w C++) class Wektor 3f { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float tab[3]; public : float x( ) const { return tab[0]; } ... float operator [ ] (int i) const { return tab[i]; } float &operator [ ] (int i) { return tab[i]; } float operator & (Wektor 3f const &W) const { return x()∗W.x()+y()∗W.y()+z()∗W.z(); } ... }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . class Macierz J3f { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . public : // Ten zbiór wektorów odpowiada za podmacierz rotacji Wektor 3f wiersz Rot[ 3 ]; Wektor 3f trans; // Wektor ten reprezentuje kolumne˛ wektora translacji float operator ( )(int i, int j) const { return j < 3 ? wiersz Rot[ i ][ j ] : trans[ i ]; } float &operator ( )(int i, int j) { return j < 3 ? wiersz Rot[ i ][ j ] : trans[ i ]; } const &V) const ; Wektor 3f operator ∗ (Wektor 3f Macierz J3f operator ∗ (Macierz J3f const &M) const ; ... }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 Mnożenie przez wektor (implementacja w C++) Wx Wy = R 11 R 12 R 13 Tx Vx R 21 R 22 R 23 Ty Vy * Wz R 31 R 32 R 33 Tz Vz 1 0 0 0 1 1 Vx Wx = R 11 R 12 R 13 * =⇒ Vy + Tx Vz . . . Wektor 3f Macierz J3f::operator ∗ (Wektor 3f const &V) const { Wektor 3f V wyn; for (int i = 0; i < 3; i++ ) V wyn[i] = ( wiersz Rot[i] & V) + trans[i]; return V wyn; } Należy zwrócić na konieczność użycia nawiasów w przypadku stosowania operatora “&” jako implementacji iloczynu skalarnego. 9 Mnożenie przez macierz (implementacja w C++) R* R* R* 13 T* R *21 R *22 R *23 Ty* * R 31 * R 32 * R 33 0 0 0 11 12 R 11 R 12 R 13 Tx R’11 R’12 R’13 T’x R 21 R 22 R 23 Ty R’21 R’22 R’23 T’y Tz* R 31 R 32 R 33 Tz R’31 R’32 R’33 T’z 1 0 0 0 1 0 0 0 1 x = * * R 11 R *12 R *13 Tx* * R 21 R *22 R *23 Ty* R *31 R *32 R *33 0 0 0 R 11 R 12 R 13 Tx R 21 R 22 R 23 Ty Tz* R 31 R 32 R 33 1 0 0 Tx* R 11 R’11 R’12 R’13 T’x R’21 R’22 R’23 T’y Tz R’31 R’32 R’33 T’z 0 1 0 0 0 1 R 12 R 13 Tx R 21 R 22 R 23 Ty Tz* R 31 R 32 R 33 Tz T’z 1 0 0 0 1 1 = * =⇒ Ty* = T’x * T’y Macierz J3f Macierz J3f::operator ∗ (Macierz J3f const &M) const { Macierz J3f M wyn; for ( int i = 0; i < 3; i++ ) { for ( int j = 0; j < 3; j++, M wyn( i,j ) = 0 ) for ( int k = 0; k < 3; k++) M wyn( i,j ) += (∗this )( i,k )∗M( k,j ); // Obliczanie elementów rotacji } M wyn. trans = ∗this ∗ M. trans; // Obliczanie wektora translacji return M wyn; } Obliczenie wektora translacji macierzy wynikowej możemy uprościć gdyż jest on rezultatem przemnożenia wektora translacji argumentu drugiego przez macierz bed ˛ acej ˛ pierwszym argumentem operacji. 10 Organizacja struktur danych R 11 R 12 R 13 Tx R 21 R 22 R 23 Ty R 31 R 32 R 33 Tz Możliwość organizowania poszczególnych pól macierzy we wzajemnie “przenikajace” ˛ sie˛ struktury wektorów pozwala wykonywać operacje na poszczególnych wierszach i kolumnach. 11 Klasa wektora (wersja ciekawsza cz. 1) class VWektor 3f { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float & x, & y, & z; public : float x() const { return x; } float y() const { return y; } float z() const { return z; } float operator [ ] (int i) const { return i ? (i == 1 ? y : z) : x; } float &operator [ ] (int i) { return i ? (i == 1 ? y : z) : x; } float operator & (VWektor 3f const &W) const { return x()∗W.x()+y()∗W.y()+z()∗W.z(); } VWektor 3f &operator = (VWektor 3f const &V) { x = V. x; y = V. y; z = V. z; } VWektor 3f(float &xx, float &yy, float &zz): x(xx), y(yy), z(zz) { } ... }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . class Wektor 3f: public VWektor 3f { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . float tab[3]; public : Wektor 3f(): VWektor 3f( tab[0], tab[1], tab[2]) {} }; // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 Klasa macierzy (wersja ciekawsza cz. 2) class Macierz J3f { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . public : wiersz Rot[3]; Wektor 3f Wektor 3f trans; VWektor 3f kolumna0; VWektor 3f kolumna1; VWektor 3f kolumna2; float operator ()(int i, int j) const { return j < 3 ? wiersz Rot[ i ][ j ] : trans[ i ]; } float &operator ()(int i, int j) { return j < 3 ? wiersz Rot[ i ][ j ] : trans[ i ]; } Wektor 3f operator ∗ (Wektor 3f const &V) const ; Macierz J3f operator ∗ (Macierz J3f const &M) const ; VWektor 3f const &Wiersz(int i) const { return wiersz Rot[ i ]; } VWektor 3f const &Kolumna(int i) const { return i ? (i==1 ? kolumna1 : kolumna2) : kolumna0; } Macierz J3f(): kolumna0( wiersz Rot[0][0], wiersz Rot[1][0], wiersz Rot[2][0]), kolumna1( wiersz Rot[0][1], wiersz Rot[1][1], wiersz Rot[2][1]), kolumna2( wiersz Rot[0][2], wiersz Rot[1][2], wiersz Rot[2][2]) { } }; / . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 Mnożenie macierzy (wersja ciekawsza) R *11 R *12 * R 13 Tx* R *21 R *22 R *23 Ty* * R 31 * R 32 * R 33 0 0 0 R’11 R’12 R’13 T’x R’21 R’22 R’23 T’y Tz R’31 R’32 R’33 T’z 1 0 0 0 1 R 11 R 12 R 13 Tx R 21 R 22 R 23 Ty Tz* R 31 R 32 R 33 1 0 0 0 = * =⇒ * R 11 R *12 R *13 Tx* * R 21 R *22 R *23 Ty* R *31 R *32 R *33 0 0 0 R 11 R 12 R 13 T’x R 21 R 22 R 23 T’y Tz R 31 R 32 R 33 T’z 0 1 0 0 0 1 R 12 R 13 Tx R 21 R 22 R 23 Ty Tz* R 31 R 32 R 33 Tz T’z 1 0 0 0 1 1 R 11 R 12 R 13 Tx R 21 R 22 R 23 Ty Tz* R 31 R 32 R 33 1 0 0 Tx* R 11 Ty* = = * T’x * T’y Macierz J3f Macierz J3f::operator ∗ (Macierz J3f const &M) const { Macierz J3f M wyn; for ( int i = 0; i < 3; i++ ) { for ( int j = 0; j < 3; j++) M wyn(i,j) = this ->Wiersz( i ) & M.Kolumna( j ); } M wyn. trans = ∗this ∗ M. trans; return M wyn; } Odpowiednie zorganizowanie struktur danych pozwala na uproszczenie zapisu złożonych operacji. 14 Diagram klas VWektor_3f Macierz_J3f −_wiersz_Rot[3]: Wekto_3f −_trans: Wektor_3f −_kolumna0: VWektor_3f −_kolumna1: VWektor_3f −_kolumna2: VWektor_3f +operator ( )(i:int,j:int): Wektor_3f +operator *(V:Wektor_3f): wektor_3f +operator *(M:Macierz_J3f): Macierz_J3f +Wiersz(i:int): VWektor_3f +Kolumna(i:int): VWektor_3f −_x: float & −_y: float & −_z: float & +operator ( )(i:int): float +operator [ ](i:int): float +operator &(W:VWektor_3f const &): float +operator = (V:VWektor_3f const &): VWektor_3f & Wektor_3f −_tab[3]: float Diagram ten obrazuje zależność klasy Macierz J3f od VWektor 3f i Wektor 3f oraz dziedziczenie przez klase˛ Wektor 3f klasy VWektor 3f. 15 Pytania i ćwiczenia 1. Niech klasa Wektor bedzie ˛ zdefiniowana w nastepuj ˛ acy ˛ sposób: class Wektor { public: float x, y, z; float operator& (Wektor const&W) const{ return x∗W. x + y∗W. y + z∗W. z; } Wektor(float xx, float yy, float zz): x(xx), y(yy), z(zz) { } }; (a) W liście inicjalizacji można wymusić uruchomienie odpowiednich konstruktorów dla obiektu dziedziczonej klasy. Czy przedstawiony powyżej zapis listy inicjalizacji jest poprawny. (b) Czy w oparciu o te˛ definicje˛ klasy można stwierdzić, że poniższy zapis jest poprawny: Wektor W1(1,2,5), W2(4,2,2); W1. x = W1 & W2 + W1. x; Jeżeli tak, to jaki jest wynik tej operacji? Jeżeli nie, to co należy zrobić, aby zapis ten był poprawny. 2. Majac ˛ na uwadze definicje˛ klasy Macierz J3f przedstawiona˛ na stronie 13 czy można stwierdzić, że poniższy zapis jest poprawny? Macierz J3f M; bool rezultat = M.Kolumna(2)[1] == M.Wiersz(1)[2]; Jeżeli tak, to czy można jednoznacznie określić jaka˛ wartość przyjmie zmienna rezultat? Jeżeli nie, to co należy zrobić, aby zapis ten był poprawny? 16