"BOKU"のITな日常

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

Pythonでグラフィックス:「雪の結晶風図形」(コッホ曲線)を描くクラス

f:id:arakan_no_boku:20210912183619p:plain

目次

雪の結晶風図形(コッホ曲線)

今回は、Pythonで「雪の結晶風図形」(コッホ曲線)を描くクラスを実装してみます。

再帰図形です。

描いた結果はこんな感じになります。

f:id:arakan_no_boku:20210918221707p:plain

図形を描くソースコード

クラスのソースです。

グラフの描画機能を使うため、matplotlibは必要です。

タイトルを日本語にしているので、日本語フォントも必要です。

arakan-pgm-ai.hatenablog.com

あと、内部で計算にNumpyを使ってます。

class_graphics.py

from matplotlib import pyplot as plt
import numpy as np


class KochCurve():

    def __init__(self):
        self.__tx = []
        self.__ty = []
        self.__title = "コッホ曲線(雪の結晶風図形)を描く"

    def __move(self, leng, lpx, lpy, angle):
        x = lpx + leng * np.cos(angle * np.pi / 180.0)
        y = lpy - leng * np.sin(angle * np.pi / 180.0)
        self.__tx.append(x)
        self.__ty.append(y)
        return x, y

    def __turn(self, angle, chg_angle):
        ang = angle + chg_angle
        ang2 = ang - ang + ang % 360.0
        return ang2

    def __koch(self, leng, lpx, lpy, angle):
        leng_t = 0.0
        px = lpx
        py = lpy
        ang = angle
        if(leng <= 60.0):
            px, py = self.__move(leng, px, py, ang)
        else:
            leng_t = leng / 3.0
            px, py = self.__koch(leng_t, px, py, ang)
            ang = self.__turn(ang, 60.0)
            px, py = self.__koch(leng_t, px, py, ang)
            ang = self.__turn(ang, -120.0)
            px, py = self.__koch(leng_t, px, py, ang)
            ang = self.__turn(ang, 60.0)
            px, py = self.__koch(leng_t, px, py, ang)
        return px, py

    def __draw(self, r0):
        x0 = 0.0
        y0 = 0.0
        sx = x0 - r0 * np.sqrt(3.0) / 2
        sy = y0 - r0 / 2
        self.__tx.append(sx)
        self.__ty.append(sy)
        leng = r0 * np.sqrt(3.0)
        angle = 0.0
        for i in range(3):
            sx, sy = self.__koch(leng, sx, sy, angle)
            angle = self.__turn(angle, -120.0)
        return self.__tx, self.__ty

    def draw(self, r0=1000.0):
        tx1, ty1 = self.__draw(r0)

        plt.plot(tx1, ty1)
        plt.title(self.__title)
        plt.show()

 

ソースコードの補足説明

タートルグラフィックという手法です。

亀がある地点で、くるっと方向を変えて、指定の距離だけ移動する。

その地点に到達したら、そこでまた方向を変えて、移動する。

そんな動きを繰り返すことで、なんらかの図形が描かれる・・という考え方です。

その「くるっと方向を変える」という部分が

    def __turn(selfanglechg_angle):
        ang = angle + chg_angle
        ang2 = ang - ang + ang % 360.0
        return ang2

です。

360度の中で、現在が「angle」度の方向を向いているのを「chg_angle」度だけターンするという働きです。

 

「指定の距離だけ移動する」部分が、 

    def __move(selflenglpxlpyangle):
        x = lpx + leng * np.cos(angle * np.pi / 180.0)
        y = lpy - leng * np.sin(angle * np.pi / 180.0)
        self.__tx.append(x)
        self.__ty.append(y)
        return x, y

 です。

 

今いる地点(lpx、lpy)から、指定距離(leng)分、今向いている角度(angle)の方向へ移動して、新しい移動地点を記録してます。

 

これを使って「コッホ曲線」を描く部分が、

    def __koch(selflenglpxlpyangle):
        leng_t = 0.0
        px = lpx
        py = lpy
        ang = angle
        if(leng <= 60.0):
            px, py = self.__move(leng, px, py, ang)
        else:
            leng_t = leng / 3.0
            px, py = self.__koch(leng_t, px, py, ang)
            ang = self.__turn(ang, 60.0)
            px, py = self.__koch(leng_t, px, py, ang)
            ang = self.__turn(ang, -120.0)
            px, py = self.__koch(leng_t, px, py, ang)
            ang = self.__turn(ang, 60.0)
            px, py = self.__koch(leng_t, px, py, ang)
        return px, py

 です。

指定距離が「60.0」以下になったら、指定距離へ移動します。

そうでない場合は、指定距離を三分の一にして、60度ターンして「__koch()」を呼出し、-120度ターンして「__koch()」を呼出し、さらに60度ターンして「__koch()」を呼び出すということをしてます。

再帰構造というやつです。

この動きで、円の三分の一分の「コッホ曲線」が描けます。

 

それを円形に配置して雪の結晶っぽくするのが

    def __draw(selfr0):
        x0 = 0.0
        y0 = 0.0
        sx = x0 - r0 * np.sqrt(3.0) / 2
        sy = y0 - r0 / 2
        self.__tx.append(sx)
        self.__ty.append(sy)
        leng = r0 * np.sqrt(3.0)
        angle = 0.0
        for i in range(3):
            sx, sy = self.__koch(leng, sx, sy, angle)
            angle = self.__turn(angle, -120.0)
        return self.__tx, self.__ty

です。

-120度ずつ角度を変えながら、「__koch()」を3回呼び出しています。

実行してみます

実行するのはこうします。

from class_graphics import KochCurve

KochCurve().draw()

 パラメータ「r0」があります。

r0 直径です。大きな径になるほど、複雑な図形になります。

 デフォルト(1000.0)で実行すると。

f:id:arakan_no_boku:20210918221707p:plain

です。

ただ、1000より増やしても、あまり変化がありません。

引数の数字少ない方向に変えていきます。

KochCurve().draw(500)

500だと、こうなります。

f:id:arakan_no_boku:20210918223501p:plain

200だと

KochCurve().draw(200)

f:id:arakan_no_boku:20210918223735p:plain

こんな感じです。

ではでは。