著者:飯尾 淳
本連載では「Pythonを昔から使っているものの、それほど使いこなしてはいない」という筆者が、いろいろな日常業務をPythonで処理することで、立派な「蛇使い」に育つことを目指します。その過程を温かく見守ってください。皆さんと共に勉強していきましょう。第4回は、フラクタル図形の一種である「ヒルベルト曲線」を描く方法を解説します。
シェルスクリプトマガジン Vol.74は以下のリンク先でご購入できます。![]()
![]()
図1 1次のヒルベルト曲線を描くPythonコード
#!/usr/bin/env python
from turtle import *
SIZE = 300
p = [[-0.5, 0.5], [-0.5, -0.5], [0.5, -0.5], [0.5, 0.5]]
def screen_pos(x):
return (x[0]*SIZE, x[1]*SIZE)
setup(width = 2*SIZE, height = 2*SIZE)
color('red')
width(5)
hideturtle()
for x in p:
goto(screen_pos(x))
mainloop()
図4 1 次のヒルベルト曲線を描くPythonコードの改良版
#!/usr/bin/env python
from turtle import *
SIZE = 300
penup_flag = True
p = [[-0.5, 0.5], [-0.5, -0.5], [0.5, -0.5], [0.5, 0.5]]
def screen_pos(x):
return (x[0]*SIZE, x[1]*SIZE)
setup(width = 2*SIZE, height = 2*SIZE)
color('red')
width(5)
hideturtle()
penup()
for x in p:
goto(screen_pos(x))
if penup_flag:
pendown()
penup_flag = False
onkey(exit, 'q')
listen()
mainloop()
図7 2 次のヒルベルト曲線を描くPythonコード
#!/usr/bin/env python
from turtle import *
SIZE = 300
penup_flag = True
tm = [
[ [0.0, -0.5, -0.5], [-0.5, 0.0, 0.5], [0.0, 0.0, 1.0] ],
[ [0.5, 0.0, -0.5], [ 0.0, 0.5, -0.5], [0.0, 0.0, 1.0] ],
[ [0.5, 0.0, 0.5], [ 0.0, 0.5, -0.5], [0.0, 0.0, 1.0] ],
[ [0.0, 0.5, 0.5], [ 0.5, 0.0, 0.5], [0.0, 0.0, 1.0] ]
]
e = [ [ 1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0] ]
p = [ [-0.5, 0.5], [-0.5, -0.5], [0.5, -0.5], [0.5, 0.5] ]
def affine_transform(m, p):
return [ m[0][0] * p[0] + m[0][1] * p[1] + m[0][2],
m[1][0] * p[0] + m[1][1] * p[1] + m[1][2] ]
def mat_mul(m0, m1):
return [ [m0[0][0]*m1[0][0]+m0[0][1]*m1[1][0]+m0[0][2]*m1[2][0],
m0[0][0]*m1[0][1]+m0[0][1]*m1[1][1]+m0[0][2]*m1[2][1],
m0[0][0]*m1[0][2]+m0[0][1]*m1[1][2]+m0[0][2]*m1[2][2]],
[m0[1][0]*m1[0][0]+m0[1][1]*m1[1][0]+m0[1][2]*m1[2][0],
m0[1][0]*m1[0][1]+m0[1][1]*m1[1][1]+m0[1][2]*m1[2][1],
m0[1][0]*m1[0][2]+m0[1][1]*m1[1][2]+m0[1][2]*m1[2][2]],
[m0[2][0]*m1[0][0]+m0[2][1]*m1[1][0]+m0[2][2]*m1[2][0],
m0[2][0]*m1[0][1]+m0[2][1]*m1[1][1]+m0[2][2]*m1[2][1],
m0[2][0]*m1[0][2]+m0[2][1]*m1[1][2]+m0[2][2]*m1[2][2]] ]
def screen_pos(x):
return (x[0]*SIZE, x[1]*SIZE)
setup(width = 2*SIZE, height = 2*SIZE)
color('red')
width(5)
hideturtle()
penup()
for m in tm:
p2 = [ affine_transform(mat_mul(e, m), x) for x in p ]
for x in p2:
goto(screen_pos(x))
if penup_flag:
pendown()
penup_flag = False
onkey(exit, 'q')
listen()
mainloop()
図9 書き換えた2 次のヒルベルト曲線を描くPythonコード
#!/usr/bin/env python
from turtle import *
import numpy as np
SIZE = 300
penup_flag = True
tm = [ np.matrix(x) for x in [
[ [0.0, -0.5, -0.5], [-0.5, 0.0, 0.5], [0.0, 0.0, 1.0] ],
[ [0.5, 0.0, -0.5], [ 0.0, 0.5, -0.5], [0.0, 0.0, 1.0] ],
[ [0.5, 0.0, 0.5], [ 0.0, 0.5, -0.5], [0.0, 0.0, 1.0] ],
[ [0.0, 0.5, 0.5], [ 0.5, 0.0, 0.5], [0.0, 0.0, 1.0] ] ]
]
e = np.eye(3) # 3次の単位行列
p = [ np.matrix(x).T for x in [
[-0.5, 0.5, 1.0], [-0.5, -0.5, 1.0],
[ 0.5, -0.5, 1.0], [ 0.5, 0.5, 1.0] ] ]
def screen_pos(x):
return (x[0,0]*SIZE, x[1,0]*SIZE)
setup(width = 2*SIZE, height = 2*SIZE)
color('red', 'yellow')
width(5)
hideturtle()
penup()
for m in tm:
for x in [ e * m * x for x in p ]:
goto(screen_pos(x))
if penup_flag:
pendown()
penup_flag = False
onkey(exit, 'q')
listen()
mainloop()
図10 図9 の赤枠部分を置き換えるPythonコード
def hilbert(n, m):
if n > 1:
for m_tm in tm: hilbert(n-1, m * m_tm)
else:
for x in [ m * x.T for x in p ]:
goto(screen_pos(x))
global penup_flag
if penup_flag:
pendown()
penup_flag = False
hilbert(3, e)
図13 1 ~ 7 次のヒルベルト曲線を重ねて描画するPythonコード
#!/usr/bin/env python
from turtle import *
import numpy as np
SIZE = 300
MARGIN = 50
penup_flag = True
settings = [
{ 'color': 'forestgreen', 'width': 5 },
{ 'color': 'navy', 'width': 4 },
{ 'color': 'purple', 'width': 3 },
{ 'color': 'brown', 'width': 2 },
{ 'color': 'red', 'width': 2 },
{ 'color': 'orange', 'width': 1 },
{ 'color': 'yellowgreen', 'width': 1 }
]
tm = [ np.matrix(x) for x in [
[ [0.0, -0.5, -0.5], [-0.5, 0.0, 0.5], [0.0, 0.0, 1.0] ],
[ [0.5, 0.0, -0.5], [ 0.0, 0.5, -0.5], [0.0, 0.0, 1.0] ],
[ [0.5, 0.0, 0.5], [ 0.0, 0.5, -0.5], [0.0, 0.0, 1.0] ],
[ [0.0, 0.5, 0.5], [ 0.5, 0.0, 0.5], [0.0, 0.0, 1.0] ] ] ]
e = np.eye(3)
p = [ np.matrix(x).T for x in [
[-0.5, 0.5, 1.0], [-0.5, -0.5, 1.0],
[ 0.5, -0.5, 1.0], [ 0.5, 0.5, 1.0] ] ]
def draw_line_to(x):
goto(x[0,0]*SIZE, x[1,0]*SIZE)
global penup_flag
if penup_flag:
pendown()
penup_flag = False
def reset():
penup()
global penup_flag
penup_flag = True
def hilbert(n, m):
if n > 1:
for m_ in tm: hilbert(n-1, m * m_)
else:
for q in [ m * p_ for p_ in p ]: draw_line_to(q)
setup(width = 2*SIZE+MARGIN, height = 2*SIZE+MARGIN)
hideturtle()
for i in range(len(settings)):
reset()
color(settings[i]['color'], 'white')
width(settings[i]['width'])
hilbert(i+1, e)
onkey(exit, 'q')
listen()
mainloop()