Man, this class is pretty!

Lecture Notes Two: Using Objects for Animation

We start by discussing threads, and the universal animation loop.

Here they are, in combined action.

frilled.cs.indiana.edu%pwd
/nfs/grouchy/home/user2/www/classes/a348-dger/t540/lectures/two
frilled.cs.indiana.edu%ls -ld Broad* Three*
-rw-r--r--   1 dgerman      2169 Nov  4 16:37 Broadway.java
-rw-r--r--   1 dgerman       503 Nov  4 17:01 Three.html
frilled.cs.indiana.edu%cat Three.html
<html>
  <title> Using Objects for Animation </title> 
  <body bgcolor=white>

    <p> 

    This animation lasts forever. When you're done looking at 
    it simply go back to the page you came from. Your browser
    will know to stop the applet when you leave this page. 

    Please notice the flicker... <p> 

    <applet code="Broadway.class" width=300 height=300>
    </applet>

    <p> <em>Broadway Boogie Woogie</em>, dedicated to Piet Mondrian. 

    

  </body>
</html>

frilled.cs.indiana.edu%cat Broadway.java
import java.applet.*;
import java.awt.*;

public class Broadway extends Applet implements Runnable {
    
    Thread animation;
    
    int locx,locy;               // location of rectangle
    int width, height;           // dimensions of rectangle
    
    static final byte UP    = 0; // direction of motion
    
    static final byte DOWN  = 1;
    static final byte LEFT  = 2;
    static final byte RIGHT = 3;
    
    byte state; // state the rect is in (up, down, left, right) 
    
    static final int REFRESH_RATE = 100;        
    // length of pausing interval (in ms)
    
    public void init() {
	System.out.println("Initializing... ");	
	setBackground(Color.black);
	locx = 80; locy = 100;	
	width = 110; height = 90;
	state = UP;
    }

    public void start() {
	System.out.println("Starting... ");
	animation = new Thread(this); // see run() below... 
	if (animation != null) {
	    animation.start();
	}
    }

    public void paint(Graphics g) {
	System.out.println("Painting... ");

	g.setColor(Color.yellow);
	g.fillRect(0,0,90,90);
	g.fillRect(250,0,40,190);
	g.fillRect(80,110,100,20); // hidden rectangle
	
	g.setColor(Color.blue);
	g.fillRect(80,200,220,90);
	g.fillRect(100,10,90,80);
	
	g.setColor(Color.lightGray);
	g.fillRect(locx,locy,width,height);
	
	g.setColor(Color.red);
	g.fillRect(200,0,45,45);
	g.fillRect(0,100,70,200);
	
	g.setColor(Color.magenta);
	g.fillRect(200,55,60,135);
    }
    
    
    void updateRectangle() {
	switch (state) {
	case DOWN:
	    locy += 2; 
	    if (locy >= 110) {
		state = UP;
	    }
	    break;
	case UP:
	    locy -= 2;
	    if (locy <= 90) {
		state = RIGHT;
	    }
	    break;
	case RIGHT:
	    locx += 2;
	    if (locx >= 90) {
		state = LEFT;
	    }
	    break;
	case LEFT:
	    locx -= 2;   
	    if (locx <= 70) {
		state = DOWN;
	    }
	    break;
	    
	}
    }

    public void run() {
	while (true) {
	    repaint();
	    updateRectangle();
	    try {
		Thread.sleep (REFRESH_RATE);
	    } catch (Exception e) { 

            };
	}
    }

    public void stop() {
	System.out.println("Stopping... ");
	if (animation != null) {
	    animation.stop();
	    animation = null;
	}
    }
}
frilled.cs.indiana.edu%javac Broad*.java
Note: Broadway.java uses or overrides a deprecated API.
Note: Recompile with -deprecation for details.
frilled.cs.indiana.edu%ls -ld Broad*
-rw-------   1 dgerman      2173 Nov  4 17:02 Broadway.class
-rw-r--r--   1 dgerman      2169 Nov  4 16:37 Broadway.java
frilled.cs.indiana.edu%
Here's the applet in action. (Notice the flicker).

To eliminate the flicker we apply double-buffering.

frilled.cs.indiana.edu%pwd
/nfs/grouchy/home/user2/www/classes/a348-dger/t540/lectures/two
frilled.cs.indiana.edu%ls -ld Four* Broad*DB.java
-rw-r--r--   1 dgerman      2593 Nov  4 17:20 BroadwayDB.java
-rw-r--r--   1 dgerman       352 Nov  4 17:19 Four.html
frilled.cs.indiana.edu%cat Four.html
<html>
  <head><title>A Better Boogie Woogie</title></head>
  <body bgcolor="white">

    This is the animation from before, with double buffering. No flicker... <p>

    <applet code="BroadwayDB.class" width=300 height=300>
    </applet>

    <p> <em>A Better Boogie Woogie</em>, also dedicated to Piet Mondrian <p> 

  </body>
</html>

frilled.cs.indiana.edu%cat Broad*DB.java
import java.applet.*;
import java.awt.*;

// double buffered and clipped

public class BroadwayDB extends Applet implements Runnable {
    
    Thread animation;
    
    int locx,locy;          // location of rectangle
    int width, height;      // dimensions of rectangle
    
    static final byte UP = 0; // direction of motion
    
    static final byte DOWN = 1;
    static final byte LEFT = 2;
    static final byte RIGHT = 3;
    
    Graphics offscreen;
    Image image;
    
    byte state;              // state the rect is in (same as before) 
    
    static final int REFRESH_RATE = 100;        // (that is, in ms)
    
    public void init() {
	System.out.println("Initializing... "); 
	setBackground(Color.black);
	locx = 80; 
	locy = 100; 
	width = 110; 
	height = 90; 
	state = UP;
	
	image = createImage(300,300);
	offscreen = image.getGraphics();
    }
    
    public void start() {
	System.out.println("Starting... ");
	animation = new Thread(this);
	if (animation != null) {
	    animation.start();
	}
    }
    
    public void update(Graphics g) { // this is new...
	g.clipRect(70,90,130,110);
	paint(g);
    }
    
    public void paint(Graphics g) {
	System.out.println("Painting... ");
	
	offscreen.setColor(Color.black);
	offscreen.fillRect(0,0,300,300);  // clear buffer
	offscreen.setColor(Color.yellow);
	offscreen.fillRect(0,0,90,90);
	offscreen.fillRect(250,0,40,190);
	offscreen.fillRect(80,110,100,20);
	
	offscreen.setColor(Color.blue);
	offscreen.fillRect(80,200,220,90);
	offscreen.fillRect(100,10,90,80);
	
	offscreen.setColor(Color.lightGray);
	offscreen.fillRect(locx,locy,width,height);
	
	offscreen.setColor(Color.red);
	offscreen.fillRect(200,0,45,45);
	offscreen.fillRect(0,100,70,200);
	
	offscreen.setColor(Color.magenta);
	offscreen.fillRect(200,55,60,135);
	g.drawImage(image,0,0,this);
	
    }
    
    
    void updateRectangle() {
	switch (state) {
	case DOWN:
	    locy += 2; 
	    if (locy >= 110) {
		state = UP;
	    }
	    break;
	case UP:
	    locy -= 2;
	    if (locy <= 90) {
		state = RIGHT;
	    }
	    break;
	case RIGHT:
	    locx += 2;
	    if (locx >= 90) {
		state = LEFT;
	    }
	    break;
	case LEFT:
	    locx -= 2;   
	    if (locx <= 70) {
		state = DOWN;
	    }
	    break;
	    
	}
	
    }
    
    public void run() {
	while (true) {
	    repaint();
	    updateRectangle();
	    try {
		Thread.sleep (REFRESH_RATE);
	    } catch (Exception exc) { };
	}
    }
    
    public void stop() {
	System.out.println("Stopping... ");
	if (animation != null) {
	    animation.stop();
	    animation = null;
	}
    }
    
}
frilled.cs.indiana.edu%javac BroadwayDB.java
Note: BroadwayDB.java uses or overrides a deprecated API.
Note: Recompile with -deprecation for details.
frilled.cs.indiana.edu%ls -ld Broad*DB.*
-rw-------   1 dgerman      2702 Nov  4 17:21 BroadwayDB.class
-rw-r--r--   1 dgerman      2593 Nov  4 17:20 BroadwayDB.java
-rw-r--r--   1 dgerman      2667 Nov  4 17:15 BroadwayDB.java~
frilled.cs.indiana.edu%
Here's the applet in action. (Do you see any flicker?)

Let's now redesign the interior of the applet.

frilled.cs.indiana.edu%pwd
/nfs/grouchy/home/user2/www/classes/a348-dger/t540/lectures/two
frilled.cs.indiana.edu%ls -ld Five.html Chicago.java
-rw-r--r--   1 dgerman      1757 Nov  4 18:20 Chicago.java
-rw-r--r--   1 dgerman       347 Nov  4 18:30 Five.html
frilled.cs.indiana.edu%cat Five.html
<html>
  <head><title>An Object-Oriented Boogie</title></head>
  <body bgcolor=white>

    This is the same Mondrian of before, only the inside is different. <p> 

    <applet code="Chicago.class" width=300 height=300>
    </applet>

    <p> Applet called Chicago, uses DancingRect, a standing still dancer. <p> 

  </body>
</html>

frilled.cs.indiana.edu%cat Chicago.java
import java.applet.*;
import java.awt.*;

// An Object-Oriented Boogie. 

public class Chicago extends Applet  {
    
    static final int NUM_RECTS = 9;       
    
    DancingRect r[]; // defined below 
    
    public void init() {

	System.out.println("Initializing... ");

	setBackground(Color.black);
	initRectangles();
    }
    
    public void initRectangles() {
	
	// allocate dancing rectangles
	
	r = new DancingRect[NUM_RECTS];
	
	r[0] = new DancingRect(0,0,90,90,Color.yellow);
	r[1] = new DancingRect(250,0,40,190,Color.yellow);
	r[2] = new DancingRect(200,55,60,135,Color.yellow);
	r[3] = new DancingRect(80,200,220,90,Color.blue);
	r[4] = new DancingRect(100,10,90,80,Color.blue);
	r[5] = new DancingRect(80,100,110,90,Color.lightGray);
	r[6] = new DancingRect(200,0,45,45,Color.red);
	r[7] = new DancingRect(0,100,70,200,Color.red);
	r[8] = new DancingRect(200,55,60,135,Color.magenta);
	
    }
    
    public void start() {
	
	System.out.println("Starting... ");
	
    }
    
    public void paint(Graphics g) {
	
	for (int i=0; i<NUM_RECTS; i++) {
	    r[i].paint(g);          // paint each rectangle
	}
    }
    
    public void stop() {
	
	System.out.println("Stopping... ");
	
    }
    
}

class DancingRect {
    
    int locx, locy;        
    // (locx,locyy) are coordinates of upper left corner of rectangle
    
    int width, height;     // width and height of rectangle
    Color myColor;         // color of rectangle
    
    public DancingRect(int x,int y,int w,int h,Color c) {
	locx = x;
	locy = y;
	width = w;
	height = h;
	myColor = c;
    }
    
    public void danceStep() {
	
	// does nothing
	
    }
    
    public void paint(Graphics g) {
	g.setColor(myColor);
	g.fillRect(locx,locy,width,height);
    }
    
}

frilled.cs.indiana.edu%javac Chicago.java
frilled.cs.indiana.edu%ls -ld Dan* Chi* Fiv*
-rw-r--r--   1 dgerman      1396 Nov  4 18:32 Chicago.class
-rw-r--r--   1 dgerman      1757 Nov  4 18:20 Chicago.java
-rw-------   1 dgerman       679 Nov  4 18:32 DancingRect.class
-rw-r--r--   1 dgerman       347 Nov  4 18:30 Five.html
frilled.cs.indiana.edu%
The output should be undistinguishable from the one of the previous version.

What follows is an exercise in another one of the fundamental aspects of Java.

We take a look at dynamic method binding. First, we add a few more classes.

frilled.cs.indiana.edu%pwd
/nfs/grouchy/home/user2/www/classes/a348-dger/t540/lectures/two
frilled.cs.indiana.edu%ls -ld *.java
-rw-------   1 dgerman      1225 Nov  4 19:09 BoogieRect.java
-rw-r--r--   1 dgerman      2169 Nov  4 16:37 Broadway.java
-rw-r--r--   1 dgerman      2593 Nov  4 17:20 BroadwayDB.java
-rw-r--r--   1 dgerman      1215 Nov  4 19:09 Chicago.java
-rw-------   1 dgerman       563 Nov  4 19:09 DancingRect.java
-rw-------   1 dgerman      2211 Nov  4 19:10 Simphony.java
-rw-------   1 dgerman       981 Nov  4 19:09 WaltzRect.java
frilled.cs.indiana.edu%
Indeed, we have taken DancingRect out of the Chicago source code, to keep each thing into its own.

frilled.cs.indiana.edu%cat BoogieRect.java
import java.awt.*;

class BoogieRect extends DancingRect {
    
    // BoogieRect inherits all instance variables and
    // methods from DancingRect
    
    static final byte UP = 0;   // direction of motion
    static final byte DOWN = 1;
    static final byte LEFT = 2;
    static final byte RIGHT = 3;
    
    byte state;                 // state of rectangle
    
    int max_x;                  // max x value
    int min_x;                  // min x value
    int max_y;                  // max y value
    int min_y;                  // min y value
    
    public BoogieRect(int x,int y,int w,int h,Color c) {

	super(x,y,w,h,c);         // call superclass constructor
	
	max_x = locx + 13;
	min_x = locx - 13;
	max_y = locy + 13;
	min_y = locy - 13;
	
    }
    
    // override danceStep()
    public void danceStep() {
	switch (state) {
	case DOWN:
	    locy += 2; 
	    if (locy >= max_y) {
		state = UP;
	    }
	    break;
	case UP:
	    locy -= 2;
	    if (locy <= min_y) {
		state = RIGHT;
	    }
	    break;
	case RIGHT:
	    locx += 2;
	    if (locx >= max_x) {
		state = LEFT;
	    }
	    break;
	case LEFT:
	    locx -= 2;   
	    if (locx <= min_x) {
		state = DOWN;
	    }
	    break;	    
	}
    }
}
frilled.cs.indiana.edu%
That was one new class, with its own danceStep.

Here's another class, taken directly from Strauss:

frilled.cs.indiana.edu%cat WaltzRect.java
import java.awt.*; 

class WaltzRect extends DancingRect {

    byte state;

    static final byte SE = 0; // going southeast
    static final byte NE = 1; // going northeast
    static final byte W = 2;  // going west
    
    int bottom_x; // the x coordinate of bottom point of the waltz
    int right_x;  // the x coordinate of right point of the waltz
    int left_x;   // the x coordinate of left point of the waltz
    
    public WaltzRect(int x,int y,int w,int h,Color c) {

	super(x,y,w,h,c);         // call superclass constructor

	bottom_x = locx + 17;
	right_x = bottom_x + 17;
	left_x = locx;

    }

    // override danceStep()
    public void danceStep() {
	switch (state) {
	case SE:
	    locx++;
	    locy++; 
	    if (locx == bottom_x) {
		state = NE;
	    }
	    break;
	case NE:
	    locx++;
	    locy--;
	    if (locx == right_x) {
		state = W;
	    }
	    break;
	case W:
	    locx-- ;
	    if (locx == left_x) {
		state = SE;
	    }
	    break;
	}
    }
}
frilled.cs.indiana.edu%
We now create a visual simphony, nothing less.

The picture look like that:

So here's the source code for the applet that puts everything together.

frilled.cs.indiana.edu%cat Simphony.java
import java.applet.*;
import java.awt.*;

public class Simphony extends Applet implements Runnable {
  
  Thread animation;
  
  Graphics offscreen;
  Image image;

  static final int NUM_RECTS = 9;          // in milliseconds
  static final int REFRESH_RATE = 100;     // in milliseconds 

  DancingRect r[];

  public void init() {

    System.out.println("Initializing... ");

    setBackground(Color.black);
    initRectangles();
    image = createImage(bounds().width,bounds().height);
    offscreen = image.getGraphics();
  }

  public void initRectangles() {

    // allocate dancing rectangles

    r = new DancingRect[NUM_RECTS];

    r[0] = new DancingRect(0,0,90,90,Color.yellow);
    r[1] = new BoogieRect(250,0,40,190,Color.yellow);
    r[2] = new WaltzRect(200,55,60,135,Color.yellow);
    r[3] = new BoogieRect(80,200,220,90,Color.blue);
    r[4] = new WaltzRect(100,10,90,80,Color.blue);
    r[5] = new BoogieRect(80,100,110,90,Color.lightGray);
    r[6] = new WaltzRect(200,0,45,45,Color.red);
    r[7] = new WaltzRect(0,100,70,200,Color.red);
    r[8] = new BoogieRect(200,55,60,135,Color.magenta);
  }

  public void start() {

    System.out.println("Starting... ");
    animation = new Thread(this);
     if (animation != null) {
       animation.start();
     }
  }

  // update each rectangle's position.
  // DYNAMIC METHOD BINDING OCCURS HERE!
  public void updateRectangles() {
    for (int i=0; i<NUM_RECTS; i++) {
      r[i].danceStep();      // each rectangles dance step

    }
  }

  // override update so it doesn't erase screen
  public void update(Graphics g) {
    paint(g);
  }

  public void paint(Graphics g) {

    offscreen.setColor(Color.black);
    offscreen.fillRect(0,0,300,300);  // clear buffer

    for (int i=0; i<NUM_RECTS; i++) {
      r[i].paint(offscreen);          // paint each rectangle
    }

    g.drawImage(image,0,0,this);
  }

  public void run() {
    while (true) {
      repaint();
      updateRectangles();
      try {
	Thread.sleep (REFRESH_RATE);
      } catch (Exception exc) { };
    }
  }

  public void stop() {
    System.out.println("Stopping... ");
    if (animation != null) {
      animation.stop();
      animation = null;
    }
  }
}
frilled.cs.indiana.edu%cat Six.html
<html>
  <head><title>A Visual Simphony, Nothing Less... </title></head>
  <body bgcolor=white>

    <p>A Visual Simphony, Nothing Else... <p>

    <applet code="Simphony.class" width=300 height=300>
    </applet>

    <p>Dedicated to Piet Mondrian and Victor Borge. <p> 

  </body>
</html>

frilled.cs.indiana.edu%javac Simphony.java
Note: Simphony.java uses or overrides a deprecated API.
Note: Recompile with -deprecation for details.
frilled.cs.indiana.edu%ls -ld *.class
-rw-r--r--   1 dgerman       837 Nov  4 19:10 BoogieRect.class
-rw-r--r--   1 dgerman      2173 Nov  4 17:02 Broadway.class
-rw-r--r--   1 dgerman      2702 Nov  4 17:21 BroadwayDB.class
-rw-r--r--   1 dgerman      1398 Nov  4 19:10 Chicago.class
-rw-r--r--   1 dgerman       683 Nov  4 19:10 DancingRect.class
-rw-r--r--   1 dgerman      2620 Nov  4 19:25 Simphony.class
-rw-r--r--   1 dgerman       746 Nov  4 19:10 WaltzRect.class
frilled.cs.indiana.edu%
And that should be the end of it. (Can you hear the music?)


Last updated: Nov 4, 2001 by Adrian German for A348/A548