Introduction to Computer Graphics and Java Programming for Artists


School of the Museum of Fine Arts ::: Continuing Education

George Aroush, instructor


Lecture Seven -- A True Java Applet; 2D Array; 2D Transform; Switches


 

Readings

  1. The handouts named:
  2. Sample Programs in Batch Seven:
  3. "Java How to Program" by Paul J. Deitel, and Harvey M. Deitel

 

Exercises

    Do before our next lecture

  1. Re-write last week's array program so that rather then using two arrays to store a shape's data, it uses one two-dimensional array. In order for this to work correctly you must revise all methods that were working with one-dimensional array to work with the two-dimensional array.
  2. Write a program that will display a shape on the screen which will move, rotate, scale, etc.; use methods to do each operation.

Back to the top of this page. Back to main Index

 

A True and Complete  Java Applet

What?!

Up until now we have been creating Java applet that don't quite confirm to the Java coding specification and to the Web. Our focus has been mostly on getting an applet up and running that generates a graph or an animation. It's time to write a good applet and in order to do so we must learn a bit about key elements of applets and their behavior on the Web.

Creating an Applet -- a Review

As we have been doing to date, we create an Applet by subleasing the class java.applet.Applet like this:

public class MyClass extends java.applet.Applet
{
    ...
}

The Applet class provides behavior to enable our applet not only to work within the browser itself but also to take advantage of the capabilities of AWT (Abstract Windowing Toolkit -- the Java package that deals with User Interface elements, mouse and keyword events, and to draw to the screen.) When Java encounters the applet in a Web page, it loads it's initial applet class over the network, as well as any other helper classes that first class uses.

Major Applet Activities & Components

Applets have many different activities that correspond to various major events in the life cycle of the applet -- for example, initialization, painting, or mouse events. Each activity has a corresponding method, so when an event occurs, the browser or other Java-capable too calls those specific methods. Lets take a look at the five of the more important methods in an applet's execution: initialization, starting, stopping, destroying and painting. Below is a complete applet with all those five components.

/*
 *  Program:    TrueApplet.java
 *  Purpose:    do-while loop demo
 *  Author:     George Aroush
 *  Date:       1/1/1998
 *  Change Log: None
 *
 *  Full description of Program:
 *      Shows all the components of a Java applet
 */

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

public class TrueApplet extends Applet
{
    String  buffer;

        // this method gets called by Java only once after when 
        // the applet has been loaded into the browser
    public void init()
    {
        buffer = new String();
        addItem("initializing... ");
    }

        // this method gets called by Java whenever the applet is
        // ready to start/re-start (coming back to the applet URL
        // from a different URL
    public void start()
    {
        addItem("starting... ");
    }

        // this method gets called by Java whenever the applet is
        // about to be stopped (leaving the current URL of the applet
        // to a different URL
    public void stop()
    {
        addItem("stopping... ");
    }

        // this method gets called by Java when ever the applet is
        // about to be terminated (removed from memory)
    public void destroy()
    {
        addItem("preparing for unloading...");
    }

        // this method gets called by Java when ever the applet's
        // display area needs to be drawn/re-drawn
    public void paint(Graphics g)
    {
            // draw a Rectangle around the applet's display area
        g.drawRect(0, 0, size().width - 1, size().height - 1);

            // draw the current string inside the rectangle
        g.drawString(buffer, 5, 15);
    }

        // a helper method to demonstrate this applet; it appends
        // the above messages into our buffer and then forces
        // a repaint to occur by calling repaint()
    void addItem(String newWord)
    {
        buffer += newWord;  // take the current string and add it to our buffer
        repaint();          // for the method paint() to be called
    }
}

Now let us examine each component in this applet.

Initialization

Initialization occurs when the applet is first loaded (or reloaded) into a browser. Initialization might include creating the objects the applet needs, setting up an initial state, loading images or fonts, or setting parameters. To provide behavior for the initialization of an applet, we must provide an init() method:

public void init()
{
    ...
}

Starting

After an applet is initialized, it is started. Starting can also occur if the applet was previously stopped. For example, an applet is stopped if the reader follows a link to a different page, and it is started again when the reader returns to this page. Note that starting can occur server times during an applet's life cycle, whereas initialization happens only once. To provide startup behavior for our applet, we must provide the start() method:

public void start()
{
    ...
}

Stopping

Stopping and starting go hand in hand. Stopping occurs when the reader leaves the page that contains a currently running applet, or we can stop an applet ourselves by calling stop():

public void stop()
{
    ...
}

Destroying

Destroying enables the applet to clean up after itself just before it is freed or he browser exits:

public void destroy()
{
    ...
}

Painting

Painting is how an applet actually draws something on the screen, be it text, a line, a colored background, or an image. Painting can occur many hundreds of times during an applet's life cycle -- for example once after the applet is initialized, if the browser is placed behind another  window on the screen and then brought forward again, if the browser window is moved to a different position on the screen, or  repeatedly in the case of animations. The paint() method looks like this:

public void paint(Graphics g)
{
    ...
}

Note that unlike the other major methods in this section, paint() takes an argument: Graphics. This object is created and passed to paint() by the browser, so we don't have to worry about it.


Back to the top of this page. Back to main Index

 

New Data Type & Switching

char Data Type

The char data type is used in the demo program TwoD.java. char is a two byte sized variable that is intended to hold the code value for a character, such as found on the keyboard. We can give a char variable a value by enclosing a character in single quotes ' ' such as:

char c = 'A';

gives the value of "A" to the char variable c. Numerically, a char can hold a value between 0 and 65535. "A" actually has a numerical value of 65, but is interpreted as a char to be "A". We will be using the char data type to read the user's typing on the keyboard.

boolean Data Type

The boolean data type is a special data type in Java that is used for testing for logical result. Here is an example of how to create and use a boolean data type like this:

int         x,y; 
boolean     value;

x = 10;
y = 20;

value = x < y;

if (value == true)
{
    /* do something because: x is-less-then y
}
else
{
    /* do something else because: x is-not-less-then y
}

In this example, we used Java's test statement with the line: x < y to figure out the condition of the test. The result of the test is stored into the variable value which we later use inside the if statement.

Beside letting Java assign a value for our boolean data type, we can assign it a value ourselves, but note that only true or false can be assigned:

boolean boolTest;
int     x;

x = 20;

boolTest = true;     /* OK */
boolTest = false;    /* OK */
boolTest = 0;        /* ERROR */
boolTest = x;        /* ERROR */

Using switch

Programs often have to make decisions. A choice between two alternatives is easily made with an if..else statement such as:

if ( x > 319)
    System.out.println("thunk!");
else
    System.out.println("whoosh!");

The switch statement allows us to choose from more than two possibilities. It looks at the value of a particular variable and picks an option from the various cases that are presented for different values of the variable:

int     a;

a = 1;

switch (a)
{
case 0:         /* is a == 0 ? */
    System.out.println("nothing\n");            /* a is 0 */
    break;

case 1:         /* is a == 1 ? */
    System.out.println("something\n");          /* a is 1 */
    break;

case 2:         /* is a == 2 ? */
    System.out.println("more than something\n"); /* a is 2 */
    break;

default:        /* no match for a */
    System.out.println("negative or many\n");   /* a is none of */
                                                /* the above */
}

In the above example, the switch statement looks at the value of a variable a. It has specific case for a equal 0, 1 or 2. It also has a default case for any other value of a. The variable to be looked at is in parentheses following the keywords switch. Everything that follows is enclosed in curly braces. Each case is followed by a possible value for a and a colon " : ". The action(s) to be taken follow.

break; shows where that particular case ends. If (a == 0), it prints "nothing"; if (a == 1), it prints "something"; if (a == 2) it prints "more than something" and for all other values of a (the default) it prints "negative or many". We should usually include a default case. It is also possible to have more than one case produce the same effect, that is do/execute the same section of code. In that instance list the two cases, and then the statements they produce as:

case 2:
case 3:
    System.out.println("much more than something\n");
    break;


Back to the top of this page. Back to main Index

 

Two Dimensional Arrays & and 2D Transformations

Two Dimensional Array

In TwoD.java, the x and y coordinates are combined into a two-dimensional array or an array of arrays. The data for a star is put into an array called theStar[NP][2] where NP is the number of points and the second pair of square brackets distinguishes between X and Y. theStar[][] consists of NP arrays of 2 integers, an X coordinate and a Y coordinate. The arrays are stored in memory in the following form:

theStar[0][0]       [   ]   (holding the value of x0)
theStar[0][1]       [   ]   (holding the value of y0)
theStar[1][0]       [   ]   (holding the value of x1)
theStar[1][1]       [   ]   (holding the value of y1)
theStar[2][0]       [   ]   (holding the value of x2)
theStar[2][1]       [   ]   (holding the value of y2)
                    [   ]
. . . . . . .
. . . . . . .
. . . . . . .
                    [   ]
theStar[NP-1][0]    [   ]   (holding the value of y(NP - 1) )
theStar[NP-1][0]    [   ]   (holding the value of y(NP - 1) )

Another way to picture this would be as follows:

theStar[0]  ==> x0, y0  (or)    theStar[0][0]   ==> x0
                                theStar[0][1]   ==> y0

theStar[1]  ==> x1, y1  (or)    theStar[1][0]   ==> x1
                                theStar[1][1]   ==> x1

theStar[2]  ==> x2, y2  (or)    theStar[2][0]   ==> x2
                                theStar[2][1]   ==> y2

theStar[3]  ==> x3, y3  (or)    theStar[3][0]   ==> x3
                                theStar[3][1]   ==>	x3
. . . . . . .
. . . . . . .

theStar[0][0] refers to the x coordinate in array 0; theStar[0][1] refers to the y coordinate in the same array. The second x and y are in theStar[1][0] and theStar[1][1].

2D Transformations

The switch statement is useful in the demo, TwoD.java, which shows how to transform a 2D polygon figure with re-sizing, rotation, and movement. It has 6 methods -- one to create data for a 12 pointed star, and the others for the transformation. The star is created by a method called MakeStar(). This method takes the array theStar[][] and an integer r, which controls the irregularity of the star. The star is generated by moving a radius around a center using Math.sin() and Math.cos() and an angle which increases by a set increment, based on the number of points in the star. The flip-flops or alternates in value between a minimum and a maximum, producing the "valleys" and the points of the star. A random number between 0.0 and r is added to each radius to make the star irregular. The star will have both positive and negative x and y values (since Math.sin() and Math.cos() deliver both positive and negative values.) When the star is displayed, the value of the center of the screen will be added to each x and y to relocate the star. After creating the star and opening a screen, a message is displayed on the graphics screen. Next, the program waits for the user to type a character. We use a new method to do this:

public boolean keyDown(Event event, int key)
{
    opt = (char) key; /* convert the key into a char type */
    repaint(); /* will update the drawing */
    return (true);
}

The method keyDown() is given to us by Java and it is called when ever a key is pressed (is changed to a "down" state) on the keyboard. Here is our first use of a char variable -- opt holds the value of the key. The user can move the form right, left, up, or down, by typing "r", "l", "u", or "d". "+" or "-" enlarge and shrink the form, and "w" and "c" rotate it clock-wise and counter-clockwise. A switch statement uses the value of opt to change the array t[] for movement, size for scaling or resizing, and angle for rotation. The values of these variables are used by the DisplayObject() function to rotate, scale, and move the object before displaying it as a filled in star. theStar[][] is copied to xy[][] so that it can be altered by the RotateObject(), ScaleObject() and MoveObject() methods, and eventually displayed at the center of the screen. The program re-copies the original values at the start of each loop, and applies all three transformations with the new values of t[], size, and angle.

2D Rotation

The MoveObject() and ScaleObject() methods should be familiar, but RotateObject() is a new one. RotateObject() uses the sine and cosine of the angle to rotate the form about the origin (where x and y = 0) This is the main reason we defined the form with the origin at its center. A positive angle will rotate counter-clockwise and a negative angle will rotate clockwise. The float variables, s and c, hold the values of Math.sin(angle) and Math.cos(angle) so that we need only calculate them once. Trigromatic methods, that is Math.sin() and Math.cos(), take a long time to calculate, so this speeds up the program. New values for x[] and y[] are calculated so: the new x is equal to the cosine times the old x minus the sine times the old y. The new y is equal to the sine times the old x plus the cosine times the old y. It is important not to trash the old value of x, so the new value of x is stored in a float variable newX in :

newX = c * xy[point][X] - s * xy[point][Y];

This is so xy[point][X]'s old value will be available for:

      xy[point][Y] = s * xy[point][X] + c * xy[point][Y];

Then we can say:

      xy[point][X] = newX;

Back to the top of this page. Back to main Index.

Sample Programs -- Batch Seven

TwoD.java

/*
 *  Program:    TwoD.java
 *  Purpose:    switch and rotation
 *  Author:     George Aroush
 *  Date:       1/1/1998
 *  Change Log: None
 *
 *  Full description of Program:
 *      Demonstrates the use of two dimensional arrays and the switch keyword
 */

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

public class TwoD extends Applet
{
    final int   XC = 320;   /* center of screen */
    final int   YC = 240;
    final int   X = 0;
    final int   Y = 1;
    final int   NP = 24;        /* number of points in star */
    final float MIN = 10.0f;    /* minimum and maximum radius of star */
    final float MAX = 35.0f;
    final float MOVE = 4f;      /* move the star by 4 pixels each time */
    final float SIZE = 1.05f;   /* scale the star by 5% */
    final float ROT = 0.1f;     /* rotation angle for the star */

    char        opt;            /* to hold users choices */
    int         theStar[][] = new int[NP][2];
    int         xy[][] = new int[NP][2];
    int         t[] = new int[2];
    float       size = 1.0f, angle = 0.0f;		/* size and rotation of star */

        // this method gets called by Java only once after when 
        // the applet has been loaded into the browser
    public void init()
    {
        MakeStar(theStar, 5);   /* irregular 12 pointed star data generated */
        t[X] = t[Y] = 0;        /* initial movement is set to zero */
    }

        // this method gets called by Java whenever the applet is
        // ready to start/re-start (coming back to the applet URL
        // from a different URL
    public void start()
    {
    }

        // this method gets called by Java whenever the applet is
        // about to be stopped (leaving the current URL of the applet
        // to a different URL
    public void stop()
    {
    }

        // this method gets called by Java when ever the applet is
        // about to be terminated (removed from memory)
    public void destroy()
    {
    }

        // this method gets called by Java when ever we press a
        // key on the keyboard
    public boolean keyDown(Event event, int key)
    {
        opt = (char) key;   /* convert the key into a char type */
        repaint();          /* will update the drawing */
        return (true);
    }

        // this method gets called by Java when ever the applet's
        // display area needs to be drawn/re-drawn
    public void paint(Graphics g)
    {
            /* NOTE: It would be better to move this code into a method of it's own */
            /*       This is an example of something not to do */

        g.clearRect(0, 0, 640, 480);

        g.drawString("Enter:   r l u d + - c w or q", 10, 400);

        CopyObject(theStar, xy, NP);    /* copy original values to temp arrays */

            /* change variables tx, ty, size, and angle, based on opt */
        switch (opt)
        {
        case	'r':
           t[X] += MOVE;        /* move MOVE to right */
            break;

        case	'l':
            t[X] -= MOVE;       /* move MOVE to left */
            break;

        case	'd':
            t[Y] += MOVE;       /* move MOVE down */
            break;

        case	'u':
            t[Y] -= MOVE;       /* move MOVE up */
            break;

        case	'+':            /* note how we are handling */
        case	'=':            /* two cases at once */
            size *= SIZE;       /* increase by SIZE */
            break;

        case	'-':
            size /= SIZE;       /* decrease by SIZE */
            break;

        case	'c':
            angle += ROT;       /* rotate counter clockwise */	
            break;

        case	'w':
            angle -= ROT;       /* rotate clockwise */
            break;

        default:
            if (opt == 'q')
                destroy();      /* will terminate the program */
        }

        DisplayObject(xy, NP, t, size, angle, g);   /* scale, rotate, move and display form */

        opt = 0;        /* assume no key is pressed */
    }


    /*----------------------------------------------*/
    /*  The following methods are our own methods   */
    /*----------------------------------------------*/


    /*
     *  void    CopyObject(a, b, n)
     *
     *  Copy one array of 2D integers to another
     */
    void	CopyObject(int a[][], int b[][], int n)
    {
        int	point, coord;

        for (point = 0; point < n; point++)
            for (coord = 0; coord < 2; coord++)
                b[point][coord] = a[point][coord];
    }

    /*
     *  void    DisplayObject(xy, n, t, size, angle)
     *
     *  Rotate, scale, move and display star
     */
    void    DisplayObject(int xy[][], int n, int t[], float size, float angle, Graphics g)
    {
        boolean ok;
        int     point;

        RotateObject(xy, n, angle);
        ScaleObject(xy, n, size, size);
        MoveObject(xy, n, t);

        ok = true;

        for (point = 0; point < n; point++)
        {
            xy[point][X] += XC;
            xy[point][Y] += YC;

            if ( xy[point][X] < 0 || xy[point][Y] > 639 || xy[point][Y] < 0 || xy[point][Y] > 479)
                ok = false;
        }

        if (ok)     /* all vertices are on screen */
            DrawPolygon(xy, n, g);
    }

    /*
     *  void    MoveObject(xy,n,t)
     *
     *  Move form by adding tx and ty to each x[] and y[]
     */
    void    MoveObject(int xy[][], int n, int t[])
    {
        int     point, coord;

        for (point = 0; point < n; point++)
        {
            for (coord = 0; coord < 2; coord++)
                xy[point][coord] += t[coord];
        }
    }

    /*
     *  void    ScaleObject(xy, n, xScale, yScale)
     *	
     *  Resize form by multiplying each x[] and y[] by xScale
     */
    void    ScaleObject(int xy[][], int n, float xScale, float yScale)
    {
        int	point, coord;

        for (point = 0; point < n; point++)
        {
            for (coord = 0; coord < 2; coord++)
            {
                xy[point][coord] = (int) (xScale * (float) xy[point][coord]);
            }
        }
    }

    /*
     *  void    RotateObject(xy, n, angle)
     *	
     *  Rotate form about origin by angle degrees
     */
    void    RotateObject(int xy[][], int n, float angle)
    {
        int     point;
        float   s, c, newX;

        s = (float) Math.sin(angle);
        c = (float) Math.cos(angle);

        for (point = 0; point < n; point++)
        {
            newX = c * xy[point][X] - s * xy[point][Y];
            xy[point][Y] = (int) (s * xy[point][X] + c * xy[point][Y]);
            xy[point][X] = (int) newX;
        }
    }

    /*
     *  void    DrawPolygon(xy, n)
     *
     *  Copy 2D int array to two 1D int arrays and send to area()
     */
    void    DrawPolygon(int xy[][], int n, Graphics g)
    {
        int     x[] = new int[n];
        int     y[] = new int[n];
        int     i;

        for (i = 0; i < n; i++)
        {
            x[i] = xy[i][X];
            y[i] = xy[i][Y];
        }

        g.fillPolygon(x, y, n);
    }

    /*
     *  void    MakeStar(o, r)
     *
     *  Generate an irregular star
     */
    void	MakeStar(int o[][], int r)
    {
        float   inc, angle = 0.0f;
        int     p, rad = (int) MIN;

        inc = 2.0f * (float) Math.PI / NP;  /* change angle by 1/24 of a circle */

        for (p = 0; p < NP; p++)
        {
                /* use angle to rotate around center */
            o[p][X] = (int) ( rad * Math.cos(angle) );
            o[p][Y] = (int) ( rad * Math.sin(angle) );

            angle += inc;   /* next angle */

                /* flip-flop between MIN and MAX radius use random number from 0 to r for irregularity */
            if ( rad >= MAX )
                rad = (int) (MIN + (float) Random(r));
            else
                rad = (int) (MAX + (float) Random(r));
        }
    }

    /*
     *  int     Random()
     *
     *  Generate a random number between 0 to range
     */
    public int  Random(int range)
    {
        return ((int) (Math.random() * range));
    }
}

TwoD.htm

<html>
<head>
<title>TwoD</title>
</head>
<body>
    <hr>
    <applet code=TwoD width=640 height=480></applet>
    <hr>
</body>
</html>

Back to the top of this page. Back to main Index.