Chris Pollett>Old Classes>PIC 20, Winter 2000>Hw6>Hw6 Solutions

Winter 2000 PIC 20 HW6 Solutions Page

// Program Name: AqFrame.java
//
// Purpose: This applications displays an Aquarium. A menu bar is
//	    used to add fish, seaweed to the auqarium. There is
//          also an option to clear the aquarium and to play sound.
//
// Known Bugs: The factorization of the classes AqComponent, Fish, Seaweed
//             should be improved.
//
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.net.*;

public class AqFrame extends JFrame 
{
	static final int FISH=0,SEAWEED =1; //internal constants
				// used to keep track of type of
				// an AqComponent. Would be better
				// to use instanceof but forgot to 
				// mention in class.
	Container c;
	AudioClip fishMusic;
	JMenuBar bar;
	JMenu optionsMenu, newMenu;
	JMenuItem  fishItem,seaweedItem,clearItem;
	JCheckBoxMenuItem soundItem;
	Aquarium aq;
	MyTimer redrawTimer;
	ImageIcon fish[],seaweed[];
	JApplet applet;	

	// Method Name: AqFrame
	//
	// Purpose: creates an instance of AqFrame, loads in sounds,
	//          sets up menus, creates the Aquarium, 
	//	    starts movement timer for things in the Aquarium
	//	    loads in the different fish and seaweed images. 
	//     
	public AqFrame() 
	{

		super("AqFrame");

		try{
		   fishMusic =
			Applet.newAudioClip(
			new
	URL("http://www.math.ucla.edu/~cpollett/20.1.00w/sparcle.wav"));
		}
 		catch(MalformedURLException e)
		{
			System.err.println("no music");
		}


		setUpMenus();

		c=getContentPane();
		c.setLayout(new BorderLayout());

		aq = new Aquarium();	
		c.add(aq);

		redrawTimer = new MyTimer(100,new ActionListener()
			{
				public void actionPerformed(ActionEvent e)
				{
					aq.move();
					c.repaint();
				}
			}
		);
		redrawTimer.start();

		setUpFishSeaweed();
	
		setSize(aq.getPreferredSize());
		show();

	}

	// Method Name: setUpMenus
	//
	// Purpose: set up the menubar, menus, and their items
	//	    for the AqFrame. Also add their Listeners
	//     
	void setUpMenus()
	{
		fishItem =new JMenuItem("Fish");
		fishItem.setMnemonic('F');
		fishItem.addActionListener(new AqListener(FISH));

		seaweedItem =new JMenuItem("Seaweed");
		seaweedItem.setMnemonic('S');
		seaweedItem.addActionListener(new AqListener(SEAWEED));

		soundItem =new JCheckBoxMenuItem("Sound");
		soundItem.setMnemonic('d');
		soundItem.addActionListener(new ActionListener()
			{ public void actionPerformed(ActionEvent e)
				{
				if(soundItem.isSelected())
					fishMusic.loop();
				
				else fishMusic.stop();
						
				}			
			}
		);

		clearItem =new JMenuItem("Clear");
		clearItem.setMnemonic('C');
		clearItem.addActionListener(new ActionListener()
			{ public void actionPerformed(ActionEvent e)
				{
					aq.clear();		
	 	 	 	}			
			}
		);

		newMenu =new JMenu("New");
		newMenu.setMnemonic('N');
		newMenu.add(fishItem);
		newMenu.add(seaweedItem);

		optionsMenu =new JMenu("Options");
		optionsMenu.setMnemonic('O');
		optionsMenu.add(newMenu);
		optionsMenu.addSeparator();
		optionsMenu.add(clearItem);
		optionsMenu.add(soundItem);

		bar= new JMenuBar();
		bar.add(optionsMenu);


		
		setJMenuBar(bar);
	}

	// Method Name: setUpFishSeaweed
	//
	// Purpose: instantiates the ImageIcons for the
	//	    kinds of fish and seaweed available in the aquarium
	//     
	public void setUpFishSeaweed()
	{
		fish = new ImageIcon[3];
		seaweed = new ImageIcon[3];

		fish[0] =new ImageIcon("fish0.gif");
		fish[1] =new ImageIcon("fish1.gif");
		fish[2] =new ImageIcon("fish2.gif");

		seaweed[0] =new ImageIcon("seaweed0.gif");
		seaweed[1] =new ImageIcon("seaweed1.gif");
		seaweed[2] =new ImageIcon("seaweed2.gif");
	}

	// Method Name: main
	//
	// Purpose: create an instance of AqFrame and add a
	//	    window listener. Might be better to put
	//	    this in a wrapper class.
	//     
	public static void main (String args[])
	{
		AqFrame app = new AqFrame();

		app.addWindowListener(
			new WindowAdapter(){
			public void windowClosing ( WindowEvent e)
				{
					System.exit(0);
				}
			}
		);
	
	}

	// Class Name: AqListener
	//
	// Purpose: listener attached to Fish and Seaweed MenuItems
	//          It creates a AqDialog window to allow the
	//	    user to add this AqComponent
	//     
	class AqListener implements ActionListener
	{
		int type;

		public AqListener(int t)
		{type =t;}

		public void actionPerformed(ActionEvent e)
		{
			AqDialog ad =new AqDialog(type);

		}
	}

	// Class Name: AqDialog
	//
	// Purpose: class used to create dialog window used to add
	//	    AqComponents to aquarium
	//    
	class AqDialog extends JFrame
	{
		Container c;

		String names[]; //names of images of AqComponent for JList
		JList imList; //images for AqComponent

		JSlider xSlider,ySlider,scaleSlider,xVelSlider;
		JSlider yVelSlider;
		int type;

		// Class Name: AqDialog
		//
		// Purpose: create an instance of AqDialog window
		// sets up the sliders for fish or seaweed subcases
		// and set up JList of available images. Also
		// set up Create and Cancel buttons and their listeners
		//    
		public AqDialog(int t)
		{
			super("AqComponent Dialog");
			type=t;
			
			c = getContentPane();
			
			switch(type) 
			{	
				case FISH:
				   c.setLayout(new GridLayout(6,1,5,5));
				   names =new String[fish.length];
				   for(int i=0; i<fish.length; i++)
					{names[i]="fish"+i;}	
				break;
	
				case SEAWEED:
				   c.setLayout(new GridLayout(5,1,5,5));
				   names =new String[seaweed.length];
				   for(int i=0; i<seaweed.length; i++)
					{names[i]="seaweed"+i;}
				break;

				default:
				   names = new String[1];
				   names[0] = "default";	
			}
			
			imList =new JList(names);
			imList.setVisibleRowCount(2);
			imList.setSelectedIndex(0);
			imList.setSelectionMode(
				ListSelectionModel.SINGLE_SELECTION);

			JPanel listPanel = new JPanel();
			listPanel.setLayout(new FlowLayout
					(FlowLayout.CENTER));

			listPanel.add(new JScrollPane(imList));

			c.add(listPanel);

			xSlider=addSlider("X Location:");

			
			switch(type)
			{
				case FISH:
				  ySlider=addSlider("Y Location: ");
			          xVelSlider=addSlider("X Velocity:" );
				  yVelSlider=addSlider("Y Velocity:");
				break;
			
				case SEAWEED:
				  scaleSlider=addSlider("Seaweed Size:"); 
				break; 
			}
			
			JButton cancel = new JButton("Cancel");
			cancel.addActionListener( new ActionListener()
				{ public void actionPerformed(ActionEvent e)
					{
						dispose();
					}
				}
			);


			JButton create = new JButton("Create");
			create.addActionListener( new ActionListener()
				{ public void actionPerformed(ActionEvent e)
					{
						switch(type)		
	 					{
						case AqFrame.FISH:
						  aq.add( new Fish(
				               fish[imList.getSelectedIndex()], 				
	   				      (float)(xSlider.getValue()/100.0),				
 	  		 		     (float)(ySlider.getValue()/100.0),      
                                      (float)(xVelSlider.getValue()/10000.0), 	 	  	   
		             (float)(yVelSlider.getValue()/10000.0)));  
			

						break;   	 	    
	 	                			 		
						case AqFrame.SEAWEED:  
	  	  	 			  aq.add( new Seaweed( 	
	 	     	   			seaweed[imList.getSelectedIndex()],
					     (float)(xSlider.getValue()/100.0), 	  	 	   	 
					      (float)1.0, 
	  	  	           (float)(scaleSlider.getValue()/100.)));    
	 	 	 	 				
						break;

   		 	 			}
						AqFrame.this.c.repaint();
						dispose();
					}
				}
			);

			JPanel buttonPanel =new JPanel();
			buttonPanel.setLayout(new FlowLayout());

			buttonPanel.add(cancel);
			buttonPanel.add(create);
			c.add(buttonPanel);

			switch(type)
			{
				case FISH:
			           setSize(250,300);
				break;

				case SEAWEED:
				    setSize(250,250);
				break;			
			}
			show();
		}

		// Class Name: addSlider
		//
		// Purpose: sets up a single slider to add to window
		//    
		JSlider addSlider(String s)
		{
			JPanel sliderPanel = new JPanel();
			sliderPanel.setLayout(new FlowLayout());

			JLabel label = new JLabel(s);
			sliderPanel.add(label);
		
			JSlider slider =new JSlider(SwingConstants.HORIZONTAL,
					0,100,10);
			slider.setValue(50);
			sliderPanel.add(slider);

			c.add(sliderPanel);
			return slider;	
		}

	}//end AqDialog inner class
} //end AqFrame

// Program Name: Aquarium.java
//
// Purpose: This is the JPanel where the actual aquarium is drawn
//           it also contains a Vector of the AqComponents
//		on the aquarium
//
// Known Bugs: None
//      
//

import java.awt.*;
import javax.swing.*;
import java.util.*;

public class Aquarium extends JPanel
{
	Vector marineLife;
	Color lightBlue = new Color(130,220,255);
	Color sand =new Color(255,255,170);
	BasicStroke aqStroke = new BasicStroke(20.0f);

	// Method Name: Aquarium
	//
	// Purpose: set-up up panel and Vector of AqComponents
	//     
	public Aquarium()
	{
		super();
		
		marineLife = new Vector();
	}

	// Method Name: paintComponent
	//
	// Purpose: draws the aquarium and the fish on it
	//     
	public void paintComponent(Graphics g)
	{
		Graphics2D g2d = (Graphics2D) g;
	
		Dimension d= getSize();
		int height = d.height;
		int width = d.width;
		int topX,topY,bottomX, bottomY;
		int skyHeight = (int)(height*.3);
		int sandHeight = (int)(height*.8);
		int imWidth,imHeight;

		Image im;
		
		AqComponent tmp;
	
		Rectangle screen = new Rectangle(width,height);

		g2d.setPaint(Color.blue);
		g2d.fill(screen);		
			
		g2d.setPaint(sand);
		g2d.fill(new Rectangle(0,sandHeight,width,height));
		
		g2d.setPaint(Color.black);
		g2d.setStroke(aqStroke);
		g2d.draw(screen);

		g2d.setPaint(lightBlue);
		g2d.fill(new Rectangle(0,0,width,skyHeight));

		g2d.setPaint(Color.black);
		g2d.fill(new Rectangle(0,skyHeight-10,10,skyHeight));
		g2d.fill(new Rectangle(width-10,skyHeight-10,width,skyHeight));

		for(int i=0; i < marineLife.size(); i++)
		{
			tmp = (AqComponent)marineLife.get(i);
			im = tmp.icon.getImage();
			imWidth = (int)(width*.10*tmp.scale);
			imHeight = (int)(height*.20*tmp.scale);

			topX = (int)((width-20-imWidth)*tmp.xLoc+10);
			bottomX = imWidth;

			topY = skyHeight+
			    (int)((sandHeight-skyHeight-imHeight)*tmp.yLoc);
	 	 	bottomY = imHeight;

		 	g2d.drawImage(im,topX,topY,bottomX,bottomY,this); 		}
	 }

	// Method Name: getPreferredSize()
	//
	// Purpose: returns standard size of aquarium
	//     
	public Dimension getPreferredSize()
	{
		return new Dimension(500,300);
	}

	// Method Name: getMinimumSize()
	//
	// Purpose: returns minimum size to draw aquarium
	//     
	public Dimension getMinimumSize()
	{
		return new Dimension(100,100);
	}

	// Method Name: add
	//
	// Purpose: add a new AqComponent to aquarium
	//     
	public void add(AqComponent aq)
	{
		marineLife.add(aq);
	}

	// Method Name: clear
	//
	// Purpose: deletes all the AqComponents from the aquarium
	//     
	public void clear()
	{
		marineLife.clear();
	}

	// Method Name: move
	//
	// Purpose: move all the AqComponents on the aquarium one step
	//		accroding to their respective move methods
	//     
	public void move()
	{
		AqComponent lifeForm;

		for(int i=0; i < marineLife.size();i++ )
		{
			lifeForm = (AqComponent)marineLife.get(i);
			lifeForm.move();
		}

	}
}



// Program Name: MyTimer.java
//
// Purpose: this class acts like the usual java class Timer. We
//	have written this class by extending Thread although
//	it might be better to try to implement the Runnable interface
//	instead so we could have a stop() method which doesn't give
//	deprecated stop method
//           
//
// Known Bugs: None
//      
//

import java.awt.event.*;

public class MyTimer extends Thread
{
	ActionListener act; 
	int sleepTime;
	boolean running;

	// Method Name: MyTimer
	//
	// Purpose:
	//     
	public MyTimer(int sleep, ActionListener action)
	{
		sleepTime =sleep;
		act =action;
		running=true;
	}

	// Method Name: isRunning
	//
	// Purpose: returns if the timer is currently calling
	//	tact.actionPreformed every sleepTime milliseconds.
	//     
	public boolean isRunning()
	{ return running;}

	// Method Name: run
	//
	// Purpose: The run method just loops and does the
	//	following two things: (1)sleeps the time specified in the
	//	the constructor.
	//	(2) call act.actionPerformed
	//     
	public void run()
	{
		try{ 
			while(true)
			{
			 running =true;
			 while(running)
			 {
				
				act.actionPerformed(
					new ActionEvent(this, 
						ActionEvent.ACTION_PERFORMED,
						"timer")
					);
				Thread.sleep( sleepTime );
				
			 }
			 while(!running)
			 { Thread.sleep(sleepTime);}
			}//end while true
		}
		catch (InterruptedException e)
		{
			System.err.println(e.toString());
			running =false;
		}	
	}

	// Method Name: restart
	//
	// Purpose: start the timer timer again
	//     
	public void restart()
	{
		running =true;
	}

	// Method Name: myStop
	//
	// Purpose: stop the timer
	//     
	public void myStop()
	{
		running=false;
	}

}

// Program Name: AqComponent.java
//
// Purpose: Contains the base class for object that can appear in the aquarium
//	such object have an associated ImageIcon, an x and y location
//	and a scale (which is not set in the constructor, but is settable
//	in constructors of subclasses)  
//           
//
// Known Bugs: might be better to make this class abstract
//      
//

import java.awt.*;
import javax.swing.*;

public class AqComponent
{
	public ImageIcon icon;
	public float xLoc, yLoc;
	public float scale=(float)0.5;

	// Method Name: Aqcomponent
	//
	// Purpose: createa an instance of AqComponent. Sets
	//	the image and x and y location
	//     

	public AqComponent(ImageIcon i, float x, float y)
	{
		icon=i;
		xLoc=x;
		yLoc=y;
	}

	
	// Method Name: move
	//
	// Purpose: stub method used by subclasses
	//     

	public void move()
	{}
}


// Program Name: Seaweed.java
//
// Purpose: This class is a subclass of AqComponent which
//	is used for object on the seabed, also unlike fish
//	size can be rescaled
//           
//
// Known Bugs: None
//      
//

import java.awt.*;
import javax.swing.*;

public class Seaweed extends AqComponent
{

	// Method Name: Seaweed
	//
	// Purpose: create an instance of Seaweed. Unlike
	//	AqComponent can set scale in constructor
	//     
	public Seaweed(ImageIcon i, float x, float y, float s)
	{
		super(i,x,y);
		scale=s;
	}
	
}

// Program Name: Fish.java
//
// Purpose: this subclass of AqComponent is used to create
//           moving objects in the aquarium
//
// Known Bugs: None
//      
//

import java.awt.*;
import javax.swing.*;

public class Fish extends AqComponent
{
	public float xVel, yVel;

	// Method Name: Fish
	//
	// Purpose: creates an instance of Fish. Unlike AqComponent
	//	specify x and y velocity in the constructor
	//           

	public Fish(ImageIcon i, float x, float y, float xV, float yV)
	{
		super(i,x,y);
		xVel=xV;
		yVel=yV;
	}

	// Method Name: move()
	//
	// Purpose: changes the fishes location by xVel and yVel  
	//          and if bumps into side of tank reverses direction of motion

	public void move()
	{
		xLoc +=xVel;
		yLoc +=yVel;

		if (xLoc > 1 || xLoc<0) //handles horizontal boundaries
			xVel = - xVel;
		if(yLoc > 1 || yLoc <0) //handles vertical boundaries
			yVel = - yVel;

	}
	
}