Skip to content

Instantly share code, notes, and snippets.

@agrif
Created December 13, 2012 21:52
Show Gist options
  • Save agrif/4280301 to your computer and use it in GitHub Desktop.
Save agrif/4280301 to your computer and use it in GitHub Desktop.
a mandelbrot set renderer using the Overviewer dispatcher
#!/usr/bin/python2
import sys
sys.path += ["../../"]
from PIL import Image
from math import log, ceil
import os
from overviewer_core.dispatcher import MultiprocessingDispatcher
from overviewer_core.observer import ProgressBarObserver
class Renderer(object):
def get_size(self):
raise NotImplementedError("Renderer.get_size")
def render(self, origin, im):
raise NotImplementedError("Renderer.render")
class MandelbrotSet(Renderer):
def __init__(self, size, iterations, palette):
self.size = size
self.iterations = iterations
self.palette = list(Image.open(palette).getdata())
self.palsize = len(self.palette)
def get_size(self):
return (self.size, self.size)
def _get_color(self, n, z, bound):
nu = n - log(log(abs(z), bound), 2)
nu = nu * self.palsize
intpart = int(nu % self.palsize)
fracpart = nu % 1
a = self.palette[intpart]
b = self.palette[(intpart + 1) % self.palsize]
color = [int(x * (1.0 - fracpart) + y * fracpart) for (x, y) in zip(a, b)]
return tuple(color)
def render(self, origin, im):
for x in xrange(im.size[0]):
for y in xrange(im.size[1]):
point = [2 * float(2 * i - self.size) / self.size for i in (x + origin[0], y + origin[1])]
z = 0
c = complex(*point)
in_set = True
for i in xrange(self.iterations):
if abs(z) > 2.0:
# we do escape, colorize this
in_set = False
im.putpixel((x, y), self._get_color(i, z, 2.0))
break
z = z ** 2 + c
if in_set:
# we do not escape, make this black
im.putpixel((x, y), (0, 0, 0, 255))
class SimpleCanvas(object):
region_size = 256
def __init__(self, renderer, output):
self.renderer = renderer
self.output = output
self.size = renderer.get_size()
self.numx = int(ceil(float(self.size[0]) / self.region_size))
self.numy = int(ceil(float(self.size[1]) / self.region_size))
def get_num_phases(self):
return 2
def get_phase_length(self, phase):
if phase == 0:
return self.numx * self.numy
return 1
def iterate_work_items(self, phase):
if phase == 1:
# do the one final thing
yield (None, [])
else:
# do all the regions
for x in xrange(self.numx):
for y in xrange(self.numy):
yield ((x, y), [])
def do_work(self, item):
if item is None:
bigim = Image.new("RGBA", self.size)
for x in xrange(self.numx):
for y in xrange(self.numy):
path = self.output + '.' + str(x) + '.' + str(y) + '.png'
im = Image.open(path)
bigim.paste(im, (x * self.region_size, y * self.region_size))
os.remove(path)
bigim.save(self.output)
return
x, y = item
origin = (x * self.region_size, y * self.region_size)
size = tuple([min([self.size[i] - origin[i], self.region_size]) for i in range(2)])
im = Image.new("RGBA", size)
self.renderer.render(origin, im)
im.save(self.output + '.' + str(x) + '.' + str(y) + '.png')
if __name__ == "__main__":
r = MandelbrotSet(10000, 100, "palette.png")
c = SimpleCanvas(r, "output.png")
d = MultiprocessingDispatcher()
observer = ProgressBarObserver()
observer.UPDATE_INTERVAL = 1
d.render_all([c], observer)
d.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment