//
import java.awt.*; import java.util.*; /* THINGS TO ADD: Cleaner API - arrays should be created internally Shapes other than circular disk Allow some robots to be more "stubborn" than others General obstacle objects */ public class pathApplet extends BufferedApplet { int w = 0, h = 0, step = 0, nSteps = 1<<6, relaxing = 0, randomSeed = 0; PathFinder pf; // EACH ROBOT HAS A RADIUS OF PERSONAL SPACE double radius[] = {.20,.18,.16,.14,.12,.10}; // EACH ROBOT HAS A COLOR double color[][] = { {1 , 0, 0}, // RED {1 ,.5, 0}, // ORANGE {1 ,.8, 0}, // YELLOW {0 ,.7, 0}, // GREEN {0 , 0, 1}, // BLUE {.5, 0,.5}, // VIOLET }; double X[][] = new double[radius.length][nSteps+1]; double Y[][] = new double[radius.length][nSteps+1]; // DISPLAY EVERYTHING public void render(Graphics g) { // MAKE INITIAL PATH if (w == 0) { w = bounds().width; h = bounds().height; pf = new PathFinder(radius, X, Y); newRandomPaths(randomSeed); } // BLANK OUT THE APPLET WINDOW g.setColor(Color.white); g.fillRect(0,0,w,h); // DRAW THE SOLID DISK OF EACH ROBOT for (int i = 0 ; i < radius.length ; i++) { g.setColor(new Color((float)(.7+.3*color[i][0]), (float)(.7+.3*color[i][1]), (float)(.7+.3*color[i][2]))); int x = X2x(X[i][step]); int y = Y2y(Y[i][step]); int r = R2r(radius[i]); g.fillOval(x-r,y-r,2*r+1,2*r+1); } // DRAW THE PATHS AND THE ROBOT OUTLINES for (int i = 0 ; i < radius.length ; i++) { g.setColor(new Color((float)color[i][0], (float)color[i][1], (float)color[i][2])); for (int s = 0 ; s <= nSteps ; s++) { int x = X2x(X[i][s]); int y = Y2y(Y[i][s]); g.fillOval(x-1,y-1,3,3); } int x = X2x(X[i][step]); int y = Y2y(Y[i][step]); int r = R2r(radius[i]); g.drawOval(x-r,y-r,2*r,2*r); int d = step==0 || step==nSteps ? 5 : 3; g.fillOval(x-d,y-d,2*d,2*d); } // DRAW THE "SEED POINTS" RECTANGLE, AND SHOW THE SETUP ID g.setColor(Color.black); //g.drawRect(X2x(-.75),Y2y(.75),R2r(1.5),R2r(1.5)); g.drawString("" + randomSeed, 4, 14); // GRADUALLY RELAX THE PATHS TO SMOOTH THEM OUT if (relaxing-- > 0) { pf.relax(); animating = true; } } // RESPOND TO USER KEY COMMANDS public boolean keyUp(Event e, int key) { switch (key) { case '-': // MAKE ROBOTS SMALLER changeSizes(-.02); break; case '+': // MAKE ROBOTS LARGER changeSizes(.02); break; case 1002: // PAGE UP: GO TO END OF PATHS step = nSteps; damage = true; break; case 1003: // PAGE DOWN: GO TO START OF PATHS step = 0; damage = true; break; case 1004: // UP ARROW: ADVANCE ALONG PATHS step = Math.min(step + nSteps/32, nSteps); damage = true; break; case 1005: // DOWN ARROW: RETREAT ALONG PATHS step = Math.max(step - nSteps/32, 0); damage = true; break; case 1006: // LEFT ARROW: PREVIOUS RANDOM SETUP newRandomPaths(--randomSeed); break; case 1007: // RIGHT ARROW: NEXT RANDOM SETUP newRandomPaths(++randomSeed); break; } return true; } // ALLOW USER TO DRAG ROBOTS AROUND WITH THE MOUSE int mx, my, id = -1; public boolean mouseDown(Event e, int x, int y) { // ON MOUSE DOWN: SEE WHAT DISK, IF ANY, THE MOUSE IS OVER if (step == 0 || step == nSteps) for (id = radius.length-1 ; id >= 0 ; id--) { double dx = x2X(x) - X[id][step]; double dy = y2Y(y) - Y[id][step]; if (dx*dx + dy*dy < radius[id]*radius[id]) break; } mx = x; my = y; return true; } public boolean mouseDrag(Event e, int x, int y) { if (id >= 0) { X[id][step] += r2R(x - mx); Y[id][step] -= r2R(y - my); mx = x; my = y; pf.repel(step); pf.modifyPaths(); relaxing = 500; damage = true; } return true; } public boolean mouseUp(Event e, int x, int y) { id = -1; damage = true; return true; } // CHANGE SIZES OF ROBOTS (WITHIN LIMITS) void changeSizes(double delta) { // MAKE SURE WE'RE NOT TRYING TO MAKE ROBOTS TOO BIG OR TOO SMALL double minRadius = 100, maxRadius = -100; for (int i = 0 ; i < radius.length ; i++) { minRadius = Math.min(minRadius, radius[i]); maxRadius = Math.max(maxRadius, radius[i]); } // IF WITHIN LIMITS, THEN CHANGE SIZES AND RECOMPUTE THE PATHS if (minRadius + delta > .01 && maxRadius + delta < .4) { for (int i = 0 ; i < radius.length ; i++) radius[i] += delta; pf.repel(0); pf.repel(nSteps); pf.findPaths(); damage = true; } } // MAKE A NEW PATH, GIVEN A RANDOM SEED void newRandomPaths(int seed) { // CHOOSE RANDOM POSITIONS AT START AND END OF PATH Random rand = new Random(seed); randomize(rand, 0); randomize(rand, nSteps); // FIND THE PATHS, AND START GRADUAL RELAXATION pf.findPaths(); step = 0; relaxing = 500; damage = true; } // ARRANGE ROBOTS IN SOME RANDOM CONFIGURATION AROUND A SQUARE void randomize(Random rand, int s) { for (int i = 0 ; i < radius.length ; i++) { double x = (Math.abs(rand.nextDouble()) % 1) - .5; double y = (Math.abs(rand.nextDouble()) % 1) - .5; if (Math.abs(x) > Math.abs(y)) { y /= Math.abs(x); x /= Math.abs(x); } else { x /= Math.abs(y); y /= Math.abs(y); } X[i][s] = .75*x; Y[i][s] = .75*y; } pf.repel(s); } // CONVERT SCREEN COORDS TO ROBOT COORDS double x2X(int x) { return (double)(x - w/2) / (w/2); } double y2Y(int y) { return (double)(y - h/2) / -(w/2); } double r2R(int r) { return (double)r / (w/2); } // CONVERT ROBOT COORDS TO SCREEN COORDS int X2x(double X) { return (int)(w/2 + X * w/2); } int Y2y(double Y) { return (int)(h/2 - Y * w/2); } int R2r(double R) { return (int)(R * w/2); } }