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