/* * Program Name: Graham_Anim.java * * Author: Connie K. Peng * * Description: * * This program is written in JAVA. It draw a convext hull for a * given set of points. The algorithm used here is Graham Scan. * This is a JAVA applet can be run under Netscape 2.0 or HotJAVA * brower. * You can try this demo at * http://www.cs.umd.edu/~peng/cmsc754/convex_hull.html * */ import java.awt.*; /* * define the convex hull applet */ public class Graham_Anim extends java.applet.Applet implements Runnable { DrawingBoard board; ControlArea control; Thread runner = null; boolean first = true; public void init() { setLayout(new GridLayout(1, 2, 10, 10)); /* * Create Canvas Area */ board = new DrawingBoard(this); /* * Create Button Area */ control = new ControlArea(board, "CLEAR", "OK"); add(board); add(control); } public void start() { if (runner == null && first == false) { runner = new Thread(this); runner.start(); } first = false; } public void stop() { if (runner != null) { runner.stop(); runner = null; } } public void run() { /* * push point 0 and point n-1 to the stack */ board.extream.Push(board.currspots-1); board.extream.Push(0); int i = 1; while (runner != null) i = board.Graham_Scan(i); } public Insets insets() { return new Insets(10, 10, 10, 10); } } /* * Define the ControlArea Class */ class ControlArea extends Panel { Button ok, clear; /* two buttons */ DrawingBoard caller; /* the object that the button controls */ /* * Constructor */ ControlArea(DrawingBoard target, String l1, String l2) { this.caller = target; setLayout(new GridLayout(2, 1, 40, 40)); clear = new Button(l1); ok = new Button(l2); add(clear); add(ok); } public Insets insets() { return new Insets(10, 10, 10, 10); } /* * Mouse button event handler */ public boolean action(Event evt, Object arg) { if (evt.target == ok) this.caller.convex_hull(); if (evt.target == clear) this.caller.clear(); return true; } } /* * Define the DrawingBoard class */ class DrawingBoard extends Canvas { final int MAXSPOTS = 100; /* set maximun number of points to be 100 */ final int LEFT = 1; final int COLLINEAR = 0; final int RIGHT = -1; int xspots[] = new int[MAXSPOTS]; /* the array of all points */ int yspots[] = new int[MAXSPOTS]; Point_Stack extream = new Point_Stack(MAXSPOTS + 1); /* the stack that contains all the extream points of convex hull */ int currspots = 0; /* current number of points drawn on the canvas */ Graham_Anim caller; DrawingBoard(Graham_Anim parent) { caller = parent; setBackground(Color.white); } public Insets insets() { return new Insets(10, 10, 10, 10); } /* * mouse action handler */ public boolean mouseDown(Event evt, int x, int y) { if (currspots < MAXSPOTS) addspot(x, y); else System.out.println("Too many points"); return true; } /* * define the function that draw a spot on the canvas */ void addspot(int x, int y) { caller.stop(); xspots[currspots] = x; yspots[currspots] = y; currspots++; extream.top = -1; repaint(); } /* * define the function that clear the screen of canvas */ void clear() { caller.stop(); currspots = 0; extream.top = -1; repaint(); } /* * the paint function that paints the screen when repaint() is called. */ public void paint(Graphics g) { int i; int index; g.setColor(Color.black); /* * paint all the points on the canvas */ for (i = 0; i < currspots; i++) g.fillOval(xspots[i] - 3, yspots[i] - 3, 6, 6); /* * if the convex hull is caculated, also paint the convex hull * on to the canvas */ if (extream.top > -1) { Polygon poly = new Polygon(); for (i = 0; i <= extream.top; i++) { index = extream.index[i]; poly.addPoint(xspots[index], yspots[index]); } g.setColor(Color.blue); g.drawPolygon(poly); } } /* * the main function. It calculates the convex hull of the given * points in spots[]. The function use the Graham Scan algorithm. */ void convex_hull() { if (currspots > 2) { /* * find the lowest right points, make it to be spots[0]. */ Find_Lowest(); /* * Sort all the points in the order of graham scan. */ Quick_Sort(1, currspots - 1); /* * do the graham scan, store all the extream points of the convex * hull into the stack. */ extream.top = -1; caller.start(); /* * paint the convex hull to the canvas */ repaint(); } /* * there is no convex hull for less than 2 points. */ else System.out.println("Too Few Points."); } /* * the function that find the lowest right point */ void Find_Lowest() { int xlow; int ylow; int k = 0; for (int i = 1; i < currspots; i++) { if (yspots[i] < yspots[k]) k = i; else if (yspots[i] == yspots[k]) { if (xspots[i] > xspots[k]) k = i; } } /* * swap the two points, make lowest right point to be spots[0] */ xlow = xspots[k]; ylow = yspots[k]; xspots[k] = xspots[0]; yspots[k] = yspots[0]; xspots[0] = xlow; yspots[0] = ylow; } /* * the function that does the quick sort for the points */ void Quick_Sort(int p, int r) { int q; if (p < r) { q = partition(p, r); Quick_Sort(p, q); Quick_Sort(q + 1, r); } } int partition(int p, int r) { int xp; int yp; int xtemp; int ytemp; xp = xspots[p]; yp = yspots[p]; int i = p - 1; int j = r + 1; while (1 == 1) { do j--; while (Compare(xp, yp, xspots[j], yspots[j]) == -1); do i++; while (Compare(xp, yp, xspots[i], yspots[i]) == 1); if (i < j) { /* * swap the two points */ xtemp = xspots[i]; ytemp = yspots[i]; xspots[i] = xspots[j]; yspots[i] = yspots[j]; xspots[j] = xtemp; yspots[j] = ytemp; } else { xspots[p] = xspots[j]; yspots[p] = yspots[j]; xspots[j] = xp; yspots[j] = yp; break; } } return j; } /* * the function that compare two points to determine which one should * be the front element in the array */ int Compare(int x1, int y1, int x2, int y2) { int side; int xdist1; int ydist1; int xdist2; int ydist2; /* * check if p2 is to the left or to the right of line segement of * point spots[0] and p1 */ side = Which_Side(xspots[0], yspots[0], x1, y1, x2, y2); if (side == LEFT) return -1; else if (side == RIGHT) return 1; else /* * if p2 is collinear with the line segment, then check which point * among p1 and p2 is closer to spots[0] */ { xdist1 = x1 - xspots[0]; ydist1 = y1 - yspots[0]; xdist2 = x2 - xspots[0]; ydist2 = y2 - yspots[0]; if ((xdist1 * xdist1 + ydist1 * ydist1) < (xdist2 * xdist2 + ydist2 * ydist2)) return -1; else return 1; } } /* * function that determine which side is point 3 to the segement * 1-2. */ int Which_Side(int x1, int y1, int x2, int y2, int x3, int y3) { int area; area = x1 * y2 - y1 * x2 + y1 * x3 - x1 * y3 + x2 * y3 - x3 * y2; if (area > 0) return LEFT; else if (area == 0) return COLLINEAR; else return RIGHT; } /* * the function does graham scan as described in the book */ int Graham_Scan(int i) { if (i >= currspots) { caller.stop(); return i; } /* * if it is a left turn, push the point to the stack */ if (Which_Side(xspots[extream.index[extream.top-1]], yspots[extream.index[extream.top-1]], xspots[extream.index[extream.top]], yspots[extream.index[extream.top]], xspots[i], yspots[i]) == LEFT) { extream.Push(i); i++; } /* * if it is not a left turn, pop out one point from the stack */ else extream.Pop(); repaint(); try {Thread.sleep(300);} catch (InterruptedException e) { } return i; } } /* * define the class of a stack that contains points */ class Point_Stack { int top; /* the array index for the top element */ int index[]; /* * constructor */ Point_Stack(int max) { index = new int[max]; top = -1; } /* * Push function */ void Push(int i) { top++; index[top] = i; } /* * Pop function */ void Pop() { top--; } }