今回は、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)
こうすることで描ける図形はこんな感じです。
なんとなく、雪の結晶っぽく・・見えないこともないです。
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が勝手に色を変えてくれるので、ちょっとイイ感じになってます。
面白いですねえ。
これも良いおもちゃです。
今回は、こんなところで・・。
ではでは。