PythonでL-Systemsを作る(5) 植物の成長システム
今回で,2次元の L-Sytems の構築はひとまず完成する。
今までのコマンドに,現在の状態を記憶する [ と,記憶したものを取り出す ] を追加する。これは,スタックという構造になっていて,複数のものを記憶すると,取り出すときは,最後に記憶したものを取り出すようになっている。
これを用いると「枝分かれ」ができる。次の図が植物の枝分かれを模した図だ。(The Algorithmic Beauty of Plants)
下から上に進む。回転角は45°。
まず上に進む F
ここで枝分かれするので,現在の状態を記憶する [
左を向いて1歩進む +F
枝分かれ地点に戻って,記憶した状態(上を向いている)に戻る ]
枝分かれは3本なので,ここでもう一度記憶する [
右を向いて1歩進む -F
ここで枝分かれするので,状態を記憶 [
右を向いて1歩進む -F
枝分かれ地点に戻る ]
前を向いているのでそのまま1歩進む F
はじめの枝分かれ地点に戻る ]
前を向いているのでそのまま1歩進む F
ここで枝分かれするので,現在の状態を記憶する [
左を向いて1歩進む +F
枝分かれ地点に戻る ]
状態を記憶して右を向いて進み,枝分かれ地点に戻る [-F]
ここで終わるなら,最後は [-F] でなく -F でもよいことになる。
この枝分かれをプログラミングしよう。記憶のためのリストを stack とする。
「記憶」は,亀の状態 state をリストに追加すればよい。 stack.appned(state)
このとき,state は stack の末尾に追加される。
「戻る」には,追加した state:stack の末尾の要素を取り出す。これは,Pythonの pop というメソッドで実現できる。引数は不要。
かくして,亀を動かす turtle に次のようにコマンドを追加すればよい。stack = [] はあらかじめ用意しておく。
def turtle(command, state):
if command == "A" or command == "B" or command == "F":
state = forward(state)
if command == "f":
state = translate(state)
if command == "+":
state = rotate(1, state)
if command == "-":
state = rotate(-1, state)
if command == "[":
stack.append(state)
if command == "]":
state = stack.pop()
return state
次のように設定をして動かしてみよう。
state = [[4, 1], np.pi/2] # 亀の初期状態 出発点とはじめの向き
initiator = "A"
generator = {"F":"FF","A":"F-[[A]+A]+F[+FA]-A"}
angle = 22.5 / 180 * np.pi # 角 度数法
drawcolor = 'green'
repeat = 1
distance = 2*(1/2)**repeat
stack = []
左から順に,イニシエータ,1回置き換え,2回置き換え・・・ となっている。
実際には木の高さはどんどん高くなるのだが,distance = 2*(1/2)**repeat で,1歩の長さを縮小している。
最後に,でき上がった L-Systems のコード全体と,リンデンマイヤーの本に載っている例を載せておこう。
import numpy as np
import matplotlib.pyplot as plt
plt.figure(figsize=(8, 8))
ax = plt.axes()
plt.axis([0, 8, 0, 8])
plt.xticks([])
plt.yticks([])
# 描画せずに移動する
def translate(state):
th = state[1]
x = state[0][0] + distance*np.cos(th)
y = state[0][1] + distance*np.sin(th)
return [[x, y],th]
# distance だけ描画して進む
def forward(state):
th = state[1]
x1 = state[0][0]
y1 = state[0][1]
x2 = x1 + distance*np.cos(th)
y2 = y1 + distance*np.sin(th)
plt.plot([x1, x2], [y1, y2], lw=1, color=drawcolor)
return [[x2, y2],th]
# 向きを変える coef 1/0 左 / 右
def rotate(coef, state):
th = state[1]
th = th + coef*angle
return [state[0], th]
# 亀を命令に従って動かす L , R は何もしない 亀の状態を返す
def turtle(command, state):
if command == "A" or command == "B" or command == "F":
state = forward(state)
if command == "f":
state = translate(state)
if command == "+":
state = rotate(1, state)
if command == "-":
state = rotate(-1, state)
if command == "[":
stack.append(state)
if command == "]":
state = stack.pop()
return state
# 置き換え
def rewriting(initiator, generator, repeat):
com = initiator
for i in range(repeat):
for rule in generator:
str1 = rule[0]
str2 = rule[1]
com = com.replace(str1, str2)
return com
# ここから定義
state = [[4, 1], np.pi/2] # 亀の初期状態 出発点とはじめの向き
initiator = "A"
generator = {"F":"FF","A":"F-[[A]+A]+F[+FA]-A"}
angle = 22.5 / 180 * np.pi # 回転角 度数法 -> 弧度法
drawcolor = 'green'
repeat = 4
distance = 2*(1/2)**repeat # 1歩の長さ。ものによって変える
# 定義はここまで
stack = []
com = initiator
for i in range(repeat):
com = com.translate(str.maketrans(generator))
for command in com:
state = turtle(command, state)
#plt.savefig("Lsystem2.png")
plt.show()
この後,さらに,確率を導入した L-Systems ,3Dの L-Systems と続くが,ここでちょっと一休み。
さきに知りたい人は,CindyScriptで構築した L-Sytems についてのページがあるので,それを読んでPythonに移植されるとよいだろう。