第7章 7.1 ヒルベルト曲線 【リスト7-1-1】 turtleモジュールによるヒルベルト曲線 import turtle #■C曲線をTurtleグラフィックスで描く #相対座標位置への移動 def draw(t,X,Y): P=t.pos(); NX=P[0]+X*0.1; NY=P[1]+Y*0.1;t.goto(NX,NY) def setOrder(order): H = 1; S = 2000; S = S / H for i in range(2, order+1): H=H*2+1 return S/H #ヒルベルト曲線を描く def HilbertCurve(t,order): H=setOrder(order); ID=order RUL(t,ID,H) def RUL(t,ID,H):#右→上→左 if ID>0: ID=ID-1 URD(t,ID,H); draw(t,H,0) ; RUL(t,ID,H); draw(t,0,H) RUL(t,ID,H); draw(t,-H,0); DLU(t,ID,H); ID=ID+1 def DLU(t,ID,H):#下→左→上 if ID>0: ID=ID-1 LDR(t,ID,H); draw(t,0,-H); DLU(t,ID,H); draw(t,-H,0) DLU(t,ID,H); draw(t,0,H) ; RUL(t,ID,H); ID=ID+1 def LDR(t,ID,H):#左→下→右 if ID>0: ID=ID-1 DLU(t,ID,H); draw(t,-H,0); LDR(t,ID,H); draw(t,0,-H) LDR(t,ID,H); draw(t,H,0) ; URD(t,ID,H); ID=ID+1 def URD(t,ID,H):#上→右→下 if ID>0: ID=ID-1 RUL(t,ID,H); draw(t,0,H) ; URD(t,ID,H); draw(t,H,0) URD(t,ID,H); draw(t,0,-H); LDR(t,ID,H); ID=ID+1 #■メイン処理 t=turtle.Pen() t.screen.title("Hilbert Curve used Turtle graphics") t.speed(0) #タートル移動を速くする t.screen.tracer(100000,0) #描く順序を見たいとき第1引数を小さくする t.hideturtle() #タートルを見えなくする t.up(); t.goto(-300,40); t.down() #描画開始位置に移動 t.width(2); t.color('blue') #線幅とカラーを設定 HilbertCurve(t,5) #ヒルベルト曲線描画開始 t.screen.tracer(1,0) t.screen.mainloop() 【リスト7-1-2】 相対的描画曲線(ファイル名をRelativeCurve.pyとする) モジュールtkinterTurtle.pyは【リスト1-3】に示したものです。 from tkinter import * from tkinterTurtle import * #■相対位置まで曲線を描くクラス class RelativeCurve(RelativeDraw): def draw(self,X,Y): NX=self.x+X; NY=self.y+Y self.canvas.create_line(self.screenX(self.x), self.screenY(self.y), self.screenX(NX) , self.screenY(NY) , width = self.lineWidth, fill = self.color) self.x=NX; self.y=NYdef draw(t,X,Y) 【リスト7-1-3】 tkinterモジュールによるヒルベルト曲線 from tkinter import * from RelativeCurve import * #■ヒルベルト曲線のクラス定義(主処理以外はRelativeDrawクラスからの継承) class HilbertCurve(RelativeCurve): #移動量の設定 def setH(self,order): self.ID=order H = 1; S = 2000; S = S / H for i in range(2, order+1): H=H*2+1 self.H=S/H #ヒルベルト曲線を描く def curve(self, order): self.setH(order); self.goto(0,0); self.RUL() #右→上→左 def RUL(self): if self.ID>0: self.ID=self.ID-1 self.URD(); self.draw(self.H,0); self.RUL(); self.draw(0,self.H) self.RUL(); self.draw(-self.H,0);self.DLU(); self.ID=self.ID+1 #下→左→上 def DLU(self): if self.ID>0: self.ID=self.ID-1 self.LDR(); self.draw(0,-self.H);self.DLU(); self.draw(-self.H,0) self.DLU(); self.draw(0,self.H); self.RUL(); self.ID=self.ID+1 #左→下→右 def LDR(self): if self.ID>0: self.ID=self.ID-1 self.DLU(); self.draw(-self.H,0);self.LDR(); self.draw(0,-self.H) self.LDR(); self.draw(self.H,0); self.URD(); self.ID=self.ID+1 #上→右→下 def URD(self): if self.ID>0: self.ID=self.ID-1 self.RUL(); self.draw(0,self.H); self.URD(); self.draw(self.H,0) self.URD(); self.draw(0,-self.H);self.LDR(); self.ID=self.ID+1 #■ tkinter,Canvas共通処理 def initTk(Title): tk=Tk(); tk.title(Title); return tk def initCanvas(W,H): canvas=Canvas(tk,width=600,height=300); canvas.pack() return canvas #■ メイン処理 tk=initTk("Hilbert Curve used tkinter") canvas=initCanvas(600,300) h=HilbertCurve(canvas, 50, 250, 0.1, -0.1, 2, 'blue') h.curve(5) tk.mainloop() 7.2 シェルピンスキ曲線 【リスト7-2-1】 turtleモジュールによるシェルピンスキ曲線 import turtle #相対座標位置への移動 def draw(t,X,Y): P=t.pos(); NX=P[0]+X; NY=P[1]+Y; t.goto(NX,NY) def setOrder(order): H = 6; S = 2000; S = S / H for i in range(2, order+1):H = H * 2 + 2 return S/H #シェルピンスキ曲線を描く def SierpinskiCurve(t,order): H=setOrder(order); ID=order URD(t, ID, H); draw(t, H, H); LUR(t, ID, H); draw(t,-H, H) DLU(t, ID, H); draw(t,-H, -H); RDL(t, ID, H); draw(t, H, -H) def RDL(t,ID,H):#右→下→左 if ID>0: ID=ID-1 RDL(t, ID, H); draw(t, H, -H); URD(t, ID, H); draw(t, 0, -2*H) DLU(t, ID, H); draw(t,-H, -H); RDL(t, ID, H); ID=ID+1 def DLU(t,ID,H):#下→左→上 if ID>0: ID=ID-1 DLU(t, ID, H); draw(t,-H, -H); RDL(t, ID, H); draw(t,-2*H, 0) LUR(t, ID, H); draw(t,-H, H); DLU(t, ID, H); ID=ID+1 def LUR(t,ID,H):#左→上→右 if ID>0: ID=ID-1 LUR(t, ID, H); draw(t,-H, H); DLU(t, ID, H); draw(t,0, 2*H) URD(t, ID, H); draw(t, H, H); LUR(t, ID, H); ID=ID+1 def URD(t,ID,H):#上→右→下 if ID>0: ID=ID-1 URD(t, ID, H); draw(t,H, H); LUR(t, ID, H); draw(t,2*H, 0) RDL(t, ID, H); draw(t,H, -H); URD(t, ID, H); ID=ID+1 #■メイン処理 t=turtle.Pen() t.screen.title("Sierpinski Curve used Turtle graphics") t.speed(0) #タートル移動を速くする t.screen.tracer(100000,0) #描く順序を見たいとき第1引数を小さくする t.hideturtle() #タートルを見えなくする t.up(); t.goto(-250,-100); t.down() #描画開始位置に移動 t.width(2); t.color('blue') #線幅とカラーを設定 SierpinskiCurve(t,5) #シェルピンスキ曲線描画開始 t.screen.tracer(1,0) t.screen.mainloop() 【リスト7-2-2】 tkinterモジュールによるシェルピンスキ曲線 from tkinter import * from RelativeCurve import * #■シエルピンスキ曲線のクラス定義 class SierpinskiCurve(RelativeCurve): #移動量の設定 def setH(self,order): self.ID=order H = 6; S = 2000; S = S / H for i in range(2, order+1): H=H*2+2 self.H=S/H   #シエルピンスキ曲線を描く def curve(self, order): self.setH(order); self.goto(0,0) self.URD();self.draw( self.H, self.H) self.LUR();self.draw(-self.H, self.H) self.DLU();self.draw(-self.H, -self.H) self.RDL();self.draw( self.H, -self.H) #右→下→左 def RDL(self): if self.ID>0: self.ID=self.ID-1 self.RDL();self.draw( self.H, -self.H) self.URD();self.draw( 0 , -2*self.H) self.DLU();self.draw(-self.H, -self.H) self.RDL();self.ID=self.ID+1 #下→左→上 def DLU(self): if self.ID>0: self.ID=self.ID-1 self.DLU();self.draw(-self.H , -self.H) self.RDL();self.draw(-2*self.H, 0) self.LUR();self.draw(-self.H , self.H) self.DLU();self.ID=self.ID+1 #左→上→右 def LUR(self): if self.ID>0: self.ID=self.ID-1 self.LUR();self.draw(-self.H, self.H) self.DLU();self.draw( 0 , 2*self.H) self.URD();self.draw( self.H, self.H) self.LUR();self.ID=self.ID+1 #上→右→下 def URD(self): if self.ID>0: self.ID=self.ID-1 self.URD();self.draw( self.H, self.H) self.LUR();self.draw(2*self.H, 0 ) self.RDL();self.draw( self.H, -self.H) self.URD();self.ID=self.ID+1 #■ tkinter,Canvas共通処理 def initTk(Title):tk=Tk();tk.title(Title); return tk def initCanvas(W,H): canvas=Canvas(tk,width=W,height=H); canvas.pack() return canvas #■ メイン処理 tk=initTk("Sierpinski Curve used tkinter") canvas=initCanvas(500,500) S=SierpinskiCurve(canvas, 50, 400, 1, -1, 2, 'blue') S.curve(5) 7.3 C曲線 【リスト7-3-1】 turtleモジュールによるC曲線 import turtle CC=("#0000FF", "#777700", "#007700", "#FF0000", "#770077") #相対座標位置への移動 def C_draw(t, X,Y): global CC P=t.pos(); NX=P[0]+X; NY=P[1]+Y t.color(CC[int((NX*NX+NY*NY)/8) % 5]);t.goto(NX,NY) #C曲線を描く def C_Curve(t,X,Y,ML): if X*X+Y*Y255: return 255 return int(CH) def drawPoint(X,Y,C): global canvas XX=ScXY(X); YY=500-ScXY(Y) canvas.create_rectangle(XX, YY, XX,YY, fill=C,outline="") def drawLine2(X1, Y1, X2, Y2, i, C1, C2, C3): CH= i% 3 drawLine(X1,Y1, X2,Y2, "#%02X%02X%02X" % (chkCL(CH*C1), chkCL(CH*C2), chkCL(CH*C3))) def drawStalk(X1, Y1, X2, Y2, X3, Y3, X4, Y4, C1, C2, C3): DX=X2-X1; DY=Y2-Y1; ADX=abs(DX); ADY=abs(DY) DX1=DX/5; DY1=DY/5; DX2=(X4-X3)/5; DY2=(Y4-Y3)/5 XX2=X3; YY2=Y3; i=0 if ADX>ADY: if X2>X1: X=X1; Y=Y1 while X<=X2: drawLine2(X,Y, XX2,YY2, i, C1, C2, C3) X+=DX1; Y+=DY1; XX2+=DX2; YY2+=DY2; i+=1 else: X=X1; Y=Y1 while X>=X2: drawLine2(X,Y, XX2,YY2, i, C1, C2, C3) X+=DX1; Y+=DY1; XX2+=DX2; YY2+=DY2; i+=1 else: if Y2>Y1: X=X1; Y=Y1 while Y<=Y2: drawLine2(X,Y, XX2,YY2, i, C1, C2, C3) X+=DX1; Y+=DY1; XX2+=DX2; YY2+=DY2; i+=1 else: X=X1; Y=Y1 while Y>=Y2: drawLine2(X,Y, XX2,YY2, i, C1, C2, C3) X+=DX1; Y+=DY1; XX2+=DX2; YY2+=DY2; i+=1 def drawStalk1(X1, Y1, X2, Y2, X3, Y3, X4, Y4): drawStalk(X1, Y1, X2, Y2, X3, Y3, X4, Y4, 0, 64, 0) def drawStalk2(X1, Y1, X2, Y2, X3, Y3, X4, Y4): drawStalk(X1, Y1, X2, Y2, X3, Y3, X4, Y4, 64, 32, 0)   【リスト7-5-2】 シダの葉を描くプログラム from tkinter import * from PlantElement import * import math def drawPlant(X, Y, W, L, TH, DTH): global counter counter+=1 if (counter %500) ==0: counter=0; root.update() X0=X; Y0=Y; TH0=TH; WW=W; LL=L cosTH =math.cos(TH0); sinTH=math.sin(TH0) X1 = X0 - WW * sinTH; Y1 = Y0 + WW * cosTH X2 = X0 + WW * sinTH; Y2 = Y0 - WW * cosTH while WW > 0.05: WW *= 0.95 XN = X0 + LL * cosTH; YN = Y0 + LL * sinTH X3 = XN - WW * sinTH; Y3 = YN + WW * cosTH X4 = XN + WW * sinTH; Y4 = YN - WW * cosTH drawStalk1(X1, Y1, X2, Y2, X3, Y3, X4, Y4) XX1 = X1 - 3 * WW * sinTH; YY1 = Y1 + 3 * WW * cosTH XX2 = X2 + 3 * WW * sinTH; YY2 = Y2 - 3 * WW * cosTH drawLine (X1, Y1, XX1, YY1, "#000000") drawLine (X2, Y2, XX2, YY2, "#000000") drawPlant(X1 - 3 * WW * sinTH, Y1 + 3 * WW * cosTH, WW * 0.3, LL * 0.3, TH + PI / 3, abs(DTH) * 0.8) drawPlant(X2 + 3 * WW * sinTH, Y2 - 3 * WW * cosTH, WW * 0.3, LL * 0.3, TH - PI / 3, -abs(DTH) * 0.8) X0 = XN; Y0 = YN; X1 = X3; Y1 = Y3; X2 = X4; Y2 = Y4 TH0 -= DTH; cosTH = math.cos(TH0); sinTH = math.sin(TH0) LL *= 0.9 def drawMain(): drawStalk1(-5,-5,-5,0,5,-5,5,0) drawPlant(0,0,5,50,PI/2,0.05) drawMain() root.update() root.mainloop()   【リスト7-6】 立木(以下の記述以外は【リスト7-5-2】と同じ) import random def drawPlant(X, Y, W, L, TH, DTH): X0 = X; Y0 = Y; TH0 = TH; WW = W; LL = L cosTH = math.cos(TH0); sinTH = math.sin(TH0) X1 = X0 - WW * sinTH; Y1 = Y0 + WW * cosTH X2 = X0 + WW * sinTH; Y2 = Y0 - WW * cosTH if L < 10: for i in range(1,100): DX = (random.random() - 0.5) * 100 DY = (random.random() - 0.5) * 100 if(DX*DX+DY*DY<2500) : CC="#007700"; CR=random.random() if CR>0.7: CC="#00AA00" elif CR>0.5: CC="#003300" elif CR>0.2: CC="#775500" drawPoint(X + DX, Y + DY, CC) return; i=0 while WW > 0.1: WW *= 0.9 XN = X0 + LL * cosTH; YN = Y0 + LL * sinTH X3 = XN - WW * sinTH; Y3 = YN + WW * cosTH X4 = XN + WW * sinTH; Y4 = YN - WW * cosTH drawStalk2(X1, Y1, X2, Y2, X3, Y3, X4, Y4) i+=1 if i == 5: drawPlant(X1,Y1,WW * 0.8,LL* 0.8,TH + PI / 6,abs(DTH) * 0.8) drawPlant(X2,Y2, WW *0.8,LL * 0.8,TH - PI / 6,-abs(DTH) * 0.8) elif i > 5: if (i % 3) == 0: drawPlant(X1,Y1,WW*0.8,LL * 0.8,TH+PI/6,abs(DTH) * 0.8) elif (i % 5) == 2: drawPlant(X2,Y2,WW*0.8,LL*0.8,TH-PI/6,-abs(DTH) * 0.8) X0 = XN; Y0 = YN; X1 = X3; Y1 = Y3; X2 = X4; Y2 = Y4 TH0 -= DTH; cosTH = math.cos(TH0); sinTH = math.sin(TH0); LL *= 0.9; def drawMain(): drawStalk2(-5,-5,-5,0,5,-5,5,0) drawPlant(0,0,5,50,PI/2,0.02);   【リスト7-7】 スギナ(以下の記述以外は【リスト7-5-2】と同じ) def drawPlant(X, Y, W, L, TH, DTH): X0 = X; Y0 = Y; TH0 = TH; WW = W; LL = L cosTH = math.cos(TH0); sinTH = math.sin(TH0) X1 = X0 - WW * sinTH; Y1 = Y0 + WW * cosTH X2 = X0 + WW * sinTH; Y2 = Y0 - WW * cosTH LW = 0; DLW = LL * 4; i = 0 while WW > 0.1: WW *=0.95 XN = X0 + LL * cosTH; YN = Y0 + LL * sinTH X3 = XN - WW * sinTH; Y3 = YN + WW * cosTH X4 = XN + WW * sinTH; Y4 = YN - WW * cosTH i = (i + 1) % 2 if i == 0: drawPlant(X1,Y1,WW*0.3,LL*0.3,TH+PI/3,abs(DTH)*0.8) drawPlant(X2,Y2,WW*0.3,LL*0.3,TH-PI/3,-abs(DTH)*0.8) drawStalk1(X1, Y1, X2, Y2, X3, Y3, X4, Y4) X0 = XN; Y0 = YN; X1 = X3; Y1 = Y3; X2 = X4; Y2 = Y4 TH0 -= DTH; cosTH = math.cos(TH0); sinTH = math.sin(TH0) LL *= 0.9; def drawMain(): drawStalk2(-5,-5,-5,0,5,-5,5,0) drawPlant(0,0,5,50,PI/2,0.02); 【リスト7-8】 野草(下記以外は【リスト7-5-2】と同じ) def drawPlant(X, Y, W, L, TH, DTH): X0 = X; Y0 = Y; TH0 = TH; WW = W; LL = L cosTH = math.cos(TH0); sinTH = math.sin(TH0) X1 = X0 - WW * sinTH; Y1 = Y0 + WW * cosTH X2 = X0 + WW * sinTH; Y2 = Y0 - WW * cosTH if L<5: for i in range(10): DX = (random.random() - 0.5) * 7; DY = (random.random() - 0.5) * 7 drawPoint(X + DX, Y + DY, "#FF0000") return LW = 0; DLW = LL * 4; i = 0 while WW > 0.1: WW = WW * 0.95; XN = X0 + LL * cosTH; YN = Y0 + LL * sinTH X3 = XN - WW * sinTH; Y3 = YN + WW * cosTH X4 = XN + WW * sinTH; Y4 = YN - WW * cosTH i = (i + 1) % 2 if(i==0): drawPlant(X1,Y1,WW*0.3,LL*0.3,TH+PI/3,abs(DTH)*0.8) else: drawPlant(X2,Y2,WW*0.3,LL*0.3,TH-PI/3,-abs(DTH)*0.8) drawStalk1(X1, Y1, X2, Y2, X3, Y3, X4, Y4) X0 = XN; Y0 = YN; X1 = X3; Y1 = Y3; X2 = X4; Y2 = Y4 TH0 -= DTH; cosTH = math.cos(TH0); sinTH = math.sin(TH0); LL *= 0.9 def drawMain(): drawStalk1(-5,-5,-5,0,5,-5,5,0); drawPlant(0,0,5,50,PI/2,0.02); 7.6 シェルピンスキのギャスケット 【リスト7-9】 シェルピンスキのギャスケット from tkinter import * import math #■三角形の描画 def drawTriangle(X, Y, L, sin60): IX1 = X   IX2 = X + L   IX3 = X + L / 2 IY1 = 300 – Y   IY2 = IY1 + L * sin60 canvas.create_polygon(IX1,IY1, IX3,IY2, IX2, IY1,fill='',outline='blue') #■描画 def Sier(N, X, Y, L, sin60): if(N<=0):return H = L * sin60; H2 = H / 2   L2 = L / 2; L4 = L / 4 drawTriangle(X + L4, Y + H2, L2, sin60) # 中央の三角形描画   # 左、右、上に進行 Sier(N - 1, X , Y , L2, sin60) Sier(N - 1, X + L2, Y , L2, sin60) Sier(N - 1, X + L4, Y + H2, L2, sin60) #■ギャスケット描画メイン def gasket(): sin60 = math.sin(60 * math.pi / 180) drawTriangle(290, 10, -280, sin60) Sier(6, 10, 10, 280, sin60) #■メイン処理 tk=Tk() tk.title("Sierpinski Gasket") canvas=Canvas(tk, width=300,height=300) canvas.pack() gasket() tk.update() tk.mainloop() 【リスト7-10】 シェルピンスキのピラミッド from tkinter import * import math PI=math.pi SC=100; X0=100; Y0=180 cos30=math.cos(PI/6);cos15=math.cos(PI/12) sin30=math.sin(PI/6);sin15=math.sin(PI/12) def scXY(x,y, z): global cos30, cos15, sin30, sin15, SC,X0, Y0 R=[0 for i in range(2)] R[0]=int(X0+SC*(y*cos15-x*cos30)+0.5) R[1]=int(Y0-SC*(y*sin15+x*sin30+z)+0.5) return R def setPoint(x,y, z, L, H): R=[0 for i in range(4)] R[0]=scXY(x , y, z); R[1]=scXY(x+0.5*L, y+0.5*L, z+H) R[2]=scXY(x+L, y, z); R[3]=scXY(x , y+L , z) return R def setV(DT): ii=int(255*DT) if ii<0: return 0 if ii>255: return 255 return ii Lmin=0.05 Beta=0.1 def pyramid(n, x, y, z, L, H, r, g, b): global canvas, Pmod, Lmin if L10: M=3 root.title("Lace M = %d" % M) canvas.delete(ALL) drawCircle(canvas, 320, 320, 300, 0) root.update() root=Tk() canvas=Canvas(root, width=700,height=700) canvas.pack() leftMouseDown(0) canvas.bind("",leftMouseDown) root.mainloop() 【リスト7-13】 レース編みのような画像 from tkinter import * import math cTab=("#0000FF", "#FF77FF", "#7777FF", "#77FF77", "#FFFF00", "#000055", "#FF0000", "#007777") PI=math.pi; M=10; count=0 def drawCircle(canvas, X0, Y0, R, N): global root, PI, M,count; count+=1 if (count %500)==0: root.update() NN= N % 8 if R<0.5: XX=int(X0); YY=int(Y0); C=cTab[NN] canvas.create_rectangle(XX, YY, XX, YY, fill=C, outline="") return DTH=PI/M; sinDTH=math.sin(DTH) RR=R*sinDTH/(sinDTH+1); RX=R-RR TH=0; DT=2*PI/M; NC = NN+2 drawCircle(canvas,X0, Y0, RX-RR,NC) for i in range(M): drawCircle(canvas,X0+RX*math.cos(TH), Y0+RX*math.sin(TH), RR,NC+3) TH+=DT def carpet(canvas, X,Y,L,N): if L<2:return # Lが小さければ何もしない L1 = L / 3; XX=X for i in range(1,4): # 横3分割 YY=Y for j in range(1,4): # 縦3分割 carpet(canvas, XX,YY,L1,N+1); YY+=L1 XX+=L1 XX=X+L1; YY=Y+L1 #最後に中央を抜き円形を描く #以下2行をコメントにすると雰囲気の異なる画像になる canvas.create_oval(XX, YY, XX+L1, YY+L1, fill=cTab[(N+1) % 8], outline="") drawCircle(canvas, XX+L1/2,YY+L1/2,L1/2, N+1) def leftMouseDown(event): global canvas, root,M M = M + 1 if M>37: M=3 root.title("Lace M = %d" % M) canvas.delete(ALL) carpet(canvas, 10, 10, 420,0) #drawCircle(canvas, 320, 320, 300, 0) root.update() root=Tk() canvas=Canvas(root, width=700,height=700) canvas.pack() leftMouseDown(0) canvas.bind("",leftMouseDown) root.mainloop()from tkinter import * import math