#!BPY
"""
Name: 'BlenderBrot'
Blender: 248
Group: 'Misc'
Tip: 'Generate Mandelbrot Sets'
"""

__author__ = "Malte Reimold"
__url__ = ['www.blender.org', 'blendpolis.de']
__version__ = "0.1"

__bpydoc__ = """\
3d Mandelbrot fractal generator.

Preview Image Support.

"""

# --------------------------------------------------------------------------
# BlenderBrot v0.1 by Malte Reimold
# --------------------------------------------------------------------------
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------



import Blender
from Blender import *
from Blender.Draw import *
from Blender import Image
from math import *
from cmath import *

editmode = Window.EditMode()
if editmode: Window.EditMode(0)
scene = Blender.Scene.GetCurrent ()

global itersteps, maxiterperstep, cutoff, cutmin, zfaktor, cut
xmin = -2.0
xmax = 2.0
ymin = -2.0
ymax = 2.0
faces = []
vertices = []
itersteps = 10
maxiterperstep = 20
cutoff = 100000000
cutmin = 0.30
zfaktor = 1.0/(maxiterperstep*itersteps)
iter1 = 4
quadfill = 0
cut = 4
previewsize = 256


def compress(z):
	global zcompression, zfaktor, zfaktor_log, zfaktor_sqr
	if zcompression is 2:
		c = zfaktor_log*abs(log(z))
	elif zcompression is 1:
		c = z * zfaktor
	elif zcompression is 3:
		c = z**0.5 * zfaktor_sqr
	return (c)


def formula(c, c0):
	return (c**2+c0)

def iterate(x,y):
	global itersteps, maxiterperstep, cutoff, cutmin, cut
	iter = 0
	i = 0
	c0 = complex(x,y)
	c = c0
	i0 = 0
	while iter < itersteps:
		iter = iter+1
		imax = iter*maxiterperstep
		while abs(c) < cutoff and i < imax:
			i = i + 1
			c = formula(c, c0)
		if i < imax:
			iter = itersteps
		elif abs(c) < cutmin:
			if abs(formula(c, c0)) < abs(c):
				i = itersteps * maxiterperstep
				iter = itersteps
	return(i)

def mandelbrot():
	global xmin, xmax, ymin, ymax, iter1, quadfill, cut
	ob = Object.New('Mesh')
	me = Mesh.New()
	faces = []
	vertices = []
	print 'mesh calculation...'


	areas = []
	areas.append([xmin, xmax, ymin, ymax, iterate(xmin, ymin), iterate(xmin, ymax), iterate(xmax, ymin), iterate(xmax, ymax)])
	areanew = []
	vertcount = 0
	areadata = {}
	for i in range(iter1):
		print "iteration:", i+1
		print "Areas left:", len(areas)
		for j in range(len(areas)):
			abweichung = 0
			for xcut in range(cut+1):
				for ycut in range(cut+1):
					x = (areas[j][1]-areas[j][0])/cut*xcut+areas[j][0]
					y = (areas[j][3]-areas[j][2])/cut*ycut+areas[j][2]
					if not xcut:
						if not ycut:
							areadata[xcut, ycut] = areas[j][4]
						elif ycut is cut +1:
							areadata[xcut, ycut] = areas[j][5]
						else:
							areadata[xcut, ycut] = iterate(x,y)
					elif xcut is cut+1:
						if not ycut:
							areadata[xcut, ycut] = areas[j][6]
						elif ycat is cut +1:
							areadata[xcut, ycut] = areas[j][7]
						else:
							areadata[xcut, ycut] = iterate(x,y)
					else:
						areadata[xcut, ycut] = iterate(x,y)
					if abs(areadata[xcut, ycut]-areadata[0, 0]) > abweichung:
						abweichung = abs(areadata[xcut, ycut]-areadata[0, 0])
			if abweichung is 0:
				z = compress(areadata[0,0])
				me.verts.extend([[areas[j][0], areas[j][2], z], [areas[j][1], areas[j][2], z], [areas[j][0], areas[j][3], z], [areas[j][1], areas[j][3], z]])
				vertcount = vertcount + 4
				faces.append([me.verts[vertcount-1],me.verts[vertcount-2],me.verts[vertcount-4],me.verts[vertcount-3]])
			else:
				for xcut in range(cut):
					for ycut in range(cut):
						areanew.append([(areas[j][1]-areas[j][0])/cut*xcut+areas[j][0], (areas[j][1]-areas[j][0])/cut*(xcut+1)+areas[j][0], (areas[j][3]-areas[j][2])/cut*ycut+areas[j][2], (areas[j][3]-areas[j][2])/cut*(ycut+1)+areas[j][2], areadata[xcut, ycut], areadata[xcut, ycut+1], areadata[xcut+1, ycut], areadata[xcut+1, ycut+1]])		
		areas = areanew
		areanew = []
	print "final areas:", len(areas)
	
	if not quadfill:
		for j in range(len(areas)):					
			if (areas[j][4] is areas[j][5] and areas[j][6] is areas[j][7]) or (areas[j][4] is areas[j][6] and areas[j][5] is areas[j][7]):
				me.verts.extend([[areas[j][0], areas[j][2], compress(areas[j][4])], [areas[j][1], areas[j][2], compress(areas[j][6])], [areas[j][0], areas[j][3], compress(areas[j][5])], [areas[j][1], areas[j][3], compress(areas[j][7])]])	
				vertcount = vertcount + 4
				faces.append([me.verts[vertcount-1],me.verts[vertcount-2],me.verts[vertcount-4],me.verts[vertcount-3]])	
			else:
				me.verts.extend([[areas[j][0], areas[j][2], compress(areas[j][4])], [areas[j][1], areas[j][2], compress(areas[j][6])], [areas[j][0], areas[j][3], compress(areas[j][5])], [areas[j][1], areas[j][3], compress(areas[j][7])]])				
				xmitte = areas[j][0]+0.5*(areas[j][1]-areas[j][0])
				ymitte = areas[j][2]+0.5*(areas[j][3]-areas[j][2])			
				me.verts.extend(xmitte, ymitte, compress(iterate(xmitte, ymitte)))
				vertcount = vertcount + 5
				faces.append([me.verts[vertcount-1],me.verts[vertcount-3],me.verts[vertcount-5]])
				faces.append([me.verts[vertcount-1],me.verts[vertcount-4],me.verts[vertcount-2]])
				faces.append([me.verts[vertcount-1],me.verts[vertcount-2],me.verts[vertcount-3]])
				faces.append([me.verts[vertcount-1],me.verts[vertcount-5],me.verts[vertcount-4]])
	else:
		for j in range(len(areas)):					
			me.verts.extend([[areas[j][0], areas[j][2], compress(areas[j][4])], [areas[j][1], areas[j][2], compress(areas[j][6])], [areas[j][0], areas[j][3], compress(areas[j][5])], [areas[j][1], areas[j][3], compress(areas[j][7])]])	
			vertcount = vertcount + 4
			faces.append([me.verts[vertcount-1],me.verts[vertcount-2],me.verts[vertcount-4],me.verts[vertcount-3]])			
					
	me.faces.extend(faces)
	ob.link (me)
	ob.loc = (0,0,0)
	scene.objects.link (ob)
	me.remDoubles(0.00001)
	Blender.Redraw()
	
def preview():
	global xmin, xmax, ymin, ymax, iter1, quadfill, previewsize
	try:
		im = Image.Get('preview')
		im.setPixelI( previewsize-1, previewsize-1,color[0])
	except:
		im = Image.New('preview', previewsize, previewsize, 32)
	color = []
	for l in range(128):
		b = int(64+l/2)
		r = int(l**0.5*22.5)
		g = int((l/128.0)**4*255)
		color.append([r,g,b,255])
		
	
	
	for x in range(previewsize):
		for y in range(previewsize):
			i = 0
			c0 = complex((xmax-xmin)/previewsize*x+xmin ,(ymax-ymin)/previewsize*y+ymin)
			c = c0
			while i < 127 and abs(c) < 15:
				c = formula(c, c0)
				i = i + 1
			im.setPixelI(x,y,color[i])	

def data_convert():
	global xmin, xmax, ymin, ymax, itersteps, maxiterperstep, cutoff, cutmin, iter1, quadfill, cut, previewsize
	global xmin_but, xmax_but, ymin_but, ymax_but, itersteps_but, maxiterperstep_but, cutoff_but, cutmin_but, iter1_but, quadfill_but, cut_but, previewsize_but
	global zcompression_but, zcompression, zfaktor, zfaktor_log, zfaktor_sqr
	
	xmin = xmin_but.val
	xmax = xmax_but.val
	ymin = ymin_but.val
	ymax = ymax_but.val
	itersteps = itersteps_but.val
	maxiterperstep = maxiterperstep_but.val
	cutoff = cutoff_but.val
	cutmin = cutmin_but.val
	iter1 = iter1_but.val
	quadfill = quadfill_but.val
	cut = cut_but.val
	previewsize = previewsize_but.val
	zcompression = zcompression_but.val	
	zfaktor = 1.0/(maxiterperstep*itersteps)
	zfaktor_log = 1.0 / abs(log(maxiterperstep*itersteps))
	zfaktor_sqr = 1.0 / (maxiterperstep*itersteps)**0.5


def gui():
	global xmin, xmax, ymin, ymax, itersteps, maxiterperstep, cutoff, cutmin, iter1, quadfill, cut, previewsize
	global xmin_but, xmax_but, ymin_but, ymax_but, itersteps_but, maxiterperstep_but, cutoff_but, cutmin_but, iter1_but, quadfill_but, cut_but, previewsize_but
	global zcompression_but

#	archiusebut = Toggle('Use Architectures', 127, 10, 240, 360, 19, archiusebut.val, 'Use Architectures?')
#	bkonzfbut = Slider('Centre concentration ', 9, 10, 450, 360,19, bkonzfbut.val, 0.0, 1.0)
#	bkonzsbut = Slider('Concentration stiffness ', 10, 10,430,360,19, bkonzsbut.val, 1, 10)

	text1 = Label('coords', 10, 470, 360, 19)
	xmin_but = Slider('x min ', 4, 10, 450, 180, 19, xmin_but.val, -10.0, 10.0)
	xmax_but = Slider('x max ', 5, 190, 450, 180, 19, xmax_but.val, -10.0, 10.0)
	ymin_but = Slider('y min ', 6, 10, 430, 180, 19, ymin_but.val, -10.0, 10.0)
	ymax_but = Slider('y max ', 7, 190, 430, 180, 19, ymax_but.val, -10.0, 10.0)
	
	text2 = Label('Iteration parameters ', 10, 400, 360, 19)
	itersteps_but = Slider('Iteration batches ', 8, 10, 380, 360, 19, itersteps_but.val, 1, 250)
	maxiterperstep_but = Slider('Steps per batch ', 9, 10, 360, 360, 19, maxiterperstep_but.val, 1, 250)
	iterations = 'Max Iterations: ' + str(itersteps_but.val*maxiterperstep_but.val)
	text3 = Label(iterations, 10, 340, 360, 19)	
	cutoff_but = Slider('Cutoff ',10, 10, 320, 360, 19, cutoff_but.val, 1, 200000000)
	cutmin_but = Slider('Lower Limit ',11, 10, 300, 360, 19, cutmin_but.val, 0.0, 0.5)

	text4 = Label('Resolution parameters ', 10, 270, 360, 19)
	iter1_but = Slider('Iterations ',12, 10, 250, 360, 19, iter1_but.val, 1, 32)
	cut_but = Slider('Number of cuts ',13, 10, 230, 360, 19, cut_but.val, 2, 2048)
	quadfill_but = Toggle('Fill remaining areas with quads?', 14, 10, 210, 360, 19, quadfill_but.val)
	maxverts = 'Max Number of vertices: ' + str(cut_but.val**(2*iter1_but.val))
	text5 = Label(maxverts, 10, 190, 360, 19)	

	text6 = Label('Vizualisation parameters ', 10, 160, 360, 19)
	zcompchoice = "z-compression %t|linear(normalize) %x1|logarithmic %x2|square root %x3"
	zcompression_but = Menu(zcompchoice, 15, 10, 140, 360, 19, zcompression_but.val)
	
	text7 = Label('preview parameters ', 10, 110, 360, 19)
	previewsize_but = Slider('Cutoff ',16, 10, 90, 360, 19, previewsize_but.val, 64, 2048)

	Button('Preview',3, 10, 40, 360, 19, 'Generate Preview Image')
	Button('Build',1, 10, 20, 180, 19, 'Build fractal. ATTENTION: Dependent on the parameters chosen this may take some time')
	Button('Cancel',2, 190, 20, 180, 19)

def event(evt, val):
	if (evt == QKEY and not val): Exit()
		
def bevent(evt):
	global xmin, xmax, ymin, ymax, itersteps, maxiterperstep, cutoff, cutmin, iter1, quadfill, cut, previewsize
	global xmin_but, xmax_but, ymin_but, ymax_but, itersteps_but, maxiterperstep_but, cutoff_but, cutmin_but, iter1_but, quadfill_but, cut_but, previewsize_but
	global zcompression_but, zcompression
	
	if evt: Redraw()
	if evt == 2: Exit()
	elif evt == 1:
		data_convert()
		mandelbrot()
		Redraw()
	elif evt == 3:
		data_convert()
		preview()
		Redraw()
	
def initialize():
	global xmin, xmax, ymin, ymax, itersteps, maxiterperstep, cutoff, cutmin, iter1, quadfill, cut, previewsize
	global xmin_but, xmax_but, ymin_but, ymax_but, itersteps_but, maxiterperstep_but, cutoff_but, cutmin_but, iter1_but, quadfill_but, cut_but, previewsize_but
	global zcompression_but, zcompression
	
	xmin_but = Create(xmin)
	xmax_but = Create(xmax)
	ymin_but = Create(ymin)
	ymax_but = Create(ymax)
	itersteps_but = Create(itersteps)
	maxiterperstep_but = Create(maxiterperstep)
	cutoff_but = Create(cutoff)
	cutmin_but = Create(cutmin)
	iter1_but = Create(iter1)
	quadfill_but = Create(quadfill)
	cut_but = Create(cut)
	previewsize_but = Create(previewsize)
	zcompression_but = Create(2)
	Register(gui, event, bevent)







initialize()

Blender.Redraw()