// // ConvexHull.java // ConvexHull // // Created by Ed Hong on 10/21/05. // Copyright (c) 2005 __MyCompanyName__. All rights reserved. // import java.util.*; import java.awt.geom.*; import java.io.*; public class ConvexHull { // This program runs the two convex hull algorithms on 2D input from // text file. The text input file format is a simple numbers // separated by white space, x coordinate first. // Both Brute-force and quickhull are run. It is your job to finish // implementing quickhull. public static void main (String args[]) { // insert code here... Point2D.Double[] inputpoints, outputpolygon; String inputfilename; // if you enter a command line argument, assume it's the filename of the // text file containing the input points; otherwise ask the user for the input filename. if (args.length >0) { inputfilename = args[0]; } else { System.out.print("Enter file name for input points: "); Scanner sc = new Scanner(System.in); inputfilename = sc.next(); } System.out.println("Reading input points from file " + inputfilename); inputpoints = readPoints(inputfilename); // debugging print statements /* for(int i = 0; i < inputpoints.length; i++) System.out.println(i + ") " + inputpoints[i]); */ // call the convex-hull algorithms & print results. outputpolygon = bruteForceConvexHull(inputpoints); System.out.println("Polygon result from brute-force: "); for(int i = 0; i < outputpolygon.length; i++) { if (outputpolygon[i] == null) break; System.out.println(i + ") " + outputpolygon[i]); } outputpolygon = QuickHull(inputpoints); System.out.println("Polygon result from QuickHull: "); for(int i = 0; i < outputpolygon.length; i++) { if (outputpolygon[i] == null) break; System.out.println(i + ") " + outputpolygon[i]); } } // Creates array of Point2D.Double read from the input filename. // returns array of points. public static Point2D.Double [] readPoints(String filename) { Point2D.Double[] outputpoints; Scanner sc; try { sc = new Scanner(new File (filename)); } catch (IOException e) { System.out.println("Error reading file " + filename); return null; } // first create an ArrayList of points, so we don't have to // worry about how many there are when reading the file. ArrayList temppoints = new ArrayList(); //loop for reading one point at a time. while (sc.hasNextDouble()) { double xval, yval; xval = sc.nextDouble(); if (sc.hasNextDouble() == false) { System.out.println("Warning: last point has no y-coordinate; assigning y-coord of zero"); yval =0.0; } else yval = sc.nextDouble(); temppoints.add(new Point2D.Double(xval,yval)); } // Former debugging print statement. // System.out.println(temppoints); //convert the arraylist into an array of the right type, and return it. // the input parameter to toArray makes sure the types are correct. // let me know if you know a better way to do this.. outputpoints = temppoints.toArray(new Point2D.Double[1]); return outputpoints; } // executes the brute-force algorithm for finding the convex hull of its input points. // The output polygon is an array of vertices in order. The array size of the polygon // may be larger than the number of vertices; if this the case, the index after the // last vertex stores a null Object. public static Point2D.Double [] bruteForceConvexHull(Point2D.Double [] inputpointset) { Point2D.Double[] result = new Point2D.Double[inputpointset.length]; // first search for the point with minimum x value and start there. Point2D.Double startpoint, p1, p2; startpoint = inputpointset[0]; int i; for (i=1; i < inputpointset.length; i++) { if (startpoint.x > inputpointset[i].x) startpoint = inputpointset[i]; } result[0] = startpoint; // now start a loop, searching for the next vertex after the first one. // in this code, we always search in the clockwise direction. int currentvertexindex = 0; Point2D.Double currentvertex = result[currentvertexindex]; Point2D.Double pointtocheck = null; boolean done; if (inputpointset.length==1) done = true; else done = false; while (!done) { // loop through each point to see if it is the next vertex for (i=0; i < inputpointset.length; i++) { pointtocheck = inputpointset[i]; // can't test equal points because they don't form a line. if (!pointtocheck.equals(currentvertex)) { // check to see if all points are on right side of line // from currentvertex to pointtocheck. if (allPointsOnRight(currentvertex, pointtocheck, inputpointset)) { // pointtocheck is a vertex of polygon; break out. break; } } } // at this point, we should have found a vertex. // if i== inputpointset.length at this point, we didn't find a vertex, but that // should never happen! // if the found vertex is the same as the first vertex, we have finished. // otherwise, add in the vertex and continue the loop. if (pointtocheck.equals(result[0])) done = true; else { // add a new vertex, get ready for next loop iteration. currentvertexindex++; result[currentvertexindex] = pointtocheck; currentvertex = pointtocheck; } } return result; } // Takes input points p1 and p2, array of points A. // consider the line from p1 to p2. // if all points in A are on the line from p1 to p2 or to the right // of the line (The "right" side of the line is the one where // you are assumed to be at p1 looking towards p2) public static boolean allPointsOnRight(Point2D.Double p1, Point2D.Double p2, Point2D.Double[] A) { // check all points to see if they are all on the right. // use the properties of the determinant. for (int i=0; i < A.length; i++) { if (determinantformula(p1, p2, A[i]) > 0) return false; } return true; } // for input points p1 = (x1,y1), p2 = (x2,y2), p3 = (x3,y3), computes the determinant // | x1 y1 1 | // | x2 y2 1 | // | x3 y3 1 | // This determinant is positive when point p3 is to the left of the line // from p1 to p2, and negative when point p3 is to the right of the line // Furthermore, the magnitude of this determinant is the twice the area // of the enclosed triangle. public static double determinantformula(Point2D.Double p1,Point2D.Double p2, Point2D.Double p3) { return (p1.x * p2.y + p3.x * p1.y + p2.x * p3.y - p3.x * p2.y - p2.x * p1.y - p1.x * p3.y); } // executes the QuickHull algorithm for finding the convex hull of its input points. // The output polygon is an array of vertices in order. The array size of the polygon // may be larger than the number of vertices; if this the case, the index after the // last vertex stores a null Object. public static Point2D.Double [] QuickHull(Point2D.Double [] inputpointset) { System.out.println("QuickHull is not yet implemented and currently returns its input"); return inputpointset; } }