import java.lang.Comparable; import java.util.SortedSet; import java.util.TreeSet; interface XY { public Double x(); public Double y(); public XY plus(XY other); public Polar toPolar(); } interface Polar extends Comparable<Polar> { public Double radius(); public Double angle(); public XY toXY(); } interface PointInAPlane extends XY, Polar { public void setLabel(String label); public String getLabel(); public String toXYString(); public String toPolarString(); }
public class RandomPointsOnACircle { /** * Prints the Cartesian and polar coordinates of a sorted list of points that are * created based on the program's command-line arguments. The first argument is the * number of points to create. The second argument is the radius of a circle that * each point will be on. The third argument is the x-offset of the circle. The * fourth argument is the y-offset of the circle. All arguments are optional. * Default values are used if the radius and offset values are not specified. */ public static void main(String[] args) { int n = args.length > 0 ? Integer.parseInt(args[0]) : 0; double r = args.length > 1 ? Double.parseDouble(args[1]) : 1.0; double x = args.length > 2 ? Double.parseDouble(args[2]) : 0; double y = args.length > 3 ? Double.parseDouble(args[3]) : 0; SortedSet<PointInAPlane> orderedRandomPoints = new TreeSet<PointInAPlane>(); while (n-- > 0) { // 1. Create an object to hold the polar coordinates of a // random point on the circumference of a circle with radius r. // 2. Add the x-coordinate and y-coordinate values passed to the // program via the command-line arguments and the x-coordinate // and y-coordinate values that correspond to the polar coordinates // of the object created in the previous step. Store the resulting // coordinates in an object of type XY. // 3. Use the XY object from the previous step to create an object // of type Point2D, and add that object to orderedRandomPoints.
Polar polarCoords = new PolarCoords(r, 2 * Math.PI * Math.random()); XY xyCoords = new CartesianCoords(x, y).plus(polarCoords.toXY()); orderedRandomPoints.add(new Point2D(xyCoords));
} int pointNumber = 0; for (PointInAPlane point : orderedRandomPoints) point.setLabel("p" + ++pointNumber); System.out.println("Polar Coordinates\n"); for (PointInAPlane point : orderedRandomPoints) System.out.println(point.toPolarString()); System.out.println("\nCartesian Coordinates\n"); for (PointInAPlane point : orderedRandomPoints) System.out.println(point.toXYString()); } }
/** * Used to determine whether the difference between two * floating point numbers is significant. (Sometimes we * want to ignore small differences that are the result * of rounding or truncation.) */ class Difference { public static boolean isSignificant(double d1, double d2) { // Sometimes (d1 - d2 == 0.0) is true even though (d1 == d2) is false. return Difference.isSignificant(d1, d2, 0.0); } public static boolean isSignificant(double d1, double d2, double epsilon) { // If the absolute value of d1 - d2 is greater than epsilon // then return true; otherwise, return false.
return Math.abs(d2 - d2) > epsilon;
} } /** * A generic ordered pair of objects. */ class OrderedPair<T> { private final T first, second; public OrderedPair(T first, T second) { // Set the value of the object's immutable instance variables.
this.first = first; this.second = second;
} public T first() { return this.first; } public T second() { return this.second; } }
/** * An ordered pair of numbers used to identify a point in 2-dimensional space. */ abstract class Coords2D { private final OrderedPair<Double> values; public Coords2D(Double d1, Double d2) { // Set the value of the object's immutable instance variable.
this.values = new OrderedPair(d1, d2);
} protected Double firstValue() { return values.first(); } protected Double secondValue() { return values.second(); } public String toString() { // Return a string of the form (x, y) where x and y are the // first and second coordinate values respectively.
return "(" + firstValue() + ", " + secondValue() + ")";
} }
/** * An ordered pair of Cartesian coordinates: x and y values. */ class CartesianCoords extends Coords2D implements XY { public CartesianCoords(Double x, Double y) { // Pass the values of x and y to the superclass's (the Coords class's) // constructor method.
super(x, y);
} public Double x() { return super.firstValue(); } public Double y() { return super.secondValue(); } public XY plus(XY other) { return new CartesianCoords(this.x() + other.x(), this.y() + other.y()); } public Polar toPolar() { // 1. Compute the distance between the origin (0, 0) and the point (x, y). // This is the radius of the corresponding polar coordinates. // 2. Compute the multi-valued inverse tangent of y and x. This is the // angle of the corresponding polar coordinates. // 3. Use the computed values to create an object of type Polar. // Return the object.
Double x = this.x(), y = this.y(); Double radius = Math.sqrt(x*x + y*y); Double angle = Math.atan2(y, x); return new PolarCoords(radius, angle);
} }
/** * An ordered pair of polar coordinates: radius and angle values. */ class PolarCoords extends Coords2D implements Polar { public PolarCoords(Double radius, Double angle) { // Call the superclass's constructor. Pass the radius value and a possibly // modified angle value. Note that the angle conversion must be done inline. // It can't be performed using statements that precede the call to the // superclass's constructor, because the superclass constructor call must be // executed first, before any other statements in this constructor method // are executed. // // Using the mod (%) operator, convert the angle to a value that is greater // than or equal to -2 * Math.PI and is less than or equal to 2 * Math.PI; // If the (possibly) converted angle is less than zero, then add 2 * Math.PI // and pass that value to the superclass's constructor; otherwise, pass the // converted value.
super(radius, angle < 0 ? (angle % (2 * Math.PI)) + (2 * Math.PI) : (angle % (2 * Math.PI)) );
} public Double radius() { return super.firstValue(); } public Double angle() { return super.secondValue(); } public XY toXY() { // Multiply the value of the radius by Math.cos(this.angle) and by // Math.sin(this.angle) to get the x and y coordinate values respectively. // Create and return an object of type XY using the computed x and y values.
Double x = this.radius() * Math.cos(this.angle()); Double y = this.radius() * Math.sin(this.angle()); return new CartesianCoords(x, y);
} public int compareTo(Polar other) { final Double r1 = this.radius(), r2 = other.radius(); final int radiusCompare = r1.compareTo(r2); return (radiusCompare != 0 && Difference.isSignificant(r1, r2)) ? radiusCompare : this.angle().compareTo(other.angle()); } }
/** * A point in two-dimensional space. */ class Point2D implements PointInAPlane { private final XY xyCoords; private final Polar polarCoords; private String label = null; public Point2D(Point2D p) { this.xyCoords = p.xyCoords; this.polarCoords = p.polarCoords; } public Point2D(XY xyCoords) { this.xyCoords = xyCoords; this.polarCoords = xyCoords.toPolar(); } public Point2D(Polar coords) { this.polarCoords = coords; this.xyCoords = coords.toXY(); } public Double x() { return xyCoords.x(); } public Double y() { return xyCoords.y(); } public Double radius() { return polarCoords.radius(); } public Double angle() { return polarCoords.angle(); } public XY plus(XY other) { return xyCoords.plus(other); } public XY toXY() { return xyCoords; } public Polar toPolar() { return polarCoords; } public int compareTo(Polar other) { return this.polarCoords.compareTo(other); } public void setLabel(String label) { this.label = label; } public String getLabel() { return label; } public String toXYString() { return label + ": " + xyCoords.toString(); } public String toPolarString() { return label + ": " + polarCoords.toString(); } }