#!BPY
"""
Name: '_L-system_forest_2.48'
Blender: 248
Group: 'Wizards'
Tip: 'Create L_System trees.'
"""

__author__ = "Armagan Yavuz, Jean-Michel Soler (jms), Chris Dawson "
__url__ = ("blender", "blenderartists.org")
__version__ = "248"

__bpydoc__ = """\
This script creates trees based on the L-system formula
Usage:

Open it from Wizards Menu
"""

################################################
# Blender Tree-maker Script v3.3.15
# The Forest Build
#
# This script is public domain. You can copy,
# distribute, and modify it in any way you
# like.
# Please keep the contribution information
# intact and add your details as you see fit
#
# CONTRIBUTORS:
# Versions to 1.3: 2001-2005 Armagan YAVUZ (www.geocites.com/blenderdungeon/) Original script including tree generation functionalty and UI
# Version	2.x: JMSoler (jmsoler.free.fr/util/blenderfile/images/lsystem/lsystem.htm)
# Versions 3.x: 2008 Chris Dawson (lsystem.liquidweb.co.nz) See website for details
# 
#########################################
#																		  	#
# PRESS ALT+P TO ACTIVATE THE SCRIPT		#
#																			  #
#########################################
#
# Modify the line below to set a default
# directory for configs
# Make sure you use forward slashes: 
# i.e. use	/ and not \ 
# this path is relative to either:
# * lsystem blend (if opened directly)
# * blender (if lsystem blend opened
#		 using menu:file:open)
#
#########################################

defaultdir = "savedconfigs/"





import Blender
import time
from lsystem import *
from rulesets import *
from stemRules import *
from leafRules import *
from Blender.Draw import *
from Blender.BGL import *
from Helpers import *

def GetVersionAsString():
	return str(versionMajor) + "." + str(versionMinor) + "." + str(versionRevision)



# version as used for file loading/saving
versionMajor = 3
versionMinor = 3
versionMinorName = "The Forest Build"
versionRevision = 15
# version as displayed in text
versionString = GetVersionAsString() + " - " + versionMinorName

fileMenuSelection = Create(1)	 # file menu button
btnGenericButton = None				 # empty button for catching button actions when return value is not needed (prevents memory leaks - see http://www.blender.org/documentation/246PythonDoc/Draw-module.html)

# control group coordinates
g_all_x = 10
g_all_y = 0
g_header_x = g_all_x + 0
g_header_y = g_all_y + 380
g_tabs_x = g_all_x + 0
g_tabs_y = g_all_y + 357
g_status_x = g_all_x + 0
g_status_y = g_all_y + 0
g_command_x = g_all_x + 0
g_command_y = g_all_y + 20

g_branches_x = g_all_x + 0
g_branches_y = g_all_y + 190
g_branchMaterial_x = g_all_x + 200
g_branchMaterial_y = g_all_y + 145
g_stem_x = g_all_x + 200
g_stem_y = g_all_y + 190
g_leafbranch_x = g_all_x + 0
g_leafbranch_y = g_all_y + 45

g_force_x = g_all_x + 200
g_force_y = g_all_y + 150
g_random_x = g_all_x + 200
g_random_y = g_all_y + 250
g_prune_x = g_all_x + 0
g_prune_y = g_all_y + 290
g_general_x = g_all_x + 0
g_general_y = g_all_y + 170

g_vertcalc_x = g_all_x + 0
g_vertcalc_y = g_all_y + 550

g_leaves_x = g_all_x + 0
g_leaves_y = g_all_y + 170

g_placement_x = g_all_x + 0
g_placement_y = g_all_y + 170
g_namingOptions_x = g_all_x + 200
g_namingOptions_y = g_all_y + 240
g_namingOptionsGroup_x = g_all_x + 200
g_namingOptionsGroup_y = g_all_y + 175
g_placementReplace_x = g_all_x + 0
g_placementReplace_y = g_all_y + 245

message = "Ready"
message_col = [0,0,0]

randSeed_rangeLow = 0						 # constant values for random seed ranges
randSeed_rangeHigh = 9999999				# constant values for random seed ranges

class Leaf:
	def __init__(self):
		self.MeshName = Create("a")
		self.scale = Create(1.0)
		self.slope = Create(90.0)
		self.orient = Create(0.0)
		self.probability = Create(10)
		self.rotSide = None
		self.rotForward = None

class TreeConfig():
	global Leaf
	def __init__(self, randSeed_rangeHigh, randSeed_rangeLow):
		
		# PROPERTIES
		self.orientation = Create(0.5)
		self.force = [Create(0.0), Create(90.0), Create(0.0)]
		self.scaleforce = Create(0.0)
		self.iteration = Create(2)
		self.resolutionLOD= Create(0.75)
		self.resolution= Create(8)
		
		self.stemresolutionV= Create(2)
		self.leafresolutionV= Create(1)
		self.repeat= Create(1)
		self.stemrepeat= Create(1)
		self.leafrepeat= Create(1)
		self.bangle = Create(90.0)
		self.stembangle = Create(90.0)
		self.leafbangle = Create(90.0)
		self.menu= Create(5)
		self.stemmenu= Create(1)
		self.leafname= Create("a")
		self.leafProbability = Create(10)
		self.useleaf = Create(1)
		self.leafscale = Create(1.0)
		self.thickness = Create(0.1)
		self.length = Create(0.5)
		self.leaflength = Create(1.0)
		self.rotF = Create(0.0)
		self.stemrotF = Create(0.0)
		self.leafrotF = Create(0.0)
		self.curve = Create(0.0)
		self.stemcurve = Create(0.0)
		self.leafcurve = Create(0.0)
		self.stemorientation = Create(0.0)
		self.conism = Create(0.05)
		self.shorten = Create(0.05)
		self.branch_scale = Create(0.75)
		self.stembranch_scale = Create(0.75)
		self.lenscale = Create(0.75)
		self.stemlenscale = Create(0.75)
		self.prune = Create(0.0)
		self.prunefirst = Create(0)
		
		# stem members

		# branch members
		self.resolutionV = Create(16)
		
		#branch texture
		self.branchMaterial = Create(0)
		
		self.branchSeed_lock = Create(0)
		self.branchSeed_seed = Create(random.randint(randSeed_rangeLow, randSeed_rangeHigh))
		
		self.selectedLeaf = Create(0)
		self.leafSeed_lock = Create(0)
		self.leafSeed_seed = Create(random.randint(randSeed_rangeLow, randSeed_rangeHigh))

		self.leafbank = Create(0.0)
		self.maxdepth = Create(30)
		self.randScale = Create(0.1)
		self.rot_rand_per = Create(0.1)
		self.rot_rand_abs = Create(0.1)
		self.prune_level = Create(6)
		self.branchLeafRot = Create(0.0)
		self.addEndPoint = Create(0)
		self.leafmenu= Create(9)
		self.leavesAroundBranch = Create(3)
		self.leavesRepeatAlongSegment = Create(1)
		
		# leaves
		self.leaves = [Leaf()]									# collection of Leaf objects for this tree (with default leaf)


	'''
	leaf population is [x] where x is array index of leaf in leaves, repeated for probability times, for each leaf in leaves
	example: [0,0,0,0,0,0,1,1,1,1,1,1,1,1,1] leaves[0].probability is 6, leaves[1] probability is 9
	if there is only leaf in leaves, leafPopulation is processed as [0] (one instance of leaves[0]) in an attempt speed things up during generation
	'''
	def GetLeafPopulation(self):
		leafPopulation = []
		if (len(self.leaves) > 1):
			for i in range(0, len(self.leaves)):
				for j in range(0, self.leaves[i].probability.val):
					leafPopulation.append(i)
		else:
			leafPopulation = [0]
		return leafPopulation

class GenerationSettings:
	pass

class GenerationConfig:
	def __init__(self):
		self.TreeConfig = TreeConfig(randSeed_rangeHigh, randSeed_rangeLow)										 # the tree configuration that this config is all about
		self.matchScale = Create(0)																														 # scale is same as object tree is placed at
		self.matchRotation = Create(0)																													# rotation is same as object tree is placed at
		self.identical = Create(0)																															# all trees generated are identical (use same random seed settings)
		self.probability = Create(10)																													 # probability of this tree being selected from a collection
		self.generationSettings = None																													# object that stores settings used during generation
		self.filename = ""																																			# filename of config that stores this trees data
		
GenerationConfigs = []																																			# a list of GenerationConfig objects
GenerationConfigs.append(GenerationConfig())																								# the default config
currentGenerationConfig = Create(0)																												 # the generation config used by the UI

regenLastObject = Create(1)																														 # regenerate the current object (wired for placing single trees only)
regenLastObjectNames = [None,None]																												# treeObject, leafObject
regenLastSelectedItems = None				 # stores the last selected item for comparison
regenLastCursorPos = None						 # stores the last cursor pos for comparison
regenLastPlacementOptionUsed = None	 # stores the last placement option used so we can check when they have been switched between

# config population is [x] where x is array index of config in configs, repeated for probability times, for each config in configs
# example: [0,0,0,0,0,0,1,1,1,1,1,1,1,1,1] config[0].probability is 6, config[1] probability is 9
# if there is only config in configs, configPopulation is processed as [0] (one instance of configs[0])
def GetConfigPopulation():
	configurationPopulation = []
	if (len(GenerationConfigs) > 1):
		for i in range(0, len(GenerationConfigs)):
			for j in range(0, GenerationConfigs[i].probability.val):
				configurationPopulation.append(i)
	else:
		configurationPopulation = [0]
	return configurationPopulation

# placement options
placementSelectionMenu = Create(0)													# menu for selecting method of placement
placementMultipleConfigurationMenu = Create(1)

# Naming Options
newTreeObjectPrefix = Create("my")
newTreeBranchObjectName = Create("tree")
newTreeLeavesObjectName = Create("leaves")
newTreeGroupNameMenu = Create(0)
newTreeGroupName = Create("treeGroup")

#tabs
tabGeneral = Create(0)
tabBranches = Create(0)
tabLeaves = Create(0)
tabOptions = Create(1)

deg2rad = 3.1416 / 180.0



# UI HELPER FUNCTIONS
# error is 1, status is 0
def SetMessage(messageString, messageTypeInt):
	global message, message_col
	message = messageString
	if (messageTypeInt == 1):
		message_col = [Create(1), Create(0), Create(0)]
	else:
		message_col = [Create(0), Create(0), Create(0)]
	Draw()
	
# write text
def WriteText(text, x , y):
	glRasterPos2i(x,y)
	Text(text)
	
# get file name from full path
def GetFileNameFromFullPath(filename):
# global userDefaultsFileName, originalDefaultsFileName, tempDefaultsFileName
	thruple = filename.rpartition("\\")
	thruple2 = thruple[2].rpartition("/")
	if thruple2[2] != userDefaultsFileName:
		if thruple2[2] != originalDefaultsFileName:
			if thruple2[2] != tempDefaultsFileName:
				return thruple2[2]
	# else, the file name is 'restricted' so we don't want to show it in the UI
	return ""

#selectedConfig = Create(0)
	
def AddNewLeafToLeaves():
	config = GenerationConfigs[currentGenerationConfig.val]
	
	config.TreeConfig.leaves.append(Leaf())
	config.TreeConfig.selectedLeaf.val = len(config.TreeConfig.leaves)
	SetMessage("Leaf added",0)
	Draw()

def RemoveLeafFromLeaves():
	config = GenerationConfigs[currentGenerationConfig.val]

	if (len(config.TreeConfig.leaves) > 1):
		config.TreeConfig.leaves.pop(selectedLeaf.val - 1)
		config.TreeConfig.selectedLeaf.val = len(leaves)
		SetMessage("Leaf removed",0)
	else:
		SetMessage("Must define at least 1 leaf",1)
	Draw()
	
def SetFriendlySelectedConfig(event, val):															# allows the current generated config to display index 0 as config 1
	currentGenerationConfig.val = val - 1
	
def AddGenerationConfig(event, val):
	GenerationConfigs.append(GenerationConfig())
	currentGenerationConfig.val = len(GenerationConfigs) - 1
	SetMessage("Tree config added", 0)
	
def RemoveGenerationConfig(event, val):
	if (len(GenerationConfigs) > 1):
		GenerationConfigs.pop(currentGenerationConfig.val)
		if (currentGenerationConfig.val > len(GenerationConfigs) - 1):
			currentGenerationConfig.val -= 1
	else:
		SetMessage("Must specify at least one tree config", 1)

def GetNumberOfVertsInObject(objectName):
	return len(Blender.Object.Get(objectName).data.verts)
def GetNumberOfFacesInObject(objectName):
	return len(Blender.Object.Get(objectName).data.faces)
def GetNumberOfEdgesInObject(objectName):
	return len(Blender.Object.Get(objectName).data.edges)

def draw(ui=0):
	if (ui > 0):
		pass
	elif (ui == 0):
		global tabGeneral, tabOptions, tabLeaves, tabBranches
		global fileMenuSelection, currentGenerationConfig
		
		config = GenerationConfigs[currentGenerationConfig.val]
		
		#COLORS
		glClearColor(0.72, 0.72, 0.75, 0.0)
		glClear(GL_COLOR_BUFFER_BIT)
		
		#HEADER
		glColor3f(0.63,0.63,0.63)
		glRasterPos2i(2 + g_header_x, 24 + g_header_y)
		Text("LSystem v" + versionString, "normal")
		glColor3f(0.1,0.1,0.1)
		glRasterPos2i(1 + g_header_x, 25 + g_header_y)
		Text("LSystem v" + versionString, "normal")
		fileMenuOptions = "File Menu%t | File Menu %x0 | Load Config... %x1 | Save Config... %x2 |%l| Load User Defaults %x4| Save User Defaults %x5 | %l | Load Original Defaults %x6 | Load Last Generated %x7"
		fileMenuSelection = Menu(fileMenuOptions, 14, 294 + g_header_x, 20 + g_header_y, 96, 18, 0 ,"File Menu")
		
		#CONFIG SELECTION
		btnGenericButton = Button("+", 2, 76 +g_header_x, 0 + g_header_y, 18, 18, "Add a new default config", AddGenerationConfig)
		btnGenericButton = Number(str(len(GenerationConfigs)) + " #", 2, 18 + g_header_x, 0 + g_header_y, 58, 18, currentGenerationConfig.val + 1, 1, len(GenerationConfigs), "Current configuration", SetFriendlySelectedConfig)
		btnGenericButton = Button("-", 2, 0 + g_header_x, 0 + g_header_y, 18, 18, "Remove this config", RemoveGenerationConfig)
		WriteText("Name: " + GetFileNameFromFullPath(config.filename), 100 + g_header_x, 5 + g_header_y)
		
		#TABS
		tabGeneral = Toggle("General", 30, 0 + g_tabs_x, 0 + g_tabs_y, 96, 18, tabGeneral.val, "General Options")
		tabBranches = Toggle("Branches", 31, 98 + g_tabs_x, 0 + g_tabs_y, 96, 18, tabBranches.val, "Branching Options")
		tabLeaves = Toggle("Leaves", 32, 196+ g_tabs_x, 0 + g_tabs_y, 96, 18, tabLeaves.val, "Leaf Options")
		tabOptions = Toggle("Options", 33, 294 + g_tabs_x, 0 + g_tabs_y, 96, 18, tabOptions.val, "Options")
			
		#TABS SEPARATORS
		glColor3f(0.6,0.6,0.6)
		if (tabGeneral.val == 1):
			for i in range(99,391):
				glRasterPos2i(g_tabs_x -1 + i, g_tabs_y + -3)
				Text(".")
		elif (tabBranches.val == 1):
			for i in range(0,96):
				glRasterPos2i(g_tabs_x -1 + i, g_tabs_y + -3)
				Text(".")
			for i in range(197,391):
				glRasterPos2i(g_tabs_x -1 + i, g_tabs_y + -3)
				Text(".")
		elif (tabLeaves.val == 1):
			for i in range(0,194):
				glRasterPos2i(g_tabs_x -1 + i, g_tabs_y + -3)
				Text(".")
			for i in range(295,391):
				glRasterPos2i(g_tabs_x -1 + i, g_tabs_y + -3)
				Text(".")
		elif (tabOptions.val == 1):
			for i in range(0,292):
				glRasterPos2i(g_tabs_x -1 + i, g_tabs_y + -3)
				Text(".")
		
		#COMMAND
		btnGenericButton = Button("GENERATE", 3, 0 + g_command_x, 0 + g_command_y, 94, 18)
		btnGenericButton = Button("EXIT", 1, 296 + g_command_x, 0 + g_command_y, 94, 18, "Exit (Q)")
			
		#SEPARATOR
		#vertical
		glColor3f(0.6,0.6,0.6)
		for i in range(1,320):
			glRasterPos2i(204, 25 + i*1)
			Text(".")
		 
		#STATUS
		#status separator
		glColor3f(0.6,0.6,0.6)
		for i in range(0,390):
			glRasterPos2i(g_status_x + i*1, g_status_y + 16)
			Text(".")
		glColor3f(0,0,0)
		WriteText("Status: ", 1 + g_status_x, 5 + g_status_y)
		glColor3f(message_col[0].val, message_col[1].val, message_col[2].val)
		glRasterPos2i(45 + g_status_x, 5 + g_status_y)
		Text(message)
		glColor3f(0,0,0)
	
		# call draw for active tab
		if (tabGeneral.val == 1):
			drawMain()
		elif (tabOptions.val == 1):
			drawOptions()
		elif (tabLeaves.val == 1):
			drawLeaves()
		elif (tabBranches.val == 1):
			drawBranches()
		
def drawBranches():
	config = GenerationConfigs[currentGenerationConfig.val]
	
	#BRANCHES
	#Branch Material
	WriteText("Branch Material:", 1 + g_branchMaterial_x, 25 + g_branchMaterial_y)
	branchMaterialOptions = "Materials %t |"
	matCount = len(Material.Get())
	matCounter = 0
	for mat in Material.Get():
		branchMaterialOptions += mat.getName() + " %x" + str(matCounter)
		if matCounter +1 < matCount: branchMaterialOptions += "| "
		matCounter += 1
	config.TreeConfig.branchMaterial = Menu(branchMaterialOptions, 2, 0+ g_branchMaterial_x, 0 + g_branchMaterial_y, 190, 18, config.TreeConfig.branchMaterial.val, "Material used for branches")

	#BRANCHES
	WriteText("Branches:", 1 + g_branches_x, 145 + g_branches_y)
	branchMenuOptions = "Ruleset%t| Straight %x1| Arm1 2D%x10| Arm1 3D%x11| Arm1+1 2D%x4| Arm1+1 3D%x12| Split2 2D%x2| Split2 3D%x3| Arm2 2D%x5| Arm2 3D%x6| Arm2+2 %x7| Split3%x8| Arm3%x9"
	config.TreeConfig.menu					= Menu(branchMenuOptions, 2, 96 + g_branches_x, 140 + g_branches_y, 94, 18, config.TreeConfig.menu.val,"Branching Pattern")
	config.TreeConfig.orientation	 = Slider("subOrient ", 2, 0 + g_branches_x, 40 + g_branches_y, 190, 18, config.TreeConfig.orientation.val, 0.0, 1.0, 0, "Orientation of child branch.(Pattern specific)")
	config.TreeConfig.bangle				= Slider("subSlope ", 2, 0 + g_branches_x, 60 + g_branches_y, 190, 18, config.TreeConfig.bangle.val, 0.0, 180.0, 0, "Angle between child branch and parent branch")
	config.TreeConfig.lenscale			= Slider("subLen ", 2, 0 + g_branches_x, 20 + g_branches_y, 190, 18, config.TreeConfig.lenscale.val, 0.5, 1.0, 0, "Length of child branch compared to parent")
	config.TreeConfig.branch_scale	= Slider("subThickns ", 2, 0 + g_branches_x, 0 + g_branches_y, 190, 18, config.TreeConfig.branch_scale.val, 0.5, 1.0, 0, "Thickness of child branch compared to parent")
	config.TreeConfig.rotF					= Slider("Spin ", 2, 0 + g_branches_x, 100 + g_branches_y, 190, 18, config.TreeConfig.rotF.val, -360.0, 360.0, 0, "amount of spinning along the branch")
	config.TreeConfig.curve				 = Slider("Curve ", 2, 0 + g_branches_x, 80 + g_branches_y, 190, 18, config.TreeConfig.curve.val, -360.0, 360.0, 0, "curvature of the branch")
	config.TreeConfig.resolutionV	 = Number("resV: ", 2, 0 + g_branches_x, 120 + g_branches_y, 94, 18, config.TreeConfig.resolutionV.val, 1, 16, "Vertical resolution: Number of mesh strips at each branch segment" )
	config.TreeConfig.repeat				= Number("repeat: ", 2, 96 + g_branches_x, 120 + g_branches_y, 94, 18, config.TreeConfig.repeat.val, 1, 10, "Number of times pattern is repeated before next iteration")

	#STEM
	WriteText("Stem:", 1 + g_stem_x, 145 + g_stem_y)
	stemMenuOptions = "Ruleset%t| None %x1| Straight%x2| Arm1%x3| Arm1+1%x4| Arm2%x5| Arm2+2%x6| Arm3%x7"
	config.TreeConfig.stemmenu = Menu(stemMenuOptions, 2, 96 + g_stem_x, 140 + g_stem_y, 94, 18, config.TreeConfig.stemmenu.val, "use a special ruleset for the stem")
	if (config.TreeConfig.stemmenu.val != 1):
		config.TreeConfig.stemorientation	 = Slider("subOrient ", 2, 0 + g_stem_x, 40 + g_stem_y, 190, 18, config.TreeConfig.stemorientation.val, -180.0, 180.0, 0)
		config.TreeConfig.stembangle				= Slider("subSlope ", 2, 0 + g_stem_x, 60 + g_stem_y, 190, 18, config.TreeConfig.stembangle.val, 0.0, 180.0, 0)
		config.TreeConfig.stemlenscale			= Slider("subLen ", 2, 0 + g_stem_x, 20 + g_stem_y, 190, 18, config.TreeConfig.stemlenscale.val, 0.2, 2.0, 0)
		config.TreeConfig.stembranch_scale	= Slider("subThickns", 2, 0 + g_stem_x, 0 + g_stem_y, 190, 18, config.TreeConfig.stembranch_scale.val, 0.2, 1.0, 0)
		config.TreeConfig.stemrotF					= Slider("Spin ", 2, 0 + g_stem_x, 100 + g_stem_y, 190, 18, config.TreeConfig.stemrotF.val, -360.0, 360.0, 0)
		config.TreeConfig.stemcurve				 = Slider("Curve ", 2, 0 + g_stem_x, 80 + g_stem_y, 190, 18, config.TreeConfig.stemcurve.val, -360.0, 360.0, 0)
		config.TreeConfig.stemresolutionV	 = Number("resV: ", 2, 0 + g_stem_x, 120 + g_stem_y, 94, 18, config.TreeConfig.stemresolutionV.val, 1, 16)
		config.TreeConfig.stemrepeat				= Number("repeat: ", 2, 96 + g_stem_x, 120 + g_stem_y, 94, 18, config.TreeConfig.stemrepeat.val, 1, 30)

	#LEAFBRANCHES
	WriteText("Leaf Branches:", 1 + g_leafbranch_x, 125 + g_leafbranch_y)
	# these values effect branches even with leaves turned off. set always visible
	config.TreeConfig.leaflength									= Slider("lengthMul ", 2, 0 + g_leafbranch_x, 80 + g_leafbranch_y, 190, 18, config.TreeConfig.leaflength.val, 0.2, 5.0, 0, "Additional length adjustment for leaf carrying branches") 
	config.TreeConfig.leafcurve									 = Slider("ExtraCurve	", 2, 0 + g_leafbranch_x, 60 + g_leafbranch_y, 190, 18, config.TreeConfig.leafcurve.val, -90.0, 90.0, 0, "extra curvature for leaf-carrying branches")
	
	leafMenuOptions = "Ruleset%t| EndPoint%x1|Active Point%x7| 1+1 Perpend%x3| 2 Perpend%x5| 2+2 Perpend%x6| 3 Perpend%x8| Advanced%x9"
	config.TreeConfig.leafmenu										= Menu(leafMenuOptions, 2, 0 + g_leafbranch_x, 100 + g_leafbranch_y, 94, 18, config.TreeConfig.leafmenu.val,"Leaf placement pattern") 
	
	# resV doesn't do anything
	#leafresolutionV= Number("resV: ", 2, 0 + g_leaves_x, 160 + g_leaves_y, 94, 18, leafresolutionV.val, 1, 1, "Vertical resolution for leaf carrying branches")
	
	if not (config.TreeConfig.leafmenu.val in [1,7]):
		config.TreeConfig.leafrotF									= Slider("ExtraSpin ", 2, 0 + g_leafbranch_x, 40 + g_leafbranch_y, 190, 18, config.TreeConfig.leafrotF.val, -90.0, 90.0, 0, "extra spinning for leaf-carrying branches")
		config.TreeConfig.leafrepeat								= Number("Repeat: ", 2, 96 + g_leafbranch_x, 100 + g_leafbranch_y, 94, 18, config.TreeConfig.leafrepeat.val, 1, 30,"repeat pattern this many times for each branch")
	# additional controls for l_advanced
	if (config.TreeConfig.leafmenu.val == 9):
		config.TreeConfig.leavesAroundBranch				= Slider("Leaves Around Branch ", 2, 0 + g_leafbranch_x, 20 + g_leafbranch_y, 190, 18, config.TreeConfig.leavesAroundBranch.val, 1, 10, 1,"Number of leaves around branch")
		config.TreeConfig.leavesRepeatAlongSegment	= Slider("Leaves Repeat ", 2, 0 + g_leafbranch_x, 0 + g_leafbranch_y, 190, 18, config.TreeConfig.leavesRepeatAlongSegment.val, 1, 10, 1,"Repeat n times along segment (auto rotates 360)")

def drawOptions():
	config = GenerationConfigs[currentGenerationConfig.val]
	
	#PLACEMENT
	global placementSelectionMenu, placementMultipleConfigurationMenu
	global placementMatchScale, placementMatchRotation, placementMultipleConfiguration
	global newTreeObjectPrefix, newTreeBranchObjectName, newTreeLeavesObjectName
	global configurations, selectedConfiguration
	global regenLastObject
	
	WriteText("Placement:", 1 + g_placement_x, 165 + g_placement_y)
	placementSelectionOptions = "Tree Placement Options %t | Cursor %x0 | Grid (Center) %x1 | Selected or Active Object %x2 | Selected Objects (Forest) %x3"
	placementSelectionMenu = Menu(placementSelectionOptions, 2, 0 + g_placement_x, 140 + g_placement_y, 190, 18, placementSelectionMenu.val ,"Where a new tree will be placed")
	
	if (placementSelectionMenu.val in [2]):										 # selected object
		config.matchScale			= Toggle("Match Scale", 2 , 0 + g_placement_x, 120 + g_placement_y, 94, 18, config.matchScale.val, "Match the scale of the placement marker object")
		config.matchRotation	 = Toggle("Match Rot", 2 , 96 + g_placement_x, 120 + g_placement_y, 94, 18, config.matchRotation.val, "Match the rotation of the placement marker object")


	# if we are working with multiple placements
	if (placementSelectionMenu.val in [3]):
		placementMultipleConfigurationOptions = "Multiple Placement Type %t | Single (selected) Configuration %x0 | Multiple Configurations %x1"
		placementMultipleConfigurationMenu = Menu(placementMultipleConfigurationOptions, 2, 0 + g_placement_x, 120 + g_placement_y, 190, 18, placementMultipleConfigurationMenu.val, "Type of multiple (selected objects) placement to use")
		
		if (placementMultipleConfigurationMenu.val in [0]):		 # using single config
			config.matchScale			= Toggle("Match Scale", 2 , 0 + g_placement_x, 100 + g_placement_y, 94, 18, config.matchScale.val, "Match the scale of the placement marker object")
			config.matchRotation	 = Toggle("Match Rot", 2 , 96 + g_placement_x, 100 + g_placement_y, 94, 18, config.matchRotation.val, "Match the rotation of the placement marker object")
			config.identical			 = Toggle("Make Identical", 2, 0 + g_placement_x, 80 + g_placement_y, 94, 18, config.identical.val, "If you want all your trees to look the same, turn random locks on and select this option. Otherwise random lock settings will only be used for the first tree generated.")

		if (placementMultipleConfigurationMenu.val in [1]):			# using multiple configs
			WriteText("Multiple Configuration Settings:", 1 + g_placement_x, 105 + g_placement_y)
			# options are config specific. show control to change between configs
			WriteText("Configuration:", 1 + g_placement_x, 85 + g_placement_y)
			btnGenericButton = Number(str(len(GenerationConfigs)) + " #", 2, 96 + g_placement_x, 80 + g_placement_y, 94, 18, currentGenerationConfig.val + 1, 1, len(GenerationConfigs), "Current configuration", SetFriendlySelectedConfig)
			
			# options for the selected config

			config.matchScale			= Toggle("Match Scale", 2 , 0 + g_placement_x, 60 + g_placement_y, 94, 18, config.matchScale.val, "Match the scale of the placement marker object")
			config.matchRotation	 = Toggle("Match Rot", 2 , 96 + g_placement_x, 60 + g_placement_y, 94, 18, config.matchRotation.val, "Match the rotation of the placement marker object")
			config.identical			 = Toggle("Make Identical", 2, 0 + g_placement_x, 40 + g_placement_y, 94, 18, config.identical.val, "If you want all your trees to look the same, turn random locks on and select this option. Otherwise random lock settings will only be used for the first tree generated.")
			config.probability		 = Slider("Probability ", 2, 0 + g_placement_x, 20 + g_placement_y, 190, 18, config.probability.val, 1, 10, 0, "Probability of this configuartion being used (compares to other configurations)")
			
	# classic placement option
	if (placementSelectionMenu.val in [0,1,2]):					 # if we are only placing one tree
		WriteText("Regenerate Last Object:", 1 + g_placementReplace_x, 25 + g_placementReplace_y)
		regenLastObject = Toggle("Regen Last Object", 2, 0 + g_placementReplace_x, 0 + g_placementReplace_y, 96, 18, regenLastObject.val, "Replace last generated tree (single objects only) 'Legacy Mode'")
		

	#NAMING OPTIONS
	WriteText("Naming Options For New Trees:", 1 + g_namingOptions_x, 95 + g_namingOptions_y)
	newTreeObjectPrefix = String("Name Prefix: ", 2, 0 + g_namingOptions_x, 70 + g_namingOptions_y, 190, 18, newTreeObjectPrefix.val, 6, "Prefix for new tree and leaf object and mesh names. Can be blank.")
	newTreeBranchObjectName = String("Trunk Object Name: ", 2, 0 + g_namingOptions_x, 50 + g_namingOptions_y, 190, 18, newTreeBranchObjectName.val, 12, "Name for new tree object and mesh names. Suffixed with .001, .002 etc for multiple trees. Required. If not set will default to 'Tree'")
	newTreeLeavesObjectName = String("Leaf Object Name: ", 2, 0 + g_namingOptions_x, 30 + g_namingOptions_y, 190, 18, newTreeLeavesObjectName.val, 12, "Name for new leaf object and mesh names. Suffixed with .001, .002 etc for multiple trees. Requird. If not set will default to 'Leaves'")
	if (newTreeBranchObjectName == ""):
		newTreeBranchObjectName.val = "Tree"
		Draw()
	if (newTreeLeavesObjectName == ""):
		newTreeLeavesObjectName.val = "Leaves"
		Draw()
	WriteText("Example: ", 1 + g_namingOptions_x, 15 + g_namingOptions_y)
	WriteText(newTreeObjectPrefix.val + newTreeBranchObjectName.val + ".001", 55 + g_namingOptions_x, 15 + g_namingOptions_y)
	WriteText(newTreeObjectPrefix.val + newTreeLeavesObjectName.val + ".001", 55 + g_namingOptions_x, 0 + g_namingOptions_y)
	
	#GROUPING OPTIONS
	global newTreeGroupNameMenu, newTreeGroupName
	WriteText("Grouping Options:", 1+ g_namingOptionsGroup_x, 45 + g_namingOptionsGroup_y)
	if (regenLastObject.val == 1) & (placementSelectionMenu.val != 3):
		newTreeGroupNameMenuOptions = "Grouping Options %t|Don't add to group %x0|Add to specific group: %x2"
		if (newTreeGroupNameMenu.val == 1):
			newTreeGroupNameMenu.val = 0
			Draw()
	else:
		newTreeGroupNameMenuOptions = "Grouping Options %t|Don't add to group %x0|Add to new group (based on tree name) %x1|Add to specific group: %x2"
	newTreeGroupNameMenu = Menu(newTreeGroupNameMenuOptions, 2, g_namingOptionsGroup_x, 20 + g_namingOptionsGroup_y, 192, 18, newTreeGroupNameMenu.val, "Options for new trees to be placed in groups")
	if (newTreeGroupNameMenu.val == 2):
		newTreeGroupName = String("Group Name: ", 2, g_namingOptionsGroup_x, 0 + g_namingOptionsGroup_y, 192, 18, newTreeGroupName.val, 20, "Group name to be used")
		if (newTreeGroupName == ""):
			newTreeGroupName.val = "groupName"
			Draw()

def drawLeaves():
	config = GenerationConfigs[currentGenerationConfig.val]
	
	#LEAVES
	WriteText("Leaves:", 1 + g_leaves_x, 165 + g_leaves_y)
	config.TreeConfig.useleaf = Toggle("Draw Leaves", 2, 96 + g_leaves_x, 160 + g_leaves_y, 94, 18, config.TreeConfig.useleaf.val, "Add leaves")
	if (config.TreeConfig.useleaf.val == 1):

		leafCount = len(config.TreeConfig.leaves)
		selectedLeaf = config.TreeConfig.selectedLeaf.val

		config.TreeConfig.branchLeafRot = Slider("Leaf Rot ", 2, 0 + g_leaves_x, 140 + g_leaves_y, 190, 18, config.TreeConfig.branchLeafRot.val, -90, 90.0, 0,"Sets the starting angle for rotation of leaves around branch")

		# leaf addition controls
		btnGenericButton = Button("+", 4, 172 + g_leaves_x, 120 + g_leaves_y, 18, 18)
		config.TreeConfig.selectedLeaf = Number(str(leafCount) + " #", 2, 114 + g_leaves_x, 120 + g_leaves_y, 58, 18, config.TreeConfig.selectedLeaf.val, 1, leafCount, "Current leaf")
		btnGenericButton = Button("-", 5, 96 + g_leaves_x, 120 + g_leaves_y, 18, 18)

		# leafname
		config.TreeConfig.leaves[selectedLeaf - 1].MeshName = String("leafmesh: ", 2, 0 + g_leaves_x, 120 + g_leaves_y, 94, 18, config.TreeConfig.leaves[selectedLeaf - 1].MeshName.val, 12, "Name of Blender Mesh that serves as a model for leaves")

		#leafscale
		config.TreeConfig.leaves[selectedLeaf - 1].scale = Slider("LeafScale ", 2, 0 + g_leaves_x, 100 + g_leaves_y, 190, 18, config.TreeConfig.leaves[selectedLeaf - 1].scale.val, 0.2, 5.0, 0, "Leaf scale")

		#leafbangle
		if not (config.TreeConfig.leafmenu.val in [1,7]):
			config.TreeConfig.leaves[selectedLeaf - 1].slope = Slider("LeafSlope ", 2, 0 + g_leaves_x, 80 + g_leaves_y, 190, 18, config.TreeConfig.leaves[selectedLeaf - 1].slope.val, 0.0, 180.0, 0,"Angle between leaf and branch")

		#leafbank
		config.TreeConfig.leaves[selectedLeaf - 1].orient = Slider("LeafOrient ", 2, 0 + g_leaves_x, 60 + g_leaves_y, 190, 18, config.TreeConfig.leaves[selectedLeaf - 1].orient.val, -90.0, 90.0, 0, "Leaf orientation")

		# probability
		config.TreeConfig.leaves[selectedLeaf - 1].probability = Slider("Probability: ", 2, 0 + g_leaves_x, 40 + g_leaves_y, 190, 18, config.TreeConfig.leaves[selectedLeaf -1].probability.val, 1, 10, 0, "Probability of leaf being added to mesh (compares with other leaves probability)")

		#random leaf
		config.TreeConfig.leafSeed_lock = Toggle("Lock Seed",2, 0 + g_leaves_x, 20 + g_leaves_y, 94, 18, config.TreeConfig.leafSeed_lock.val, "Lock random seed for leaf generation")
		config.TreeConfig.leafSeed_seed = Number("Seed: ", 2, 96 + g_leaves_x, 20 + g_leaves_y, 94, 18, config.TreeConfig.leafSeed_seed.val, randSeed_rangeLow, randSeed_rangeHigh, "Seed to use (" + str (randSeed_rangeLow) + " to " + str(randSeed_rangeHigh) + ")")

		#endpoint
		if not (config.TreeConfig.leafmenu.val in [1,7]):
			config.TreeConfig.addEndPoint = Toggle("Add Endpoint",2, 0 + g_leaves_x, 0 + g_leaves_y, 94, 18, config.TreeConfig.addEndPoint.val, "Adds an endpoint leaf to the branch")


def drawMain():
	config = GenerationConfigs[currentGenerationConfig.val]

	#FORCE
	WriteText("Force:", 1 + g_force_x, 85 + g_force_y)
	config.TreeConfig.scaleforce	= Slider("useThickness", 2, 0 + g_force_x, 60 + g_force_y, 190, 18, config.TreeConfig.scaleforce.val, 0.0, 1.0, 0, "Make force affect only thinner branches")
	config.TreeConfig.force[0]		= Slider("Bearing ", 2, 0 + g_force_x, 40 + g_force_y, 190, 18, config.TreeConfig.force[0].val, 0.0, 360.0, 0, "bearing of force vector")
	config.TreeConfig.force[1]		= Slider("Elevation ", 2, 0 + g_force_x, 20 + g_force_y, 190, 18, config.TreeConfig.force[1].val, -90.0, 90.0, 0, "elevation of force vector")
	config.TreeConfig.force[2]		= Slider("Magnitude ", 2, 0 + g_force_x, 0 + g_force_y, 190, 18, config.TreeConfig.force[2].val, 0.0, 1.0, 0, "magnitude of force vector")

	#PRUNE
	WriteText("Prune:", 1 + g_prune_x, 45 + g_prune_y)
	pruneLevelMenuText = "Level%t| All%x6 | All but 2%x1| All but 2-3%x2| Last two%x4|	Last%x5"
	config.TreeConfig.prune_level		 = Menu(pruneLevelMenuText, 2, 96 + g_prune_x, 40 + g_prune_y, 94, 18, config.TreeConfig.prune_level.val,"restrict pruning to these levels")
	config.TreeConfig.prune					 = Slider("Prob ", 2, 0 + g_prune_x, 20 + g_prune_y, 190, 18, config.TreeConfig.prune.val, 0.0, 0.9, 0, "Probability of pruning")
	config.TreeConfig.prunefirst			= Number("PruneFirst: ", 2, 0 + g_prune_x, 0 + g_prune_y, 94, 18, config.TreeConfig.prunefirst.val, 0, 29, "Prune the first n iterations" )
	config.TreeConfig.maxdepth				= Number("MaxDepth: ", 2, 96 + g_prune_x, 0 + g_prune_y, 94, 18, config.TreeConfig.maxdepth.val, 0, 30, "Maximum branching depth")

	#RANDOMNESS
	WriteText("Randomness:", 1 + g_random_x, 85 + g_random_y)
	config.TreeConfig.randScale			 = Slider("RnScale ", 2, 0 + g_random_x, 60 + g_random_y, 190, 18, config.TreeConfig.randScale.val, 0.0, 0.5, 0, "randomly scale down child branches")
	config.TreeConfig.rot_rand_per		= Slider("RnDir ",	2, 0 + g_random_x, 40 + g_random_y, 190, 18, config.TreeConfig.rot_rand_per.val, 0.0, 1.0, 0, "randomly change direction of child branches")
	config.TreeConfig.rot_rand_abs		= Slider("RnTwist ", 2, 0 + g_random_x, 20 + g_random_y, 190, 18, config.TreeConfig.rot_rand_abs.val, 0.0, 1.0, 0, "randomly twist the tree")
	config.TreeConfig.branchSeed_lock = Toggle("Lock Seed",2, 0 + g_random_x, 0 + g_random_y, 94, 18, config.TreeConfig.branchSeed_lock.val, "Lock random seed")
	config.TreeConfig.branchSeed_seed = Number("Seed: ", 2, 96 + g_random_x, 0 + g_random_y, 94, 18, config.TreeConfig.branchSeed_seed.val, randSeed_rangeLow, randSeed_rangeHigh, "Seed to use (" + str (randSeed_rangeLow) + " to " + str(randSeed_rangeHigh) + ")")

	# TODO: find out where this is used and/or how:
	# randThin = Slider("RnThinDwn ", 2, 40, 125 + g_random_y, 150, 18, randThin.val, 0.0, 1.0, 0)
	
	#GENERAL
	WriteText("General:", 1 + g_general_x, 105 + g_general_y)
	config.TreeConfig.resolution			= Number("resU: ", 2, 0 + g_general_x, -20 + g_general_y, 94, 18, config.TreeConfig.resolution.val, 3, 32, "Number of vertices in the cross-section of a branch")
	config.TreeConfig.resolutionLOD	 = Slider("LOD ", 2, 0 + g_general_x, 80 + g_general_y, 190, 18, config.TreeConfig.resolutionLOD.val, 0.0, 1.0, 0, "Level-Of-Detail: Decrease resolutionU as branches get thinner.")
	config.TreeConfig.length					= Slider("Scale ", 2, 0 + g_general_x, 60 + g_general_y, 190, 18, config.TreeConfig.length.val, 0.0, 1.0, 0, "scale of the tree")
	config.TreeConfig.thickness			 = Slider("Thickness ", 2, 0 + g_general_x, 40 + g_general_y, 190, 18, config.TreeConfig.thickness.val, 0.0, 0.5, 0, "thickness of the tree")
	config.TreeConfig.shorten				 = Slider("Shorten ", 2, 0 + g_general_x, 20 + g_general_y, 190, 18, config.TreeConfig.shorten.val, 0.0, 0.2, 0, "uniform shortening along the tree")
	config.TreeConfig.conism					= Slider("ThinDwn ", 2, 0 + g_general_x, 0 + g_general_y, 190, 18, config.TreeConfig.conism.val, 0.0, 0.2, 0, "uniform thinning down along the tree")
	config.TreeConfig.iteration			 = Number("Iterations: ", 2, 96 + g_general_x, -20 + g_general_y, 94, 18, config.TreeConfig.iteration.val, 1, 30, "Number of iterations")

def GenerateSampleLeafMesh(sampleleafmesh, sampleleafscale, leafMaterials):
	verts = []
	faces = []
	uvs = []
	modes = []
	smooths = []
	mats = []

	for v in sampleleafmesh.verts:
		verts.append([v.co[0]*sampleleafscale,v.co[1]*sampleleafscale,v.co[2]*sampleleafscale])
	
	for f in sampleleafmesh.faces:
		nf = []
		faces.append(nf)
		for pv in f.v:
			for iv in range(len(sampleleafmesh.verts)):
				if iv is pv.index:
					nf.append(iv)

	for f in sampleleafmesh.faces:
		if sampleleafmesh.faceUV:
			nuv = []
			for uv in f.uv:
				nuv.append((uv[0], uv[1]))
			uvs.append(nuv)
			modes.append(f.mode)
			
	for f in sampleleafmesh.faces:
		smooths.append(f.smooth)
		
	for f in sampleleafmesh.faces:
		materialSet = 0
		materialObject = sampleleafmesh.materials[f.mat]
		for i in range(0,len(leafMaterials)):
			if (leafMaterials[i] == materialObject):
				mats.append(i)
				materialSet = 1
		if (materialSet != 1):
			# material has not been added to leafMaterials
			leafMaterials.append(materialObject)
			mats.append(len(leafMaterials)-1)

	return [verts,faces,uvs,modes,smooths,mats]

def event(evt, val):	
	if (evt == QKEY and not val): Exit()
	if (evt == ONEKEY and not val): bevent(30)
	if (evt == TWOKEY and not val): bevent(31)
	if (evt == THREEKEY and not val): bevent(32)
	if (evt == FOURKEY and not val): bevent(33)
		
def SetGenerationSettings(tree, config):
	config.leafMaterials = []																					 # create the materials to be applied to the leaf mesh datablock
	
	if (tree.useleaf.val == 1):																				 # generate sample leaves if leaves are being applied
		for leaf in tree.leaves:
			currentLeafMesh = None
			# check that object exists and object has mesh
			try:
				currentLeafMesh = Blender.Mesh.Get(leaf.MeshName.val)
			except:
				SetMessage("Mesh datablock `" + str(leaf.MeshName.val) + "' not found",1)
				return

			# generate sample leaf mesh
			leaf.sampleMesh = GenerateSampleLeafMesh(currentLeafMesh, leaf.scale.val, config.leafMaterials)
			# set rotSide, rotForward. (rot Up is not used in leaf)
			leaf.rotSide = leaf.slope.val * deg2rad
			leaf.rotForward = leaf.orient.val * deg2rad
		
	bang = tree.bangle.val * deg2rad
	nrotF = tree.rotF.val * deg2rad / tree.resolutionV.val
	ncurve = tree.curve.val * deg2rad / tree.resolutionV.val
	if tree.menu.val == 1:
		config.rules = b_straight(tree.repeat.val,	nrotF,	ncurve,	tree.length.val,	tree.branch_scale.val,	tree.lenscale.val,	bang,	tree.resolutionV.val,	tree.orientation.val)
	elif tree.menu.val == 2:
		config.rules = b_split2_2d(tree.repeat.val,	nrotF, ncurve, tree.length.val, tree.branch_scale.val, tree.lenscale.val, bang, tree.resolutionV.val, tree.orientation.val)
	elif tree.menu.val == 3:
		config.rules = b_split2_3d(tree.repeat.val, nrotF, ncurve, tree.length.val, tree.branch_scale.val, tree.lenscale.val, bang, tree.resolutionV.val, tree.orientation.val)
	elif tree.menu.val == 4:
		config.rules = b_fork1_alt_2d(tree.repeat.val, nrotF, ncurve, tree.length.val, tree.branch_scale.val, tree.lenscale.val, bang, tree.resolutionV.val, tree.orientation.val)
	elif tree.menu.val == 5:
		config.rules = b_fork2_2d(tree.repeat.val, nrotF, ncurve, tree.length.val, tree.branch_scale.val, tree.lenscale.val, bang, tree.resolutionV.val, tree.orientation.val)
	elif tree.menu.val == 6:
		config.rules = b_fork2_3d(tree.repeat.val, nrotF, ncurve, tree.length.val, tree.branch_scale.val, tree.lenscale.val, bang, tree.resolutionV.val, tree.orientation.val)
	elif tree.menu.val == 7:
		config.rules = b_fork2_alt(tree.repeat.val, nrotF, ncurve, tree.length.val, tree.branch_scale.val, tree.lenscale.val, bang, tree.resolutionV.val, tree.orientation.val)
	elif tree.menu.val == 8:
		config.rules = b_split3(tree.repeat.val, nrotF, ncurve, tree.length.val, tree.branch_scale.val, tree.lenscale.val, bang, tree.resolutionV.val, tree.orientation.val)
	elif tree.menu.val == 9:
		config.rules = b_fork3(tree.repeat.val, nrotF, ncurve, tree.length.val, tree.branch_scale.val, tree.lenscale.val, bang, tree.resolutionV.val, tree.orientation.val)
	elif tree.menu.val == 10:
		config.rules = b_fork1_2d(tree.repeat.val, nrotF, ncurve, tree.length.val, tree.branch_scale.val, tree.lenscale.val, bang, tree.resolutionV.val, tree.orientation.val)
	elif tree.menu.val == 11:
		config.rules = b_fork1_3d(tree.repeat.val, nrotF, ncurve, tree.length.val, tree.branch_scale.val, tree.lenscale.val, bang, tree.resolutionV.val, tree.orientation.val)
	elif tree.menu.val == 12:
		config.rules = b_fork1_alt_3d(tree.repeat.val, nrotF, ncurve, tree.length.val, tree.branch_scale.val, tree.lenscale.val, bang, tree.resolutionV.val, tree.orientation.val)

	stemrules = None
	stembang = tree.stembangle.val * deg2rad
	nrotF = tree.stemrotF.val * deg2rad / tree.stemresolutionV.val
	ncurve = tree.stemcurve.val * deg2rad / tree.stemresolutionV.val
	if tree.stemmenu.val == 1:
		pass
	elif tree.stemmenu.val == 2:
		stemrules = s_straight(tree.stemrepeat.val, nrotF, ncurve, tree.length.val, tree.stembranch_scale.val, tree.stemlenscale.val, stembang, tree.stemresolutionV.val, tree.stemorientation.val * deg2rad)
	elif tree.stemmenu.val == 3:
		stemrules = s_fork1(tree.stemrepeat.val, nrotF, ncurve, tree.length.val, tree.stembranch_scale.val, tree.stemlenscale.val, stembang, tree.stemresolutionV.val, tree.stemorientation.val * deg2rad)
	elif tree.stemmenu.val == 4:
		stemrules = s_fork1_alt(tree.stemrepeat.val, nrotF, ncurve, tree.length.val, tree.stembranch_scale.val, tree.stemlenscale.val, stembang, tree.stemresolutionV.val, tree.stemorientation.val * deg2rad)
	elif tree.stemmenu.val == 5:
		stemrules = s_fork2(tree.stemrepeat.val, nrotF, ncurve, tree.length.val, tree.stembranch_scale.val, tree.stemlenscale.val, stembang, tree.stemresolutionV.val, tree.stemorientation.val * deg2rad)
	elif tree.stemmenu.val == 6:
		stemrules = s_fork2_alt(tree.stemrepeat.val, nrotF, ncurve, tree.length.val, tree.stembranch_scale.val, tree.stemlenscale.val, stembang, tree.stemresolutionV.val, tree.stemorientation.val * deg2rad)
	elif tree.stemmenu.val == 7:
		stemrules = s_fork3(tree.stemrepeat.val, nrotF, ncurve, tree.length.val, tree.stembranch_scale.val, tree.stemlenscale.val, stembang, tree.stemresolutionV.val, tree.stemorientation.val * deg2rad)

	if stemrules:
		config.rules[0] = stemrules[0] # copy axiom from stem
		config.rules[1].extend(stemrules[1])
		config.rules[2].extend(stemrules[2])
		
	leaf_len = tree.leaflength.val * tree.length.val
	nrotF = (tree.leafrotF.val + tree.rotF.val / tree.leafrepeat.val)* deg2rad 
	ncurve = (tree.leafcurve.val + tree.curve.val / tree.leafrepeat.val)* deg2rad 
	branchLeafRotRad = tree.branchLeafRot.val * deg2rad
	if tree.leafmenu.val == 1:
		leafrules = l_endpoint(tree.resolutionV.val, deg2rad * tree.rotF.val / tree.resolutionV.val, deg2rad * tree.curve.val / tree.resolutionV.val, leaf_len, tree.stembranch_scale.val, tree.leafresolutionV.val, branchLeafRotRad)
	elif tree.leafmenu.val == 7:
		leafrules = l_activepoint(tree.resolutionV.val, deg2rad * tree.rotF.val / tree.resolutionV.val, deg2rad * tree.curve.val / tree.resolutionV.val, leaf_len, tree.stembranch_scale.val, tree.leafresolutionV.val, branchLeafRotRad)
	elif tree.leafmenu.val == 3:
		leafrules = l_fork1_alt_prp(tree.leafrepeat.val, nrotF, ncurve, leaf_len, tree.stembranch_scale.val, tree.leafresolutionV.val, branchLeafRotRad, tree.addEndPoint.val)
	elif tree.leafmenu.val == 5:
		leafrules = l_fork2_prp(tree.leafrepeat.val, nrotF, ncurve, leaf_len, tree.stembranch_scale.val, tree.leafresolutionV.val, branchLeafRotRad, tree.addEndPoint.val)
	elif tree.leafmenu.val == 6:
		leafrules = l_fork2_alt_prp(tree.leafrepeat.val, nrotF, ncurve, leaf_len, tree.stembranch_scale.val, tree.leafresolutionV.val, branchLeafRotRad, tree.addEndPoint.val)
	elif tree.leafmenu.val == 8:
		leafrules = l_fork3_prp(tree.leafrepeat.val, nrotF, ncurve, leaf_len, tree.stembranch_scale.val, tree.leafresolutionV.val, branchLeafRotRad, tree.addEndPoint.val)
	elif tree.leafmenu.val == 9:
		leafrules = l_advanced(tree.leafrepeat.val, nrotF, ncurve, leaf_len, tree.stembranch_scale.val, tree.leafresolutionV.val, branchLeafRotRad, tree.addEndPoint.val, tree.leavesAroundBranch.val, tree.leavesRepeatAlongSegment.val)
		
	if leafrules:
		config.rules[1].extend(leafrules[1])
		config.rules[2].extend(leafrules[2])

	config.maxd = 30
	if tree.maxdepth.val > 0:
		config.maxd = tree.maxdepth.val
	if tree.prune_level.val == 1:
		config.min_prune_level = 2
		config.max_prune_level = 30
	elif tree.prune_level.val == 2:
		config.min_prune_level = 3
		config.max_prune_level = 30
	elif tree.prune_level.val == 3:
		config.min_prune_level = 2
		config.max_prune_level = 2
	elif tree.prune_level.val == 4:
		config.min_prune_level = iteration.val -2
		config.max_prune_level = iteration.val -1
	elif tree.prune_level.val == 5:
		config.min_prune_level = iteration.val -1
		config.max_prune_level = iteration.val -1
	elif tree.prune_level.val == 6:
		config.min_prune_level = 1
		config.max_prune_level = 30
		
	config.cart_force = [cos(tree.force[0].val * deg2rad) * cos(tree.force[1].val * deg2rad) * tree.force[2].val, sin(tree.force[0].val * deg2rad) * cos(tree.force[1].val * deg2rad) * tree.force[2].val, sin(tree.force[1].val * deg2rad) * tree.force[2].val]

# places the provided meshes at the specified co-ordinates
# returns the mesh objects created
def PlaceTree(meshs, coords):
	scn = Scene.GetCurrent()
	objTrunk = Blender.Mesh.New(newTreeObjectPrefix.val + newTreeBranchObjectName.val)			# make mesh data object
	objLeaves = Blender.Mesh.New(newTreeObjectPrefix.val + newTreeLeavesObjectName.val)			 # make mesh data object
	obTrunk = scn.objects.new(objTrunk)										 # make a new object in this scene using the mesh data
	obLeaves = scn.objects.new(objLeaves)									 # make a new object in this scene using the mesh data
	obTrunk.setLocation(coords[0], coords[1], coords[2])			 # position the object in the scene
	obLeaves.setLocation(coords[0], coords[1], coords[2])			 # position the object in the scene
	# Assign the mesh to the tree objects
	NMesh.PutRaw(meshs[0], objTrunk.name)
	NMesh.PutRaw(meshs[1], objLeaves.name)
	return [[objTrunk, objLeaves],[obTrunk, obLeaves]]

# replaces the contents of the specified mesh with the provided meshes
# returns the mesh objects placed
def ReplaceTree(meshs, regenObjectNames):
	objTrunk = Blender.Mesh.Get(regenObjectNames[0])
	objLeaves = Blender.Mesh.Get(regenObjectNames[1])
	# get the meshobject - only used to return appropriate values
	obTrunk = Blender.Object.Get(regenObjectNames[0])
	obLeaves = Blender.Object.Get(regenObjectNames[1])
	NMesh.PutRaw(meshs[0], objTrunk.name)
	NMesh.PutRaw(meshs[1], objLeaves.name)
	return [[objTrunk, objLeaves],[obTrunk, obLeaves]]

# GENERATE
# Main function for calling lsystem generating and placing resulting trees
def Generate():
	global regenLastSelectedItems, regenLastCursorPos, regenLastObjectNames,regenLastPlacementOptionUsed
	
	time1 = time.time()																																 # timer tracking total generation time
	print
	print "GENERATING..."
	SetMessage("Generating mesh...",0)
		
	placementObjects = []																															 # the objects that new trees will be placed at
	useCursor = 0																																			 # overrides placementObjects and informs script to place new trees at cursor
	useGrid = 0																																				 # overrides placementObjects and informs script to place new trees at grid
	leafMaterials = []																																	# leafMaterials - stores materials used by leaves as assigned at a mesh datablock level
	currentConfig = None																																# stores the int index config in GenerationConfigs currently being generated
	scn = Scene.GetCurrent()																														# get the selected objects
	treeGroup = None																																		# tree group object to be used if groups are being used
	markerGroup = None																																	# marker group (when placing at selected objects aka markers) to be used if groups and placing at selected objects are being used
	
	if (placementSelectionMenu.val == 3):																							 # selected objects
		for selectedObject in scn.objects.selected:
			placementObjects.append(selectedObject)																					 # add selected objects to placementObjects[]
		if (len(placementObjects) == 0):																										# return an error if nothing is selected
			SetMessage("No Objects Selected",1)
			return
		
	elif (placementSelectionMenu.val == 2):																						 # selected object
		placementObjects = [scn.objects.active]
		if ((len(placementObjects) == 0) | (placementObjects[0] == None)):																										# return an error if nothing is selected
			SetMessage("No Object Selected",1)
			return
		
	elif (placementSelectionMenu.val == 1):																						 # grid (center)
		placementObjects = [1]																															# add a dummy item to placementObjects so we generate one item
		useGrid = 1																																				 # set useGrid to true
		
	elif (placementSelectionMenu.val == 0):																						 # cursor
		placementObjects = [1]																															# add a dummy item to placementObjects so we generate one item
		useCursor = 1																																			 # set useCursor to true
		
	placingSingleTree = (placementSelectionMenu.val in [0,1,2])
	regen = ((placingSingleTree) & (regenLastObject.val == 1))
	
	# for regeneration feature - monitors cursor, selection and option changes
	if ((placementSelectionMenu.val == 0) & (regenLastCursorPos != Window.GetCursorPos())):				 # if using cursor and the cursor position has changed
		regenLastPlacementOptionUsed = placementSelectionMenu.val
		regenLastCursorPos = Window.GetCursorPos()
		regenLastSelectedItems = None
		regenLastObjectNames = [None,None]																																	# reset the last generated object names to None so they are re-evaluated
	if ((placementSelectionMenu.val == 2) & (regenLastSelectedItems != scn.objects.active)):					# if using active object and the active object has changed
		regenLastPlacementOptionUsed = placementSelectionMenu.val
		regenLastCursorPos = None
		regenLastSelectedItems = scn.objects.active
		regenLastObjectNames = [None,None]																																	# reset the last generated object names to None so they are re-evaluated
	if ((placementSelectionMenu.val == 1) & (regenLastPlacementOptionUsed != 1)):
		regenLastPlacementOptionUsed = placementSelectionMenu.val
		regenLastCursorPos = None
		regenLastSelectedItems = None
		regenLastObjectNames = [None,None]
	# check that the objects exist
	if (regenLastObjectNames[0] != None):
		try:
			testA = Blender.Object.Get(regenLastObjectNames[0])
		except:
			regenLastObjectNames[0] = None
		finally:
			testA = None
	if (regenLastObjectNames[1] != None):
		try:
			testB = Blender.Object.Get(regenLastObjectNames[1])
		except:
			regenLastObjectNames[1] = None
		finally:
			testB = None
	
	# Iterative Tree Generation
	for treeIteration in range(0,len(placementObjects)):																# for each tree being generated
	
		if ((placementSelectionMenu.val == 3) & (placementMultipleConfigurationMenu.val == 1)):																	 # if placing multiple trees, pick one
			currentConfig = random.sample(GetConfigPopulation(),1)[0]
		else:																																							 # else use the current one
			currentConfig = currentGenerationConfig.val

		config = GenerationConfigs[currentConfig]																					 # being the config being generated to a local name (that is easier to code with)
		tree = GenerationConfigs[currentConfig].TreeConfig

		if (GenerationConfigs[currentConfig].generationSettings == None):																									 # check if the generation settings have been generated
			GenerationConfigs[currentConfig].generationSettings = GenerationSettings()
			SetGenerationSettings(tree, GenerationConfigs[currentConfig].generationSettings)																	# if not, create them
			
		settings = GenerationConfigs[currentConfig].generationSettings
		



		# if seeds are not locked, regenerate random seed
		# or, if the random seed is locked, and we are placing multiple trees, and this is not the first tree being generated, and the user does not want all the trees the same, then regenerate random seed
		# todo: this logic does not work properly
		if ((tree.branchSeed_lock.val != 1) | ((((tree.branchSeed_lock.val == 1) & (placementSelectionMenu.val == 3)) & (treeIteration > 0)) & (config.identical.val != 1))):
			tree.branchSeed_seed.val = random.randint(randSeed_rangeLow, randSeed_rangeHigh)
		if ((tree.leafSeed_lock.val != 1) | ((((tree.leafSeed_lock.val == 1) & (placementSelectionMenu.val == 3)) & (treeIteration > 0)) & (config.identical.val != 1))):
			tree.leafSeed_seed.val = random.randint(randSeed_rangeLow, randSeed_rangeHigh)
			
		timerEachTree1 = time.time()
		
		generatedObjects = lsystem_build(
			settings.rules[0],
			settings.rules[1],
			settings.rules[2],
			tree.iteration.val,
			tree.length.val * tree.thickness.val,
			tree.conism.val,
			tree.shorten.val,
			tree.resolution.val,
			tree.resolutionLOD.val,
			tree.rot_rand_per.val * 4.0,
			tree.rot_rand_abs.val * 2.0,
			tree.randScale.val,
			tree.prune.val,
			tree.prunefirst.val,
			settings.maxd,
			settings.min_prune_level,
			settings.max_prune_level,
			settings.cart_force,
			tree.scaleforce.val,
			tree.useleaf.val,
			tree.branchSeed_seed.val,
			tree.leafSeed_seed.val,
			tree.leaves,
			tree.GetLeafPopulation()
			)
			
		if (placingSingleTree):
			if (((regen) and (regenLastObjectNames[0] != None)) and ((tree.useleaf.val == 1 and regenLastObjectNames[1] != None) or (tree.useleaf.val == 0))):
				createdMeshs = ReplaceTree([generatedObjects[0],generatedObjects[1]], regenLastObjectNames)
			elif (useCursor):
				createdMeshs = PlaceTree([generatedObjects[0],generatedObjects[1]], Window.GetCursorPos())
			elif (useGrid):
				createdMeshs = PlaceTree([generatedObjects[0],generatedObjects[1]], [0,0,0])
			else: # (placingAtSelection)
				createdMeshs = PlaceTree([generatedObjects[0],generatedObjects[1]], placementObjects[treeIteration].getLocation())
		else: # if generating multiple trees
			createdMeshs = PlaceTree([generatedObjects[0],generatedObjects[1]], placementObjects[treeIteration].getLocation())
		
		# assign the return values from placement
		objTrunk = createdMeshs[0][0]
		objLeaves = createdMeshs[0][1]
		obTrunk = createdMeshs[1][0]
		obLeaves = createdMeshs[1][1]
		regenLastObjectNames[0] = obTrunk.getName()
		regenLastObjectNames[1] = obLeaves.getName()
				
		# Add the materials for trunk and leaves
		# this is done after the NMesh.PutRaw calls as PutRaw destroys the existing MeshDatablock
		objTrunk.materials += [Material.Get()[tree.branchMaterial.val]]									 # add the trunk material
		objLeaves.materials += settings.leafMaterials								 # add the leaf materials

		# if applying rotation
		if (config.matchRotation.val == 1):
			obTrunk.rot = placementObjects[treeIteration].rot
			obLeaves.rot = placementObjects[treeIteration].rot
		# if applying scale
		if (config.matchScale.val == 1):
			obTrunk.setSize(placementObjects[treeIteration].getSize())
			obLeaves.setSize(placementObjects[treeIteration].getSize())
			
		# if adding to a group
		if ((newTreeGroupNameMenu.val != 0) & ((not regen) or (regen and newTreeGroupNameMenu.val == 2))): # if we are adding to a group and we are not regenerating, or we are regenerating and adding to a specific group
			if ((newTreeGroupNameMenu.val == 1) & (treeGroup == None)): # adding to new group based on tree name
				treeGroup = Group.New(obTrunk.getName() + "TreeGroup") # create new tree group
				if (not placingSingleTree): # only add markers to a group if using multiple markers
					markerGroup = Group.New(obTrunk.getName() + "Markers") # create new marker group
			if ((newTreeGroupNameMenu.val == 2) & (treeGroup == None)): # adding to specific group name
				try:
					treeGroup = Group.Get(newTreeGroupName.val) # try to get existing group by name
				except:
					treeGroup = Group.New(newTreeGroupName.val) # create new group if try fails
				if (not placingSingleTree): # only add markers to a group if using multiple markers
					try:
						markerGroup = Group.Get(newTreeGroupName.val + "Markers") # try to get existing marker group by name
					except:
						markerGroup = Group.New(newTreeGroupName.val + "Markers") # create new marker group if try fails
	
			treeGroup.objects.link(obTrunk)		 # add tree objects to tree group
			treeGroup.objects.link(obLeaves)		# add leaf objects to tree group
			if (not placingSingleTree): # only add markers to a group if using multiple markers
				markerGroup.objects.link(placementObjects[treeIteration]) # add marker object to marker object group
	
		# timing for individual trees
		timerEachTree2 = time.time()
		timerEachTree3 = round(timerEachTree2 - timerEachTree1, 3)
		
		# print out status information
		# get the currently used file name or use "None"
		configUsed = GetFileNameFromFullPath(config.filename)
		if configUsed == "": configUsed = "None"
		if (placementObjects[0] == 1): # only generating one object
			if (useCursor): placedAtText = "Cursor"
			if (useGrid): placedAtText = "Grid"
		else:
			placedAtText = placementObjects[treeIteration].getName()
		print "Pass:", treeIteration + 1, "| Placed At:", placedAtText, "| Config:", configUsed, "| Tree Object Name:", obTrunk.getName(), "| Leaf Object Name:", obLeaves.getName(), "| Time:", timerEachTree3, "| Branch Seed:", tree.branchSeed_seed, "| Leaf Seed:", tree.leafSeed_seed

	# FINALISE
	# EMPTY stored GenerationConfigs
	for config in GenerationConfigs:
		config.generationSettings = None
	
	# save temp config if generating only one tree
	if (placementSelectionMenu.val != 3):
		SaveConfigFile(defaultdir + "/" + tempDefaultsFileName)
		
	if ((placementSelectionMenu.val == 3) & (treeIteration == 0)):
		scn.objects.selected = markerGroup.objects
	else:
		scn.objects.selected = []
		
	# get time for total generation
	time2 = time.time()
	time3 = round(time2 - time1, 3)
	print "DONE in", time3, "seconds"
	SetMessage("Generation complete (" + str(time3) + " seconds)",0)
	Redraw()

# event register:
# 1:	exit
# 2:	redraw UI
# 3:	generate
# 4:	addleaftoleaves
# 5:	removeleaffromleaves
# 14: File Menu
# 15: Placement Menu
# 30: Tab - Main
# 31: Tab - Branches
# 32: Tab - Leaves
# 33: Tab - Options
# 98: Tests
# 99: Tests
def bevent(evt):
	if	 (evt == 1): Exit()
	elif (evt == 2): Draw()
	elif (evt == 4): AddNewLeafToLeaves()
	elif (evt == 5): RemoveLeafFromLeaves()
	elif (evt == 30):
		tabGeneral.val = 1
		tabBranches.val = 0
		tabLeaves.val = 0
		tabOptions.val = 0
		Draw()
	elif (evt == 31):
		tabGeneral.val = 0
		tabBranches.val = 1
		tabLeaves.val = 0
		tabOptions.val = 0
		Draw()
	elif (evt == 32):
		tabGeneral.val = 0
		tabBranches.val = 0
		tabLeaves.val = 1
		tabOptions.val = 0
		Draw()
	elif (evt == 33):
		tabGeneral.val = 0
		tabBranches.val = 0
		tabLeaves.val = 0
		tabOptions.val = 1
		Draw()
	elif (evt== 14):
		if (fileMenuSelection.val == 1): Blender.Window.FileSelector(LoadConfigFile,"Load",defaultdir + "\\" + GetFileNameFromFullPath(GenerationConfigs[currentGenerationConfig.val].filename))
		if (fileMenuSelection.val == 2): Blender.Window.FileSelector(SaveConfigFile,"Save",defaultdir + "\\" + GetFileNameFromFullPath(GenerationConfigs[currentGenerationConfig.val].filename))
		if (fileMenuSelection.val == 4):
			LoadConfigFile(defaultdir + "/" + userDefaultsFileName)
			SetMessage("User defaults loaded",0)
		if (fileMenuSelection.val == 5):
			SaveConfigFile(defaultdir + "/" + userDefaultsFileName)
			SetMessage("User defaults saved",0)
		if (fileMenuSelection.val == 6):
			LoadConfigFile(defaultdir + "/" + originalDefaultsFileName)
			SetMessage("Original defaults loaded",0)
		if (fileMenuSelection.val == 7):
			LoadConfigFile(defaultdir + "/" + tempDefaultsFileName)
			SetMessage("Loaded last generated configuration",0)
		Draw()
	elif (evt == 15):
		pass
	elif (evt == 99):
		testSave()
		Draw()
	elif (evt == 98):
		testLoad()
		Draw()
	elif (evt== 3):
		Generate()
	else:
		print "Event", evt, "was raised and not handled"

Register(draw, event, bevent)

# HELP FUNCTION
def lsystemHelp(event, val):
	pass

















from string import strip
from xml.dom.minidom import Document, parse

originalDefaultsFileName = "lsysOriginalDefaults.lsys"
userDefaultsFileName = "lsysUserDefaults.lsys"
tempDefaultsFileName = "lsysTempDefaults.lsys"

def testSave():
	SaveConfigFile(defaultdir + "/both")

def testLoad():
	LoadConfigFile(defaultdir + "/both")


def writeln(f,x):
	f.write(str(x))
	f.write("\n")

def readint(f):
	return int(f.readline())
def readfloat(f):
	return float(f.readline())
def readstr(f):
	s = (f.readline())
	return strip(s)


def LoadConfigFile(filename, replaceCurrent=0):
	# grab the loaded file so we can use the name later
	
	# Ensure access to the file
	try:
		f = open(filename,'r')
	except:
		SetMessage("File not found",1)
		return
	
	SetMessage("Loading file...",0)
	
	# Identify version being loaded
	# Versions < 3 have int as first line
	# Versions >= 3 are XML based
	oldFile = 0
	try:
		firstLine = f.readline()
		firstLineInt = int(firstLine)
		f.close()
		oldFile = 1
	except:
		oldFile = 0
		f.close()
		
	if (oldFile):
		newTreeConfig = LoadConfigFilePrevious(filename)
	else:
		newTreeConfig = LoadConfigFileCurrent(filename)
		
	if (replaceCurrent):
		GenerationConfigs[currentGenerationConfig.val].TreeConfig = newTreeConfig
	else:
		GenerationConfigs.append(GenerationConfig())
		GenerationConfigs[len(GenerationConfigs)-1].TreeConfig = newTreeConfig
		currentGenerationConfig.val = len(GenerationConfigs) - 1
	
	GenerationConfigs[currentGenerationConfig.val].filename = filename
		

		
def LoadConfigFileCurrent(filename):
	config = TreeConfig(randSeed_rangeHigh, randSeed_rangeLow)
	
	doc = parse(filename)
	
	nLSystem = doc.documentElement
	file_version = str(nLSystem.getAttribute("version"))
	
	for node in nLSystem.childNodes:
		if not node.nodeType == 3: # ignore text nodes
			if node.tagName == "leaves":
				config.useleaf.val = int(node.getAttribute("useLeaves"))
				config.leafSeed_lock.val = int(node.getAttribute("lockSeed"))
				config.leafSeed_seed.val = int(node.getAttribute("seed"))
				config.addEndPoint.val = int(node.getAttribute("addEndPoint"))
				if node.getAttribute("branchLeafRot") != "":
					config.branchLeafRot.val = float(node.getAttribute("branchLeafRot"))
				config.leaves = []
				for leafNode in node.childNodes:
					if not leafNode.nodeType == 3: # ignore text nodes
						config.leaves.append(Leaf())
						config.leaves[len(config.leaves)-1].MeshName.val = str(leafNode.getAttribute("meshName"))
						config.leaves[len(config.leaves)-1].slope.val = float(leafNode.getAttribute("slope"))
						config.leaves[len(config.leaves)-1].orient.val = float(leafNode.getAttribute("orient"))
						config.leaves[len(config.leaves)-1].probability.val = int(leafNode.getAttribute("probability"))
						config.leaves[len(config.leaves)-1].scale.val = float(leafNode.getAttribute("scale"))
			if node.tagName == "branches":
				if node.getAttribute("material") != "": # added in v3.6.6
					config.branchMaterial.val = GetMaterialIndexByMaterialName(node.getAttribute("material"))
				for branchNode in node.childNodes:
					if not branchNode.nodeType == 3: # ignore text nodes
						config.menu.val = int(branchNode.getAttribute("ruleSet"))
						config.orientation.val = float(branchNode.getAttribute("subOrient"))
						config.bangle.val = float(branchNode.getAttribute("subSlope"))
						config.lenscale.val = float(branchNode.getAttribute("subLength"))
						config.branch_scale.val = float(branchNode.getAttribute("subThickness"))
						config.rotF.val = float(branchNode.getAttribute("spin"))
						config.curve.val = float(branchNode.getAttribute("curve"))
						config.resolutionV.val = int(branchNode.getAttribute("resV"))
						config.repeat.val = int(branchNode.getAttribute("repeat"))
			if node.tagName == "leafbranches":
				config.leafmenu.val = int(node.getAttribute("configuration"))
				config.leafrepeat.val = int(node.getAttribute("repeat"))
				config.leaflength.val = float(node.getAttribute("lengthMultiply"))
				config.leafcurve.val = float(node.getAttribute("extraCurve"))
				config.leafrotF.val = float(node.getAttribute("extraSpin"))
				config.leavesAroundBranch.val = int(node.getAttribute("leavesAroundBranch"))
				config.leavesRepeatAlongSegment.val = int(node.getAttribute("leavesRepeatAlongsSegment"))
			if node.tagName == "stem":
				config.stemmenu.val = int(node.getAttribute("ruleSet"))
				config.stemorientation.val = float(node.getAttribute("subOrient"))
				config.stembangle.val = float(node.getAttribute("subSlope"))
				config.stemlenscale.val = float(node.getAttribute("subLength"))
				config.stembranch_scale.val = float(node.getAttribute("subThickness"))
				config.stemrotF.val = float(node.getAttribute("spin"))
				config.stemcurve.val = float(node.getAttribute("curve"))
				config.stemresolutionV.val = int(node.getAttribute("resV"))
				config.stemrepeat.val = int(node.getAttribute("repeat"))
			if node.tagName == "random":
				config.randScale.val = float(node.getAttribute("randomScale"))
				config.rot_rand_per.val = float(node.getAttribute("randomDirection"))
				config.rot_rand_abs.val = float(node.getAttribute("randomTwist"))
				config.branchSeed_lock.val = int(node.getAttribute("randomSeedLocked"))
				config.branchSeed_seed.val = int(node.getAttribute("randomSeed"))
			if node.tagName == "force":
				config.scaleforce.val = float(node.getAttribute("force"))
				config.force[0].val = float(node.getAttribute("bearing"))
				config.force[1].val = float(node.getAttribute("elevation"))
				config.force[2].val = float(node.getAttribute("magnitude"))
			if node.tagName == "prune":
				config.prune_level.val = int(node.getAttribute("level"))
				config.prune.val = float(node.getAttribute("probability"))
				config.prunefirst.val = int(node.getAttribute("firstn"))
				config.maxdepth.val = int(node.getAttribute("maxDepth"))
			if node.tagName == "general":
				config.resolution.val = int(node.getAttribute("resolution"))
				config.resolutionLOD.val = float(node.getAttribute("levelOfDetail"))
				config.length.val = float(node.getAttribute("length"))
				config.thickness.val = float(node.getAttribute("thickness"))
				config.shorten.val = float(node.getAttribute("shorten"))
				config.conism.val = float(node.getAttribute("thinDown"))
				config.iteration.val = int(node.getAttribute("iterations"))
				
	SetMessage("File loaded: v" + str(file_version),0)
	return config

# Handler for all previous versions.
def LoadConfigFilePrevious(filename):
	config = TreeConfig(randSeed_rangeHigh, randSeed_rangeLow)
	
	f = open(filename,'r')
	file_version = readint(f)
	
	config.iteration.val = readint(f)
	config.orientation.val = readfloat(f)
	config.stemorientation.val = readfloat(f)
	config.menu.val = readint(f)
	config.stemmenu.val = readint(f)
	
	leafmenuvalue = readint(f)
	if (leafmenuvalue == 2):
		config.leafmenu.val = 9
	elif (leafmenuvalue == 4):
		config.leafmenu.val = 9
	else:
		config.leafmenu.val = leafmenuvalue
	
	config.thickness.val = readfloat(f)
	config.length.val = readfloat(f)
	config.leaflength.val = readfloat(f)
	config.rotF.val = readfloat(f)
	config.stemrotF.val = readfloat(f)
	config.leafrotF.val = readfloat(f)
	config.conism.val = readfloat(f)
	config.branch_scale.val = readfloat(f)
	config.stembranch_scale.val = readfloat(f)
	config.lenscale.val = readfloat(f)
	config.stemlenscale.val = readfloat(f)
	config.prune.val = readfloat(f)
	config.prunefirst.val = readint(f)
	config.resolutionLOD.val = readfloat(f)
	config.resolution.val = readint(f)
	config.resolutionV.val = readint(f)
	config.stemresolutionV.val = readint(f)
	config.repeat.val = readint(f)
	config.stemrepeat.val = readint(f)
	config.leafrepeat.val = readint(f)
	config.rot_rand_per.val = readfloat(f)
	config.rot_rand_abs.val = readfloat(f)
	
	config.leaves[0].MeshName.val = readstr(f)
	
	config.useleaf.val = readint(f)
	
	config.leaves[0].scale.val = readfloat(f)
	
	config.force[0].val = readfloat(f)
	config.force[1].val = readfloat(f)
	config.force[2].val = readfloat(f)
	config.scaleforce.val = readfloat(f)
	config.bangle.val = readfloat(f)
	config.stembangle.val = readfloat(f)
	
	config.leaves[0].slope.val = readfloat(f)
	
	config.curve.val = readfloat(f)
	config.stemcurve.val = readfloat(f)
	config.leafcurve.val = readfloat(f)
	
	config.leaves[0].orient.val = readfloat(f)
	
	config.shorten.val = readfloat(f)
	config.maxdepth.val = readint(f)

# added in version 1.2
	if file_version >= 120: config.randScale.val = readfloat(f)
	if file_version >= 120: config.prune_level.val = readint(f)

# added in version 1.3.1
	if file_version >= 131: config.branchSeed_lock.val = readint(f)
	if file_version <= 131: config.branchSeed_lock.val = 0
	if file_version >= 131: config.branchSeed_seed.val = readint(f)
		
	f.close()
	SetMessage("File loaded: v" + str(file_version),0)
	return config


def SaveConfigFile(filename):
	# grab the loaded file so we can use the name later
	GenerationConfigs[currentGenerationConfig.val].filename = filename
	
	config = GenerationConfigs[currentGenerationConfig.val].TreeConfig
	
	# Ensure access to the file
	try:
		f = open(filename,'w')
	except:
		SetMessage("Unable to save file",1)
		return
	
	SetMessage("Saving configuration to file...",0)
	
	#Create the xml minidom document
	doc = Document()
	
	# Create the <lsystem> root element
	nLSystem = doc.createElement("lsystem")
	nLSystem.setAttribute("version", GetVersionAsString())
	doc.appendChild(nLSystem)
	
	# Create a <leaves> element
	nLeaves = doc.createElement("leaves")
	nLSystem.appendChild(nLeaves)
	nLeaves.setAttribute("useLeaves", str(config.useleaf.val))
	nLeaves.setAttribute("lockSeed", str(config.leafSeed_lock.val))
	nLeaves.setAttribute("seed", str(config.leafSeed_seed.val))
	nLeaves.setAttribute("addEndPoint", str(config.addEndPoint.val))
	nLeaves.setAttribute("branchLeafRot", str(config.branchLeafRot.val))
	for leafObject in config.leaves:
		# Create a <leaf> element for each leaf
		nLeaf = doc.createElement("leaf")
		nLeaves.appendChild(nLeaf)
		nLeaf.setAttribute("meshName", str(leafObject.MeshName.val))
		nLeaf.setAttribute("slope", str(leafObject.slope.val))
		nLeaf.setAttribute("orient", str(leafObject.orient.val))
		nLeaf.setAttribute("probability", str(leafObject.probability.val))
		nLeaf.setAttribute("scale", str(leafObject.scale.val))
		
	# Create a <branches> element
	nBranches = doc.createElement("branches")
	nLSystem.appendChild(nBranches)
	# Create a <branch> element for each branch
	# there is only one, but the intention is to add more... perhaps
	nBranch = doc.createElement("branch")
	nBranches.appendChild(nBranch)
	nBranches.setAttribute("material", str(GetMaterialNameByMaterialIndex(config.branchMaterial.val)))
	nBranch.setAttribute("ruleSet", str(config.menu.val))
	nBranch.setAttribute("subOrient", str(config.orientation.val))
	nBranch.setAttribute("subSlope", str(config.bangle.val))
	nBranch.setAttribute("subLength", str(config.lenscale.val))
	nBranch.setAttribute("subThickness", str(config.branch_scale.val))
	nBranch.setAttribute("spin", str(config.rotF.val))
	nBranch.setAttribute("curve", str(config.curve.val))
	nBranch.setAttribute("resV", str(config.resolutionV.val))
	nBranch.setAttribute("repeat", str(config.repeat.val))
	
	# Create a <leafbranches> element
	nLeafBranches = doc.createElement("leafbranches")
	nLSystem.appendChild(nLeafBranches)
	nLeafBranches.setAttribute("configuration", str(config.leafmenu.val))
	nLeafBranches.setAttribute("repeat", str(config.leafrepeat.val))
	nLeafBranches.setAttribute("lengthMultiply", str(config.leaflength.val))
	nLeafBranches.setAttribute("extraCurve", str(config.leafcurve.val))
	nLeafBranches.setAttribute("extraSpin", str(config.leafrotF.val))
	nLeafBranches.setAttribute("leavesAroundBranch", str(config.leavesAroundBranch.val))
	nLeafBranches.setAttribute("leavesRepeatAlongsSegment", str(config.leavesRepeatAlongSegment.val))
	
	# Create a <stem> element
	nStem = doc.createElement("stem")
	nLSystem.appendChild(nStem)
	nStem.setAttribute("ruleSet", str(config.stemmenu.val))
	nStem.setAttribute("subOrient", str(config.stemorientation.val))
	nStem.setAttribute("subSlope", str(config.stembangle.val))
	nStem.setAttribute("subLength", str(config.stemlenscale.val))
	nStem.setAttribute("subThickness", str(config.stembranch_scale.val))
	nStem.setAttribute("spin", str(config.stemrotF.val))
	nStem.setAttribute("curve", str(config.stemcurve.val))
	nStem.setAttribute("resV", str(config.stemresolutionV.val))
	nStem.setAttribute("repeat", str(config.stemrepeat.val))
	
	# Create a <random> element
	nRandom = doc.createElement("random")
	nLSystem.appendChild(nRandom)
	nRandom.setAttribute("randomScale", str(config.randScale.val))
	nRandom.setAttribute("randomDirection", str(config.rot_rand_per.val))
	nRandom.setAttribute("randomTwist", str(config.rot_rand_abs.val))
	nRandom.setAttribute("randomSeedLocked", str(config.branchSeed_lock.val))
	nRandom.setAttribute("randomSeed", str(config.branchSeed_seed.val))
	
	# Create a <force> element
	nForce = doc.createElement("force")
	nLSystem.appendChild(nForce)
	nForce.setAttribute("force", str(config.scaleforce.val))
	nForce.setAttribute("bearing", str(config.force[0].val))
	nForce.setAttribute("elevation", str(config.force[1].val))
	nForce.setAttribute("magnitude", str(config.force[2].val))
	
	# Create a <prune> element
	nPrune = doc.createElement("prune")
	nLSystem.appendChild(nPrune)
	nPrune.setAttribute("level", str(config.prune_level.val))
	nPrune.setAttribute("probability", str(config.prune.val))
	nPrune.setAttribute("firstn", str(config.prunefirst.val))
	nPrune.setAttribute("maxDepth", str(config.maxdepth.val))
	
	# Create a <general> element
	nGeneral = doc.createElement("general")
	nLSystem.appendChild(nGeneral)
	nGeneral.setAttribute("resolution", str(config.resolution.val))
	nGeneral.setAttribute("levelOfDetail", str(config.resolutionLOD.val))
	nGeneral.setAttribute("length", str(config.length.val))
	nGeneral.setAttribute("thickness", str(config.thickness.val))
	nGeneral.setAttribute("shorten", str(config.shorten.val))
	nGeneral.setAttribute("thinDown", str(config.conism.val))
	nGeneral.setAttribute("iterations", str(config.iteration.val))
	
	# Print our newly created XML
	#print doc.toprettyxml(indent="	")
	
	# Write the contents to the specified file
	f.write(doc.toprettyxml(indent="	"))
	f.close()
	SetMessage("File saved",0)

LoadConfigFile(defaultdir + "/" + userDefaultsFileName, 1)
SetMessage("",0)
print ""
print "Script Loaded"
print "-----------------------------------------------------"