class Tracker {
List<Vec3D> objectTrails;
Vec3D speed;
Vec3D acc = new Vec3D(0, 0, 0);
Vec3D loc;
String type, babyType;
float maxforce = 0.01;
float max = 4.0f;
float vel = 1.0;
float amp,r;
float maxDist = 100;
boolean sepActive = false;
boolean wonderTrigger = false;
boolean instanceable;
int maxChildren,instanceTriggerCount;
float wandertheta;
float wanderTVal; //value to swap rotation after x position changes
float wanderD; // Distance for our "wander circle"
float wanderR; //Radius for our "wander circle"
float wanderT; // Randomly change wander theta
//-------------------------------------------------------------------------------------
Tracker(Vec3D location, Vec3D Speed, boolean instance, String Type, String BabyType) {
this.loc = location;
this.objectTrails = new ArrayList<Vec3D>();
this.r = 12;
this.instanceTriggerCount = 0;
this.instanceable = instance;
this.type = Type;
this.wandertheta = 0;
this.babyType = BabyType;
this.speed = Speed;
}
//----------------------------------------------------------------------
//Run-------------------------------------------------------------------
//Run all the other methods for this dude-------------------------------
//----------------------------------------------------------------------
void run() {
pathFollow();
if (this.sepActive && this.instanceable) {
separate();
}
if(this.wonderTrigger){
wander();
}
/*
if (this.wonderTrigger) {
wander();
}
*/
if (simulate) {
move();
}
viz();
trail();
}
//--------------------------------------------------------------------------
//Wanderer Behavior---------------------------------------------------------
//“Wandering is a type of random steering which has some long term order: -
//the steering direction on one frame is related to the steering direction
//on the next frame. This produces more interesting motion than, for example
//,simply generating a random steering direction each frame.” Reynolds-----
//--------------------------------------------------------------------------
void wander() {
if (stepCount < this.wanderTVal) {
if (type == "parent") {
this.wandertheta = this.wanderT;
} else {
if (babyType == "w_a") {
this.wandertheta = this.wanderT;
} else {
this.wandertheta = -this.wanderT;
}
}
} else if (stepCount >= this.wanderTVal && stepCount <this.wanderTVal*2) {
if (type == "parent") {
this.wandertheta = -this.wanderT;
} else {
if (babyType == "w_a") {
this.wandertheta = -this.wanderT;
} else {
this.wandertheta = this.wanderT;
}
}
} else {
stepCount = 0;
}
// Now we have to calculate the new location to steer towards on the wander circle
Vec3D circleloc = speed.copy(); // Start with velocity
circleloc.normalize(); // Normalize to get heading
circleloc.scaleSelf(this.wanderD); // Multiply by distance
circleloc.addSelf(this.loc); // Make it relative to boid's location
float h = speed.headingXY(); // We need to know the heading to offset wandertheta
Vec3D circleOffSet = new Vec3D(this.wanderR*cos(this.wandertheta+h), this.wanderR*sin(this.wandertheta+h), 0);
Vec3D target = circleloc.add(circleOffSet);
seek(target);
if (diagram) drawWanderStuff(loc, circleloc, target, wanderR); // Render wandering circle, etc.
}
//-------------------------------------------------------------------------------------------
//Diagram------------------------------------------------------------------------------------
//A method just to draw the circle associated with wandering---------------------------------
//-------------------------------------------------------------------------------------------
void drawWanderStuff(Vec3D location, Vec3D circle, Vec3D target, float rad) {
stroke(255);
noFill();
ellipseMode(CENTER);
ellipse(circle.x, circle.y, rad*2, rad*2);
ellipse(target.x, target.y, 4, 4);
line(location.x, location.y, circle.x, circle.y);
line(circle.x, circle.y, target.x, target.y);
}
//-------------------------------------------------------------------------------------------
//Path Follow Method-------------------------------------------------------------------------
//Follows the path if inside the radius------------------------------------------------------
//-------------------------------------------------------------------------------------------
void pathFollow() {
Vec3D predict = this.speed.copy();
predict.normalize();
predict.scaleSelf(this.amp);
Vec3D nextPosPrev = loc.add(predict);
Vec3D target = null;
Vec3D normal = null;
float worldRecord = 1000000;
for (int z = 0; z < pathList.size(); z++) {
for (int i = 0; i < pathList.get(z).points.size()-1; i++) {
Vec3D a = pathList.get(z).points.get(i);
Vec3D b = pathList.get(z).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);
}
}
}
//remove if you want to just go to the line
//maxDist = 10000000000000f;
if (worldRecord < this.maxDist) {
this.sepActive = true;
this.wonderTrigger = true;
if (worldRecord > tempPath.radius) {
seek(target);
} else {
//IF TYPE IS PARENT AND HAS CREATED LESS THAN THE ALLOWED CHILDREN THEN CREATE MORE
if (this.instanceable && instanceTriggerCount < maxChildren) {
addNew = true;
triggerLoc = nextPosPrev.copy();
childSpawners.add(new Vec3D(this.loc));
childSpawnType.add(instanceTriggerCount);
triggerCount++;
instanceTriggerCount ++;
} else {
addNew = false;
}
Vec3D zero = new Vec3D(0, 0, 0);
zero.scaleSelf(3);
acc.addSelf(zero);
}
} else {
wonderTrigger = false;
}
// Draw the debugging stuff
if (drawFutureLoc) {
// 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(this.loc);
desired.normalize();
desired.scaleSelf(this.max);
Vec3D steer = desired.sub(this.speed);
steer.limit(this.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------------------------------------------------------------------
//Move and update the position------------------------------------------
//----------------------------------------------------------------------
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---------------------------------------------------------------------------------
//Draw heads---------------------------------------------------------------------------------
void viz() {
if (type == "parent") {
stroke(255, 0, 0);
strokeWeight(6);
point(this.loc.x, this.loc.y);
} else {
if (babyType == "w_a") {
stroke(0, 0, 255);
strokeWeight(6);
point(this.loc.x, this.loc.y);
} else {
stroke(0, 255, 255);
strokeWeight(6);
point(this.loc.x, this.loc.y);
}
}
}
//-------------------------------------------------------------------------------------------
//Separation Method--------------------------------------------------------------------------
//Method checks for nearby boids and steers away---------------------------------------------
//-------------------------------------------------------------------------------------------
void separate () {
float desiredseparation = this.r*2;
Vec3D steer = new Vec3D(0, 0, 0);
int count = 0;
// For every boid in the system, check if it's too close
for (int i = 0; i < agentList.size(); i++) {
Tracker other = (Tracker) agentList.get(i);
float d = this.loc.distanceTo(other.loc);
// 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
Vec3D diff = loc.sub(other.loc);
diff.normalize();
diff.normalizeTo(1/d); // Weight by distance
steer.addSelf(diff);
count++; // Keep track of how many
}
}
// Average -- divide by how many
if (count > 0) {
steer.scaleSelf(1.0/(float)count);
}
// As long as the vector is greater than 0
if (steer.magnitude() > 0) {
// Implement Reynolds: Steering = Desired - Velocity
steer.normalize();
steer.scaleSelf(this.max);
steer.subSelf(this.speed);
steer.limit(this.maxforce);
}
steer.scaleSelf(3);
applyForce(steer);
}
//-------------------------------------------------------------------------------------------
//Trail Method-------------------------------------------------------------------------------
//The trail is drawn from previous loc to new location---------------------------------------
//-------------------------------------------------------------------------------------------
void trail() {
if (this.objectTrails.size() > 0) {
for (int j = 0; j < this.objectTrails.size(); j++) {
if (j != 0) {
Vec3D pos = this.objectTrails.get(j);
Vec3D prevpos = this.objectTrails.get(j - 1);
if(this.type == "parent"){
stroke(255, 0, 0, map(j, 0, this.objectTrails.size(), 0, 200));
strokeWeight(map(j, 0, this.objectTrails.size(), 0.45, 1.5));
}else if(this.babyType == "w_a"){
stroke(0, 0, 255, map(j, 0, this.objectTrails.size(), 0, 200));
strokeWeight(map(j, 0, this.objectTrails.size(), 0.45, 1.0));
}else{
stroke(0,255, 255, map(j, 0, this.objectTrails.size(), 0, 200));
strokeWeight(map(j, 0, this.objectTrails.size(), 0.45, 1.0));
}
line(pos.x, pos.y, prevpos.x, prevpos.y);
}
}
}
}
}
Update