// Client that implements a connected web of particles // Colours of particles decay with inactivity import java.awt.*; import java.util.Vector; import java.util.Enumeration; import PS10Applet; public class ParticleClient extends PS10Applet implements Runnable { Thread animThread; Vector particleList; int frameDelay = 100; int frameNumber = 0; Color grey50; public void init() { super.init(); this.setBackground(Color.white); this.setForeground(Color.black); update(getGraphics()); // force window create String frameDelayString = getParameter("frameDelay"); try { if (frameDelayString != null) frameDelay = Integer.parseInt(frameDelayString); } catch (Exception e) {} String otherServerHost = getParameter("serverHost"); if (otherServerHost != null) serverHost = otherServerHost; grey50 = new Color(.5f, .5f, .5f); particleList = new Vector(20); } public boolean mouseDown(Event e, int x, int y) { sendMessage(x, y); return true; } // when we get our ID set, move ourselves to the center. // also looks like we "create" the particle for ourselves. public void setID(int id) { super.setID(id); sendMessage(imageBufferSize.width / 2, imageBufferSize.height / 2); } public void receiveMessage(int otherID, int x, int y) { // System.out.println("Applet received " + otherID + " " + x + " " + y); if (x < 0 && y < 0) // ignore negatives return; // look to see if we already have a particle for this ID boolean oldParticle = false; Enumeration e = particleList.elements(); while (e.hasMoreElements() && !oldParticle) { IDParticle p = (IDParticle)e.nextElement(); if (p.id == otherID) { oldParticle = true; p.setGoal(x, y); p.setTime(System.currentTimeMillis()); } } // no particle - make one! if (oldParticle == false) { Rectangle size = new Rectangle(0, 0, imageBufferSize.width, imageBufferSize.height); IDParticle p = new IDParticle(size, x, y, 0, 0, 0, 0, otherID); p.setGoal(x, y); p.setTime(System.currentTimeMillis()); p.setHue((otherID % 8) / 8.0f); particleList.addElement(p); } } public void redrawImage() { clearImageBuffer(); Graphics g = imageBuffer.getGraphics(); g.setColor(Color.black); g.drawRect(0, 0, imageBufferSize.width - 1, imageBufferSize.height - 1); g.setColor(Color.white); long now = System.currentTimeMillis(); int maxAge = 30000; // milliseconds int n = particleList.size(); for (int i = 0; i < n; i++) { IDParticle p = (IDParticle)particleList.elementAt(i); for (int j = i + 1; j < n; j++) { long age; IDParticle p2 = (IDParticle)particleList.elementAt(j); // age is the older of the two, in milliseconds if (p.lastTime < p2.lastTime) age = (int)(now - p.lastTime); else age = (int)(now - p2.lastTime); if (age < maxAge) { float normAge = (float)age / maxAge; float grey = 1 - ((1 - normAge) * (1 - normAge)); g.setColor(new Color(grey, grey, grey)); drawThickLine(g, (int)p.x, (int)p.y, (int)p2.x, (int)p2.y, 7); } } } for (int i = 0; i < n; i++) { IDParticle p = (IDParticle)particleList.elementAt(i); int age = (int)(now - p.lastTime); if (age > maxAge) continue; // don't even draw it. float normAge = (float)age / maxAge; float s; if (normAge < 1) s = ((1 - normAge) * (1 - normAge)); else s = 0; g.setColor(Color.black); thickCenteredCircle(g, (int)p.x, (int)p.y, 14, 4, new Color(1-s, 1-s, 1-s), Color.getHSBColor(p.hue, s, 1)); } repaint(); } public void run() { while (Thread.currentThread() == animThread) { frameNumber++; redrawImage(); Enumeration e = particleList.elements(); while (e.hasMoreElements()) { Particle p = (Particle)e.nextElement(); p.step(1.0); } try { Thread.sleep(frameDelay); } catch (InterruptedException ex) {}; } } public void start() { super.start(); animThread = new Thread(this); animThread.start(); } public void stop() { animThread = null; super.stop(); } public void thickCenteredCircle(Graphics g, int x, int y, int radius, int thickness, Color c1, Color c2) { g.setColor(c1); g.fillOval(x - radius, y - radius, radius * 2 + 1, radius * 2 + 1); g.setColor(c2); g.fillOval(x - (radius - thickness), y - (radius - thickness), (radius - thickness) * 2 + 1, (radius - thickness) * 2 + 1); } // this code isn't 100% correct, but it's close // don't know what the caps are doing public void drawThickLine(Graphics g, int x1, int y1, int x2, int y2, int thick) { int i; int off = Math.round(thick/2); for (i = 0; i < thick; i++) { g.drawLine(x1 - off + i, y1, x2 - off + i, y2); g.drawLine(x1, y1 - off + i, x2, y2 - off + i); } } }