// Class: MinnowDisplay // // Author: Alyce Brady // // This class is based on the College Board's AbstractFishDisplay, // FishDisplay, and LittleFishDisplay classes, as allowed by the // GNU General Public License. // AbstractFishDisplay, FishDisplay, and LittleFishDisplay are // black-box classes within the AP(r) CS Marine Biology Simulation // case study (see www.collegeboard.com/ap/students/compsci). // // This class is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation. // // This class is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. 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:
* * AMinnowDisplay
draws a minnow. * * @author Alyce Brady (based on code by Julie Zelenski, Alyce Brady, and Chris Nevison) * @version 1 September 2002 **/ public class EggDisplay implements LocatableDisplay { private static final double BODY_WIDTH = .4, BODY_LENGTH = .4; private static final double TAIL_WIDTH = 0, TAIL_LENGTH = 0; private static final double EYE_SIZE = 0; 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 EggDisplay() { 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:obj
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); } }