
function padding(laenge) {
  result = '';
  for (i = 0; i < laenge; i++)
    result = result + '___';
  return result;
}

function print_r(das_array, ebene) {
  var result = '';  
  for (var wert in das_array)
    if (typeof das_array[wert] == "object")
      result = result + ' ' + padding(ebene) + wert + "\n" + print_r(das_array[wert], ebene + 1);
    else
      result = result + ' ' + padding(ebene) + wert + ' = ' + das_array[wert] + "\n";

  return result;
}



function debugprint (s)
{
	var e = $("#log").attr("value");
	$("#log").attr("value", e+s);	
}

function debugprintT (titel,s)
{
	var e = $("#log").attr("value");
	$("#log").attr("value", e+"=== "+titel+" ===\n"+s+"\n");	
}


function Array_copy(a) {
        return a.slice(0, a.length);
      }


/**
*
*  Javascript trim, ltrim, rtrim
*  http://www.webtoolkit.info/
*
*
**/

function trim(str, chars) {
    return ltrim(rtrim(str, chars), chars);
}

function ltrim(str, chars) {
    chars = chars || "\\s";
    return str.replace(new RegExp("^[" + chars + "]+", "g"), "");
}

function rtrim(str, chars) {
    chars = chars || "\\s";
    return str.replace(new RegExp("[" + chars + "]+$", "g"), "");
}

//String in Matrix umwandeln
function StringToMatrix (s)
{
	var result = new Array();
	var lines = new Array();
	lines = trim(s).split("\n");	
	
	var len = 0;
	
	for (var i = 0; i < lines.length; i++)
	{
		var line = lines[i].split(" ");
		
		var resultline = new Array();
		
		for (var j = 0; j < line.length; j++)
		{ 	
			resultline.push( line[j]);
		}
		
		if ( i == 0)
		{
			len = resultline.length;
		}
		
		if (resultline.length == len)
		{
			result.push(resultline);
		}
	}
	return result;	
}

//Matrix in String umwandeln
function MatrixToString (m)
{
	var result = "";
	for (var i = 0; i < m.length; i++)
	{
		var line = m[i];
		
		for (var j = 0; j < line.length; j++)
		{ 	
			result += line[j].toString();
			result += " ";
		}
		
		result += "\n";
	}
	return result;
}

function isNumber(x)
{
	return (parseFloat(x) == x);
}

//prüft ob eine Matrix valide ist
function isValid(A)
{
	var rowcount = A.length;
	var colcount = A[0].length;
	for (var lineNr = 0; lineNr < A.length; lineNr++)
	{
		if (A[lineNr].length != colcount)
		{
			return false;
		}
		
		for (var j = 0; j < A[lineNr].length; j++)
		{
			if (!isNumber(A[lineNr][j]))
			{
				return false;
			}
		}
	}
		
	return true;
}

function rowcount(A)
{
	return A.length;
}

function colcount(A)
{
	return A[0].length;
}

function ThrowIfNotValid(A,name)
{
	if (!isValid(A))
		throw "Matix "+name+" ist keine gueltige Matrix.";	
}

function ThrowIfNotSameHeight(A,B,name1, name2)
{
	if (rowcount(A) != rowcount(B))
		throw "Die Matrizen "+name1+" und "+name2+" muessen die gleiche Hoehe haben.";
}

function ThrowIfNotMultiplicatable(A,B,name1, name2)
{
	if (colcount(A) != rowcount(B))
		throw "Die Matrizen "+name1+"("+colcount(A)+" x "+rowcount(A)+") und "+name2+"("+colcount(B)+" x "+rowcount(B)+") muessen multiplizierbar sein.\n";
}


function ThrowIfNotSameSize(A,B,name1, name2)
{
	if (rowcount(A) != rowcount(B))
		throw "Die Matrizen "+name1+" und "+name2+" muessen die gleiche Groesse haben.";
}

function ThrowIfNotVector(x, name)
{
	if (colcount(x) != 1) 
		throw "Der Vektor "+name+" ist kein gueltiger Vektor.";
}

function ThrowIfNoNumber(x,name)
{
	if (! isNumber(x))
		throw ""+name+" muss eine Zahl sein.";
}

function ThrowIfNoFunction(x,name)
{
	if (! jQuery.isFunction(x))
		throw ""+name+" muss eine Funktion sein.";
}





//Obere Dreiecksmatrix lösen
function SolveUpperTriangleMatrix (A,b)
{
	ThrowIfNotValid(A,"A");
	ThrowIfNotValid(b,"b");
	ThrowIfNotSameHeight(A,b,"A","b");
	
	var x = new Array();
	for (var lineNr = A.length-1; lineNr >= 0; lineNr--)
	{
		var line = A[lineNr];
		var c = 0;
		
		for (var colNr = lineNr + 1; colNr < A.length; colNr++)
		{
			c += line[colNr] * x[colNr][0];
		}
		
		x[lineNr] = new Array()
		x[lineNr][0] = (b[lineNr][0] - c ) / line[lineNr];
	}
	return x;
}

//Untere Dreiecksmatrix mit 1 auf der Diagonalen Lösen (L von LR-Zerlegung)
function SolveL (A , b)
{
	var x = new Array();
	for (var lineNr = 0; lineNr < A.length; lineNr++)
	{
		var line = A[lineNr];
		var c = 0;
		
		for (var colNr = 0; colNr < lineNr; colNr++)
		{
			c += line[colNr] * x[colNr][0];
			
		}
		x[lineNr] = new Array()
		x[lineNr][0] = (b[lineNr][0] - c );
	}
	return x;

}


function DiagonalStrategie(A, pos)
{
	//nichts vertauschen
	return pos;
}

function SpaltenmaximumsStrategie(A,pos)
{
	var max_val = Math.abs(A[pos][pos]);
	var max_pos = pos;
	for (var i = pos +1; i < A.length; i++)
	{
		if (Math.abs(A[i][pos]) > max_val)
		{
			max_val = Math.abs(A[i][pos]);
			max_pos = i;
		}
	}
	return max_pos;		
}

function RelativeSpaltenmaximumsStrategie(A,pos)
{
	var rel = new Array();
	for (var i = pos; i < A.length; i++)
	{
		var linesum = 0;
		for (var j = pos; j < A.length; j++)
		{
			linesum += Math.abs(A[i][j]);
		}
		if (A[i][pos] == 0)
		{
			rel[i] = 0;
		}
		else
		{
			rel[i] = A[i][pos] / linesum;
		}	
	}
	
	
	
	var max_val = Math.abs(rel[pos]);
	var max_pos = pos;
	for (var i = pos +1; i < A.length; i++)
	{
		if (Math.abs(rel[i]) > max_val)
		{
			max_val = Math.abs(rel[i]);
			max_pos = i;
		}
	}
	
	//debugprintT("Strategiematrix " + pos ,MatrixToString(A));
	//debugprintT("Strategierel " + pos ,rel[0]+"\n"+rel[1]+"\n"+rel[2]+"\n"+rel[3]+"\n");
	//debugprintT("Strategiepos " + pos ,max_pos);
	
	return max_pos;		
}


//LR-Zerlegung berechnen
function LRZerlegung (A, strategie)
{
	var permutations = new Array();
	
	for (var lineNr = 0; lineNr < A.length; lineNr++)
	{
		permutations[lineNr] = strategie(A, lineNr);
		if (permutations[lineNr] != lineNr)
		{
			//swap lines
			var swap = A[lineNr];
			A[lineNr] = A[permutations[lineNr]];
			A[permutations[lineNr]] = swap;
		}
		
		//debugprintT("Schritt " + lineNr ,MatrixToString(A));
		
		for (var lineNr2 =  lineNr + 1; lineNr2 < A.length; lineNr2++)
		{
			var c = A[lineNr2][lineNr] / A[lineNr][lineNr];
			
			for (colNr = lineNr+1; colNr < A.length; colNr++)
			{
				A[lineNr2][colNr] -= A[lineNr][colNr] * c;
			}	
			
			A[lineNr2][lineNr] = c;
		}
	}
	
	
	
	var result = new Object();
	result.A = A;
	result.permutations = permutations;
	
	return result;
}

function permutate(A, permutations)
{
	for (i=0; (i< permutations.length) && (i < A.length); i++)
	{
		if (i != permutations[i])
		{
			//swap lines
			
			var swap = A[i];
			A[i] = A[permutations[i]];
			A[permutations[i]] = swap;	
		}
	}
	return A;
}

//Gleichungssystem mit LR-Zerlegung lösen
function SolveLR (A,b,strategie)
{
	var zerlegung = LRZerlegung(A, strategie);
	
	//debugprintT("Zerlegung",MatrixToString(zerlegung.A));
	
	var bb = permutate(b, zerlegung.permutations);
	
	var c = SolveL (zerlegung.A, bb);
	
	//debugprintT("c",MatrixToString(c));
	
	var xp = SolveUpperTriangleMatrix(zerlegung.A, c);
	
	//debugprintT("xp",MatrixToString(xp));
	
	//alert (MatrixToString(xp));
	//debugprintT("Permutations",zerlegung.permutations);
	
	//var x = permutate(xp, zerlegung.permutations);
	
	return xp;
}


//Transponierte berechnen
function Transpose (A)
{
	ThrowIfNotValid(A,"A");
	
	var AT = new Array();
	
	for (var j = 0; j < A[0].length; j++)
	{
		AT[j] = new Array();
	}
	
	for (var i = 0; i < A.length; i++)
	{
		for (var j = 0; j < A[i].length; j++)
		{
			AT[j][i] = A[i][j];				
		}	
		
	}
	
	return AT;
}

//Matrixmultiplikation
function Mult (A,B)
{
	ThrowIfNotValid(A,"A");
	ThrowIfNotValid(B,"B");
	ThrowIfNotMultiplicatable(A,B,"A","B");
	
	var R = new Array();
	for (var i = 0; i < A.length; i++)
	{
		R[i] = new Array();
		for (var j = 0; j < B[i].length; j++)
		{
			R[i][j] = 0;
			for (var a = 0; a < B.length; a++)
			{
				//debugprintT("R[i="+i+"][j="+j+"] += ","A[i="+i+"][a="+a+"]*B[a="+a+"][j="+j+"]");
				R[i][j] += A[i][a]*B[a][j];
			}
			
		}	
		
	}
	return R;	
}


//Methode der kleinsten Quadrate
function LeastSquares (A, b)
{
	ThrowIfNotValid(A,"A");
	ThrowIfNotValid(b,"b");
	ThrowIfNotSameHeight(A,b,"A","b");
	
	var AT = Transpose(A);
	
	var SA = Mult(AT, A);
	var Sb = Mult(AT, b);
	
	//debugprintT("SA",MatrixToString(SA));
	//debugprintT("Sb",MatrixToString(Sb));
	var x = SolveLR(SA,Sb, RelativeSpaltenmaximumsStrategie);
	return x;
}


function Add(a,b)
{
	ThrowIfNotValid(a,"a");
	ThrowIfNotValid(b,"b");
	ThrowIfNotSameSize(a,b,"a","b");
	
	var x = new Array();
	for (var i = 0; i < a.length; i++)
	{
		x[i] = new Array();
		for (var j = 0; j < b.length; j++)
		{
			x[i][j] = a[i][j] + b[i][j];
		}
	}
	return x;	
}

function Sub(a,b)
{
	ThrowIfNotValid(a,"a");
	ThrowIfNotValid(b,"b");
	ThrowIfNotSameSize(a,b,"a","b");
	
	var x = new Array();
	for (var i = 0; i < a.length; i++)
	{
		x[i] = new Array();
		for (var j = 0; j < a[i].length; j++)
		{
			x[i][j] = a[i][j] - b[i][j];
		}
	}
	return x;	
}

function Norm(x, n)
{
	ThrowIfNotValid(x,"x");
	ThrowIfNotVector(x,"x");
	ThrowIfNoNumber(n,"n");
	
	var sum = 0;
	for (var i = 0; i < x.length; i++)
	{
		sum += Math.abs(Math.pow(x[i][0], n));
	}
	return Math.pow(sum, 1/n);
}

//Jacobi-Verfahren
function Jacobi (A, b, x0, precision)
{
	ThrowIfNotValid(A,"A");
	ThrowIfNotValid(b,"b");
	ThrowIfNotValid(b,"x0");
	ThrowIfNotSameHeight(A,b,"A","b");
	ThrowIfNotMultiplicatable(A,x0,"A","x0");
	ThrowIfNoNumber(precision,"precision");
	
	
	var x = new Array();
	
	for (var i = 0; i < b.length; i++)
	{
		x[i] = new Array();
		var c = 0;
		for (var j = 0; j < x0.length; j++)
		{
			if (i != j)
			{
				c += A[i][j] * x0[j][0]; 
			}
		}
		if (c == 0)
		{
			x[i][0] = b[i][0] / A[i][i];
		}
		else
		{
			x[i][0] = (b[i][0] - c) / A[i][i];
		}
	}
	var dx = Sub(x0,x);
	//debugprintT("Rekursionschritt: ",MatrixToString(x));
	//debugprintT("dx: ",MatrixToString(dx));
	//debugprintT("Norm: ",Norm(dx,1));
	if (Norm(dx,1) <= precision)
	{
		return x;
	}
	else
	{
		
		return Jacobi(A, b, x, precision);
	}
}

//Seidel-Gauß-Verfahren
function SeidelGauss (A, b, x0, precision)
{
	ThrowIfNotValid(A,"A");
	ThrowIfNotValid(b,"b");
	ThrowIfNotValid(b,"x0");
	ThrowIfNotSameHeight(A,b,"A","b");
	ThrowIfNotMultiplicatable(A,x0,"A","x0");
	ThrowIfNoNumber(precision,"precision");
	
	var x = x0.slice(0);
	
	for (var i = 0; i < b.length; i++)
	{
		x[i] = new Array();
		var c = 0;
		for (var j = 0; j < x.length; j++)
		{
			if (i != j)
			{
				c += A[i][j] * x[j][0]; 
			}
		}
		if (c == 0)
		{
			x[i][0] = b[i][0] / A[i][i];
		}
		else
		{
			x[i][0] = (b[i][0] - c) / A[i][i];
		}
	}
	var dx = Sub(x0,x);
	///debugprintT("Rekursionschritt: ",MatrixToString(x));
	///debugprintT("b: ",MatrixToString(b));
	//debugprintT("x0: ",MatrixToString(x0));
	//debugprintT("x: ",MatrixToString(x));
	//debugprintT("dx: ",MatrixToString(dx));
	//debugprintT("Norm: ",Norm(dx,1));
	if (Norm(dx,1) <= precision)
	{
		return x;
	}
	else
	{
		
		return SeidelGauss(A, b, x, precision);
	}
}

function PointDistance(x0,y0,x1,y1){
	return Math.sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0));
}

//berechnet die 2 übrigen Stützpunkte der besten Bezierkurve durch gegebene Punkte
function Bezierkurve(StartX, StartY, EndX, EndY, PointsX, PointsY) {
	var A = new Array();
	var b = new Array();
	
	//überbestimmtes Gleichungssystem erstellen:
	for (var i = 0; i < PointsX.length; i++) {
		var x = PointsX[i];
		var y = PointsY[i];
		
		
		
		//t nährungsweise bestimmen:
		var t = PointDistance(StartX,StartY, x,y) / (PointDistance(StartX,StartY, x,y) + PointDistance(EndX,EndY, x,y))
		
		//debugprint(x + ", " + y + " -> " + t + "\n");	
			
		//x-Koordinaten-Gleichung
		//var rs = x - (1-t)*(1-t)*(1-t)*StartX + t*t*t*EndX
		var rs = x - (1-t)*(1-t)*(1-t)*StartX - t*t*t*EndX;
		//debugprint("x = " + x + "\n");

		//debugprint("xrs = " + rs + "\n");
		
		var ls = new Array();
		
		ls[0] = 3*t*(1-t)*(1-t);
		ls[1] = 3*t*t*(1-t);
		ls[2] = 0;
		ls[3] = 0;
		
		A.push(ls);
		var temp = new Array();
		temp[0] = rs;
		b.push(temp);
		
		
		
		//y-Koordinaten-Gleichung
		//rs = y - (1-t)*(1-t)*(1-t)*StartY + t*t*t*EndY;
		rs = y - (1-t)*(1-t)*(1-t)*StartY - t*t*t*EndY;
		
		//debugprint("yrs = " + rs + "\n");
		
		ls = new Array();
		
		ls[0] = 0;
		ls[1] = 0;
		ls[2] = 3*t*(1-t)*(1-t);
		ls[3] = 3*t*t*(1-t);
		
		
		A.push(ls);
		temp = new Array();
		temp[0] = rs;
		b.push(temp);
		
		
	}
	//debugprintT("A = ",MatrixToString(A))
	//debugprintT("b = ",MatrixToString(b))
	
	var result = LeastSquares(A,b);
	
	return result;	
}

//berechnet die 2 übrigen Stützpunkte der besten Bezierkurve durch gegebene Punkte
function BezierkurveT(StartX, StartY, EndX, EndY, PointsX, PointsY, PointsT) {
	var A = new Array();
	var b = new Array();
	
	//überbestimmtes Gleichungssystem erstellen:
	for (var i = 0; i < PointsX.length; i++) {
		var x = PointsX[i];
		var y = PointsY[i];
		
		
		
		//t nährungsweise bestimmen:
		var t = PointsT[i] / PointsT[PointsT.length-1];
		
		//debugprint(x + ", " + y + " -> " + t + "\n");	
			
		//x-Koordinaten-Gleichung
		//var rs = x - (1-t)*(1-t)*(1-t)*StartX + t*t*t*EndX
		var rs = x - (1-t)*(1-t)*(1-t)*StartX - t*t*t*EndX;
		//debugprint("x = " + x + "\n");

		//debugprint("xrs = " + rs + "\n");
		
		var ls = new Array();
		
		ls[0] = 3*t*(1-t)*(1-t);
		ls[1] = 3*t*t*(1-t);
		ls[2] = 0;
		ls[3] = 0;
		
		A.push(ls);
		var temp = new Array();
		temp[0] = rs;
		b.push(temp);
		
		
		
		//y-Koordinaten-Gleichung
		//rs = y - (1-t)*(1-t)*(1-t)*StartY + t*t*t*EndY;
		rs = y - (1-t)*(1-t)*(1-t)*StartY - t*t*t*EndY;
		
		//debugprint("yrs = " + rs + "\n");
		
		ls = new Array();
		
		ls[0] = 0;
		ls[1] = 0;
		ls[2] = 3*t*(1-t)*(1-t);
		ls[3] = 3*t*t*(1-t);
		
		
		A.push(ls);
		temp = new Array();
		temp[0] = rs;
		b.push(temp);
		
		
	}
	//debugprintT("A = ",MatrixToString(A))
	//debugprintT("b = ",MatrixToString(b))
	
	var result = LeastSquares(A,b);
	
	return result;	
}





//berechnet die 2 übrigen Stützpunkte der besten Bezierkurve durch gegebene Punkte
function BezierkurveTfastStetig(StartX, StartY, TangenteX, TangenteY, EndX, EndY, PointsX, PointsY, PointsT) {
	var A = new Array();
	var b = new Array();
	
	//überbestimmtes Gleichungssystem erstellen:
	for (var i = 0; i < PointsX.length; i++) {
		var x = PointsX[i];
		var y = PointsY[i];
		
		
		
		//t nährungsweise bestimmen:
		var t = PointsT[i] / PointsT[PointsT.length-1];
		
		//debugprint(x + ", " + y + " -> " + t + "\n");	
			
		//x-Koordinaten-Gleichung
		//var rs = x - (1-t)*(1-t)*(1-t)*StartX + t*t*t*EndX
		var rs = x - (1-t)*(1-t)*(1-t)*StartX - 3*t*(1-t)*(1-t)*StartX - t*t*t*EndX;
		//debugprint("x = " + x + "\n");

		//debugprint("xrs = " + rs + "\n");
		
		var ls = new Array();
		
		ls[0] = 3*t*(1-t)*(1-t)*TangenteX;
		ls[1] = 3*t*t*(1-t);
		ls[2] = 0;
		
		
		A.push(ls);
		var temp = new Array();
		temp[0] = rs;
		b.push(temp);
		
		
		
		//y-Koordinaten-Gleichung
		//rs = y - (1-t)*(1-t)*(1-t)*StartY + t*t*t*EndY;
		rs = y - (1-t)*(1-t)*(1-t)*StartY - 3*t*(1-t)*(1-t)*StartY - t*t*t*EndY;
		
		//debugprint("yrs = " + rs + "\n");
		
		ls = new Array();
		
		ls[0] = 3*t*(1-t)*(1-t)*TangenteY;
		ls[1] = 0;
		ls[2] = 3*t*t*(1-t);
		
		
		A.push(ls);
		temp = new Array();
		temp[0] = rs;
		b.push(temp);
		
		
	}
	//debugprintT("A = ",MatrixToString(A))
	//debugprintT("b = ",MatrixToString(b))
	
	var result1 = LeastSquares(A,b);
	
	
	
	var result2 = new Array();
	
	var temp = new Array();
	temp[0] = StartX + result1[0][0] * TangenteX;
	result2[0] = temp;
	
	result2[1] = result1[1];
	
	temp = new Array();
	temp[0] = StartY + result1[0][0] * TangenteY;
	result2[2] = temp;
	
	result2[3] = result1[2];
		
		
	return result2;	
}



//berechnet die 2 übrigen Stützpunkte der besten Bezierkurve durch gegebene Punkte
function BezierkurveTStetig(StartX, StartY, TangenteX, TangenteY, EndX, EndY, PointsX, PointsY, PointsT) {
	var A = new Array();
	var b = new Array();
	
	//überbestimmtes Gleichungssystem erstellen:
	for (var i = 0; i < PointsX.length; i++) {
		var x = PointsX[i];
		var y = PointsY[i];
		
		
		
		//t nährungsweise bestimmen:
		var t = PointsT[i] / PointsT[PointsT.length-1];
		
		//debugprint(x + ", " + y + " -> " + t + "\n");	
			
		//x-Koordinaten-Gleichung
		//var rs = x - (1-t)*(1-t)*(1-t)*StartX + t*t*t*EndX
		var rs = x - (1-t)*(1-t)*(1-t)*StartX - 3*t*(1-t)*(1-t)*(StartX + TangenteX ) - t*t*t*EndX;
		//debugprint("x = " + x + "\n");

		//debugprint("xrs = " + rs + "\n");
		
		var ls = new Array();
		
		ls[0] = 3*t*t*(1-t);
		ls[1] = 0;

		
		
		A.push(ls);
		var temp = new Array();
		temp[0] = rs;
		b.push(temp);
		
		
		
		//y-Koordinaten-Gleichung
		//rs = y - (1-t)*(1-t)*(1-t)*StartY + t*t*t*EndY;
		rs = y - (1-t)*(1-t)*(1-t)*StartY - 3*t*(1-t)*(1-t)*(StartY + TangenteY) - t*t*t*EndY;
		
		//debugprint("yrs = " + rs + "\n");
		
		ls = new Array();
		
		ls[0] = 0;
		ls[1] = 3*t*t*(1-t);
		
		
		A.push(ls);
		temp = new Array();
		temp[0] = rs;
		b.push(temp);
		
		
	}
	//debugprintT("A = ",MatrixToString(A))
	//debugprintT("b = ",MatrixToString(b))
	
	var result1 = LeastSquares(A,b);
	
	
	
	var result2 = new Array();
	
	var temp = new Array();
	temp[0] = StartX + TangenteX;
	result2[0] = temp;
	
	result2[1] = result1[0];
	
	temp = new Array();
	temp[0] = StartY + TangenteY;
	result2[2] = temp;
	
	result2[3] = result1[1];
		
		
	return result2;	
}



function BezierAbstand(x0,y0,x1,y1,x2,y2,x3,y3,PointsX, PointsY, PointsT) {
	var sum = 0;
	for (var i = 0; i < PointsX.length; i++) {
		var t = PointsT[i]/PointsT[PointsT.length-1];

		var x = (1-t)*(1-t)*(1-t)*x0 + 3*t*(1-t)*(1-t)*x1 + 3*t*t*(1-t)*x2 + t*t*t*x3;
		var y = (1-t)*(1-t)*(1-t)*y0 + 3*t*(1-t)*(1-t)*y1 + 3*t*t*(1-t)*y2 + t*t*t*y3;
		

		
		sum += PointDistance(x,y,PointsX[i],PointsY[i]);
	}	
	
	
	
	return sum / PointsX.length;
}

function BezierAbstandMax(x0,y0,x1,y1,x2,y2,x3,y3,PointsX, PointsY, PointsT) {
	var max = 0;
	for (var i = 0; i < PointsX.length; i++) {
		var t = PointsT[i]/PointsT[PointsT.length-1];

		var x = (1-t)*(1-t)*(1-t)*x0 + 3*t*(1-t)*(1-t)*x1 + 3*t*t*(1-t)*x2 + t*t*t*x3;
		var y = (1-t)*(1-t)*(1-t)*y0 + 3*t*(1-t)*(1-t)*y1 + 3*t*t*(1-t)*y2 + t*t*t*y3;
		

		
		var r = PointDistance(x,y,PointsX[i],PointsY[i]);
		if (r > max) { 
			max = r;
		}
	}	
	
	
	
	return max;
}


function BezierAbstandDraw(x0,y0,x1,y1,x2,y2,x3,y3,PointsX, PointsY, PointsT) {
	for (var i = 0; i < PointsX.length; i++) {
		var t = PointsT[i]/PointsT[PointsT.length-1];

		var x = (1-t)*(1-t)*(1-t)*x0 + 3*t*(1-t)*(1-t)*x1 + 3*t*t*(1-t)*x2 + t*t*t*x3;
		var y = (1-t)*(1-t)*(1-t)*y0 + 3*t*(1-t)*(1-t)*y1 + 3*t*t*(1-t)*y2 + t*t*t*y3;
		

		
		var svg = $('#paint').svg('get');
		var path = "M"+x+","+y+"L"+PointsX[i]+","+PointsY[i];
		var element = svg.path(path,  {'stroke-linejoin': 'round','stroke-linecap': 'round','stroke-opacity': currentopacity, 'fill-opacity': currentbackopacity, fill: 'none', stroke: 'red', 'stroke-width': currentwidth/10});

	}	
	
	

}


function BezierAbstandDraw2(x0,y0,x1,y1,x2,y2,x3,y3) {
	for (var t = 0; t <= 1; t+= 1/100) {
		

		var x = (1-t)*(1-t)*(1-t)*x0 + 3*t*(1-t)*(1-t)*x1 + 3*t*t*(1-t)*x2 + t*t*t*x3;
		var y = (1-t)*(1-t)*(1-t)*y0 + 3*t*(1-t)*(1-t)*y1 + 3*t*t*(1-t)*y2 + t*t*t*y3;
		

		
		var svg = $('#paint').svg('get');
		var path = "M"+x+","+y+"L"+x+","+y;
		var element = svg.path(path,  {'stroke-linejoin': 'round','stroke-linecap': 'round','stroke-opacity': currentopacity/4, 'fill-opacity': currentbackopacity, fill: 'none', stroke: 'red', 'stroke-width': currentwidth});

	}	
	
	

}



function BezierImproveT(x0,y0,x1,y1,x2,y2,x3,y3,PointsX, PointsY, PointsT) {
	var newT = new Array();
	newT[0] = PointsT[0];
	
	var prev_t = PointsT[0]/PointsT[PointsT.length-1];

	var prev_x = (1-prev_t)*(1-prev_t)*(1-prev_t)*x0 + 3*prev_t*(1-prev_t)*(1-prev_t)*x1 + 3*prev_t*prev_t*(1-prev_t)*x2 + prev_t*prev_t*prev_t*x3;
	var prev_y = (1-prev_t)*(1-prev_t)*(1-prev_t)*y0 + 3*prev_t*(1-prev_t)*(1-prev_t)*y1 + 3*prev_t*prev_t*(1-prev_t)*y2 + prev_t*prev_t*prev_t*y3;
	
	
	
	var newlen = newT[0];
	
	for (var i = 1; i < PointsX.length; i++) {
		var t = PointsT[i]/PointsT[PointsT.length-1];

		var x = (1-t)*(1-t)*(1-t)*x0 + 3*t*(1-t)*(1-t)*x1 + 3*t*t*(1-t)*x2 + t*t*t*x3;
		var y = (1-t)*(1-t)*(1-t)*y0 + 3*t*(1-t)*(1-t)*y1 + 3*t*t*(1-t)*y2 + t*t*t*y3;
		
		
		//bezier-difference
		var bdx = x - prev_x;
		var bdy = y - prev_y;
		var bdlen = Math.sqrt(bdx*bdx + bdy*bdy);
		
		//curve-difference
		//var cdx = PointsX[i] - PointsX[i-1];
		//var cdy = PointsY[i] - PointsY[i-1];
		//var cdlen = Math.sqrt(cdx*cdx + cdy*cdy);
		var cdlen = PointsT[i] - PointsT[i-1];
		
		//difference-quotient
		var q = cdlen / bdlen;
		
		//old T difference
		var dt = PointsT[i] - PointsT[i-1];
		
		newlen += dt * q;
		//debugprintT("cdlen / bdlen",cdlen+ " / "+bdlen);

		//debugprintT("q",q);
		//debugprintT("newlen",newlen);
		
		newT[i] = newlen;
		
		prev_t = t;
		prev_x = x;
		prev_y = y;
		
		
	}	
	
	return newT;
}

function BezierImproveT_old(x0,y0,x1,y1,x2,y2,x3,y3,PointsX, PointsY, PointsT) {
	var newT = new Array();
	newT[0] = PointsT[0];
	
	var prev_t = PointsT[0]/PointsT[PointsT.length-1];

	var prev_x = (1-prev_t)*(1-prev_t)*(1-prev_t)*x0 + 3*prev_t*(1-prev_t)*(1-prev_t)*x1 + 3*prev_t*prev_t*(1-prev_t)*x2 + prev_t*prev_t*prev_t*x3;
	var prev_y = (1-prev_t)*(1-prev_t)*(1-prev_t)*y0 + 3*prev_t*(1-prev_t)*(1-prev_t)*y1 + 3*prev_t*prev_t*(1-prev_t)*y2 + prev_t*prev_t*prev_t*y3;
	
	
	
	var newlen = newT[0];
	
	for (var i = 1; i < PointsX.length; i++) {
		var t = PointsT[i]/PointsT[PointsT.length-1];

		var x = (1-t)*(1-t)*(1-t)*x0 + 3*t*(1-t)*(1-t)*x1 + 3*t*t*(1-t)*x2 + t*t*t*x3;
		var y = (1-t)*(1-t)*(1-t)*y0 + 3*t*(1-t)*(1-t)*y1 + 3*t*t*(1-t)*y2 + t*t*t*y3;
		
		
		//bezier-difference
		var bdx = x - prev_x;
		var bdy = y - prev_y;
		var bdlen = Math.sqrt(bdx*bdx + bdy*bdy);
		
		//curve-difference
		var cdx = PointsX[i] - PointsX[i-1];
		var cdy = PointsY[i] - PointsY[i-1];
		var cdlen = Math.sqrt(cdx*cdx + cdy*cdy);
		
		//difference-quotient
		var q = cdlen / bdlen;
		
		//old T difference
		var dt = PointsT[i] - PointsT[i-1];
		
		newlen += dt * q;
		//debugprintT("cdlen / bdlen",cdlen+ " / "+bdlen);

		//debugprintT("q",q);
		//debugprintT("newlen",newlen);
		
		newT[i] = newlen;
		
		prev_t = t;
		prev_x = x;
		prev_y = y;
		
		
	}	
	
	return newT;
}


function BezierImproveConstrict(x0,y0,x1,y1,x2,y2,x3,y3,PointsX, PointsY, PointsT, factor) {
	var newT = new Array();
	newT[0] = 0;
	
	var t = 0;
	var step = 1 / PointsX.length;

	var prev_x = x0;
	var prev_y = y0;
	
	
	
	var newlen = 0;
	
	for (var i = 1; i < PointsX.length; i++) {
		t += step;

		var x = (1-t)*(1-t)*(1-t)*x0 + 3*t*(1-t)*(1-t)*x1 + 3*t*t*(1-t)*x2 + t*t*t*x3;
		var y = (1-t)*(1-t)*(1-t)*y0 + 3*t*(1-t)*(1-t)*y1 + 3*t*t*(1-t)*y2 + t*t*t*y3;
		
		
		var dx = x-prev_x;
		var dy = y-prev_y;
		
		var len =  Math.pow(Math.sqrt(dx*dx+dy*dy), factor);
		newlen += len;
		newT[i] = newlen;
		
		prev_x = x;
		prev_y = y;		
	}	
	
	return newT;
}


function BezierImproveConstrictAuto(x0,y0,x1,y1,x2,y2,x3,y3,PointsX, PointsY, PointsT, minfac, maxfac) {
	var newT;
	
	//binay search the best factor
	while (Math.abs(minfac-maxfac) > 0.1) {
		//test min side
		
		var TMin = BezierImproveConstrict(x0,y0,x1,y1,x2,y2,x3,y3,PointsX, PointsY, PointsT, minfac*0.75 + maxfac*0.25);
		
		var AMin = BezierAbstand(x0,y0,x1,y1,x2,y2,x3,y3,PointsX, PointsY, TMin);
		
		//test max 
		var TMax = BezierImproveConstrict(x0,y0,x1,y1,x2,y2,x3,y3,PointsX, PointsY, PointsT, minfac*0.25 + maxfac*0.75);
		
		var AMax = BezierAbstand(x0,y0,x1,y1,x2,y2,x3,y3,PointsX, PointsY, TMax);
		
		
		if (AMax < AMin) {
			minfac = minfac * 0.5 + maxfac*0.5;
			newT = TMax;
		} else {
			maxfac = minfac * 0.5 + maxfac*0.5;
			newT = TMin;
		}
	}
	
	
	
	return newT;
}


function BezierkurveTCI(StartX, StartY, EndX, EndY, PointsX, PointsY, PointsT, iterations) {
	var points;
	var IPointsT = Array_copy(PointsT);
	
	if (iterations <= 0){
		return BezierkurveT(StartX, StartY, EndX, EndY, PointsX, PointsY, IPointsT);
	}
	
	for (var iterate = 0; iterate < iterations; iterate++) {
		points = BezierkurveT(StartX, StartY, EndX, EndY, PointsX, PointsY, IPointsT);
		IPointsT = BezierImproveConstrictAuto(StartX, StartY, points[0][0],points[2][0], points[1][0],points[3][0], EndX, EndY, PointsX, PointsY, IPointsT,-5,5);	

	}
	
	return points;
}


function BezierkurveTI(StartX, StartY, EndX, EndY, PointsX, PointsY, PointsT, iterations) {
	var points;
	var IPointsT = Array_copy(PointsT);
	
	if (iterations <= 0){
		return BezierkurveT(StartX, StartY, EndX, EndY, PointsX, PointsY, IPointsT);
	}
	
	for (var iterate = 0; iterate < iterations; iterate++) {
		points = BezierkurveT(StartX, StartY, EndX, EndY, PointsX, PointsY, IPointsT);
		IPointsT = BezierImproveT(StartX, StartY, points[0][0],points[2][0], points[1][0],points[3][0], EndX, EndY, PointsX, PointsY, IPointsT);	

	}
	
	return points;
}


function BezierkurveI(StartX, StartY, EndX, EndY, PointsX, PointsY, iterations) {
	var points;
	var IPointsT = new Array();
	
	for (var i = 0; i < PointsX.length; i++) {
		var x = PointsX[i];
		var y = PointsY[i];
		IPointsT[i] = PointDistance(StartX,StartY, x,y) / (PointDistance(StartX,StartY, x,y) + PointDistance(EndX,EndY, x,y))
	}
	
	points = Bezierkurve(StartX, StartY, EndX, EndY, PointsX, PointsY);
	for (var iterate = 0; iterate < iterations; iterate++) {
		
		IPointsT = BezierImproveT(StartX, StartY, points[0][0],points[2][0], points[1][0],points[3][0], EndX, EndY, PointsX, PointsY, IPointsT);	
		points = BezierkurveT(StartX, StartY, EndX, EndY, PointsX, PointsY, IPointsT);
	}
	
	return points;
}

/****************************/

//@ TODO
// Typensicherung / Fehlerbehandlung
// Jacobi + Gauß-Seidel-Verfahren.
