morikomorou’s blog

自分が学んだことなどの備忘録的なやつ

【python】matplotlibでフラクタル図形(フラクタルツリー)を描く

フラクタルツリーを描いてみる

前回はフラクタル図形の一つであるコッホ曲線を描きました。
【python】matplotlibでフラクタル図形(コッホ曲線)を描く - morikomorou’s blog
今回はフラクタルで木を描いてみます。

フラクタルツリーについて

下図に示すものがフラクタルツリー(H木)と呼ばれるものです。

複雑に見えますが、これも簡単なルールに基づいて直線をひくだけです。
以下に、木が出来ていく過程を示します。



なんとなくルールがわかりましたか?

木の作成手順

始点aから長さlの直線を角度αだけ傾けてプロットします。
次は、その直線の終点bを始点として長さl×減衰比rの直線を角度α±θ傾けてプロットします。
その次は点c, dを起点に長さl×r×rの直線をプロットしていきます。
これをどんどん繰り返していくことで完成です。

実際に描いてみる

それでは実装していきましょう。
これも繰り返しの処理なので以下の記事で紹介した再帰関数が、便利です。


実装コードは下記です。

import math
import matplotlib.pyplot as plt

n = 10    # 木の深さ
a = (0.0, 0.0)  # 始点のx, y座標
alpha = math.pi * 90 / 180
theta = math.pi * 20 / 180
init_length = 10
rate = 0.7

def tree(a, alpha, deepness):
    length = init_length * rate ** (n - deepness)
    if deepness:
        b = (a[0] + math.cos(angle) * length, a[1] + math.sin(angle) * length)
        plt.plot([a[0], b[0]], [a[1], b[1]], color='saddlebrown')
        tree(b, angle - th, deepness - 1)
        tree(b, angle + th, deepness - 1)
    else:
        return

plt.figure()
tree(a, init_angle, n)
plt.axis('equal')
plt.savefig('HTree1.png')


グラフを木っぽくする

なんかあんまり木っぽくないので少し調整します。

線幅の指定

木は先端の枝に行くにつれて細くなっていきますよね?
線幅にも減衰比をつかって、深さごとに線幅を細くしていってみましょう。

init_width = 15

def tree(a, alpha, deepness):
    length = init_length * rate ** (n - deepness)
    width = init_width * rate ** (n - deepness)
    if deepness:
        b = (a[0] + math.cos(angle) * length, a[1] + math.sin(angle) * length)
        plt.plot([a[0], b[0]], [a[1], b[1]], color='saddlebrown', linewidth=width)
        tree(b, angle - th, deepness - 1)
        tree(b, angle + th, deepness - 1)
    else:
        return

plt.figure()
tree(a, init_angle, n)
plt.axis('equal')
plt.savefig('HTree1_2.png')

どうでしょうか?
多少木っぽくなった気がします。

角度を変えてみる

続いて枝をはやす角度を変えてみましょう。
↓この部分をかえるだけです。

theta = math.pi * 30 / 180     # 20 -> 30

30度の時

45度の時

60度の時

オシャレな木が出来ました 笑
私は角度30度のやつがファンタジックな木でお気に入りです。

色を変えてみる

最後にカラーマップをつかって深さごとに色を変えてグラデーションを作ってみましょう。

import matplotlib.cm as cm
def tree(a, angle, deepness):
    length = init_length * rate ** (n - deepness)
    width = init_width * rate ** (n - deepness)
    if deepness:
        b = (a[0] + math.cos(angle) * length, a[1] + math.sin(angle) * length)
        plt.plot([a[0], b[0]], [a[1], b[1]], color=cm.summer(deepness/n), linewidth=width)
        tree(b, angle - th, deepness - 1)
        tree(b, angle + th, deepness - 1)
    else:
        return

summerというカラーマップを使ってみました。

カラーマップはmatplotlibの公式ドキュメントにいろいろ載ってますので好きなものを使ってみてください。
https://matplotlib.org/stable/tutorials/colors/colormaps.html

おわりに

今回は左右対称&減衰比一定で枝を伸ばしましたが、そこにランダム要素を加えるとリアルワールドの木みたいになるんじゃないでしょうか。
色々試して見てください。