/*

	An Adobe Illustrator L-System through Scriptographer v1.0
	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 & axiom.
		- 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 the rule: rule, grammar & [probabilities]
		- 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.
		- 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.
		- lineChange: 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 lineChange variable
		>	Divide the current line length by a number defined by the lineChange 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 come 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 - can be found in the book The Algorithmic Beauty of
	Plants available for download 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 = []
			this.alphabet = axiom
}


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

			// Goes through every value to make an interval out of them
			if(probabilities){

					var previousValue = nextValue = 0

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

			} else { this.probability = [1] }


			this.getRule = function(){

					var randomRule = Math.random()
					var counter = 0

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

					return this.probability.length > 1 ? this.grammar[counter] : this.grammar
			}
}


Lsystem.prototype.generateAlphabet = function(iterations){
			var temp = ''

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

							var W = this.alphabet.charAt(j)

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

							// Replace letter with rule
							temp += W
					}

					// Change alphabet
					this.alphabet = temp
					temp = ''		
			}

			return this.alphabet
}



Lsystem.prototype.draw = function(iter, lineLength, lineChange){
	
			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++){

					symbol = this.alphabet.charAt(i)
				
					switch(symbol){
							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
									vector.angle -= 360 / this.angle
									break
							case('-'):
									// Change angle
									vector.angle += 360 / this.angle
									break
							case('|'):
									// Turn around 180°
									vector.angle += 180
									break
							case('<'):
									// Increases length of line if lineChange > 1, otherwise decrease
									vector.length *= lineChange
									break
							case('>'):
									// Decreases length of line if lineChange > 1, otherwise increase
									vector.length /= lineChange
									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 lsystem = new Lsystem(8, '++FX')
lsystem.rules.push(new Rule('X', '<<<<[-FX]+FX'))
lsystem.draw(6, 10, 0.9)










/*
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')
*/