Exercises - Subclasses

Some of the exercises below provide sample runs to better clarify what the program in question is supposed to do. In these sample runs, text given in green has been typed by the user, while white text has been output by the program. Additionally, the "$" symbol indicates the command prompt, while the "↵" symbol indicates the user has pressed the return key.

Exercises identified by the ACM logo (shown at left) require one or more of the following libraries:
acm.jar, acm.breadboards.jar, or acm.toys.jar.

  1. Make a class named SpinningOval that extends the GOval class of the ACM libraries and offers an update() instance method that modifies the oval drawn so that it appears to have turned by some number of degrees. In this way, the update() could be called repeatedly using a timer to create an animated effect of the oval spinning along its vertical axis. Create a class called SpinningOvalTest to demonstrate the nature of the SpinningOval class.

    import java.awt.Color;
    
    import acm.graphics.GOval;
    
    public class SpinningOval extends GOval { 
    	
    	double cx;
    	double cy;
    	double theta;
    	double degreesTurnedPerTick;
    
    	public SpinningOval(double x, double y, double radius) {
    		super(radius,radius);   
    		this.setFilled(true);
    		this.cx = x;
    		this.cy = y;
    		this.theta = 0;
    		this.degreesTurnedPerTick = 1;
    		this.setFillColor(Color.YELLOW);
    	}
    	
    	public void setTheta(double theta) {
    		this.theta = theta;
    	}
    	
    	public void setDegreesTurnedPerTick(double degrees) {
    		this.degreesTurnedPerTick = degrees;
    	}
    	
    	private double currentWidth() {
    		return this.getHeight()*Math.abs(Math.cos(theta));
    	}
    	
    	private boolean frontFaceShowing() {
    		return (-Math.PI/2 < theta && theta < Math.PI/2);
    	}
    	
    	public double centerX() {
    		return cx;
    	}
    	
    	public double centerY() {
    		return cy;
    	}
    	
    	public void update() {
    		theta += degreesTurnedPerTick * Math.PI / 180; 
    		this.setSize(this.currentWidth(),this.getHeight());
    		this.setLocation(this.cx, this.cy);  
    		this.setFillColor( this.frontFaceShowing() ? 
    		                   Color.YELLOW : Color.ORANGE);
    		if (theta > 3*Math.PI/2) {
    			theta = -Math.PI/2;
    		}
    	}
    	
    	public void setLocation(double x, double y) {
    		this.cx = x;
    		this.cy = y;
    		super.setLocation(this.cx - this.currentWidth()/2, 
    		                  this.cy - this.getHeight()/2);  
    	}
    	
    	public void move(double dx, double dy) {
    		this.cx += dx;
    		this.cy += dy;
    		this.setLocation(cx+dx,cy+dy);  
    	}
    }
    
    import java.util.Random;
    
    import acm.breadboards.OneButtonBreadboard;
    import acm.graphics.GOval;
    
    public class SpinningOvalTest extends OneButtonBreadboard {
    	
    	SpinningOval[][] coins;
    	GOval oval;
    	
    	public void run() {
    	  Random random = new Random();
    		
    	  coins = new SpinningOval[5][5];
    	  for (int i = 0; i < 5; i++) {
    	    for (int j = 0; j < 5; j++) {
    	      coins[i][j] = new SpinningOval(150+50*i,100+50*j,40);
    	      coins[i][j].setTheta(2*Math.PI * random.nextDouble());
    	      coins[i][j].setDegreesTurnedPerTick(1 + random.nextInt(3));
    	      this.add(coins[i][j]);
    	    }
    	  }
    
              this.getTimer().setDelay(5);
    	  this.getTimer().start();
    		
    	  SpinningOval c = new SpinningOval(50,30,50);
    	  c.setDegreesTurnedPerTick(3);
    	  oval = c;
    		
    	  this.add(oval);
    	}
    	
    	public void onTimerTick() {
    	  for (int i = 0; i < 5; i++) {
    	    for (int j = 0; j < 5; j++) {
    		coins[i][j].update();
    	    }
    	  }
    		
    	  oval.move(1,0);   
    	  ((SpinningOval) oval).update();
    		
    	  if (oval.getLocation().getX() > 500) {
    	    oval.setLocation(-50, 30);
    	  }
    	}
    }