Click here to Skip to main content
15,886,724 members
Articles / Web Development / HTML

Physical Phenomena

Rate me:
Please Sign up or sign in to vote.
3.55/5 (12 votes)
22 Feb 20054 min read 53.3K   528   16  
JavaScript simulation of common physical phenomena -- Newtonian basics and 3D Engine. Favourite of Kevin Fortuna.
<HTML xmlns:v="urn:schemas-microsoft-com:vml">
<STYLE>
v\:* { behavior: url(#default#vml); }
</STYLE>

<head>

<style>
	button { font-face:arial;font-size:10px;border:1px solid black;}
	input { font-face:arial;font-size:10px;border:1px solid black;}
</style>

<script>
	/********************************************/
	/*********************************************
		JavaScript 3D - points
		BY JOEY FORTUNA
		http://neoncelery.com
		COPYRIGHT (C) 2004, Joey Fortuna
		PERMISSION TO USE GRANTED PROVIDING THE
		ADOPTER INCLUDES THIS NOTICE
	*********************************************/
	/********************************************/
var nGon;
var moveX=0;
var oldMoveX=0;
var moveY=0;
var oldMoveY=0;
var xRotate=0;
var yRotate=0;
var bRightMouseDown=false;
var bLeftMouseDown=false;
var iDistance=525;
var iFov=590;
var iLineYDiff=60;

function drawPolygon() {
		nGon=new nGonObj();
		getWinSize();

		/*******************
			X-WING FIGHTER
			point coords
			..probably better to define faces in triplets
		********************/
		var strPoints="-25,-75,25 25,-75,25 25,-75,-25 -25,-75,-25"; // roof of cab
		strPoints+=" -50,-50,50 50,-50,50 50,-50,-50 -50,-50,-50"; // outer roof of cab
		strPoints+=" -50,50,-50 50,50,-50 50,50,50 -50,50,50" // outer base of cab
		strPoints+=" -25,75,25 25,75,25 25,75,-25 -25,75,-25"; //base of cab
		strPoints+=" 25,25,75 -25,25,75 -25,-25,75 25,-25,75"; //nose
		strPoints+=" 25,25,-75 -25,25,-75 -25,-25,-75 25,-25,-75"; //aft
		strPoints+=" -75,25,25 -75,25,-25 -75,-25,25 -75,-25,-25"; // left inner strut
		strPoints+=" 75,25,25 75,25,-25 75,-25,25 75,-25,-25"; // right inner strut
		strPoints+=" -125,15,25 -125,15,-25 -125,-15,25 -125,-15,-25"; // left outer strut
		strPoints+=" 125,15,25 125,15,-25 125,-15,25 125,-15,-25"; // right outer strut
		strPoints+=" -135,25,45 -135,25,-45 -135,-25,45 -135,-25,-45"; // left wing hub
		strPoints+=" 135,25,45 135,25,-45 135,-25,45 135,-25,-45"; // right wing hub
		strPoints+=" -155,105,50 -155,105,-50 -155,-105,50 -155,-105,-50"; // left inner wing
		strPoints+=" 155,105,50 155,105,-50 155,-105,50 155,-105,-50"; // right inner wing
		strPoints+=" -175,80,60 -175,80,-60 -175,-80,60 -175,-80,-60"; // left outer wing
		strPoints+=" 175,80,70 175,80,-70 175,-80,70 175,-80,-70"; // right outer wing
		nGon.init(strPoints);
		nGon.rotate(0,0,0);
}
function getWinSize()
	{

			winX = document.body.clientWidth - 10;
			winY = document.body.clientHeight - 10;
			nGon.centerX=winX/2;
			nGon.centerY=winY/2;
	}



function nGonPointObj(tid,x,y,z) {
	this.id=tid;
	this.x=x;
	this.y=y;
	this.z=z;
	this.scrX=x;
	this.scrY=y;
	var newObj=newObj=document.createElement("div");
	newObj.id="nGonPoint"+tid;
	newObj.innerHTML="";
	newObj.style.position="absolute";
	newObj.style.display="inline";
	newObj.style.color="red";
	newObj.style.textAlign="center";
	newObj.style.fontWeight="bold";
	newObj.style.fontSize="16px";
	newObj.style.fontFamily="arial";
	newObj.style.width="20px";
	newObj.style.heigth="20px";
	newObj.style.cursor="hand";
	newObj.style.top=this.y+"px";
	newObj.style.left=this.x+"px";
	var divObj=document.body.appendChild(newObj);
	this.divObj=divObj;
	this.redraw=NGON_POINT_redraw;
	return this;
}

function nGonFaceObj(p1,p2,p3) {
	var objShape=document.body.appendChild(document.createElement("v:shape"));
	objShape.style.position="absolute";
	objShape.style.display="inline";
	objShape.style.zindex="-1";
	objShape.fillcolor="silver";
	objShape.coordorigin="0,0";
	this.shape=objShape;

	this.p1=p1;
	this.p2=p2;
	this.p3=p3;
	return this;
}
function nGonObj() {
	this.centerX=0;
	this.centerY=0;
	this.centerZ=0;
	this.parallel=false;
	this.name="NGonObject";
	this.nPoints=new Array();
	this.nFaces=new Array();
	this.angleDivisor=0;
	this.angle=0;
	this.rotate=NGON_rotate;
	this.clear=NGON_clear;
	this.addPoint=NGON_addPoint;
	this.addFace=NGON_addFace;
	this.init=NGON_init;
	this.drawFaces=NGON_drawFaces;
	return this;
}

function NGON_POINT_redraw() {
	if (this.z<-50) {
		this.divObj.style.backgroundColor="#990000";
		this.divObj.style.zIndex="10";
		this.divObj.style.fontSize="10px";
		}
	else if (this.z<-25)  {
		this.divObj.style.backgroundColor="#999900";
		this.divObj.style.zIndex="20";
		this.divObj.style.fontSize="12px";
		}
	else if (this.z<-0)  {
		this.divObj.style.backgroundColor="#339900";
		this.divObj.style.zIndex="30";
		this.divObj.style.fontSize="14px";
		}
	else if (this.z<-25)  {
		this.divObj.style.backgroundColor="#009999";
		this.divObj.style.zIndex="40";
		this.divObj.style.fontSize="16px";
		}
	else if (this.z<50)  {
		this.divObj.style.backgroundColor="#003399";
		this.divObj.style.zIndex="50";
		this.divObj.style.fontSize="18px";
		}
	else  {
		this.divObj.style.backgroundColor="#000099";
		this.divObj.style.zIndex="60";
		this.divObj.style.fontSize="20px";
		}
	this.divObj.style.left=this.scrX+"px";
	this.divObj.style.top=this.scrY+"px";
}


function NGON_init(strPoints) {
	// passed an array of points: "200,200,0 300,200,0 250,200,0"
	// create n pointObjs
	// create n faceObjs
	var pointArray=strPoints.split(" ");
	for (i=0;i<pointArray.length;i++) {
		var coorArray=pointArray[i].split(",");
		var objPoint= new nGonPointObj(i,this.centerX+parseInt(coorArray[0]),this.centerY+parseInt(coorArray[1]),parseInt(coorArray[2]));
		this.addPoint(objPoint);
/*
		if (i==2) {
			var objFace = new nGonFaceObj(this.nPoints[i],this.nPoints[i-1],this.nPoints[i-2]);
			this.addFace(objFace);
		}
*/
	}
}
function NGON_clear() {
	for (i=this.nPoints.length-1;i>=0;i--) {
		this.nPoints[i].divObj.style.display="none";
		this.nPoints[i].divObj=null;
		this.nPoints.pop(i);
	}
	while (this.nPoints.length>0) this.nPoints.pop(0);
	for (i=this.nFaces.length-1;i>=0;i--) {
		this.nFaces[i].shape.style.display="none";
		this.nFaces[i].shape=null;
		this.nFaces[i]=null;
		this.nFaces.pop(i);
	}
	while (this.nFaces.length>0) this.nFaces.pop(0);
}



function NGON_addFace(obj) {
	this.nFaces[this.nFaces.length]=obj;
}

function NGON_addPoint(obj) {
	this.nPoints[this.nPoints.length]=obj;
}



function showFace(objFace,strPath) {
	objFace.shape.style.display="inline";
	objFace.shape.style.width=winX;
	objFace.shape.style.height=winY;
	objFace.shape.coordsize=winX+","+winY;
	objFace.shape.path.v=strPath;
}

function NGON_drawFaces () {
	var strPath="";
	var dprod=0;
	for (i=0;i<this.nFaces.length;i++) {

		strPath="m";
		strPath+=parseInt(this.nFaces[i].p1.scrX)+","+parseInt(this.nFaces[i].p1.scrY-iLineYDiff);
		strPath+=" l ";
		strPath+=parseInt(this.nFaces[i].p2.scrX)+","+parseInt(this.nFaces[i].p2.scrY-iLineYDiff);
		strPath+=" " + parseInt(this.nFaces[i].p3.scrX)+","+parseInt(this.nFaces[i].p3.scrY-iLineYDiff);
		strPath+="xe";

		dprod=faceNormal(this.nFaces[i].p1,this.nFaces[i].p2,this.nFaces[i].p1,this.nFaces[i].p3);

		if (dprod>0) {
			showFace(this.nFaces[i],strPath);
		} else this.nFaces[i].shape.style.display="none";

	}
}

function faceNormal(point1,point2,point3,point4) {
		var sx1=point1.scrX-point2.scrX;
		var sy1=point1.scrY-point2.scrY;
		var sz1=point1.z-point2.z;
		var sx2=point3.scrX-point4.scrX;
		var sy2=point3.scrY-point4.scrY;
		var sz2=point3.z-point4.z;
		var dpx = sy1 * sz2 - sy2 * sz1;
		var dpy = sx2 * sz1 - sx1 * sz2;
		var dpz= sx1 * sy2 - sx2 * sy1;
	    var dprod = 0 * dpx + 0 * dpy + iFov*(dpz/iDistance);
	    return dprod;
}

function NGON_rotate(xRotate,yRotate,iZoom) {


	var xRotateAngle=(-yRotate)*(2*Math.PI)/180;
	var yRotateAngle=(-xRotate)*(2*Math.PI)/180;
	var zRotateAngle=0;//xRotate*(2*Math.PI)/180;


	for (i=0;i<this.nPoints.length;i++) {
		var pointObj=this.nPoints[i];
		var x=pointObj.x-this.centerX;
		var y=this.centerY-pointObj.y;

		var z=pointObj.z;
		var newy=y;
		var newx=x;
		var newz=z;

		newy=(y*Math.cos(xRotateAngle))-(z*Math.sin(xRotateAngle)); // X axis
		newz=(z*Math.cos(xRotateAngle))+(y*Math.sin(xRotateAngle));
		y=newy;
		z=newz;

		newz=(z*Math.cos(yRotateAngle))-(x*Math.sin(yRotateAngle)); // Y axis
		newx=(x*Math.cos(yRotateAngle))+(z*Math.sin(yRotateAngle));
		z=newz;
		x=newx;

		newx=(x*Math.cos(zRotateAngle))-(y*Math.sin(zRotateAngle)); // Z axis
		newy=(y*Math.cos(zRotateAngle))+(x*Math.sin(zRotateAngle));
		x=newx;
		y=newy;



		// PARALLEL PROJECTION //
		pointObj.x=newx+this.centerX;
		pointObj.y=this.centerY-newy;


		// PERSPECTIVE PROJECTION //

		// ** DO THIS WITH X/Y VALUES
		// ** DERIVED FROM PARALLEL ROTATION
		// ** BUT DON''T UPDATE POINTOBJ.X/Y
		// ** WITH PROJECTED RESULTS!

		iDistance+=iZoom;
		if (!this.parallel) {
			pointObj.scrX = ((iFov*newx) / (iDistance-(newz+this.centerZ))) + (this.centerX);
	  		pointObj.scrY = (this.centerY) - ((iFov*newy) / (iDistance-(newz+this.centerZ)));
		}
		else {
			pointObj.scrX=pointObj.x;
			pointObj.scrY=pointObj.y;
		}

		pointObj.z=newz;
		pointObj.redraw();
	}
		this.drawFaces();

}



	function MoveHandler(e) {

		moveX = window.event.x + document.body.scrollLeft;
		moveY = window.event.y;
		if (moveX<oldMoveX) xRotate=1;
		else if(moveX>oldMoveX) xRotate=-1;
		else xRotate=0;

		if (moveY<oldMoveY) yRotate=1;
		else if(moveY>oldMoveY) yRotate=-1;
		else yRotate=0;

		oldMoveX=moveX;
		oldMoveY=moveY;

		if (bRightMouseDown && moveX%2==0 && (xRotate!=0 || yRotate!=0)) {
			nGon.rotate(xRotate,yRotate,0);
		}
		else if (nGon)  {
			for (i=1;i<nGon.nPoints.length;i++) {
				var objPoint=nGon.nPoints[i];
				if (moveX>objPoint.scrX-10 &&
					moveX<objPoint.scrX+10 &&
					moveY>objPoint.scrY-10 &&
					moveY<objPoint.scrY+10 &&
					bLeftMouseDown) {
						objPoint.x=moveX;
						objPoint.y=moveY;
						objPoint.scrX=moveX;
						objPoint.scrY=moveY;
						objPoint.redraw();
					}

			}

		}
	}



	function KeyHandler(e) {

		switch(window.event.keyCode) {
		case 51:
			nGon.clear();
			nGon.init();
		break;
		case 16: // strafe left;
			nGon.centerX-=10;
			nGon.rotate(0,0,0);
		break;
		case 67: // strafe right;
			nGon.centerX+=10;
			nGon.rotate(0,0,0);
		break;
		case 80: // toggle parallel
			nGon.parallel=!nGon.parallel;
			projection.innerHTML=(nGon.parallel)?"parallel projection":"perspective projection";
			nGon.rotate(0,0,0);
		break;
		case 37:
			nGon.rotate(1,0,0);
		break;


		case 38:
			nGon.rotate(0,1,0);
		break;

		case 39:
			nGon.rotate(-1,0,0);
		break;

		case 40:
			nGon.rotate(0,-1,0);
		break;

		case 83:
			nGon.rotate(0,0,-1);
		break;

		case 90:
			nGon.rotate(0,0,1);
		break;
		default:
		break;
		}
	}
	function PressHandler(e) {
		mousegrabber.setCapture(true);
		if (window.event.button==1) bLeftMouseDown=true;
		else if (window.event.button==2) {bRightMouseDown=true;}
		return false;
	}
	function UpHandler(e) {
		mousegrabber.releaseCapture();
		bLeftMouseDown=false;
		bRightMouseDown=false;
		return false;
	}
	window.onresize = getWinSize;
	document.onmouseup=UpHandler;
	document.onkeydown = KeyHandler;
	document.onmousemove = MoveHandler;
	document.onmousedown = PressHandler;
	document.oncontextmenu=new Function("return false;");
</script>



<LINK rel="stylesheet" type="text/css" href="style.css">


</head>


<body onload="drawPolygon();">
<div onmouseup="this.releaseCapture();" id="mousegrabber" style="border:none;font-family:arial;">
<table width=100% cellpadding=0 cellspacing=0>
<tr>
<td>3D Engine - Points</td>
<td align=right><font style="font-size:12px;">
<script src="nav.js"></script>

</td>
</tr>
</table>


<table><tr>

<td><font style="font-size:10px;"><b>arrow keys</b> </td><td><font style="font-size:10px;">- step rotate </td></tr><tr>
<td><font style="font-size:10px;"><b>right-click & drag</b> </td><td><font style="font-size:10px;">- free rotate</td></tr>
<td><font style="font-size:10px;"><b>s</b> </td><td><font style="font-size:10px;">- zoom in</td></tr>
<td><font style="font-size:10px;"><b>z</b> </td><td><font style="font-size:10px;">- zoom out</td></tr>
<td><font style="font-size:10px;"><b>p</b> </td><td><font style="font-size:10px;">- toggle parallel / perspective projection</td></tr>
<td colspan=2><font style="font-size:10px;">*note: window resize shifts center and may cause funky orbit</td></tr>
</tr>
</td>
</table><br>
<font style="font-size:10px;">

</font>

<div id="projection" style="font-size:10px;font-weight:bold;color:#990000;">perspective projection</div>

</div>

<div id="centerPoint" style="position:absolute;top=100px;left=100px;display:none;">@</div>


</body>

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States

Comments and Discussions