class Agent {
float vel = 1.0;
float r, amp;
float maxforce = 0.01;
float max = 4.0f;
List<Vec3D> objectTrails;
Vec3D speed = new Vec3D(vel, 0, 0);
Vec3D acc = new Vec3D(0, 0, 0);
Vec3D loc;
//-------------------------------------------------------------------------------------------
Agent(Vec3D location) {
loc = location;
this.objectTrails = new ArrayList<Vec3D>();
r = 12;
}
//-------------------------------------------------------------------------------------------
//---------------------------------------Runs all other methods------------------------------
//-------------------------------------------------------------------------------------------
void run() {
pathFollow();
separate();
if (simulate) {
move();
}
viz();
trail();
}
//-------------------------------------------------------------------------------------------
//---------------------------------------Path Follow Method----------------------------------
//-------------------------------------------------------------------------------------------
void pathFollow() {
Vec3D predict = this.speed.copy();
predict.normalize();
predict.scaleSelf(this.amp);
Vec3D nextPosPrev = this.loc.add(predict);
Vec3D target = null;
Vec3D normal = null;
float worldRecord = 1000000;
for (int i = 0; i < tempPath.points.size()-1; i++) {
Vec3D a = tempPath.points.get(i);
Vec3D b = tempPath.points.get(i+1);
Vec3D normalPoint = getNormalPoint(nextPosPrev, a, b); //Finding the normals for each line segment
if (normalPoint.x < min(a.x, b.x) || normalPoint.x > max(a.x, b.x)) {
normalPoint = b.copy();
}
float distance = nextPosPrev.distanceTo(normalPoint);
if (distance < worldRecord) {
worldRecord = distance;
normal = normalPoint;
Vec3D dir = b.sub(a); // Look at the direction of the line segment so we can seek a little bit ahead of the normal
dir.normalize();
dir.scaleSelf(10);
target = normalPoint.copy();
target.add(dir);
}
}
if (worldRecord > tempPath.radius ) {
seek(target);
} else {
Vec3D zero = new Vec3D(0, 0, 0);
zero.scaleSelf(3);
this.acc.addSelf(zero);
}
// Draw the debugging stuff
if (debug) {
// Draw predicted future location
stroke(255);
fill(0);
line(this.loc.x, this.loc.y, this.loc.x, this.loc.y);
ellipse(nextPosPrev.x, nextPosPrev.y, 4, 4);
// Draw normal location
stroke(255);
fill(0);
ellipse(normal.x, normal.y, 4, 4);
// Draw actual target (red if steering towards it)
line(nextPosPrev.x, nextPosPrev.y, normal.x, normal.y);
if (worldRecord > tempPath.radius) fill(255, 0, 0);
noStroke();
ellipse(target.x, target.y, 8, 8);
}
}
//-------------------------------------------------------------------------------------------
//-------------------Get the Normal Point on the Path Method---------------------------------
//-------------------------------------------------------------------------------------------
Vec3D getNormalPoint(Vec3D p, Vec3D a, Vec3D b) {
Vec3D ap = p.sub(a);
Vec3D ab = b.sub(a);
ab.normalize();
ab.scaleSelf(ap.dot(ab)); //Using the dot product for scalar projection
Vec3D normalPoint = a.add(ab); // Finding the normal point along the line segment
return normalPoint;
}
//-------------------------------------------------------------------------------------------
//---------------------------------------Seek Target Method----------------------------------
//-------------------------------------------------------------------------------------------
void seek(Vec3D target) {
Vec3D desired = target.sub(loc);
desired.normalize();
desired.scaleSelf(max);
Vec3D steer = desired.sub(speed);
steer.limit(maxforce); // Limit the magnitude of the steering force.
steer.scaleSelf(3);
applyForce(steer);
}
//-------------------------------------------------------------------------------------------
//---------------------------------------Apply Force Method----------------------------------
//-------------------------------------------------------------------------------------------
void applyForce(Vec3D force) {
this.acc.addSelf(force);
}
//-------------------------------------------------------------------------------------------
//-------------------------------------------Move Method-------------------------------------
//-------------------------------------------------------------------------------------------
void move() {
this.speed.addSelf(this.acc);
this.speed.limit(this.max);
this.loc.addSelf(this.speed);
this.objectTrails.add(new Vec3D(this.loc));
this.acc.clear();
}
//-------------------------------------------------------------------------------------------
//-------------------------------------------Viz Method--------------------------------------
//-------------------------------------------------------------------------------------------
void viz() {
stroke(255, 0, 0);
strokeWeight(6);
point(this.loc.x, this.loc.y);
}
//-------------------------------------------------------------------------------------------
//---------------------------------------Separation Method----------------------------------
//-------------------------------------------------------------------------------------------
void separate () {
float desiredseparation = r*2;
Vec3D steer = new Vec3D(0, 0, 0);
int count = 0;
for (int i = 0; i < agentList.size(); i++) { // For every agent in the system, check if it's too close
Agent other = (Agent) agentList.get(i);
float d = this.loc.distanceTo(other.loc);
if ((d > 0) && (d < desiredseparation)) { // If the distance is greater than 0 and less than an arbitrary amount (0 when you are yourself)
Vec3D diff = loc.sub(other.loc); // Calculate vector pointing away from neighbor
diff.normalize();
diff.normalizeTo(1/d); // Weight by distance
steer.addSelf(diff);
count++;// Keep track of how many
}
}
if (count > 0) {
steer.scaleSelf(1.0/(float)count); // Average
}
if (steer.magnitude() > 0) { // As long as the vector is greater than 0
// Implement Reynolds: Steering = Desired - Velocity
steer.normalize();
steer.scaleSelf(this.max);
steer.subSelf(this.speed);
steer.limit(this.maxforce);
}
steer.scaleSelf(1);
applyForce(steer);
}
//-------------------------------------------------------------------------------------------
//---------------------------------------Trail Method----------------------------------------
//-------------------------------------------------------------------------------------------
void trail() {
if (objectTrails.size() > 0) {
for (int j = 0; j < objectTrails.size(); j++) {
if (j != 0) {
Vec3D pos = objectTrails.get(j);
Vec3D prevpos = objectTrails.get(j - 1);
stroke(255, 0, 0, map(j, 0, objectTrails.size(), 0, 200));
strokeWeight(map(j, 0, objectTrails.size(), 0.45, 1.0));
line(pos.x, pos.y, prevpos.x, prevpos.y);
}
}
}
}
}
Update