On this page we demonstrate the code for the transparent cube applet. If you'd rather see the well commented code in action, download the fla file:

- Flash CS3 file simple3d_1.fla

(This is the same fla file as on the first page of this tutorial.) The comments in the code below, enclosed between /*...*/, explain each step and relate the code to the explanations on the previous page.

/*

vertsArray will store vertices of our cube.

*/

var vertsArray:Array=[];

var facesArray:Array=[];

/*

fLen variable stores the distance from the camera to the origin of the
xyz-coordinate system. The origin coincides with the center of the cube.
Large values for fLen produce less distortion for perspective, smaller
values more perspective. Anything below 200 is too close to the surface
and will give undesirable effects.

*/

var fLen:Number=2000;

/*

The next two variables store the number of vertices and the number
of polygonal faces for our surface. Since we are drawing a cube,
the values are 8 and 6.

*/

var numVertices:uint=8;

var numFaces:uint=6;

/*

We define a new Sprite, spBoard, in which all dynamic drawings will reside.
We are adding spBoard to the Display List and positioning it within the main movie.

*/

var spBoard:Sprite=new Sprite();

this.addChild(spBoard);

spBoard.x=200;

spBoard.y=190;

/*

We will draw dynamically a background for spBoard.
The background will be drawn in its child, shBack.

*/

var shBack:Shape=new Shape();

spBoard.addChild(shBack);

/*

We are calling a function that will draw a background
in spBoard.

*/

drawBack();

/*

Our cube will be drawn in a child of spBoard called shCube.

*/

var shCube:Shape=new Shape();

spBoard.addChild(shCube);

/*

doRotate remembers if the user is rotating the cube with the mouse or not.
prevX, prevY store the latest mouse coordinates. curPhi, curTheta store
the current view angles, faceColors the colors of the cube's sides.

*/

var doRotate:Boolean=false;

var prevX:Number;

var prevY:Number;

var curTheta:Number=20;

var curPhi:Number=70;

var facesColors:Array=[0xFFFFCC,0x00FF66,0x0066FF,0x33FFFF,0x9A7DDF,0xFFCCFF];

//The function that draws the back of spBoard.

function drawBack():void {

shBack.graphics.beginFill(0x000000);

shBack.graphics.drawRect(-160,-160,320,320);

shBack.graphics.endFill();

}

/*

We are calling the functions that define the vertices and the faces
of the cube in 3D space, and then the function that renders an
initial view. The three functions are defined and described below.

*/

setVertices();

setFaces();

renderView(curTheta,curPhi);

/*

The function setVertices populates the array of vertices, vertsArray,
with three-elements arrays. Each three-element array represents x, y, and z
coordinates of a vertex in 3D space. The units are pixels. The origin
is in the center of spBoard. The x-axis is perpendicular to the computer
screen pointing away from the screen, the y-axis lies on the horizontal
plane and points to the right, the z-axis is vertical and points upward.
Thus, we are in the standard xyz-coordinate system and the observer is
located on the positive x-axis fLen units from the origin.
We will translate the coordinates to Flash's coordinate system after moving
the observer in 3D and projecting the cube onto the new view plane.

*/

function setVertices():void {

vertsArray[0]=[70,-70,70];

vertsArray[1]=[70,70,70];

vertsArray[2]=[-70,70,70];

vertsArray[3]=[-70,-70,70];

vertsArray[4]=[70,-70,-70];

vertsArray[5]=[70,70,-70];

vertsArray[6]=[-70,70,-70];

vertsArray[7]=[-70,-70,-70];

}

/*

The setFaces function populates the array of faces, facesArray. Each face
is defined as a list of vertices that will be joined, in the order they appear,
by lineal elements to create the face. Each vertex is referred to by its
index in the vertsArray. Since we are drawing a cube, each face has four
vertices.

*/

function setFaces():void {

facesArray[0]=[0,4,5,1];

facesArray[1]=[1,5,6,2];

facesArray[2]=[2,6,7,3];

facesArray[3]=[3,7,4,0];

facesArray[4]=[4,5,6,7];

facesArray[5]=[0,1,2,3];

}

/*

The next function rotates the positon of the observer horizontally by the angle t,
and vertically by the angle phi. Both angles are measured in degrees.
t measured counterclockwise from the positive x-axis. p is the angle between
the positive z-axis and the new position vector of the observer (called the new view vector).
In other words, t is the theta coordinate in spherical coordinates and
p is the phi coordinate in spherical coordinates of the new position of the observer.
The new view plane is perpendicular to the new view vector and passes
through the origin. We assume that there is no rotation about the new view vector.
See the pictures on the previous html page of this tutorial. The pictures illuminate
the process of moving of the observer. Mathematically, we choose a new 3D coordinate
system such that the new view vector (normalized) is the new first coordinate. We choose
an orthogonal coordinate system on the view plane such that the old positve z-axis
projects onto the positive vertical axes on the new view plane and the new coordinate
system is right-handed.

*/

function renderView(t:Number,p:Number):void {

//We define local variables whose meaning will become clear below.

var i:int;

var distArray=[];

var dispArray=[];

var vertsNewArray=[];

var midPoint:Array=[];

var dist:Number;

var curFace:uint;

//We convert t and p into radians.

t=t*Math.PI/180;

p=p*Math.PI/180;

//We clear the previous view of the cube.

shCube.graphics.clear();

for(i=0;i<numVertices;i++){

/*

For each vertex, we calculate its coordinates in the new coordinate system

corresponding to the new position of the observer given by the angles t and p.

The calculations are done by the function pointNewView. From now on,

all positions are going to be relative to the new coordinate system.

*/

vertsNewArray[i]=pointNewView(vertsArray[i],t,p);

}

for(i=0;i<numFaces;i++){

/*

For each face, we calculate its midpoint in 3D by taking the averages of

the corresponding coordinates of each vertex. Note, the number of vertices

for each face, 4, is hard-wired here. We will have to change that

if we want to draw arbitrary polygons. Observe also that facesArray[i] is

the number of a face. A face is defined as an array of numbers of vertices.

That is, facesArray[i][0] is the position of the first vertex of the face i

in the vertices array vertsNewArray.

*/

midPoint[0]=(vertsNewArray[facesArray[i][0]][0]+vertsNewArray[facesArray[i][1]][0]

+vertsNewArray[facesArray[i][2]][0]+vertsNewArray[facesArray[i][3]][0])/4;

midPoint[1]=(vertsNewArray[facesArray[i][0]][1]+vertsNewArray[facesArray[i][1]][1]

+vertsNewArray[facesArray[i][2]][1]+vertsNewArray[facesArray[i][3]][1])/4;

midPoint[2]=(vertsNewArray[facesArray[i][0]][2]+vertsNewArray[facesArray[i][1]][2]

+vertsNewArray[facesArray[i][2]][2]+vertsNewArray[facesArray[i][3]][2])/4;

/*

We calculate the distance of each face from the observer. That is, the distance

of its midpoint from the observer. The observer in the old as well as

in the new coordinate system is located on the positive x-axis

at the distance fLen from the origin.

*/

dist=Math.sqrt(Math.pow(fLen-midPoint[0],2)+Math.pow(midPoint[1],2)

+Math.pow(midPoint[2],2));

/*

distArray is an array of two-element arrays. The first element is the distance

of the i-th face from the observer, the second the number

of the face (as stored in facesArray).

*/

distArray[i]=[dist,i];

}

/*

We sort distArray according to the function byDist defined later in the script.

The sorted distArray, has faces that are farther from the observer appearing

first. We will draw them first so they will appear behind faces

which are closer to the observer.

*/

distArray.sort(byDist);

for(i=0;i < numVertices;i++){

/*

We are projecting vertices onto the view plane. In the new coordinate

system the second and the third coordinates of each vertex give the projection.

We multiply the coordinates by fLen/(fLen-vertsNewArray[i][0]) which gives

the perspective effect.

*/

dispArray[i]=[fLen/(fLen-vertsNewArray[i][0])*vertsNewArray[i][1],

-fLen/(fLen-vertsNewArray[i][0])*vertsNewArray[i][2]];

}

for(i=0;i < numFaces;i++){

/*

After projecting on the view plane, we are drawing each face in the order

determined by the sorted distArray (from back to front). We are using

what is known as the 'painter's algorithm'.

*/

shCube.graphics.lineStyle(1,0xCC0000);

curFace=distArray[i][1];

shCube.graphics.beginFill(facesColors[curFace],0.8);

shCube.graphics.moveTo(dispArray[facesArray[curFace][0]][0],

dispArray[facesArray[curFace][0]][1]);

shCube.graphics.lineTo(dispArray[facesArray[curFace][1]][0],

dispArray[facesArray[curFace][1]][1]);

shCube.graphics.lineTo(dispArray[facesArray[curFace][2]][0],

dispArray[facesArray[curFace][2]][1]);

shCube.graphics.lineTo(dispArray[facesArray[curFace][3]][0],

dispArray[facesArray[curFace][3]][1]);

shCube.graphics.lineTo(dispArray[facesArray[curFace][0]][0],

dispArray[facesArray[curFace][0]][1]);

shCube.graphics.endFill();

}

}

/*

Below are a few helper functions: for sorting distArray, for projecting a point

onto the new view plane, and for calculating coordinates a point's

coordinates in a new coordinate system corresponding to

the new position of the observer. The tranformation matrix

is what it is because of the choice of the new coordinate system.

*/

function byDist(v:Array,w:Array):Number {

if (v[0] > w[0]){

return -1;

} else if (v[0] < w[0]){

return 1;

} else {

return 0;

}

}

function pointNewView(v:Array,theta:Number,phi:Number):Array {

var newCoords:Array=[];

newCoords[0]=v[0]*Math.cos(theta)*Math.sin(phi)

+v[1]*Math.sin(theta)*Math.sin(phi)+v[2]*Math.cos(phi);

newCoords[1]=-v[0]*Math.sin(theta)+v[1]*Math.cos(theta);

newCoords[2]=-v[0]*Math.cos(theta)*Math.cos(phi)

-v[1]*Math.sin(theta)*Math.cos(phi)+v[2]*Math.sin(phi);

return newCoords;

}

/*

We add listeners to spBoard that allow the user to rotate the cube.
The MOUSE_MOVE listener calls renderView function with the values of t and p
that correspond to the horizotal and vertical changes in the position
of the mouse.

*/

spBoard.addEventListener(MouseEvent.ROLL_OUT,boardOut);

spBoard.addEventListener(MouseEvent.MOUSE_MOVE,boardMove);

spBoard.addEventListener(MouseEvent.MOUSE_DOWN,boardDown);

spBoard.addEventListener(MouseEvent.MOUSE_UP,boardUp);

function boardOut(e:MouseEvent):void {

doRotate=false;

}

function boardDown(e:MouseEvent):void {

prevX=spBoard.mouseX;

prevY=spBoard.mouseY;

doRotate=true;

}

function boardUp(e:MouseEvent):void {

doRotate=false;

}

function boardMove(e:MouseEvent):void {

var locX:Number=prevX;

var locY:Number=prevY;

if(doRotate){

prevX=spBoard.mouseX;

prevY=spBoard.mouseY;

curTheta+=(prevX-locX);

curPhi+=(prevY-locY);

renderView(curTheta,curPhi);

e.updateAfterEvent();

}

}

On the next page, we use the ideas developed in the cube applet to create a different surface and allow for continuing rotation after the user releases the mouse.