// FILE: Eyes.java
// A Java applet that draws eyes that follow the mouse when the cursor
// is inside the applet window. Clicking anywhere will place a new eye
// and launch a new thread for that eye's processing.
// Written by: Grant Macklem (Grant.Macklem@colorado.edu)
import java.applet.Applet; // Provides the Applet class.
import java.awt.*; // Provides the Button class, etc.
import java.awt.event.*; // Provides ActionEvent, ActionListener
import java.text.*; // Provides DecimalFormat
/**
* The Eye Java class implements an eye on the screen
* that looks at the mouse pointer. Each eye is a separate thread.
* @author
* Grant Macklem (macklem@colorado.edu)
**/
class Eye extends Thread
{
/**
* Every time the mouse is moved, this is set to the x-coordinate of the mouse.
**/
public static int mouseX;
/**
* Every time the mouse is moved, this is set to the y-coordinate of the mouse.
**/
public static int mouseY;
private static final int WIDTH = 50; // Eye width, in pixels
private static final int HEIGHT = 75; // Eye height, in pixels
private static final int IRISSIZE = 30; // Iris size, in pixels
private static final int PUPILSIZE = 12; // Pupil size, in pixels
private Color irisColor;
// Radii of the inner ellipse that the iris slides along
private static final int SMALLXRAD = (WIDTH - IRISSIZE)/2;
private static final int SMALLYRAD = (HEIGHT - IRISSIZE)/2;
private int x, y; // Current position of the eye
private double newx, newy; // Position of the iris
private Graphics g; // Graphics context
/**
* Constructs a new eye thread.
* @param x
* The x-coordinate of the eye (in pixels) in the current window.
* @param y
* The y-coordinate of the eye (in pixels) in the current window.
* @param g
* The graphics context to which the eye should be drawn.
**/
public Eye(int x, int y, Graphics g)
{
this.g = g;
this.x = x;
this.y = y;
// Create a random iris color
irisColor = new Color((float)Math.random(), (float)Math.random(), (float)Math.random());
}
private void draw()
{
synchronized(g)
{
// Erase the old eye
g.setColor(Color.white);
g.fillOval(x - WIDTH/2, y - HEIGHT/2, WIDTH, HEIGHT);
// Draw the iris
g.setColor(irisColor);
g.fillOval((int)newx - IRISSIZE/2 + 1, (int)newy - IRISSIZE/2 + 1, IRISSIZE, IRISSIZE);
// Draw the pupil
g.setColor(Color.black);
g.fillOval((int)newx - PUPILSIZE/2 + 1, (int)newy - PUPILSIZE/2 + 1, PUPILSIZE, PUPILSIZE);
// Draw the eye outline
g.drawOval(x - WIDTH/2, y - HEIGHT/2, WIDTH, HEIGHT);
}
}
/**
* Continually calculates where the eye should be and redraws it on the screen
* until the thread is destroyed.
**/
public void run()
{
for(;;)
{
updateCoordinates();
draw();
try
{
// Sleep to reduce flicker
sleep(50);
}
catch (InterruptedException e)
{
// Continue execution
}
}
}
private void updateCoordinates()
{
if (mouseX == x)
{ // mouse is vertical above eye center
newx = mouseX;
if (Math.abs(y - mouseY) >= SMALLYRAD)
{ // Pointer is outside the eye
if ( (y - mouseY) > 0 )
newy = y - SMALLYRAD;
else
newy = y + SMALLYRAD;
}
else // pointer is in the eye
newy = mouseY;
return;
}
// Find intersection point of line to mouse with eye ellipse
double slope = (double)(mouseY - y) / (double)(mouseX - x);
double numerator = SMALLXRAD * SMALLXRAD * SMALLYRAD * SMALLYRAD;
double denominator = SMALLYRAD * SMALLYRAD + slope * slope * SMALLXRAD * SMALLXRAD;
newx = Math.sqrt(numerator / denominator);
newy = slope * newx;
// Choose appropriate intersection point
if (mouseX < x)
newx = -Math.abs(newx);
else
newx = Math.abs(newx);
if (mouseY < y)
newy = -Math.abs(newy);
else
newy = Math.abs(newy);
newx += x;
newy += y;
if ( (double)(mouseX - x)*(mouseX - x) / (SMALLXRAD * SMALLXRAD) + (double)(mouseY - y)*(mouseY - y) / (SMALLYRAD * SMALLYRAD) < 1 )
{ // Mouse is inside of the eye
newx = mouseX;
newy = mouseY;
}
}
}
/**
* The Eyes Java applet implements eyes on the screen
* that follow the mouse pointer. Each eye is a separate thread.
* There can be a maximum of 50 eyes on the screen.
* @author
* Grant Macklem (macklem@colorado.edu)
**/
public class Eyes extends Applet
{
// Most comments are for double buffering, which does not help too much
// on my system since the buffers can swap before all threads have
// drawn their eye.
static final int NUM_EYES = 50; // Number of threads
Eye[] eyes = new Eye[NUM_EYES]; // Array of Eyes
int count = -1; // Count of eyes
int width, height; // Height and width of applet
//Image myOffScreenImage; // Second drawing buffer
//Graphics myOffScreenGraphics; // Second graphics context
/**
* Initializes the applet by loading sizes and starting two eye threads.
**/
public void init( )
{
addMouseMotionListener( new MouseMotionListener( ) {
public void mouseDragged(MouseEvent e) {}
public void mouseMoved(MouseEvent e) {
Eye.mouseX = e.getX();
Eye.mouseY = e.getY();
repaint();
}
} );
}
/**
* (Re)starts all the eye threads after initializing or having been suspended.
* Start is called once the applet has been placed on the screen.
* It is recalled whenever the browser goes elsewhere and later comes
* back to this applet.
**/
public void start()
{
if (count == -1)
{ // Nothing has been placed on the screen yet
width = getSize().width; // Width of the applet
height = getSize().height; // Height of the applet
//myOffScreenImage = createImage(width, height);
//myOffScreenGraphics = myOffScreenImage.getGraphics();
final Graphics g = getGraphics( );
//eyes[++count] = new Eye(width/4, height/2, myOffScreenGraphics);
eyes[++count] = new Eye(width/4, height/2, g);
eyes[count].start();
//eyes[++count] = new Eye(3*width/4, height/2, myOffScreenGraphics);
eyes[++count] = new Eye(3*width/4, height/2, g);
eyes[count].start();
addMouseListener( new MouseListener ( ) {
public void mouseClicked(MouseEvent e) {
if ((count+1) >= NUM_EYES)
return;
//eyes[++count] = new Eye(e.getX(), e.getY(), myOffScreenGraphics);
eyes[++count] = new Eye(e.getX(), e.getY(), g);
eyes[count].start();
}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
} );
}
else
{
for (int i = 0; i < count; i++)
eyes[i].resume();
}
repaint();
}
/**
* Pauses all the eye threads after minimizing the applet.
**/
public void stop()
{
for (int i = 0; i < count; i++)
eyes[i].suspend();
}
/**
* Destroys all the eye threads when the applet is exiting..
**/
public void destroy()
{
for (int i = 0; i < count; i++)
eyes[i].stop();
}
/**
* Redraws a border around the applet.
**/
public void update(Graphics g)
{
g.drawRect(0,0,width-1,height-1);
//paint(myOffScreenGraphics); // Draws on the db
// draws the double buffer onto applet
//g.drawImage(myOffScreenImage,0,0,this);
}
}