import Tkinter import Image import ImageTk import math import cmath import _chaos import os from vibgyor import vibgyor def read_palette(filename): palette_data = open(filename).xreadlines() palette = [] for line in palette_data: rgb = line.split() try: palette += [int(rgb[0]), int(rgb[1]), int(rgb[2])] except: pass return palette class Viewer: def __init__(self, W=500, H=500, width=4.0, center=0.0+0.0j, palette=vibgyor): self.W, self.H, self.aspect = W, H, float(H)/float(W) self.image = None self.dragging = 0 self.box = None self.dot = None self.palette = palette if Tkinter._default_root: self.window = Tkinter.Toplevel(Tkinter._default_root) else: self.window = Tkinter.Tk() self.canvas = Tkinter.Canvas(self.window, width=W, height=H,cursor='plus') self.canvas.bind("", self.motion) self.canvas.bind("", self.leave) self.canvas.bind("", self.mouse_down) self.canvas.bind("", self.mouse_up) self.canvas.bind("", self.zoom_out) self.canvas.pack() #sublcasses should call self.set # Subclasses should override this. def set(self, width=None, center=None): if width != None: self.width = width if center != None: self.center = center def motion(self, event): if self.dragging == 1: if self.box: self.canvas.delete(self.box) delta_x = event.x - self.x delta_y = int(self.aspect*delta_x) x0, x1 = self.x - delta_x, self.x + delta_x y0, y1 = self.y - delta_y, self.y + delta_y self.box = self.canvas.create_line(x0,y0,x1,y0,x1,y1,x0,y1,x0,y0, fill='gray') def leave(self,event): if self.box: self.canvas.delete(self.box) def mouse_down(self, event): self.x, self.y = event.x, event.y self.dragging = 1 def mouse_up(self, event): self.dragging = 0 if self.box: self.canvas.delete(self.box) if abs(self.x - event.x) > 2: delta_x = self.width*(self.x - self.W/2)/self.W delta_y = self.aspect*self.width*(self.H/2 - self.y)/self.H newcenter = self.center + complex(delta_x,delta_y) newwidth = abs(2*self.width*float(self.x - event.x)/self.W) self.set(center=newcenter, width=newwidth) def zoom_out(self, event): self.set(width=2*self.width) def set_dot(self, z): self.clear_dot() x = int((0.5 + (z.real - self.center.real)/self.width)*self.W) y = int((0.5 + (self.center.imag - z.imag)/(self.width*self.aspect))*self.H) self.dot= self.canvas.create_oval(x-2, y-2, x+2, y+2, fill='white') def clear_dot(self): if self.dot: self.canvas.delete(self.dot) class Mandelbrot(Viewer): def __init__(self, W=500, H=500, width=2.75, center=-0.75+0.0j, palette=vibgyor): Viewer.__init__(self, W, H, width, center, palette) self.julia = None self.canvas.bind("", self.show_julia) self.set(width, center) def set(self, width=None, center=None): if width != None: self.width = width if center != None: self.center = center self.window.title('Mandelbrot set -- center = %f + %fi, width = %f'% (self.center.real, self.center.imag, self.width)) height = 1j*self.width*self.aspect c0 = self.center - (self.width + height)/2 c1 = self.center + (self.width + height)/2 self.imagestring = _chaos.iterate(self.W,self.H,255,0+0j,0+0j,c0,c1) self.image = Image.fromstring('P', (self.W, self.H), self.imagestring) self.image.putpalette(self.palette) self.bitmap = ImageTk.PhotoImage(self.image) self.canvas.create_image(0,0, anchor=Tkinter.NW, image=self.bitmap) def show_julia(self, event=None, center = 0+0j): if event: x, y = event.x, event.y delta_x = self.width*(x - self.W/2)/self.W delta_y = self.aspect*self.width*(self.H/2 - y)/self.H center = self.center + complex(delta_x,delta_y) if self.julia == None: self.julia = Julia(c=center) else: self.julia.filled = 'Filled' self.julia.set(width=4.0, center=0.0+0.0j, c=center) class Julia(Viewer): def __init__(self, W=500, H=500, width=4.0, center=0.0+0.0j, c=0.0+0.0j, palette=vibgyor): Viewer.__init__(self, W, H, width, center, palette) self.canvas.bind("", self.toggle_fill) self.set(width, center, c) def set(self, width=None, center=None, c=None): if width != None: self.width = width if center != None: self.center = center if c != None: self.c = c self.filled = 'Filled' self.set_title() height = 1j*self.width*float(self.H)/float(self.W) z0 = self.center - (self.width + height)/2 z1 = self.center + (self.width + height)/2 self.imagestring = _chaos.iterate(self.W,self.H,255,z0,z1,self.c,self.c) self.image = Image.fromstring('P', (self.W, self.H), self.imagestring) self.image.putpalette(self.palette) self.bitmap = ImageTk.PhotoImage(self.image) self.canvas.create_image(0,0, anchor=Tkinter.NW, image=self.bitmap) def set_title(self): self.window.title( '%s Julia set: c=%.3f+%.3fi, center=%.3f+%.3fi, width=%e'% (self.filled, self.c.real, self.c.imag, self.center.real, self.center.imag, self.width)) def toggle_fill(self, event): if self.filled == 'Filled': self.filled = '' boundary = _chaos.boundary(self.imagestring, self.W, self.H, 255) self.image = Image.fromstring('L', (self.W, self.H), boundary) else: self.filled = 'Filled' self.image = Image.fromstring('P', (self.W, self.H), self.imagestring) self.image.putpalette(self.palette) self.bitmap = ImageTk.PhotoImage(self.image) self.canvas.create_image(0,0, anchor=Tkinter.NW, image=self.bitmap) if self.filled == '': self.canvas.create_rectangle(20, self.H-14, 300, self.H, fill='white', outline = '') self.canvas.create_text(20, self.H-14, fill='red', anchor=Tkinter.NW, text= 'Box dimension is approximately %.3f'%self.box_dimension(boundary)) self.set_title() def box_dimension(self,boundary): boxcount = _chaos.boxcount(boundary, self.W, self.H, 255) data = [] for i in range(len(boxcount)): try: data.append( (-(i+1)*math.log(2), math.log(boxcount[i]) ) ) except OverflowError: pass X, Y, XY, XX = 0.0, 0.0, 0.0, 0.0 if len(data) == 0: return 0.0 n = float(len(data)) for point in data: x, y = point X += x Y += y XY += x*y XX += x*x return (n*XY - X*Y)/(n*XX - X*X)