「プログラムでシダを描画する」シリーズに便乗して、Pythonでも書いてみました。
- プログラムでシダを描画する - 強火で進め
- 「プログラムでシダを描画する」をDartで描画する - Qiita
- 「プログラムでシダを描画する」をGoで描画する - Qiita
- Clojure - プログラムでシダを描画する - Qiita
出力結果
ソースコード
再帰呼び出し版
- オリジナルと類似の再帰呼び出し版です。
- 関数定義には
lambda
を使ってスッキリ書いてみました。 - 画像ライブラリ PIL(Python Imaging Library) または Pillow が必要です。
import random
N = 20
xm = 0
ym = 0.5
h = 0.6
width = 500
height = 500
W1x = lambda x, y: 0.836 * x + 0.044 * y
W1y = lambda x, y: -0.044 * x + 0.836 * y + 0.169
W2x = lambda x, y: -0.141 * x + 0.302 * y
W2y = lambda x, y: 0.302 * x + 0.141 * y + 0.127
W3x = lambda x, y: 0.141 * x - 0.302 * y
W3y = lambda x, y: 0.302 * x + 0.141 * y + 0.169
W4x = lambda x, y: 0
W4y = lambda x, y: 0.175337 * y
def f(im, k, x, y):
if 0 < k:
f(im, k - 1, W1x(x, y), W1y(x, y))
if random.random() < 0.3:
f(im, k - 1, W2x(x, y), W2y(x, y))
if random.random() < 0.3:
f(im, k - 1, W3x(x, y), W3y(x, y))
if random.random() < 0.3:
f(im, k - 1, W4x(x, y), W4y(x, y))
else:
s = 490
im.putpixel((int(x * s + width * 0.5), int(height - y * s)), (0, 128, 0))
if __name__ == '__main__':
from PIL import Image
im = Image.new("RGB", (width, height), (255, 255, 255))
f(im, N, 0, 0)
im.show()
これでは、関数 f()
で画像関連の引数を渡したり、関数内部に描画ロジックを書く必要があったりして、ちょっと美しくないので、もう少し Pythonic にしてみました。
再帰呼び出し+ジェネレータ版
- ジェネレータを使って、再帰呼び出しの結果を
yield
で返してやるようにしました。 - 描画ロジックは、関数
f()
の外へ追い出すことができました。
import random
N = 20
xm = 0
ym = 0.5
h = 0.6
width = 500
height = 500
W1x = lambda x, y: 0.836 * x + 0.044 * y
W1y = lambda x, y: -0.044 * x + 0.836 * y + 0.169
W2x = lambda x, y: -0.141 * x + 0.302 * y
W2y = lambda x, y: 0.302 * x + 0.141 * y + 0.127
W3x = lambda x, y: 0.141 * x - 0.302 * y
W3y = lambda x, y: 0.302 * x + 0.141 * y + 0.169
W4x = lambda x, y: 0
W4y = lambda x, y: 0.175337 * y
def f(k, x, y):
if 0 < k:
for p in f(k - 1, W1x(x, y), W1y(x, y)):
yield p
if random.random() < 0.3:
for p in f(k - 1, W2x(x, y), W2y(x, y)):
yield p
if random.random() < 0.3:
for p in f(k - 1, W3x(x, y), W3y(x, y)):
yield p
if random.random() < 0.3:
for p in f(k - 1, W4x(x, y), W4y(x, y)):
yield p
else:
s = 490
yield x * s + width * 0.5, height - y * s
if __name__ == '__main__':
from PIL import Image
im = Image.new("RGB", (width, height), (255, 255, 255))
for p in f(N, 0, 0):
im.putpixel((int(p[0]), int(p[1])), (0, 128, 0))
im.show()
まとめ
- 再帰呼び出し+ジェネレータの合わせ技
- 描画ロジックと座標計算ロジックを分離
-
yield
でさらりと書けるジェネレータはお気に入りです。