Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
100
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

updated at

Organization

アフィン変換/平行移動だって変換行列で

図形情報はベクトルで考えるときれいにプログラミングできるというコツを書きました。
* 座標系 - 図形情報を扱うのに大事なことは、全部高校で教わった - Qiita
その続編として、高校で習わなかった アフィン変換 を使うと図形の回転・ズーム・移動がすべて行列で表現できて便利になるのをご紹介します。

拡大縮小と回転には変換行列、これは高校で習いました

座標をベクトルで表せば、特定の行列をかけるだけで拡大縮小と回転ができると習いました。
横にx倍、縦にy倍のズームは$\left(\begin{array}{ccc} x & 0 \\ 0 & y\end{array}\right)$で、
原点を中心にθ度の回転は$\left( \begin{array}{ccc} \cos \theta & -\sin \theta \\ \sin \theta & \cos \theta \end{array}\right)$です。

試しにやってみましょう。(10,10)を横に2倍、縦に0.5倍でズームすると
$\left( \begin{array}{ccc} 2 & 0 \\ 0 & 0.5 \end{array}\right)\left( \begin{array}{ccc} 10 \\ 10 \end{array}\right)=\left( \begin{array}{ccc} 20 \\ 5 \end{array}\right)$

(10,10)を90°回転させると
$\left( \begin{array}{ccc} 0 & -1 \\ 1 & 0 \end{array}\right)\left( \begin{array}{ccc} 10 \\ 10 \end{array}\right)=\left( \begin{array}{ccc} -10 \\ 10 \end{array}\right)$

行列による座標変換のいいところは、変換行列同士を掛け合わせてしまうことができることです。何段階かの変換も、その行列を掛け合わせてたった一つの行列にできるので、記述も簡潔になるしなにより計算が早い。不可逆な変換でなければ、逆行列を求めることで逆変換も簡単に記述できます。

でも、回転とズームだけではたいした変換はできませんね。やはり水平移動がないと…… 水平移動は、無理なのかな?

余分な一次元が、水平移動を可能にする

2次元のベクトルに2×2行列を掛けるだけの変換では、水平移動を記述することはできません。が、余分な一次元を加えることで、簡単にそれが実現できてしまうのです。
余分な一次元ってなに?
それは、座標(a,b)をこう書くことです――(a,b,1)。
ダミーの次元を付け加えて、値を1としておく。これだけです。座標が3次元表示になるから、変換行列は3×3になりますね。
ズームと回転の変換行列は、こうなります。
$\left( \begin{array}{ccc} x & 0 & 0 \\ 0 & y & 0 \\ 0 & 0 & 1 \end{array}\right)\left( \begin{array}{ccc} a \\ b \\ 1 \end{array}\right)=\left( \begin{array}{ccc} ax \\ by \\ 1 \end{array}\right)$
$\left( \begin{array}{ccc} \cos \theta & -\sin \theta & 0 \\ \sin \theta & \cos \theta & 0 \\0&0&1\end{array}\right)\left( \begin{array}{ccc} a\\ b\\1\end{array}\right)=\left( \begin{array}{ccc} a\cos \theta -b\sin \theta \\ a \sin \theta + b \cos \theta \\ 1 \end{array} \right) $
2×2のときの変換行列の右と下に0を詰め込んだだけですね。これで、ダミーの3次元表示を回転&ズームできます。

次に平行移動。ここで初めて、3×3行列の余った場所を使います。
$\left( \begin{array}{ccc} 1 & 0 & x \\ 0 & 1 & y \\ 0 & 0 & 1 \end{array}\right)\left( \begin{array}{ccc} a \\ b \\ 1 \end{array}\right)=\left( \begin{array}{ccc} a+x \\ b+y \\ 1 \end{array}\right)$
うわ、ほれぼれするくらい簡潔に移動が記述できましたね!

使い道

ダミーの次元を加えるというこのアフィン変換、初めて知った人にはかなりトリッキーなやり方に思えるかもしれません。でもこれは、図形を扱うシステムではわりとよく使われる技法です。
例えばPostScriptでは平面図形やフォントをアフィン変換するため3×3行列を使います。Direct3Dだと、空間図形の座標は4次元表示。3次元の図形を移動するには4×4行列を使うんですね。

(本人ブログ記事 http://sampo.hatenadiary.jp/entry/20070921/p1 を改題再編集しました)

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
100
Help us understand the problem. What are the problem?