"BOKU"のITな日常

BOKUが勉強したり、考えたことを頭の整理を兼ねてまとめてます。

Pythonで「バーンズリーのシダ」っぽいのを描く

f:id:arakan_no_boku:20210912183619p:plain

目次

Pythonで「バーンズリーのシダ」っぽいのを描く

今回は、Pythonで「バーンズリーのシダ」を描画してみます。

バーンズリーのシダは有名なフラクタル図形です。

フラクタルは、全体と一部が自己相似(つまり、全体を見ても、それを構成する一部を見ても同じ形状が現れる)になっている図形です。

今回は、それを`Pythonとmatplotlibのグラフ描画機能を使って描きます。

ただ、散布図グラフを使い、ドットで描く実装にしたせいか、できあがった図をみるとたしかに「シダ」なんですけど、フラクタルっぽくはないです。

なので、「バーンズリーのシダ」っぽい・・としてます(笑)

 

ソースコード

ソースコードです。

class_graphics.py

from matplotlib import pyplot as plt
import random


class Fern():
    def __init__(self):
        self.__tx = [0]
        self.__ty = [0]
        self.__title = "バーンズリーのシダっぽいの"

    def __tran_1(self, p):
        x = p[0]
        y = p[1]
        x1 = 0.85 * x + 0.04 * y
        y1 = -0.04 * x + 0.85 * y + 1.6
        return x1, y1

    def __tran_2(self, p):
        x = p[0]
        y = p[1]
        x1 = 0.2 * x - 0.26 * y
        y1 = 0.23 * x + 0.22 * y + 1.6
        return x1, y1

    def __tran_3(self, p):
        x = p[0]
        y = p[1]
        x1 = -0.15 * x + 0.28 * y
        y1 = 0.26 * x + 0.24 * y + 0.44
        return x1, y1

    def __tran_4(self, p):
        # x = p[0]
        y = p[1]
        x1 = 0
        y1 = 0.16 * y
        return x1, y1

    def __get_index(self):
        prob = [0.85, 0.07, 0.07, 0.01]
        r = random.random()
        c = 0
        sump = []
        for p in prob:
            c += p
            sump.append(c)
        for item, sp in enumerate(sump):
            if(r <= sp):
                return item
        return len(prob) - 1

    def __tran(self, p):
        trans = [self.__tran_1, self.__tran_2, self.__tran_3, self.__tran_4]
        tindex = self.__get_index()
        t = trans[tindex]
        x, y = t(p)
        return x, y

    def __draw(self, n):
        x1 = 0
        y1 = 0
        for i in range(n):
            x1, y1 = self.__tran((x1, y1))
            self.__tx.append(x1)
            self.__ty.append(y1)
        return self.__tx, self.__ty

    def draw(self, n=10000):
        x, y = self.__draw(n)
        plt.plot(x, y, '.')
        plt.title(self.__title)
        plt.show()

 

ソースコードの補足説明

メインの処理は「__draw()」です。

 def __draw(selfn):
        x1 = 0
        y1 = 0
        for i in range(n):
            x1, y1 = self.__tran*1
            self.__tx.append(x1)
            self.__ty.append(y1)
        return self.__tx, self.__ty

座標を示す「x1、y1」を更新しながら、「n」で指定した回数繰り返します。

nは、デフォルトで10000回にしてます。

ここの数字を変えると、図形の密度が変わります。

 

x1,y1を更新する処理は「__tran()」です。

    def __tran(selfp):
        trans = [self.__tran_1, self.__tran_2, self.__tran_3, self.__tran_4]
        tindex = self.__get_index()
        t = trans[tindex]
        x, y = t(p)
        return x, y

pというタプルで(x1,y1)を受け取り,それを引数にして「tran_1」「tran_2」「tran_3」「tran_4」のいずれかのメソッドをよびだします。

 

どのメソッドを呼び出すかを決めるのは「__get_index()」です。

    def __get_index(self):
        prob = [0.850.070.070.01]
        r = random.random()
        c = 0
        sump = []
        for p in prob:
            c += p
            sump.append(c)
        for item, sp in enumerate(sump):
            if(r <= sp):
                return item
        return len(prob) - 1

prob=[0.85,0.07,0.07,0.01]のリストを順番に加算した値と、乱数を比較して、乱数が合計値よりも小さかったら、そのインデックスを返します。

この数字を変更すると、できあがる図形の形が変わります。

シダの葉っぱのような図形にするには、この値が適切といわれてます。

 

入力された「x1とy1」の変換の仕方が4パターンあります。

1パターン目

x1 = 0.85*x + 0.04*y

y1 = -0.04*x + 0.85*y + 1.6

2パターン目

x1 = 0.2*x - 0.26*y

y1 = 0.23*x + 0.22*y + 1.6

3パターン目

x1 = -0.15*x + 0.28*y

y1 = 0.26*x + 0.24*y + 0.44

4パターン目

x1 = 0

y1 = 0.16*y

このパターンをランダムに繰り返していくことで、図形を描き出していくわけです。 

 

実行して描かれたイメージ

ソースはこんな感じです。

from class_graphics import Fern

Fern().draw()

 

デフォルトで実行すると、こういう図形が描けます。

f:id:arakan_no_boku:20220412232354p:plain

 

 

ただ、点をうつことで描画しているので、回数を減らして1000回くらいにすると。

from class_graphics import Fern

Fern().draw(1000)

f:id:arakan_no_boku:20220412232531p:plain

上記のように、スカスカになります(笑)

 

おまけの情報

こちらの記事と見比べてもらうとわかると思うのですが.

arakan-pgm-ai.hatenablog.com

今回の「シダの葉」を描くプログラムと、「シェルピンスキーのギャスケット」を描くプログラムは、x1・y1の変換する部分の比率と、__get_index()のリストの数値が違うだけで、ほぼ、構造は同じです。

なので。

この構造のままで、いろいろと数値を変えrると他の図形も描けそうだなと、思ってはいますが試してはいません。。

いちおう、参考情報です。

ではでは。

*1:x1, y1