import java.awt.*; public class Repel2 extends BufferedApplet { RepelThread2 rt = null; String[] spinButtonLabels = {"SPIN","STOP"}; Button spinButton; String[] sliderLabels = {"TREND1","TREND2","TREND3","ENERGY","GRAVITY"}; Slider[] slider = new Slider[sliderLabels.length]; public void stop() { super.stop(); if (rt != null) rt.stop(); } int n=8, N[] = {4,6,8,12,20,50,70,100,140,200,280,400,560,800,1120,1600}; double theta = 0, cos = 0, sin = 0; int mx = 0, px = -1, py = 0, chosen = -1; boolean mouseIsDown = false; public boolean mouseDown(Event e, int x, int y) { mouseIsDown = true; if (spinButton.down(x,y)) return true; for (int i = 0 ; i < slider.length ; i++) if (slider[i].down(x,y)) return true; if (x >= 50 && x < w-20) mx = x; return true; } public boolean mouseMove(Event e, int x, int y) { mouseIsDown = false; x -= w/2; y -= h/2; if (x*x + y*y < w*w/16) { px = x + w/2; py = y + h/2; } else px = -100; return true; } public boolean mouseDrag(Event e, int x, int y) { mouseIsDown = true; if (spinButton.drag(x,y)) return true; for (int i = 0 ; i < slider.length ; i++) if (slider[i].drag(x,y)) { rt.setVar(i, slider[i].getValue()); return true; } if (x >= 100) { if (chosen >= 0) { px = x; py = y; } else { rotate(-.01 * (x - mx)); mx = x; } } return true; } public boolean mouseUp(Event e, int x, int y) { mouseIsDown = false; px = -100; if (spinButton.up(x,y)) return true; for (int i = 0 ; i < slider.length ; i++) if (slider[i].up(x,y)) return true; if (x < 50) { int i = (y - 10) / 14; if (i >= 0 && i < N.length) { n = i; P = initP(); rt.set(P, nColors); } return true; } if (x >= w-20) { int i = (y - 10) / 14 + 1; if (i >= 1 && i <= 9) rt.set(P, nColors = i); } return true; } void rotate(double t) { theta += t; cos = w/4 * Math.cos(theta); sin = w/4 * Math.sin(theta); } int w = 0, h; public void render(Graphics g) { int i, j, k; if (w == 0) { w = bounds().width; h = bounds().height; rotate(0); spinButton = new Button(3, h-20, 34, 15); spinButton.labels = spinButtonLabels; for (i = 0 ; i < slider.length ; i++) { slider[i] = new Slider(w/2, h+(i-slider.length)*20, w/2-5, 15); slider[i].label = sliderLabels[i]; } slider[RepelThread2.TREND1].setValue(.9); slider[RepelThread2.TREND2].setValue(.1); slider[RepelThread2.TREND3].setValue(.9); slider[RepelThread2.ENERGY].setValue(1); rt = new RepelThread2(slider.length); rt.set(P, nColors); for (int s = 0 ; s < slider.length ; s++) rt.setVar(s,slider[s].getValue()); rt.start(); } g.setColor(Color.white); g.fillRect(0,0,w,h); int rad = 4;//(int)(4 + 4 * slider[5].getValue()); g.setColor(sphereColor); g.fillOval(w/4+rad,h/4+rad,w/2-2*rad,h/2-2*rad); for (i = 0 ; i < N[n] ; i++) if (sin * P[i][0] - cos * P[i][2] <= -w/20) { int x = w/2 + (int)(cos * P[i][0] + sin * P[i][2]); int y = h/2 + (int)(h/4 * P[i][1]); g.setColor(backColor[colorId(i)]); g.fillOval(x-rad,y-rad,2*rad,2*rad); } g.setColor(edgeColor); g.drawOval(w/4+rad,h/4+rad,w/2-2*rad,h/2-2*rad); if (mouseIsDown) { if (chosen >= 0) { double X = (double)(px-w/2) / (w/4); double Y = (double)(py-h/2) / (h/4); double RR = X*X + Y*Y; if (RR < 1) { double Z = Math.sqrt(1 - RR); P[chosen][0] = (cos * X + sin * Z) / (w/4); P[chosen][1] = Y; P[chosen][2] = (sin * X - cos * Z) / (w/4); } } } else chosen = -1; for (i = 0 ; i < N[n] ; i++) if (sin * P[i][0] - cos * P[i][2] > -w/20) { int x = w/2 + (int)(cos * P[i][0] + sin * P[i][2]); int y = h/2 + (int)(h/4 * P[i][1]); if (!mouseIsDown && chosen<0 && (px-x)*(px-x)+(py-y)*(py-y) <= 16) chosen = i; g.setColor(i == chosen ? Color.red : color[colorId(i)]); g.fillOval(x-rad,y-rad,2*rad,2*rad); } if (! mouseIsDown) rt.setChosen(chosen); for (i = 0 ; i < N.length ; i++) { g.setColor(n==i ? Color.red : lightBrown); g.drawString("" + N[i], 5, 20 + 14*i); } for (i = 0 ; i < 9 ; i++) { g.setColor(nColors==i+1 ? Color.black : lightGray); g.drawString("" + (i+1), w-10, 20 + 14*i); } spinButton.render(g); for (i = 0 ; i < slider.length ; i++) slider[i].render(g); if (spinButton.getValue() == 1) rotate(.025); g.setColor(new Color(160,160,255)); g.drawString("" + version, w-20, h/2); } int version = 10; Color buttonColor = new Color(255,230,230); Color lightBrown = new Color(200,150,150); Color lightGray = new Color(200,200,200); int[] srgb = {250,240,240}; Color sphereColor = new Color(srgb[0],srgb[1],srgb[2]); Color edgeColor = new Color(srgb[0]/2,srgb[1]/2,srgb[2]/2); int[][] rgb = { {255,0,0}, {240,150,0}, {140,200,0}, {0,140,0}, {0,160,255}, {0,0,255}, {200,0,120}, {180,120,50}, {0,0,0}, }; int nColors = 8; int colorId(int i) { return (i % nColors) * color.length / nColors; } Color[] color = newColors(), backColor; Color[] newColors() { color = new Color[rgb.length]; backColor = new Color[rgb.length]; for (int i = 0 ; i < color.length ; i++) { color[i] = new Color(rgb[i][0],rgb[i][1],rgb[i][2]); backColor[i] = new Color( (rgb[i][0] + 5*srgb[0])/6, (rgb[i][1] + 5*srgb[1])/6, (rgb[i][2] + 5*srgb[2])/6 ); } return color; } double[][] P = initP(); double[][] initP() { java.util.Random R = new java.util.Random(0); P = new double[N[n]][3]; for (int i=0; i 1); p[0] = u; p[1] = v; p[2] = w; RepelThread2.normalize(p); } } class RepelThread2 extends Thread { final static int TREND1 = 0; final static int TREND2 = 1; final static int TREND3 = 2; final static int ENERGY = 3; final static int GRAVITY = 4; double[][] P = null, currentP = null; int chosen = -1, M = 0; double[] var; RepelThread2(int nVars) { var = new double[nVars]; } public void set(double[][] newP, int n) { currentP = newP; this.M = n; } public void setVar(int v, double value) { var[v] = value; } public void setChosen(int i) { chosen = i; } double[] v = new double[3]; double[] p = new double[3]; int off = 1; public void run() { int i, j, k; try { while (true) { P = currentP; int N = P.length; double t = .02 / Math.sqrt(N); double RR = 8./N/N, A, B, T; for (int a = 0 ; a < N ; a++) if (a != chosen) { for (j = 0 ; j < 3 ; j++) p[j] = P[a][j]; for (int b = 0 ; b < N ; b++) { if (b != a) { for (j = 0 ; j < 3 ; j++) v[j] = P[b][j] - P[a][j]; double rr = v[0]*v[0] + v[1]*v[1] + v[2]*v[2]; T = t * var[ENERGY] / rr; int C = ( (M*1000 + b-a) % M ) & 7; if (C==1 || C==2 || C==4) // ONE BIT SET T *= 1 + 2 * var[TREND1]; else if (C==3 || C==5 || C==6) // TWO BITS SET T *= 1 + 2 * var[TREND2]; else if (C==7) // THREE BITS SET T *= 1 + 2 * var[TREND3]; for (j = 0 ; j < 3 ; j++) p[j] -= T * v[j]; if (var[GRAVITY] != 0) p[1] += var[GRAVITY] / N; normalize(p); } } for (j = 0 ; j < 3 ; j++) P[a][j] = p[j]; } sleep(30); } } catch(InterruptedException e){}; } double near(double t, double r) { t = Math.abs(t); if (t > r) return 0; return 1 - t / r; } static void normalize(double v[]) { double s = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); v[0] = v[0] / s; v[1] = v[1] / s; v[2] = v[2] / s; } }