// Class: MinnowDisplay
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.GradientPaint;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.AffineTransform;
/**
* Minnow Program:<br>
*
* A <code>MinnowDisplay</code> draws a minnow.
*
* @author Alyce Brady (based on code by Julie Zelenski, Alyce Brady, and Chris
Nevison)
* @version 1 September 2002
**/
public class FrogDisplay implements LocatableDisplay
{
private static final double BODY_WIDTH = .5, BODY_LENGTH = .5;
private static final double TAIL_WIDTH = .25, TAIL_LENGTH = .4;
private static final double EYE_SIZE = .08;
private static final int GRADIENT_SIZE = 50;
private static final AffineTransform ATX = AffineTransform.getScaleInstance(GRADIENT_SIZE,
GRADIENT_SIZE);
private Shape bodyAndTail, eye1, eye2;
/** Constructs an object that knows how to draw minnows.
**/
public FrogDisplay()
{
buildPaths(BODY_WIDTH, BODY_LENGTH, TAIL_WIDTH, TAIL_LENGTH, EYE_SIZE);
}
/** Sets up the paths used for the minnow body, tail, and eyes.
* Different parameters will change the proportions, and thereby
* control the "look" of the minnow. The various parameters should
be
* specified assuming the minnow will occupy a cell of size (1, 1).
* @param bodyWidth width of the elliptical body
* @param bodyLength length of the elliptical body
* @param tailWidth width of the triangular tail
* @param tailLength length of the triangular tail
* @param eyeSize diameter of the eye
*/
protected void buildPaths(double bodyWidth, double bodyLength,
double tailWidth, double tailLength, double eyeSize)
{
// Build a set of paths for a minnow facing North in a unit-length cell.
// We will rotate/scale as needed later.
float halfFishLength = (float)(bodyLength + tailLength/3)/2;
// The fish body is an ellipse of the given body width and length.
// The ellipse is horizontally centered and slightly above vertical
// center (to leave room for tail).
Shape body = new Ellipse2D.Double(-bodyWidth/2, -halfFishLength, bodyWidth,
bodyLength);
// The fish tail is a triangle overlapping the end of body.
GeneralPath tail = new GeneralPath();
tail.moveTo(-(float)tailWidth/2, halfFishLength); // lower left
tail.lineTo(0, halfFishLength-(float)tailLength); // top of tail
tail.lineTo((float)tailWidth/2, halfFishLength); // lower right
tail.closePath();
// Join body and tail together in one path.
tail.append(body, false);
bodyAndTail = tail;
// The fish eyes are circles.
eye1 = new Ellipse2D.Double(-bodyWidth/4, -halfFishLength + bodyLength/4, eyeSize,
eyeSize);
eye2 = new Ellipse2D.Double(+bodyWidth/4 - eyeSize, -halfFishLength + bodyLength/4,
eyeSize, eyeSize);
}
/** Draws the given Minnow.
* Fills a simple fish with gradient paint using the paths created in
* the construtor. The Minnow is drawn facing North in a cell of
* size (1,1) centered around (0,0) on the drawing surface.
* (All scaling/rotating has been done beforehand).
* @param minnow minnow we want to draw
* @param comp component on which to draw
* @param g2 drawing surface
**/
public void draw(Minnow minnow, Component comp, Graphics2D g2)
{
Color fishColor = minnow.color();
// Stroke outline of minnow body and tail in slightly darker color.
g2.setPaint(fishColor.darker());
g2.draw(bodyAndTail);
// Fill fish body and tail with gradient (scale up temporarily to get smooth
dither).
g2.scale(1.0/GRADIENT_SIZE, 1.0/GRADIENT_SIZE);
g2.setPaint(new GradientPaint(-GRADIENT_SIZE/4, -GRADIENT_SIZE/2, Color.white,
GRADIENT_SIZE/4, GRADIENT_SIZE/4, fishColor));
g2.fill(ATX.createTransformedShape(bodyAndTail));
g2.scale(GRADIENT_SIZE, GRADIENT_SIZE);
// Fill black circles for the eyes.
g2.setPaint(Color.black);
g2.fill(eye1);
g2.fill(eye2);
}
/** Draw the given object.
* Scales and rotates the coordinate appropriately then invokes
* the simple draw method above that is only responsible for
* drawing a unit-length Minnow facing North.
* (Precondition: <code>obj</code> is a Minnow.)
* @param obj object we want to draw
* @param comp component on which to draw
* @param g2 drawing surface
* @param rect rectangle in which to draw
**/
public void draw(Locatable obj, Component comp, Graphics2D g2, Rectangle rect)
{
Minnow fish = (Minnow)obj;
float scaleFactor = Math.min(rect.width, rect.height);
// Translate to center of fish
g2.translate(rect.x + rect.width/2, rect.y + rect.height/2);
// Rotate drawing surface before drawing to capture fish's
// orientation (direction).
int rotationInDegrees = fish.direction().inDegrees();
g2.rotate(Math.toRadians(rotationInDegrees));
if (rotationInDegrees > 180) g2.scale(-1, 1); // flip side-to-side
// Scale to size of rectangle, adjust stroke back to 1-pixel wide
g2.scale(scaleFactor, scaleFactor);
g2.setStroke(new BasicStroke(1.0f/scaleFactor));
draw(fish, comp, g2);
}
}