The geometry of the agents is always a simple 3D sphere of a radius of 3
Here is the main script with the super class and one of the subclasses. The rest are similar.
I hope it helps. :(
import peasy.org.apache.commons.math.*; // imports all 3D related libraries
import peasy.*;
import peasy.org.apache.commons.math.geometry.*;
import processing.opengl.*;
PeasyCam cam;
PMatrix3D currCameraMatrix;
PGraphics3D g3;
float worldSize = 300; // the space - rendered as a cube - limiting agents movement
StopWatchTimer sw; // a visual timer-clock implementation on canvas
int time; // a counter for time triggering events
int wait = 1000; // 1 second
Facilities work; // wondering agent -- represents (W)
Facilities retail; // wondering agent -- represents (R)
Facilities school; // wondering agent -- represents (S)
Facilities leisure; // wondering agent -- represents (L)
ArrayList <Capsule> agents;
float d = 25;
int counter=0; // counter inception
int temp=0;
boolean animate = true; // boolean for pausing the animation sequence
void setup() {
size(600,600,OPENGL);
cam = new PeasyCam(this, 300);
cam.lookAt(150, 150, 300);
cam.setMinimumDistance(10);
cam.setMaximumDistance(1500);
time = millis(); // store the current time
smooth(); // setup of visual time clock
println (millis());
sw = new StopWatchTimer();
sw.start();
// creating the facilities
work = new Facilities(50,50,50); // generates a hypothetic invisible agent (W)
retail = new Facilities(30,30,30); // generates a hypothetic invisible agent (R)
school = new Facilities(240,240,240); // generates a hypothetic invisible agent (S)
leisure = new Facilities(90,90,90); // generates a hypothetic invisible agent (L)
agents = new ArrayList<Capsule>(); // creating the agents
// setting the emitter in random positions for each group
for (int i = 0; i < 115; i++) {
if (i < 20) agents.add(new MW1(150,150,150,color(255,0,0),i)); // Red
else if (i < 45) agents.add(new FW1(100,100,100,color(0,255,0),i)); // Green
else if (i < 65) agents.add(new MW2(200,200,200,color(0,255,255),i)); // Cyan
else if (i < 85) agents.add(new FnW2(80,80,80,color(0,155,0),i)); // dark Green
else if (i < 95) agents.add(new MW3(130,130,130,color(255,255,0),i)); // Yellow
else if (i < 105) agents.add(new FnW3(60,60,60,color(255,0,255),i)); // Purple
else agents.add(new CS3(10,10,10,color(0,0,255),i)); // Blue
}
}
//--------------configuring the canvas & the counter-------------
void draw() {
background(255);
time(); // reference to the visual timer
displayWorldCenter();
noLights();
cam.beginHUD();
cam.endHUD();
cam.setMouseControlled(true);
if(mouseY<80) {
cam.setMouseControlled(false);
}
// check the difference between now and the previously stored time is greater than the wait interval
if (animate) {
if(millis() - time >= wait){
println(counter);// if it is, do something
temp=counter;
time = millis();// also update the stored time
counter++; // counter augmentation
if(counter >180){ // check whether time exceeds 180 sec. aka 24:00 hours - enables looping
counter=0;
}
}
}
//-------------------------------------------------------------
work.Default(); // main-basic functions of (W) facility
retail.Default(); // main-basic functions of (R) facility
school.Default(); // main-basic functions of (S) facility
leisure.Default(); // main-basic functions of (L) facility
PVector vectorOfwork = new PVector(work.location.x, work.location.y, work.location.z); // creates a vector that connects all individual agents with the (W) facility
PVector vectorOfretail = new PVector(retail.location.x, retail.location.y, retail.location.z); // creates a vector that connects all individual agents with the (R) facility
PVector vectorOfschool = new PVector(school.location.x, school.location.y, school.location.z); // creates a vector that connects all individual agents with the (S) facility
PVector vectorOfleisure = new PVector(leisure.location.x, leisure.location.y, leisure.location.z); // creates a vector that connects all individual agents with the (L) facility
int i = 0; // i & j make sure that each agent returns to the family it belongs
int j = 0;
for (Capsule agent : agents) { // instancing - creating objects
agent.display();
if (animate) {
agent.boundaries(); // basic behavior for ALL the agents
agent.update();
agent.wander();
agent.wander3D();
agent.separateSpecial(agents); // global separation that affects ALL the agents
if (counter>0 && counter<=60) {
if (agent instanceof FnW2){ // females attach&follow males - couples
agent.arrive( agents.get(i-20).location );
}
else if (agent instanceof FnW3) { // females attach&follow males - 3 member family
agent.arrive( agents.get(i-10).location );
}
else if (agent instanceof CS3) { // children attach&follow females - 3 member family
agent.arrive( agents.get(i-10).location );
}
i++;
}
if (counter>60 && counter<=120) { // time reference to 6.00-14.00 hours
agent.separate(agents);
if (agent instanceof MW1){
agent.arrive( vectorOfwork );
}
else if (agent instanceof MW2) {
agent.arrive( vectorOfwork );
}
else if (agent instanceof MW3) {
agent.arrive( vectorOfwork );
}
else if (agent instanceof FW1) {
agent.arrive( vectorOfwork );
}
else if (agent instanceof FnW2) {
agent.arrive( vectorOfretail );
}
else if (agent instanceof FnW3) {
agent.arrive( vectorOfretail );
}
else if (agent instanceof CS3) {
agent.arrive( vectorOfschool );
}
}
if (counter>120 && counter<=180) {
if (agent instanceof FnW2){ // females attach&follow males - couples
agent.arrive( agents.get(j-20).location );
}
else if (agent instanceof FnW3) { // females attach&follow males - 3 member family
agent.arrive( agents.get(j-10).location );
}
else if (agent instanceof CS3) { // children attach&follow females - 3 member family
agent.arrive( agents.get(j-10).location );
}
j ++;
}
}
}
}
void displayWorldCenter(){
pushMatrix();
translate(150,150,150);
stroke(120); noFill(); // stroke of the cube (box)
box(worldSize);
stroke(#209889); noFill(); // stroke of the ellipses(center)
ellipse(0,0,20,20);
rotateY(radians(90));
ellipse(0,0,20,20);
rotateZ(radians(90));
ellipse(0,0,20,20);
rotateX(radians(90));
rotateY(radians(90));
ellipse(0,0,20,20);
popMatrix();
}
//-------------------------------------------------------------
void time() { // visual configuration for the timer-clock
fill(#000000);
textAlign(CENTER);
text(nf(sw.hour(), 2)+":"+nf(sw.minute(), 2)+":"+nf(sw.second(), 2)+":"+nf(sw.hundrensec(), 2), 260, 290);
}
void keyPressed(){
if (key=='a') animate = true;
else if (key=='s') animate = false;
}
abstract class Capsule { //the super-class that defines the chararacteristics of the agents
PVector location;
PVector velocity;
PVector acceleration;
float r; // the radius of the circles-spheres/ agents
float wandertheta, wanderphi, wanderpsi;
float maxforce; // Maximum steering force
float maxspeed; // Maximum speed
color fillColor; // differentiation of agent types are based on complementary coloring
int name; // gives an ID - number to each different agent
Capsule(float x, float y, float z, color c, int nam) { // the class constructor
acceleration = new PVector(0,0,0);
velocity = new PVector(0,0,0);
location = new PVector(x,y,z);
r = 6;
wandertheta = 0;
wanderphi = 0;
wanderpsi = 0;
maxspeed = 0.5;
maxforce = 0.05;
fillColor = c;
name = nam;
}
void update() { // Method to update location
velocity.add(acceleration); // Update velocity
velocity.limit(maxspeed); // Limit speed
location.add(velocity); // Reset accelertion to 0 each cycle
acceleration.mult(0);
}
void wander() { // Method that makes the agent float into 2D space
float wanderR = 25; // Radius for our "wander circle"
float wanderD = 200; // Distance for our "wander circle"
float change = 0.05;
wandertheta += random(-change,change); // Randomly change wander theta
// calculating the new location to steer towards on the wander circle
PVector circleloc = velocity.get(); // Start with velocity
circleloc.normalize(); // Normalize to get heading
circleloc.mult(wanderD); // Multiply by distance
circleloc.add(location); // Make it relative to boid's location
float h = velocity.heading2D(); // We need to know the heading to offset wandertheta
PVector circleOffSet = new PVector(wanderR*cos(wandertheta+h),wanderR*sin(wandertheta+h));
PVector target = PVector.add(circleloc,circleOffSet);
seek(target);
}
PVector wander3D() {
float wanderR = 30.0; // Radius for our "wander circle"
float wanderD = 30.0; // Distance for our "wander circle"
float change = 0.5;
wandertheta += random(-change, change); // Randomly change wander theta
wanderphi += random(-change, change); // Randomly change wander phi
wanderpsi += random(-change, change); // Randomly change wander psi
PVector circleloc = velocity.get(); // Start with velocity
circleloc.normalize(); // Normalize to get heading
circleloc.mult(wanderD); // Multiply by distance
circleloc.add(location); // Make it relative to boid's location
PVector circleOffSet = new PVector(wanderR*cos(wandertheta),wanderR*sin(wanderphi), wanderR*cos(wanderpsi));
PVector target = PVector.add(circleloc,circleOffSet);
return target;
}
void applyForce(PVector force) { // Newton's second law; We could add mass here if we want A = F / M
acceleration.add(force);
}
// A method that calculates and applies a steering force towards a target
// STEER = DESIRED MINUS VELOCITY
void seek(PVector target) {
PVector desired = PVector.sub(target,location); // A vector pointing from the location to the target
desired.normalize(); // Normalize desired and scale to maximum speed
desired.mult(maxspeed);
PVector steer = PVector.sub(desired,velocity); // Steering = Desired minus Velocity
steer.limit(maxforce); // Limit to maximum steering force
applyForce(steer);
}
// Method that lowers the speed of the agent by the time he reaches the target - see: "buffer zone"
void arrive(PVector target) {
PVector desired = PVector.sub(target,location); // A vector pointing from the location to the target
float d = desired.mag();
desired.normalize(); // Normalize desired and scale with arbitrary damping within 100 pixels
if (d < r) {
float m = map(d,0,15,0,maxspeed);
desired.mult(m);
} else {
desired.mult(maxspeed);
}
PVector steer = PVector.sub(desired,velocity); // Steering = Desired minus Velocity
steer.limit(5*maxforce); // Limit to maximum steering force
applyForce(steer);
}
void display() { // Draws an ellipse that represents an agent-capsule
fill(fillColor);
noStroke();
pushMatrix();
translate(location.x, location.y, location.z);
sphere(3);
popMatrix();
}
// Method that checkes the maximum floating range of the agents - bouncing on the edges of the cube
void boundaries() {
if (location.x < 0 || location.x > worldSize) {
velocity.x *=-1;
}
if (location.y < 0 || location.y > worldSize) {
velocity.y *=-1;
}
if (location.z < 0 || location.z > worldSize) {
velocity.z *=-1;
}
}
// Method that separates the agents from one-another -- see: "buffer zone"
void separate (ArrayList<Capsule> agent) {
float desiredseparation = r*4; // 4*r = separation ONLY for wandering
PVector sum = new PVector();
int count = 0;
// For every agent in the system, check if it's too close
for (Capsule v : agent) {
float d = PVector.dist(location, v.location);
// If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
if ((d > 0) && (d < desiredseparation)) {
// Calculate vector pointing away from neighbor
PVector diff = PVector.sub(location, v.location);
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);
sum.normalize(); // Our desired vector is the average scaled to maximum speed
sum.mult(maxspeed);
// Implement Reynolds: Steering = Desired - Velocity
PVector steer = PVector.sub(sum, velocity);
steer.limit(maxforce);
applyForce(steer);
}
}
// Method that separates the agents from one-another -- see: "buffer zone"
void separateSpecial (ArrayList<Capsule> agent) {
float desiredseparation = r; //
PVector sum = new PVector();
int count = 0;
// For every agent in the system, check if it's too close
for (Capsule v : agent) {
float d = PVector.dist(location, v.location);
// If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
if ((d > 0) && (d < desiredseparation)) {
// Calculate vector pointing away from neighbor
PVector diff = PVector.sub(location, v.location);
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);
sum.normalize(); // Our desired vector is the average scaled to maximum speed
sum.mult(maxspeed);
// Implement Reynolds: Steering = Desired - Velocity
PVector steer = PVector.sub(sum, velocity);
steer.limit(maxforce);
applyForce(steer);
}
}
// Alignment
// For every nearby agent in the system, calculate the average velocity
PVector align (ArrayList<Capsule> agent) {
float neighbordist = 50;
PVector sum = new PVector(0,0);
int count = 0;
for (Capsule v : agent) {
float d = PVector.dist(location,v.location);
if ((d > 0) && (d < neighbordist)) {
sum.add(v.velocity);
count++;
}
}
if (count > 0) {
sum.div((float)count);
sum.normalize();
sum.mult(maxspeed);
PVector steer = PVector.sub(sum,velocity);
steer.limit(maxforce);
return steer;
} else {
return new PVector(0,0);
}
}
}
class MW1 extends Capsule { // subclass: male_worker_single
MW1(float x_mw1, float y_mw1, float z_mw1, color c_mw1, int nam_mw1) {
super(x_mw1,y_mw1,z_mw1,c_mw1,nam_mw1);
}
void display() {
fill(255,0,0);
noStroke();
pushMatrix();
translate(location.x, location.y, location.z);
sphere(3);
popMatrix();
}
}