package convexHull; import java.applet.*; import java.awt.*; import java.awt.event.*; import java.util.*; import java.io.*; /** * This class implements a user interface (both for main() and Applet) * to test the code for convex hull algorithm. */ public class ConvexHull extends Applet implements ActionListener, ItemListener { protected Vector points; // set of all points protected Vector hull; // points on the convex hull protected Vector stairs; // stairs points protected Canvas canvas; // drawing canvas protected Button quitButton; // quit button protected Button clearButton; // clear button protected Checkbox hullCheckbox; // hull checkbox protected Checkbox stairsCheckbox; // stairs checkbox protected float minx, miny, maxx, maxy; // bounding box for the points private static final float MIND = 0.000001f; private static boolean running_as_applet = true; /** * initializes empty data-structures */ public ConvexHull() { points = new Vector(); hull = new Vector(); stairs = new Vector(); miny = minx = -1.0f; maxx = maxy = 1.0f; } /** * Handles button events. * * quit button --> exit the program * * clear button --> erase the points and the convex hull, * then redraw the screen */ public void actionPerformed( ActionEvent e) { if (e.getSource() == quitButton) System.exit(0); else if (e.getSource() == clearButton) { points = new Vector(); hull = new Vector(); stairs = new Vector(); canvas.paint(canvas.getGraphics()); } } /** * Handles checkbox events. */ public void itemStateChanged( ItemEvent e) { // recompute what needs to be recomputed if( hullCheckbox.getState()) hull = GrahamScan.computeHull( points); if( stairsCheckbox.getState()) stairs = GrahamScan.computeStairs( points); // now we would like to draw the result... canvas.paint(canvas.getGraphics()); } /** * adds a new point to points[] */ protected void addPoint(CPoint p) { // add the new point to the vector points.addElement(p); // recompute what needs to be recomputed if( hullCheckbox.getState()) hull = GrahamScan.computeHull( points); if( stairsCheckbox.getState()) stairs = GrahamScan.computeStairs( points); // redraw the data canvas.paint(canvas.getGraphics()); } /** * Gets the applet information. * @return java.lang.String */ public String getAppletInfo() { return "convexHull.ConvexHull created by Pavol Federl"; } /** * Gets a floating point number from the stream. * * @param st StreamTokenizer * * @return floating point number (next on the input) * * @exception */ private float getNum( StreamTokenizer st) throws EOFException, IOException { while( true) { int tok = st.nextToken(); if( tok == st.TT_EOF) throw new EOFException(); else if( tok == st.TT_EOL) continue; else if( tok == st.TT_NUMBER) return (float) st.nval; else throw new IOException(); } } /** * Read points from a data file. * @param fname name of the file to read the data from * @return boolean true = error occurd, false = no errors */ public boolean readData( String fname) { StreamTokenizer st = null; try { // open a stream tokenizer st = new StreamTokenizer( new FileReader( fname)); // start reading in points while( true) { float x = getNum( st); float y = getNum( st); points.addElement( new CPoint( x, y)); } } catch( FileNotFoundException e) { System.err.println( "File '" + fname + "' not found."); return true; } catch( EOFException e) { // we have parsed the file successfuly System.out.println( "File '" + fname + "' contains " + points.size() + " points."); return false; } catch( IOException e) { // some formattin was incorrect if( st == null) System.err.println( fname+": format error"); else System.err.println( fname+":"+st.lineno()+": format error"); return true; } } /** * Initializes the user interface for the applet */ public void init() { setName("ConvexHull"); setLayout(new BorderLayout()); // add a bottom panel for buttons Panel buttonPanel = new Panel(); buttonPanel.setLayout(new FlowLayout()); if( ! running_as_applet) { quitButton = new Button("Quit"); quitButton.addActionListener(this); quitButton.setForeground(Color.red); buttonPanel.add(quitButton); } else { quitButton = null; } clearButton = new Button("Clear points"); clearButton.addActionListener(this); buttonPanel.add(clearButton); hullCheckbox = new Checkbox("Show hull", null, false); hullCheckbox.addItemListener(this); stairsCheckbox = new Checkbox( "Show stairs", null, false); stairsCheckbox.addItemListener(this); buttonPanel.add(stairsCheckbox); buttonPanel.add(hullCheckbox); add("South", buttonPanel); // create a canvas canvas = new Canvas() { public void paint(Graphics orig_g) { Graphics2D g = new Graphics2D( orig_g, minx, miny, maxx, maxy, canvas.getSize().width, canvas.getSize().height); // clear the screen g.setColor(getBackground()); g.clearRect(minx, miny, maxx-minx, maxy-miny); // draw all points (in black) g.setColor(Color.black); for (int i = 0; i < points.size(); i++) g.drawPoint( ((CPoint) points.elementAt(i)).x, ((CPoint) points.elementAt(i)).y, 1); if( stairsCheckbox.getState()) { // draw stair points (in green) g.setColor( new Color( 0, 167, 0)); for( int i = 0 ; i < stairs.size(); i ++) g.drawPoint( ((CPoint) stairs.elementAt(i)).x, ((CPoint) stairs.elementAt(i)).y, 3); } if( hullCheckbox.getState()) { // draw the convex hull edges (in red) g.setColor(Color.red); for (int i = 0; i < hull.size() - 1; i++) g.drawLine( ((CPoint) hull.elementAt(i)).x, ((CPoint) hull.elementAt(i)).y, ((CPoint) hull.elementAt(i + 1)).x, ((CPoint) hull.elementAt(i + 1)).y); if (hull.size() > 0) g.drawLine( ((CPoint) hull.lastElement()).x, ((CPoint) hull.lastElement()).y, ((CPoint) hull.firstElement()).x, ((CPoint) hull.firstElement()).y); // draw convex hull points in red g.setColor(Color.red); for (int i = 0; i < hull.size(); i++) g.drawPoint( ((CPoint) hull.elementAt(i)).x, ((CPoint) hull.elementAt(i)).y, 2); // label the convex hull points (grey color) g.setColor( new Color( 44, 44, 209)); for (int i = 0; i < hull.size(); i++) g.drawString( "" + i, ((CPoint) hull.elementAt(i)).x, ((CPoint) hull.elementAt(i)).y); } } }; canvas.setSize(500, 500); /** * Add a mouse event handler to the canvas. */ canvas.addMouseListener( new MouseAdapter() { public void mousePressed(MouseEvent e) { // add a new point float x = (e.getX() / (float) canvas.getSize().width) * (maxx-minx) + minx; float y = maxy - (e.getY() / (float) canvas.getSize().height) * (maxy-miny); addPoint(new CPoint(x, y)); } }); // put this canvas into the MouseContainer MouseCanvas mouseContainer = new MouseCanvas(canvas); mouseContainer.setBackground(Color.lightGray); add("Center", mouseContainer); } /** * main entrypoint - starts the part when it is run as an application * @param args java.lang.String[] */ public static void main( String[] args) { // we are not running as an applet running_as_applet = false; // create an applet and initialize it ConvexHull applet = new ConvexHull(); applet.runMain( args); } public void runMain( String args[]) { // if we have a command line argument, it is a filename from which // we have to read in the set of points if( args.length == 1) { // read the data if( readData( args[0]) || points.size() < 1) { System.err.println( "Cannot read data from file " + args[0]); System.exit( -1); } // find the bounding box of the input points CPoint p = (CPoint) points.elementAt( 0); minx = p.x; maxx = p.x; miny = p.y; maxy = p.y; for( int i = 1 ; i < points.size() ; i ++) { p = (CPoint) points.elementAt( i); if( minx > p.x) minx = p.x; if( miny > p.y) miny = p.y; if( maxx < p.x) maxx = p.x; if( maxy < p.y) maxy = p.y; } // add 10% to the boundaries float maxd = maxx - minx; if( maxy - miny > maxd) maxd = maxy - miny; if( maxd < MIND) maxd = MIND; minx -= maxd * 0.1; miny -= maxd * 0.1; maxx += maxd * 0.1; maxy += maxd * 0.1; /* // compute the stairs stairs = GrahamScan.computeStairs( points); System.out.println( "There are " + stairs.size() + " stairs:"); GrahamScan.printPoints( stairs); */ // compute the hull hull = GrahamScan.computeHull( points); System.out.println( "There are " + hull.size() + " points on the convex hulll:"); GrahamScan.printPoints( hull); } else if( args.length != 0) { // report an error System.out.println( "Usage error!!!"); System.out.println( "Usage: java ConvexHull [filename]."); System.exit( -1); } // call my own init method - applet stuff init(); // create a frame and set its layout to be BorderLayout Frame frame = new Frame(); frame.setLayout(new BorderLayout()); // add the applet into the frame frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); frame.add("Center", this); // frame.pack(); frame.setSize(500, 400); frame.setVisible(true); } }