Pages

Points on the Circumference of a Circle

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