import java.awt.geom.GeneralPath; // combinations of lines and curves import java.awt.geom.AffineTransform; // translation, rotation, scale import java.awt.Shape; // general class for shapes // all imports below this line needed if you are implementing Shape import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.Rectangle; import java.awt.geom.PathIterator; import java.awt.geom.AffineTransform; /** A Snowman that implements Shape @author Phill Conrad @version for CS10, S09, UCSB, 05/06/2009 */ public class Snowman implements Shape { private GeneralPath snowman; /** * Constructor for objects of class Snowman. * * x and y is the upper left corner of the bounding box. * For nice round circles, height should be 2*width * If you use anything else, you may get a distorted snowman. * * @param x upper left corner of bounding box * @param y upper left corner of bounding box * @param width width of snowman in pixels * @param height of snowman in pixels (2.0*width for nice round snowballs) */ public Snowman(double x, double y, double width, double height) { // now a snowman: three circles // here we use constants, so that if we want to change // the dimensions later, or move the snowman around, // it becomes easier. // Instead of doing the math ourselves, and putting "hard coded numbers" // in the constructors for the Ellipses, we let the computer do the math! final double bottomRadius = 15; final double middleRadius = 10; final double topRadius = 5; final double snowManCenterBottomX = 400; final double snowManCenterBottomY = 300; final double ORIG_WIDTH = bottomRadius * 2; final double ORIG_HEIGHT = bottomRadius * 2 + middleRadius * 2 + topRadius * 2; final double ORIG_UPPER_LEFT_X = snowManCenterBottomX - bottomRadius; final double ORIG_UPPER_LEFT_Y = snowManCenterBottomY - ORIG_HEIGHT; Circle snowManBottomCircle = new Circle ( snowManCenterBottomX, snowManCenterBottomY - bottomRadius, bottomRadius ); Circle snowManMiddleCircle = new Circle ( snowManCenterBottomX, snowManCenterBottomY - bottomRadius * 2 - middleRadius, middleRadius ); Circle snowManTopCircle = new Circle ( snowManCenterBottomX, snowManCenterBottomY - bottomRadius * 2 - middleRadius * 2 - topRadius, topRadius ); // put the whole snow man together GeneralPath wholeSnowMan = new GeneralPath (); wholeSnowMan.append(snowManBottomCircle, false); wholeSnowMan.append(snowManMiddleCircle, false); wholeSnowMan.append(snowManTopCircle, false); // @@@ ADD THIS CODE.... // translate to the origin by subtracting the original upper left x and y // then translate to (x,y) by adding x and y Shape s = ShapeTransforms.translatedCopyOf(wholeSnowMan, -ORIG_UPPER_LEFT_X, -ORIG_UPPER_LEFT_Y); // scale to correct height and width s = ShapeTransforms.scaledCopyOf(s, width/ORIG_WIDTH, height/ORIG_HEIGHT) ; s = ShapeTransforms.translatedCopyOf(s,x,y); // Use the GeneralPath constructor that takes a shape and returns // it as a general path to set our instance variable cup this.snowman = new GeneralPath(s); } /** A convenience method to automatically make a snowman, calculating the width from the height, * and allow us to specify the center bottom point instead of the upper left corner * * @param centerBottomX center bottom x * @param centerBottomY center bottom y * @param height of the snowman in pixels */ public Snowman (double centerBottomX, double centerBottomY, double height) { // invoke constructor with these parameters this(centerBottomX - height/4.0, centerBottomY - height, height/2.0, height); } // EVERYTHING BELOW THIS LINE IS JUST A WRAPPER AROUND // THE METHODS OF THE GENERAL PATH OBJECT. SINCE GENERAL PATH IS FINAL, // WE CAN'T INHERIT FROM IT. (OTHERWISE, THIS WOULDN'T BE NECESSARY) /** Tests if the specified coordinates are inside the boundary of the Shape. */ public boolean contains(double x, double y) { return snowman.contains(x,y); } /** Tests if the interior of the Shape entirely contains the * specified rectangular area. */ public boolean contains(double x, double y, double w, double h) { return snowman.contains(x,y,w,h); } /** Tests if a specified Point2D is inside the boundary of * the Shape. */ public boolean contains(Point2D p) { return snowman.contains(p); } /** Tests if the interior of the Shape entirely contains the specified Rectangle2D. */ public boolean contains(Rectangle2D r) { return snowman.contains(r); } /** Returns an integer Rectangle that completely encloses the * Shape. */ public Rectangle getBounds() { return snowman.getBounds(); } /** Returns a high precision and more accurate bounding box * of the Shape than the getBounds method. */ public Rectangle2D getBounds2D() { return snowman.getBounds2D(); } /** Returns an iterator object that iterates along the * Shape boundary and provides access to the geometry of * the Shape outline. */ public PathIterator getPathIterator(AffineTransform at) { return snowman.getPathIterator( at); } /** Returns an iterator object that iterates along the * Shape boundary and provides access to a flattened view * of the Shape outline geometry. */ public PathIterator getPathIterator(AffineTransform at, double flatness) { return snowman.getPathIterator( at, flatness); } /** Tests if the interior of the Shape intersects the * interior of a specified rectangular area. */ public boolean intersects(double x, double y, double w, double h) { return snowman.intersects(x, y, w, h); } /** Tests if the interior of the Shape intersects the interior of * a specified Rectangle2D. */ public boolean intersects(Rectangle2D r) { return intersects(r); } }