行列と幾何変換

本日の授業では、行列を使って、平面にある点のリストの扱いを勉強します。特に、行列を道具として、点の幾何変換を行います。

点の幾何変化とは、点を倍にすること、点の平行移動と回転などの変換です。

内容の概要

  • 1回目:平面上の点とグラフの表示, 線形変換
  • 2回目:アフィン変換, 同次座標系

1. 平面上の点とグラフの表示

Pythonでは、matplotlib.pyplotというモジュールを導入することで点の表示ができます。

使い方:

  • matplotlib.pyplot.plot( 点列のX座標のリスト、点列のY座標のリスト、プロットのフォマット)
matplotlib.pyplot.plot(,,,)
と書くのは長いので、matplotlibを導入する行において
import matplotlib.pyplot as plt
とすると、asに続く文字列で省略表記を定義することができます。

例1:点のリストを描く

(1,0)と (3,1)2つの点を赤丸で表示するために、下のコードを使用します。

import matplotlib.pyplot as plt
plt.plot ([1,3], [0,1], 'ro')
In [27]:
import matplotlib.pyplot as plt
#2つの点の表示, markersizeによって、点のサイズを調整できます。
plt.plot ([1,3], [0,1], 'ro',markersize=20)
plt.grid()
plt.show()

x軸とy軸のアスペクト比

以下の命令によって、x軸とy軸のアスペクト比を1にすることができます。

plt.axes().set_aspect('equal')

In [49]:
import matplotlib.pyplot as plt

plt.axes().set_aspect('equal')

plt.plot ([1,3], [0,1], 'ro',markersize=20)
plt.grid()

配列を使って、点列を描画します。

点のリストとNumpyの行列

これからの計算では、長さがnである点列を$2\times n$の形の行列で表現します。そうすると、行列の各列は点列の点に対応します。 行列として処理する場合、Pythonライブラリにあるnumpyを使うと便利です。

numpyでは、「:」は「すべて」の意味をもっています。即ちここでは、「すべての行」、または、「すべての列」です。

例: p_list =np.array[[0, 1, 1],[0, 0, 1]] について、

列を取るために、「すべての行」を使います。

  • p_list[:,0] はp_listのすべての行、第0列を示しています。即ち、点列の0番目(0から数える)の点。
  • p_list[:,1] はp_listのすべての行、第1列を示しています。即ち、点列の1番目(0から数える)の点。

行を取るために、「すべての列」を使います。

  • p_list[0,:] はp_listのすべての列、第0行を示しています。即ち、すべての点のx座標のリスト。
  • p_list[1,:] はp_listのすべての列、第1行を示しています。即ち、すべての点のy座標のリスト。

例2:三角形を描く

三点(0,0), (1,0), (1,1)が成す三角形を描いてみます。x軸とy軸のアスペクト比を1とします。

特に、閉じている多角形を描くために、点のリストの最後に、はじめの点を入れるのは必要です。即ち、点(0,0), (1,0), (1,1), (0,0)のリストが必要です。

In [32]:
import numpy as np
import matplotlib

p_list =np.array([[0, 1, 1, 0],
                  [0, 0, 1, 0]])
print("p_listの1行目(x座標)",p_list[0])
print("p_listの2行目(y座標)",p_list[1])

plt.axes().set_aspect('equal')
plt.plot( p_list[0,:], p_list[1,:], 'r-')
plt.grid()
p_listの1行目(x座標) [0 1 1 0]
p_listの2行目(y座標) [0 0 1 0]

「家」の輪郭を描く

以下の三角の点列と四角の点列を使って、シンプルな「家」を描けます。 特に、閉じている多角形を描くために、点のリストの最後に、はじめの点を入れるのは必要です。

  • roof_nodes:(-3,2),(3,2),(0,3)
  • wall_nodes:(-2,2), (-2,0),(2,0),(2,2)

点の並び方と行列の転置

以下のコードでは、点の入力を楽にするために、行列の転置を使っています。

roof_nodesの点のリストを用意するとき、一点づつで設定するのは便利なので、縦の行列を先に作成します。その後、以下のコードで行列の転置を行います。 roof_nodes = roof_nodes.T

例3:家を描く

In [47]:
#ここでコードを書いてください。
import matplotlib.pyplot as plt
roof_nodes = np.array([[-3,2], [3,2],[0,3],[-3,2]])
roof_nodes = roof_nodes.T
wall_nodes = np.array([[-2,2],[-2,0], [2,0], [2,2],[-2,2]])
wall_nodes = wall_nodes.T

plt.axes().set_aspect('equal')

#変換前の家
plt.plot(roof_nodes[0,:], roof_nodes[1,:],'ro-')
plt.plot(wall_nodes[0,:], wall_nodes[1,:],'ro-')
plt.grid()

演習1

上記の「家」の例とは異なく、自分なりの家を描いてください。

適当に、窓、ドアを追加しても良いです。その時、それぞれの家の構成部分のために、点のリストを用意してください。

例えば、window_nodes, door_nodes 。

In [50]:
#ここにコードを書いてください。

2. 線形変換

2.1 拡大・縮小変換

点の$x$座標または$y$座標を$t$倍にすることで、図形の拡大と縮小ができます。

$x$方向で$\alpha$倍にする変換

$$ x'=\alpha x,y' = y \Longleftrightarrow \left(\begin{array}{c} x' \\ y' \end{array}\right) = A_x \left(\begin{array}{c} x \\ y \end{array}\right) \quad A_x=\left(\begin{array}{cc} \alpha & 0 \\ 0 & 1 \end{array}\right) $$

$y$方向で$\beta$倍にする変換

$$ x'=x,y' = \beta y \Longleftrightarrow \left(\begin{array}{c} x' \\ y' \end{array}\right) = A_y \left(\begin{array}{c} x \\ y \end{array}\right) \quad A_y=\left(\begin{array}{cc} 1 & 0 \\ 0 & \beta \end{array}\right) $$

$x$方向で$\alpha$倍,$y$方向で$\beta$倍にする変換

$$ x'=\alpha x,y' = \beta y \Longleftrightarrow \left(\begin{array}{c} x' \\ y' \end{array}\right) = A \left(\begin{array}{c} x \\ y \end{array}\right) \quad A=\left(\begin{array}{cc} \alpha & 0 \\ 0 & \beta \end{array}\right) = A_x \times A_y $$

例:

以下の例では、「家」を$x$方向$2$倍、$y$方向$1.5$倍に伸ばしています。変換前の家は赤、変換後の家は青で描かれています。

In [51]:
#ここで演習のコードを書いてください。
roof_nodes = np.array([[-3,2], [3,2],[0,3],[-3,2]]).T
wall_nodes = np.array([[-2,2],[-2,0], [2,0], [2,2],[-2,2]]).T

plt.axes().set_aspect('equal')

#変換前の家
plt.plot(roof_nodes[0,:], roof_nodes[1,:],'ro-')
plt.plot(wall_nodes[0,:], wall_nodes[1,:],'ro-')
plt.grid()

#変換行列
A=np.array([[2, 0], [0, 1.5]]);

#変換後の家
new_roof_nodes = np.dot(A,roof_nodes)
new_wall_nodes = np.dot(A,wall_nodes)
plt.plot(new_roof_nodes[0,:], new_roof_nodes[1,:],'bo-')
plt.plot(new_wall_nodes[0,:], new_wall_nodes[1,:],'bo-')
Out[51]:
[<matplotlib.lines.Line2D at 0x7f4c697c9c50>]

演習2

演習1で考えた「家」を$x$方向と$y$方向に適当に伸ばして、描いてください。

In [48]:
#ここにコードを書いてください。

レポート課題

演習1で作成した家をベースにして、拡大・縮小変換によって、2つのサイズの異なる家を作成してください。