#!/usr/local/bin/python
# coding: iso-8859-1
# Copyright © 2009 by Amos Newcombe

from imagelib.Drawing.PILDrawing import PILImage
from imagelib.Turtle.Turtle import Turtle2D
from math import sqrt, cos, pi, floor, ceil, log
from mathlib.Polynomial import Polynomial
# from PIL.Image import FLIP_LEFT_RIGHT
from PIL import Image, ImageDraw
from time import clock

radians = pi / 180.

class TreeTurtle(Turtle2D):
	'a turtle that can draw an osculating binary tree'
	
	def __init__(self, **d):
		Turtle2D.__init__(self, **d)
		self.xR0 = 0.5
	
	def tree(self, degTheta, xR, xTrunk, xMin, nC=0, n2=1, xDim=None):
# 		print 'tree(%f, %f, %f, %f)' % (degTheta, xR, xTrunk, xMin)
		if xDim == None: xDim = -1./log(xR, 2)
		if xMin < xTrunk:
			self.setColor(nC, n2, xDim)
			self.set('pen', True)
			self.fd(xTrunk)
			self.rt(degTheta)
			self.tree(degTheta, xR, xTrunk*xR, xMin, 2*nC  , n2*2, xDim)
			self.rt(-2*degTheta)
			self.tree(degTheta, xR, xTrunk*xR, xMin, 2*nC+1, n2*2, xDim)
			self.rt(degTheta)
			self.set('pen', False)
			self.fd(-xTrunk)
	
	def setColor(self, nC, n2, xDim):
		self.setColorRGB(255, 255, 255)
	
	def setColorHSL(self, nH, nS, nL):
		'''Set the color to the given hue, saturation and lightness values.
		
		nH is the hue given as an angle between 0 and 360 (red=0, green=120, blue=240),
		nS is a saturation value between 0% and 100% (gray=0%, full color=100%),
		nL is a lightness value between 0% and 100% (black=0%, normal=50%, white=100%).
		For example, "hsl(0,100%,50%)" is pure red.
		''' # http://www.pythonware.com/library/pil/handbook/imagecolor.htm
		self.set('color', 'hsl(%d,%d%%,%d%%)' % (nH, nS, nL))
# 		print self.get('color')
	
	def setColorRGB(self, nR, nG, nB):
		'''Set the color to the given red, green, blue values. Use a 0...255 range for integers, or pass strings such as "100%".
		
		RGB functions, given as "rgb(red, green, blue)" where the colour values are integers in the range 0 to 255. Alternatively, the color values can be given as three percentages (0% to 100%). For example, "rgb(255,0,0)" and "rgb(100%,0%,0%)" both specify pure red.
		''' # http://www.pythonware.com/library/pil/handbook/imagecolor.htm
		self.set('color', 'rgb(%s,%s,%s)' % (nR, nG, nB))
	
	def OsculatingRatio(self, degTheta):
		xEpsilon = 2**-24
# 		xR = self.xR0
		if degTheta == 0.:
			xR, isSolved = 0.5, True
		else:
			poly = self.OsculatingPolynomial(degTheta)
			try: xR, isSolved = poly.Newton(self.xR0, xEpsilon)
			except ZeroDivisionError, ex:
				print 'Zero division:', `poly`, self.xR0
				raise
		self.xR0 = xR
		return xR, isSolved
	
	def OsculatingPolynomial(self, degTheta):
		if degTheta < 90.:
			lPoly = [-1., 0.] + [
				2. * cos(j*degTheta*radians)
				for j in range(0, int(ceil(90./degTheta)))
			]
		elif degTheta < 135.:
			lPoly = [1.] + [2. * cos(j*degTheta*radians) for j in [1, 2]]
		else:
			lPoly = [1., 2. * cos(degTheta*radians)]
		lPoly.reverse()
		print lPoly
		return Polynomial(*lPoly)

class ColorTurtle(TreeTurtle):
	
	def setColor(self, nC, n2, xDim):
		self.setColorHSL(
			int(360.*(nC+.5)/n2),
			100,
			int(ceil(100/xDim)), # It looks better fading to white as dim -> 1,
# 			50,                  # rather than being the real hue all the time.
		)

def Tree(degTheta, xTrunk, xMin, **dState):
	trtl = ColorTurtle(**dState)
	try: xR, isSolved = trtl.OsculatingRatio(degTheta)
	except ZeroDivisionError, ex: isSolved = False
	if not isSolved:
		print 'Can\'t solve polynomial at %f: %s' % degTheta, ex
	else:
		trtl.tree(degTheta, xR, xTrunk, xMin)
		return trtl.getDrawing()

def Forest(degDelta, tpImg, sclrBkgnd, xTrunk, path, **dState):
	c, degTheta = 0, 0.
	cMax = int(ceil(360./degDelta))
	xMin = 0.5
	xClock = xClockT = -clock()
	while degTheta <= 180.:
		img = PILImage('RGB', tpImg, sclrBkgnd, Tree(degTheta, xTrunk, xMin, **dState))
		img.save('%s/BT%03d.gif' % (path,      c))
		img.transpose(Image.FLIP_LEFT_RIGHT)
		img.save('%s/BT%03d.gif' % (path, cMax-c))
		xClock += clock()
		print '%s\t%.2f' % (degTheta, xClock)
		xClock = -clock()
		degTheta += degDelta
		c += 1
	xClockT += clock()
	print 'total %.2f' % xClockT

Forest(1., (320, 240), '#000', 96, 'reversed',
	lnPos=[160, 216],
	llnHeading=[[0,-1], [1,0]],
)

PILImage('RGB',
	tpImg     = (108, 100), 
	sclrBkgnd = '#fff',
	drwg = Tree(
		degTheta = 45,
		xTrunk   = 40,
		xMin     = .5,
		lnPos=[54, 90],
		llnHeading=[[0,-1], [1,0]],
	)
).save('tree.tn.png')
