CHAPTER 5
And Now For Something Completely Different TOC

The Cube ProgramTOC


I now wish to analyze a single program from beginning to end to give the reader and idea of the Java block structure and flow of the program. I have chosen the program -Cube.java- since it has many of the object features which I would like to discuss. The program begins with a list of library paths. Each import statement allows the java compiler to search the indicated object, method or class for predifined objects, methods or classes which the programmer has used in the program. For instance, since we wish to create an applet we need to put the applet library in our path. The command

import java.awt.*;

allows the programmer to use any of the classes in that library. On the other hand the programmer could just import the classes from

java.awt.*;

which were desired, but the programmer must have a good idea of the minmal set of classes in

java.awt.*;

to do this. The program starts with the import statements:


import java.applet.*;
import java.awt.*;
import java.lang.Math;
import java.lang.Object;


Next we have the program/class definition statement. Our programs extend the applet class and so that must be mentioned so that they can be compiled as applets. Note that the name of the program -Cube- must match exactly with the filename, since they both identify a class in Java.

public class Cube extends Applet {


Next we have the class definitions for our program. In this case we have several data structures which we will want to clone. Unless the program is a simple one, I would encourage the programmer to create the cloning overhead when the data structure is created during the programming process. Note once again that the data structure must implement cloning and so we must also import the clone object. As we have seen before the -super.clone- will handle the overhead for many of the basic data types. However more complicated data structures must be specifically cloned by the programmer as I have done below for the Rcube class.

    public class point implements Cloneable{
        public int x;
        public int y;


        public Object clone(){ //begin clone
        try{ //begin try
        return super.clone();
        } //end try

        catch(CloneNotSupportedException e){ // begin catch
        return null; //if there is a catch
        } //end catch
        } //end cloning structure for point

        } //end point def

    public class rpoint implements Cloneable{
        public double x;
        public double y;
        public double z;

    public Object clone(){ //begin clone
        try{ //begin try
        return super.clone();
        } //end try

        catch(CloneNotSupportedException e){ // begin catch
        return null; //if there is a catch
        } //end catch
        } //end cloning structure for point

        } //end rpoint def


Now we come to the basic object which we wish to draw and rotate: the cube in real space. To implement this I have imagined that a center of the cube exists at some origin point. From that point in real space I find the center of the top face, the front face and the side face in real space units (i.e. as real valued vector components).

Using these vectors in three space I then find the corners of this virtual cube in real space and project the corners into a two dimensional space much as if I were looking at the sunlit shadow of a wire-frame cube. The corners are given by finding interger approximations to the projected corners and labeling these eight corners as up1, up2, up3, up4, down1, down2, down3, down4. These labels are in fact characteristics of our cube object.

public class RCube implements Cloneable{
    public rpoint front=new rpoint();
    public rpoint side=new rpoint();
    public rpoint top=new rpoint();
    public point up1=new point();
    public point up2=new point();
    public point up3=new point();
    public point up4=new point();
    public point down1=new point();
    public point down2=new point();
    public point down3=new point();
    public point down4=new point();
    public double esize=30.0;
    public rpoint raxis=new rpoint();
        public double theta=0.0;

        public Object clone(){ //begin clone
        try{ //begin try
        RCube cu=(RCube)super.clone();
        cu.front=(rpoint)front.clone();
        cu.top=(rpoint)top.clone();
        cu.side=(rpoint)side.clone();
        cu.raxis=(rpoint)raxis.clone();
        cu.up1=(point)up1.clone();
        cu.up2=(point)up2.clone();
        cu.up3=(point)up3.clone();
        cu.up4=(point)up4.clone();
        cu.down1=(point)down1.clone();
        cu.down2=(point)down2.clone();
        cu.down3=(point)down3.clone();
        cu.down4=(point)down4.clone();
        return cu;
        } //end try
    catch(CloneNotSupportedException e){
    return null;
    }//end catch

        } //end cloning structure for body

        } // end Cube def


Next we define the basic variables which will be needed to carry out the project.

    private int i;
    private int last_x=0;
    private int last_y=0;

    private Color current_color=Color.black;
    private Button clear_button;
    private Button draw_button;
    private Choice ang_choices;

    public RCube er=new RCube();
    public RCube cu=new RCube();
    public RCube c=new RCube();

    private double xmult=0.5;
    private double mult=0.01;
    private double phi=0.0;
    private double psi=0.0;
    private int delX;
    private int delY;



Since we are running an applet and not a traditional program we must tell the browser how to initialize the applets dwelling place. Since in particular we are interested in graphics applications, we set the background color and add buttons to the tableau. These buttons will allow the user to work or interact within the applet dwelling place.


//init applet
    public void init(){

//set backgd color
    this.setBackground(Color.white);

//create a button
    clear_button=new Button("Clear");
    clear_button.setForeground(Color.black);
    clear_button.setBackground(Color.lightGray);
    this.add(clear_button);

//create a button
    draw_button=new Button("Draw Cube");
    draw_button.setForeground(Color.black);
    draw_button.setBackground(Color.lightGray);
    this.add(draw_button);

    }
//end init applet


This portion of the program will interrupt the usual or super-class function of the mouseDown (i.e. click on mouse) event and it will store the pixel position of the mouseDown event for use in the program. If the mouse is held in the down position the the super-class will continue to monitor and periodically feed new pixel information to the program. In addtion as the mouse is dragged across the screen the super-class will give the position of the mouse under the dragging process periodically.

Consequently, we may use the last_x and last_y from mouseDown and the new values x and y from mouseDrag to obtain an approximation for the change in the mouse postion from time to time. Then considering the change in mouse position under drag we can obtain a change in x which we rescale to rotate the cube in the xz-plane.


//called when mouse click
    public boolean mouseDown(Event e, int x, int y){
        last_x=x; last_y=y;
        return true;
    }


//called when mouse moves
    public boolean mouseDrag(Event e, int x, int y){
        Graphics g=this.getGraphics();
        er=(RCube)c.clone();

// figure phi and psi

    delX=x - last_x;
    delY=y - last_y;

    phi=phi+ mult*delX;
    psi=0.0;

c.front.x=Math.cos(psi)*Math.cos(phi);
c.front.z=Math.sin(psi)*Math.cos(phi);
c.front.y= Math.sin(phi);

c.side.x= -Math.cos(psi)*Math.sin(phi);
c.side.z= -Math.sin(psi)*Math.sin(phi);
c.side.y= Math.cos(phi);

c.top.x =c.front.y*c.side.z - c.front.z*c.side.y;
c.top.y = c.front.z*c.side.x - c.front.x*c.side.z;
c.top.z = c.front.x*c.side.y - c.front.y*c.side.x;

//figure vertices

c.down1.y=(int)(80 + c.esize*(xmult*(c.front.x - c.top.x - c.side.x)
        + c.top.z - c.front.z + c.side.z));
c.down1.x=(int)(60 + c.esize*(xmult*(-c.front.x + c.top.x + c.side.x)
        + c.front.y + c.top.y - c.side.y));
c.down2.y=(int)(80 + c.esize*(xmult*(c.front.x - c.top.x + c.side.x)
        + c.top.z - c.front.z + c.side.z));
c.down2.x=(int)(60 + c.esize*(xmult*(-c.front.x + c.top.x - c.side.x)
        + c.front.y + c.top.y + c.side.y));
c.down4.y=(int)(80 + c.esize*(xmult*(-c.front.x - c.top.x + c.side.x)
        + c.top.z + c.front.z + c.side.z));
c.down4.x=(int)(60 + c.esize*(xmult*(c.front.x + c.top.x - c.side.x)
        - c.front.y + c.top.y + c.side.y));
c.down3.y=(int)(80 + c.esize*(xmult*(-c.front.x - c.top.x - c.side.x)
        + c.top.z + c.front.z + c.side.z));
c.down3.x=(int)(60 + c.esize*(xmult*(c.front.x + c.top.x + c.side.x)
        - c.front.y - c.top.y - c.side.y));

c.up1.y=(int)(80 + c.esize*(xmult*(c.front.x + c.top.x - c.side.x)
        - c.top.z - c.front.z - c.side.z));
c.up1.x=(int)(60 + c.esize*(xmult*(-c.front.x - c.top.x + c.side.x)
        + c.front.y - c.top.y - c.side.y));
c.up2.y=(int)(80 + c.esize*(xmult*(c.front.x + c.top.x + c.side.x)
        - c.top.z - c.front.z - c.side.z));
c.up2.x=(int)(60 + c.esize*(xmult*(-c.front.x - c.top.x - c.side.x)
        + c.front.y + c.top.y + c.side.y));
c.up4.y=(int)(80 + c.esize*(xmult*(-c.front.x + c.top.x + c.side.x)
        - c.top.z + c.front.z - c.side.z));
c.up4.x=(int)(60 + c.esize*(xmult*(c.front.x - c.top.x - c.side.x)
        - c.front.y + c.top.y + c.side.y));
c.up3.y=(int)(80 + c.esize*(xmult*(-c.front.x + c.top.x - c.side.x)
        - c.top.z + c.front.z - c.side.z));
c.up3.x=(int)(60 + c.esize*(xmult*(c.front.x - c.top.x + c.side.x)
        - c.front.y - c.top.y - c.side.y));
// begin erase last cube

    g.setColor(Color.white);

g.drawLine(er.up3.x,er.up3.y,er.up4.x,er.up4.y);//up3 to up4
g.drawLine(er.up3.x,er.up3.y,er.up1.x,er.up1.y);//up3 to up1
g.drawLine(er.up4.x,er.up4.y,er.up2.x,er.up2.y);//up4 to up2
g.drawLine(er.up1.x,er.up1.y,er.up2.x,er.up2.y);//up1 to up2
g.drawLine(er.up3.x,er.up3.y,er.down3.x,er.down3.y);//up3 to down3
g.drawLine(er.up4.x,er.up4.y,er.down4.x,er.down4.y);//up4 to down4
g.drawLine(er.up1.x,er.up1.y,er.down1.x,er.down1.y);//up1 to down1
g.drawLine(er.up2.x,er.up2.y,er.down2.x,er.down2.y);//up2 to down2
g.drawLine(er.down3.x,er.down3.y,er.down1.x,er.down1.y);//down3 to down1
g.drawLine(er.down2.x,er.down2.y,er.down4.x,er.down4.y);//down4 to down2
g.drawLine(er.down4.x,er.down4.y,er.down3.x,er.down3.y);//down3 to down4
g.drawLine(er.down1.x,er.down1.y,er.down2.x,er.down2.y);//down1 to down2

// bein draw next cube

g.setColor(Color.black);

g.drawLine(c.up1.x,c.up1.y,c.up2.x,c.up2.y);//up1 to up2
g.drawLine(c.up3.x,c.up3.y,c.up1.x,c.up1.y);//up3 to up1
g.drawLine(c.up4.x,c.up4.y,c.up2.x,c.up2.y);//up4 to up2
g.drawLine(c.up3.x,c.up3.y,c.up4.x,c.up4.y);//up3 to up4
g.setColor(Color.red);
g.drawLine(c.up3.x,c.up3.y,c.down3.x,c.down3.y);//up3 to down3
g.drawLine(c.up4.x,c.up4.y,c.down4.x,c.down4.y);//up4 to down4
g.drawLine(c.up1.x,c.up1.y,c.down1.x,c.down1.y);//up1 to down1
g.drawLine(c.up2.x,c.up2.y,c.down2.x,c.down2.y);//up2 to down2
g.drawLine(c.down4.x,c.down4.y,c.down3.x,c.down3.y);//down3 to down4
g.drawLine(c.down1.x,c.down1.y,c.down2.x,c.down2.y);//down1 to down2
g.drawLine(c.down3.x,c.down3.y,c.down1.x,c.down1.y);//down3 to down1
g.drawLine(c.down2.x,c.down2.y,c.down4.x,c.down4.y);//down4 to down2

    last_x=x;
    last_y=y;
    return true;
    }


We need the data for at least two cubes at a time. The old cube and a new one. We therefore need to be able to clone the Rcube structure. We use white to erase or draw over the old cube and then we draw the new cube. All mouse driven animation can be handle in this way.

Next we would like to override some of the mouse functions. We may add buttons in the init portion of the applet, but the buttons will be of no use unless we can control the class characteristics of the mouse. These method-classes appear in the

java.awt.Component

class.

//user clicks button or chooses a color
    public boolean action(Event event, Object arg){
        if(event.target==clear_button){
        Graphics g=this.getGraphics();
        Rectangle r=this.bounds();
        g.setColor(this.getBackground());
        g.fillRect(r.x,r.y,r.width,r.height);
    return true;
    }
    //end clear

    // in case of click
    else if(event.target==draw_button){

// draw
c.front.x=1.0;
c.front.y=0.0;
c.front.z=0.0;
c.side.x=0.0;
c.side.y=1.0;
c.side.z=0.0;
c.top.x=0.0;
c.top.y=0.0;
c.top.z=1.0;
phi=0.0;
psi=0.0;

c.down1.y=(int)(80 + c.esize*(xmult*(c.front.x - c.top.x - c.side.x)
        + c.top.z - c.front.z + c.side.z));
c.down1.x=(int)(60 + c.esize*(xmult*(-c.front.x + c.top.x + c.side.x)
        + c.front.y + c.top.y - c.side.y));
c.down2.y=(int)(80 + c.esize*(xmult*(c.front.x - c.top.x + c.side.x)
        + c.top.z - c.front.z + c.side.z));
c.down2.x=(int)(60 + c.esize*(xmult*(-c.front.x + c.top.x - c.side.x)
        + c.front.y + c.top.y + c.side.y));
c.down4.y=(int)(80 + c.esize*(xmult*(-c.front.x - c.top.x + c.side.x)
        + c.top.z + c.front.z + c.side.z));
c.down4.x=(int)(60 + c.esize*(xmult*(c.front.x + c.top.x - c.side.x)
        - c.front.y + c.top.y + c.side.y));
c.down3.y=(int)(80 + c.esize*(xmult*(-c.front.x - c.top.x - c.side.x)
        + c.top.z + c.front.z + c.side.z));
c.down3.x=(int)(60 + c.esize*(xmult*(c.front.x + c.top.x + c.side.x)
        - c.front.y - c.top.y - c.side.y));

c.up1.y=(int)(80 + c.esize*(xmult*(c.front.x + c.top.x - c.side.x)
        - c.top.z - c.front.z - c.side.z));
c.up1.x=(int)(60 + c.esize*(xmult*(-c.front.x - c.top.x + c.side.x)
        + c.front.y - c.top.y - c.side.y));
c.up2.y=(int)(80 + c.esize*(xmult*(c.front.x + c.top.x + c.side.x)
        - c.top.z - c.front.z - c.side.z));
c.up2.x=(int)(60 + c.esize*(xmult*(-c.front.x - c.top.x - c.side.x)
        + c.front.y + c.top.y + c.side.y));
c.up4.y=(int)(80 + c.esize*(xmult*(-c.front.x + c.top.x + c.side.x)
        - c.top.z + c.front.z - c.side.z));
c.up4.x=(int)(60 + c.esize*(xmult*(c.front.x - c.top.x - c.side.x)
        - c.front.y + c.top.y + c.side.y));
c.up3.y=(int)(80 + c.esize*(xmult*(-c.front.x + c.top.x - c.side.x)
        - c.top.z + c.front.z - c.side.z));
c.up3.x=(int)(60 + c.esize*(xmult*(c.front.x - c.top.x + c.side.x)
        - c.front.y - c.top.y - c.side.y));

    Graphics g=this.getGraphics();
    g.setColor(Color.black);

g.drawLine(c.up3.x,c.up3.y,c.up4.x,c.up4.y);//up3 to up4
g.drawLine(c.up3.x,c.up3.y,c.down3.x,c.down3.y);//up3 to down3
g.drawLine(c.down4.x,c.down4.y,c.down3.x,c.down3.y);//down3 to down4
g.drawLine(c.up4.x,c.up4.y,c.down4.x,c.down4.y);//up4 to down4
g.drawLine(c.up1.x,c.up1.y,c.up2.x,c.up2.y);//up1 to up2
g.drawLine(c.up1.x,c.up1.y,c.down1.x,c.down1.y);//up1 to down1
g.drawLine(c.up2.x,c.up2.y,c.down2.x,c.down2.y);//up2 to down2
g.drawLine(c.down1.x,c.down1.y,c.down2.x,c.down2.y);//down1 to down2
g.drawLine(c.up3.x,c.up3.y,c.up1.x,c.up1.y);//up3 to up1
g.drawLine(c.up4.x,c.up4.y,c.up2.x,c.up2.y);//up4 to up2
g.drawLine(c.down3.x,c.down3.y,c.down1.x,c.down1.y);//down3 to down1
g.drawLine(c.down2.x,c.down2.y,c.down4.x,c.down4.y);//down4 to down2

    return true;
    } //end draw button event
    // other wise let superclass handle it
    else return super.action(event, arg);
    }
// end action method
}
//end Applet

References TOC