自分なりの家の図を描画する関数house()
を定義し、その関数を呼び出してください。前回の演習1の内容に加えて、適宜塗りつぶしや色の変更を行うこと。
import numpy as np
import matplotlib.pyplot as plt
def house():
roof_nodes = np.array([[0,3],[-3,2],[3,2],[0,3]]).T #屋根の点からなる行列
wall_nodes = np.array([[-2,2],[-2,0],[2,0],[2,2],[-2,2]]).T #壁の点からなる行列
window_nodes = np.array([[0.75,1.5],[0,1.5],[0,0.5],[0.75,0.5],[0.75,1.5],[1.5,1.5],[1.5,0.5],[0.75,0.5]]).T #窓の点からなる行列
door_nodes = np.array([[-1.5,1.5],[-1.5,0],[-0.5,0],[-0.5,1.5],[-1.5,1.5]]).T #ドアの点からなる行列
knob_nodes = np.array([[-0.7,0.9],[-0.8,0.8],[-0.7,0.7],[-0.6,0.8],[-0.7,0.9]]).T #ドアノブの点からなる行列
plt.gca().set_aspect("equal")
plt.plot(roof_nodes[0,:],roof_nodes[1,:],color="black") #屋根の描画
plt.fill(roof_nodes[0,:],roof_nodes[1,:],color="tomato") #屋根の塗りつぶし
plt.plot(wall_nodes[0,:],wall_nodes[1,:],color="black") #壁の描画
plt.fill(wall_nodes[0,:],wall_nodes[1,:],color="ivory") #壁の塗りつぶし
plt.plot(window_nodes[0,:],window_nodes[1,:],color="black") #窓の描画
plt.fill(window_nodes[0,:],window_nodes[1,:],color="lightskyblue") #窓の塗りつぶし
plt.plot(door_nodes[0,:],door_nodes[1,:],color="black") #ドアの描画
plt.fill(door_nodes[0,:],door_nodes[1,:],color="chocolate") #ドアの塗りつぶし
plt.plot(knob_nodes[0,:],knob_nodes[1,:],color="black") #ドアノブの描画
plt.fill(knob_nodes[0,:],knob_nodes[1,:],color="gold") #ドアノブの塗りつぶし
house() #定義した関数を呼び出す
plt.show()
演習1の家に対して行列 $A$ と ベクトル $b$ によるアフィン変換を行った結果を描画する関数house_transform(A,b)
を定義し、変換前の家と適当なアフィン変換を行った家を一つの図に描画してください。
import numpy as np
import matplotlib.pyplot as plt
def house_transform(A,b):
roof_nodes = np.array([[0,3],[-3,2],[3,2],[0,3]]).T #屋根の点からなる行列
wall_nodes = np.array([[-2,2],[-2,0],[2,0],[2,2],[-2,2]]).T #壁の点からなる行列
window_nodes = np.array([[0.75,1.5],[0,1.5],[0,0.5],[0.75,0.5],[0.75,1.5],[1.5,1.5],[1.5,0.5],[0.75,0.5]]).T #窓の点からなる行列
door_nodes = np.array([[-1.5,1.5],[-1.5,0],[-0.5,0],[-0.5,1.5],[-1.5,1.5]]).T #ドアの点からなる行列
knob_nodes = np.array([[-0.7,0.9],[-0.8,0.8],[-0.7,0.7],[-0.6,0.8],[-0.7,0.9]]).T #ドアノブの点からなる行列
plt.gca().set_aspect("equal")
roof_nodes = A@roof_nodes+b #new_roof_nodesのような新たな変数を使ってもよいが、自身に代入する方が楽
wall_nodes = A@wall_nodes+b
window_nodes = A@window_nodes+b
door_nodes = A@door_nodes+b
knob_nodes = A@knob_nodes+b
plt.plot(roof_nodes[0,:],roof_nodes[1,:],color="black") #屋根の描画
plt.fill(roof_nodes[0,:],roof_nodes[1,:],color="tomato") #屋根の塗りつぶし
plt.plot(wall_nodes[0,:],wall_nodes[1,:],color="black") #壁の描画
plt.fill(wall_nodes[0,:],wall_nodes[1,:],color="ivory") #壁の塗りつぶし
plt.plot(window_nodes[0,:],window_nodes[1,:],color="black") #窓の描画
plt.fill(window_nodes[0,:],window_nodes[1,:],color="lightskyblue") #窓の塗りつぶし
plt.plot(door_nodes[0,:],door_nodes[1,:],color="black") #ドアの描画
plt.fill(door_nodes[0,:],door_nodes[1,:],color="chocolate") #ドアの塗りつぶし
plt.plot(knob_nodes[0,:],knob_nodes[1,:],color="black") #ドアノブの描画
plt.fill(knob_nodes[0,:],knob_nodes[1,:],color="gold") #ドアノブの塗りつぶし
house() #変換前の家の描画
A = np.array([[1.5,0],[0,4]]) #拡大・縮小変換のための行列
b = np.array([[7,3]]).T #平行移動のための縦ベクトル
house_transform(A,b) #変換後の家の描画
plt.show()
演習1の関数house()
と演習2の関数house_transform(A,b)
を利用して、
変換前の家
反時計回りに $15$ 度だけ回転した後、$x$ 軸方向に $2$、$y$ 軸方向に $3$ だけ平行移動した家
$x$ 軸方向に $2$、$y$ 軸方向に $3$ だけ平行移動した後、反時計回りに $15$ 度だけ回転した家
を一つの図に描画してください。
さらに、線形変換と平行移動の順番を変えることの意味について考察してください。
house() #変換前の家の描画
theta = 15/180*np.pi #度をラジアンに直す
A = np.array([[np.cos(theta),-np.sin(theta)],[np.sin(theta),np.cos(theta)]]) #回転変換のための行列
b = np.array([[2,3]]).T #平行移動のための縦ベクトル
house_transform(A,b) #変換後の家の描画(回転変換→平行移動)
house_transform(A,A@b) #変換後の家の描画(平行移動→回転変換)
#勘違いしやすいところだが、関数を呼び出す際の引数を関数定義で用いた引数の変数に合わせる必要はない
plt.show()
縦ベクトルとして表される点 $x$ に対して、行列 $A$ による線形変換と縦ベクトル $b$ による平行移動を順に行うと
$$ Ax+b, $$縦ベクトル $b$ による平行移動と行列 $A$ による線形変換を順に行うと
$$ A(x+b)=Ax+Ab $$が得られる。多くの場合には $b\neq Ab$ であるから、上で描画した図のように線形変換と平行移動の順番を変えた際の結果は異なる。
例えば
$$ A= \begin{pmatrix} 1 & 0\\ 0 & -1 \end{pmatrix} ,\quad b= \begin{pmatrix} p\\ 0 \end{pmatrix} $$として $x$ 軸に関する対称変換と $x$ 軸方向のみの平行移動を行う場合には、$b=Ab$ が成り立つため順番を変えても同じ結果になる。
上の図で家と家が重なった部分の表示がおかしい(見えないはずの線が見えている)ことが気になった方がいると思いますが、これはmatplotlib.pyplot
においてデフォルトで、塗りつぶしよりも線の描画の方が優先して表示されるよう設定されているためです。
表示順を変えたい場合には、個々のplot
関数やfill
関数の三つ目以降の引数としてzorder=数値
を指定すればよいです。zorder
の数値の大きいものが、手前に表示されます。デフォルトだと、plot
関数はzorder=2
、fill
関数はzorder=1
になっているようです。
演習3の図を正常に表示するための方法としては、例えば次のように、関数house()
とhouse_transform(A,b)
にzorder
を指定する引数を追加することが考えられます。
import numpy as np
import matplotlib.pyplot as plt
def house(k): #引数kを追加して定義
roof_nodes = np.array([[0,3],[-3,2],[3,2],[0,3]]).T
wall_nodes = np.array([[-2,2],[-2,0],[2,0],[2,2],[-2,2]]).T
window_nodes = np.array([[0.75,1.5],[0,1.5],[0,0.5],[0.75,0.5],[0.75,1.5],[1.5,1.5],[1.5,0.5],[0.75,0.5]]).T
door_nodes = np.array([[-1.5,1.5],[-1.5,0],[-0.5,0],[-0.5,1.5],[-1.5,1.5]]).T
knob_nodes = np.array([[-0.7,0.9],[-0.8,0.8],[-0.7,0.7],[-0.6,0.8],[-0.7,0.9]]).T
plt.gca().set_aspect("equal")
plt.plot(roof_nodes[0,:],roof_nodes[1,:],color="black",zorder=k) #zorder=kにする
plt.fill(roof_nodes[0,:],roof_nodes[1,:],color="tomato",zorder=k)
plt.plot(wall_nodes[0,:],wall_nodes[1,:],color="black",zorder=k)
plt.fill(wall_nodes[0,:],wall_nodes[1,:],color="ivory",zorder=k)
plt.plot(window_nodes[0,:],window_nodes[1,:],color="black",zorder=k)
plt.fill(window_nodes[0,:],window_nodes[1,:],color="lightskyblue",zorder=k)
plt.plot(door_nodes[0,:],door_nodes[1,:],color="black",zorder=k)
plt.fill(door_nodes[0,:],door_nodes[1,:],color="chocolate",zorder=k)
plt.plot(knob_nodes[0,:],knob_nodes[1,:],color="black",zorder=k)
plt.fill(knob_nodes[0,:],knob_nodes[1,:],color="gold",zorder=k)
def house_transform(A,b,k): #引数kを追加して定義
roof_nodes = np.array([[0,3],[-3,2],[3,2],[0,3]]).T
wall_nodes = np.array([[-2,2],[-2,0],[2,0],[2,2],[-2,2]]).T
window_nodes = np.array([[0.75,1.5],[0,1.5],[0,0.5],[0.75,0.5],[0.75,1.5],[1.5,1.5],[1.5,0.5],[0.75,0.5]]).T
door_nodes = np.array([[-1.5,1.5],[-1.5,0],[-0.5,0],[-0.5,1.5],[-1.5,1.5]]).T
knob_nodes = np.array([[-0.7,0.9],[-0.8,0.8],[-0.7,0.7],[-0.6,0.8],[-0.7,0.9]]).T
plt.gca().set_aspect("equal")
roof_nodes = A@roof_nodes+b
wall_nodes = A@wall_nodes+b
window_nodes = A@window_nodes+b
door_nodes = A@door_nodes+b
knob_nodes = A@knob_nodes+b
plt.plot(roof_nodes[0,:],roof_nodes[1,:],color="black",zorder=k) #zorder=kにする
plt.fill(roof_nodes[0,:],roof_nodes[1,:],color="tomato",zorder=k)
plt.plot(wall_nodes[0,:],wall_nodes[1,:],color="black",zorder=k)
plt.fill(wall_nodes[0,:],wall_nodes[1,:],color="ivory",zorder=k)
plt.plot(window_nodes[0,:],window_nodes[1,:],color="black",zorder=k)
plt.fill(window_nodes[0,:],window_nodes[1,:],color="lightskyblue",zorder=k)
plt.plot(door_nodes[0,:],door_nodes[1,:],color="black",zorder=k)
plt.fill(door_nodes[0,:],door_nodes[1,:],color="chocolate",zorder=k)
plt.plot(knob_nodes[0,:],knob_nodes[1,:],color="black",zorder=k)
plt.fill(knob_nodes[0,:],knob_nodes[1,:],color="gold",zorder=k)
house(1) #変換前の家の描画(zorder=1)
theta = 15/180*np.pi
A = np.array([[np.cos(theta),-np.sin(theta)],[np.sin(theta),np.cos(theta)]])
b = np.array([[2,3]]).T
house_transform(A,b,2) #変換後の家の描画(回転変換→平行移動)(zorder=2)
house_transform(A,A@b,3) #変換後の家の描画(平行移動→回転変換)(zorder=3)
plt.show()