package lab.turtle; import java.awt.Color; /** * A class that supports turtle graphics inside a circular area. The turtle has a * position, an angle representing the direction that the turtle is facing in, * and a pen color. The turtle stays inside the circle centered at the point * (0, 0) and having radius equal to 1. * The angle of the turtle is measured relative to the positive x-axis, and * the turtle ensures that the angle is always greater than -360 degrees and * less than +360 degrees. * * @author */ public class Turtle { /** * The position of this turtle. */ private Point2D position; /** * The direction of this turtle. */ private double angle; /** * The pen color of this turtle. */ private Color penColor; //@ invariant -Turtle.MAX_ANGLE < this.angle < Turtle.MAX_ANGLE && this.penColor != null && this.position != null && this.position.distanceTo(Turtle.ORIGIN) <= 1 /** * Origin. */ private static final Point2D ORIGIN = new Point2D(); /** * Maximal angle */ private static final double MAX_ANGLE = 360; /** * Initialize this turtle so that it is located at the exact middle of the * circle with an angle of 0.0 degrees and a pen color of * Color.BLACK. */ public Turtle() { this(new Point2D(), 0, Color.BLACK); } /** * Initialize this turtle from another turtle. This turtle has the same * position, angle, and pen color as the other turtle. * * @param other * the turtle to copy */ public Turtle(Turtle other) { this(other.getPosition(), other.getAngle(), other.getPenColor()); } /** * Initialize this turtle with the given starting position, angle, and pen color. * The starting position must be inside or on the circle that the turtle * is confined to, otherwise an IllegalArgumentException will be thrown. * * @param position * the starting position of the turtle * @param angle * the angle in degrees from the x axis that the turtle is facing in * @pre. -360 < angle && angle < 360 * @param penColor * the pen color * @pre. penColor != null * @throws IllegalArgumentException if the starting position is * not inside or on the circle that the turtle is confined to */ public Turtle(Point2D position, double angle, Color penColor) throws IllegalArgumentException { this.setPosition(position); this.setAngle(angle); this.setPenColor(penColor); } /** * Moves the turtle by a given distance in the direction the turtle is * currently facing. A line is drawn as the turtle moves to the new position. * *

* The turtle will not move outside of the circle that it is confined to. If the * specified distance would cause the turtle to move outside of the circle, * then the turtle will move as far as possible in its current direction and * then stop at the perimeter of the circle. * * @param distance * the distance to move * @throws IllegalArgumentException * if the distance is less than zero */ public void move(double distance) { if (distance < 0) { throw new IllegalArgumentException("Distance should be nonnegative"); } else { // determine the x- and y-coordinate of initial position double x1 = this.getPosition().getX(); double y1 = this.getPosition().getY(); // determine the actual distance distance = Math.min(distance, this.maxDistance()); // determine the x- and y-coordinate of final position IVector2D move = IVector2D.dirAndMag(this.getAngle(), distance); double x2 = x1 + move.getX(); double y2 = y1 + move.getY(); // draw the line StdDraw.setPenColor(this.penColor); StdDraw.line(x1, y1, x2, y2); // set the new position this.setPosition(new Point2D(x2, y2)); } } /** * Returns the maximum distance that the turtle can move from its current * position until it hits the * perimeter of the circle that the turtle is constrained to move on. The * maximum distance is computed by considering the turtle's current position * and direction, as well as the perimeter of the circle. * * * @return the maximum distance that the turtle can move from its * current position */ public double maxDistance() { // get the x- and y-coordinates double x = this.getPosition().getX(); double y = this.getPosition().getY(); // compute the coefficients of the equation double a = 1; double b = 2 * (x * Math.cos(Math.toRadians(this.getAngle())) + y * Math.sin(Math.toRadians(this.getAngle()))); double c = x * x + y * y - 1; // find the largest real root greater than or equal to zero double root = TurtleUtil.semipositiveRoot(a, b, c); // the root is the maximal distance return root; } /** * Turns the turtle to the left, increasing its angle by 90.0 degrees. The * angle of the turtle is always corrected to be greater than * -360 degrees and less than +360 degrees. * */ public void turnLeft() { final double LEFT = 90; this.turn(LEFT); } /** * Turns the turtle to the right, decreasing its angle by 90.0 degrees. The * angle of the turtle is always corrected to be greater than * -360 degrees and less than +360 degrees. */ public void turnRight() { final double RIGHT = -90; this.turn(RIGHT); } /** * Turns the turtle by the specified amount in degrees. A positive * delta turns the turtle to the left (counterclockwise) and the * negative delta turns the turtle to the right (clockwise). The * angle of the turtle is always corrected to be greater than * -360 degrees and less than +360 degrees. * * @param delta * the amount by which to turn the turtle */ public void turn(double delta) { this.setAngle((this.getAngle() + delta) % Turtle.MAX_ANGLE); } /** * Sets the pen color. * * @param penColor * the new pen color * @pre. penColor != null */ public void setPenColor(Color penColor) { this.penColor = penColor; StdDraw.setPenColor(penColor); } /** * Gets the current pen color. * * @return the current pen color */ public Color getPenColor() { return this.penColor; } /** * Sets the position of this turtle to the given position. * * @param position the new position of this turtle. * @throws IllegalArgumentException if the new position of the turtle * is not inside or on the circle to which this turtle is confined. */ private void setPosition(Point2D position) throws IllegalArgumentException { if (position == null || position.distanceTo(Turtle.ORIGIN) > 1) { throw new IllegalArgumentException("Invalid position"); } else { this.position = new Point2D(position); } } /** * Gets the current position of the turtle. The client cannot change the * position of the turtle using the point returned by this * method. To move the turtle the client must use move. * * @return the current position of the turtle */ public Point2D getPosition() { return new Point2D(this.position); } /** * Gets the direction that the turtle is facing in as an angle measured from * the x axis. The angle of the turtle is always in the range of * -360 degrees and +360 degrees. * * @return the angle measured in degrees from the x axis that the turtle is * facing */ public double getAngle() { return this.angle; } /** * Sets the angle of this turtle to the given angle. * * @param angle the new angle of this turtle. * @pre. -360 < angle && angle < 360 */ private void setAngle(double angle) { this.angle = angle; } /** * Returns a string representation of this turtle. The string returned * is simply the position of the turtle, followed by the angle * of the turtle, followed by the pen color of the turtle. For example, * the turtle made with the default constructor has the following * string representation: * *

(0.5, 0.5), 0.0 degrees, java.awt.Color[r=0,g=0,b=0]
* *

* The string for the pen color is identical to that produced by * invoking toString in java.awt.Color * * @return a string representation of this turtle * @see java.lang.Object#toString() */ @Override public String toString() { return this.getPosition() + ", " + this.getAngle() + " degrees, " + this.getPenColor(); } }