
function FractalTree(canvas){
	return new fractalTreeClass(canvas);
}
//F=>FF[+FF][++FF][+++F][---F][--FF][-FF]
//F=>F[+F][+F][+F][+F]
//F=>FPFP[+FPFPFPFP][++FPFPFP][-FPFPFPFP][--FPFPFP]
function fractalTreeClass(canvas){
	this.canvas=canvas
	this.rules=[[/F/g,'FF-[-F+F+FP]+[+F-F-P][F+FP]']];
	this.commandString='F'
	this.rootX=100;
	this.rootY=100;	
	this.iterations=4;
	this.angle=33;
	this.magnitude=15;
	this.magnitudeDec=0.7;
	this.name="";
	
	this._x=0;
	this._y=0;
	this._width=400;
	this._height=800;	
}
fractalTreeClass.prototype.x = function(v){
	this._x=v;
}
	
fractalTreeClass.prototype.y = function(v){
	this._y=v;
}

fractalTreeClass.prototype.width = function(v){
	this._width=v;
}

fractalTreeClass.prototype.height = function(v){
	this._height=v;
	
}


fractalTreeClass.prototype.init=function(){
	this.make();
	this.draw();
	if(this.name!=""){
		window[this.name]=this;
	}
}
fractalTreeClass.prototype.make=function(){
	for(var i=0;i<this.iterations;i++){
		for (var r in this.rules) {
			this.commandString = this.commandString.replace(this.rules[r][0], this.rules[r][1]);
		}
	}
}
fractalTreeClass.prototype.draw = function(){
	this.canvas.clipRect(this._x,this._y,this._width,this._height);	
	this.branchDraw=this.stickBranch;
	this.drawTree(0,{x:this.rootX,y:this.rootY},90,this.magnitude,1);		
}
fractalTreeClass.prototype.stickBranch = function(curPos, newPos,it){
    this.canvas.strokeStyle("rgba(0,0,0,"+Math.max(1-it/5,0)+")");		
	this.canvas.beginPath();
	this.canvas.moveTo(curPos.x,curPos.y);
	this.canvas.lineTo(newPos.x,newPos.y);
	this.canvas.stroke();	
}

fractalTreeClass.prototype.trunkBranch = function(curPos, newPos,it){
	var width=20;
	var dec=3;	

	var ort={x:(newPos.y-curPos.y),y:-(newPos.x-curPos.x)};
	var l=Math.sqrt(ort.x*ort.x+ort.y*ort.y);
	ort.x/=l;
	ort.y/=l;
	var sw=width-dec*it;
	var ew=width-dec*(it+1);
	
	this.canvas.fillStyle("#52af65");
	this.canvas.beginPath();		
	this.canvas.moveTo(curPos.x+ort.x/2*sw,curPos.y+ort.y/2*sw);
	this.canvas.lineTo(newPos.x+ort.x/2*sw,newPos.y+ort.y/2*sw);
	this.canvas.lineTo(newPos.x-ort.x/2*sw,newPos.y-ort.y/2*sw);
	this.canvas.lineTo(curPos.x-ort.x/2*sw,curPos.y-ort.y/2*sw);			
	this.canvas.fill();		
}

fractalTreeClass.prototype.drawTree=function(stringPos, curPos, curAngle, curLength,it){
	var i=stringPos;
	while (i <this.commandString.length ){
		var c =this.commandString.charAt(i);
		switch (c){
			case '[':
				i = this.drawTree(i+1, curPos, curAngle, curLength*this.magnitudeDec,it+1);
			break;
			case ']':
				return i;
			case 'F':			   
				var newPos = this.FromAngleAndMagnitude(curPos, curAngle, curLength + Math.random());	
				this.branchDraw(curPos,newPos,it);
				curPos = newPos;
			break;	
			case 'P':		
				if(it>this.iterations-2){
					var rgb='rgb('+Math.floor(80+Math.random()*100)+","+Math.floor(160+Math.random()*80)+","+Math.floor(80+Math.random()*80)+")";
					this.canvas.fillStyle(rgb);
					this.canvas.fillCircle(newPos.x,newPos.y,2);
				}	   
			break;				
			case '-':
				curAngle -= this.angle;
			break;
			case 'R':
				this.angle=Math.random()*90;
			break;			
			case '+':
				curAngle +=this.angle;
			break;
		}
		i++;
	}
	return i;
}
fractalTreeClass.prototype.FromAngleAndMagnitude = function(origin, angle, magnitude){
	var radians = angle * Math.PI / 180;
	
	return {
		x: magnitude * Math.cos(radians) + origin.x,
		y: magnitude * -Math.sin(radians) + origin.y
	}
}

if(typeof(JCanvasDSLClass)=="function"){
	JCanvasDSLClass.prototype.rootNode.fractalTree=function(self,node){			
		var ele=FractalTree(self.canvas);
		self.mapAttributes(node,ele,[
				['x','x',parseInt],
				['y','y',parseInt],
				['width','width',parseInt],
				['height','height',parseInt],														
				['rootX','rootX',parseInt],
				['rootY','rootY',parseInt],
				['name','name']
				]
		);	
		var c=self.firstChild(node);
		ele.init();		
	}
}
