見出し画像

PythonでL-Systemsを作る(5) 植物の成長システム

 今回で,2次元の L-Sytems の構築はひとまず完成する。
 今までのコマンドに,現在の状態を記憶する [ と,記憶したものを取り出す ] を追加する。これは,スタックという構造になっていて,複数のものを記憶すると,取り出すときは,最後に記憶したものを取り出すようになっている。

 これを用いると「枝分かれ」ができる。次の図が植物の枝分かれを模した図だ。(The Algorithmic Beauty of Plants)

画像1

下から上に進む。回転角は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歩の長さを縮小している。

画像2

最後に,でき上がった 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()

画像3

この後,さらに,確率を導入した L-Systems ,3Dの L-Systems と続くが,ここでちょっと一休み。
さきに知りたい人は,CindyScriptで構築した L-Sytems についてのページがあるので,それを読んでPythonに移植されるとよいだろう。