// ***************** COPYRIGHT (c) 2006 STEFAN WANER ******************// *********************** ALL RIGHTS RESERVED ************************// Globals// window.onerror = myErrorTrap;var e = 2.718281828459045;var pi = 3.141592653589793;var epsilon = .000000000001;var canvas, ctx; // for excanvasvar MouseUp = false;var okToRoll = true;var theMode = 0;// (0 = explicit, 1 = parametric)var theDirection = "up" // rotation directionvar justErased = false; // pressed the erase buttonvar canvasWidth = 400;var canvasHeight = 400;var theTriangle = 0;	// the current trianglevar theColor = 0; // the color of a pixelvar numColors = 7;var lineColor = new Array();	lineColor[0] = 	"red";	lineColor[1] = 	"blue";	lineColor[2] = 	"purple";	lineColor[3] = 	"green";	lineColor[4] = 	"magenta";	lineColor[5] = 	"grey";	lineColor[6] = 	"orange";	lineColor[7] = 	"yellow";// Surface polygon globalsvar maxNumTriangles = 1024;var numTriangles = 0;var numIncrements = 10; 		// number of rectangles = square of thisvar numX = 400; // the actual screen size of the palettevar numY = 400; var squareUp = false; // if true, all three scales equalvar framed = false; // whether or mot to put it in a framed boxvar u = 0; var v = 0; 		// needed for eval parametersvar theTriangleArray = new makeArray2(maxNumTriangles,12);// format of [i] th entry: 	must draw? 0/1 , color , minx, miny, maxx, maxy, x1, y1, x2, y2, x3, y3var vertexArray = new makeArray3(numIncrements+1, numIncrements+1,3);var theTriangleData = new makeArray2(maxNumTriangles, 6);// format of [i] the entry: inverse matrix of adjacent direction vectors and coordinates of opposte vertex in this basis.var theTriangleData2 = new makeArray2(maxNumTriangles, 3);// format front/back (1 or 0), light intensity, distance to viewervar theSequence = new makeArray(maxNumTriangles)var xMin = -.2;				// viewport coordsvar xMax = .2 ;var yMin = -.2-.02;var yMax = .2-.02 ;var DXX = xMax-xMin;var DYY = yMax-yMin;var cs = Math.cos(pi/20);			// rotation stpsvar sn = Math.sin(pi/20);var xL = 5; var yL = 5; var zL = 10;		// light source positionvar deltaIntens = 0;						// to adjust the light intensityvar xv = 5; var yv = 5; var zv = 2;			// camera lens positionvar lenv = Math.pow(xv*xv+yv*yv+zv*zv,.5); 	// length of above vectorvar xup = 0; var yup = 0; var zup = 1;		// up direction of camera		var xt = 0; var  yt = 0; var  zt = 0;		// target point you are looking atvar xn = xt - xv;				// needed globalsvar yn = yt - yv;var zn = zt - zv;var norm =  Math.pow(xn*xn + yn*yn + zn*zn, 0.5);xn = xn/norm;yn = yn/norm;zn = zn/norm;var xe = xv - xn;var ye = yv - yn;var ze = zv - zn;var dot = xn*xup + yn*yup + zn*zup;var xtu = xup - dot*xn;var ytu = yup - dot*yn;var ztu = zup - dot*zn;var xtx = yn * ztu - zn*ytu;var ytx = zn * xtu - xn*ztu;var ztx = xn * ytu - yn*xtu;norm = Math.pow(xtu*xtu + ytu*ytu + ztu*ztu, 0.5);xtu = xtu/norm;ytu = ytu/norm;ztu = ztu/norm;var sigDig = 6;		// default accuracy // graphing globalsvar theFunctionString = ''; // the function it will graphvar x = 0;  // wants a global x for eval...// *** Generate the bounding cube (again)var theResult = new makeArray(3);var x1, y1, z1, x2, y2, z2;var xCube1, xCube2, xCube3, xCube4, xCube5, xCube6, xCube7, xCube8;var yCube1, yCube2, yCube3, yCube4, yCube5, yCube6, yCube7, yCube8;x1 = -1;y1 = -1;z1 = -1;theResult = persp_proj(x1, y1, z1);xCube1 = Math.round( numX*(theResult[1]-xMin)/DXX );yCube1= Math.round( numY*(yMax- theResult[2])/DYY );x1 = -1;y1 = 1;z1 = -1;theResult = persp_proj(x1, y1, z1);xCube2 = Math.round( numX*(theResult[1]-xMin)/DXX );yCube2= Math.round( numY*(yMax- theResult[2])/DYY );x1 = -1;y1 = 1;z1 = 1;theResult = persp_proj(x1, y1, z1);xCube3 = Math.round( numX*(theResult[1]-xMin)/DXX );yCube3= Math.round( numY*(yMax- theResult[2])/DYY );x1 = -1;y1 = -1;z1 = 1;theResult = persp_proj(x1, y1, z1);xCube4 = Math.round( numX*(theResult[1]-xMin)/DXX );yCube4= Math.round( numY*(yMax- theResult[2])/DYY );x1 = 1;y1 = -1;z1 = -1;theResult = persp_proj(x1, y1, z1);xCube5 = Math.round( numX*(theResult[1]-xMin)/DXX );yCube5= Math.round( numY*(yMax- theResult[2])/DYY );x1 = 1;y1 = 1;z1 = -1;theResult = persp_proj(x1, y1, z1);xCube6 = Math.round( numX*(theResult[1]-xMin)/DXX );yCube6= Math.round( numY*(yMax- theResult[2])/DYY );x1 = 1;y1 = 1;z1 = 1;theResult = persp_proj(x1, y1, z1);xCube7 = Math.round( numX*(theResult[1]-xMin)/DXX );yCube7= Math.round( numY*(yMax- theResult[2])/DYY );x1 = 1;y1 = -1;z1 = 1;theResult = persp_proj(x1, y1, z1);xCube8 = Math.round( numX*(theResult[1]-xMin)/DXX );yCube8= Math.round( numY*(yMax- theResult[2])/DYY );// *** save and recover globalsvar cookiesEnabled = true;// *** undo erase globalsvar functionstrt, xMint, xMaxt, yMint, yMaxt, xfunct, yfunct, zfunct, uMint, vMint, uMaxt, vMaxt;// * Now rotation function definitionsvar rotate = (function(){  var isRunning;  var action;  return {    start : function(){// alert(direction);		if (theDirection == "up") action = calc2(9);		else if (theDirection == "down") action = calc2(8);		else if (theDirection == "left") action = calc2(5);		else if (theDirection == "right") action = calc2(4);      isRunning = setTimeout('rotate.start();', 200);    },    stop : function (){      if (isRunning) clearTimeout(isRunning);	getReady(); drawFilled(true); // draw the graph    }  }})(); // ***testing *****// generate a surfacevar theFunctionX = "u";var theFunctionY = "v";var theFunctionZ = "Math.pow(u,2) + Math.pow(v,2)";// **** end testing ****function makeArray3 (X,Y,Z)	{	var count;	this.length = X+1;	for (var count = 1; count <= X+1; count++)		// to allow starting at 1		this[count] = new makeArray2(Y,Z);	} // makeArray2function makeArray2 (X,Y)	{	var count;	this.length = X+1;	for (var count = 1; count <= X+1; count++)		// to allow starting at 1		this[count] = new makeArray(Y);	} // makeArray2function makeArray (Y)	{	var count;	this.length = Y+1;	for (var count = 1; count <= Y+1; count++)		this[count] = 0;	} // makeArrayfunction readAccuracy() {var acc = parent.right.document.inputPanel.accuracy.value;if (acc == "") document.output.value = "You must enter a value 1-10";else 	{	var accn = eval(acc);	if ( (accn < 1) || (accn >10) ) document.output.value = "You must enter a value 1-10";	else sigDig = accn;	}} // read accuracy// ******** RESET THE GLOBAL VIEWING VARIABLESfunction resetView() {		var norm =  Math.pow(xn*xn + yn*yn + zn*zn, 0.5);		xn = xn/norm;		yn = yn/norm;		zn = zn/norm;		xe = xv - xn;		ye = yv - yn;		ze = zv - zn;		dot = xn*xup + yn*yup + zn*zup;		xtu = xup - dot*xn;		ytu = yup - dot*yn;		ztu = zup - dot*zn;		xtx = yn * ztu - zn*ytu;		ytx = zn * xtu - xn*ztu;		ztx = xn * ytu - yn*xtu;		norm = Math.pow(xtu*xtu + ytu*ytu + ztu*ztu, 0.5);		xtu = xtu/norm;		ytu = ytu/norm;		ztu = ztu/norm;	// *** Generate the bounding cube (again)	var theResult = new makeArray(3);	var x1, y1, z1, x2, y2, z2;	x1 = -1;	y1 = -1;	z1 = -1;	theResult = persp_proj(x1, y1, z1);	xCube1 = Math.round( numX*(theResult[1]-xMin)/DXX );	yCube1= Math.round( numY*(yMax- theResult[2])/DYY );	x1 = -1;	y1 = 1;	z1 = -1;	theResult = persp_proj(x1, y1, z1);	xCube2 = Math.round( numX*(theResult[1]-xMin)/DXX );	yCube2= Math.round( numY*(yMax- theResult[2])/DYY );	x1 = -1;	y1 = 1;	z1 = 1;	theResult = persp_proj(x1, y1, z1);	xCube3 = Math.round( numX*(theResult[1]-xMin)/DXX );	yCube3= Math.round( numY*(yMax- theResult[2])/DYY );	x1 = -1;	y1 = -1;	z1 = 1;	theResult = persp_proj(x1, y1, z1);	xCube4 = Math.round( numX*(theResult[1]-xMin)/DXX );	yCube4= Math.round( numY*(yMax- theResult[2])/DYY );	x1 = 1;	y1 = -1;	z1 = -1;	theResult = persp_proj(x1, y1, z1);	xCube5 = Math.round( numX*(theResult[1]-xMin)/DXX );	yCube5= Math.round( numY*(yMax- theResult[2])/DYY );	x1 = 1;	y1 = 1;	z1 = -1;	theResult = persp_proj(x1, y1, z1);	xCube6 = Math.round( numX*(theResult[1]-xMin)/DXX );	yCube6= Math.round( numY*(yMax- theResult[2])/DYY );	x1 = 1;	y1 = 1;	z1 = 1;	theResult = persp_proj(x1, y1, z1);	xCube7 = Math.round( numX*(theResult[1]-xMin)/DXX );	yCube7= Math.round( numY*(yMax- theResult[2])/DYY );	x1 = 1;	y1 = -1;	z1 = 1;	theResult = persp_proj(x1, y1, z1);	xCube8 = Math.round( numX*(theResult[1]-xMin)/DXX );	yCube8= Math.round( numY*(yMax- theResult[2])/DYY );}// ********* FUNCTION PARSER ***********function myEval(theString){return(eval(myParse(theString)));	}// The above function requires all the following routinesfunction breakApart(InString) {	// ******* Input: any string such as aaa*bbb or (aa*aa)*bbb	// *** This Routine Retuns two pieces	// *** bbb starts at the left-most operation *, /,  - or +	// *** if it sees a paren, it stops after closure.	theString = InString;	var Length = theString.length;	var outArray = new Array();	outArray[1] = theString;	outArray[2] = "";	var parenCount = 0;	var parenWatch = false;	var looking = true;			if (theString.substring (0,1) == "(")		{		parenCount++;		parenWatch = true;		looking = false;		}	// Look for operators		for (Count=1; Count < Length; Count++)  		{		TempChar=theString.substring (Count, Count+1);		if (TempChar == "(") 			{parenCount ++;}		else if (TempChar == ")") {parenCount = parenCount-1};		if ((parenCount == 0) && (parenWatch == true))			 {			parenWatch = false;			outArray[1] = theString.substring (0, Count+1);			outArray[2] = theString.substring (Count+1, Length);			}		if (looking == true)			{		if ( ( (TempChar == "*") || (TempChar == "/") || (TempChar == "-")  ) || ( (TempChar == "+") || (TempChar == ')' )  )  )				{ 									{					// alert(Count);					looking = false;					outArray[1] = theString.substring (0, Count);					outArray[2] = theString.substring (Count, Length);					// alert (outArray[1]);					// alert(outArray[2]);					} // end if hit one				} 			} // end if looking		} // end of loop	return (outArray);} // end of breakApartfunction isNumberChar (InString)  {	if(InString.length!=1) 		return (false);	RefString="1234567890";	if (RefString.indexOf (InString, 0)==-1) 		return (false);	return (true);}function isCharHere (InString, RefString)  {	if(InString.length!=1) 		return (false);	if (RefString.indexOf (InString, 0)==-1) 		return (false);	return (true);}function putProduct(InString) {OutString="";for (Count=0; Count < InString.length; Count++)  {		TempChar=InString.substring (Count, Count+1);		if (!isCharHere(TempChar,"tTyYzZxXeslcap") || (Count == 0) )			{OutString=OutString+TempChar}		else 			{			if (isNumberChar(InString.substring(Count-1,Count)))				{OutString=OutString+"*"+TempChar}			else OutString=OutString+TempChar			}	}	return (OutString);}function reverse (InString)  {	OutString="";	var Length = InString.length;	for (Count=Length; Count > -1; Count--)  {		TempChar=InString.substring (Count, Count+1);		if (TempChar == "(") {TempChar = ")"}		else if  (TempChar == ")") {TempChar = "("}		OutString=OutString+TempChar;		}	return (OutString);	}function powFix2(InString) {	// ****Replaces one "^" by "pow"	theString = InString;	var Length = theString.length;	outString = theString; 		// in case nothing happens			// Look for wedge	var looking = true;	for (Count=0; Count < Length; Count++)  		{		if (looking)			{			TempChar=theString.substring (Count, Count+1);			if (TempChar == "^")				{				looking = false;				rightStr = theString.substring (Count+1,Length);				leftStr = theString.substring (0,Count);				// deal with right-hand string				Aray = breakApart(rightStr);				Arg2 = Aray[1];				rightRest = Aray[2];							backString = reverse(leftStr);				Aray = breakApart(backString);				Arg1 = reverse(Aray[1]);				leftRest = reverse(Aray[2]);				outString = leftRest+"Math.pow("+Arg1+","+Arg2+")"+rightRest;								} // end hif hit a wedge			} // end of looking for a wedge		} // end of loop// ****** testing *******// document.Extra.diagnostics.value += outString + cr;// ***** end testing *****return (outString);} // end of powFix2function powCheck(InString) {	// ****checks for ^		theString = InString;	var Length = theString.length;		// Look for wedge	var found = false;	for (Count=0; Count < Length; Count++)  		{		TempChar=theString.substring (Count, Count+1);		if (TempChar == "^")			{			found = true;			} // end if hit a wedge		} // end of looking for a wedge	return(found);} // end of powCheckfunction myParse(expression){		var theString = stripSpaces(expression);		theString = replaceSubstring (theString,"cos","Math.cos");		theString = replaceSubstring (theString,"sin","Math.sin");		theString = replaceSubstring (theString,"ln","Math.ln");		theString = replaceSubstring (theString,"log","Math.log");		theString = replaceSubstring (theString,")(",")*(");		theString = replaceSubstring (theString,")ln",")*ln");		theString = replaceSubstring (theString,"sqrt","Math.sqrt");			with (Math)			{		// now convert formatting from GC formatting				theString = putProduct(theString);		theString = replaceSubstring(theString,"log","(1/log(10))*log");		theString = replaceSubstring(theString,"ln","log");			while (powCheck(theString))				{				theString = powFix2(theString);				// alert (theString);				}		theString = replaceChar(theString,"X","x");			} // with Math\	return(theString);} // myParse// ******** END FUNCTION PARSER **************function replaceChar (InString,oldSymbol,newSymbol)  {	var OutString="";	var TempChar = "";	for (Count=0; Count < InString.length; Count++)  {		TempChar=InString.substring (Count, Count+1);		if (TempChar!=oldSymbol)			OutString=OutString+TempChar		else OutString=OutString+newSymbol;	}	return (OutString);}function replaceSubstring (InString,oldSubstring,newSubstring)  {	OutString="";	var sublength = oldSubstring.length;	for (Count=0; Count < InString.length; Count++)  {		TempStr=InString.substring (Count, Count+sublength);		TempChar=InString.substring (Count, Count+1);		if (TempStr!= oldSubstring)			OutString=OutString+TempChar		else 			{			OutString=OutString+ newSubstring;			Count +=sublength-1			}	}	return (OutString);}// ****Get the function and prepare to generate surfacefunction getReady() {justErased = false;canvas = document.getElementById("cv");ctx = canvas.getContext("2d");okToRoll = true;if(document.inputPanel.S1[0].checked) squareUp = false;else squareUp = true;if(document.inputPanel.R1[0].checked) theMode = 0;else theMode = 1;if(document.inputPanel.F1[0].checked) framed = true;else framed = false;// alert(theMode);if (theMode == 0) 	{	// explicit	var functionExpr = stripSpaces(document.inputPanel.functionstr.value);	var xMinstr = document.inputPanel.xMin.value;	var xMaxstr = document.inputPanel.xMax.value;	var yMinstr = document.inputPanel.yMin.value;	var yMaxstr = document.inputPanel.yMax.value;	if (functionExpr == "") {alert("You must enter a function to graph."); okToRoll = false}	else if (!looksLikeANumber(xMinstr)) { alert("You have not entered a numerical value for xMin."); okToRoll = false}	else if (!looksLikeANumber(xMaxstr)) { alert("You have not entered a numerical value for xMax."); okToRoll = false}	else if (!looksLikeANumber(yMinstr)) { alert("You have not entered a numerical value for yMin."); okToRoll = false}	else if (!looksLikeANumber(yMaxstr)) { alert("You have not entered a numerical value for yMax."); okToRoll = false}	if (okToRoll)		{		var uMin = myEval(xMinstr);		var uMax = myEval(xMaxstr);		var vMin = myEval(yMinstr);		var vMax = myEval(yMaxstr);		if (uMin >= uMax) { alert("xMin should be less than xMax."); okToRoll = false}		else if (vMin >= vMax) { alert("yMin should be less than yMax."); okToRoll = false}		}	if (okToRoll)		{		var theString = myParse(document.inputPanel.functionstr.value);		theString = replaceChar (theString,"x","u");		theString = replaceChar (theString,"y","v");		theString = replaceChar (theString,"X","u");		theString = replaceChar (theString,"Y","v");		generateTriangles("u", "v", theString, uMin, uMax, vMin, vMax);		// drawFilled(true);		}	}else				{	// parametric	var functionExprx = stripSpaces(document.inputPanel.xfunc.value);	var functionExpry = stripSpaces(document.inputPanel.yfunc.value);	var functionExprz = stripSpaces(document.inputPanel.zfunc.value);	var uMinstr = document.inputPanel.uMin.value;	var uMaxstr = document.inputPanel.uMax.value;	var vMinstr = document.inputPanel.vMin.value;	var vMaxstr = document.inputPanel.vMax.value;	if ((functionExprx == "") ||(functionExpry == "") || (functionExprz == "") ) {alert("You must enter x, y, and z as funcitons of u and v."); okToRoll = false}	else if (!looksLikeANumber(uMinstr)) { alert("You have not entered a numerical value for uMin."); okToRoll = false}	else if (!looksLikeANumber(uMaxstr)) { alert("You have not entered a numerical value for uMax."); okToRoll = false}	else if (!looksLikeANumber(vMinstr)) { alert("You have not entered a numerical value for vMin."); okToRoll = false}	else if (!looksLikeANumber(vMaxstr)) { alert("You have not entered a numerical value for vMax."); okToRoll = false}	if (okToRoll)		{		var uMin = myEval(uMinstr);		var uMax = myEval(uMaxstr);		var vMin = myEval(vMinstr);		var vMax = myEval(vMaxstr);	if (uMin >= uMax) { alert("uMin should be less than uMax."); okToRoll = false}	else if (vMin >= vMax) { alert("vMin should be less than vMax."); okToRoll = false}		}	if (okToRoll)		{		var theStringx = myParse(document.inputPanel.xfunc.value);		var theStringy = myParse(document.inputPanel.yfunc.value);		var theStringz = myParse(document.inputPanel.zfunc.value);		generateTriangles(theStringx, theStringy, theStringz, uMin, uMax, vMin, vMax);	//	drawFilled(true);		}		}} // *** End of getReady// *** GENERATE TRIANGLES FOR A FUNCTION ****function generateTriangles(theFunctionX, theFunctionY, theFunctionZ, uMin, uMax, vMin, vMax) {// the parametric variables are "u" and "v"numTriangles = 0;var deltau = (uMax-uMin)/numIncrements;var deltav = (vMax-vMin)/numIncrements;var x1, y1, z1, x2, y2, z2, x3, y3, z3, x4, y4, z4, t1, t2, t3, xg1, yg1, xg2, yg2, norm, norm2;u = uMin;v = vMin; var xLow = eval(theFunctionX);	// these are the bounds of the enveloping parallelopipedvar xHigh = xLow; if (isNaN(xLow)) {xLow = 1000000; xHigh = -1000000}var yLow = eval(theFunctionY);var yHigh = yLow;if (isNaN(yLow)) {yLow = 1000000; yHigh = -1000000}var zLow = eval(theFunctionZ);var zHigh = zLow;if (isNaN(zLow)) {zLow = 1000000; zHigh = -1000000}for (var i = 1; i <= numIncrements+1; i++)	{	for (var j = 1; j <= numIncrements+1; j++)		{		x1 = eval(theFunctionX);		if (x1 < xLow) xLow = x1;		else if (x1 > xHigh) xHigh = x1;		vertexArray[i][j][1] = x1;				y1 = eval(theFunctionY);		if (y1 < yLow) yLow = y1;		else if (y1 > yHigh) yHigh = y1;		vertexArray[i][j][2] = y1;				z1 = eval(theFunctionZ);		if (z1 < zLow) zLow = z1;		else if (z1 > zHigh) zHigh = z1;		vertexArray[i][j][3] = z1;// alert("u = " + u + "; v = " + v + "("+x1 + " " + y1 + " " + z1 + ")");// alert(vertexArray[i][j][3]);		v += deltav;		} // j	u += deltau;	v = vMin;	} // ivar deltaX = xHigh-xLow;var deltaY = yHigh-yLow;var deltaZ = zHigh-zLow;// alert("deltaX = "+ deltaX + "deltay = "+ deltaY + "deltaZ = "+ deltaZ);if((squareUp)&&(theMode == 1)) {var biggest = deltaX;var biggestScale = "X";if (deltaY > biggest) {biggest = deltaY; biggestScale = "Y"}if (deltaZ > biggest) {biggest = deltaZ; biggestScale = "Z"}if (biggestScale == "X") {	yLow -= (biggest-deltaY)/2;	zLow -= (biggest-deltaZ)/2;	yHigh += (biggest-deltaY)/2;	zHigh += (biggest-deltaZ)/2	}else if (biggestScale == "Y") {	xLow -= (biggest-deltaX)/2;	zLow -= (biggest-deltaZ)/2;	xHigh += (biggest-deltaX)/2;	zHigh += (biggest-deltaZ)/2	}else{	xLow -= (biggest-deltaX)/2;	yLow -= (biggest-deltaY)/2;	xHigh += (biggest-deltaX)/2;	yHigh += (biggest-deltaY)/2	}deltaX = xHigh-xLow;deltaY = yHigh-yLow;deltaZ = zHigh-zLow;// alert("deltaX = "+ deltaX + "deltay = "+ deltaY + "deltaZ = "+ deltaZ);} // for squaring up scales// now scale everything to fit into a unit cube [-1, 1]^3for (var i = 1; i <= numIncrements+1; i++)	{	for (var j = 1; j <= numIncrements+1; j++)		{		x1 = vertexArray[i][j][1];		y1 = vertexArray[i][j][2];		z1 = vertexArray[i][j][3];		vertexArray[i][j][1] = 2*(x1-xLow)/deltaX - 1;		vertexArray[i][j][2] = 2*(y1-yLow)/deltaY - 1;		vertexArray[i][j][3] = 2*(z1-zLow)/deltaZ - 1;// alert("i = " + i + "; j = " + j + "("+x1 + " " + y1 + " " + z1 + ")");		} // i	} // j// alert ("HERE***HERE");// now generate the actual trianglesvar theResult = new makeArray(3);var dd1 = 0;var dd2 = 0;var dd3 = 0;	// this will be the vector from the lens to the midpoint of the facefor (var i = 1; i <= numIncrements; i++)	{	for (var j = 1; j <= numIncrements; j++)		{		numTriangles += 1;		theTriangleArray[numTriangles][1] = 1;	// set to draw it		x1 = vertexArray[i][j][1];		y1 = vertexArray[i][j][2];		z1 = vertexArray[i][j][3];		theResult = persp_proj(x1, y1, z1);		xg1 = theResult[1];	// x1		yg1 = theResult[2];	// y1		xg2 = Math.round( numX*(xg1-xMin)/DXX );		yg2= Math.round( numY*(yMax-yg1)/DYY );		theTriangleArray[numTriangles][7] = xg2;	// x1		theTriangleArray[numTriangles][8] = yg2;	// y1// alert("i = " + i + "; j = " + j + "("+xg1 + " " + yg1 + ")");		x2 = vertexArray[i+1][j][1];		y2 = vertexArray[i+1][j][2];		z2 = vertexArray[i+1][j][3];		theResult = persp_proj(x2, y2, z2);		xg1 = theResult[1];	// x1		yg1 = theResult[2];	// y1		xg2 = Math.round( numX*(xg1-xMin)/DXX );		yg2= Math.round( numY*(yMax-yg1)/DYY );		theTriangleArray[numTriangles][9] = xg2;	// x1		theTriangleArray[numTriangles][10] = yg2;	// y1// alert("i = " + i + "; j = " + j + "("+xg1 + " " + yg1 + ")");		x3 = vertexArray[i][j+1][1];		y3 = vertexArray[i][j+1][2];		z3 = vertexArray[i][j+1][3];		theResult = persp_proj(x3, y3, z3);		xg1 = theResult[1];	// x1		yg1 = theResult[2];	// y1		xg2 = Math.round( numX*(xg1-xMin)/DXX );		yg2= Math.round( numY*(yMax-yg1)/DYY );		theTriangleArray[numTriangles][11] = xg2;	// x1		theTriangleArray[numTriangles][12] = yg2;	// y1		x4 = vertexArray[i+1][j+1][1];		y4 = vertexArray[i+1][j+1][2];		z4 = vertexArray[i+1][j+1][3]; // these for later (next triangle)// alert("i = " + i + "; j = " + j + "("+xg1 + " " + yg1 + ")");				// now set up the normal stuff and light intensity// alert(x1 + " " + y1 + " " + z1 + ") ( " + x2 + " " + y2 + " " + z2 + ") ( " + x3 + " " + y3 + " " + z3);		theResult = cross(x2-x1,y2-y1,z2-z1,x3-x1,y3-y1,z3-z1);	norm = Math.pow(theResult[1]*theResult[1] + theResult[2]*theResult[2] + theResult[3]*theResult[3],0.5);  		if(norm < epsilon) {theResult = cross(x4-x1,y4-y1,z4-z1,x4-x2,y4-y2,z4-z2); norm = Math.pow(theResult[1]*theResult[1] + theResult[2]*theResult[2] + theResult[3]*theResult[3],0.5)}			var dot = theResult[1]*(x1-xv) + theResult[2]*(y1-yv) + theResult[3]*(z1-zv);			if (dot < 0) theTriangleData2[numTriangles][1] = 1; 			else theTriangleData2[numTriangles][1] = 0;			theTriangleData2[numTriangles+1][1] = theTriangleData2[numTriangles][1];			// both triangles face the same way					norm2 = Math.pow((xL-x1)*(xL-x1) + (yL-y1)*(yL-y1) + (zL-z1)*(zL-z1),0.5);// alert(norm + " " + norm2);					dot =  (theResult[1]*(xL-x1) + theResult[2]*(yL-y1) + theResult[3]*(zL-z1))/(norm*norm2);// alert(dot);			theTriangleData2[numTriangles][2] = -dot;			theTriangleData2[numTriangles+1][2] = -dot; // both have same intensity			// distance to viewer squared				dd1 = xv - (x2+x3)/2;			dd2 = yv - (y2+y3)/2;			dd3 = zv - (z2+z3)/2;			norm = dd1*dd1+dd2*dd2+dd3*dd3;			theTriangleData2[numTriangles][3] = norm;			theTriangleData2[numTriangles+1][3] = norm; // both have same distance			// now the next triangle;		numTriangles ++;				theResult = persp_proj(x4, y4, z4);		xg1 = theResult[1];	// x1		yg1 = theResult[2];	// y1		xg2 = Math.round( numX*(xg1-xMin)/DXX );		yg2= Math.round( numY*(yMax-yg1)/DYY );		theTriangleArray[numTriangles][7] = xg2;	// x1		theTriangleArray[numTriangles][8] = yg2;	// y1// alert("i = " + i + "; j = " + j + "("+xg1 + " " + yg1 + ")");		// now the other vertices (already computed)		theTriangleArray[numTriangles][9] = theTriangleArray[numTriangles-1][11];			theTriangleArray[numTriangles][10] = theTriangleArray[numTriangles-1][12];		theTriangleArray[numTriangles][11] = theTriangleArray[numTriangles-1][9];			theTriangleArray[numTriangles][12] = theTriangleArray[numTriangles-1][10];	// alert(z2);// alert(i);// alert(j);		} // j		} // i// alert(numTriangles); // ok now  ** HERE we need to sort the distances and normalize the intensities (1-10)var dmin = theTriangleData2[1][3];	// distance of first trianglevar dmax = dmin;if (isNaN(dmin)) {dmin = 50000; dmax = 0} // should be ok because everything is only a few units away in the unit cubevar dist;var theTriangle = 1;var theTriangle2 = 1;var sorting = true;var theIndex = 1;var found = false;var theStart = 1; 		// loop starts looking herewhile (sorting) 	{	for (var i = theStart; i <= numTriangles; i++)		{ 		found = false;		if (theTriangleArray[i][1] != 0)			{			dist = theTriangleData2[i][3];			if  (dist <= dmin)  { dmin = dist; theTriangle2 = i}			else if (dist >= dmax) { dmax = dist; theTriangle = i}			else if (isNaN(dist)) {theTriangle2 = i}			}		i++; 	// do it in twos		} // i	 	theTriangleArray[theTriangle][1] = 0;	theTriangleArray[theTriangle+1][1] = 0;	theTriangleArray[theTriangle2][1] = 0;	theTriangleArray[theTriangle2+1][1] = 0;	theSequence[theIndex] = theTriangle;	theSequence[theIndex+1] = theTriangle+1;	theSequence[numTriangles-theIndex] = theTriangle2;	theSequence[numTriangles-theIndex+1] = theTriangle2+1;	theIndex += 2;// alert(theIndex);// alert(theTriangle);	sorting = false;	for (var i = theStart; i <= numTriangles; i +=2)		{		if (theTriangleArray[i][1] == 1) 			{			sorting = true;			dmin = theTriangleData2[i][3];			dmax = dmin;			if (isNaN(dmin)) {dmin = 50000; dmax = 0} // should be ok because everything is only a few units away in the unit cube			theTriangle = i;			theTriangle2 = i; 			theStart = i;				break;			// next round starts here			} // if saw a 1			} // i		} // whle sorting;// alert("sorted");	// now do the colors.// intensities should be between -1 and +1var intens, theColor;for (var i = 1; i <= numTriangles; i++)	{	intens = Math.round((theTriangleData2[i][2] + 1)*5);	// should be scaled 0-10 now// alert(theTriangleData2[i][2]);// now adjust the intensity as per control panel;	intens += deltaIntens;	if(intens < 0) intens = 0;	if(intens > 9) intens = 9;	if (theTriangleData2[i][1] == 1) theColor = 10+intens;	else theColor = 20+intens;	theTriangleArray[i][2] = theColor;	}// alert(numTriangles);// testing .....// this puts up all the coordinates// var theSt = "";// for (var i = 1; i <= numTriangles; i++)// 	{//	for (var j = 1; j <= 12; j++)//		{//		theSt += theTriangleArray[i][j] + " ";//		}//		theSt += unescape( "%0D" );//	} // document.inputPanel.theText.value = theSt;// end testing ...// document.inputPanel.theText.value += "DONE";} // generate triangles// ******* END GENERATE FUNCTION ***********// ***********************// *    cross product    *// ***********************function cross(x1,y1,z1,x2,y2,z2) {var out = new makeArray(3);out[1] = y1*z2-z1*y2;out[2] = z1*x2-z2*x1;out[3] = x1*y2-y1*x2;return(out);} // cross product// *** END CROSS PRODUCT***// *******************************// *   routine perspective projection   *// *******************************// globals are various: xn, yn, zn, xr, yr, zefunction persp_proj(xp,yp,zp) {var tq,xq,yq,zq, xvq, yvq, zvqvar out = new makeArray(3);tq = (xn*(xv-xp) + yn*(yv-yp) + zn*(zv-zp)) / (xn*(xe-xp) + yn*(ye-yp) + zn*(ze-zp));xvq = xp + (xe - xp)*tq -xv;yvq = yp+ (ye - yp)*tq -yv;zvq = zp+ (ze - zp)*tq -zv;out[1] = xvq*xtx+yvq*ytx+zvq*ztx;out[2] = xvq*xtu+yvq*ytu+zvq*ztu;return(out)} // persp proj// ******** END PERSPECTIVE PROJ *************// ******* FUNCTION FIX TRIANGLE ARRAY BOUNDS// sets drawing windows for each trianglefunction fixArrayBounds() {var minx = 0; var maxx = 0; var miny = 0; var maxy = 0;var x1, x2, x3, y1, y2, y3;var crossProd = new makeArray(3);for (var i = 1; i <= numTriangles; i++)	{ 	if (theTriangleArray[i][1] == 1)		{		x1 = theTriangleArray[i][7];		y1 = theTriangleArray[i][8];		x2 = theTriangleArray[i][9];		y2 = theTriangleArray[i][10];		x3 = theTriangleArray[i][11];		y3 = theTriangleArray[i][12];		minx = x1;		maxx = x1;		miny = y1;		maxy = y1;		if (x2 > maxx) maxx = x2;		if (x3 > maxx) maxx = x3;		if (x2 < minx) minx = x2;		if (x3 < minx) minx = x3;		if (y2 > maxy) maxy = y2;		if (y3 > maxy) maxy = y3;		if (y2 < miny) miny = y2;		if (y3 < miny) miny = y3;		theTriangleArray[i][3] = Math.round(minx);		theTriangleArray[i][4] = Math.round(miny);		theTriangleArray[i][5] = Math.round(maxx);		theTriangleArray[i][6] = Math.round(maxy);				}	} // end of main loop i} // fix array bounds// ****** END FIX ARRAY BOUNDS ***************// ******* FUNCTION DRAW FILLED RECTANGLE *******// Needs a rectangle array theTriangleangles of dimension 	numTriangles x 12// format of [i] th entry: 	must draw? 0/1 , color , minx, miny, maxx, maxy, x1, y1, x2, y2, x3, y3// dealing with screen coords by this point...// the rectangles are assumed sorted furthest to closest// if edges are on, just paint part of outer boundaryfunction drawFilled (edgeon) { var x1, x2, x3, y1, y2, y3, minx, miny, maxx, maxy;fixArrayBounds();		// insert all array bounds	var p = 1; // Step 2 Draw them// set up excanvasctx.clearRect(0,0,canvasWidth, canvasHeight);// ***Testing// var theStringTest = '';// for (var k = 1; k <= numTriangles; k++) { p = theSequence[k]; theStringTest += (" "+p)}// alert(theStringTest);// END of test// first draw the box if requiredif(framed)drawBackOfBox();theTriangle = 0;	// resets the current trianglefor (var k = 1; k <= numTriangles; k+=2)	{	p = theSequence[k];// alert("k = "+k + " p = " + p + "color = "+ theTriangleArray[p][2]);	var style = '';// now create the colors// stored in theTriangleArray[p][2]// 10-19 purples // 20-29: greensvar theColorCode = theTriangleArray[p][2]; if ((10 <= theColorCode) && (theColorCode <= 19))	{	// purples	var theRed = 130 + (theColorCode-10)*25;	if (theRed > 255) theRed = 255;	var theGreen = 50 + (theColorCode-10)*20;	if (theGreen > 255) theGreen = 255;	var theBlue = 220 + (theColorCode-10)*25;	if (theBlue > 255) theBlue = 255;	}else if ((20 <= theColorCode) && (theColorCode <= 30))	{	// greens	var theRed = (theColorCode-20)*25;	if (theRed > 255) theRed = 255;	var theGreen = 100 + (theColorCode-20)*33;	if (theGreen > 255) theGreen = 255;	var theBlue = (theColorCode-20)*30;	if (theBlue > 255) theBlue = 255;	}style = "rgb(" + theRed + "," + theGreen + "," + theBlue + ")";	if (!isNaN(theColorCode)) ctx.strokeStyle = style;	else ctx.strokeStyle = "rgb(100,100,100)"; // will not plot this actually	if (!isNaN(theColorCode)) ctx.fillStyle = style;	else ctx.fillStyle = "rgb(100,100,100)"; // will not plot this actually	var x1 = theTriangleArray[p][7];	var y1 = theTriangleArray[p][8];	var x2 = theTriangleArray[p][9];	var y2 = theTriangleArray[p][10];	var x3 = theTriangleArray[p][11];	var y3 = theTriangleArray[p][12];	var xn1 = theTriangleArray[p+1][7];	var yn1 = theTriangleArray[p+1][8];	var xn2 = theTriangleArray[p+1][9];	var yn2 = theTriangleArray[p+1][10];	var xn3 = theTriangleArray[p+1][11];	var yn3 = theTriangleArray[p+1][12];// alert("(" + x1 + "," + y1 + "), ("+ x2 + "," + y2 + "), ("+ x3 + "," + y3 + ")");// if (k == 101) alert(isNaN(y1));if (!isNaN(x1)&&!isNaN(x2)&&!isNaN(x3)&&!isNaN(xn1)&&!isNaN(xn2)&&!isNaN(xn3)&& !isNaN(y1)&&!isNaN(y2)&&!isNaN(y3)&&!isNaN(yn1)&&!isNaN(yn2)&&!isNaN(yn3)) {	ctx.beginPath();	ctx.moveTo(x1,y1);	ctx.lineTo(x2,y2);	ctx.lineTo(x3,y3);	ctx.fill();	ctx.beginPath();	ctx.moveTo(x2,y2);	ctx.lineTo(x3,y3);	ctx.stroke();	ctx.beginPath();	ctx.moveTo(xn1,yn1);	ctx.lineTo(xn2,yn2);	ctx.lineTo(xn3,yn3);	ctx.fill();	ctx.beginPath();	ctx.moveTo(xn2,yn2);	ctx.lineTo(xn3,yn3);	ctx.stroke();	// now the outline	theRed = Math.round(theRed*.7);	theGreen = Math.round(theGreen*.7);	theBlue = Math.round(theBlue*.7);	style = "rgb(" + theRed + "," + theGreen + "," + theBlue + ")";	ctx.strokeStyle = style;	ctx.beginPath();	ctx.moveTo(x3,y3);	ctx.lineTo(x1,y1);	ctx.lineTo(x2,y2);	ctx.stroke();}	} // done with all the trianglesif(framed)drawFrontOfBox();		} // drawFilled function det(A)	{	var Length = A.length-1;		// formal length of a matrix is one bigger	if (Length == 1) return (A[1][1]);	else		{		var i;		var sum = 0;		var factor = 1;		for (var i = 1; i <= Length; i++)			{			if (A[1][i] != 0)				{				// create the minor				minor = new makeArray2(Length-1,Length-1);				var m;				var n;				var theColumn;				for (var m = 1; m <= Length-1; m++) // columns					{					if (m < i) theColumn = m;					else theColumn = m+1;					for (var n = 1; n <= Length-1; n++)						{						minor[n][m] = A[n+1][theColumn];// alert(minor[n][m]);						} // n					} // m				// compute its determinant				sum = sum + A[1][i]*factor*det(minor);				}			factor = -factor;	// alternating sum			} // end i		} // recursion	return(sum);	} // end determinantfunction inverse(A) {	var Length = A.length - 1;	B = new makeArray2(Length, Length);  // inverse	var d = det(A);	if (d == 0) alert("singular matrix--check data");	else		{		var i;		var j;		for (var i = 1; i <= Length; i++)			{			for (var j = 1; j <= Length; j++)				{				// create the minor				minor = new makeArray2(Length-1,Length-1);				var m;				var n;				var theColumn;				var theRow;				for (var m = 1; m <= Length-1; m++) // columns					{					if (m < j) theColumn = m;					else theColumn = m+1;					for (var n = 1; n <= Length-1; n++)						{						if (n < i) theRow = n;						else theRow = n+1;						minor[n][m] = A[theRow][theColumn];// alert(minor[n][m]);						} // n					} // m				// inverse entry				var temp = (i+j)/2;				if (temp == Math.round(temp)) factor = 1;				else factor = -1;								B[j][i] =  det(minor)*factor/d; 								} // j						} // end i		} // recursion	return(B);	} // end inversefunction shiftRight(theNumber, k) {	if (k == 0) return (theNumber)	else		{		var k2 = 1;		var num = k;		if (num < 0) num = -num;		for (var i = 1; i <= num; i++)			{			k2 = k2*10			}		}	if (k>0) 		{return(k2*theNumber)}	else 		{return(theNumber/k2)}	}function roundSigDig(theNumber, numDigits) {	with (Math)		{		if (theNumber == 0) return(0);		else if(abs(theNumber) < 0.000000000001) return(0);// WARNING: ignores numbers less than 10^(-12)		else			{			var k = floor(log(abs(theNumber))/log(10))-numDigits+1			var k2 = shiftRight(round(shiftRight(abs(theNumber),-k)),k)			if (theNumber > 0) return(k2);			else return(-k2)			} // end else		}} // roundSigDigfunction clearForms (){	document.inputPanel.output.value=""	document.inputPanel.coeff.value =""	for (i = 0; i <= 15; i++)		{		document.inputPanel[2+3*i].value = "";	// xval[i]		document.inputPanel[3+3*i].value = "";	// yval[i]		document.inputPanel[4+3*i].value = "";	// ansl[i]		// document.inputPanel.xval[i].value=""		// document.inputPanel.yval[i].value=""		// document.inputPanel.ans[i].value=""		}	}function stripSpaces (InString)  {	OutString="";	for (var Count=0; Count < InString.length; Count++)  {		TempChar=InString.substring (Count, Count+1);		if (TempChar!=" ")			OutString=OutString+TempChar;		}	return (OutString);}function stripChar(InString,symbol)  {	var OutString="";	for (var Count=0; Count < InString.length; Count++)  {		TempChar=InString.substring (Count, Count+1);		if (TempChar!=symbol)			OutString=OutString+TempChar;	}	return (OutString);}function parser (InString, Sep)  {	NumSeps=1;	for (Count=1; Count < InString.length; Count++)  {		if (InString.charAt(Count)==Sep)			NumSeps++;	}	parse = new makeArray (NumSeps);	Start=0; Count=1; ParseMark=0;	LoopCtrl=1;	while (LoopCtrl==1)  {		ParseMark = InString.indexOf(Sep, ParseMark);		TestMark=ParseMark+0;		if ((TestMark==0) || (TestMark==-1)){			parse[Count]= InString.substring (Start, InString.length);			LoopCtrl=0;			break;		}		parse[Count] = InString.substring (Start, ParseMark);		Start=ParseMark+1;		ParseMark=Start;		Count++;	}	parse[0]=Count;	return (parse);}function buildxy()  {	readAccuracy();	e = 2.718281828459045;	pi = 3.141592653589793;		with (Math)		{		N = 0; 		// number of variables 		var searching = true;		for ( i = 0; i <= 15; i++)			// arrays start at 0			{			theString = stripSpaces(document.inputPanel[2+3*i].value);											if (theString == "") searching = false;			if (searching)				{ 				N++;				X[N] = eval(theString);				theString = stripSpaces(document.inputPanel[3+3*i].value);				Y[N] = eval(theString);				}			} // of i = 1 to 15		} // end of with math	}function looksLikeANumber(theString) {// returns true if theString looks like it can be evaluatedvar result = true;theString = stripSpaces(theString);if (theString == "") return(false);theString = replaceSubstring(theString,"pi","3");	// just temporary!!!theString = replaceSubstring(theString,"e","2");	// dittotheString = replaceSubstring(theString,"sin","2");	// dittotheString = replaceSubstring(theString,"cos","2");	// dittotheString = replaceSubstring(theString,"ln","2");	// dittotheString = replaceSubstring(theString,"log","2");	// dittotheString = replaceSubstring(theString,"sqrt","2");	// dittovar length = theString.length;var x = ""var y = "1234567890-+/*.()"var yLength = y.length;for (var i = 0; i <= length; i++)	{ 	x = theString.charAt(i);		result = false;		for (var j = 0; j <= yLength; j++) 			{			if (x == y.charAt(j)) {result = true; break}			} // j	if (result == false) return(false);	} // ireturn(result);} // looks like a numberfunction calc2(){		var num = calc2.arguments[0];	//**********	// Option 1		if (num == 1)		{		if(okToRoll) drawFilled(true);		}	// Option 2 Draw box	else  if (num == 2)		{		drawBox();		} // of this option	// Option 3 OLD UP	else  if (num == 3)		{		// rotate up vector too		var xupold = xup;		var yupold = yup;		var zupold = zup;				var xvold = xv;		var yvold = yv;		var zvold = zv;		xv = xvold*cs - zvold*sn;		zv = xvold*sn + zvold*cs;		xup = xupold*cs - zupold*sn;		zup = xupold*sn + zupold*cs;		xn = xt - xv;		yn = yt - yv;		zn = zt - zv;		// new zn		resetView();		drawBox();		}		// Option 4 RIGHT	else  if (num == 4)		{		var xupold = xup;		var yupold = yup;		var zupold = zup;		var xvold = xv;		var yvold = yv;		var zvold = zv;		xv = xvold *cs + yvold *sn;		yv = -xvold *sn + yvold *cs;		xup = xupold *cs + yupold *sn;		yup = -xupold *sn + yupold *cs;		xn = xt - xv;		yn = yt - yv;		zn = zt - zv;		// new zn		resetView();		drawBox();		}	// Option 5 LEFTelse  if (num == 5)		{		var xupold = xup;		var yupold = yup;		var zupold = zup;		var xvold = xv;		var yvold = yv;		var zvold = zv;		xv = xvold *cs - yvold *sn;		yv = xvold *sn + yvold *cs;		xup = xupold *cs - yupold *sn;		yup = xupold *sn + yupold *cs;		xn = xt - xv;		yn = yt - yv;		zn = zt - zv;		// new zn		resetView();		drawBox();	} // end option 5	// Option 6 OLD DOWN	else  if (num == 6)		{		// rotate up vector too		var xupold = xup;		var yupold = yup;		var zupold = zup;		var xvold = xv;		var yvold = yv;		var zvold = zv;		xv = xvold *cs + zvold *sn;		zv = -xvold *sn + zvold *cs;		xup = xupold*cs + zupold*sn;		zup = -xupold*sn + zupold*cs;		xn = xt - xv;		yn = yt - yv;		zn = zt - zv;		// new zn		resetView();		drawBox();		}	// Option 7 RESET	else  if (num == 7)		{		// rotate up vector too		xv = 5;		yv = 5;		zv = 2;		xup = 0; yup = 0; zup = 1;		xn = xt - xv;		yn = yt - yv;		zn = zt - zv;		// new zn		resetView();		drawBox();		}		// Option 8 TRUE UP	else  if (num == 8)		{		// rotate up vector too		// now fix up up vector (right angles to this)		var crossprod = cross(xv,yv,zv,xup,yup,zup);		var xr = crossprod[1];		var yr = crossprod[2];		var zr = crossprod[3];  // this is a vector at right angles to up & v		// var xvt = .2*xup + .8*xv;		// var yvt = .2*yup + .8*yv;		// var zvt = .2*zup + .8*zv;		var xvt = 5*sn*xup + cs*xv;		var yvt = 5*sn*yup + cs*yv;		var zvt = 5*sn*zup + cs*zv;		var len = Math.pow(xvt*xvt+yvt*yvt+zvt*zvt,.5);		var rat = lenv/len;		xv = xvt*rat;			// fix up length		yv = yvt*rat;		zv = zvt*rat;// alert(xv + " " + yv + " " + zv);		var crossprod2 = cross(xr,yr,zr,xv,yv,zv);		var xupt = crossprod2[1];		var yupt = crossprod2[2];		var zupt = crossprod2[3];		len = Math.pow(xupt*xupt+yupt*yupt+zupt*zupt,.5);		xup = xupt/len;		yup = yupt/len;		zup = zupt/len// alert(xup + " " + yup + " " + zup);		xn = xt - xv;		yn = yt - yv;		zn = zt - zv;		// new zn		resetView();		drawBox();		}	// Option 9 TRUE DOWN	else  if (num == 9)		{		// rotate up vector too		// now fix up up vector (right angles to this)		var crossprod = cross(xv,yv,zv,xup,yup,zup);		var xr = crossprod[1];		var yr = crossprod[2];		var zr = crossprod[3];  // this is a vector at right angles to up & v		var xvt = -5*sn*xup + cs*xv;		var yvt = -5*sn*yup + cs*yv;		var zvt = -5*sn*zup + cs*zv;		var len = Math.pow(xvt*xvt+yvt*yvt+zvt*zvt,.5);		var rat = lenv/len;		xv = xvt*rat;			// fix up length		yv = yvt*rat;		zv = zvt*rat;// alert(xv + " " + yv + " " + zv);		var crossprod2 = cross(xr,yr,zr,xv,yv,zv);		var xupt = crossprod2[1];		var yupt = crossprod2[2];		var zupt = crossprod2[3];		len = Math.pow(xupt*xupt+yupt*yupt+zupt*zupt,.5);		xup = xupt/len;		yup = yupt/len;		zup = zupt/len// alert(xup + " " + yup + " " + zup);		xn = xt - xv;		yn = yt - yv;		zn = zt - zv;		// new zn		resetView();		drawBox();		}	}function drawLine(x1,y1,x2,y2) {ctx.beginPath();ctx.moveTo(x1,y1);ctx.lineTo(x2,y2);ctx.stroke();}//************ Draw Boxfunction drawBox() {canvas = document.getElementById("cv");ctx = canvas.getContext("2d");ctx.clearRect(0,0,canvasWidth, canvasHeight);drawBackOfBox();drawFrontOfBox();} // drawBoxfunction drawBackOfBox() {ctx.strokeStyle = "rgb(100,100,100)";		if (1+xe >= 0)			{			drawLine(xCube1,yCube1,xCube2,yCube2);			drawLine(xCube1,yCube1,xCube4,yCube4);			drawLine(xCube3,yCube3,xCube4,yCube4);			drawLine(xCube2,yCube2,xCube3,yCube3);			}		if (1-xe >=0)			{			drawLine(xCube5,yCube5,xCube6,yCube6);			drawLine(xCube6,yCube6,xCube7,yCube7);			drawLine(xCube7,yCube7,xCube8,yCube8);			drawLine(xCube8,yCube8,xCube5,yCube5);					}		if (1+ye >=0)			{			drawLine(xCube1,yCube1,xCube5,yCube5);			drawLine(xCube5,yCube5,xCube8,yCube8);			drawLine(xCube8,yCube8,xCube4,yCube4);			drawLine(xCube4,yCube4,xCube1,yCube1);			}			if (1-ye>=0)			{			drawLine(xCube2,yCube2,xCube3,yCube3);			drawLine(xCube3,yCube3,xCube7,yCube7);			drawLine(xCube7,yCube7,xCube6,yCube6);			drawLine(xCube6,yCube6,xCube2,yCube2);					}			if (1+ze>=0)			{			drawLine(xCube6,yCube6,xCube5,yCube5);			drawLine(xCube5,yCube5,xCube1,yCube1);			drawLine(xCube1,yCube1,xCube2,yCube2);			drawLine(xCube2,yCube2,xCube6,yCube6);			}			if (1-ze >=0)			{			drawLine(xCube8,yCube8,xCube7,yCube7);			drawLine(xCube7,yCube7,xCube3,yCube3);			drawLine(xCube3,yCube3,xCube4,yCube4);			drawLine(xCube4,yCube4,xCube8,yCube8);					}} // drawBackOfBoxfunction drawFrontOfBox() {ctx.strokeStyle = "rgb(100,100,100)";		if (1+xe < 0)			{			drawLine(xCube1,yCube1,xCube2,yCube2);			drawLine(xCube1,yCube1,xCube4,yCube4);			drawLine(xCube3,yCube3,xCube4,yCube4);			drawLine(xCube2,yCube2,xCube3,yCube3);			}		if (1-xe < 0)			{			drawLine(xCube5,yCube5,xCube6,yCube6);			drawLine(xCube6,yCube6,xCube7,yCube7);			drawLine(xCube7,yCube7,xCube8,yCube8);			drawLine(xCube8,yCube8,xCube5,yCube5);					}		if (1+ye < 0)			{			drawLine(xCube1,yCube1,xCube5,yCube5);			drawLine(xCube5,yCube5,xCube8,yCube8);			drawLine(xCube8,yCube8,xCube4,yCube4);			drawLine(xCube4,yCube4,xCube1,yCube1);			}			if (1-ye< 0)			{			drawLine(xCube2,yCube2,xCube3,yCube3);			drawLine(xCube3,yCube3,xCube7,yCube7);			drawLine(xCube7,yCube7,xCube6,yCube6);			drawLine(xCube6,yCube6,xCube2,yCube2);					}			if (1+ze< 0)			{			drawLine(xCube6,yCube6,xCube5,yCube5);			drawLine(xCube5,yCube5,xCube1,yCube1);			drawLine(xCube1,yCube1,xCube2,yCube2);			drawLine(xCube2,yCube2,xCube6,yCube6);			}			if (1-ze < 0)			{			drawLine(xCube8,yCube8,xCube7,yCube7);			drawLine(xCube7,yCube7,xCube3,yCube3);			drawLine(xCube3,yCube3,xCube4,yCube4);			drawLine(xCube4,yCube4,xCube8,yCube8);					}} // drawFrontofBox// ********************** Saving and Loading Utilities ************function sesame(url,hsize,vsize){ // Default size is 550 x 400        var tb="toolbar=0,directories=0,status=0,menubar=0"        tb+=",scrollbars=1,resizable=1,"    var tbend="width="+hsize+",height="+vsize;    if(tbend.indexOf("<undefined>")!=-1){tbend="width=550,height=400"}      tb+=tbend     	Win_1 = window.open("","win1",tb);      Win_1 = window.open(url,"win1",tb); 	Win_1.focus();}function setCookie (cookieName, cookieValue, expires, path, domain, secure) {  document.cookie =     escape(cookieName) + '=' + escape(cookieValue)     + (expires ? '; EXPIRES=' + expires.toGMTString() : '')    + (path ? '; PATH=' + path : '')    + (domain ? '; DOMAIN=' + domain : '')    + (secure ? '; SECURE' : '');}function getCookie (cookieName) {  var cookieValue = null;  var posName = document.cookie.indexOf(escape(cookieName) + '=');  if (posName != -1) {    var posValue = posName + (escape(cookieName) + '=').length;    var endPos = document.cookie.indexOf(';', posValue);    if (endPos != -1)      cookieValue = unescape(document.cookie.substring(posValue, endPos));    else      cookieValue = unescape(document.cookie.substring(posValue));  }  return cookieValue;}function saveIt()  {// first test to see if cookies are enabledvar now = new Date();var tomorrow = new Date(now.getTime() + 1000 * 60 * 60 * 24);var nextyear = new Date(now.getTime() + 1000 * 60 * 60 * 24 * 365);var twosecs = new Date(now.getTime() + 1000 * 2);var yesterday = new Date(now.getTime() - 1000 * 60 * 60 * 24);var twomin = new Date(now.getTime() + 1000 * 60 * 2);setCookie('cookieTest','You will meet a tall dark stranger.', twosecs);var testing = getCookie('cookieTest');if(testing == null) {alert("Cookies are disabled on your computer. 'Save' and 'Load' will not work unless cookies are enabled."); cookiesEnabled = false}// now get the various data to savevar theString = stripSpaces(document.inputPanel.functionstr.value); //1theString += '$' + stripSpaces(document.inputPanel.xMin.value); //2theString += '$' + stripSpaces(document.inputPanel.xMax.value); //3theString += '$' + stripSpaces(document.inputPanel.yMin.value); //4theString += '$' + stripSpaces(document.inputPanel.yMax.value); //5theString += '$' + stripSpaces(document.inputPanel.xfunc.value); //6theString += '$' + stripSpaces(document.inputPanel.yfunc.value); //7theString += '$' + stripSpaces(document.inputPanel.zfunc.value); //8theString += '$' + stripSpaces(document.inputPanel.uMin.value); //9theString += '$' + stripSpaces(document.inputPanel.uMax.value); //10theString += '$' + stripSpaces(document.inputPanel.vMin.value); //11theString += '$' + stripSpaces(document.inputPanel.vMax.value); //12var testString = stripChar(theString,'$');if (cookiesEnabled && (stripSpaces(testString) != '')) {// *** Open a window	verifyList(); // reconcile save list with older saved files	// add the global orientation data	theString += '$' + xup; //13	theString += '$' + yup; //14	theString += '$' + zup; //15	theString += '$' + xv; //16	theString += '$' + yv; //17	theString += '$' + zv; //18	theString += '$' + xn; //19	theString += '$' + yn; //20	theString += '$' + zn; //21	setCookie('temp1', theString, twomin);	var pqr = sesame("save.html",500,150);	} // if cookies are enabledelse if (!cookiesEnabled) alert("Cookies are not enabled on your computer, so Save will not work.");else alert("You have entered no data to save.");} // saveItfunction verifyList() {// This reconciles the save list with the actual files. // In particular it removes files that have expired.var now = new Date();var nextyear = new Date(now.getTime() + 1000 * 60 * 60 * 24 * 365);var yesterday = new Date(now.getTime() - 1000 * 60 * 60 * 24);var theList = getCookie('saveList');// alert(theList);var theStr = '';var theName = '';if (theList != null) {	var theArray = parser(theList,"$");	var n = theArray[0]-1;// alert(theArray[2]);	if (n <= 0) setCookie('saveList','',yesterday);	else {		for (var i = 1; i <= n; i++) {			theName = theArray[i];			var theData = getCookie(theName);			if (theData != null) {				theStr += theName + '$';				setCookie(theName, theData, nextyear);				// to update old data cookies				} // end if			} // i		if (theStr == '') setCookie('saveList','',yesterday);		else setCookie('saveList',theStr,nextyear);		} // end else	} // end if}function loadData() {// first test to see if cookies are enabledvar now = new Date();var tomorrow = new Date(now.getTime() + 1000 * 60 * 60 * 24);var nextyear = new Date(now.getTime() + 1000 * 60 * 60 * 24 * 365);var twosecs = new Date(now.getTime() + 1000 * 2);var yesterday = new Date(now.getTime() - 1000 * 60 * 60 * 24);var twomin = new Date(now.getTime() + 1000 * 60 * 2);setCookie('cookieTest','You will meet a tall dark stranger.', twosecs);var testing = getCookie('cookieTest');if(testing == null) {alert("Cookies are disabled on your computer. 'Save' and 'Load' will not work unless cookies are enabled."); cookiesEnabled = false}// now do the actual loadingif (cookiesEnabled) sesame('recover.html',500,150);// note that the code for this is in that document// all it does is create a temporary cookie of the saed data fot eh page to load on focus// the function updateData takes the temporary cookie data and loads it onto the page} // loadDatafunction updateData() {// this is activated on focus and updates any graph data currently available in a short-lived cookie called temp// should really strt with a cookies enabled test but the code takes care of it if notif (cookiesEnabled) {	var theS = getCookie('temp');	var now = new Date();	var tomorrow = new Date(now.getTime() + 1000 * 60 * 60 * 24);	var nextyear = new Date(now.getTime() + 1000 * 60 * 60 * 24 * 365);	var twosecs = new Date(now.getTime() + 1000 * 2);	var yesterday = new Date(now.getTime() - 1000 * 60 * 60 * 24);	setCookie('temp', '', yesterday);// if(theS == null) alert(theS);	if ((theS == null) || (theS == '')) theS = "hello";	// in case not enabled	else {		var kdata = parser(theS, '$');		document.inputPanel.functionstr.value = kdata[1];		document.inputPanel.xMin.value = kdata[2];		document.inputPanel.xMax.value = kdata[3];		document.inputPanel.yMin.value = kdata[4];		document.inputPanel.yMax.value = kdata[5];		document.inputPanel.xfunc.value = kdata[6];		document.inputPanel.yfunc.value = kdata[7];		document.inputPanel.zfunc.value = kdata[8];		document.inputPanel.uMin.value = kdata[9];		document.inputPanel.uMax.value = kdata[10];		document.inputPanel.vMin.value = kdata[11];		document.inputPanel.vMax.value = kdata[12];		xup = kdata[13];		yup = kdata[14];		zup = kdata[15];		xv = kdata[16];		yv = kdata[17];		zv = kdata[18];		xn = kdata[19];		yn = kdata[20];		zn = kdata[21];		// now throw up the graph		resetView();		getReady(); 		drawFilled(true);		} // data to load	for (var i = 1; i <= 100; i++) {theS = ''}// I forget why the above line is there	} // if cookies are enabled} // updateData
