โจทย์ - http://www.codenone.com/node/242
ดูของ Ruby ใช้ OpenGL แล้วอยากเขียนมั่ง แต่ดันบ้าจี้ใช้ Tkinter ตามโจทย์ไปซะแล้ว เอาแค่นี้ก่อน โค้ดหลักของการหาค่าแล้วเอาไปวาดจะใช้ Tkinter
#!/usr/bin/env python from Tkinter import * def mandelbrot(ul,lr,callback,step=(200,200),maxiter=255,limit=2): xs,ys = step x0,y0 = ul.real,ul.imag x1,y1 = lr.real,lr.imag xd,yd = (x1-x0)/xs,(y1-y0)/ys i = 0 y = y0 for i in range(ys): x = x0 for j in range(xs): color = mandelbrot_point(x,y,maxiter,limit) callback(i,j,x,y,color) y += yd class App: def __init__(self,parent): self.parse_args() self.init(parent) def parse_args(self): import sys args = sys.argv[1:] if not args: args = ['-2+1j','1-1j'] self.upleft,self.lowright = map(complex,args[:2]) self.width,self.height = 200,200 self.maxiter = 30 self.limit = 2 def init(self,parent): self.frame = Frame(parent,width=self.width,height=self.height) self.frame.pack() self.canvas = Canvas(self.frame,width=self.width,height=self.height) self.canvas.pack(side=TOP) def palette(self,c): c = c*255.0/self.maxiter return '#%02x%02x%02x' % (c,(c+64)%256,(c+32)%256) def run(self): params = { 'step': (self.width,self.height), 'maxiter': self.maxiter, 'limit': self.limit, } self.pixels = [0]*(self.width*self.height) mandelbrot(self.upleft,self.lowright,self.plot,**params) def plot(self,i,j,x,y,c): index = i*self.width+j #print i,j,c color = self.palette(c) p = self.canvas.create_line(j,i,j+1,i,fill=color) #print i,j,index self.pixels[index] = p self.frame.update() import mandelbench mandelbrot_point = mandelbench.is_mandel_4 if __name__ == '__main__': root = Tk() app = App(root) app.run() root.mainloop()
ส่วนฟังก์ชั่นจริงๆ จะใช้ mandelbrot_point ซึ่งข้างบนคือ mandelbench.is_mandel_4 หน้าตาของ mandelbench.py จะเป็นแบบนี้
def is_mandel_1(x,y,maxiter=30,limit=2): p = complex(x,y) i = 0 z = 0+0j while abs(z) < limit and i < maxiter: z = z*z+p i += 1 return i def is_mandel_2(x,y,maxiter=30,limit=2): x0,y0 = x,y x2,y2 = x*x,y*y i = 0 while x2+y2 < limit*limit and i < maxiter: x,y = x2-y2+x0,2*x*y+y0 x2,y2 = x*x,y*y i += 1 return i def is_mandel_3(x,y,maxiter=30,limit=2): x0,y0 = x,y x2,y2 = x*x,y*y lim = limit*limit i = 0 while x2+y2 < lim and i < maxiter: x,y = x2-y2+x0,2*x*y+y0 x2,y2 = x*x,y*y i += 1 return i import mandelbrot is_mandel_4 = mandelbrot.is_mandel_4
ซึ่ง is_mandel_4() ได้มาจาก Pyrex อีกที เพื่อการนี้ต้องใช้ไฟล์อีกสองไฟล์
def is_mandel_4(double x,double y,int maxiter=30,int limit=2):
cdef double x0,y0,x2,y2,lim
x0,y0 = x,y
x2,y2 = x*x,y*y
lim = limit*limit
i = 0
while x2+y2 < lim and i < maxiter:
x,y = x2-y2+x0,2*x*y+y0
x2,y2 = x*x,y*y
i = i+1
return iPYINCLUDE = -I/usr/include/python$(shell python -c "import sys;print sys.version.split()[0]")
all: mandelbrot.so
%.c: %.pyx
pyrexc $<
%.o: %.c
gcc -c -fPIC $(PYINCLUDE) $<
%.so: %.o
gcc -shared $< -lm -o $@
clean:
@rm -f *.c *.o *.so *~ core core.*ก่อนจะรันได้ต้องสั่ง make all ซักครั้งเพื่อให้ได้ mandelbrot.so เอาไว้ใช้ต่อไป
เฮ้อ inline ของ Ruby ดีกว่าเยอะ มีผลการรันนิดๆ ด้วย
กระทู้เก่าๆ จะย้ายตามไปในภายหลัง ตอนนี้ปิดการโพสต์กระทู้ไว้ เหลือไว้เฉพาะอ้างอิงเท่านั้น
Pyrex ของ python ดูเหมือนจะเอาไปใช้งานจริงๆจัง มันก็เลยหนักๆ
แต่ inline ของ ruby มันเป็น quick hack
เวลา run มันก็จะแอบไปสร้าง c code + wrapper code
แล้วก็สั่ง compile ให้เรา
Tkinter มันช้าจริงๆ แค่ธรรมดายังแย่ zoom ยิ่งแย่กว่า ลองแบบ wxPython บ้าง
หน้าตาเหมือนใน Ruby เกือบเปีะ เพิ่ม zoom out เข้าไปอีกหน่อยกับให้เรียก callback เพื่อวาดทันที จะได้ประหยัดหน่วยความจำอีกนิดนึง
คล้าย ๆ inline ของ Ruby ครับ แต่เป็น FORTRAN แล้วก็เอามา plot ด้วย matplotlib จริง ๆ ตอนพลอทนี่แหละครับที่เสียเวลา