//
import java.awt.*; import java.util.*; public class topView extends BufferedApplet { double time0 = 0, theta = 0, rate = 3; double X[], Y[], TX[], TY[]; Color color[] = { new Color(255,0,0), // RED new Color(255,160,0), // ORANGE new Color(255,255,0), // YELLOW new Color(0,200,0), // GREEN new Color(0,255,255), // CYAN new Color(0,0,255), // BLUE new Color(200,0,255), // INDIGO new Color(255,0,190), // VIOLET new Color(0,0,0), // BLACK }; // INITIALIZE EVERYTHING public void init() { super.init(); time0 = System.currentTimeMillis() / 1000.; // INITIAL CLOCK TIME X = new double[color.length]; // ALLOCATE ARRAYS Y = new double[color.length]; TX = new double[color.length]; TY = new double[color.length]; Random R = new Random(); for (int i = 0 ; i < color.length ; i++) { // ASSIGN X[i] = 2 * (R.nextDouble() - .5); // RANDOM POSITIONS Y[i] = 2 * (R.nextDouble() - .5); TX[i] = i % 3 - 1; // ASSIGN TARGET TY[i] = i / 3 - 1; // POSITIONS } } double C0 = -10, S0; // PREVIOUS SINE AND COSINE double R = .2; // DISK SIZE public void render(Graphics g) { // CLEAR THE WINDOW g.setColor(Color.gray.brighter()); g.fillRect(0,0,width(),height()); int r = R2r(R); // ADVANCE TO NEXT STIRRING ANGLE theta = rate * Math.PI * (System.currentTimeMillis()/1000.-time0); double C = .06 * Math.cos(theta); double S = .06 * Math.sin(theta); if (C0 == -10) { C0 = C; S0 = S; } // DRAW THE STIRRER TABLE int x0 = width()/10, y0 = height()/10; int x = x0 + R2r(C), y = y0 - R2r(S), w = 8*width()/10; int d = R2r(.09); g.setColor(Color.gray); g.fillOval(x0 -d, y0 -d, 2*d,2*d); g.fillOval(x0+w-d, y0 -d, 2*d,2*d); g.fillOval(x0+w-d, y0+w-d, 2*d,2*d); g.fillOval(x0 -d, y0+w-d, 2*d,2*d); g.setColor(Color.white); g.fillRect(x,y,w,w); g.setColor(Color.black); g.drawRect(x,y,w,w); // SHAKE EACH DISK TOWARD ITS TARGET for (int i = 0 ; i < X.length ; i++) if (i != ci) { double DX = TX[i] - X[i], DY = TY[i] - Y[i]; if (DX*S + DY*C < 0) { double ampl = Math.min(1, Math.sqrt(DX*DX + DY*DY) / R); if (ampl > .01) { X[i] += ampl * (C - C0); Y[i] -= ampl * (S - S0); } } } // DON'T LET DISKS GO OFF TABLE for (int i = 0 ; i < X.length ; i++) { X[i] = Math.min(X[i], 1.5 - R/2); X[i] = Math.max(X[i], R/2 - 1.5); Y[i] = Math.min(Y[i], 1.5 - R/2); Y[i] = Math.max(Y[i], R/2 - 1.5); } // DON'T LET DISKS INTERPENETRATE EACH OTHER for (int i = 0 ; i < X.length-1 ; i++) for (int j = i+1 ; j < X.length ; j++) { double DX = X[j] - X[i], DY = Y[j] - Y[i]; if (DX*DX + DY*DY < 4*R*R) { double t = Math.sqrt(DX*DX + DY*DY); double u = DX / t * (2*R - t); double v = DY / t * (2*R - t); double f = ci == i ? 0 : ci == j ? 1 : .5; X[i] -= u * f; Y[i] -= v * f; X[j] += u * (1-f); Y[j] += v * (1-f); } } // DRAW THE DISKS for (int i = 0 ; i < X.length ; i++) { g.setColor(color[i].darker()); g.fillOval(X2x(X[i])-r+2, Y2y(Y[i])-r+2, 2*r, 2*r); g.setColor(color[i]); g.fillOval(X2x(X[i])-r, Y2y(Y[i])-r, 2*r, 2*r); } // REMEMBER PREVIOUS STIR POSITION. FORCE ANIMATION. C0 = C; S0 = S; animating = true; } // CONVERSION BETWEEN CONTINUOUS TABLE COORDS AND PIXEL COORDS int X2x(double X) { return (int)(width ()/2 + width()/4 * X); } int Y2y(double Y) { return (int)(height()/2 + width()/4 * Y); } int R2r(double R) { return (int)( width()/4 * R); } double x2X(int x) { return (double)(x - width ()/2) / (width()/4); } double y2Y(int y) { return (double)(y - height()/2) / (width()/4); } double r2R(int r) { return (double) r / (width()/4); } // LET USER DRAG A DISK AROUND WITH THE MOUSE int mx, my, ci = -1; public boolean mouseDown(Event e, int x, int y) { for (ci = X.length-1 ; ci >= 0 ; ci--) { double DX = x2X(x) - X[ci], DY = y2Y(y) - Y[ci]; if (DX*DX + DY*DY < R*R) break; } mx = x; my = y; return true; } public boolean mouseDrag(Event e, int x, int y) { if (ci >= 0) { X[ci] += r2R(x - mx); Y[ci] += r2R(y - my); mx = x; my = y; } return true; } public boolean mouseUp(Event e, int x, int y) { ci = -1; return true; } // LET USER TOGGLE RATE WITH SPACE KEY public boolean keyUp(Event e, int key) { switch (key) { case ' ': rate = rate == 3 ? 9 : 3; break; } return true; } }