CSC 012

Introduction to Computer Science

Summary of 12/4 Lecture

Review of the 11/20 Lab. Two-dimensional arrays; the gradebook example.   Recognizing two-dimensional arrays as one-dimensional arrays of one-dimensional arrays with dimensions x.length and x[row].length.  (9.2)

Review of the 11/20 Lab

We will review the 11/20 group-programming lab. Please pay careful attention to the discussion. You will have a new homework assigment based on this discussion.

The 11/20 lab had three problems:

Each animation had to be able to be resized when run with appletviewer. Each animation had to repeat without manual intervention.

The problems all involve animations, but differ in the required number of different images. The first problem needs only a few distinct images showing the same head with eyes in various states. The other two problems require a large number of distinct frames.

When there are only a few frames, one simple solution is to extend the buffered drawing approach we used to reduce flicker to allow for multiple Graphics objects, one per frame. We draw the frames and then run through them repeatedly, making a virtual flip-book.



//File:  FlipBook.java
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.applet.*;

public class FlipBook{

//Data Structures

private Image [] image_g;
public Graphics [] g;
public int curWidth=0, curHeight=0, curFrames=0;
private Graphics graphics;

// Constructor
public FlipBook( Graphics g_real, 
                        int width, int height, 
                        Applet a, int frames) {
  int i;
  
  image_g = new Image[frames];
  g = new Graphics[frames];
  for (i = 0 ; i < frames; i++ ){
    image_g[i] = a.createImage(width, height);
    g[i] = (image_g[i]).getGraphics();
  }
  graphics = g_real;
  curWidth = width;
  curHeight = height;
  curFrames = frames;
  
}


// Put out the buffered image
public void ShowPage(ImageObserver observer, int frame) {
     graphics.drawImage(image_g[frame],0,0,observer);
}

}

//File: BlinkApplet.java

/* This is a simple flip-book based drawing of a head
   with one eye shifting side-to-side and the other
   winking.
   
   H. J. Bernstein, 2 December 2001
   
 */
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.applet.Applet;
import FlipBook;

public class BlinkApplet extends Applet
{
// Global data
    FlipBook fb = null;

// Initialization
  public void init() {
      setBackground(Color.white);
  }


// paint method to do the actual drawing
  public void paint (Graphics g_real) {
    
    // declare variables
    Graphics g;       // a graphics object
    Dimension size    // dimensions of the window
        = getSize();
    int frame;        // the current frame
    int frames;       // the total number of frames
    int cx = size.width,
        cy = size.height,
        diameter = (int)(.8*Math.min(cx, cy));

    // If we have a 300 pixel image, we wil have 24 frames
    // At 90000 pixels per frame, this means 2,160,000
    // pixels.
     
    frames = diameter/10;
    
    // If the flip-book exists and is the right size
    // use the pages we already created.
    
    if (fb == null 
       || fb.curFrames != frames 
       || fb.curWidth != cx 
       || fb.curHeight != cy ) {
       
    fb = new FlipBook(g_real,cx,cy, this, frames);

    
    for (frame = 0; frame < frames; frame++) {

      g = fb.g[frame];

      // Draw hair
      g.setColor(Color.red);
      g.fillOval(cx/2-diameter/3,(cy-diameter)/2-diameter/12,
                2*diameter/3, diameter/3);
                
      // Draw the head itself

      g.setColor(Color.yellow);
      g.fillOval((cx-diameter)/2, (cy-diameter)/2,
                diameter, diameter);

      // Draw two ears

      g.fillOval((cx-diameter)/2-diameter/10,cy/2-diameter/8,
                diameter/5,diameter/4);
      g.fillOval((cx+diameter)/2-diameter/10,cy/2-diameter/8,
                diameter/5,diameter/4);

      // Draw brow line and smile

      g.setColor(Color.blue);
      g.drawArc((cx-3*diameter/4)/2,(cy-3*diameter/4)/2,
                 3*diameter/4,3*diameter/4, 60, 60);
      g.drawArc((cx-3*diameter/4)/2,(cy-3*diameter/4)/2,
                 3*diameter/4,3*diameter/4, -50, -80);

      // Now draw two eyes
      { int shift;
      
        shift = frame - frames/4;
        if (shift > frames/2) shift = 3*frames/4 - frame;
        
        g.setColor(Color.blue);
        
        // left side eye (actually the right eye of the head)
        // this eye will shift
        
        g.drawOval((cx-3*diameter/8-diameter/5)/2,
                 (cy-diameter/10)/2,
                 diameter/5,diameter/10);
        g.fillOval((cx-3*diameter/8-diameter/10)/2+shift,
                 (cy-diameter/10)/2,
                 diameter/10,diameter/10);
                 
        // right side eye
        // this eye will blink
       
        g.drawOval((cx+3*diameter/8-diameter/5)/2,
                 (cy-diameter/10)/2,
                 diameter/5,diameter/10);
        g.fillOval((cx+3*diameter/8-diameter/10)/2,
                 (cy-diameter/10)/2,
                 diameter/10,diameter/10);

        if (shift > -frames/6 && shift < frames/6) {
          // go to half closed
          g.setColor(Color.yellow);
          g.fillArc((cx+3*diameter/8-diameter/5)/2-1,
                 (cy-diameter/10)/2-1,
                 diameter/5+2,diameter/10+2, 0, 180);
          g.setColor(Color.blue);
          if (shift > -frames/8 && shift < frames/8 ) {
            // go to fully closed
            g.setColor(Color.yellow);
            g.fillArc((cx+3*diameter/8-diameter/5)/2-1,
                 (cy-diameter/10)/2,
                 diameter/5+2,diameter/10-1, 0, -180);
            g.setColor(Color.blue);
            g.drawArc((cx+3*diameter/8-diameter/5)/2-1,
                 (cy-diameter/10)/2,
                 diameter/5+2,diameter/10-1, 0, -180);
          } else {
            g.drawLine((cx+3*diameter/8-diameter/5)/2,cy/2,
                 (cx+3*diameter/8+diameter/5)/2,cy/2);
          }
        }
        
      }
    }
    }
    
    // display the entire flip-book
    for (frame = 0; frame < frames; frame++) {

      g = fb.g[frame];
 
      fb.ShowPage(this,frame);
      try {
        Thread.sleep(50);               // sleep for 50 msec
      } catch (InterruptedException t){}
    }
   try {
      Thread.sleep(1000);              // sleep or 1 sec
    } catch (InterruptedException t) {}
    repaint();
  }

}



For the other two problems, we need to be more careful in using memory. Each 300 by 300 pixel frame would need room for 90,000 pixels. If we use 200 frames, we would need 18,000,000 pixels at several bytes each. In such a case, it is more efficient to do our animation frames as lists of objects to be drawn, rather than arrays of pixels. If we do each frame's object list as an array, we need an array of arrays, i.e. a two dimensional array. We define a DisplayObjectList class:




//File: DrawingObjectList.java
//Uses: FlipBook.java

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.applet.*;
import FlipBook;

public class DrawingObjectList{

//Data Structures

private int [] [] dol;
private FlipBook fb;
private Graphics g;
public int curWidth=0, curHeight=0, curFrames=0;
public int curFrame = 0;

private static final int DOL_InitialSize = 100;
private static final int DOL_SetColor = 1;
private static final int DOL_DrawLine = 2;
private static final int DOL_DrawOval = 3;
private static final int DOL_FillOval = 4;
private static final int DOL_DrawArc  = 5;
private static final int DOL_FillArc  = 6;
private static final int DOL_DrawRect = 7;
private static final int DOL_FillRect = 8;



// Constructor
public DrawingObjectList( Graphics g_real, 
                        int width, int height, 
                        Applet a, int frames) {
  int i;
  
  fb = new FlipBook(g_real, width ,height, a, 1);
  dol = new int[frames][DOL_InitialSize];
  for (i=0; i<frames; i++) {
    dol[i][0] = 0;
  }
  curWidth = width;
  curHeight = height;
  curFrames = frames;
}

//Add an object to a frame

private void addDO(int[] obj, int fr) {
  int i, j;

  if ( dol[fr][0] + obj.length > dol[fr].length -1 ) {
    int old [];
    
    j = dol[fr].length;
    old = dol[fr];
    dol[fr] = new int[2*j];
    for (i=0; i <= old[0]; i++) {
      dol[fr][i] = old[i];
    }
  }
  
  j = dol[fr][0];
  for (i = 0; i < obj.length; i ++ ) {
    dol[fr][++j] = obj[i];
  }
  dol[fr][0] = j;
  
}

// Set a frame

public void setFrame( int frame ) {
  curFrame = frame;
}

// Set a color in the current frame
public void setColor( Color c ) {
  int [] cobj;
  cobj = new int [5];
  cobj[0] = DOL_SetColor;
  cobj[1] = c.getRed();
  cobj[2] = c.getGreen();
  cobj[3] = c.getBlue();
  /* cobj[4] = c.getAlpha(); */
  addDO(cobj,curFrame);
}

// Set a color in a particular frame
// and update the current frame to that frame
public void setColor( Color c, int frame ) {
  setFrame(frame);
  setColor( c);
}

// Set a color in a range of frames
// and update the current frame to the last frame
public void setColor( Color c, int framelo, int framehi ) {
  int i;
  for (i = framelo; i <=framehi; i++) {
    setFrame( i);
    setColor( c);
  }
}

// Draw a line in the current frame
public void drawLine( int x1, int y1, int x2, int y2 ) {
  int [] lobj;
  lobj = new int [5];
  lobj[0] = DOL_DrawLine;
  lobj[1] = x1;
  lobj[2] = y1;
  lobj[3] = x2;
  lobj[4] = y2;
  addDO(lobj,curFrame);
}

// Draw a line in a particular frame
// and update the current frame to that frame
public void drawLine( int x1, int y1, int x2, int y2,
                      int frame ) {
  setFrame(frame);
  drawLine(x1, y1, x2, y2);
}

// Draw a line in a range of frames
// and update the current frame to the last frame
public void drawLine( int x1, int y1, int x2, int y2, 
                      int framelo, int framehi ) {
  int i;
  for (i = framelo; i <=framehi; i++) {
    setFrame( i);
    drawLine(x1, y1, x2, y2);
  }
}

// Draw an oval in the current frame
public void drawOval( int x, int y, int xw, int yw ) {
  int [] oobj;
  oobj = new int [5];
  oobj[0] = DOL_DrawOval;
  oobj[1] = x;
  oobj[2] = y;
  oobj[3] = xw;
  oobj[4] = yw;
  addDO(oobj,curFrame);
}

// Draw an oval in a particular frame
// and update the current frame to that frame
public void drawOval( int x, int y, int xw, int yw, 
                      int frame ) {
  setFrame(frame);
  drawOval(x, y, xw, yw);
}

// Draw an oval in a range of frames
// and update the current frame to the last frame
public void drawOval( int x, int y, int xw, int yw, 
                      int framelo, int framehi ) {
  int i;
  for (i = framelo; i <=framehi; i++) {
    setFrame( i);
    drawOval(x, y, xw, yw);
  }
}

// Fill an oval in the current frame
public void fillOval( int x, int y, int xw, int yw ) {
  int [] oobj;
  oobj = new int [5];
  oobj[0] = DOL_FillOval;
  oobj[1] = x;
  oobj[2] = y;
  oobj[3] = xw;
  oobj[4] = yw;
  addDO(oobj,curFrame);
}

// Fill an oval in a particular frame
// and update the current frame to that frame
public void fillOval( int x, int y, int xw, int yw,
                      int frame ) {
  setFrame(frame);
  fillOval(x, y, xw, yw);
}

// Fill an oval in a range of frames
// and update the current frame to the last frame
public void fillOval( int x, int y, int xw, int yw, 
                      int framelo, int framehi ) {
  int i;
  for (i = framelo; i <=framehi; i++) {
    setFrame( i);
    fillOval(x, y, xw, yw);
  }
}


// Draw an arc in the current frame
public void drawArc( int x, int y, int xw, int yw,
                     int alo, int deg ) {
  int [] aobj;
  aobj = new int [7];
  aobj[0] = DOL_DrawArc;
  aobj[1] = x;
  aobj[2] = y;
  aobj[3] = xw;
  aobj[4] = yw;
  aobj[5] = alo;
  aobj[6] = deg;
  addDO(aobj,curFrame);
}

// Draw an arc in a particular frame
// and update the current frame to that frame
public void drawArc( int x, int y, int xw, int yw, 
                     int alo, int deg, int frame ) {
  setFrame(frame);
  drawArc(x, y, xw, yw, alo, deg);
}

// Draw an arc in a range of frames
// and update the current frame to the last frame
public void drawArc( int x, int y, int xw, int yw, 
                      int alo, int deg,  
                      int framelo, int framehi ) {
  int i;
  for (i = framelo; i <= framehi; i++) {
    setFrame( i);
    drawArc(x, y, xw, yw, alo, deg);
  }
}


// Fill an arc in the current frame
public void fillArc( int x, int y, int xw, int yw,
                     int alo, int deg ) {
  int [] aobj;
  aobj = new int [7];
  aobj[0] = DOL_FillArc;
  aobj[1] = x;
  aobj[2] = y;
  aobj[3] = xw;
  aobj[4] = yw;
  aobj[5] = alo;
  aobj[6] = deg;
  addDO(aobj,curFrame);
}

// Fill an arc in a particular frame
// and update the current frame to that frame
public void fillArc( int x, int y, int xw, int yw, 
                     int alo, int deg, int frame ) {
  setFrame(frame);
  fillArc(x, y, xw, yw, alo, deg);
}

// Fill an arc in a range of frames
// and update the current frame to the last frame
public void fillArc( int x, int y, int xw, int yw, 
                      int alo, int deg,  
                      int framelo, int framehi ) {
  int i;
  for (i = framelo; i <= framehi; i++) {
    setFrame( i);
    fillArc(x, y, xw, yw, alo, deg);
  }
}

// Draw a rectangle in the current frame
public void drawRect( int x, int y, int xw, int yw ) {
  int [] robj;
  robj = new int [5];
  robj[0] = DOL_DrawRect;
  robj[1] = x;
  robj[2] = y;
  robj[3] = xw;
  robj[4] = yw;
  addDO(robj,curFrame);
}

// Draw a rectangle in a particular frame
// and update the current frame to that frame
public void drawRect( int x, int y, int xw, int yw,
                      int frame ) {
  setFrame(frame);
  drawRect(x, y, xw, yw);
}

// Draw a rectangle in a range of frames
// and update the current frame to the last frame
public void drawRect( int x, int y, int xw, int yw, 
                      int framelo, int framehi ) {
  int i;
  for (i = framelo; i <=framehi; i++) {
    setFrame( i);
    drawRect(x, y, xw, yw);
  }
}


// Fill a rectangle in the current frame
public void fillRect( int x, int y, int xw, int yw ) {
  int [] robj;
  robj = new int [5];
  robj[0] = DOL_FillRect;
  robj[1] = x;
  robj[2] = y;
  robj[3] = xw;
  robj[4] = yw;
  addDO(robj,curFrame);
}

// Fill a rectangle in a particular frame
// and update the current frame to that frame
public void fillRect( int x, int y, int xw, int yw,
                      int frame ) {
  setFrame(frame);
  fillRect(x, y, xw, yw);
}

// Fill a rectangle in a range of frames
// and update the current frame to the last frame
public void fillRect( int x, int y, int xw, int yw, 
                      int framelo, int framehi ) {
  int i;
  for (i = framelo; i <=framehi; i++) {
    setFrame( i);
    fillRect(x, y, xw, yw);
  }
}


// Put out the buffered image
public void ShowPage(ImageObserver observer, int frame) {
  int i=1;
  g = fb.g[0];
  g.setColor(Color.white);
  g.fillRect(0,0,curWidth-1,curHeight-1);
  while (i <= dol[frame][0] ) {
    switch (dol[frame][i]) {

      case DOL_SetColor:
        g.setColor(new Color(dol[frame][i+1],
                             dol[frame][i+2],
                             dol[frame][i+3] 
                         /* ,dol[frame][i+4] */));
        i += 5;        
        break;

      case DOL_DrawLine:
        g.drawLine(dol[frame][i+1], dol[frame][i+2],
                   dol[frame][i+3], dol[frame][i+4]);
        i += 5;        
        break;


      case DOL_DrawOval:
        g.drawOval(dol[frame][i+1], dol[frame][i+2],
                   dol[frame][i+3], dol[frame][i+4]);
        i += 5;        
        break;

      case DOL_FillOval:
        g.fillOval(dol[frame][i+1], dol[frame][i+2],
                   dol[frame][i+3], dol[frame][i+4]);
        i += 5;        
        break;


      case DOL_DrawArc:
        g.drawArc(dol[frame][i+1], dol[frame][i+2],
                  dol[frame][i+3], dol[frame][i+4],
                  dol[frame][i+5], dol[frame][i+6]);
        i += 7;        
        break;

      case DOL_FillArc:
        g.fillArc(dol[frame][i+1], dol[frame][i+2],
                  dol[frame][i+3], dol[frame][i+4],
                  dol[frame][i+5], dol[frame][i+6]);
        i += 7;        
        break;

      case DOL_DrawRect:
        g.drawRect(dol[frame][i+1], dol[frame][i+2],
                   dol[frame][i+3], dol[frame][i+4]);
        i += 5;        
        break;

      case DOL_FillRect:
        g.fillRect(dol[frame][i+1], dol[frame][i+2],
                   dol[frame][i+3], dol[frame][i+4]);
        i += 5;        
        break;



    }
  }

  fb.ShowPage(observer, 0);
}

}



The DisplayObjectList class provides a reasonable basis for the last two animations.

Drawing a Train

We make a crude train which puts out puffs of smoke out of simple, colorful shapes. We draw the track in all the frame object lists eactly the same way, but the train itself needs to be shifted to the right for each frame, and the puffs of smoke need to rise.



//File: TrainApplet.java
//Uses: DrawingObjectList.java
//Uses: FlipBook.java

/* This is a simple object-list and flip-book based
   drawing of train moving along a track
   making puffs of smoke.
   
   H. J. Bernstein, 2 December 2001
   
 */
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.applet.Applet;
import DrawingObjectList;

public class TrainApplet extends Applet
{
// Global data
    DrawingObjectList dol = null;

// Initialization
  public void init() {
    dol = null;
  }


// paint method to to the actual drawing
  public void paint (Graphics g_real) {
    
    // declare variables
    Graphics g;       // a graphics object
    Dimension size    // dimensions of the window
        = getSize();
    int frame;        // the current frame
    int frames;       // the total number of frames
    int cx = size.width,
        cy = size.height,
        diameter = (int)(.8*Math.min(cx, cy));
    int ii;

     
    frames = (int)(cx*1.6);
    
    // If the DOL exists and is the right size
    // use the pages we already created.
    
    if (dol == null 
       || dol.curFrames != frames 
       || dol.curWidth != cx 
       || dol.curHeight != cy ) {
       
      dol = new DrawingObjectList(g_real,cx,cy, this, frames);

      // draw the rails
      dol.setColor(Color.black,0,frames-1);
      dol.fillRect(0,cy-diameter/20,cx-1,diameter/40,0,frames-1);
      dol.fillRect(0,cy-diameter/20-diameter/8,cx-1,diameter/40,0,frames-1);
      
      for(ii=0; ii< cx-diameter/12; ii += diameter/12) {
        dol.drawLine(ii,cy-diameter/20,
                   ii+diameter/12,cy-diameter/20-diameter/8,
                   0, frames-1);
      }
    
      for (frame = 0; frame < frames; frame++) {
        dol.setFrame(frame);
        
        //Draw the back wheels
        dol.setColor(Color.blue);
        dol.fillOval(frame+diameter/12,
          cy-diameter/20-diameter/5-diameter/8,diameter/5,diameter/5);
        dol.fillOval(frame+diameter/12-diameter/6,
          cy-diameter/20-diameter/5-diameter/8,diameter/5,diameter/5);
        dol.fillOval(frame+diameter/12-3*diameter/6,
          cy-diameter/20-diameter/4-diameter/8,diameter/4,diameter/4);
       
        // Draw the boiler
        dol.setColor(Color.red);
        dol.fillRect(frame-3*diameter/6,
          cy-diameter/10-3*diameter/8,3*diameter/4,diameter/4);

        // Draw the cab
        dol.setColor(Color.red);
        dol.fillRect(frame-3*diameter/6,
          cy-diameter/10-diameter/2,diameter/4,diameter/4);
          
        // Every 40th frame, emit a puff of smoke, rising in
        // following frames
        if ( frame%40 == 0 ) {
          for (ii = frame; ii < frames; ii++) {
            int irise;
            dol.setFrame(ii);
            dol.setColor(Color.gray);
            irise = (int)Math.sqrt(25.0*(ii-frame))+diameter/16;
            dol.fillOval(frame-3*diameter/6+5*diameter/8,
              cy-diameter/10-diameter/2-irise,
              diameter/8,diameter/8);
          }
          dol.setFrame(frame);
        }

        // Draw the smoke stack
        dol.setColor(Color.green);
        dol.fillRect(frame-3*diameter/6+5*diameter/8,
          cy-diameter/10-diameter/2,diameter/8,diameter/8);


        // Draw the front wheels
        dol.setColor(Color.blue);
        dol.fillOval(frame,
           cy-diameter/20-diameter/5,diameter/5,diameter/5);
        dol.fillOval(frame-diameter/6,
           cy-diameter/20-diameter/5,diameter/5,diameter/5);
        dol.fillOval(frame-3*diameter/6,
           cy-diameter/20-diameter/4,diameter/4,diameter/4);
        
      }

    } 
    
    // display the entire set of frames
    for (frame = 0; frame < frames; frame++) {

 
      dol.ShowPage(this,frame);
      try {
        Thread.sleep(20);               // sleep for 20 msec
      } catch (InterruptedException t){}
    }
   try {
      Thread.sleep(1000);              // sleep or 1 sec
    } catch (InterruptedException t) {}
    repaint();
  }

}



Drawing a Spiral

A spiral is a path that winds around a fixed center moving progressively further inwards or outwards. We desribe it as a "parametric curve". We give the x and y coordinates of points on the spiral as functions of a parameter, t, which we think of as time. Let us start with the so-called logarithmic spiral, which, like the shell of a nautilus, repeats the same shape as the spiral grows, holding a constant angle between a tangent to the curve and a line to the center. We give the curve in terms of two constants u and s. The constant s gives the "speed" in radians of rotation per unit time. The constant u determines the angle of the tangent grows:

x(t) = r*cos(s*t)*exp(u*s*t)
y(t) = r*sin(s*t)*exp(u*s*t)

This spiral starts at radius r, when t=0.

The logarithmic sprial grow rapidly. We can form a spiral which grows more slowly, like the groves of a record, by making the radius of the spiral proportional to the angle of rotation around the center:

x(t) = p*cos(s*t)*s*t/(2*PI)
y(t) = p*sin(s*t)*s*t/(2*PI)

This sprial starts at the center and grows by p, the "pitch" for each rotation.

We can also make spirals by piecing together portions of circles or short straight lines.

We choose to make a spiral that grows at a constant pitch.



//File: SpiralApplet.java

/* This is a simple object-list and flip-book based
   drawing of a ball following a spiral curve from the
   outside to the center of an image.
   
   H. J. Bernstein, 2 December 2001
   
 */
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.applet.Applet;
import DrawingObjectList;

public class SpiralApplet extends Applet
{
// Global data
    DrawingObjectList dol = null;

// Initialization
  public void init() {
    dol = null;
  }


// paint method to to the actual drawing
  public void paint (Graphics g_real) {
    
    // declare variables
    Graphics g;       // a graphics object
    Dimension size    // dimensions of the window
        = getSize();
    int frame;        // the current frame
    int frames;       // the total number of frames
    int cx = size.width,
        cy = size.height,
        diameter = (int)(.8*Math.min(cx, cy));

     
    frames = 36*5;
    
    // If the DOL exists and is the right size
    // use the pages we already created.
    
    if (dol == null 
       || dol.curFrames != frames 
       || dol.curWidth != cx 
       || dol.curHeight != cy ) {
       
      dol = new DrawingObjectList(g_real,cx,cy, this, frames);

    
      for (frame = 0; frame < frames; frame++) {
    
        int i,xprev,yprev,x,y;

        dol.setFrame(frame);
      
        xprev = cx/2; yprev = cy/2;
      
        dol.setColor(Color.green);
        for (i = 0; i < frames-frame; i++) {
          x = (int)(cx/2 + Math.cos(.17*(double)(i))
                 *(double)(i*diameter)/(frames*2.));
          y = (int)(cy/2 + Math.sin(.17*(double)(i))
                 *(double)(i*diameter)/(frames*2.));
          dol.drawLine(xprev,yprev,x,y);
          xprev = x;
          yprev = y;      
        }
        dol.setColor(new Color((255*(frames-frame))/frames,0,
                               (255*frame)/frames));
        dol.fillOval(xprev-diameter/20,yprev-diameter/20,
                     diameter/10, diameter/10);
      }
    } 
    
    // display the entire set of frames
    for (frame = 0; frame < frames; frame++) {

 
      dol.ShowPage(this,frame);
      try {
        Thread.sleep(20);               // sleep for 20 msec
      } catch (InterruptedException t){}
    }
   try {
      Thread.sleep(1000);              // sleep or 1 sec
    } catch (InterruptedException t) {}
    repaint();
  }

}



Review of Gradebook Lab

The last time we discussed the gradebook, we had arrived at two class descriptions (GradeBook and Student). The gradebook consisted of multiple student records. Let us look at what happens if we are not careful to create the objects (instances of the classes) that we need as we need them.

//File:    GradeBook0.java
import iostuff.*;
class GradeBook0
{
    Student0 [] students;
    int size = 0;

    public GradeBook0 (int number)
    {
        students = new Student [number];
    }

    public void readAndAverage()
    {
        // Read names and exam grades; size counts inputs
        String nextname;

        while (true)
        {
            System.out.print("Enter name and 2 scores (CTL-Z to End)");
            nextname = Keyboard.readString();
           
            if (Keyboard.eof())
            {
                break;
            }
           
            students [size] . setName (nextname);
           
            for (int k=1; k<=2; k++)
            {
                students [size].setExam (k, Keyboard.readInt());
            }
            size++;
        }

        System.out.println();
        System.out.println();
        System.out.println("\tName\tAverage");
        for (int i=0; i<size; i++)
        {
            System.out.println("\t" + students [i] . getName() +
                        "\t" + students [i] . getAvg());
        }
    }
}

//File:    Student0.java
import iostuff.*;
class Student0
{
    //Data fields
    private String name;
    private int [] exam;

    //Constructor
    public Student0 ()
    {
    }
   
    //Accessor methods
    public String getName ()
    {
        return name;
    }

    public int getExam (int which)
    {
        return exam [which-1];
    }
       
    public int getAvg ()
    {
        return (exam[0]+exam[1])/2;
    }

    //Mutator methods
    public void setName (String s)
    {
        name = s;
    }
   
    public void setExam (int which, int s)
    {
        exam [which-1] = s;
    }   
}

The driver program was simply

//File:    ReadAndAvg.java
import iostuff.*;
class ReadAndAvg
{
    static final int INPUT_MAX = 100;

    public static void main (String[] args)
    {
        GradeBook0 grades = new GradeBook0 (INPUT_MAX);
        grades . readAndAverage();
    }
}

The output from this program is:

Enter name and 2 scores (CTL-Z to End)Bill
Exception in thread "main" java.lang.NullPointerException
at GradeBook.readAndAverage(GradeBook.java, Compiled Code)
at ReadAndAvg.main(ReadAndAvg.java:10)

The problem stems from the GradeBook class.  If you look closely at the while loop, you'll see that there are no instantiations of the Student class.   That is, we must create a Student object each time the user presents a new name.   The code in question is modified as follows:

//File:    GradeBook.java
import iostuff.*;
class GradeBook
{
        ...

        while (true)
        {
            System.out.print("Enter name and 2 scores (CTL-Z to End)");
            nextname = Keyboard.readString();
           
            if (Keyboard.eof())
            {
                break;
            }
           
            students [size] = new Student0 ();
            students [size] . setName (nextname);
           
            for (int k=1; k<=2; k++)
            {
                students [size].setExam (k, Keyboard.readInt());
            }
            size++;
        }

        ...
    }
}

Rerunning the program, we get the following:

Enter name and 2 scores (CTL-Z to End)Bill
96
Exception in thread "main" java.lang.NullPointerException:
at Student.setExam(Student.java:38)
at GradeBook.readAndAverage(GradeBook.java, Compiled Code)
at ReadAndAvg.main(ReadAndAvg.java:10)


We got as far as entering the first of the two student exam scores before encountering the same "NullPointerException".  The cause is similar to that of the first version.  This time, however, the problem lies in the Student class.  If you look carefully at the code (above) for the Student class, you'll see that nowhere is the exam data field instantiated.  Memory is never allocated for the exam scores.   We can remedy this by adding the necessary code to the Student class as follows:

//File:    Student.java
import iostuff.*;
class Student
{
    //Data fields
    private String name;
    private int [] exam = new int [10]; //allows as many as 10 quiz scores per student

    //Constructor
    public Student ()
    {
    }

        ...

}

Now, after updating the GradeBook class to reflect the change from Student0 to Student, the output is as follows:

Enter name and 2 scores (CTL-Z to End)Bill
90
80
Enter name and 2 scores (CTL-Z to End)Joe
10
20
Enter name and 2 scores (CTL-Z to End)
Name Average
Bill      85
Joe     15

Nested Loops

Example.  Write a program that creates 9 rows and 9 columns of asterisks (*).

//File:    FunAndGames0.java
public class FunAndGames0
{
    public static void main (String [] args)
    {
        System.out.println ("**********");
        System.out.println ("**********");
        System.out.println ("**********");
        System.out.println ("**********");
        System.out.println ("**********");
        System.out.println ("**********");
        System.out.println ("**********");
        System.out.println ("**********");
        System.out.println ("**********");
        System.out.println ("**********");
    }
}

The following outputs results.

**********
**********
**********
**********
**********
**********
**********
**********
**********
**********

Although the program works as desired, it is, I think you would agree, rather trite.  A somewhat more interesting approach would be to utilize a nested loop as follows.

//File:    FunAndGames1.java
public class FunAndGames1
{
    public static void main (String [] args)
    {
        for (int row=0; row < 10; row++)    //<----
        {
            //Print a line of *'s
            for (int col = 0; col < 10; col++)
            {
                System.out.print ("*");
            }
            System.out.println(); //go to next line
        }
    }
}

Suppose we want to create a triangle of *'s as illustrated below

*
**
***
****
*****
******
*******
********
*********
**********

How about

//File:    Triangle.java
public class Triangle
{
    public static void main (String [] args)
    {
        for (int row=0; row < 10; row++)
        {
            //Print a line of *'s
            for (int col = 0; col < row+1; col++)    //<-----
            {
                System.out.print ("*");
            }
            System.out.println(); //go to next line
        }
    }
}

Finally, we attempted to create the following:

******
*****
****
***
**
*
**
***
****
*****
******

After some trial and error, you'll find the following does the trick:

//File:    Vee.java
public class Vee
{
    public static void main (String [] args)
    {
        for (int row=0; row < 11; row++)
        {
            //Print a line of *'s
            for (int col = 0; col <= Math.abs (row-5); col++)
            {
                System.out.print ("*");
            }
            System.out.println(); //go to next line
        }
    }
}


Lab Exercise 1.  (p. 279/#2)  Write an application that prints a "sawtooth" using asterisks:

*
**
***
****
*****
*
**
***
****
*****
*
**
***
****
*****

The program should read from the Keyboard two integers, telling (1) how wide to make each triangle and (2) how many triangles to print.  Give it a try.   If you need inspiration, feel free to check Prof. Steinmetz's solution this one (Teeth.java).


Lab Exercise 2.  (p 280/#5)  To assist a yong friend learning to multiply, write an application to print the following multiplication table:

                    MULTIPLICATION TABLE

1     2     3     4      5     6     7      8     9     10      11     12
2     4     6     8      10   12   14  16   18     20     22     24
3     6    
4     8    
5     10   
6     12    
7     14                                  etc
8     16    
9     18   
10    20    
11    22   
12    24


Back to CSC 012 Home Page