001 // This class is based on the FishDisplay class, version 1 August 2002
002 // by Julie Zenekski, Alyce Brady, and Chris Nevison
003
004 // Original copyright notice:
005
006 // AP(r) Computer Science Marine Biology Simulation:
007 // The FishDisplay class is copyright(c) 2002 College Entrance
008 // Examination Board (www.collegeboard.com).
009 //
010 // This class is free software; you can redistribute it and/or modify
011 // it under the terms of the GNU General Public License as published by
012 // the Free Software Foundation.
013 //
014 // This class is distributed in the hope that it will be useful,
015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
017 // GNU General Public License for more details.
018
019
020 package model.fish.display;
021
022 import sysModel.fish.IFishDisplay;
023
024 import java.awt.*;
025 import java.awt.geom.AffineTransform;
026 import java.awt.geom.Ellipse2D;
027 import java.awt.geom.GeneralPath;
028
029 /**
030 * Parameterized fish display.
031 *
032 * @author Mathias Ricken
033 */
034 public class ParamFishDisplay implements IFishDisplay {
035 private static final double BODY_WIDTH = .55;
036 private static final double BODY_LENGTH = .75;
037 private static final double TAIL_WIDTH = .5;
038 private static final double TAIL_LENGTH = .4;
039 private static final double EYE_SIZE = .08;
040
041 private static final int GRADIENT_SIZE = 50;
042 private static final AffineTransform ATX = AffineTransform.getScaleInstance(GRADIENT_SIZE, GRADIENT_SIZE);
043
044 private Shape bodyAndTail;
045 private Shape eye1;
046 private Shape eye2;
047
048 /**
049 * Make an object that knows how to drawFish simple fish.
050 *
051 * @param bodyWidth width of the body
052 * @param bodyLength length of the body
053 * @param tailWidth width of the tail
054 * @param tailLength length of the tail
055 * @param eyeSize size of the eyes
056 */
057 public ParamFishDisplay(double bodyWidth, double bodyLength, double tailWidth, double tailLength, double eyeSize) {
058 buildPaths(bodyWidth, bodyLength, tailWidth, tailLength, eyeSize);
059 }
060
061 /**
062 * Default constructor.
063 */
064 public ParamFishDisplay() {
065 this(BODY_WIDTH, BODY_LENGTH, TAIL_WIDTH, TAIL_LENGTH, EYE_SIZE);
066 }
067
068 /**
069 * Set up the paths used for the fish body, tail, and eyes. Different parameters will change the proportions, and
070 * thereby control the "look" of the fish. The various parameters should be specified assuming the fish will occupy
071 * a cell of size (1, 1).
072 *
073 * @param bodyWidth width of the elliptical body
074 * @param bodyLength length of the elliptical body
075 * @param tailWidth width of the triangular tail
076 * @param tailLength length of the triangular tail
077 * @param eyeSize diameter of the eye
078 */
079 private void buildPaths(double bodyWidth, double bodyLength, double tailWidth, double tailLength, double eyeSize) {
080 // Build a set of paths for a fish facing North in a unit-length cell.
081 // We will rotate/scale as needed later.
082
083 float halfFishLength = (float)(bodyLength + tailLength / 3) / 2;
084
085 // The fish body is an ellipse of the given body width and length.
086 // The ellipse is horizontally centered and slightly above vertical
087 // center (to leave room for tail).
088 Shape body = new Ellipse2D.Double(-bodyWidth / 2, -halfFishLength, bodyWidth, bodyLength);
089
090 // The fish tail is a triangle overlapping the end of body.
091 GeneralPath tail = new GeneralPath();
092 tail.moveTo(-(float)tailWidth / 2, halfFishLength); // lower left
093 tail.lineTo(0, halfFishLength - (float)tailLength); // top of tail
094 tail.lineTo((float)tailWidth / 2, halfFishLength); // lower right
095 tail.closePath();
096
097 // Join body and tail together in one path.
098 tail.append(body, false);
099 bodyAndTail = tail;
100
101 // The fish eyes are circles.
102 eye1 = new Ellipse2D.Double(-bodyWidth / 4, -halfFishLength + bodyLength / 4, eyeSize, eyeSize);
103 eye2 = new Ellipse2D.Double(+bodyWidth / 4 - eyeSize, -halfFishLength + bodyLength / 4, eyeSize, eyeSize);
104 }
105
106 /**
107 * Draw the fish facing north on the Graphics2D object. The Graphics2D object has been prepared such that the center
108 * of the fish is at the origin.
109 *
110 * @param g2 drawing surface
111 * @param comp the component to drawFish on
112 * @param fishColor color of the fish
113 */
114 public void draw(Graphics2D g2, Component comp, Color fishColor) {
115 Color oldColor = g2.getColor();
116
117 // Stroke outline of fish body and tail in slightly darker color.
118 g2.setPaint(fishColor.darker());
119 g2.draw(bodyAndTail);
120
121 // Fill fish body and tail with gradient (scale up temporarily to get smooth dither).
122 g2.scale(1.0 / GRADIENT_SIZE, 1.0 / GRADIENT_SIZE);
123 g2.setPaint(new GradientPaint(-GRADIENT_SIZE / 4,
124 -GRADIENT_SIZE / 2,
125 Color.white,
126 GRADIENT_SIZE / 4,
127 GRADIENT_SIZE / 4,
128 fishColor));
129 g2.fill(ATX.createTransformedShape(bodyAndTail));
130 g2.scale(GRADIENT_SIZE, GRADIENT_SIZE);
131
132 // Fill black circles for the eyes.
133 g2.setPaint(Color.black);
134 g2.fill(eye1);
135 g2.fill(eye2);
136
137 g2.setColor(oldColor);
138 }
139 }