/*

	An Adobe Illustrator L-System through Scriptographer v1.1
	by Håkan Lundgren
	© 2011 Monovektor.com
	
	
	A quick introduction of L-Systems:
	http://en.wikipedia.org/wiki/Lsystem
	
	
	
	
	
	The L-System object takes two arguments.
		- angle: a number by how much a full circle is divided (ie. angle: 4 = 90°).
		- axiom: the starting value (a string) of the system.
	
	
	
	The rules have two or three arguments depending on wether the system is stochastic or not.
		- rule: a single letter/char string.
		- grammar: a string (or an array of strings IF probabilities are used) that replace the rule char.
		- probabilities: an array of numbers indicating the probability of its corresponding grammar. For instance,
		  if grammar is an array of two or more strings, probabilities would then be an array of equal length such:

			  rule: 'F'
			  grammar: ['F[+F]-F', 'F[+F+F]-']
			  probabilities: [0.2, 0.8]
			  
		  Would look like this:

			  	new Rule('F', ['F[+F]-F', 'F[+F+F]-'], [0.2, 0.8])
	
		  Whenever an F occurs it will be substituted by either one of the grammars according to each grammars probability.
		  The first one will only be picked about 20% of the times. It is important that the numbers in the probabilities
		  array adds up to exactly 1 (one). A L-System with this particular feature is called a stochastic L-system.
		- If however no probabilities are used (as in most cases) only one rule and grammar string are needed.
		- A grammar cannot have an empty string (''), if no substitution is to be made use a space (' ') instead.
	
	
	
	The draw function also takes two or three arguments.
	
		lsystem.draw(iter, lineLength, lineModification)
	
		- iter: number of times to iterate through the initial axiom. The alphabet grows exponentially so beware of high
		  numbers, usually a value between 4 - 9 is sufficient. Sometimes as low as 2 - 6.
		- lineLength: the initial length of a line meassured in points. Most grammars never change the length at all.
		- lineModification: a multiplier (and divider) by how much a line will grow och shrink. ~0.9-1.1 seem to yield the best
		  results as any lower/higher value will radically change the length.
	
	
	
	A typical L-System will look like this:
		var lsystem = new Lsystem(8, '++FX')
		lsystem.rules.push(new Rule('X', '<<<<[-FX]+FX'))
			::
		lsystem.rules.push(...More rules, if any)
			::
		lsystem.draw(12, 40, 0.9)
	
	
	
	The constants used are:
		F	Moves forward one step (length defined by the lineLength variable) and draws a line.
		f	Moves forward one step without drawing a line.
		+	Changes the angle in a counter clockwise direction.
		-	Changes the angle in a clockwise direction.
		|	Turns 180°
		<	Multiplies the current line length by a number defined by the lineModification variable
		>	Divide the current line length by a number defined by the lineModification variable
		[	Create a branch, similar to the array method push()
		]	End branch and return to its parrent, similar to the array method pop()
		
	More constants may be added later.
	
	
	
	At the bottom is a collection of nice looking grammars I have found all over the place.
	More reading about L-Systems (written by the inventor of it all) can be found in the book The Algorithmic Beauty of
	Plants available for free as a high quality PDF here:
	http://algorithmicbotany.org/papers/abop/abop.pdf
	
*/


var Lsystem = function(angle, axiom){
			this.angle = angle
			this.axiom = axiom
			this.rules = []
}


Lsystem.prototype.generateAlphabet = function(iterations){
			this.alphabet = this.axiom

			var temp, W
			// Iterator loop
			for(var i = 0; i < iterations; i++){
					temp = ''

					// Loop through all letters
					for(var j = 0; j < this.alphabet.length; j++){

							W = this.alphabet.charAt(j)

							// Find rule
							for(var k in this.rules){
									if(this.rules[k].name == W){
											W = this.rules[k].getRule()
									}
							}

							temp += W
					}

					this.alphabet = temp
			}

			return this.alphabet
}


Lsystem.prototype.draw = function(iter, lineLength, lineModification){
	
			this.generateAlphabet(iter)

			var vector = new Point({ length: lineLength, angle: 0 })
			var branches = []
			var path = new Path()
			var currentPosition = new Point(0, 0)
			path.add(currentPosition)

			for(var i = 0; i < this.alphabet.length; i++){
				
					switch(this.alphabet.charAt(i)){
							case('F'):												// Move forward and draw line
									currentPosition += vector
									path.lineTo(currentPosition)
									break
							case('f'):												// Move forward without drawing a line
									currentPosition += vector
									path = new Path()
									path.add(currentPosition)
									break
							case('+'):												// Change angle counter clockwise
									vector.angle -= 360 / this.angle
									break
							case('-'):												// Change angle clockwise
									vector.angle += 360 / this.angle
									break
							case('|'):												// Turn around 180°
									vector.angle += 180
									break
							case('<'):												// Increases length of line if lineModification > 1, otherwise decrease
									vector.length *= lineModification
									break
							case('>'):												// Decreases length of line if lineModification > 1, otherwise increase
									vector.length /= lineModification
									break
							case('['):												// Adds another branch
									branches.push([path, currentPosition, vector])
									vector = new Point(vector)
									path = new Path()
									path.add(currentPosition)
									break
							case(']'):												// Returns to the parrent branch
									branchArray = branches[branches.length -1]
									path = branchArray[0]
									currentPosition = branchArray[1]
									vector = branchArray[2]
									branches.pop()
									break				
					}
			}
}


var Rule = function(rule, grammar, probabilities){
			this.name = rule
			this.grammar = grammar

			// Sets up a probability array
			if(probabilities && probabilities.length > 1){
					var tempArray = []
					var previousValue = nextValue = 0

					for(var i = 0; i < probabilities.length; i++){
							nextValue = previousValue + probabilities[i]
							tempArray.push(nextValue)
							previousValue = nextValue
					}

					if(previousValue == 1){ this.probability = tempArray }
			}
}


Rule.prototype.getRule = function(){
			if(this.probability){
					var randomRule = Math.random()
					var counter = 0

					while(randomRule > this.probability[counter]){
							counter++
					}

					return this.grammar[counter]
			}

			return this.grammar
}


/*	


	lsystem.draw(iter, lineLength, lineModification)
		iter = Number of iterations
		lineLength = Length of each line in points
		lineModification = Amount by which to multiply/divide the line length (preferably ~ 0.9 to 1.1)
*/
var lsystem = new Lsystem(20, '+++++FX')
lsystem.rules.push(new Rule('X', '<<<<[-FX]+FX'))
lsystem.draw(12, 10, 0.95)



/*

These are a bunch of grammars I've found on the internet. Try them out, they're nice looking.

Fractals
		Basic Y Fractal
				8, '++FX'
				new Rule('X', '<<<<[-FX]+FX')
		Levy C Curve
				8, 'F'
				new Rule('F', '+F--F+')
		Sierpinski Triangle
				3, 'F'
				new Rule('F', 'FXF')
				new Rule('X', '+FXF-FXF-FXF+')
		Sierpinski Carpet
				4, 'F'
				new Rule('F', 'F+F-F-F-G+F+F+F-F')
				new Rule('G', 'GGG')
		Sierpinski Square
				4, 'F-F-F-F'
				new Rule('F', 'FF-F-F-F-FF')
		Carpet
				4, 'F-F-F-F'
				new Rule('F', 'F[F]-F+F[--F]+F-F')
		Koch Curve #1
				6, 'F'
				new Rule('F', 'F+F--F+F')
		Koch Curve #2
				4, '-F'
				new Rule('F', 'F+F-F-F+F')
		Koch Curve #3
				4, 'F-F-F-F'
				new Rule('F', 'FF-F-F-F-F-F+F')
		Koch Curve #4
				4, 'F-F-F-F'
				new Rule('F', 'FF-F+F-F-FF')
		Koch Curve #5
				4, 'F-F-F-F'
				new Rule('F', 'FF-F--F-F')
		Koch Curve #6
				4, 'F-F-F-F'
				new Rule('F', 'F-FF--F-F')
		Koch Curve #7
				4, 'F-F-F-F'
				new Rule('F', 'F-F+F-F-F')
		Quadratic Koch Island #1
				4, 'F-F-F-F'
				new Rule('F', 'F-F+F+FF-F-F+F')
		Quadratic Koch Island #2
				4, 'F-F-F-F'
				new Rule('F', 'F+FF-FF-F-F+F+FF-F-F+F+FF+FF-F')
		Quadratic Koch Island #3
				4, 'F+F+F+F'
				new Rule('F', 'F+f-FF+F+FF+Ff+FF-f+FF-F-FF-Ff-FFF')
				new Rule('f', 'ffffff')
		Koch Snowflake
				6, 'F--F--F'
				new Rule('F', 'F+F--F+F')
		Koch Antisnowflake
				6, 'F++F++F'
				new Rule('F', 'F+F--F+F')
		Dragon Curve
				4, 'FX'
				new Rule('X', 'X+YF+')
				new Rule('Y', '-FX-Y')
		Cesaro's Sweep
				34, 'FX'
				new Rule('X', '----F!X!++++++++F!X!----')
		Cesaro's Sweep Alternate
				9, 'F'
				new Rule('F', 'F++F----F++F')
		Moore
				4, 'X'
				new Rule('X', 'FX+FX+FXFYFX+FXFY-FY-FY-')
				new Rule('Y', '+FX+FX+FXFY-FYFXFY-FY-FY')
		Maze Triangle
				3, 'X'
				new Rule('X', 'FY+FYFY-FY')
				new Rule('Y', 'FX-FXFX+FX')
		T-Square
				4, 'F+XFF+XFF+XFF+XF'
				new Rule('X', '-Y-F+XFF+XFF+XF-Y')
				new Rule('Y', 'YFY')
				
Peano Curves
		Original Peano
				4, 'F-F-F-F'
				new Rule('F', 'F-F+F+F+F-F-F-F+F')
		Modified Peano
				8, 'FXY++F++FXY++F'
				new Rule('X', 'XY-F-FXY++F++FXY')
				new Rule('Y', '-F-FXY')
		Quartet Peano
				4, 'FB'
				new Rule('A', 'FBFA+HFA+FB-FA')
				new Rule('B', 'FB+FA-FB-JFBFA')
				new Rule('F', ' ')
				new Rule('H', '-')
				new Rule('J', '+')
		Square Curve
				4, 'X'
				new Rule('X','XF-F+F-XF+F+XF-F+F-X')
		Flow Snake
				6, 'FL'
				new Rule('L', 'FL-FR--FR+FL++FLFL+FR-')
				new Rule('R', '+FL-FRFR--FR-FL++FL+FR')
				new Rule('F', ' ')
		Spinx
				6, 'X'
				new Rule('X', '+FF-YFF+FF--FFF|X|F--YFFFYFFF|')
				new Rule('Y', '-FF+XFF-FF++FFF|Y|F++XFFFXFFF|')
				new Rule('F', 'GG')
				new Rule('G', 'GG')
		Dekking
				3, 'X+X+X'
				new Rule('X', 'FY+GX-FY')
				new Rule('Y', 'GX+FY-GX')
				new Rule('F', ' ')
				new Rule(' G', ' ')
		Lace
				12, 'W'
				new Rule('W', '+++X--F--ZFX+')
				new Rule('X', '---W++F++YFW-')
				new Rule('Y', '+ZFX--F--Z+++')
				new Rule('Z', '-YFW++F++Y---')
				
Space-filling Peano
		Hilbert
				4, 'X'
				new Rule('X', '-YF+XFX+FY-')
				new Rule('Y', '+XF-YFY-FX+')
		Quadratic Gosper
				4, '-FR'
				new Rule('L', 'FLFL-FR-FR+FL+FL-FR-FRFL+FR+FLFLFR-FL+FR+FLFL+FR-FLFR-FR-FL+FL+FRFR-')
				new Rule('R', '+FLFL-FR-FR+FL+FLFR+FL-FRFR-FL-FR+FLFRFR-FL-FRFL+FL+FR-FR-FL+FL+FRFR')
		FASS #1
				4, 'L'
				new Rule('L', 'LF+RFR+FL-F-LFLFL-FRFR+')
				new Rule('R', '-LFLF+RFRFR+F+RF-LFL-FR')
		FASS #2
				4, 'L'
				new Rule('L', 'LFLF+RFR+FLFL-FRF-LFL-FR+F+RF-LFL-FRFRFR+')
				new Rule('R', '-LFLFLF+RFR+FL-F-LF+RFR+FLF+RFRF-LFL-FRFR')
		FASS #3
				4, 'L'
				new Rule('L', 'LFRFL-F-RFLFR+F+LFRFL')
				new Rule('R', 'RFLFR+F+LFRFL-F-RFLFR')
		FASS #4
				8, 'L'
				new Rule('L', 'L+F+R-F-L+F+R-F-L-F-R+F+L-F-R-F-L+F+R-F-L-F-R-F-L+F+R+F+L+F+R-F-L+F+R+F+L-R-F+F+L+F+R-F-L+F+R-F-L')
				new Rule('R', 'R-F-L+F+R-F-L+F+R+F+L-F-R+F+L+F+R-F-L+F+R+F+L+F+R-F-L-F-R-F-L+F+R-F-L-F-R+F+L-F-R-F-L+F+R-F-L+F+R')
				
Organic
		Bush
				16, '++++F'
				new Rule('F', 'FF-[-F+F+F]+[+F-F-F]')
		Branch
				8, '++X'
				new Rule('A', 'N')
				new Rule('N', 'O')
				new Rule('O', 'P')
				new Rule('P', 'X')
				new Rule('B', 'E')
				new Rule('E', 'H')
				new Rule('H', 'J')
				new Rule('J', 'Y')
				new Rule('X', 'F[+A]FY')
				new Rule('Y', 'F[-B]FX')
				new Rule('F', '>>F<<')
		Plant #1
				14, 'F'
				new Rule('F', 'F[+F]F[-F]F')
		Plant #2
				18, 'F'
				new Rule('F', 'F[+F]F[-F][F]')
		Plant #3
				14, 'Z'
				new Rule('Z', 'ZFX[+Z][-Z]')
				new Rule('X', 'X[-FFF][+FFF]FX')
		Plant #4
				20, 'SLFFF'
				new Rule('S', '[+++Z][---Z]TS')
				new Rule('Z', '+H[-Z]L')
				new Rule('H', '-Z[+H]L')
				new Rule('T', 'TL')
				new Rule('L', '[-FFF][+FFF]F')
		Tree #1
				12, '+++FX'
				new Rule('X', '<<<<[-FY]+FX')
				new Rule('Y', 'FX+FY-FX')
		Tree #2
				14, 'X'
				new Rule('X', 'F[+X][-X]FX')
				new Rule('F', 'FF')
		Tree #3
				16, 'X'
				new Rule('X', 'F-[[X]+X]+F[+FX]-X')
				new Rule('F', 'FF')
		Tree #4
				8, '++A'
				new Rule('A', 'F[+X]FB')
				new Rule('B', 'F[-Y]FA')
				new Rule('X', 'A')
				new Rule('Y', 'B')
				new Rule('F', '>>>F<<<')
		Stochastic Plant
				16, '++++F'
				new Rule('F', ['F[+F][-F]F', 'F[-F]F', 'F[+F]F'], [0.5, 0.3, 0.2])

Shapes & Shape-tiling
		Pentaflake
				10, 'F++F++F++F++F'
				new Rule('F', 'F++F++F|F-F++F')
		Pentigree
				5, 'F-F-F-F-F'
				new Rule('F', 'F-F++F+F-F-F')
		Penrose
				10, '+WF--XF---YF--ZF'
				new Rule('W', 'YF++ZF----XF[-YF----WF]++')
				new Rule('X', '+YF--ZF[---WF--XF]+')
				new Rule('Y', '-WF++XF[+++YF++ZF]-')
				new Rule('Z', '--YF++++WF[+ZF++++XF]--XF')
				new Rule('F', ' ')
		Circular Tile
				24, 'X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X+X'
				new Rule('X', '[F+F+F+F[---X-Y]+++++F++++++++F-F-F-F]')
				new Rule('Y', '[F+F+F+F[---Y]+++++F++++++++F-F-F-F]')

Dragons
		Boundary
				4, 'OTUZ'
				new Rule('O', 'FO+F-T')
				new Rule('P', '++F--U+F-X')
				new Rule('Q', '-F+V++F--Q')
				new Rule('R', '-F+ZFS')
				new Rule('S', 'FW')
				new Rule('T', '++F--U')
				new Rule('U', '++F--Y')
				new Rule('V', 'FS')
				new Rule('W', 'FO+F-P')
				new Rule('X', '++F--Y+F-X')
				new Rule('Y', '-F+R++F--Q)')
				new Rule('Z', '-F+ZFW')
				new Rule('F', ' ')
		Cross
				4, 'FX'
				new Rule('X', 'FX+FX+FXFY-FY-')
				new Rule('Y', '+FX+FXFY-FY-FY')
				new Rule('F', ' ')
		Round
				8, 'X'
				new Rule('X', 'FX+FZ+FY')
				new Rule('Y', 'FX-FZ-FY')
				new Rule('Z', 'FZ')
				new Rule('F', ' ')

Misc.
		Church
				4, 'F'
				new Rule('F', 'F+FF-F-FF+F')
		Dekking's Church
				4, 'WZYX'
				new Rule('W', 'FW+F-XFW-F+Z')
				new Rule('X', '++F--Y-F+Z++F--Y-F+Z')
				new Rule('Y', '++F--Y+F-X')
				new Rule('Z', 'FW+F-X')
				new Rule('F', ' ')
		Daisy Chain
				8, 'F'
				new Rule('F', 'F+F-F-FF+F+F')
		Doily
				12, 'F--F--F--F--F--F'
				new Rule('F', '-F[--F--F]++F--F+')
		Hex Crystal
				12, 'F--F--F--F--F--F'
				new Rule('F', '-F[--F--F]++F[++F--F]--F+')
		Zig-Zag
				4, 'F'
				new Rule('F', 'F-F+F')
*/