第5章 5.1 複素数の収束計算で描く集合 【リスト5-1-1】 turtleモジュールによるマンデルブロ集合 import turtle import time #■マンデルブロ集合をTurtleグラフィックスで描く #■繰返し演算 # C  : 漸化式 Zn = Zn^2+C のC # LOOP  : 収れん判定のための繰返し回数 # 戻り値 : 収れん回数/発散検出回数 #      0 のとき,LOOP回数では収れん/発散を判定できなかったことを示す。 def Iter(C, LOOP): Z=complex(0) for i in range(1,LOOP+1): # 以下を ZZ=Z*Z*Z+C としても興味深い画像になる ZZ=Z*Z+C if(abs(ZZ)>=4) : return -i if(abs(ZZ-Z)<0.001): return i Z=ZZ return 0 #■マンデルブロー集合描画 # t : タートル・モジュールのペンオブジェクト # DX : 点の刻み幅 # X0 : 実数部の計算開始位置 # Y0 : 虚数部の計算開始位置 # XE : 実数部の計算終了位置 # YE : 虚数部の計算終了位置 # LOOP : 計算回数 # MD : 表示時のModulus def drawFrac(t, DX,X0,Y0, XE,YE, LOOP,MD): t.up(); X=X0 while X<=XE: Y=Y0 while Y<=YE: #繰返し回数による色の選定 k=Iter(complex(X,Y),LOOP) if k>=0: #CL=Ctab1[k % MD] XX=int((X-X0)/DX+0.5)-300 YY=int((Y-Y0)/DX+0.5)-50 t.goto(XX,YY); t.dot(2,'blue') #Y増分 Y+=DX #X増分 X+=DX #■メイン処理 t=turtle.Pen() t.screen.title("Mandelbrot set ") t.speed(0) #タートル移動を速くする t.screen.tracer(10000,0) #描く順序を見たいとき第1引数を小さくする t.hideturtle()#タートルを見えなくする t1=timer.clock() drawFrac(t,0.01,-2,-1.5,1,1.5,200,5) #描画開始 t2=timer.clock(); print(t2-t1) t.screen.tracer(1,0) t.screen.title("Mandelbrot set drawing finish ") t.screen.mainloop() 【リスト5-1-2】 tkinterモジュールによるマンデルブロ集合 from tkinter import * import time #■配列宣言 # N1,N2 : 配列の大きさ # 戻り値 : ゼロ設定された配列(A[N1][N2]を宣言したこととなる) # 実装的には[N2個のリスト,N2個のリスト,・・・]となる。 def array2(N1,N2): return [[0 for j in range(N2)]for k in range(N1)] #■画素初期設定(画素をピクセル1の正方形で代表させる) # canvas : Canvasクラスオブジェクト # NX : X方向最大数+1を指定すること # NY : Y方向最大数+1を指定すること # 戻り値 : 画素IDの配列 def initDt(canvas, NX,NY): A=array2(NX,NY) for i in range(NX): for j in range(NY): A[i][j]=canvas.create_rectangle(i,j,i,j,fill='white', outline='') return A #■Tk初期設定 # Title : 最初に表示するウィンドウのタイトル # 戻り値 : Tkクラスオブジェクト def initTk(Title): tk=Tk(); tk.title("Mandelbrot"); tk.resizable(0,0) return tk #■キャンバス初期設定 # tk : Tkクラスオブジェクト # W : キャンバスの幅 # H : キャンバスの高さ # 戻り値 : Canvasクラスオブジェクト def initCanvas(tk, W,H): canvas=Canvas(tk,width=W,height=H,highlightthickness=0) canvas.pack() return canvas #■繰返し演算 # C  : 漸化式 Zn = Zn^2+C のC # LOOP  : 収れん判定のための繰返し回数 # 戻り値 : 収れん回数/発散検出回数 #      0 のとき,LOOP回数では収れん/発散を判定できなかったことを示す。 def Iter(C, LOOP): Z=complex(0) for i in range(1,LOOP+1): # 以下を ZZ=Z*Z*Z+C としても興味深い画像になる ZZ=Z*Z+C if(abs(ZZ)>=4) : return -i if(abs(ZZ-Z)<0.001): return i Z=ZZ return 0 #■マンデルブロー集合描画 # canvas : 描画用キャンバス # CID[][] : 描画用の点識別番号配列 # DX : 点の刻み幅 # X0 : 実数部の計算開始位置 # Y0 : 虚数部の計算開始位置 # XE : 実数部の計算終了位置 # YE : 虚数部の計算終了位置 # LOOP : 計算回数 # MD : 表示時のModulus def drawFrac(canvas, CID,DX,X0,Y0, XE,YE, LOOP,MD): X=X0; XMAX=len(CID); YMAX=len(CID[0]) while X<=XE: #実行途上表示のときコメントを外す #if int(X/DX)%5==0: tk.update() Y=Y0 while Y<=YE: #繰返し回数による色の選定 k=Iter(complex(X,Y),LOOP) if k>=0: XX=int((X-X0)/DX+0.5);YY=int((Y-Y0)/DX+0.5) if XX>=0 and XX=0 and YY=4) : return -i if(abs(ZZ-Z)<0.001): return i Z=ZZ return 0 def drawFrac(t, DX,X0,Y0, XE,YE, LOOP,MD): t.up(); X=X0 # Cの値を変えて実行してみよう C=complex(0.3,0.5) while X<=XE: #実行途上表示のとき以下2行のコメントを外す #if int(X/DX)%5==0: # t.screen.title("Filled Julia Set Drawing X= %8.4f" %X) Y=Y0 while Y<=YE: #繰返し回数による色の選定 k=Iter(complex(X,Y),C,LOOP) if k>=0: XX=int((X-X0)/DX+0.5)-300; YY=int((-Y-Y0)/DX+0.5)-50 t.goto(XX,YY); t.dot(2,"blue") Y+=DX #Y増分 X+=DX #X増分 【リスト5-2-2】 ジュリア集合(tkinterモジュール使用の場合) 関数Iterは共通 def drawFrac(canvas, CID,DX,X0,Y0, XE,YE, LOOP,MD): X=X0; XMAX=len(CID); YMAX=len(CID[0]) # Cの値を変えて実行してみよう C=complex(0.2695,0.0056) while X<=XE: #実行途上表示のとき以下のコメントを外す #if int(X/DX)%5==0: tk.update() Y=Y0 while Y<=YE: #繰返し回数による色の選定 k=Iter(complex(X,Y),C,LOOP) if k>=0: XX=int((X-X0)/DX+0.5); YY=int((Y-Y0)/DX+0.5) if XX>=0 and XX=0 and YY=4) : return -i if(abs(ZZ-Z)<0.001): return i Z=ZZ return 0 #■-----以下カラー処理------------------------ cType = 2 #配色のタイプ LOOP=256 #ループ回数 #cType=0のときの配色 exID=("#000077","#0000AA","#0000FF", "#0077FF","#00FFFF","#77FFFF") #発散したときの色 cnID=("#770000","#AA0000","#FF0000", "#FF7700","#FFFF00","#FFFF77") #収斂したときの色 def RGB(r,g,b): return "#%02X%02X%02X" % (r,g,b) #cType=1のときの配色 def setColor(ID): #色変化を自分なりに変えてみよう if ID==0 : return "#000000" if ID<0 :#発散のときの配色 ID=-ID if ID<64 : return RGB(0,ID*4,0) if ID<128: return RGB(ID*4-256,255,0) if ID<192: return RGB (255,767-ID*4,0) return RGB% (255,ID*4-768,0) if ID<64 : return RGB(0,ID*4, 255) if ID<128: return RGB(ID*4-256, 255,255) if ID<192: return RGB(255,767-ID*4, 255) return RGB(255,ID*4-768,255) #cType=2のときの配色 def setCI(V): V=int(V) return RGB(V & 0xFF, (V>>8) & 0xFF, (V>>16) & 0xFF) def setC(ID): global cType, exID, cnID,LOOP if cType==0: if ID==0: return "#000000" if ID>0 : return exID[ID % 6]; return cnID[(-ID) % 6] elif cType==1: return setColor(ID%256) else : return setCI((abs(ID)%4096)*(4096/LOOP)) #------ カラー処理ここまで-------- CST = -2; CED = 2; DST = -2; DED=2 curX=0; curY=0; XST=0; YST=0; SC=1 #■マンデルブロ/ジュリア集合の描画 # canvas : 描画用キャンバス # CID[][] : 描画用の点識別番号配列 # LOOP : 計算回数 # Mandel : マンデルブロ集合のときTrue, # ジュリア集合のときFalse # C : ジュリア集合のときのパラメータ def drawFractal(canvas, CID, LOOP,Mandel=True, C=0): global CST, CED, DST,root,XST,YST, SC if Mandel: msg="Mandelbrot" else: msg="Julia" root.title(msg+ "集合の描画中") #描画用制御変数の設定 XMAX=len(CID); YMAX=len(CID[0]) if CST>CED: C=CST; CST=CED; CED=CC DR=CED-CST; SC = 400/DR;DED = DST + DR; DT = 1.0 / SC XST=200-SC*(CST+CED)/2+0.5; YST=150+SC*(DST+DED)/2+0.5 #描画開始 X=CST; CM0=complex(0) while X<=CED: if int(X/DT)%5==0: root.update() #途中経過表示 Y=DST; while Y=0 and XX=0 and YY 10) :CST -= DC*0.1*XX/AX; if(AY> 10) :DST += DD*0.1*YY/AY; CED=CST+DC; redraw() def LButtonDblClk(event):#拡大(左ボタンダブルクリック) global curX, curY,root, canvas, CID, LOOP, CST, CED, DST, DED x=event.x; y=event.y c= (x-XST)/SC;d=(YST-y)/SC CST=(c+CST*3)/4; CED=(c+CED*3)/4; DST=(d+DST*3)/4 redraw() def RButtonDown(event):#右ボタンダウン global curX, curY; curX=event.x; curY=event.y def RButtonUp(event):#縮小(右ボタンアップ) global curX, curY,root, canvas, CID, LOOP,CST, CED, DST, DED x=event.x; y=event.y XX=x-curX; YY=y-curY if abs(XX)<5 and abs(YY)<5 :return DC = (CED-CST) CST -= DC*0.1; CED += DC*0.1; DST -= DC*0.1; redraw() def RButtonDblClk(event):#配色変更(右ボタンダブルクリック) global cType,root, canvas, CID, LOOP cType+=1 if cType>2: cType=0 redraw() #■実行メイン root=Tk(); root.title("Mandelbrot"); root.resizable(0,0) root.geometry("310x348") val=IntVar(); val.set(0)# ラジオボタン r0=Radiobutton(text='Manderbrot', variable=val,value=0, command=redraw) r0.place(x=0,y=0) r1=Radiobutton(text='Julia', variable=val,value=1, command=redraw) r1.place(x=0,y=20) Label(root,text="ジュリア集合用定数").place(x=100,y=0) textBox1=Text(root,width=8, height=1) textBox1.insert(INSERT, "0.2"); textBox1.place(x=100,y=20) Label(root,text="+").place(x=160,y=17) textBox2=Text(root,width=8, height=1) textBox2.insert(INSERT, "0.5"); textBox2.place(x=175,y=20) Label(root,text="j").place(x=232,y=17) canvas=Canvas(root,width=300,height=300,highlightthickness=0) canvas.place(x=5,y=45) CID=initDt(canvas,301,301) redraw(); root.update() canvas.bind("",LButtonDown) canvas.bind("",LButtonUp) canvas.bind("",LButtonDblClk) canvas.bind("",RButtonDown) canvas.bind("",RButtonUp) canvas.bind("",RButtonDblClk) root.mainloop() 5.3 式を変える 【リスト5-1-1】,【リスト5-1-2】,【リスト5-2-1】,【リスト5-2-2】,【リスト5-3】の関数Iter中の ZZ = Z*Z + C を次のように変更するだけです。 ZZ = Z*Z*Z + C  def Iter(Z0, C, LOOP):   Z=Z0; ML=3   for i in range(1,LOOP+1):   ZZ=Z   for k in range(ML):ZZ*=Z   ZZ+=C   if(abs(ZZ)>=4) : return -i   if(abs(ZZ-Z)<0.001): return i   Z=ZZ   return 0   5.4 複素数演算の絶対値で描く 【リスト5-4】複素数演算の剰余で描く from tkinter import * import time import cmath #■画素初期設定(画素をピクセル1の正方形で代表させる) def initDT(canvas, NX,NY): A=[[0 for j in range(NY)]for k in range(NX)] for i in range(NX): for j in range(NY): A[i][j]=canvas.create_rectangle(i,j,i,j,fill='white', outline='') return A cTab=("#FF0000","#FFFF00","#00FF00", "#0077FF","#00FFFF","#0000FF") fNo=0 def func(Z): global fNo if fNo==0: return Z * Z + (0.3 + 0.5j) elif fNo==1: return Z * Z + 0.3 elif fNo==2: return Z * Z + Z - (0.3 + 0.5j) elif fNo==3: return Z * Z + Z - 0.3 elif fNo==4: return Z * Z * Z + (0.1 + 0.5j) elif fNo==5: return Z * Z * Z + (0.8 + 0.8j) elif fNo==6: return (0.3 + 0.5j)/(Z * Z) elif fNo==7: return (0.3 + 0.5j)/(Z * Z) + Z elif fNo==8: return (0.3 + 0.5j)/(Z * Z) + (0.3 + 0.5j) else : return (0.3 + 0.5j)/(Z * Z)+ Z * Z def drawDT(canvas, CID): global fNo; root.title("No.%2d" % fNo) XMAX=len(CID); YMAX=len(CID[0])#描画用制御変数 DT=0.01; SC = 100; XST=100.5; YST=100.5 Re=-1-DT; C=complex(0.3,0.5) #描画開始 while Re< 1: Re+=DT; Im=-1-DT; while Im< 1: Im+=DT; Z=complex(Re,Im); V = func(Z) XX = int(XST + Z.real *SC) YY = int(YST - Z.imag *SC) if XX>=0 and XX=0 and YY9 : fNo=0 drawDT(canvas, CID) #■実行メイン root=Tk(); root.title("複素数で描く"); root.resizable(0,0) root.geometry("200x200") canvas=Canvas(root, width=200,height= 200);canvas.pack() CID=initDT(canvas,200,200) drawDT(canvas,CID); root.update() canvas.bind("",LButtonDown) root.mainloop()