/**
* PicturePanel.java
* Author: Chuck Cusack
* Date: August 22, 2007
* Version: 2.0
*
* Modified August 22, 2008
*
* This class draws a picture of me. The hair grows when the mouse is
* clicked. There is a 1% chance every click for a haircut. If you
* drag the head with the mouse, the whole picture moves.
*
* This class is really stupid, but it demonstrates how to do some drawing
* using the Graphics object, how to handle mouse events, how to create
* and use inner classes, how to use ArrayLists with generics, etc.
*
*/
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.geom.*;
/**
* A class draws a picture on a panel
*
*/
public class PicturePanel extends JPanel
{
// The width and height of the JPanel upon which things are drawn
public static final int width = 400;
public static final int height = 400;
// The rest of the fields are specific to my implementation
int numberClicks=1; // The number of times the mouse has been clicked since it was reset.
int days=1; // Total number of mouse clicks.
Color Browns[]=new Color[4]; // An array of colors for the hair. To give it variation.
int xCoordinate=0; // The x-coordinate where the mouse entered the window
int yCoordinate=0; // The y-coordinate where the mouse entered the window
ArrayList<HairLock> standardHair; // All of the short hair
ArrayList<LongHairLock> longHair; // All of the long hair
int originX=75; // The center x-coordinate of the face.
int originY=138; // the center y-coordinate of the face.
// End of fields
/**
* Get stuff ready so when paintComponent is called, it can draw stuff.
* Depending on how complicated you want to get, your constructor may be blank.
*/
public PicturePanel()
{
// If you want to handle mouse events, you will need the following
// 3 lines of code. Just leave them as is and modify the methods
// with "mouse" in the name toward the end of the class.
MouseHandler mh=new MouseHandler();
addMouseListener(mh);
addMouseMotionListener(mh);
// Various shades of brown. Used for hair color.
Browns[0]=new Color(50,50,0);
Browns[1]=new Color(75,75,0);
Browns[2]=new Color(100,100,0);
Browns[3]=new Color(150,150,0);
//Create Standard Haircut.
// This simply creates some randomish locks of hair to cover the head with.
// This list remains static once it is created.
// When I get a haircut, the lonHairs are removed, but these remain.
standardHair=new ArrayList<HairLock>();
for(int i=-26;i<26;i+=2) {
for(int j=0;j<20;j+=2) {
int theColor=(int) (Math.random()*4);
Color lockColor = Browns[theColor];
int x1=75 + i + (int) (Math.random()*4);
int y1=105 + (int) (Math.random()*6) + (i*i+j*j)/30;
int x2=x1+(int)(Math.random()*8)-4;
int y2=y1-6;
HairLock l = new HairLock(x1,y1,x2,y2,lockColor);
standardHair.add(l);
}
}
// The longer hair. Every day, more hair is added to this list.
longHair=new ArrayList<LongHairLock>();
}
/**
* This method is called whenever the applet needs to be drawn.
* This is the most important method of this class, since without
* it, we don't see anything.
*
* This is the method where you will most likely do all of your coding.
*/
public void paintComponent(Graphics g)
{
// Always place this as the first line in paintComponent.
super.paintComponent(g);
// Translate the drawing according to where the new origin is.
// The original origin is (75,138), so translation occures
// relative to that point.
// You will probably NOT need this line in your code.
g.translate(originX-75,originY-138);
// Draw the various parts of the screen.
// I have separated the work of drawing into several methods so
// it is easier to see what is going on.
drawStuff(g);
drawFace(g);
drawStandardHair(g);
drawLongHair(g);
}
/**
* The method that draws the face
* Notice that the Graphics object is passed to the method.
* This is like passing a canvas to a friend and saying
* "Hey, can you paint something on this for me?"
* Then you can take the canvas back and give it to someone
* else so they can paint more stuff on it.
*/
private void drawFace(Graphics g)
{
// Head
g.setColor(new Color(240,240,100));
g.fillOval(50,105,50,65);
// Ears
g.fillOval(45,130,10,16);
g.fillOval(96,130,10,16);
// Eyes
g.setColor(Color.white);
g.fillOval(55,130,16,10);
g.fillOval(80,130,16,10);
//Black part of eyes
g.setColor(Color.black);
g.fillOval(58,131,8,8);
g.fillOval(83,131,8,8);
// colored part of eyes
g.setColor(new Color(0,150,0));
g.fillOval(59,132,5,5);
g.fillOval(84,132,5,5);
//Mouth
g.setColor(new Color(200,100,100));
g.fillArc(65,152,20,5,160,220);
//Nose
g.setColor(new Color(200,200,80));
g.fillArc(68,120,15,30,250,60);
}
/**
* The method that draws the standard haircut.
* It simply iterates through the list of hair
* and asks each to draw itself.
*/
private void drawStandardHair(Graphics g)
{
Color old=g.getColor();
for(HairLock a: standardHair) {
a.drawLock(g);
}
g.setColor(old);
}
/**
* The method that draws the longer hair.
* It simply iterates through the list of hair
* and asks each to draw itself.
*/
private void drawLongHair(Graphics g)
{
Color old=g.getColor();
for(LongHairLock a: longHair) {
a.drawLock(g);
}
g.setColor(old);
}
/**
* A method to draw other stuff on the applet.
* This is all of the text on the screen.
*/
private void drawStuff(Graphics g)
{
// Change the font and color.
g.setFont(new Font("Times Roman",Font.BOLD,24));
g.setColor(Color.BLUE);
g.drawString("Dr. Cusack",10,30);
// Change color/font again.
g.setColor(Color.BLACK);
g.setFont(new Font("Times Roman",Font.PLAIN,12));
// If numClocks is 0, a haircut occurred, so we inform the user.
if(numberClicks==0)
{
g.drawString("Haircut!",120,120);
}
g.drawString("Day "+days,120,150);
g.drawString("Click the mouse button and the hair will grow",10,180);
g.drawString("There is a 1% chance of a haircut each click",10,200);
// Change the font and color
g.setColor(new Color(50,150,100));
g.setFont(new Font("Arial",Font.ITALIC,16));
g.drawString("Mouse Entered at ("+xCoordinate+", "+yCoordinate+")",10,220);
// Change the font and color
g.setColor(new Color(.75f,.25f,.1f));
g.setFont(new Font("Arial",Font.ITALIC,16));
g.drawString("Drag the head around the screen",10,240);
}
/**
* This method adds more hair of a given length
* It adds about 52 locks of the given length.
*/
private void addLocks(int length)
{
for(int j=-26;j<26;j+=1)
{
int x1=75 + (j) + (int) (Math.random()*4);
int y1=105 + (int) (Math.random()*6) + (j*j)/30;
int theColor=(int) (Math.random()*4);
Color lockColor = Browns[theColor];
LongHairLock a = new LongHairLock(x1,y1,length,lockColor);
longHair.add(a);
}
}
//------------------------------------------------------------------------------------------
// HairLock class. Stores 1 strand of straight hair of a given color.
// Each hair is stored by the endpoints of a line.
private class HairLock {
int x1,y1, x2, y2;
Color myColor;
public HairLock(int x1,int y1,int x2,int y2,Color c) {
this.x1=x1;
this.y1=y1;
this.x2=x2;
this.y2=y2;
myColor=c;
}
public void drawLock(Graphics g) {
g.setColor(myColor);
g.drawLine(x1,y1,x2,y2);
}
}
//------------------------------------------------------------------------------------------
// LongHairLock class. Stores a longer "curvey" lock of hair.
// Each hair is stored by a set of n points, and the hair is the polyline connected by the points.
private class LongHairLock {
int[] x;
int[] y;
int length;
Color myColor;
public LongHairLock(int xStart,int yStart, int length, Color c) {
myColor=c;
this.length=length;
x=new int[length];
y=new int[length];
// (xStart,yStart) is the root of the hair. The rest is random.
x[0]=xStart;
y[0]=yStart;
// Attempting to create strands of hair that go in "normal"
// directions. That is, trying to make the hair look right.
// Most of this code is trial-and-error, and it could be done
// much better if I applied a little mathematics...
for(int i=1;i<length;i++)
{
if(x[i-1]>100) {
x[i]=x[i-1] + (int) (Math.random()*17)-6;
}
else if(x[i-1]<50) {
x[i]=x[i-1] + (int) (Math.random()*17)-10;
}
else {
x[i]=x[i-1] + (int) (Math.random()*17)-8;
}
y[i]=y[i-1] - (int) (Math.random()*11)+3;
}
}
public void drawLock(Graphics g) {
g.setColor(myColor);
g.drawPolyline(x,y,length);
}
}
//------------------------------------------------------------------------------------------
/**
* The main method, which allows us to run the application
* without using a webpage. In other words, this is the method
* that is called when you run a Java application.
*/
public static void main(String[] args) {
PicturePanel panel = new PicturePanel();
JFrame theFrame = new JFrame();
theFrame.setTitle("A Picture");
// Place all of the graphical components on the main window
Container cont=theFrame.getContentPane();
cont.add(panel,BorderLayout.CENTER);
// Finish setting up the main window
theFrame.setBackground(Color.white);
theFrame.pack();
theFrame.setSize(new Dimension(width,height));
theFrame.setVisible(true);
// Add window listener so it closes properly.
theFrame.addWindowListener
(
new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
}
);
}
//---------------------------------------------------------------
// A class to handle the mouse events for the applet.
// This is one of several ways of handling mouse events.
// Another is to make this class implement a MouseListener
// interface.
// There are advantages and disadvantages of doing each.
//
private class MouseHandler extends MouseAdapter implements MouseMotionListener
{
/**
* Overridden from MouseAdapter
* This grows hair or gives a haircut.
*/
public void mouseClicked(MouseEvent e)
{
// There is a 1% chance of a haircut each click
if(Math.random()*101 > 99) {
numberClicks=0;
longHair.clear();
} else { // Otherwise, the hair gets longer.
numberClicks++;
addLocks(numberClicks);
}
days++;
// After making any changes that will affect the way the screen is drawn,
// you have to call repaint.
repaint();
}
/**
* Overridden from MouseAdapter
* Just updates the coordinates with where the mouse entered the window.
* Stupid really, but it is just another example of how a mouse event can be handled.
*/
public void mouseEntered(MouseEvent e)
{
xCoordinate = e.getX();
yCoordinate = e.getY();
// After making any changes that will affect the way the screen is drawn,
// you have to call repaint.
repaint();
}
/**
* Three additional methods from the MouseAdapter class.
* I don't use them, so when invoked, they don't do anything.
*/
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
/**
* Implemented from the MouseMotionListener interface.
* I don't want this to do anything, but I need to implement it.
* (Every method of an interface needs to be implemented.)
* So, I implement it doing nothing.
*/
public void mouseMoved(MouseEvent e) {
}
/**
* Implemented from the MouseMotionListener interface
* This allows us to move the drawing around the screen. Cool.
*/
public void mouseDragged(MouseEvent e) {
// Get the current position of the mouse.
int x = e.getX();
int y = e.getY();
// If the mouse is over the head, then we want to move the origin to the
// current (x,y) location of the mouse, so the head moves.
// It turns out there is probably a better way to do this, but I am using
// this method becuase it uses MouseMotionListener interface. The other way uses
// two methods from the MouseListener. Can you figure it out?
if(Math.sqrt(Math.pow(Math.abs(x-originX),2)+Math.pow(Math.abs(y-originY),2))<25) {
originX=x;
originY=y;
// After making any changes that will affect the way the screen is drawn,
// you have to call repaint.
repaint();
}
}
}
}