"BOKU"のITな日常

還暦越えの文系システムエンジニアの”BOKU”は新しいことが大好きです。

Pythonでグラフィックス:「雪の結晶風図形」(コッホ曲線)を描くクラスを実装してみました。

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

これも、C言語のプログラムで絶版の「TurboC グラフィックス入門」という本で紹介されていたものを、Pythonで書き直したものです。

 

まずは仕様から

 

 パラメータは一つだけ。

r0という適当な名前ですけど、ご容赦を。

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

 このパラメータを変更して複数個実行すると、同心円的に図形が重なります。

 

ソースコードと補足説明です

 

Snowクラスのソースです。

gr_snow.py

import numpy as np

class Snow():

    def __init__(self):
        self.tx = []
        self.ty = []

    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=1000.0):
        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 turn(self, angle, chg_angle):
    ang = angle + chg_angle
    ang2 = ang - ang + ang % 360.0
    return ang2

です。

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

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

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

 です。

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

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

def koch(self, leng, lpx, lpy, angle):

 です。

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

if(leng <= 60.0):
    px, py = self.move(leng, px, py, ang)

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

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

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)

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

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

def draw(self, r0=1000.0):

 です。

ここでは、-120度ずつ角度を変えながら、「koch()」を3回呼び出します。

for i in range(3):
    sx, sy = self.koch(leng, sx, sy, angle)
    angle = self.turn(angle, -120.0)

こうすることで描ける図形はこんな感じです。

f:id:arakan_no_boku:20181203201801j:plain

なんとなく、雪の結晶っぽく・・見えないこともないです。

 

Snow()クラスの使い方です

 

上記の1個の図形を描く場合のソースです。

gr_snow_do.py

import matplotlib.pyplot as plt
import gr_snow as snow

sp1 = snow.Snow()
tx1, ty1 = sp1.draw()
plt.plot(tx1, ty1)

plt.title('コッホ関数')
plt.show()

Snow() のオブジェクトを生成して、draw()を実行し、返り値のX・Y座標の行列をプロットするだけです。

なので。

径を大小させながら、複数のSnow()クラスでプロットしてくと、同心円的に重なった図形がかけます。

gr_snow_do.py

import matplotlib.pyplot as plt
import gr_snow as snow

sp1 = snow.Snow()
tx1, ty1 = sp1.draw()
plt.plot(tx1, ty1)

sp2 = snow.Snow()
tx2, ty2 = sp2.draw(r0=700.0)
plt.plot(tx2, ty2)

sp3 = snow.Snow()
tx3, ty3 = sp3.draw(r0=400.0)
plt.plot(tx3, ty3)

plt.title('コッホ関数')
plt.show()

1000.0(デフォルト)、700.0、400.0の3つのSnow()を描画しています。 

結果はこう。

matplotlibが勝手に色を変えてくれるので、ちょっとイイ感じになってます。

f:id:arakan_no_boku:20181203232243j:plain

面白いですねえ。

これも良いおもちゃです。

今回は、こんなところで・・。

ではでは。