class BlackHole extends Particle {
GravityFlock parent;
public static final float G = 100.0f;
public static final float TRAPPED_DISTANCE = 5.0f;
public static final float MAX_GRAV_DISTANCE = 200.0f;
public static final int MAX_AGE = 10000;
private int birth = 0;
private float _strength = 0.6f;
public BlackHole(ParticleSystem ps, GravityFlock p, PVector l) {
this(ps, p, l, 1.0f);
}
public BlackHole(ParticleSystem ps, GravityFlock p, PVector l, float strength_mod) {
super(ps, new PVector(), new PVector(), l);
_strength = strength_mod;
parent = p;
birth = parent.millis();
}
void applyForceTo(Particle p) {
if ( this != p ) {
// calculate direction of force
PVector dir = PVector.sub(p.loc, loc);
// distance between objects
float d = dir.mag();
if ( d <= MAX_GRAV_DISTANCE ) {
d = PApplet.constrain(dir.mag(), 0.1f, 100.0f);
// normalize vector (distance doesn't matter here, we just want this vector for direction)
dir.normalize();
// calculate gravitational force magnitude
float force = strength() * (G * getMass() * p.getMass()) / (d * d);
// get force vector --> magnitude * direction
dir.mult(force);
p.acc.sub(dir);
}
}
}
public int age() {
return parent.millis() - birth;
}
public float strength() {
return _strength;
}
float getMass() {
return 2.0f;
}
void run() {
Iterator it = parent.black_holes.getParticles().iterator();
while (it.hasNext()) {
Particle hole = (Particle)it.next();
Iterator boids = parent.flock.getParticles().iterator();
while (boids.hasNext()) {
Particle boid = (Particle)boids.next();
((BlackHole)hole).applyForceTo(boid);
}
}
render();
}
void render() {
parent.fill(255 * strength(), 0, 0);
// parent.stroke(255 * strength() );
parent.ellipse(loc.x,loc.y, 20, 20);
}
}
import java.util.*;
class Boid extends Particle {
private GravityFlock parent;
/**
* * radius
*/
private float r;
/**
* * Maximum steering force
*/
private float maxforce;
private float colorR;
private float colorG;
private float colorB;
public static final float MIN_VELOCITY = 2.0f;
public static final float MAX_VELOCITY = 4.0f;
public static final float MIN_FORCE = 0.15f;
public static final float MAX_FORCE = 0.25f;
public static final float DESIRED_SEPARATION = 50.0f;
public static final float MAX_NEIGHBOR_DISTANCE = 75.0f;
public static final float DEAD_DISTANCE = 4.0f;
public static final int MAX_TRAIL_LENGTH = 7;
LinkedList trail;
public Boid(GravityFlock p, PVector l) {
this(p, l, MAX_VELOCITY, MAX_FORCE);
}
public Boid(GravityFlock p, PVector l, float ms, float mf) {
super(p.physics, new PVector(), new PVector(p.random(-1 * ms, ms), p.random(-1 * ms, ms)), l);
parent = p;
//r = 2.0f;
r = p.random(1, 4);
colorR = p.random(1, 255);
colorG = p.random(1, 255);
colorB = p.random(1, 255);
setMaxSpeed(ms);
maxforce = mf;
trail = new LinkedList();
}
void run() {
if ( MAX_TRAIL_LENGTH > 0 ) {
trail.add(new PVector(loc.x, loc.y, loc.y));
if ( trail.size() >= MAX_TRAIL_LENGTH ) {
trail.remove();
}
}
flock(parent.flock.getParticles());
update();
borders();
render();
}
// We accumulate a new acceleration each time based on three rules
void flock(ArrayList boids) {
PVector sep = separate(boids); // Separation
PVector ali = align(boids); // Alignment
PVector coh = cohesion(boids); // Cohesion
// Arbitrarily weight these forces
// sep.mult(2.0f);
// ali.mult(1.0f);
// coh.mult(1.0f);
// Add the force vectors to acceleration
acc.add(sep);
acc.add(ali);
acc.add(coh);
}
/**
* A method that calculates a steering vector towards a target
* @param target
* @param slowdown if true, slows down as it approaches the target
*/
PVector steer(PVector target) {
PVector steer; // The steering vector
PVector desired = PVector.sub(target,loc); // A vector pointing from the location to the target
float d = desired.mag(); // Distance from the target is the magnitude of the vector
// If the distance is greater than 0, calculate steering (otherwise return zero vector)
if (d > 0) {
// Normalize desired
desired.normalize();
desired.mult( getMaxSpeed() );
// Steering = Desired minus Velocity
steer = PVector.sub(desired, vel);
steer.limit(maxforce); // Limit to maximum steering force
}
else {
steer = new PVector(0,0);
}
return steer;
}
/**
* draw the boid - Draw a triangle rotated in the direction of velocity
*/
void render() {
parent.fill(colorR, colorG, colorB);
// parent.stroke(colorR, colorG, colorB);
parent.ellipse(loc.x,loc.y, r, r);
if ( MAX_TRAIL_LENGTH > 0 ) {
float x = 255;
for (ListIterator iter = trail.listIterator(trail.size()); iter.hasPrevious();) {
PVector v = (PVector)iter.previous();
x -= (255/MAX_TRAIL_LENGTH);
parent.fill(colorR, colorG, colorB, x);
parent.ellipse(v.x,v.y, r, r);
}
}
}
/**
* shift object around if it has hit a border
*/
void borders() {
if (loc.x < -r) loc.x = parent.width + r;
if (loc.y < -r) loc.y = parent.height + r;
if (loc.x > parent.width + r) loc.x = -r;
if (loc.y > parent.height + r) loc.y = -r;
}
/**
* Method checks for nearby boids and steers away
* @param boids
* @return
*/
PVector separate (ArrayList boids) {
PVector sum = new PVector(0,0,0);
int count = 0;
Iterator it = boids.iterator();
while (it.hasNext()) {
Particle other = (Particle)it.next();
float d = PVector.dist(loc, other.loc);
// If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
if ( d > 0 && d < DESIRED_SEPARATION ) {
// Calculate vector pointing away from neighbor
PVector diff = PVector.sub(loc, other.loc);
diff.normalize();
diff.div(d); // Weight by distance
sum.add(diff);
count++; // Keep track of how many
}
}
// Average -- divide by how many
if (count > 0) {
sum.div(count);
}
return sum;
}
/**
* For every nearby boid in the system, calculate the average velocity
* @param boids
* @return
*/
PVector align (ArrayList boids) {
PVector sum = new PVector(0,0,0);
int count = 0;
Iterator it = boids.iterator();
while (it.hasNext()) {
Particle other = (Particle)it.next();
float d = PVector.dist(loc,other.loc);
if (d < MAX_NEIGHBOR_DISTANCE) {
sum.add(other.vel);
count++;
}
}
if (count > 0) {
sum.div(count);
sum.limit(maxforce);
}
return sum;
}
/**
* For the average location (i.e. center) of all nearby boids, calculate steering vector towards that location
* @param boids
* @return
*/
PVector cohesion (ArrayList boids) {
PVector sum = new PVector(0,0,0); // Start with empty vector to accumulate all locations
int count = 0;
Iterator it = boids.iterator();
while (it.hasNext()) {
Particle other = (Particle)it.next();
float d = PVector.dist(loc,other.loc);
if ( d < MAX_NEIGHBOR_DISTANCE ) {
sum.add(other.loc); // Add location
count++;
}
}
if (count > 0) {
sum.div(count);
return steer(sum); // Steer towards the location
}
return sum;
}
public boolean dead() {
Iterator it = parent.black_holes.getParticles().iterator();
while (it.hasNext()) {
Particle p = (Particle)it.next();
float d = PVector.dist(loc, p.loc);
if ( d <= DEAD_DISTANCE ) {
return true;
}
}
return false;
}
}
class ParticleGroup {
private ArrayList particles;
public ParticleGroup(ParticleSystem p) {
particles = new ArrayList();
}
public void add(Particle p) {
particles.add(p);
}
public void remove(Particle p) {
particles.remove(p);
}
public ArrayList getParticles() {
return particles;
}
public void run() {
Iterator it = particles.iterator();
while (it.hasNext()) {
Particle p = (Particle)it.next();
p.run();
}
}
}
class ParticleSystem {
ArrayList groups;
public ParticleSystem() {
groups = new ArrayList();
}
void add(ParticleGroup g) {
groups.add(g);
}
void run() {
Iterator it = groups.iterator();
while (it.hasNext()) {
ParticleGroup g = (ParticleGroup)it.next();
g.run();
}
}
}
/**
* GravityFlock
*
*
* by Colin Mitchell <a href="http://muffinlabs.com/">muffinlabs.com</a>
*
* <p>Boid Flocking code with the addition of a 'black hole' which sucks boids in
* if they get too close. For every Boid that dies, a new one is generated.</p>
*
* <p>Add additional black holes by clicking on the applet. You need to reload
* to start anew.</p>
*
* <p>based on code from Daniel Shiffman <http://www.shiffman.net></p>
*/
int BOID_COUNT = 100;
ParticleGroup flock;
ParticleGroup black_holes;
ParticleSystem physics;
void setup() {
size(600, 600);
background(0);
colorMode(RGB,255,255,255,100);
physics = new ParticleSystem();
flock = new ParticleGroup(physics);
black_holes = new ParticleGroup(physics);
physics.add(flock);
physics.add(black_holes);
// Add an initial set of boids into the system
for (int i = 0; i < BOID_COUNT; i++) {
PVector v = new PVector( random(0, width), random(0, height));
addBoid(v);
}
for (int i = 0; i < 1; i++) {
PVector v = new PVector( random(0, width), random(0, height), 0);
addBlackHole(v);
}
smooth();
frameRate(30);
} // setup
void draw() {
// comment this out to keep old points on the screen
background(0);
// keep track of how many particles we need to add
int count = 0;
for (Iterator<Particle> iter = flock.getParticles().iterator(); iter.hasNext();) {
Particle p = iter.next();
if ( p.dead() ) {
iter.remove();
count++;
}
}
for (int i = 0; i < count; i++) {
PVector v;
int wall = int(random(0,4));
float px, py;
switch (wall) {
case 0:
px = 0;
py = random(0, height);
break;
case 1:
px = width;
py = random(0, height);
break;
case 2:
px = random(0, width);
py = height;
break;
default:
px = random(0, width);
py = 0;
}
v = new PVector( px, py);
addBoid(v);
}
physics.run();
} // draw
// Add a new black hole into the System
void mousePressed() {
addBlackHole(new PVector(mouseX, mouseY, 0));
}
void addBoid(PVector v) {
Boid b = new Boid(this, v, random(Boid.MIN_VELOCITY, Boid.MAX_VELOCITY), random(Boid.MIN_FORCE, Boid.MAX_FORCE));
flock.add(b);
}
void addBlackHole(PVector v) {
BlackHole bh = new BlackHole(physics, this, v, random(0.5f, 1.5f));
black_holes.add(bh);
}
class Particle {
PVector loc;
PVector vel;
PVector acc;
ParticleSystem parent;
private float maxspeed = 0f;
public boolean resetAcceleration = true;
private boolean _dead = false;
Particle(ParticleSystem p, PVector a, PVector v, PVector l) {
parent = p;
acc = a.get();
vel = v.get();
loc = l.get();
}
float getMass() {
return 1.0f;
}
void setMaxSpeed(float s) {
maxspeed = s;
}
float getMaxSpeed() {
return maxspeed;
}
void run() {
update();
render();
}
// Method to update location
void update() {
vel.add(acc);
// Limit speed
if ( maxspeed > 0 ) {
vel.limit(maxspeed);
}
loc.add(vel);
// Reset acceleration to 0 each cycle
if ( resetAcceleration ) {
acc.set(0,0,0);
}
}
// Method to display
void render() {
}
public boolean dead() {
return _dead;
}
}