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();
}
}