'''MandelbrotSet.py This program computes a fractal image based on the Mandelbrot set. The program facilitates changing the resolution of the image (and thus the time required to compute it) and the region of the complex plane that is being considered. If MONOCHROME is set to True, then the image is grayscale, and the mapping is chosen to make the image look good in that mode. If not, then if PSEUDOCOLOR is set to True, then a color image is produced that achieves a balance between having different colors and maintaining some understandable relationship between (most of) the colors and the "count" of iterations used to determine that status of a pixel. The scheme here is the following: Divergence: Dark blue Low count values: Medium green. Count values approaching 255: red. Count values above 255: red with amounts of green added that cycle from nothing to max every 128 values, thereby creating yellows and whites. High values of count (e.g., 1000): more and more blue, so that some pixels can appear white hot. If PSEUDOCOLOR is set of False, then the "pure" count values are stored into the image being produced, encoded in the blue and green components. The procedure makeColor can then be used to create a color image that is an alternative to what you get with PSEUDOCOLOR turned on. The code below contains commented-out versions of various useful settings. ''' #W=192; H=128 # For the initial range, showing the whole Mandelbrot set #W=64; H=64 #W=128; H=128 #W=256; H=256 #W=512; H=512 W=1024; H=1024 # On some parts of the complex plane, this will take a very long time. #REAL_RANGE = (-2, 1); IMAGINARY_RANGE = (-1, 1) # The initial range, showing the whole Mandelbrot set #REAL_RANGE = (0, 0.2); IMAGINARY_RANGE = (0.5, 0.7) #REAL_RANGE = (0.1, 0.15); IMAGINARY_RANGE = (0.6, 0.65) # One nice detail area. # The following region contains a tree-like detail: REAL_RANGE = [0.1208282470703125, 0.1283050537109375] IMAGINARY_RANGE = [0.6367431640625, 0.644219970703125] MONOCHROME=False PSEUDOCOLOR=False WIN = pmNewComputedImage("Mandelbrot Set within a in "+\ str(REAL_RANGE)+" and b in "+\ str(IMAGINARY_RANGE),\ W,H,"rgb(255, 230, 230)") def createMandelbrotImage(): global WIN ncols = pmGetImageWidth(WIN) nrows = pmGetImageHeight(WIN) rectWidth = float(REAL_RANGE[1]-REAL_RANGE[0]) rectHeight = float(IMAGINARY_RANGE[1]-IMAGINARY_RANGE[0]) for j in range(ncols): for i in range(nrows): real = REAL_RANGE[0]+j*rectWidth/ncols imag = IMAGINARY_RANGE[0]+i*rectHeight/nrows c = complex(real, imag) count = iterate(c) (r,g,b) = getColor(count) pmSetPixel(WIN, j, i, r, g, b) NMAX = 2000 THRESH = 2000 def iterate(c): global NMAX, THRESH n = 0 z = complex(0,0) while nTHRESH: return n n += 1 #if n<10: report(n,z) return -1 RCOUNT = 0 def report(n,z): global RCOUNT if RCOUNT < 200: print "n="+str(n)+"; z="+str(z) RCOUNT += 1 def getColor(count): global NMAX, MONOCHROME, PSEUDOCOLOR if count==-1: if MONOCHROME: return (0,0,0) else: return (0, 0, 96) else: if MONOCHROME: b = int(count/6) #% 256 b = 10*b; r = b; g = b # Special for monochrome version. else: if PSEUDOCOLOR: r = count if count>255: g = (count*2) % 256 else: g = max(0,192-count*2) b = int(count/8) else: # Preserve full count info in output image... b = count % 256 g = (count / 256) % 256 r = (count / 65536) % 256 return (r,g,b) def findNewRectangle(x, y, width, height): ''' Given the current ranges and these arguments, compute a new pair of ranges that makes it easy to zoom in on the selected region of the complex plane. The center of the new region will be (x,y) and the width, in pixels of the current image, will be used to compute the intervals.''' global W, H, REAL_RANGE, IMAGINARY_RANGE (rstart,rend)=REAL_RANGE (istart,iend)=IMAGINARY_RANGE rextent = rend-rstart iextent = iend-istart new_x_center = rstart+(float(x)/W)*rextent new_y_center = istart+(float(y)/H)*iextent new_half_width = rextent*width/(2*W) new_real_range = [new_x_center - new_half_width, new_x_center + new_half_width] new_half_height = iextent*height/(2*H) new_imaginary_range = [new_y_center - new_half_height, new_y_center + new_half_height] print "Use REAL_RANGE = "+str(new_real_range) print "and IMAGINARY_RANGE = "+str(new_imaginary_range) def makeColor(): '''Produce a colored version of the image from the pure counts image.''' global WIN newWin = pmCloneImage(WIN) pmSetSource1(WIN) pmSetDestination(newWin) # The mapping here does well on the tree-like detail, making the branches # brown and putting green around them. pmSetFormula("RGB(Blue1(x,y)*2, (4*Blue1(x,y)) mod 256 + Green1(x,y)*100,50)") pmCompute() createMandelbrotImage() if not MONOCHROME and not PSEUDCOLOR: makeColor()