第10章 10.1 アフィン行列と拡張畑写像   【リスト10-1】拡張畑写像プログラム例 from tkinter import * import math,copy #■データ定義 # # ① DSP=[X0, Y0, SX, SY, width, height] # # X0, Y0 : 表示開始位置 # SX, SY : XY方向スケール # Width, height : キャンバスの幅と高さ # # ② その他 # MT[][]: 計算用パラメータ # CL[0]: キャンバス背景色 # CL[1]: パラメータ表示の際の文字列色 #カエデの葉 MT=((complex(0.36, -0.36),complex(),complex(0.4,0.4)), (complex(0.8),complex(),complex(0.1, 0.04)), (complex(0.36,0.36),complex(),complex(0.3, 0.08)), (complex(0.5),complex(),complex(0.25, 0.4))) DSP=(100,330,300,300, 500, 500); CL=("white", "black"); LOOP=9 #■共役複素数 def conj(Z): return complex(Z.real,-Z.imag) #■RGB値の範囲内に収める def cVal(D): R=int(D*255) if R<0: return 0 if R>255: return 255 return R #■R/G/Bを Python 用色コードに変換 def setColor(C): return "#%02X%02X%02X" % (cVal(C[0]),cVal(C[1]),cVal(C[2])) #■複素数による点描画 def drawPoint(Z, cT): global DSP, canvas X=int(DSP[0]+Z.real*DSP[2]+0.5) Y=int(DSP[1]-Z.imag*DSP[3]+0.5) canvas.create_rectangle(X,Y,X,Y,fill=setColor(cT), outline='') #■描画 def drawRec(N, Z, CC): global MT,root if N==0: drawPoint(Z, CC); return NL=len(MT); m=math.pow(2,-N) for i in range(NL): MD = i % 3; CC[MD]+=m #色分け処理 drawRec(N-1, MT[i][0]*Z+MT[i][1]*conj(Z)+MT[i][2],CC) CC[MD]-=m #■複素数の文字列化 def cmpStr(z): if z.imag>=0:return "%7.4f + %6.4f j" % (z.real, z.imag) return "%7.4f - %6.4f j" % (z.real, abs(z.imag)) #■パラメータの表示 def dspValue(): global MT,DSP canvas.create_text(250,12, #基準表示位置(X0, Y0), XYスケール text=(("X0 = %7.0f Y0 = %7.0f "+ "Scale = (%7.0f, %7.0f)") % (DSP[0], DSP[1], DSP[2], DSP[3])), font=('MS ゴシック',10), fill=CL[1]) NL=len(MT) for i in range(NL): canvas.create_text(250,i*12+30, #計算用パラメータ text=(cmpStr(MT[i][0])+", "+ cmpStr(MT[i][1])+", "+ cmpStr(MT[i][2])), font=('MS ゴシック',10), fill=CL[1]) #■メイン処理 root=Tk(); root.title("畑政義写像") canvas=Canvas(root, width=DSP[4],height=DSP[5],bg=CL[0]); canvas.pack() canvas.delete(ALL); dspValue() drawRec(LOOP,complex(0),[0,0,0]) root.title("描画終了") root.update(); root.mainloop() 【リスト10-2-1】シダの葉(データ定義部分のみ。他は【リスト10-1】と同じ) MT=((complex(0.09),complex(-0.09),complex()), (complex(0.8346, -0.05),complex(),complex(0,0.1690)), (complex(),complex(-0.1420, 0.3020),complex(0,0.1270)), (complex(0.1753,0.1753),complex(),complex(0,0.16))) DSP=(300,460,300,300, 500, 500); CL=("white", "black"); LOOP=9 【リスト10-2-2】立木(データ定義部分のみ。他は【リスト10-1】と同じ) MT=((complex( 0.325),complex(-0.275),complex()), (complex(-0.225),complex( 0.275),complex(0,1)), (complex(0.420, 0.365),complex( 0.040, 0.035),complex(0, 0.6)), (complex(0.445, 0.160),complex( 0.025, 0.010),complex(0, 1.1)), (complex(0.440,-0.265),complex(-0.010, 0.015),complex(0, 1.0)), (complex(0.365,-0.305),complex( 0.055, 0.045),complex(0, 0.7))) DSP=(160,400,150,150, 500, 500); CL=("white", "black"); LOOP=6 【リスト10-2-3】ススキ(データ定義部分のみ。他は【リスト10-1】と同じ) MT=((complex( 0.55),complex(-0.05),complex()), (complex( 0.00),complex(0.50),complex(0, 1.1)), (complex( 0.50, 0.40),complex(),complex(0, 0.6)), (complex( 0.45, 0.15),complex(0.05),complex(0, 1.1))) DSP=(260,400,150,150, 500, 500); CL=("white", "black"); LOOP=7 【リスト10-3-1】アフィン変換等のクラス定義(ファイル名affinTran.py) import math class affine: def __init__(self,Nxx=1, Nxy=0, Nxo=0, Nyx=0, Nyy=1, Nyo=0): self.xx = Nxx; self.yy = Nyy self.xy = Nxy; self.yx = Nyx self.xo = Nxo; self.yo = Nyo def scale(self, a, b=0): if b==0: return affine(self.xx * a, self.xy * a, self.xo * a, self.yx * a, self.yy * a, self.yo * a) else : return affine(self.xx * a, self.xy * a, self.xo * a, self.yx * b, self.yy * b, self.yo * b) def shift(self, a, b): return affine(self.xx, self.xy, self.xo + a, self.yx, self.yy, self.yo + b) def rotate(self, theta): ct = math.cos(theta); st = math.sin(theta); return affine(self.xx * ct - self.yx * st, self.xy * ct - self.yy * st, self.xo * ct - self.yo * st, self.yx * ct + self.xx * st, self.yy * ct + self.xy * st, self.yo * ct + self.xo * st) def __mul__(self, m): return affine(self.xx * m.xx + self.yx * m.xy, self.xy * m.xx + self.yy * m.xy, self.xo * m.xx + self.yo * m.xy + m.xo, self.xx * m.yx + self.yx * m.yy , self.xy * m.yx + self.yy * m.yy , self.xo * m.yx + self.yo * m.yy + m.yo) def det(): return self.xx*self.yy - self.xy*self.yx class coordinate: def __init__(self,x=0, y=0, Left=-0.5,Right=1.5,Bottom=-1,Top=1.5): self.x=x; self.y=y; self.left=Left;self.right=Right self.bottom=Bottom;self.top=Top def transformed(self, p, m): return coordinate(p.x * m.xx + p.y * m.xy + m.xo, p.x * m.yx + p.y * m.yy + m.yo) def scXY(self,SC,X0=0,Y0=0): w=[0 for i in range(2)] w[0]=int(SC*(self.x-self.left)/(self.right-self.left)+0.5+X0) w[1]=int(SC*(self.y-self.top)/(self.bottom-self.top)+0.5+Y0) #print(w[0],w[1], self.x, self.y,self.left,self.top) return w def colorValue(DT): ii=int(255*DT) if ii<0: return 0 if ii>255: return 255 return ii 【リスト10-3-2】変換プログラム例 import math from affinTran import * M=affine( 0.94, -0.22, -0.05, 0.21, 0.96, 0.01) def printAff(M): print("\n■アフィン行列\n") print(" [[%+6.4f, %+6.4f, %+6.4f]" % (M.xx, M.xy, M.xo)) print(" [%+6.4f, %+6.4f, %+6.4f]]" % (M.yx, M.yy, M.yo)) #■複素数の文字列化 def cmpStr(z): if z.imag>=0:return "%7.4f + %6.4f j" % (z.real, z.imag) return "%7.4f - %6.4f j" % (z.real, abs(z.imag)) def printCom(C): print("\n■変換用複素数\n") print(cmpStr(C[0])+", "+cmpStr(C[1])+", "+cmpStr(C[2])) def toComplex(M): a=(M.xx+M.yy)/2 ; b=(M.yx-M.xy)/2; e=M.xo c=(M.xx-M.yy)/2 ; d=(M.yx+M.xy)/2; f=M.yo return [complex(a, b), complex(c, d), complex(e,f)] def toAffine(C): return affine(C[0].real+C[1].real, (-C[0].imag+C[1].imag), C[2].real, C[0].imag+C[1].imag, (C[0].real-C[1].real), C[2].imag) printAff(M); C = toComplex(M); printCom(C) M2=toAffine(C); printAff(M2) 10.2 反復系と再帰系   【リスト10-4-1】縮小変換の再帰的呼出し from tkinter import * import math from affinTran import * Nmax=5; SC=300; X0=-40; Y0=-40 S1=1/3; S2=2/3; D1=S1/2; D2=S1+D1 f=(affine().scale(S1).shift(D1, 0), affine().scale(S1).shift(D2, 0), affine().scale(S1).shift(S2, S1), affine().scale(S1).shift(D2, S2), affine().scale(S1).shift(D1, S2), affine().scale(S1).shift(0 , S1)) origin=coordinate() def printf(pstr, f): print(pstr) print(" %12.4f, %12.4f, %12.4f" % (f.xx, f.xy, f.xo)) print(" %12.4f, %12.4f, %12.4f" % (f.yx, f.yy, f.yo)) for i in range(len(f)): printf("%d1" % (i+1), f[i]) def drawTran(t, r, g, b): global origin, canvas, SC, X0, Y0 w =coordinate().transformed(origin,t).scXY(SC, X0, Y0) C="#%02X%02X%02X" % (colorValue(r), colorValue(g), colorValue(b)) canvas.create_rectangle(w[0], w[1],w[0], w[1],fill=C,outline="") def trans(n, t, r, g, b): global f1,f2, f3, Nmax if n>Nmax: drawTran(t, r, g, b) else : m=math.pow(2, -n);rr=r; gg=g; bb=b for i in range(len(f)): im= i% 3; rr=r; gg=g; bb=b if im==0: rr+=m elif im==1: gg+=m else : bb+=m trans(n+1, f[i] * t, rr, gg, bb) def paint(canvas): trans(0,affine(), 0,0,0) root=Tk();root.title("Similarity Transformation") canvas=Canvas(root, width=220,height=198,bg="white"); canvas.pack() paint(canvas) root.update(); root.mainloop() 【リスト10-4-2】反復関数系による縮小写像 from tkinter import * import math, random #randomのimport追加 from affinTran import * SC=300; X0=-40; Y0=-40 S1=1/3; S2=2/3; D1=S1/2; D2=S1+D1 f=(affine().scale(S1).shift(D1, 0), … #アフィン行列は【リスト10-4-1】と同じ ・ ・ ・ cof=[1/6, 2/6,3/6, 4/6,5/6,1.1] #確率と色テーブルは新たに追加 ctab=[[1,0,0],[0,0.5,0],[0,0,1],[0.5,0,0.5],[0,0.5,0.5],[0.5,0.5,0]] origin=coordinate() #以下 def trans の前まで【リスト10-4-1】同じ ・ ・ ・ def trans(): global f1,f2, f3, Nmax t=affine() for i in range(10000): R=random.random(); N=len(cof) for k in range(N): if R<=cof[k]: ID=k; break t*=f[ID] drawTran(t, ctab[ID][0], ctab[ID][1], ctab[ID][2]) def paint(canvas): trans()   #以下,メイン処理は 【リスト10-4-1】同じ ・ ・ ・ 【リスト10-5-1】アンモナイト?(データ定義のみ。他は【リスト10-4-1】と同じ) Nmax=10; SC=200; X0=40; Y0=-40 f=(affine(-0.29, 0.00, 0.59, 0.00, 0.20, -0.32), affine(-0.07, -0.02, 0.79, -0.01, 0.29, -0.06), affine( 0.94, -0.22, -0.05, 0.21, 0.96, 0.01)) 【リスト10-5-2】アンモナイト(データ定義のみ。他は【リスト10-4-2】と同じ) SC=200; X0=40; Y0=-40 f=(affine(-0.29, 0.00, 0.59, 0.00, 0.20, -0.32), affine(-0.07, -0.02, 0.79, -0.01, 0.29, -0.06), affine( 0.94, -0.22, -0.05, 0.21, 0.96, 0.01)) cof=[0.06, 0.08, 1] ctab=[[0.2,0.2,0],[0.5, 0.5,0],[0.3,0.3,0]] 【リスト10-6-1】関数paintのみ(他は【リスト4-4-1】,【リスト4-5-1】と同じ) def paint(canvas): global f ff=affine() for i in range(3): ff*=f[0]; trans(0, ff, 0,0,0) ff=affine() for i in range(1): ff*=f[1]; trans(0, ff, 0,0,0) ff=affine() for i in range(46): ff*=f[2]; trans(0, ff, 0,0,0) 【リスト10-6-2】楕円にするための値の調整。データ定義部分のみ。 (他は【リスト4-4-1】,【リスト4-5-1】,【リスト10-6-1】と同じ) Nmax=7; SC=200; X0=60; Y0=-40 alf=0.21; beta=math.sqrt(1-alf*alf) f=(affine(-0.29, 0.00, 0.59, 0.00, 0.20, -0.32), affine(-0.07, -0.02, 0.79, -0.01, 0.29, -0.06), affine( beta, -alf, -0.05, alf, beta, 0.01)) 10.3 反復関数系への複素数の適用   【リスト10-7】反復関数系に対する複素数適用 from tkinter import * import math,copy, random #■データ定義 # # ① DSP=[X0, Y0, SX, SY, width, height] # # X0, Y0 : 表示開始位置 # SX, SY : XY方向スケール # Width, height : キャンバスの幅と高さ # # ② その他 # MT[][]: 計算用パラメータ # CL[0]: キャンバス背景色 # CL[1]: パラメータ表示の際の文字列色 # cof[]: 累積関数適用比率 # ctab[]: 適用関数別色濃度表 #アンモナイト(反復関数系複素数版) MT=((complex( -0.045),complex(-0.245),complex(0.59, -0.32)), (complex( 0.11, 0.005),complex(-0.18, -0.015),complex(0.79, -0.06)), (complex( 0.95, 0.215),complex(-0.01,-0.005),complex(-0.05, 0.01))) DSP=(260,300,200,200, 500, 500); CL=("white", "black"); LOOP=50000 cof=[0.06, 0.08, 1.1] ctab=[[0.2,0.2,0],[0.5, 0.5,0],[0.3,0.3,0]] #■共役複素数 def conj(Z): return complex(Z.real,-Z.imag) #■RGB値の範囲内に収める def cVal(D): R=int(D*255) if R<0: return 0 if R>255: return 255 return R #■R/G/Bを Python 用色コードに変換 def setColor(C): return "#%02X%02X%02X" % (cVal(C[0]),cVal(C[1]),cVal(C[2])) #■複素数による点描画 def drawPoint(Z, cT): global DSP, canvas X=int(DSP[0]+Z.real*DSP[2]+0.5) Y=int(DSP[1]-Z.imag*DSP[3]+0.5) #print(X,Y) canvas.create_rectangle(X,Y,X,Y,fill=setColor(cT), outline='') #■描画 def drawRecIFS(LOOP): Z=complex(); N=len(cof) for i in range(LOOP): R=random.random() for k in range(N): if R<=cof[k]: ID=k; break Z=MT[ID][0]*Z+MT[ID][1]*conj(Z)+MT[ID][2] drawPoint(Z, ctab[ID]) #■複素数の文字列化 def cmpStr(z): if z.imag>=0:return "%7.4f + %6.4f j" % (z.real, z.imag) return "%7.4f - %6.4f j" % (z.real, abs(z.imag)) #■パラメータの表示 def dspValue(): global MT,DSP canvas.create_text(250,12, #基準表示位置(X0, Y0), XYスケール text=(("X0 = %7.0f Y0 = %7.0f "+ "Scale = (%7.0f, %7.0f)") % (DSP[0], DSP[1], DSP[2], DSP[3])), font=('MS ゴシック',10), fill=CL[1]) NL=len(MT) for i in range(NL): canvas.create_text(250,i*12+30, #計算用パラメータ text=(cmpStr(MT[i][0])+", "+ cmpStr(MT[i][1])+", "+ cmpStr(MT[i][2])), font=('MS ゴシック',10), fill=CL[1]) #■メイン処理 root=Tk(); root.title("複素数による反復関数系") canvas=Canvas(root, width=DSP[4],height=DSP[5],bg=CL[0]) canvas.pack() canvas.delete(ALL) dspValue() drawRecIFS(LOOP) root.update() root.mainloop()