diff --git a/ch_22/Exercise22_11.java b/ch_22/Exercise22_11.java index c8371c0..5abab62 100644 --- a/ch_22/Exercise22_11.java +++ b/ch_22/Exercise22_11.java @@ -22,7 +22,7 @@ * public void setRightMostLowestPoint(MyPoint p) {
* rightMostLowestPoint = p;
* }
- * \ @Override public int compareTo(MyPoint o) {
+ * \@Override public int compareTo(MyPoint o) {
* // Implement it to compare this point with point o
* // angularly along the x-axis with rightMostLowestPoint
* // as the center, as shown in Figure 22.10b. By implementing
@@ -67,72 +67,99 @@ public static void main(String[] args) { } public static ArrayList getConvexHull(double[][] s) { - /* Find Lowest and Rightest Point */ - double yMin = s[0][1]; - int minPos = 0; - // Find the bottom most point + /* Grahams Algorithm: + Step 1: Given a list of points S, select the rightmost lowest. + */ + double[] lowestRightMostPt = s[0]; // initialize to first point for (int i = 1; i < s.length; i++) { - double y = s[i][1]; + double[] nextPt = s[i]; // Pick the bottom-most or chose the right // most point in case of tie - if ((y < yMin) || (yMin == y && s[i][0] > s[minPos][0])) { - yMin = s[i][1]; - minPos = i; - - } + lowestRightMostPt = getLowestRightMostPoint(lowestRightMostPt, nextPt); } - // Swamp the bottom-most point for first position - double[] temp = s[0]; - s[0] = s[minPos]; - s[minPos] = temp; - MyPoint rightMostLowestPoint = new MyPoint(s[0][0], s[0][1]); - ArrayList rawPoints = new ArrayList<>(); - rawPoints.add(rightMostLowestPoint); - // Create array of MyPoint objects and set rightMostLowestPoint - for (int i = 1; i < s.length; i++) { - MyPoint pt = new MyPoint(s[i][0], s[i][1]); - pt.setRightMostLowestPoint(rightMostLowestPoint); - rawPoints.add(pt); - } - ArrayList cleanedPoints = new ArrayList<>(); - cleanedPoints.add(rightMostLowestPoint); - //Clean points with same angle - for (int i = 1; i < rawPoints.size() - 1; i++) { - // Only add points where the angle of p[i] and p[i+1] is NOT the same with respect to p0 - if (orientation(rightMostLowestPoint, rawPoints.get(i), rawPoints.get(i + 1)) != 0) { - cleanedPoints.add(rawPoints.get(i)); - } + MyPoint rightMostLowestPoint = new MyPoint(lowestRightMostPt[0], lowestRightMostPt[1]); + /* + Step 2: Sort the points in S angularly along the x-axis with p0 as the + center, as shown in Figure 22.10b. If there is a tie and two points have + the same angle, discard the one that is closer to p0. The points in S are + now sorted as p0, p1, p2, ..., pn-1. + */ + // For Step 2 - We use Comparable interface to sort the points to simplify coding. + ArrayList pointsList = new ArrayList<>(); + for (double[] pt : s) { + MyPoint p = new MyPoint(pt[0], pt[1]); + p.setRightMostLowestPoint(rightMostLowestPoint); + pointsList.add(p); } + // If modified array of points has less than 3 points, // convex hull is not possible - if (cleanedPoints.size() < 3) { + if (pointsList.size() < 3) { throw new RuntimeException("Graham's Algorithm for ConvexHull requires " + "at least 3 points with different angles..."); } - Collections.sort(cleanedPoints); + Collections.sort(pointsList); // Graham scan // Create an empty stack and push first three points to it + /* Step 3: Push p0, p1, and p2 into stack H. (After the algorithm finishes, + H contains all the points in the convex hull.) + i = 3; + while (i < n) { + Let t1 and t2 be the top first and second element in stack H; + if (pi is on the left side of the direct line from t2 to t1) { + Push pi to H; + i++; // Consider the next point in S. + } + else + Pop the top element off stack H. + } + Step 5: The points in H form a convex hull. + */ Stack stack = new Stack<>(); - int m = cleanedPoints.size(); - stack.push(cleanedPoints.get(0)); // p0 (lowest and rightest point) - stack.push(cleanedPoints.get(1)); // p1 - stack.push(cleanedPoints.get(2)); // p2 + int m = pointsList.size(); + MyPoint p0 = pointsList.get(0); + MyPoint p1 = pointsList.get(1); + MyPoint p2 = pointsList.get(2); + stack.push(p0); + stack.push(p1); + stack.push(p2); // Process remaining n-3 points - for (int i = 3; i < m; i++) { + int i = 3; + while (i < m) { // Keep removing top while the angle formed by // points next-to-top, top, and points[i] makes // a non-left turn - while (orientation(nextToTop(stack), stack.peek(), cleanedPoints.get(i)) != 2) { - stack.pop(); - stack.push(cleanedPoints.get(i)); + MyPoint p3 = pointsList.get(i); + int orientation = orientation(p2, p1, p3); + if (orientation == 2) { + stack.push(p3); + } else if (orientation == 1) { + stack.pop(); // pop p2 + stack.push(p3); + } else { + stack.pop(); // pop p2 } + p2 = stack.peek(); + p1 = stack.get(stack.size() - 2); + i++; } // Now stack has the output points, print contents of stack - return new ArrayList<>(stack); } + + static double[] getLowestRightMostPoint(double[] pt1, double[] pt2) { + if (pt2[1] < pt1[1]) { + return pt2; + } else if (pt2[1] == pt1[1]) { + if (pt2[0] > pt1[0]) { + return pt2; + } + } + return pt1; + } + /** * To find orientation of ordered triplet (p, q, r). * @@ -159,17 +186,6 @@ static double distSq(MyPoint p1, MyPoint p2) { (p1.y - p2.y) * (p1.y - p2.y); } - /** - * A utility function to find next to top in a stack - */ - static MyPoint nextToTop(Stack s) { - MyPoint p = s.pop(); - MyPoint res = s.peek(); - s.push(p); - return res; - } - - private static class MyPoint implements Comparable { double x, y; MyPoint rightMostLowestPoint; @@ -195,15 +211,18 @@ public int compareTo(MyPoint o) { // Find orientation int orientation = orientation(rightMostLowestPoint, this, o); if (orientation == 0) { - return distSq(rightMostLowestPoint, o) >= distSq(rightMostLowestPoint, this) ? -1 : 1; + double distSqObj1 = distSq(rightMostLowestPoint, this); + double distSqObj2 = distSq(rightMostLowestPoint, o); + return Double.compare(distSqObj1, distSqObj2); } - return (orientation == 2) ? -1 : 1; + return (orientation == 2) ? 1 : -1; } @Override public String toString() { return "(" + x + ", " + y + ")"; } + } } diff --git a/ch_22/Exercise22_13.java b/ch_22/Exercise22_13.java new file mode 100644 index 0000000..c918326 --- /dev/null +++ b/ch_22/Exercise22_13.java @@ -0,0 +1,191 @@ +package ch_22; + +import java.util.*; + +/** + * **22.13 (Geometry: convex hull animation) Programming Exercise 22.11 finds a convex hull for a set of points + * entered from the console. Write a program that enables the user to add/remove points by clicking the left/right + * mouse button, and displays a convex hull, as shown in Figure 22.8c. + */ +public class Exercise22_13 { + public static void main(String[] args) { + Scanner scanner = new Scanner(System.in); + System.out.print("How many points are in the set? "); + int numPoints = scanner.nextInt(); + if (numPoints < 3) { + throw new RuntimeException("Graham's Algorithm for ConvexHull requires at least 3 points..."); + } + + double[][] points = new double[numPoints][2]; + System.out.print("Enter " + numPoints + " points: "); + + for (int i = 0; i < points.length; i++) { + for (int j = 0; j < 2; j++) { + points[i][j] = scanner.nextDouble(); + } + } + List result = getConvexHull(points); + System.out.println("The convex hull is: "); + System.out.println(Arrays.toString(result.toArray())); + } + + public static ArrayList getConvexHull(double[][] s) { + /* Grahams Algorithm: + Step 1: Given a list of points S, select the rightmost lowest. + */ + double[] lowestRightMostPt = s[0]; // initialize to first point + for (int i = 1; i < s.length; i++) { + double[] nextPt = s[i]; + // Pick the bottom-most or chose the right + // most point in case of tie + lowestRightMostPt = getLowestRightMostPoint(lowestRightMostPt, nextPt); + } + MyPoint rightMostLowestPoint = new MyPoint(lowestRightMostPt[0], lowestRightMostPt[1]); + /* + Step 2: Sort the points in S angularly along the x-axis with p0 as the + center, as shown in Figure 22.10b. If there is a tie and two points have + the same angle, discard the one that is closer to p0. The points in S are + now sorted as p0, p1, p2, ..., pn-1. + */ + // For Step 2 - We use Comparable interface to sort the points to simplify coding. + ArrayList pointsList = new ArrayList<>(); + for (double[] pt : s) { + MyPoint p = new MyPoint(pt[0], pt[1]); + p.setRightMostLowestPoint(rightMostLowestPoint); + pointsList.add(p); + } + + // If modified array of points has less than 3 points, + // convex hull is not possible + if (pointsList.size() < 3) { + throw new RuntimeException("Graham's Algorithm for ConvexHull requires " + + "at least 3 points with different angles..."); + } + Collections.sort(pointsList); + // Graham scan + // Create an empty stack and push first three points to it + /* Step 3: Push p0, p1, and p2 into stack H. (After the algorithm finishes, + H contains all the points in the convex hull.) + i = 3; + while (i < n) { + Let t1 and t2 be the top first and second element in stack H; + if (pi is on the left side of the direct line from t2 to t1) { + Push pi to H; + i++; // Consider the next point in S. + } + else + Pop the top element off stack H. + } + Step 5: The points in H form a convex hull. + */ + Stack stack = new Stack<>(); + int m = pointsList.size(); + MyPoint p0 = pointsList.get(0); + MyPoint p1 = pointsList.get(1); + MyPoint p2 = pointsList.get(2); + stack.push(p0); + stack.push(p1); + stack.push(p2); + // Process remaining n-3 points + int i = 3; + while (i < m) { + // Keep removing top while the angle formed by + // points next-to-top, top, and points[i] makes + // a non-left turn + MyPoint p3 = pointsList.get(i); + int orientation = orientation(p2, p1, p3); + if (orientation == 2) { + stack.push(p3); + } else if (orientation == 1) { + stack.pop(); // pop p2 + stack.push(p3); + } else { + stack.pop(); // pop p2 + } + p2 = stack.peek(); + p1 = stack.get(stack.size() - 2); + i++; + + } + // Now stack has the output points, print contents of stack + return new ArrayList<>(stack); + } + + + static double[] getLowestRightMostPoint(double[] pt1, double[] pt2) { + if (pt2[1] < pt1[1]) { + return pt2; + } else if (pt2[1] == pt1[1]) { + if (pt2[0] > pt1[0]) { + return pt2; + } + } + return pt1; + } + + /** + * To find orientation of ordered triplet (p, q, r). + * + * @param p first point + * @param q second point + * @param r third point + * @return 0 --> p, q and r are co-linear + * 1 --> Clockwise + * 2 --> Counterclockwise + */ + static int orientation(MyPoint p, MyPoint q, MyPoint r) { + double val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); + if (val == 0) return 0; // co-linear + return (val > 0) ? 1 : 2; // clock or counter-clock wise + } + + + /** + * A utility function to return square of distance + * between p1 and p2 + */ + static double distSq(MyPoint p1, MyPoint p2) { + return (p1.x - p2.x) * (p1.x - p2.x) + + (p1.y - p2.y) * (p1.y - p2.y); + } + + private static class MyPoint implements Comparable { + double x, y; + MyPoint rightMostLowestPoint; + + MyPoint(double x, double y) { + this.x = x; + this.y = y; + } + + public void setRightMostLowestPoint(MyPoint p) { + rightMostLowestPoint = p; + } + + /** + * Implement it to compare this point with point o + * angularly along the x-axis with rightMostLowestPoint + * as the center, as shown in Figure 22.10b. By implementing + * the Comparable interface, you can use the Array.sort() + * method to sort the points to simplify coding. + */ + @Override + public int compareTo(MyPoint o) { + // Find orientation + int orientation = orientation(rightMostLowestPoint, this, o); + if (orientation == 0) { + double distSqObj1 = distSq(rightMostLowestPoint, this); + double distSqObj2 = distSq(rightMostLowestPoint, o); + return Double.compare(distSqObj1, distSqObj2); + } + return (orientation == 2) ? 1 : -1; + } + + @Override + public String toString() { + return "(" + x + ", " + y + ")"; + } + + } + +} \ No newline at end of file