API Docs for:
Show:

File: workspace\templates\partRender.js

//<script>;





/**
*
* Returns a normal material for meshes if the global variable "standard" is false. If "standard"
* is set to true, returns a chromadepth material instead.
*
* @method getStdMaterial
* @for renderGlobal
* @return {Object}
*
*/
function getStdMaterial(){

	if(standard === true){

		return new THREE.ShaderMaterial({
					vertexShader: document.querySelector('#chroma-vert').textContent.trim(),
					fragmentShader: document.querySelector('#chroma-frag').textContent.trim(),
					uniforms: {
						cameraNear: { value: camera.near },
						cameraFar:  { value: camera.far }
					}
				});

	}
	else{

		return new THREE.MeshNormalMaterial({transparent: true} );

	}



}




/**
*
* Returns a normal material for lines if the global variable "standard" is false. If "standard"
* is set to true, returns a chromadepth material instead.
*
* @method getStdMaterial
* @for renderGlobal
* @return {Object}
*
*/
function getStdLine(){

	if(standard === true){

		return new THREE.ShaderMaterial({
					vertexShader: document.querySelector('#chroma-vert').textContent.trim(),
					fragmentShader: document.querySelector('#chroma-frag').textContent.trim(),
					uniforms: {
						cameraNear: { value: camera.near },
						cameraFar:  { value: camera.far }
					}
				});

	}
	else{

		return new THREE.LineDashedMaterial({
					color: 0x333333,
					dashSize: 50,
					gapSize:50

				})

	}



}

/**
*
* Accepts an array of objects containing a string property called "Name" and returns
* the first index where any two "Name" properties in the array have different characters.
*
*
* @method getPartNameCutoff
* @for renderGlobal
* @param {Object Array} partFrames An array of objects, each of which should containin
* a property called "Name" with a non-null string.
* @return {Int} The first index where any two "Name" properties in the array are different.
*
*/
function getPartNameCutoff(partFrames){

	var pos=1;
	var lim=partFrames.length;
	var checkPos;
	var checkLim=partFrames[0].Name.length;
	while(pos<lim){
		checkPos=0;
		while(checkPos<checkLim){
			if(partFrames[pos].Name[checkPos]!=partFrames[0].Name[checkPos]){
				checkLim=checkPos;
			}
			checkPos++;
		}
		pos++;
	}

	return checkLim;

}


/**
*
* Accepts an array of objects containing a string property called "Name" removes
* the first N characters in each string, where N is the first index where any two
* "Name" properties in the array have different characters.
*
*
* @method cutoffPartNames
* @for renderGlobal
* @param {Object Array} partFrames An array of objects, each of which should containin
* a property called "Name" with a non-null string.
* @return {Void}
*
*/
function cutoffPartNames(partFrames){

	var cut=getPartNameCutoff(partFrames);

	var pos=0;
	var lim=partFrames.length;
	while(pos<lim){
		partFrames[pos].Name=partFrames[pos].Name.substr(cut,partFrames[pos].Name.length-(cut+4));
		pos++;
	}

}


/**
*
* Finds the average position of all the vertices in a given threeJS Geometry model.
*
* @method centerGeometry
* @for renderGlobal
* @param {threeJS Geometry Object} theGeo
* @return {threeJS Vector3 Object} A 3d coordinate, with each component being the unweighted
* average of the corresponding component in each vector in the provided geometry object. If nodeName
* vertices are present in the object, a zeroed vector is returned.
*
*/
function centerGeometry(theGeo){

	var verts=theGeo.vertices;
	var pos=0;
	var lim=verts.length;
	var avgX=0;
	var avgY=0;
	var avgZ=0;
	if(lim==0){
		return new THREE.Vector3(avgX,avgY,avgZ);
	}
	while(pos<lim){
		avgX+=verts[pos].x;
		avgY+=verts[pos].y;
		avgZ+=verts[pos].z;
		pos++;
	}



	avgX/=lim;
	avgY/=lim;
	avgZ/=lim;
	pos=0;
	while(pos<lim){
		verts[pos].x-=avgX;
		verts[pos].y-=avgY;
		verts[pos].z-=avgZ;
		pos++;
	}

	return new THREE.Vector3(avgX,avgY,avgZ);

}




/**
*
* Accepts a list of binary stl buffer objects and returns a corresponding list
* of ThreeJS objects.
*
* @method getGeometries
* @for renderGlobal
* @param {Buffer Object List} theSTLs
* @return {ThreeJS Object List}
*
*/
function getGeometries(theSTLs){

	var result=[];
	var pos=0;
	var lim=theSTLs.length;
	while(pos<lim){
		result.push(parseStlBinary(theSTLs[pos]));
		pos++;
	}
	return result;

}




/**
*
* Combines a given array of objects (each associating an array of keyframes to name) with a
* given array of objects (each associating a threeJS mesh with a name), creating an array
* of objects with keyFrame arrays and threeJS meshes associated with the same name
*
* @method bindPartsToKeyFrames
* @for renderGlobal
* @param {Array} theKeyFrameLists An array of objects, each containing an array of keyframe objects
* called "Frames" and a string property called "Name")
* @param {Array} theParts An array of objects, each containing a threeJS mesh object called "Mesh" and
* a string property called "Name"
* @return {Array}
*
*/
function bindPartsToKeyFrames(theKeyFrameLists, theParts){

	//console.log(theKeyFrameLists);
	//console.log (theParts);

	var pos=0;
	var searchPos;
	var lim=theKeyFrameLists.length;
	var searchLim=theParts.length;
	var result=[];



	while(pos<lim){
		searchPos=0;
		while(searchPos<searchLim){
			//console.log(theKeyFrameLists[pos].Name+" -- "+theParts[searchPos].Name);
			if(theKeyFrameLists[pos].Name===theParts[searchPos].Name ||
			   theKeyFrameLists[pos].Name+".STL"===theParts[searchPos].Name ||
			   theKeyFrameLists[pos].Name===theParts[searchPos].Name+".STL"){
				   //console.log("===========");
				break;
			}
			searchPos++;
		}
		if(searchPos==searchLim){
			pos++;
			continue;
		}
		theParts[searchPos].Mesh.material.transparent = true;
		console.log(theParts[searchPos].Mesh);
		result.push({
			Name: theKeyFrameLists[pos].Name,
			Frames: theKeyFrameLists[pos].Frames,
			Mesh: theParts[searchPos].Mesh
		});
		/*console.log({
			Name: theKeyFrameLists[pos].Name,
			Frames: theKeyFrameLists[pos].Frames,
			Mesh: theParts[searchPos].Mesh
		});*/
		pos++;
	}

	flipTheTimes(result);

	return result;

}



/**
*
* Combines a jagged array of objects, each object at least possessing a numeric
* property called "Time", returns the value of the greatest "Time" property
*
* @method longestTimeFromFrames
* @for renderGlobal
* @param {Array} partFrames The jagged array
* @return {Int} The greatest "Time" value in the jagged array
*
*/
function longestTimeFromFrames(partFrames){

	var x=0;
	var y;
	var xLim=partFrames.length;
	var yLim;
	var best=0;
	while(x<xLim){
		y=0;
		yLim=partFrames[x].Frames.length;
		while(y<yLim){
			best=Math.max(best,partFrames[x].Frames[y].Time);
			y++;
		}
		x++;
	}
	//console.log(best);
	return best;

}


/**
*
* Given a jagged array of objects, each object at least possessing a numeric
* property called "Time", sets each Time property to the greatest Time value in
* the jagged array minus the origional value, thus effectively reversing the
* temporal order of each object
*
* @method flipTheTimes
* @for renderGlobal
* @param {Array} partFrames The jagged array
* @return {Int} The greatest "Time" value in the jagged array
*
*/
function flipTheTimes(partFrames){

	var x=0;
	var y;
	var xLim=partFrames.length;
	var yLim;
	var tFlip=longestTimeFromFrames(partFrames);
	while(x<xLim){
		y=0;
		yLim=partFrames[x].Frames.length;
		while(y<yLim){
			partFrames[x].Frames[y].Time=tFlip-partFrames[x].Frames[y].Time;
			y++;
		}
		partFrames[x].Frames.reverse();
		x++;
	}
	return;

}


/**
*
* Logs the contents of the given jagged array of keyFrame objects, each containing numeric properties "X",
* "Y", "Z", and "Time", to the console as a string.
*
* @method showFrames
* @for renderGlobal
* @param {Array} partFrames A jagged array of keyframe objects
* @return {Void}
*
*/
function showFrames(partFrames){

	var x=0;
	var y;
	var xLim=partFrames.length;
	var yLim;
	var tFlip=longestTimeFromFrames(partFrames);
	var timeList;
	var listList=[];
	while(x<xLim){
		timeList=[];
		y=0;
		yLim=partFrames[x].Frames.length;
		while(y<yLim){
			theFrame=partFrames[x].Frames[y];
			timeList.push("X: "+theFrame.Position.x+" Y: "+theFrame.Position.y+" Z: "+theFrame.Position.z+" Time: "+theFrame.Time);
			y++;
		}
		listList.push(timeList);
		x++;
	}

	return;

}


/**
*
* Logs the contents of a given keyFrame object, containing numeric properties "X",
* "Y", "Z", and "Time", to the console as a string.
*
* @method showFrame
* @for renderGlobal
* @param {Array} theFrame the keyFrame object to be logged
* @return {Void}
*
*/
function sayFrame(theFrame){

	console.log("X: "+theFrame.Position.x+" Y: "+theFrame.Position.y+" Z: "+theFrame.Position.z+" Time: "+theFrame.Time);

}


/**
*
* Returns true if any position component of the given keyframe object is NaN
*
* @method hasNaN
* @for renderGlobal
* @param {Object} theFrame A keyFrame object
* @return {Boolean}
*
*/
function hasNaN(theFrame){

	return (isNaN(theFrame.Position.x) || isNaN(theFrame.Position.y) || isNaN(theFrame.Position.z));

}


/**
*
* Returns a copy of the provided keyframe object
*
* @method copyFrame
* @for renderGlobal
* @param {Object} theFrame A keyFrame object
* @return {Object} The copy
*
*/
function copyFrame(theFrame){
	var result={
		Quat: new THREE.Quaternion(0,0,0,0),
		Position: null,
		Time: null,
		Alpha: 1.0
	}
	result.Quat.copy(theFrame.Quat);
	result.Position=theFrame.Position.clone();
	result.Time=theFrame.Time;
	result.Alpha = theFrame.Alpha;

	return result;
}


/**
*
* Returns a copy of the provided array of keyframe objects
*
* @method copyFrameList
* @for renderGlobal
* @param {Array} partFrames A keyFrame object array
* @return {Array} The copy
*
*/
function copyFrameList (theFrameList){

	var pos=0;
	var lim=theFrameList.length;
	var result=[];
	while(pos<lim){
		result.push(copyFrame(theFrameList[pos]));
		pos++;
	}

	return result;

}





/**
*
* Creates a keyframe list for the given fastener object and adds it to currentFrameList
*
* @method makeFastenerKeyFrames
* @for renderGlobal
* @param {Object} theFst The object representation of the fastener being keyframed
* @param {Object List} runningList The current running list of keyframes
* @param {Object List} currentFrameList The list of part-keyframe list objects
* @return {Void}
*
*/
function makeFastenerKeyFrames(theFst,runningList,currentFrameList){

	var newQuat= new THREE.Quaternion();
	newQuat.setFromEuler(new THREE.Euler(0,0,0,'XYZ'));

	var presentFrame={
						Quat: newQuat,
						Position: new THREE.Vector3(theFst.X,theFst.Y,theFst.Z),
						Time: theFst.Time
					};


	runningList.push(presentFrame);

	var copiedList= copyFrameList(runningList);
	//console.log("-----------");
	if(copiedList.length===1){
		copiedList.length = 2;
		copiedList[1] = copiedList[0];
		copiedList[0] = {
						Quat: newQuat,
						Position: new THREE.Vector3(0.0,0.0,0.0),
						Time: 0.00
					};
	}
	currentFrameList.push({Name: theFst.Name, Frames: copiedList});

	console.log({Name: theFst.Name, Frames: copiedList});
	runningList.pop();

	return;

}


/**
*
* Given a tree representation of the assembly process through nested javascript objects, returns an array
* of keyframe array objects, with each keyframe array object being a representation of the movement of each part
* in the tree representation throughout the assembly proceess, with a list of keyframe objects and a given part name
*
* @method makeKeyFrames
* @for renderGlobal
* @param {Object} theTree Tree representation of the assembly process through nested javascript objects
* @param {Array} runningList Internally used variable. Should be an empty array for outside use.
* @param {Array} currentFrameList Internally used variable. Should be an empty array for outside use.
* @return {Array} The jagged array of keyFrame objects
*
*/
function makeKeyFrames(theTree, runningList, currentFrameList){

	var isRoot=0;
	if(runningList.length==0){
		isRoot=1;
		/*console.log("ROOT");
		console.log(theTree);*/
	}

	//console.log(theTree);

	var newQuat= new THREE.Quaternion();
	newQuat.setFromEuler(new THREE.Euler(0,0,0,'XYZ'));

	var presentFrame = {
						Quat: newQuat,
						Position: new THREE.Vector3(theTree.X,theTree.Y,theTree.Z),
						Time: theTree.Time,
						Alpha: 1.0
					};


	runningList.push(presentFrame);

	if(theTree.Ref === null){
		var copiedList= copyFrameList(runningList);
		//console.log("-----------");
		currentFrameList.push({Name: theTree.Name, Frames: copiedList});
		runningList.pop();
	}
	else{
		makeKeyFrames(theTree.Ref,runningList,currentFrameList);
		makeKeyFrames(theTree.Mov,runningList,currentFrameList);
		runningList.pop();
	}

	var pos = 0;
	var lim = theTree.Fst.length;
	while(pos<lim){
		makeFastenerKeyFrames(theTree.Fst[pos],runningList,currentFrameList);
		pos++;
	}


	if(isRoot===1){
		//console.log(currentFrameList);
		return currentFrameList;
	}
	return;

}



/**
*
* Given two keyFrames and a normalized float "proportion", returns an interpolation
* between the two keyframes with a weight towards the second keyframe proportional
* to "proportion"
*
* @method interpolate
* @for renderGlobal
* @param {Object} keyFrame1 The earlier keyFrame
* @param {Object} keyFrame2 The later keyFrame
* @param {Float} proportion A normalized value representing what proportion of the path of
* interpolation is between the result and the earlier keyFrame
* @return {Object} The jagged array of keyFrame objects
*
*/
function interpolate(keyFrame1, keyFrame2, proportion){

	var result={ Quat: new THREE.Quaternion(), Position: new THREE.Vector3(0,0,0), Time: null, Alpha: 1.0 };

	THREE.Quaternion.slerp (keyFrame1.Quat, keyFrame2.Quat, result.Quat, proportion);
	//result.Position.lerp(keyFrame1.Position,keyFrame2.Position,proportion);
	result.Position.x=keyFrame1.Position.x*(1-proportion)+keyFrame2.Position.x*proportion;
	result.Position.y=keyFrame1.Position.y*(1-proportion)+keyFrame2.Position.y*proportion;
	result.Position.z=keyFrame1.Position.z*(1-proportion)+keyFrame2.Position.z*proportion;
	result.Time=keyFrame1.Time*(1-proportion)+keyFrame2.Time*proportion;
	result.Alpha=keyFrame1.Alpha*(1-proportion)+keyFrame2.Alpha*proportion;
	/*
	if(hasNaN(result)){
		console.log("vvvvvvvvvvvv");
		console.log("Prop: "+proportion);
		sayFrame(keyFrame1);
		sayFrame(keyFrame2);
		sayFrame(result);
		console.log("^^^^^^^^^^^^");
	}
	*/
	return result;

}



/**
*
* Given a list of keyframes and a time quantity, returns a keyframe object interpolating
* between the two temporally closest keyframes. In cases where the provide time is beyond the
* range of times represented by the list, returns the closest keyframe
*
* @method grabInterp
* @for renderGlobal
* @param {Array} frameList A list of keyframes. Must be organized from least time value to greatest time value
* @param {Float} time Floating-point representation of what time in the keyframe progression the interpolation
* should occur
* @return {Object} The interpolated keyframe
*
*/
function grabInterp(frameList, time){


	var pos=0;
	var lim=frameList.length;
	//var timeReport="";
	while((pos<lim) && (time>frameList[pos].Time)){
		//timeReport=timeReport+" -> "+frameList[pos].Time.toString();
		pos++;
	}


	/*if(pos<lim){
		timeReport=timeReport+" -> "+time+" <- "+frameList[pos].Time.toString();
	}
	else{
		timeReport=timeReport+" -> "+frameList[lim-1].Time.toString()+" -> "+time;
	}
	console.log(timeReport);*/

	if(pos==0){
		//console.log(time.toString()+"<"+frameList[0].Time.toString());
		var theResult= copyFrame(frameList[0]);

	}
	else if(pos==lim){
		//console.log(time.toString()+">"+frameList[lim-1].Time.toString());
		var theResult= copyFrame(frameList[lim-1]);
	}
	else{

		var prop=(time-frameList[pos-1].Time)/(frameList[pos].Time-frameList[pos-1].Time);
		var theResult=interpolate(frameList[pos-1],frameList[pos],prop);
	}

	//sayFrame(theResult);
	return theResult;

}



/**
*
* Given an array of objects (each containing a threeJS mesh object and an array of
* keyFrame objects), and two floating points "time" and "timeWarp", will animate each
* mesh in the array along the keyframes in their associate objects according to the
* given "time" and returns the new time as given by the standard time step multiplied
* by "timeWarp"
*
* @method animate
* @for renderGlobal
* @param {Array} partFrames List of objects relating threeJS mesh objects with their
* respective keyframe arrays
* @param {Float} time The time to be used when interpolating keyFrames for the models
* @param {Float} timeWarp The coefficeint to be applied to the timestep in the
* animation
* @return {Float} The new value of time in the animation
*
*/
function animate(partFrames, time, timeWarp){

	var pos=0;
	var lim=partFrames.length;
	var interp;
	var eul= new THREE.Euler();
	var delt=new THREE.Vector3();
	while(pos<lim){

		interp=grabInterp(partFrames[pos].Frames,time,partFrames[pos].Mesh.position);

		eul.setFromQuaternion(interp.Quat);
		partFrames[pos].Mesh.rotation.x=eul.x;
		partFrames[pos].Mesh.rotation.y=eul.y;
		partFrames[pos].Mesh.rotation.z=eul.z;
		partFrames[pos].Mesh.position.x=interp.Position.x;
		partFrames[pos].Mesh.position.y=interp.Position.y;
		partFrames[pos].Mesh.position.z=interp.Position.z;
		partFrames[pos].Mesh.material.opacity = interp.Alpha;

		pos++;
	}

	var timeStep=timeWarp/60;
	time+=timeStep;
	return time;

}




/**
*
* Given two threeJS boundingBox objects, returns the smallest bounding box
* encompassing the two
*
* @method combineBounds
* @for renderGlobal
* @param {Object} a The first bounding box
* @param {Object} b The second bounding box
* @return {Object} The combined bounds
*
*/
function combineBounds(a,b){

	var r={};
	r.min= new THREE.Vector3();
	r.max= new THREE.Vector3();
	r.min.x = Math.min(a.min.x,b.min.x);
	r.max.x= Math.max(a.max.x,b.max.x);
	r.min.y = Math.min(a.min.y,b.min.y);
	r.max.y= Math.max(a.max.y,b.max.y);
	r.min.z = Math.min(a.min.z,b.min.z);
	r.max.z= Math.max(a.max.z,b.max.z);
	return r;

}



/**
*
* Given two threeJS boundingBox objects, returns the smallest bounding box
* encompassing the two
*
* @method getGlobBounds
* @for renderGlobal
* @param {Object} a The first bounding box
* @param {Object} b The second bounding box
* @return {Object} The combined bounds
*
*/
function getGlobBounds(partFrames){


	partFrames[0].Mesh.geometry.computeBoundingBox();
	var runningBound=partFrames[0].Mesh.geometry.boundingBox;

	var pos=1;
	var lim=partFrames.length;
	while(pos<lim){
		partFrames[pos].Mesh.geometry.computeBoundingBox();
		runningBound=combineBounds(runningBound,partFrames[pos].Mesh.geometry.boundingBox);
		pos++;
	}

	return runningBound;

}





/**
*
* Given an object, containing a threeJS mesh object as "Mesh", will
* return the center of the mesh's bounding box
*
* @method getPartCenter
* @for renderGlobal
* @param {Object} a The object containing the threeJS mesh object
* @return {Object} The center of the mesh's bounding box, represented as
* a threeJS Vector3 object
*
*/
function getPartCenter(part){

	part.Mesh.geometry.computeBoundingBox();
	var bound=part.Mesh.geometry.boundingBox;
	var center= new THREE.Vector3((bound.min.x+bound.max.x)/2,(bound.min.y+bound.max.y)/2,(bound.min.z+bound.max.z)/2);
	center.x+=part.Mesh.position.x;
	center.y+=part.Mesh.position.y;
	center.z+=part.Mesh.position.z;

	return center;

}




/**
*
* Aligns the camera to look at the point at the average of the centers of all the parts bounding boxes
* @method alignAssemblyCenter
* @for renderGlobal
* @return {Void}
*
*/
function alignAssemblyCenter(){

	var pos = 0;
	var lim = partFrames.length;
	var result = new THREE.Vector3(0,0,0);
	while(pos<lim){
		result.add(getPartCenter(partFrames[pos]));
		pos++;
	}
	result.divideScalar(partFrames.length);
	result.sub(camera.position);
	result.normalize();
	camYaw = Math.atan2(result.x,result.z) - Math.PI;
	camPitch = Math.atan2(Math.pow(result.x*result.x+result.z*result.z,0.5),result.y);

}



/**
*
* Given a threeJS scene object, a threeJS camera object, and an array of objects containing
* threeJS mesh objects, finds the first mesh in the scene which is intersected the ray extending
* through the center of the camera's field of vision. If this mesh is in the provided array of
* objects, then that object is returned, otherwise null is returned instead
*
* @method getFirstIntersect
* @for renderGlobal
* @param {Object} theScene The threeJS scene object in which intersections should
* be tested
* @param {Object} theCamera The threeJS camera object to be used to test for
* ray intersections
* @param {Array} partFrames An array containing a series of objects, each of which
* contain a threeJS mesh object (under the property "Mesh") to be tested for intersections
* @return {Object} The intersecting mesh (or null in case of no valid intersection)
*
*/
function getFirstIntersect(theScene, theCamera, partFrames){

	var caster= new THREE.Raycaster();
	var mousePos= new THREE.Vector2(0,0);

	caster.setFromCamera(mousePos,theCamera);

	var intersectList=caster.intersectObjects(theScene.children);

	if(intersectList.length===0){
		return null;
	}
	else{

		var pos=0;
		var lim=partFrames.length;
		var part=intersectList[0].object;
		while(pos<lim){
			if(part===partFrames[pos].Mesh){
				return partFrames[pos];
			}
			pos++;
		}
		return null;
	}

}






/**
*
* Given an tree representation of the movement of parts in an assembly sequence, the
* parent node of that node, and a threeJS scene object, inserts a line for each subassembly
* path along the path of movement
*
* @method addLines
* @for renderGlobal
* @param {Object} movTree Tree of nested objects representing the movement of each subassembly
* in it's assembly sequence
* @param {Object} parentNode Used for internal use. Null should be applied for external use.
* @param {Object} theScene the threeJS scene to which the line representations will be added
* @return {Void}
*
*/
function addLines(movTree,parentNode,theScene,isMov){

	if(movTree==null){
		return;
	}
	else{
		if(parentNode!=null){
			var theGeo=new THREE.Geometry();
			var startP=new THREE.Vector3(movTree.X,movTree.Y,movTree.Z);
			var endP=new THREE.Vector3(parentNode.X,parentNode.Y,parentNode.Z);
			theGeo.vertices=[startP,endP];
			movTree.Line= new THREE.LineSegments(
				theGeo,
				getStdLine()
			);
			theScene.add(movTree.Line);
		}
		else{
			movTree.Line= null;
		}
		addLines(movTree.Ref,movTree,theScene,false);
		addLines(movTree.Mov,movTree,theScene,true);
		var pos = 0;
		var lim = movTree.Fst.length;
		while(pos<lim){
			if(isMov || parentNode != null){
				addLines(movTree.Fst[pos],parentNode,theScene,false);
			}
			else{
				addLines(movTree.Fst[pos],movTree,theScene,false);
			}
			pos++;
		}

		return;
	}

}




/**
*
* Given an tree representation of the movement of parts in an assembly sequence, an
* array of Objects each associating a list of keyframes with a threeJS mesh object and aLinkcolor
* string, and the index of the keyframe associated with the tree's root node, displaces the movement
* line points associated with that particular part of the assembly to match the displacement of the
* model
*
* @method addDisplacement
* @for renderGlobal
* @param {Object} movTree Tree of nested objects representing the movement of each subassembly
* in it's assembly sequence
* @param {Array} partFrames An array of Objects each associating a list of keyframes with a threeJS
* mesh object and a string
* @param {Object} it The index of the keyframe associated with the root node of movTree. Used internally.
* For external use, apply 0.
* @return {Void}
*
*/
function addDisplacement(movTree, partFrames, it){

	if(movTree==null){
		return;
	}
	else{
		var ref=addDisplacement(movTree.Ref,partFrames,it);
		if(ref!=null){
			var mov=addDisplacement(movTree.Mov, partFrames, ref);
			var pos = 0;
			var lim = movTree.Fst.length;
			while(pos<lim){
				mov = addDisplacement(movTree.Fst[pos], partFrames, mov);
				pos++;
			}
			it=mov;
		}

		if(mov==null || ref==null){
			movTree.Disp=getPartCenter(partFrames[it]);
			it++;
		}
		else{
			movTree.Disp=new THREE.Vector3(0,0,0);
			movTree.Disp.lerpVectors(movTree.Ref.Disp,movTree.Mov.Disp,0.5);
		}
		if(movTree.Line!=null){
			movTree.Line.geometry.vertices[0].addVectors(movTree.Line.geometry.vertices[0],movTree.Disp);
			movTree.Line.geometry.vertices[1].addVectors(movTree.Line.geometry.vertices[1],movTree.Disp);
		}

		return it;
	}
}


/**
*
* Given an tree representation of the movement of parts in an assembly sequence, the
* parent node of that node, and the current time in the animation, updates the ends of the
* movement lines such that the portion of lines which have already been traversed are not shown
*
* @method updateLines
* @for renderGlobal
* @param {Object} movTree Tree of nested objects representing the movement of each subassembly
* in it's assembly sequence
* @param {Object} parentNode Used for internal use. Null should be applied for external use.
* @param {Object} theTime the threeeJS scene to which the line representations will be added
* @return {Void}
*
*/
function updateLines(movTree,parentNode,theTime, isMov){

	if(movTree==null){
		return;
	}
	else{
		if(movTree.Line!=null && parentNode!=null){
			if(theTime<=parentNode.Time){
				if(theTime>=movTree.Time){
					var normTime=(parentNode.Time-theTime)/(parentNode.Time-movTree.Time);
					movTree.Line.geometry.vertices[0].x=(movTree.X+movTree.Disp.x)*(normTime)+(parentNode.X+movTree.Disp.x)*(1-normTime);
					movTree.Line.geometry.vertices[0].y=(movTree.Y+movTree.Disp.y)*(normTime)+(parentNode.Y+movTree.Disp.y)*(1-normTime);
					movTree.Line.geometry.vertices[0].z=(movTree.Z+movTree.Disp.z)*(normTime)+(parentNode.Z+movTree.Disp.z)*(1-normTime);
				}
				else{
					movTree.Line.geometry.vertices[0].x=movTree.X+movTree.Disp.x;
					movTree.Line.geometry.vertices[0].y=movTree.Y+movTree.Disp.y;
					movTree.Line.geometry.vertices[0].z=movTree.Z+movTree.Disp.z;
				}
			}
			else{
				movTree.Line.geometry.vertices[0]=movTree.Line.geometry.vertices[1].clone();
			}
			movTree.Line.geometry.verticesNeedUpdate=true;
		}

		updateLines(movTree.Ref,movTree,theTime,false);
		updateLines(movTree.Mov,movTree,theTime,true);

		var pos = 0;
		var lim = movTree.Fst.length;
		while(pos<lim){
			if( parentNode != null ){
				updateLines(movTree.Fst[pos],parentNode,theTime,false);
			}
			else{
				updateLines(movTree.Fst[pos],movTree,theTime,false);
			}
			pos++;
		}
		return;
	}

}




/**
*
* Initializes the axis lines for the bottom-left of the screen
*
* @method initAxisLines
* @for renderGlobal
* @return {Void}
*
*/
function initAxisLines(){

	theXAxis = new THREE.Line(  new THREE.Geometry(),  new THREE.LineBasicMaterial({color: 0xff0000, depthTest: false }));
	theXAxis.geometry.vertices.push(new THREE.Vector3(0,0,0));
	theXAxis.geometry.vertices.push(new THREE.Vector3(0,0,0));
	theXAxis.frustumCulled = false;

	theYAxis = new THREE.Line(  new THREE.Geometry(),  new THREE.LineBasicMaterial({color: 0x00ff00, depthTest: false }));
	theYAxis.geometry.vertices.push(new THREE.Vector3(0,0,0));
	theYAxis.geometry.vertices.push(new THREE.Vector3(0,0,0));
	theYAxis.frustumCulled = false;

	theZAxis = new THREE.Line(  new THREE.Geometry(),  new THREE.LineBasicMaterial({color: 0x0000ff, depthTest: false }));
	theZAxis.geometry.vertices.push(new THREE.Vector3(0,0,0));
	theZAxis.geometry.vertices.push(new THREE.Vector3(0,0,0));
	theZAxis.frustumCulled = false;

	xRet = new THREE.Line(  new THREE.Geometry(),  new THREE.LineBasicMaterial({color: 0x000000 }));
	xRet.geometry.vertices.push(new THREE.Vector3(0,0,0));
	xRet.geometry.vertices.push(new THREE.Vector3(0,0,0));
	xRet.frustumCulled = false;

	yRet = new THREE.Line(  new THREE.Geometry(),  new THREE.LineBasicMaterial({color: 0x000000 }));
	yRet.geometry.vertices.push(new THREE.Vector3(0,0,0));
	yRet.geometry.vertices.push(new THREE.Vector3(0,0,0));
	yRet.frustumCulled = false;

	scene.add(theXAxis);
	scene.add(theYAxis);
	scene.add(theZAxis);
	scene.add(xRet);
	scene.add(yRet);

}




/**
*
* Updates the axis line dispay
*
* @method updateAxisLines
* @for renderGlobal
* @return {Void}
*
*/
function updateAxisLines(){

	var theRot= new THREE.Quaternion(0,0,0,0);
	theRot.setFromEuler(camera.rotation);
	var theDir= new THREE.Vector3(-3,-3,-5);
	var retX0= new THREE.Vector3(-0.3,0,-5);
	var retX1= new THREE.Vector3(0.3,0,-5);
	var retY0= new THREE.Vector3(0,-0.08,-5);
	var retY1= new THREE.Vector3(0,0.08,-5);

	theDir.applyQuaternion(theRot);
	retX0.applyQuaternion(theRot);
	retX1.applyQuaternion(theRot);
	retY0.applyQuaternion(theRot);
	retY1.applyQuaternion(theRot);

	var thePosition = camera.position.clone();

	thePosition.add(theDir);

	theXAxis.geometry.vertices[0].copy(thePosition);
	theXAxis.geometry.vertices[0].x-=0.5;
	theXAxis.geometry.vertices[1].copy(thePosition);
	theXAxis.geometry.vertices[1].x+=1;
	theXAxis.geometry.verticesNeedUpdate=true;

	theYAxis.geometry.vertices[0].copy(thePosition);
	theYAxis.geometry.vertices[0].y-=0.5;
	theYAxis.geometry.vertices[1].copy(thePosition);
	theYAxis.geometry.vertices[1].y+=1;
	theYAxis.geometry.verticesNeedUpdate=true;

	theZAxis.geometry.vertices[0].copy(thePosition);
	theZAxis.geometry.vertices[0].z-=0.5;
	theZAxis.geometry.vertices[1].copy(thePosition);
	theZAxis.geometry.vertices[1].z+=1;
	theZAxis.geometry.verticesNeedUpdate=true;



	thePosition.copy(camera.position);
	thePosition.add(retX0);
	xRet.geometry.vertices[0].copy(thePosition);

	thePosition.copy(camera.position);
	thePosition.add(retX1);
	xRet.geometry.vertices[1].copy(thePosition);
	xRet.geometry.verticesNeedUpdate=true;

	thePosition.copy(camera.position);
	thePosition.add(retY0);
	yRet.geometry.vertices[0].copy(thePosition);

	thePosition.copy(camera.position);
	thePosition.add(retY1);
	yRet.geometry.vertices[1].copy(thePosition);
	yRet.geometry.verticesNeedUpdate=true;


}


/**
*
* Performs a bezier curve interpolation of the control points in pointlist given
* the time value T, and returns a ThreeJS Vector3 object with the interpolated coordinates.
*
* @method interp
* @for renderGlobal
* @param {Vector3 Array} pointList A list of control points for use in interpolation.
* @param {Float} T A normalized value for use as a time value in interpolation.
* @return {Object}
*
*/
function interp (pointList, T){

	var pos = 0;
	var lim = pointList.length;
	var listCopy = [];
	while(pos<lim){
		listCopy.push(pointList[pos].clone());
		pos++;
	}
	while(lim>1){
		pos = 0;
		while(pos<lim-1){
			listCopy[pos].lerpVectors(listCopy[pos],listCopy[pos+1],T);
			pos++;
		}
		delete listCopy[pos];
		lim--;
	}
	return listCopy[1];

}


/**
*
* Returns a string describing the x, y, and z coordinates of theVec.
*
* @method vecDesc
* @for renderGlobal
* @param {Vector3} theVec The ThreeJS Vector3 object to be described by the output string
* @return {String}
*
*/
function vecDesc(theVec){

	return "X: "+theVec.x+" Y: "+theVec.y+" Z: "+theVec.z;

}



/**
*
* Recursively adds Vector3 objects onto the Vector3 list, target, interpolating from startDisp
* and endDisp in an arc centered at the Vector3 object center. Each layer of recursion
* adds the midpoint of the arc from startDisp to endDisp until level = 0.
*
* @method addArcSubDiv
* @for renderGlobal
* @param {Vector3 Array} target The list where the calculated Vector3 objects should be added
* @param {Vector3} center The point which the generated arc should be centered on
* @param {Vector3} startDisp The starting point of the arc
* @param {Vector3} endDisp The ending point of the arc
* @param {Int} level The desired levels of recursion in the point interpolation
* @return {Void}
*
*/
function addArcSubDiv (target, center, startDisp, endDisp, level){

	var midVec = new THREE.Vector3(0,0,0);
	var midDisp = new THREE.Vector3(0,0,0);
	var startVec = new THREE.Vector3(0,0,0);
	var endVec = new THREE.Vector3(0,0,0);
	midDisp.add(startDisp);
	midDisp.add(endDisp);
	midDisp.normalize();
	midDisp.multiplyScalar((startDisp.length()+endDisp.length())/2);
	midVec.add(midDisp);
	midVec.add(center);
	startVec.add(startDisp);
	startVec.add(center);
	endVec.add(endDisp);
	endVec.add(center);


	if(level <= 0){
		target.push(midVec);
		target.push(endVec);
	}
	else{
		addArcSubDiv(target,center,startDisp,midDisp, level-1);
		addArcSubDiv(target,center,midDisp,endDisp, level-1);
	}

}






/**
*
* Returns a list of 2^(resolution+1) points which trace an arc beginning at startPoint,
* terminating at endpoint, and centered around center
*
* @method makeArcPointList
* @for renderGlobal
* @param {Vector3} startPoint The starting point of the arc
* @param {Vector3} center The point which the generated arc should be centered on
* @param {Vector3} endPoint The ending point of the arc
* @param {Int} resolution The desired levels of recursion in the point interpolation
* @return {Vector3 Array}
*
*/
function makeArcPointList(startPoint, center, endPoint, resolution){

	var pos = 0;
	var lim = 5;
	var result = [];
	var norm;
	var workVector = new THREE.Vector3 ( 0,0,0 );
	var crossVector = new THREE.Vector3 ( 0,0,0 );
	var startDisp = new THREE.Vector3 ( 0,0,0 );
	var endDisp = new THREE.Vector3 ( 0,0,0 );

	workVector.copy(endPoint);
	workVector.sub(center);

	startDisp.copy(startPoint);
	startDisp.sub(center);

	crossVector.clone(startDisp);

	endDisp.copy(endPoint);
	endDisp.sub(center);

	if(Math.abs(workVector.dot(crossVector)) > 0.98){
		while(Math.abs(workVector.dot(crossVector)) > 0.98){
			crossVector.set(Math.random()*100, Math.random()*100, Math.random()*100);
		}
		crossVector.cross(workVector);
		crossVector.normalize();
		crossVector.multiplyScalar((startDisp.length()+endDisp.length())/2);

		addArcSubDiv(result,center,endDisp,crossVector,resolution-1);
		addArcSubDiv(result,center,crossVector,startDisp,resolution-1);
	}
	else{
		delete crossVector;
		crossVector = null;
		addArcSubDiv(result,center,endDisp,startDisp,resolution);
	}

	return result;

}





/**
*
* Adds keyframes onto the keyframe lists provided so that the keyframed parts begin their
* animation at start location and move in an arc to their previously defined start position
*
* @method addCurveKeyFrames
* @for renderGlobal
* @param {Object List} theFrameLists A list of keyframe lists describing the movement of 3d models
* @param {Vector3} startLocation The desired new start location of the 3d models in the animation
* @return {Void}
*
*/
function addCurveKeyFrames(theFrameLists, startLocation){

	var pos = 0;
	var lim = theFrameLists.length;
	var center = new THREE.Vector3(0,0,0);
	center.add(startLocation);
	center.multiplyScalar(0.5);

	pos = 0;
	lim = theFrameLists.length;
	var framePos;
	var frameLim;
	var startFrame;
	var theFrame;
	var interpPoints;
	var offSet;
	var resolution = 4;

	while(pos<lim){
		interpPoints = [];
		startFrame = (theFrameLists[pos].Frames)[(theFrameLists[pos].Frames.length)-1];
		interpPoints = makeArcPointList( startLocation, center, startFrame.Position, resolution);
		framePos = 0;
		frameLim = interpPoints.length;
		while(framePos<frameLim){
			theFrame = copyFrame(startFrame);
			theFrame.Position.copy(interpPoints[framePos]);
			theFrame.Time = startFrame.Time + framePos + 8;
			theFrame.Alpha = 1.0 - Math.pow((framePos+1)/frameLim,4);
			(theFrameLists[pos].Frames).push(theFrame);
			framePos++;
		}
		framePos = 0;
		frameLim = theFrameLists[pos].Frames.length;
		while(framePos<frameLim){
			framePos++;
		}
		pos++;
	}
	return 8 + Math.pow(2,resolution+1);

}


/**
*
* Adds a simple square grid of width equal to theSize and a number of lines equal to theDivs at Y=-1000
*
* @method addGrid
* @for renderGlobal
* @param {Int} theSize The desired grid width
* @param {Int} theDivs The desired number of lines per side of the grid
* @return {Void}
*
*/
function addGrid(theSize, theDivs, theHeight, theColor){

	var xpos = 0;
	var zpos = 0;
	var theLine = null;
	var theGeo = new THREE.Geometry();
	while(xpos<theDivs){
		theGeo.vertices.push(new THREE.Vector3(xpos*theSize/theDivs-theSize/2, theHeight , 0-theSize/2));
		theGeo.vertices.push(new THREE.Vector3(xpos*theSize/theDivs-theSize/2, theHeight , theSize/2));
		xpos++;
	}
	while(zpos<theDivs){
		theGeo.vertices.push(new THREE.Vector3(0-theSize/2, theHeight, zpos*theSize/theDivs-theSize/2));
		theGeo.vertices.push(new THREE.Vector3(theSize/2, theHeight , zpos*theSize/theDivs-theSize/2));
		zpos++;
	}
	theLine =  new THREE.LineSegments(
		theGeo,
		getStdLine()
	);
	scene.add(theLine);

}



/**
*
* Adds a simple vertical column with a radius of theRad, a base Y value of theBot, a top terminating at
* theTop, an x and z position equal to theX and theZ, a line color of theColor, a number of vertical
* segmentations equal to stacks, and a number of radial segmentations equal to slices
*
* @method addGrid
* @for renderGlobal
* @param {Float} theRad The desired radius of the column
* @param {Float} theBot The desired bottom y value of the column
* @param {Float} theTop The desired top y value of the column
* @param {Float} theX The desired x value of the column
* @param {Float} theZ The desired z value of the column
* @param {Float} slices The desired number of radial segmentations
* @param {Float} stacks The desired z value of vertical segmentations
* @param {Float} theColor The desired color of the column
* @return {Void}
*
*/
function addCylender(theRad, theBot, theTop, theX, theZ, slices, stacks, theColor){

	var slicePos = 0;
	var stackPos;
	var theLine = null;
	var theGeo = new THREE.Geometry();
	while(slicePos<slices){
		stackPos = 0;
		while(stackPos<stacks+1){
			theGeo.vertices.push(new THREE.Vector3(
													theX+theRad*Math.cos(Math.PI*2*slicePos/slices),
			                                        theBot*stackPos/stacks+theTop*(stacks-stackPos)/stacks,
													theZ+theRad*Math.sin(Math.PI*2*slicePos/slices)
												  ));
			theGeo.vertices.push(new THREE.Vector3(
													theX+theRad*Math.cos(Math.PI*2*(slicePos+1)/slices),
			                                        theBot*stackPos/stacks+theTop*(stacks-stackPos)/stacks,
													theZ+theRad*Math.sin(Math.PI*2*(slicePos+1)/slices)
												  ));
			stackPos++;
		}
		theGeo.vertices.push(new THREE.Vector3(
												theX+theRad*Math.cos(Math.PI*2*slicePos/slices),
												theBot,
												theZ+theRad*Math.sin(Math.PI*2*slicePos/slices)
											  ));
		theGeo.vertices.push(new THREE.Vector3(
												theX+theRad*Math.cos(Math.PI*2*slicePos/slices),
												theTop,
												theZ+theRad*Math.sin(Math.PI*2*slicePos/slices)
											  ));
		slicePos++;
	}
	theLine =  new THREE.LineSegments(
		theGeo,
		getStdLine()
	);
	scene.add(theLine);

}





//</script>