Man, this class is pretty!

Lecture Notes Nine: Into the Third Dimension

Motto: Never open a spaceman's helmet on an uncharted planet.

Here's cubes.html:

<html>

  <head>
    <title>Some cubes</title>
  </head>

  <body>

    <applet code="Cube.class" 
            width=400 
            height=400>  
    </applet>
  
  </body>

</html>
The approach now is top-down since we want

  1. to see (to believe) and then
  2. to understand (in this order)

So, here's Cube.java:

import java.awt.*;
import java.net.*;
import java.io.*;

/*************************************************************
 * A rotating cubes applet. See Chapter 11 of the BAJGP. 
 * 
 * Putting the classes to work with a quick and dirty applet.
 *************************************************************/

public class Cube extends fNoFlickerApplet implements Runnable {

    fGenericCamera camera;
    fPoint3d camPos;
    fAngle3d camAngle;
    
    fPolyhedron cube;
    fPolyhedronInstance cubeInstance[];
    fPoint3d pos[];
    fAngle3d agl;
    
    Thread myThread;
    
    public void init() {
	
	//-- create a camera
	camera=new fGenericCamera(400,400,Math.PI/2);
	camPos=new fPoint3d(0,0,5);
	camAngle=new fAngle3d();
	
	//-- load a model from the file cube.f3d
	try{
	    InputStream is=new URL(getCodeBase(),"cube.f3d").openStream();
	    cube=new fConvexPolyhedron(is);
	} catch(Exception e) {
	    e.printStackTrace();
	}
	
	//-- create 9 instances of the cube
	cubeInstance=new fPolyhedronInstance[9];
	for(int n=0; n<9; n++) {
	    cubeInstance[n]=new fPolyhedronInstance(cube);
	}
	
	//-- create the positions and angle
	pos=new fPoint3d[9];
	int n=0;
	for(int y=-5; y<=5; y+=5){
	    for(int x=-5; x<=5; x+=5){
         	pos[n]=new fPoint3d(x,y,0);
      		n++;
	    }
	}
	agl=new fAngle3d();

	//-- start the thread
	myThread=new Thread(this);
	myThread.start();
    }
    
    public void run() {
	while(true) {

	    //-- sleep 1/10 of a second
	    try {
		myThread.sleep(100);
	    } catch ( InterruptedException e) {
		// nothing 
	    }
	    
	    //-- update the angle of the models
	    agl.x+=Math.PI/20; agl.y+=Math.PI/30;
	    
	    //-- update camera angle and position
	    camPos.z+=0.2; camAngle.z+=Math.PI/50;
	    camera.setOrientation(camPos,camAngle);
	    
	    //-- request a repaint
	    repaint();
	}
    }

    public void start() {
	if(myThread==null) {
	    myThread=new Thread(this);
	    myThread.start();
	}
    }

    public void stop() {
	if(myThread!=null) {
	    myThread.stop();
	    myThread=null;
	}
    }
    
    public void paint(Graphics g) {

	//-- clear screen
   	g.clearRect(0,0,size().width,size().height);
   	
   	//-- paint the models
   	for(int n=0; n<9; n++){
	    cubeInstance[n].setOrientation(pos[n],agl);
	    cubeInstance[n].paint(g,camera);
   	}
    }
}
This requires the following (data) file, called cube.f3d:
8
	-1	1	1 
	-1	1	-1  
	1	1	-1  
	1	1	1
	-1	-1	1
	-1	-1	-1
	1	-1	-1
	1	-1	1
6
4	0 1 2 3		128 128 0
4	0 4 5 1 	128 0   128
4	1 5 6 2 	0   128 128
4	2 6 7 3 	128 0   0
4	0 3 7 4 	0   0   128
4	5 4 7 6 	0   128 0
It also requires the following classes:
  1. fNoFlickerApplet
  2. fGenericCamera
  3. fPoint3d
  4. fAngle3d
  5. fPolyhedron
  6. fPolyhedronInstance
So we provide them.

import java.awt.*;
import java.applet.*;

/**
 * Represents a panel that does the painting offscreen  
 * which avoids flickering. Good for using in animations.
 */

class fNoFlickerApplet extends Applet {
    private Image offScreenImage;
    private Graphics offScreenGraphics;
    private Dimension offScreenSize;
    
    public final void update(Graphics theG){
	Dimension d=size();
	if((offScreenImage==null) || 
	   (d.width != offScreenSize.width) || 
	   (d.height != offScreenSize.height)) {

	    offScreenImage = createImage(d.width,d.height);
	    offScreenSize = d;
	    offScreenGraphics = offScreenImage.getGraphics();
	    offScreenGraphics.clearRect(0,0,offScreenSize.width,
					offScreenSize.height);
	}
	paint(offScreenGraphics);
	theG.drawImage(offScreenImage,0,0,null);
    }
}
Now the generic camera.

/**
 * A generic camera.
 */
public class fGenericCamera extends Object {
    //-- a temporary buffer used for projection
    protected static fArrayOf2dPoints our2dBuffer=
	new fArrayOf2dPoints(new int[100],new int[100],100);
    //-- a temporary buffer used for WCS to VCS transform
    protected static fArrayOf3dPoints our3dBuffer=new fArrayOf3dPoints(100);
    //-- the screen distance
    protected double screendist;
    //-- screen origo
    protected int x0,y0;
    //-- the viewangle
    protected double myViewAngle;
    //-- the matrix used for the WCS to VCS tranform
    fMatrix3d myWCStoVCSmatrix;
    //-- mark if the matrix is dirty
    boolean matrixIsDirty;
    //-- the position and angle of the camera in WCS
    fPoint3d myPosition;
    fAngle3d myAngle;
    
    /**
     * constructs a camera by specifing the widht, height and viewangle
     */
    public fGenericCamera(int width,int height,double viewAngle){
	myViewAngle=viewAngle;
	//-- calculate the screen origo
	x0=width>>1; y0=height>>1;
	//-- calculate the screen distance
	screendist=(double)x0/(Math.tan(viewAngle/2));
	//-- construct the matrix
	myWCStoVCSmatrix=new fMatrix3d();
	//--
	myPosition=new fPoint3d();
	myAngle=new fAngle3d();
	matrixIsDirty=true;
    }
    /**
     * sets the position and angle of the camera.
     */
    public void setOrientation(fPoint3d pos,fAngle3d agl){
	if(myPosition.equals(pos)==false){
	    myPosition.set(pos);
	    matrixIsDirty=true;
	}
	if(myAngle.equals(agl)==false){
	    myAngle.set(agl);
	    matrixIsDirty=true;
	}
    }
    /**
     * projects an array of 3d points to the temporary 2d buffer
     */
    public fArrayOf2dPoints project(fArrayOf3dPoints p3d){
	//-- updates the matrix if it needed
	updateMatrix();
	//-- transform the WCS vertices to VCS storing the results
	//-- in a buffer
	myWCStoVCSmatrix.transform(p3d,our3dBuffer);
	//-- project the VCS coordiantes to SCS storing the results
	//-- in a buffer
	for(int n=0;n<p3d.npoints;n++){
	    double z=our3dBuffer.z[n];
	    our2dBuffer.x[n]=-(int)(screendist*our3dBuffer.x[n]/z)+x0;
	    our2dBuffer.y[n]= (int)(screendist*our3dBuffer.y[n]/z)+y0;
	}
	//-- lend the buffer to the caller.
	return our2dBuffer;
    }
    /**
     * updates the matrix
     */
    private void updateMatrix(){
	if(matrixIsDirty==true){
	    //-- only remake the matrix if it is "dirty"
	    myWCStoVCSmatrix.makeWCStoVCStransform(myPosition,myAngle);
	    matrixIsDirty=false;
	}
    }
}
Here's the three dimensional point:
public class fPoint3d{

    public double x;
    public double y;
    public double z;
    public fPoint3d (double x0, double y0, double z0) {
	x=x0; y=y0; z=z0;
    }
    
    public fPoint3d () {
	x=y=z=0;
    }
    
    public fPoint3d (fPoint3d p1, fPoint3d p2) {
	x=p2.x-p1.x; y=p2.y-p1.y; z=p2.z-p1.z;
    }
    
    public fPoint3d (fPoint3d p) {
	x=p.x; y=p.y; z=p.z;
    }
    
    void vectorProduct (fPoint3d p1, fPoint3d p2) {
	x=p1.y*p2.z-p1.z*p2.y;
	y=p1.z*p2.x-p1.x*p2.z;
	z=p1.x*p2.y-p1.y*p2.x;
    }
    
    void normalize (double length) {
	double t=length/Math.sqrt(x*x+y*y+z*z);
	x=t*x; y=t*y; z=t*z;
    }
    
    void plus (fPoint3d p) {
	x+=p.x; y+=p.y; z+=p.z;
    }
    
    double dotProduct (fPoint3d p) {
	return p.x*x+p.y*y+p.z*z;
    }
    
    boolean equals (fPoint3d p) {
	return (p.x==x)&&(p.y==y)&&(p.z==z);
    }
    
    void set (fPoint3d p) {
	x=p.x; y=p.y; z=p.z;
    }
    
    void times (double s) {
	x*=s; y*=s; z*=s;
    }
    
    void negate () {
	x=-x; y=-y; z=-z;
    }
    
    public String toString () {
	return new String("("+x+", "+y+", "+z+")");
    }
    
}
Here are the polyhedron classes, starting with the abstract Polyhedron class:
/*************************************
  polyhedronclasses.java 
*************************************/
import java.awt.*;
import java.io.*;
/**
 * A polyhedron class that is made out of a list of vertices
 * and a list of indexing polygons.
 */
abstract class fPolyhedron extends Object {
    //-- the 3d coordiantes for the model
    protected fArrayOf3dPoints myVertices;
    //-- the polygons
    protected fIndexingPolygon myPolygons[];
    protected int nbrPolygons;
    /**
     * construct a polyhedron with the supplied data.
     */
    protected fPolyhedron(fArrayOf3dPoints points,fIndexingPolygon polys[],int npolys){
	myVertices=points;
	myPolygons=polys;
	nbrPolygons=npolys;
    }
    /**
     * construct a polyhedron from a stream.
     */
    public fPolyhedron(InputStream is) throws IOException {
	fromString(is);
    }
    /**
     * paint the polyhedron using the supplied 2d coordiantes.
     */
    public abstract void paint(Graphics g,fArrayOf2dPoints point2d);
    /**
     * make a string representation of this polyhedron
     */
    public String toString(){
	String str=new String();
	//-- make the array of 3d points into a stream
	str=myVertices.toString();
	//-- write to stream how many polygons there are
	str=str+nbrPolygons+"\n";
	//-- write all polygons to the stream
	for(int n=0;n<nbrPolygons;n++){
	    str=str+myPolygons[n].toString();
	}
	return str;
    }
    /**
     * read the polyhedron from a stream
     */
    public void fromString(InputStream is) throws IOException {
	//-- make a stream tokenizer
	StreamTokenizer stream = new StreamTokenizer (is);
	stream.commentChar('#');
	
	//-- get the points
	myVertices=new fArrayOf3dPoints(is);
	myVertices.toString();
	//-- get the # polygons
	stream.nextToken(); nbrPolygons=(int)stream.nval;
	//-- create the vector
	myPolygons=new fIndexingPolygon[nbrPolygons];
	//-- read each polygon
	for(int n=0;n<nbrPolygons;n++){
	    myPolygons[n]=new fFilledPolygon(is);
	}
    }
    
    public fArrayOf3dPoints getVertices(){
	return myVertices;
    }
    
    public abstract fPolyhedron makeClone();
    
    public void scalePoints(double fx,double fy,double fz){
	for(int n=0;n<myVertices.npoints;n++){
	    myVertices.x[n]*=fx; myVertices.y[n]*=fy; myVertices.z[n]*=fz;
	}
    }
}

class fConvexPolyhedron extends fPolyhedron {
    /**
     * construct a polyhedron with the supplied data.
     */
    public fConvexPolyhedron(fArrayOf3dPoints points,fIndexingPolygon polys[],int npolys){
	super(points,polys,npolys);
    }
    /**
     * construct a polyhedron from a stream.
     */
    public fConvexPolyhedron(InputStream is) throws IOException {
	super(is);
    }
    /**
     * overrides fPolyhedron.paint(..)
     * the polygons don't need to be sorted.
     */
    public void paint(Graphics g,fArrayOf2dPoints point2d){
	//-- the polygons don't have to be sorted
	for(int n=0;n<nbrPolygons;n++){
	    myPolygons[n].paint(g,point2d.x,point2d.y);
	}
    }
    /**
     * Makes a clone of this polyhedron.
     */
    public fPolyhedron makeClone(){
	fIndexingPolygon polys[];
	polys=new fIndexingPolygon[nbrPolygons];
	for(int n=0;n<nbrPolygons;n++){
	    polys[n]=myPolygons[n].makeClone();
	}
	return new fConvexPolyhedron(myVertices.makeClone(),polys,nbrPolygons);
    }
}
Here's the polyhedron instance.

/***************************
  fpolyhedroninstance.java
****************************/ 
import java.awt.*;
/**
 * Class that represents an instance of a polyhedron.
 */
public class fPolyhedronInstance extends Object {
    //-- the transformed vertices
    protected fArrayOf3dPoints transformedVertices;
    //-- the matrix used for transformations
    protected fMatrix3d myTransformMatrix;
    //-- the polyhedron
    protected fPolyhedron thePolyhedron;
    //-- position in WCS
    protected fPoint3d myPosition;
    //-- the angle in WCS
    protected fAngle3d myAngle;
    //-- 
    protected boolean positionIsDirty,angleIsDirty;
    
    /**
     * construct an instance of the supplied polyhedron.
     */
    public fPolyhedronInstance(fPolyhedron poly){
	//-- the polyhedron that this instance is using
	thePolyhedron=poly;
	//-- create the vertices to be used for storing transformations
	try{
	    transformedVertices=(fArrayOf3dPoints)thePolyhedron.getVertices().makeClone();
	} catch(Exception e){e.printStackTrace();}
	
	myPosition=new fPoint3d();
	myAngle=new fAngle3d();
	myTransformMatrix=new fMatrix3d();
    }
    
    /**
     * set the position and angle for this polyhedron instance.
     */
    public void setOrientation(fPoint3d pos,fAngle3d agl){
	if(myPosition.equals(pos)==false){
	    //-- if position has changed then mark the matrix
	    //-- as "dirty" meaning that the transformed points
	    //-- need to be updated.
	    myPosition.set(pos);
	    positionIsDirty=true;
	}
	if(myAngle.equals(agl)==false){
	    myAngle.set(agl);
	    angleIsDirty=true;
	}
    }
    
    /**
     * paint the polyhedron instance.
     */
    public void paint(Graphics g,fGenericCamera camera){
	if(positionIsDirty || angleIsDirty){
	    //-- position or angle has changed and the transformed
	    //-- vertices need to be updated.
	    myTransformMatrix.makeMCStoWCStransform(myPosition,myAngle);
	    //-- transform the polyhedron model coordinates to world coords.
	    myTransformMatrix.transform(thePolyhedron.getVertices(),transformedVertices);
	    //-- 
	    positionIsDirty=angleIsDirty=false;
	}
	//-- project the WCS to the screen with the supplied camera
	//-- and then call the paint method of the polyhedron with
	//-- the returned 2d array
	thePolyhedron.paint(g,camera.project(transformedVertices));
    }
} 
A few other classes are needed as well:
  1. some matrix classes
  2. some helper classes
Here are the matrix classes:
/********************
  matrixclasses.java

*********************/

/**
 * A generic 3d matrix class that implements the rotation
 * about the principal axis, translation and scaling.
 */
class fGeneric3dMatrix extends Object {
    double xx, xy, xz, xo;
    double yx, yy, yz, yo;
    double zx, zy, zz, zo;
    
    /**
     * Constructs the identity matrix.
     */
    public fGeneric3dMatrix(){
	makeIdentity();
    }
    /**
     * Resets the matrix.
     */
    public void makeIdentity(){
	xx = 1; xy = 0; xz = 0; xo = 0;
	yx = 0; yy = 1; yz = 0; yo = 0; 
	zx = 0; zy = 0; zz = 1; zo = 0;
    }
    /**
     * "Smart" multiplies a rotation about Z-axis
     */
    public void concatRz(double az){
	double ct = Math.cos(az);
	double st = Math.sin(az);
	
	double Nyx = (yx * ct + xx * st);
	double Nyy = (yy * ct + xy * st);
	double Nyz = (yz * ct + xz * st);
	double Nyo = (yo * ct + xo * st);
	
	double Nxx = (xx * ct - yx * st);
	double Nxy = (xy * ct - yy * st);
	double Nxz = (xz * ct - yz * st);
	double Nxo = (xo * ct - yo * st);
	
	xx = Nxx; xy = Nxy; xz = Nxz; xo = Nxo;
	yx = Nyx; yy = Nyy; yz = Nyz; yo = Nyo;
    }
    /**
     * "Smart" multiplies a rotation about Y-axis
     */
    public void concatRy(double ay){
	double ct = Math.cos(ay);
	double st = Math.sin(ay);
	
	double Nxx = (xx * ct + zx * st);
	double Nxy = (xy * ct + zy * st);
	double Nxz = (xz * ct + zz * st);
	double Nxo = (xo * ct + zo * st);
	
	double Nzx = (zx * ct - xx * st);
	double Nzy = (zy * ct - xy * st);
	double Nzz = (zz * ct - xz * st);
	double Nzo = (zo * ct - xo * st);
	
	xx = Nxx; xy = Nxy; xz = Nxz; xo = Nxo;
	zx = Nzx; zy = Nzy; zz = Nzz; zo = Nzo;
    }
    /**
     * "Smart" multiplies a rotation about X-axis
     */
    public void concatRx(double ax){
	double ct = Math.cos(ax);
	double st = Math.sin(ax);
	
	double Nyx = (yx * ct + zx * st);
	double Nyy = (yy * ct + zy * st);
	double Nyz = (yz * ct + zz * st);
	double Nyo = (yo * ct + zo * st);
	
	double Nzx = (zx * ct - yx * st);
	double Nzy = (zy * ct - yy * st);
	double Nzz = (zz * ct - yz * st);
	double Nzo = (zo * ct - yo * st);
	
	yx = Nyx; yy = Nyy; yz = Nyz; yo = Nyo;
	zx = Nzx; zy = Nzy; zz = Nzz; zo = Nzo;
    }
    /**
     * "Smart" multiplies a translation
     */
    public void concatT(double x,double y,double z){
	xo+=x; yo+=y; zo+=z;
    }
    /**
     * "Smart" multiplies scaling
     */
    public void concatS(double sx,double sy,double sz){
	xx *= sx; xy *= sx; xz *= sx; xo *= sx;
	yx *= sy; yy *= sy; yz *= sy; yo *= sy;
	zx *= sz; zy *= sz; zz *= sz; zo *= sz;
    }
    /**
     * Multiplies the vector "ps" of 3d points and stores the result
     * in "pd".
     */
    public void transform(fArrayOf3dPoints ps,fArrayOf3dPoints pd){
	for (int i=0; i<ps.npoints; i++) {
	    double x=ps.x[i]; double y=ps.y[i]; double z=ps.z[i];
	    pd.x[i] = x*xx + y*xy + z*xz + xo;
	    pd.y[i] = x*yx + y*yy + z*yz + yo;
	    pd.z[i] = x*zx + y*zy + z*zz + zo;
	}
    }
}
/**
 * A 3d matrix that hides the making of the different 
 * transforms
 */
class fMatrix3d extends fGeneric3dMatrix {
    /**
     * construct the matrix
     */
    public fMatrix3d(){
	super();
    }
    /**
     * let matrix contain the MCS to WCS transform
     */
    public void makeMCStoWCStransform(fPoint3d pos,fAngle3d agl,fPoint3d scale){
	makeIdentity();
	concatS(scale.x,scale.y,scale.z);
	concatRx(agl.x);
	concatRy(agl.y);
	concatRz(agl.z);
	concatT(pos.x,pos.y,pos.z);
    }
    /**
     * let matrix contain the MCS to WCS transform, without scaling
     */
    public void makeMCStoWCStransform(fPoint3d pos,fAngle3d agl){
	makeIdentity();
	concatRx(agl.x);
	concatRy(agl.y);
	concatRz(agl.z);
	concatT(pos.x,pos.y,pos.z);
    }
    /**
     * let matrix contain the WCS to MCS transform
     */
    public void makeWCStoVCStransform(fPoint3d pos,fAngle3d agl){
	makeIdentity();
	concatT(-pos.x,-pos.y,-pos.z);
	concatRz(-agl.z);
	concatRy(-agl.y);
	concatRx(-agl.x);
    }
    
    public void makeLookAtPointTransform(fPoint3d p0,fPoint3d p1){
	fPoint3d vecZaxis=new fPoint3d(p0,p1);
	vecZaxis.normalize(1);
	fPoint3d vecXaxis=new fPoint3d();      
	vecXaxis.vectorProduct(new fPoint3d(0,1,0),p1);
	vecXaxis.normalize(1);
	
	fPoint3d vecYaxis=new fPoint3d();
	vecYaxis.vectorProduct(vecZaxis,vecXaxis);
	
	xo=-p0.x; yo=-p0.y; zo=-p0.z;
	xx=vecXaxis.x; xy=vecXaxis.y; xz=vecXaxis.z;
	yx=vecYaxis.x; yy=vecYaxis.y; yz=vecYaxis.z;
	zx=vecZaxis.x; zy=vecZaxis.y; zz=vecZaxis.z;
    }
    
}

Here are the helper classes:
/*********************
  polygonclasses.java
**********************/ 

import java.awt.*;
import java.io.*;
/**
 * Describes an abstract indexing polygon.
 */
abstract class fIndexingPolygon extends Object{
    /**
     * construct a polygon with the supplied indices
     */
    protected fIndexingPolygon(int indices[],int n){
	myIndices=indices;
	nbrIndices=n;
    }
    /**
     * construct a polygon from a stream
     */
    protected fIndexingPolygon(InputStream is) throws IOException {
	fromString(is);
    }
    /**
     * paints a polygon. the 2d list of coordiantes must be supplied
     */
    public abstract void paint(Graphics g,int x[],int y[]);
    /**
     * read a polygon from a stream
     */
    public void fromString(InputStream is) throws IOException {
	//-- make a stream tokenizer
   	StreamTokenizer stream = new StreamTokenizer (is);
	stream.commentChar('#');
	
	//-- get the # of indicies in this polygon
	stream.nextToken(); nbrIndices=(int)stream.nval;
	//-- allocate the vector
	myIndices=new int[nbrIndices];
	//-- read all indices
	for(int i=0;i<nbrIndices;i++){
	    stream.nextToken(); myIndices[i]=(int)stream.nval;
	}
    }
    /**
     * make a string representation of a polygon
     */
    public String toString(){
	String str=new String();
	str=str+nbrIndices;
	for(int n=0;n<nbrIndices;n++){
	    str=str+" "+myIndices[n];
	}
	return str;
    }
    /**
     * pokes out the 2d coordiantes and stores them into the
     * scratch polygon.
     */
    protected void copyIndexedPoints(int x[],int y[]){
	for(int n=0;n<nbrIndices;n++){
	    int i=myIndices[n];
	    ourScratchPoly.xpoints[n]=x[i];
	    ourScratchPoly.ypoints[n]=y[i];
	}
	ourScratchPoly.npoints=nbrIndices;
    }
    /**
     * determine the orientation of the scratch polygon.
     * if the result is positive then it is CW.
     */
    protected static int orientation() {
	int p1x=ourScratchPoly.xpoints[1],p1y=ourScratchPoly.ypoints[1];
	//-- vector from vertex #1 to vertex #2
	int v1x=ourScratchPoly.xpoints[2]-p1x;
	int v1y=ourScratchPoly.ypoints[2]-p1y;
	//-- vector from vertex #1 to vertex #0
	int v2x=ourScratchPoly.xpoints[0]-p1x;
	int v2y=ourScratchPoly.ypoints[0]-p1y;
	//-- return the determinant of the vectors
	return v1x*v2y-v2x*v1y;
    }
    /**
     * make a clone of this polygon
     */
    public abstract fIndexingPolygon makeClone();
    /**
     * the "scratch" polygon that is used for painting
     */
    protected static Polygon ourScratchPoly=new Polygon(new int[50],new int[50],50);
    /**
     * the indices that define this polygon
     */
    protected int myIndices[];
    /**
     * number of indices in this polygon.
     */
    protected int nbrIndices;
}

/**
 * A solid color polygon.
 */
class fFilledPolygon extends fIndexingPolygon {
    /**
     * The color of this polygon.
     */
    protected fColor myColor;
    /**
     * Create a polygon with the supplied data.
     */
    public fFilledPolygon(int indices[],int n,fColor color){
	super(indices,n);
	myColor=color;
    }
    /**
     * Create a polygon from a stream.
     */
    public fFilledPolygon(InputStream is) throws IOException {
	super(is);
    }
    /**
     * paints the polygon if it is cw
     */
    public void paint(Graphics g,int x[],int y[]){
	//-- copy the indexed coordiantes from the 2d list to
	//-- the scratch-pad
	copyIndexedPoints(x,y);
	render(g);
    }
    /**
     * The actual rendering.
     */
    protected void render(Graphics g){
	//-- check orientation
	if(orientation()>0){
	    g.setColor(myColor.getColor());
	    g.fillPolygon(ourScratchPoly);
	}
    }
    
    /**
     * overrides fIndexingPolygon.toString()
     * the color must also be written to the string.
     */
    public String toString(){
	//-- make the string for fIndexingPolygon
	String str=super.toString();
	//-- add the color and line break
	str=str+" "+myColor.toString()+"\n";
	return str;
    }
    /**
     * overrides fIndexingPolygon.toString()
     * the color must also be read from the stream.
     */
    public void fromString(InputStream is) throws IOException {
	super.fromString(is);
	//-- read the color
	myColor=new fColor(is);
    }
    /**
     * Makes a clone of this polygon.
     */
    public fIndexingPolygon makeClone(){
	int i[];
	System.arraycopy(myIndices,0,i=new int[nbrIndices],0,nbrIndices);
	return new fFilledPolygon(i,nbrIndices,myColor.makeClone());
    }
} 
And a few more:
public class fArrayOf2dPoints extends Object {
    int x[],y[];
    int npoints;
    
    public fArrayOf2dPoints(int x0[],int y0[],int n){
	x=x0; y=y0; npoints=n;
    }
}
Then this one:
import java.io.*;
/**
 * A class that encapsulates and array of 3d points.
 */
public class fArrayOf3dPoints extends Object {
    double x[],y[],z[];
    int npoints;
    /**
     * Constructs an array of 3d points with the supplied vectors.
     */
    fArrayOf3dPoints(double x0[],double y0[],double z0[],int n){
	x=x0; y=y0; z=z0; npoints=n;
    }
    /**
     * Constructs an empty array of 3d points with size "n"
     */
    fArrayOf3dPoints(int n){
	npoints=n;
	x=new double[n]; y=new double[n]; z=new double[n];
    }
    /**
     * construct an array of 3d points from a stream
     */
    fArrayOf3dPoints(InputStream is) throws IOException{
	fromString(is);
    }
    /**
     * ovrrides the Object method
     */
    public String toString(){
	String str=new String();
	//-- the number of vertices
	str=" "+npoints+"\n";
	//-- concat the coordinates to the string
	for(int n=0;n<npoints;n++){
	    str=str+x[n]+" "+y[n]+" "+z[n]+"\n";
	}
	return str;
    }
    /**
     * Returns a clone.
     */
    fArrayOf3dPoints makeClone(){
	double xnew[],ynew[],znew[];
	System.arraycopy(x,0,xnew=new double[npoints],0,npoints);
	System.arraycopy(y,0,ynew=new double[npoints],0,npoints);
	System.arraycopy(z,0,znew=new double[npoints],0,npoints);
	return new fArrayOf3dPoints(xnew,ynew,znew,npoints);
    }
    /**
     * Reads an array from a stream
     */
    void fromString(InputStream is) throws IOException {
	//-- make a stream tokenizer
	StreamTokenizer stream = new StreamTokenizer (is);
	stream.commentChar('#');
	
	//-- get the # points
	stream.nextToken(); npoints=(int)stream.nval;
	
	//-- create the vectors
	x=new double[npoints];
	y=new double[npoints];
	z=new double[npoints];
	
	//-- read the coordiantes
	for(int n=0;n<npoints;n++){
	    stream.nextToken(); x[n]=(double)stream.nval;
	    stream.nextToken(); y[n]=(double)stream.nval;
	    stream.nextToken(); z[n]=(double)stream.nval;
	}
    }
}
Then this one:
import java.awt.*;
import java.io.*;
/**
 * Wraps the java.awt.Color class provided by Java 
 */
public class fColor extends Object {
    public int r,g,b;
    protected Color myBaseColor;
    
    /**
     * construct a color with the RGB supplied.
     */
    public fColor(int r0,int g0,int b0){
	r=r0; g=g0; b=b0;
	myBaseColor=new Color(r,g,b);
    }
    /**
     * constructs a color from a stream
     */
    public fColor(InputStream is) throws IOException{
	fromString(is);
	myBaseColor=new Color(r,g,b);
    }
    /**
     * returns the base color
     */
    public Color getColor(){
	return myBaseColor;
    }
    /**
     * read the color from a stream
     */   
    public void fromString(InputStream is) throws IOException{
	//-- make a stream tokenizer
	StreamTokenizer stream = new StreamTokenizer (is);
	stream.commentChar('#');
	
	//-- read the RGB triple
	stream.nextToken(); r=(int)stream.nval;
	stream.nextToken(); g=(int)stream.nval;
	stream.nextToken(); b=(int)stream.nval;
    }
    /**
     * make a string representation
     */
    public String toString(){
	return new String(" "+r+" "+g+" "+b+" ");
    }
    /**
     * Makes a clone of this color.
     */
    public fColor makeClone(){
   	return new fColor(r,g,b);
    }
}
So we compile everything and here's the applet.

Once we see we are ready to understand it, and it's simple.


Last updated: Dec 26, 2001 by Adrian German for A348/A548