/* Copyright 2006 by Sean Luke and George Mason University Licensed under the Academic Free License version 3.0 See the file "LICENSE" for more information */ package sim.app.flockers; import sim.engine.*; import sim.field.continuous.*; import sim.util.*; import ec.util.*; public class Flocker implements Steppable, sim.portrayal.Orientable2D { private static final long serialVersionUID = 1; public Double2D loc = new Double2D(0,0); public Double2D lastd = new Double2D(0,0); public Continuous2D flockers; public Flockers theFlock; public boolean dead = false; public Flocker(Double2D location) { loc = location; } public Bag getNeighbors() { return flockers.getNeighborsExactlyWithinDistance(loc, theFlock.neighborhood, true); } public double getOrientation() { return orientation2D(); } public boolean isDead() { return dead; } public void setDead(boolean val) { dead = val; } public void setOrientation2D(double val) { lastd = new Double2D(Math.cos(val),Math.sin(val)); } public double orientation2D() { if (lastd.x == 0 && lastd.y == 0) return 0; return Math.atan2(lastd.y, lastd.x); } public Double2D momentum() { return lastd; } public Double2D consistency(Bag b, Continuous2D flockers) { if (b==null || b.numObjs == 0) return new Double2D(0,0); double x = 0; double y= 0; int i =0; int count = 0; for(i=0;i<b.numObjs;i++) { Flocker other = (Flocker)(b.objs[i]); if (!other.dead) { Double2D m = ((Flocker)b.objs[i]).momentum(); count++; x += m.x; y += m.y; } } if (count > 0) { x /= count; y /= count; } return new Double2D(x,y); } public Double2D cohesion(Bag b, Continuous2D flockers) { if (b==null || b.numObjs == 0) return new Double2D(0,0); double x = 0; double y= 0; int count = 0; int i =0; for(i=0;i<b.numObjs;i++) { Flocker other = (Flocker)(b.objs[i]); if (!other.dead) { double dx = flockers.tdx(loc.x,other.loc.x); double dy = flockers.tdy(loc.y,other.loc.y); count++; x += dx; y += dy; } } if (count > 0) { x /= count; y /= count; } return new Double2D(-x/10,-y/10); } public Double2D avoidance(Bag b, Continuous2D flockers) { if (b==null || b.numObjs == 0) return new Double2D(0,0); double x = 0; double y = 0; int i=0; int count = 0; for(i=0;i<b.numObjs;i++) { Flocker other = (Flocker)(b.objs[i]); if (other != this ) { double dx = flockers.tdx(loc.x,other.loc.x); double dy = flockers.tdy(loc.y,other.loc.y); double lensquared = dx*dx+dy*dy; count++; x += dx/(lensquared*lensquared + 1); y += dy/(lensquared*lensquared + 1); } } if (count > 0) { x /= count; y /= count; } return new Double2D(400*x,400*y); } public Double2D randomness(MersenneTwisterFast r) { double x = r.nextDouble() * 2 - 1.0; double y = r.nextDouble() * 2 - 1.0; double l = Math.sqrt(x * x + y * y); return new Double2D(0.05*x/l,0.05*y/l); } public void step(SimState state) { final Flockers flock = (Flockers)state; loc = flock.flockers.getObjectLocation(this); if (dead) return; Bag b = getNeighbors(); Double2D avoid = avoidance(b,flock.flockers); Double2D cohe = cohesion(b,flock.flockers); Double2D rand = randomness(flock.random); Double2D cons = consistency(b,flock.flockers); Double2D mome = momentum(); double dx = flock.cohesion * cohe.x + flock.avoidance * avoid.x + flock.consistency* cons.x + flock.randomness * rand.x + flock.momentum * mome.x; double dy = flock.cohesion * cohe.y + flock.avoidance * avoid.y + flock.consistency* cons.y + flock.randomness * rand.y + flock.momentum * mome.y; // renormalize to the given step size double dis = Math.sqrt(dx*dx+dy*dy); if (dis>0) { dx = dx / dis * flock.jump; dy = dy / dis * flock.jump; } lastd = new Double2D(dx,dy); loc = new Double2D(flock.flockers.stx(loc.x + dx), flock.flockers.sty(loc.y + dy)); flock.flockers.setObjectLocation(this, loc); } }