

//============================================================================================\\
//==                                      by Dan Pupius                                     ==\\
//==                               (c)2001 All rights reserved                              ==\\
//==                            www.pupius.net + www.endoflow.com                           ==\\
//==                                                                                        ==\\
//== This program is a real mess                                                            ==\\
//==                                                                                        ==\\ 
//==  changes:                                                                              ==\\
//==  ========                                                                              ==\\
//==     - added starfield to background                                                    ==\\
//==     - added features for zoomin/out and to increase/decrease speed                     ==\\ 
//==     - fixed Mars' orbital period (typo)                                                ==\\ 
//==                                                                                        ==\\ 
//==                                                                                        ==\\ 
//============================================================================================\\


	//Makes a 3D coordinate
	//===================================================================
	function Point(x,y,z) {
		this.x = x;
		this.y = y;
		this.z = z;			//if omit z value, just make point 2D
		return this;
	}
	
	//Makes a 3D vector A(x,y,z) -> B(x,y,z)
	//===================================================================
	function Vector(a,b) {
		this.a = a;				//Point
		this.b = b;
		return this;
	}
	

	//The points are projected onto the plane defined by the viewport vector
	var viewport = new Vector(new Point(-600,-35,-300),new Point(400,300,-300));

	//The camera's position is used for calculating focal length and where
	//the points should be plotted on the viewport
	var camera = new Point(0,0,-700);

	
	//Set new camera position
	//===================================================================
	function setCamera(x,y,z) {
		camera.x = x;
		camera.y = y;
		camera.z = z;
	}


	//Plots a DIV at the correct position and resizes it
	//===================================================================
	function plotPoint(name, pos,dontsize) {
		if(!pos.size) pos.size = 1;
		
		//Create objects for DIV
		obj = document.getElementById(name);
		css = document.getElementById(name).style;
		
		f = viewport.a.z - camera.z;		//Focal length

		//From ratios of a triangle get position of point relative to camera
		cy = (pos.y-camera.y) * f / (pos.z-viewport.a.z);		
		cx = (pos.x-camera.x) * f / (pos.z-viewport.a.z);

		//Position of point relative to viewport
		py = parseInt(camera.y + cy - viewport.a.y);	
		px = parseInt(camera.x + cx - viewport.a.x);

		if(pos.z > viewport.a.z && px > 0 && py > viewport.a.y && py < viewport.b.y - viewport.a.y && px < viewport.b.x - viewport.a.x) { 
			css.visibility = "visible";
			d = -1*pos.size*(pos.z + viewport.a.z) / 1500;
			if(d<3) d=3;
			css.left = Math.round(px - d/2);
			css.top = Math.round(py - d/2);
			
			if(!dontsize) {
				css.width = d;
				css.height = d;
				img = document.getElementById("img" + name);
				img.height = d;
				img.width = d;
			}
			css.zIndex = (100 - pos.z) + 1000;
		} else {
			css.visibility = "hidden";		//on this side of the viewport so hide it
		}
	}

	//the planets, size and orbital radius and slant are statisticly equivalent	
	//	r = orbital radius
	//	a = start angle (to make it look less steralised)
	//	av = orbital velocity, if av=2 then planet does 2 rotations for every 1 that the earth does
	//	size = relative size of planet
	// slant = orbital inclination
	// parent = the parent planet
	var planets = [{ name: "Mercury", r: 57, a: 120, av: 4.15, size: 15, slant: 7, parent: 0 },
	               { name: "Venus", r: 105, a: 90, av: 1.63, size: 45, slant: 3, parent: 0 },
	               { name: "Earth", r: 150, a: 0, av: 1, size: 50, slant: 0, parent: 0 },
	               { name: "Mars", r: 225, a: 50, av: 0.53, size: 25, slant: 2, parent: 0 },
	               { name: "Moon", r: 10, a: 120, av: 13, size: 10, slant: 5, parent: 3 },			//moons orbital radius should be 0.45, everything else is accurate
	               { name: "Deimos", r: 8, a: 290, av: 30, size: 3, slant: 2, parent: 4 },			//Mars's moons are very small and rotate the planet
	               { name: "Phobos", r: 5, a: 30, av: 60, size: 1, slant: 1, parent: 4 }];			//every 1.3 days and 0.3 days respectivly, av should 
	               																								//be 280 and 1216
	
	var stars = new Array(20);
	for(i=0;i<stars.length;i++) {
		stars[i] = {a: Math.random()*360, a2: Math.random()*360, name: "stars" + i }
	}

	var viewangle = 0;
	var doFlyBy = false;
	var paused = false;
	var speed = 1;
	var zoom = 0;
	function doMove() {
		if(doFlyBy) viewangle +=5;
		if(viewangle > 360) viewangle  -= 360;	 
		for(k=0;k<planets.length;k++) {
			var pos = [0,0,0,1];		//x,y,z position of planet			
			
			pos[0] += planets[k].r;											//translate out to radius
			pos = matrixMultiply(pos, makeRotY(planets[k].a));			//rotate to correct point in orbit
			pos = matrixMultiply(pos, makeRotZ(planets[k].slant));		//create slanty orbit
		
			pos = matrixMultiply(pos, makeRotX(viewangle));				//rotate view to current angle
			planets[k].x = pos[0];
			planets[k].y = pos[1];
			planets[k].z = pos[2];
			
			if(planets[k].parent) {											//translate out to location of parent
				planets[k].x += planets[planets[k].parent].x;
				planets[k].y += planets[planets[k].parent].y;
				planets[k].z += planets[planets[k].parent].z;
			} else
				planets[k].z += zoom			
		
			plotPoint(planets[k].name, planets[k], false);
			if(!paused) planets[k].a += (planets[k].av*speed);
			if(planets[k].a > 360) planets[k].a -= 360;	 
		}
		
		//Plot starfield at a radius of 400 from centre		
		for(k=0;k<stars.length;k++) {
			pos = [400,0,0,1]; 
			pos = matrixMultiply(pos, makeRotY(stars[k].a));	
			pos = matrixMultiply(pos, makeRotZ(stars[k].a2));		
			pos = matrixMultiply(pos, makeRotX(viewangle));
			plotPoint(stars[k].name, new Point(pos[0],pos[1],pos[2]),true);
		}
		setTimeout(doMove,10);
	}


	var pi = 3.14159265358979;		//pi to 15sf
	function degToRad(x) { return ( x/(360/(2*pi)) ); }
	function radToDeg(x) { return ( x*(360/(2*pi)) ); }

	
	// MatrixMultpiy multpys a 1x4 matrix and a 4x4 matrix of the form:
	// A =   [ x,y,z,w ];
	// B = [ [ a,b,c,d ],
	//       [ e,f,g,h ],
	//       [ i,j,k,l ],
	//       [ m,n,o,p ] ]; 
	
	
	function matrixMultiply(A,B) {
		var retVal = [0,0,0];
		for(var i=0;i<4;i++) {
			retVal[i] = 0;
			for(var j=0;j<4;j++)
				retVal[i] += A[j] * B[i][j]
		}
		return retVal;
	}

	function makeRotX(a) {
		a = degToRad(a);
		return [[1,0,0,0],[0,Math.cos(a),-Math.sin(a),0],[0,Math.sin(a),Math.cos(a),0],[0,0,0,1]];
	}
	function makeRotY(a) {
		a = degToRad(a);
		return [[Math.cos(a),0,Math.sin(a),0],[0,1,0,0],[-Math.sin(a),0,Math.cos(a),0],[0,0,0,1]];
	}
	function makeRotZ(a) {
		a = degToRad(a);
		return [[Math.cos(a),-Math.sin(a),0,0],[Math.sin(a),Math.cos(a),0,0],[0,0,1,0],[0,0,0,1]];
	}
	
	
	var speedStop = false;
	function speedUp() {
		if(!speedStop) {
			setTimeout("speedUp()",1);
			speed+=0.05;
			if(speed>3) speed=3;
			setInfo();
		}	
	}
	function speedDown() {
		if(!speedStop) {
			setTimeout("speedDown()",1);	
			speed-=0.05;
			if(speed<0) speed=0;
			setInfo();
		}
	}


	var zoomStop = false;
	function zoomIn() {
		if(!zoomStop) {
			setTimeout("zoomIn()",1);
			zoom-=1;
			if(zoom<-1) zoom=-1;
			setInfo();
		}	
	}
	function zoomOut() {
		if(!zoomStop) {
			setTimeout("zoomOut()",1);	
			zoom+=1;
			if(zoom>1) zoom=1;
			setInfo();
		}
	}
	
	function setInfo() {
		document.getElementById("infoZoom").innerHTML = Math.round(zoom) + "";
		document.getElementById("infoSpeed").innerHTML = Math.round(speed*1) + "%";
	}



 
