/* * SwarmApplet.java * * (C) 2005, Alex S. */ import java.awt.*; import java.util.Vector; class MovingObject { public double x,y; public double direction; public double speed; public double value; public int mode; public static final int NORMAL = 0; public static final int LEADER = 1; public static final int DEAD = 2; public MovingObject(double x,double y,double direction,double speed,double value,int mode){ this.x = x; this.y = y; this.direction = direction; this.speed = speed; this.value = value; this.mode = mode; } public void move(int w,int h){ double sin = Math.sin(direction); double cos = Math.cos(direction); double nx = x + cos * speed; double ny = y + sin * speed; if(mode == DEAD){ nx = x; ny = y; } if(nx > w-1){ nx = w-1; cos = -cos; speed += 0.5*(Math.random()-0.5); } if(nx < 0){ nx = 0; cos = -cos; speed += 0.5*(Math.random()-0.5); } if(ny > h-1){ ny = h-1; sin = -sin; speed += 0.5*(Math.random()-0.5); } if(ny < 0){ ny = 0; sin = -sin; speed += 0.5*(Math.random()-0.5); } if(speed < 0.1) speed = 0.1; if(speed > 10) speed = 10; if(mode == LEADER) speed = 2; direction = Math.atan2(sin,cos); x = nx; y = ny; } } /** * very basic swarm applet */ public class SwarmApplet extends BufferedApplet { private final static int NUM_OBJS = 200; private final static int GWIDTH = 40; private final static int GHEIGHT = 40; private final static int RADIUS = 5; private final static double EPSILON = 0.001; private int width,height; private int[] queue = new int[GWIDTH * GHEIGHT]; private int[] queuelast = new int[GWIDTH * GHEIGHT]; private int[] next = new int[NUM_OBJS]; private int[] objx = new int[NUM_OBJS]; private int[] objy = new int[NUM_OBJS]; private MovingObject[] objects = new MovingObject[NUM_OBJS]; /** * initialize things */ public void init(){ super.init(); width = bounds().width; height = bounds().height; initswarm(); } public void initswarm(){ for(int i=0;i 0){ dx /= len; dy /= len; cos = (1-xcenterpull)*cos + xcenterpull*dx; sin = (1-ycenterpull)*sin + ycenterpull*dy; sin += 0.03; // downward bias. objects[i].direction = Math.atan2(sin,cos); objects[i].speed = len > RADIUS/2 ? RADIUS/2:len; } } */ int bx = objx[i]; int by = objy[i]; int Ux = bx - 1 < 0 ? 0:bx-1; int Uy = by - 1 < 0 ? 0:by-1; int Lx = bx + 1 > GWIDTH-1 ? GWIDTH-1:bx+1; int Ly = by + 1 > GHEIGHT-1 ? GHEIGHT-1:by+1; //System.out.println("current bucket: "+bx+","+by+" RANGE: ("+Ux+","+Uy+")->("+Lx+","+Ly+")"); // direction stats. double _avgsin = 0; double _avgcos = 0; double _avgx = 0; double _avgy = 0; double _avgspeed = 0; double _avgvalue = 0; int _numobjects = 0; for(int cy=Uy;cy<=Ly;cy++){ for(int cx=Ux;cx<=Lx;cx++){ //System.out.println("examining: "+cx+", "+cy); int bucket = cy * GWIDTH + cx; for(int j = queue[bucket];j != -1; j = next[j]){ if(i != j){ double ox = objects[j].x; double oy = objects[j].y; double dx = ox - x; double dy = oy - y; double dd = dx * dx + dy * dy; //System.out.print("comparing: "+i+" to "+j+" dd:"+dd+" distsqrt: "+distsquared); if(dd < distsquared){ double dist = Math.sqrt(dd) - EPSILON; double disp = ((diam - dist)/2)/dist; //System.out.print(" dist: "+dist); objects[i].x -= dx * disp; objects[i].y -= dy * disp; objects[j].x += dx * disp; objects[j].y += dy * disp; //objects[i].direction = Math.atan2(-dy,-dx); //objects[j].direction = Math.atan2(dy,dx); //double speed = objects[i].speed; //objects[i].speed = objects[j].speed; //objects[j].speed = speed; } if(objects[i].mode == MovingObject.LEADER && objects[j].mode == MovingObject.LEADER){ double dist = Math.sqrt(dd) - EPSILON; double disp = (((diam*3) - dist)/2)/dist; objects[i].x -= dx * disp; objects[i].y -= dy * disp; objects[j].x += dx * disp; objects[j].y += dy * disp; } } if(objects[j].mode != MovingObject.DEAD){ _avgsin += Math.sin(objects[j].direction); _avgcos += Math.cos(objects[j].direction); _avgspeed += objects[j].speed; _avgx += objects[j].x; _avgy += objects[j].y; _numobjects++; if(objects[j].mode == MovingObject.LEADER && objects[i].mode != MovingObject.LEADER){ objects[i].value = objects[j].value; } _avgvalue += objects[j].value; } } } } // adjust current object according to averages. _avgsin /= _numobjects; _avgcos /= _numobjects; _avgx /= _numobjects; _avgy /= _numobjects; _avgspeed /= _numobjects; _avgvalue /= _numobjects; // lean the color of your flock mates. if(objects[i].mode == MovingObject.NORMAL){ if(Math.sqrt((_avgvalue - objects[i].value)*(_avgvalue - objects[i].value)) > 0.4){ objects[i].mode = MovingObject.DEAD; }else{ objects[i].value = objects[i].value + 0.5*(_avgvalue - objects[i].value); } } // add some noise into the motion _avgsin += 0.2*(Math.random()-0.5); _avgcos += 0.2*(Math.random()-0.5); objects[i].speed += 0.2*(Math.random()-0.5); // find heading into center of local group. double _dx = _avgx - objects[i].x ; double _dy = _avgy - objects[i].y ; // normalize. double _ddist = Math.sqrt(_dx * _dx + _dy * _dy); // find heading into center of -global- group. double _dxall = avgx - objects[i].x; double _dyall = avgy - objects[i].y; double _ddistall = Math.sqrt(_dxall * _dxall + _dyall * _dyall); if(_ddistall == 0) _ddistall = EPSILON; _dxall /= _ddistall; _dyall /= _ddistall; // if it's getting a bit too crowded, get away from center. if(_numobjects > 8){ //adjust number for different flocks. if(_ddist > 0){ _dx /= _ddist; _dy /= _ddist; objects[i].direction = Math.atan2(_avgsin*0.3 + -_dy*0.7,_avgcos*0.3 + -_dx*0.7); }else{ objects[i].direction = Math.atan2(_avgsin,_avgcos); } }else{ if(_ddist > 0){ _dx /= _ddist; _dy /= _ddist; objects[i].direction = Math.atan2( _avgsin*0.8 + _dy*0.195 + _dyall*0.005, _avgcos*0.8 + _dx*0.195 + _dxall*0.005); }else{ objects[i].direction = Math.atan2(_avgsin,_avgcos); } } objects[i].speed += 0.01*(_avgspeed - objects[i].speed); } if (true || damage) { g.setColor(Color.white); g.fillRect(0, 0, bounds().width, bounds().height); /* // draw grid. g.setColor(Color.gray); for(int i=0;i