## Introduction

This collection of JavaScripts represents my attempt at modeling basic physical phenomena (rotation of rigid bodies, 3D rotation, generation of n-sided polygons, collision, pendula, and spring action) using only JavaScript and HTML.

All are viewable at joey fortuna's site.

JavaScript served as a good modeling language for the algorithms, since IE handles all of the animation and rendering and I could concentrate on the math alone. I should note, for the record, that these scripts *are not cross-browser *-- mostly because they weren't really intended to be web presentations, just exercises.

Most of the source is self-explanatory and pretty heavily-commented by me for me as I was plodding through this.

## About the 3D "Engine"

The 3D code is probably the most basic way of generating a 3D image. A routine `NGON_init`

is fed a series of three values, which represent x, y and z coordinates. Each triplet is made into a point object (if that point doesn't already exist). Since the monitor space is set up as the fourth quadrant of a Cartesian axis, the Cartesian values are also stored in the point object for reference later.

At every third point, a face object is created. I'm using VML to draw the faces. One pitfall with VML is forgetting this patch of code at the top of the page:

<HTML xmlns:v="urn:schemas-microsoft-com:vml">
<STYLE>
v\:* { behavior: url(#default#vml); }
</STYLE>

The actual triangle for each face is created when the face object is created (function `nGonFaceObj`

) like this:

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;

I believe the graphics for this system could be easily transposed to DirectX or OpenGL.

The essential routines for 3D are `NGON_rotate()`

and `faceNormal()`

.

In `NGON_rotate`

, first check to see along which axis the user is rotating the figure. This is passed as a 1 or 0 to the function itself:

function NGON_rotate(xRotate,yRotate,zRotate) {
var xRotateAngle=(-yRotate)*(iRotationAngle*Math.PI)/180;
var yRotateAngle=(-xRotate)*(iRotationAngle*Math.PI)/180;
var zRotateAngle=(-zRotate)*(iRotationAngle*Math.PI)/180;

In the above block, `iRotationAngle`

is a global constant that I've set to 10. By multiplying it by PI and dividing by 180, you turn the angle of 10 degrees into radians, which the trig functions of the JavaScript `Math`

class like to work with.

The most important step in rotating in 3D is the following:

var z=pointObj.z;
var newy=y;
var newx=x;
var newz=z;
newy=(y*Math.cos(xRotateAngle))-(z*Math.sin(xRotateAngle));
newz=(z*Math.cos(xRotateAngle))+(y*Math.sin(xRotateAngle));
y=newy;
z=newz;
newz=(z*Math.cos(yRotateAngle))-(x*Math.sin(yRotateAngle));
newx=(x*Math.cos(yRotateAngle))+(z*Math.sin(yRotateAngle));
z=newz;
x=newx;
newx=(x*Math.cos(zRotateAngle))-(y*Math.sin(zRotateAngle));
newy=(y*Math.cos(zRotateAngle))+(x*Math.sin(zRotateAngle));
x=newx;
y=newy;

This step performs vector math on the three axes of each point in the object. The implications of this are most visible in the 3D Engine - Points script which **only** displays the points.

The next step in rotating and displaying the 3D object is projecting it from a parallel projection (press "p" when viewing the objects to see the difference) with no perspective distortion to a perspective projection.

pointObj.scrX = parseInt(((iFov*newx) / (iDistance-(newz+this.centerZ))) +
(this.centerX));
pointObj.scrY = parseInt((this.centerY) - ((iFov*newy) /
(iDistance-(newz+this.centerZ))));
pointObj.cartX=pointObj.scrX-this.centerX;
pointObj.cartY=pointObj.scrY-this.centerX;
pointObj.cartY=-newy;

There's a little elfin magic in this as the `iFov`

(Field-of-view) values and `iDistance`

values may need to be tweaked to suit your purposes. You can achieve any effect from fish-eye lens all the way down to straight parallel projection by playing with these values.

The last step is backface culling which is done by calculating the face normals and determining whether or not they're aimed into the receding distance. This is where the Cartesian values for each point really come in handy, since performing the fourth quadrant translation can get kind of tedious here.

var sx1=point1.cartX-point2.cartX;
var sy1=point1.cartY-point2.cartY;
var sz1=point1.z-point2.z;
var sx2=point3.cartX-point4.cartX;
var sy2=point3.cartY-point4.cartY;
var sz2=point3.z-point4.z;
var dpx = sy1 * sz2 - sy2 * sz1;
var dpy = sx1 * sz2 - sx2 * sz1;
var dpz = sx1 * sy2 - sx2 * sy1;
var dprod = 0 * dpx + 0 * dpy + iFov*(dpz/iDistance);

This, again, is a vector operation -- specifically, the cross product of any two of the face vectors (sides, here). You must calculate the edge vectors originating from the same point, otherwise your normal will be inverted and your backface will show up. This had me flummoxed for a day.

Collectively, these scripts are a favorite of Kevin Fortuna.

## The other scripts

Each page stands on its own with the exception of the "Newtonia" section, which is an object-oriented worldspace that allows for on-the-fly creation of pendula, springs, or static objects to be dropped into the same 2D world with each other.

Newtonia's collision detection relies on the idea of bounding boxes, and that the center of colliding objects intersect at a line that is of a certain angle from the horizon. This uses polar coordinates to determine where, along the edge of each object, the point of collision lies. Since these are bounding **boxes**, I have to kind of fudge it by checking where the colliding object is with respect to the...uh...other colliding object.

Here's the essential code:

function checkCollide(obj1,obj2) {
var obj1CX=obj1.x+(obj1.width/2);
var obj1CY=obj1.y+(obj1.height/2);
var obj2CX=obj2.x+(obj2.width/2);
var obj2CY=obj2.y+(obj2.height/2);
var dx=obj2CX-obj1CX;
var dy=obj1CY-obj2CY;
var theta=obj1.angleToCorner;
var alpha=Math.atan(dy/dx);
var r=Math.sqrt(dx*dx+dy*dy);
var a,b,e,f,g,h;
var c=0;
var d=0;
a=0;
b=0;
g=dx;
h=dy;
if ((alpha>theta || alpha<-theta) && dy>=0) d=obj1.height/2;
else if ((alpha>theta ||alpha<-theta) && dy<0) d=-(obj1.height/2);
else if ((alpha<theta && alpha>-theta) && dx>=0) c=(obj1.width/2);
else if ((alpha<theta && alpha>-theta) && dx<0) c=-(obj1.width/2);
if (d==0 && c!=0) d=b+((c-a)*((h-b)/(g-a)));
else if (c==0 && d!=0) c=a+((d-b)*((g-a)/(h-b)));
var dobj1x=c-a;
var dobj1y=d-b;
obj1.r=Math.sqrt((dobj1x*dobj1x)+(dobj1y*dobj1y));
}
function collide(obj1,obj2) {
var vec=new Vector(0,0);
var obj1CX=obj1.x+(obj1.width/2);
var obj1CY=obj1.y+(obj1.height/2);
var obj2CX=obj2.x+(obj2.width/2);
var obj2CY=obj2.y+(obj2.height/2);
var dx=obj2CX-obj1CX;
var dy=obj1CY-obj2CY;
var r=Math.sqrt(dx*dx+dy*dy);
checkCollide(obj1,obj2);
checkCollide(obj2,obj1);
if ((obj1.r+obj2.r)>=r) {
if (dx<0) vec.x=1;
else if (dx>0) vec.x=-1;
if (dy<0) vec.y=-1;
else if (dy>0) vec.y=1;
return vec;
}
else return vec;
}

It's easier with two bounding **circles**. For example, here's a script that will follow your mouse around the circumference of a circle. Think of the coordinates of the mouse cursor as the center of another object, and you'll get the sense of how this could be used for collisions.

<script>
var r=150;
var cx=400;
var cy=400;
function MoveHandler(e) {
var mouseX=window.event.x;
var mouseY=window.event.y;
setPoints(mouseX,mouseY);
}
function signOf(val) {
return Math.abs(val)/val;
}
function setPoints(mx,my) {
var dx=mx-cx;
var dy=my-cy;
if (dx==0) dx=1;
var theta=Math.atan(dy/dx);
var newX=signOf(dx)*r*Math.cos(theta)+cx;
var newY=signOf(dx)*r*Math.sin(theta)+cy;
eye.style.left=newX;
eye.style.top=newY;
}
document.onmousemove = MoveHandler;
</script>

The other scripts in this collection are borrowed from the math in Newtonia or the 3D engine. Kevin Fortuna particularly liked the Spinning Menu. Here's a quick description of the other scripts:

- Spinning Menu - this displays a "Lazy-Susan" style menu which spins from the background to the foreground, fading as it recedes.
- N-Gon - this allows the user to create an n-sided polygon in two or three dimensions. Rotation and zooming is also enabled. The objects are drawn in wire-frame. The code uses DHTML and VML.
- N-Gon Filled - this script has the same feature set as N-Gon, with the difference that the polygons are filled. Also uses DHTML and VML.
- Ole' Footeye - this demonstrates spring properties in the figure of a disembodied foot sporting a single bouncing eye.
- Nice Shootin' - this script demonstrates principles of gravity and trajectory using DHTML.