Výpočet matice rotace

Jak již bylo zmíněno, po nahrání stránky do prohlížeče se zavolá funkce init():

    function init() {
      rot = get_rotation_matrix(r,angle);  // Spocita matici rotace pro zadany smer osy a uhel o kolik se ma otacet.
      setInterval(animate,100);            // Nastavi, aby se funkce animate volala kazdy 100ms.
    };
Ta nejprve zavolá funkci get_rotation_matrix(r,angle), která spočítá matici rotace rot, a pak nastaví, aby se funkce animate() volala každých 100ms.

K výpočtu matici rotace rot využijeme faktu, že umíme rotovat s vektory v $\mathbb{R}^2$ okolo počátku o zadaný úhel $\alpha $. Nechť $(B)$ je libovolná uspořádaná ortonormální báze v $\mathbb{R}^2$. Matici lineárního zobrazení takového otočení vzhledem k bázi $(B)$ jsme viděli na přednášce:

\begin{displaymath}\mathbf{R}_2=\left(\begin{array}{rr}
\cos(\alpha ) & -\sin(\alpha )\\
\sin(\alpha ) & \cos(\alpha )\\
\end{array}\right) .\end{displaymath}

Nechť $\mathbf{x}\in\mathbb{R}^2$ je vektor, jehož souřadnice v $(B)$ jsou $(x_1,x_2)$, tj. v našem značení $\mathbf{x}=(x_1,x_2)_B$. Pokud chceme otočit vektor $\mathbf{x}$ uhel $\alpha $, stačí spočítat následující součin $\mathbf{R}_2\cdot\left(\begin{array}{c}x_1 x_2\end{array}\right)$. Výsledkem je matice typu $(2,1)$, jejíž složky odpovídají souřadnicím otočeného vektoru vzhledem k bázi $(B)$. Všimněte si, že pokud bychom chtěli výsledek vyjádřit jako řádkový vektor, stačí celý výraz ztransponovat, tj. souřadnice otočeného vektoru můžeme přímo vyjádřit jako $\left(\mathbf{R}_2\cdot\left(\begin{array}{c}x_1 x_2\end{array}\right)\right)^T=(x_1,x_2)\cdot\mathbf{R}_2^T$.

Nechť nyní $(B)=(\mathbf{b}_1,\mathbf{b}_2,\mathbf{b}_3)$ je libovolná uspořádaná ortonormální báze $\mathbb{R}^3$. Matici $\mathbf{R}_2$ otáčení v rovině můžeme snadno rozšířit i do 3D prostoru tak, aby otáčela vektory kolem osy dané vektorem $\mathbf{b}_3$. Při takové rotaci totiž zůstává třetí souřadnice konstantní a první dvě se otočí jako v rovině. Matice rotace vzhledem ke bázi $(B)$ kolem osy dané $\mathbf{b}_3$ je tedy takováto matice:

\begin{displaymath}\mathbf{R}=\left(\begin{array}{rrr}
\cos(\alpha ) & -\sin(\al...
...\alpha ) & \cos(\alpha ) & 0\\
0 & 0 & 1
\end{array}\right) .\end{displaymath}

Sloupce matice $\mathbf{R}$ se totiž skládají ze souřadnic otočených vektorů báze $(B)$ vzhledem k bázi $(B)$. Tedy první sloupec obsahuje souřadnice vzhledem k $(B)$ otočeného vektoru $\mathbf{b}_1=(1,0,0)_B$, což je vlastně jakoby otočený vektor se souřadnicemi $(1,0)$ v rovině pomocí $\mathbf{R}_2$ a třetí souřadnice se nemění. Podobně druhý sloupec obsahuje souřadnice otočeného vektoru $\mathbf{b}_2=(0,1,0)_B$. Poslední sloupec jsou souřadnice otočeného vektoru $\mathbf{b}_3=(0,0,1)_B$. Ten ale leží na ose rotace, a tudíž je stále stejný.

Nechť $\mathbf{x}=(x_1,x_2,x_3)_B$ je libovolný vektor v $\mathbb{R}^3$ se souřadnicemi $(x_1,x_2,x_3)$ vzhledem k $(B)$. Podobně jako předtím v rovině, pokud chceme najít souřadnice otočeného vektoru $\mathbf{x}$ vzhledem k bázi $(B)$, stačí spočítat výraz $(x_1,x_2,x_3)\cdot\mathbf{R}^T$.

Matice $\mathbf{R}$ umí tedy otáčet vektory kolem osy dané vektorem $\mathbf{b}_3$. Nicméně na to abychom ji mohli použít na otočení vektoru $\mathbf{x}\in\mathbb{R}^3$, musíme znát jeho souřadnice v bázi $(B)$. Vektor $\mathbf{x}$ máme typicky zadán pomocí souřadnic vhledem ke standardní bázi $(E)=((1,0,0),(0,1,0),(0,0,1))$. Jeho souřadnice vzhledem k bázi $(B)$ získáme pomocí matice přechodu od $(B)$ k $(E)$. Označme $\mathbf{b}_1=(b_{11},b_{12},b_{13})$, $\mathbf{b}_2=(b_{21},b_{22},b_{23})$ a $\mathbf{b}_3=(b_{31},b_{32},b_{33})$, tj. $b_{ij}$ jsou souřadnice vzhledem k $(E)$. Pak snadno získáme matici přechodu $\mathbf{B}$ od $(E)$ k $(B)$. Je to totiž matice identického zobrazení vzhledem k bázím $(B)$ a $(E)$, tzn. její sloupce jsou tvořeny souřadnicemi vektorů $\mathbf{b}_1,\mathbf{b}_2,\mathbf{b}_3$ vzhledem k $(E)$, tj.

\begin{displaymath}\mathbf{B}=\left(\begin{array}{rrr}
b_{11} & b_{21} & b_{31}\...
...} & b_{32}\\
b_{13} & b_{23} & b_{33}\\
\end{array}\right) .\end{displaymath}

Tato matice umí přepočítávat souřadnice libovolného vektoru z báze $(B)$ do báze $(E)$. Konkrétně nechť $\mathbf{x}=(x_1,x_2,x_3)_B$. Pak souřadnice $\mathbf{x}$ v bázi $(E)$ dostaneme takto:

\begin{displaymath}\left(\begin{array}{c}x_1' x_2' x_3'\end{array}\right)=\m...
...\cdot\left(\begin{array}{c}x_1 x_2 x_3\end{array}\right) .\end{displaymath}

Pokud chceme naopak přepočítat souřadnice z $(E)$ do $(B)$, stačí vynásobit předchozí rovnost $\mathbf{B}^{-1}$ zleva (což je matice přechodu od $(B)$ k $(E)$), tj. pokud vím, že $\mathbf{x}=(x_1',x_2',x_3')_E$, pak

\begin{displaymath}\mathbf{B}^{-1}\cdot\left(\begin{array}{c}x_1' x_2' x_3'\...
...ght)=\left(\begin{array}{c}x_1 x_2 x_3\end{array}\right) .\end{displaymath}

Nyní chceme umět otočit libovolný vektor $\mathbf{x}=(x_1,x_2,x_3)\in\mathbb{R}^3$ podle osy dané $\mathbf{b}_3$. Složky uspořádané trojice $(x_1,x_2,x_3)$ odpovídají souřadnicím vektoru $\mathbf{x}$ vzhledem k bázi $(E)$. Otočený vektor $\mathbf{y}=(y_1,y_2,y_3)$ získáme takto:

\begin{displaymath}\left(\begin{array}{c}y_1 y_2 y_3\end{array}\right)=\math...
...\cdot\left(\begin{array}{c}x_1 x_2 x_3\end{array}\right)\„\end{displaymath} (1)

kde součin s $\mathbf{B}^{-1}$ přepočítá souřadnice $\mathbf{x}$ z $(E)$ do $(B)$, součin s $\mathbf{R}$ otočí vektor $\mathbf{x}$ vyjádřený v bázi $(B)$ a součin s $\mathbf{B}$ přepočítá souřadnice výsledku zpět do $(E)$. Navíc protože $(B)$ je ortonormální báze (tj. $\mathbf{b}_1,\mathbf{b}_2,\mathbf{b}_3$ jsou navzájem kolmé vektory délky $1$), máme $\mathbf{B}^{-1}=\mathbf{B}^T$. Použitím této rovnosti a transpozicí rovnice (1) dostaneme:
\begin{displaymath}
(y_1,y_2,y_3)=\left(\mathbf{B}\cdot\mathbf{R}\cdot\mathbf{B}...
...1,x_2,x_3)\cdot\mathbf{B}\cdot\mathbf{R}^T\cdot\mathbf{B}^T .
\end{displaymath} (2)

Teď jsme připraveni si vysvětlit, co funkce get_rotation_matrix(r,angle) vlastně dělá. Funkce používá následující proměnné:

      var coor_matrix = Matrix.create([     
        [0,0,0],[0,0,0],[0,0,0]
      ]);
      var rot_matrix = Matrix.create([      
        [0,0,0],[0,0,0],[0,0,0]
      ]);
      var a = Vector.create([0,0,0]);       
      var b = Vector.create([0,0,0]);
Proměnná coor_matrix je proměnná pro matici přechodu $\mathbf{B}$ a rot_matrix je proměnná pro transponovanou matici rotace $\mathbf{R}^T$. Dále zde máme dva pomocné vektory $a,b$.

Na začátku funkce testuje, jestli není vektor osy rotace r nulový. V případě, že ano, nastaví r na $(1,1,1)$.

      if (r.eql(Vector.Zero(3))) {
        r.setElements([1,1,1]);
        alert("Vektor osy rotace nemuze byt nulovy!");
      }

Nyní je potřeba zkonstruovat libovolnou uspořádanou ortonormální bázi, jejíž třetí vektor je r (libovolnou až na pořadí prvních dvou bázových vektorů, jímž můžeme ovlivnit směr výsledné rotace). Takže potřebujeme najít dva navzájem kolmé vektory a,b délky $1$, které budou kolmé na vektor r. Nejprve uděláme z r vektor délky $1$. Pak zvolíme libovolný vektor a délky $1$, který je na r kolmý. Nechť r $=(r_1,r_2,r_3)$. Pokud $r_1=r_2=0$, pak takový vektor je např. $(1,0,0)$. Pokud alespoň jedno z $r_1,r_2$ je nenulové, můžeme vzít např. vektor $(-r_2,r_1,0)$ a udělat z něj vektor délky $1$. Nakonec najdeme vektor b délky $1$, který je kolmý na a i r pomocí vektorového součinu r$\times$a. Výše uvedený postup je obsahem následujícího kusu kódu:

      r = r.toUnitVector();                 
      if (r.e(1)!=0 || r.e(2)!=0) {         
        a.setElements([-r.e(2),r.e(1),0]);
        a = a.toUnitVector();              
      }
      else {
        a.setElements([1,0,0]);
      }
      
      b = r.cross(a);

Uspořádaná trojice $(B)$=(a,b,r) tvoří uspořádanou ortonormální bázi, jejíž třetí vektor je směrový vektor osy rotace. Sestavíme matici přechodu od $(E)$ k $(B)$.

      coor_matrix.setElements([             
        [a.e(1),b.e(1),r.e(1)],             
        [a.e(2),b.e(2),r.e(2)],             
        [a.e(3),b.e(3),r.e(3)]              
      ]);

Do proměnné rot_matrix zadáme matici $\mathbf{R}^T$ s úhlem $\alpha =$angle:

      rot_matrix.setElements([              
        [ Math.cos(angle), Math.sin(angle),0],
        [-Math.sin(angle), Math.cos(angle),0],
        [               0,               0,1]
      ]);

Nakonec spočítáme součin $\mathbf{B}\cdot\mathbf{R}^T\cdot\mathbf{B}^T$ (viz rovnice (2)) a jeho výsledek vrátíme jako výstup funkce:

      rot_matrix = rot_matrix.multiply(coor_matrix.transpose());  // rot_matrix*(coor_matrix)^T
      rot_matrix = coor_matrix.multiply(rot_matrix);              // coor_matrix*(rot_matrix*(coor_matrix)^T)
      return(rot_matrix);

Rostislav Horcik 2009-01-04