Tutorials (back to the list of tutorials)

## Multi-Agent 2D Examples

### Multi-Agent 2D Example 4

The following example shows a line agent which turns when it finds another line agent in front of it. It turns into the parallel direction of the another agent and it turns only left, not right. In its interact method, the agent searches the closest other agent in front within the threshold range and measures the angle of the other agent. This angle is stored in frontAngle and this is used to rotate the agent in the update method.

```import igeo.*;
import processing.opengl.*;

void setup(){
size(480,360,IG.GL);
IRand.init(3);
IG.duration(700);
new LineAgent(IG.v(-0.1,0,0), IG.v(1,0,0)).clr(0);
new LineAgent(IG.v(500,-0.1,0), IG.v(0,1,0)).clr(0);
new LineAgent(IG.v(500+0.1,500,0), IG.v(-1,0,0)).clr(0);
new LineAgent(IG.v(0,500+0.1,0), IG.v(0,-1,0)).clr(0);

for(int i=0; i < 10; i++){
new LineAgent(IRand.pt(100,100,0,400,400,0), IRand.pt(-10,-10,0,10,10,0)).clr(IRand.clr(0.4,0.4,0.4));
}
}

static class LineAgent extends IAgent{
// constants
static final double length = 2;
static final double clearance = 1.99; //less than length
static final double angleThreshold = PI/20;
static final double threshold = 10;

IVec pt1, pt2;
boolean isColliding=false;
double angle=0;
double minDist=0;
double frontAngle=0;

LineAgent(IVec pt, IVec dir){
pt1 = pt;
pt2 = pt.cp(dir.cp().len(length));
}

void interact(ArrayList < IDynamics > agents){
if(time == 0){ //only in the first time
minDist = -1; //reset
for(int i=0; i < agents.size() && !isColliding; i++){
if(agents.get(i) instanceof LineAgent){
LineAgent agent = (LineAgent)agents.get(i);
if(agent != this){ //agents include "this"
// checking clearance of end point
double dist = agent.pt2.dist(pt2);
if(dist < clearance){ isColliding=true; }
else if(dist < threshold){ //not colliding but close
if(!pt1.eq(agent.pt2) && //not the parent
pt2.dif(pt1).angle(agent.pt2.dif(pt1)) < angleThreshold && //in front
(minDist < 0 || dist < minDist) ){ //closest one
minDist = dist;
frontAngle = pt2.dif(pt1).angle(agent.pt2.dif(agent.pt1),IG.zaxis);
}
}
}
}
}
}
}

void update(){
if(time == 0){
if(isColliding){ del(); }
else{ //if not colliding
new ICurve(pt1,pt2).clr(clr());
IVec dir = pt2.dif(pt1);
if(IRand.pct(1)){ //branching
double r = red()+IRand.get(-0.1,0.1);
double g = green()+IRand.get(-0.1,0.1);
double b = blue()+IRand.get(-0.1,0.1);
new LineAgent(pt2, dir.cp().rot(IRand.get(PI/3,2*PI/3))).clr(r,g,b);
}
if(frontAngle < 0){ frontAngle+=PI; } //going in only one direction
double r = red()+IRand.get(-0.03,0.03);
double g = green()+IRand.get(-0.03,0.03);
double b = blue()+IRand.get(-0.03,0.03);
new LineAgent(pt2, dir.rot(frontAngle)).clr(r,g,b);
}
}
}
}
```

The next code change the previous code to round the turning corner by bending the lines with a few segments. To enable bending through multiple generation of agents, the instance field of bendAngle and bendCount are added to LineAgent class.

```import igeo.*;
import processing.opengl.*;

void setup(){
size(480,360,IG.GL);
IRand.init(2);
IG.duration(700);
new LineAgent(IG.v(-0.1,0,0),IG.v(1,0,0),0,0).clr(0);
new LineAgent(IG.v(500,-0.1,0),IG.v(0,1,0),0,0).clr(0);
new LineAgent(IG.v(500+0.1,500,0),IG.v(-1,0,0),0,0).clr(0);
new LineAgent(IG.v(0,500+0.1,0),IG.v(0,-1,0),0,0).clr(0);

new LineAgent(IG.v(500/2,0,0),IG.v(1,0,0),0,0).clr(0);
new LineAgent(IG.v(500,500/2,0),IG.v(0,1,0),0,0).clr(0);
new LineAgent(IG.v(500/2,500,0),IG.v(-1,0,0),0,0).clr(0);
new LineAgent(IG.v(0,500/2,0),IG.v(0,-1,0),0,0).clr(0);

for(int i=0; i < 30; i++){
new LineAgent(IRand.pt(100,100,0,400,400,0),IRand.pt(-10,-10,0,10,10,0),0,0).clr(IRand.clr(0.4,0.4,0.4));
}
}

static class LineAgent extends IAgent{
// constants
static final double length = 2;
static final double clearance = 1.99; //less than length
static final double angleThreshold = PI/20;
static final double threshold = 10;
static final int bendSize = 2;

IVec pt1, pt2;
boolean isColliding=false;
double angle=0;
double minDist=0;
double frontAngle=0;
double bendAngle=0;
int bendCount = 0;

LineAgent(IVec pt, IVec dir, double bend, int bcount){
pt1 = pt;
pt2 = pt.cp(dir.cp().len(length));
bendAngle = bend;
bendCount = bcount;
}

void interact(ArrayList < IDynamics > agents){
if(time == 0){ //only in the first time
minDist = -1; //reset
for(int i=0; i < agents.size() && !isColliding; i++){
if(agents.get(i) instanceof LineAgent){
LineAgent agent = (LineAgent)agents.get(i);
if(agent != this){ //agents include "this"
// checking clearance of end point
double dist = agent.pt2.dist(pt2);
if(dist < clearance){ isColliding=true; }
else if(dist < threshold){ //not colliding but close
if(!pt1.eq(agent.pt2) && //not the parent
pt2.dif(pt1).angle(agent.pt2.dif(pt1)) < angleThreshold && //in front
(minDist < 0 || dist < minDist) ){ //closest one
minDist = dist;
frontAngle = pt2.dif(pt1).angle(agent.pt2.dif(agent.pt1),IG.zaxis);
}
}
}
}
}
}
}

void update(){
if(time == 0){
if(isColliding){ del(); }
else{ //if not colliding
new ICurve(pt1,pt2).clr(clr());
IVec dir = pt2.dif(pt1);
if(IRand.pct(1)){ //branching
double r = red()+IRand.get(-0.1,0.1);
double g = green()+IRand.get(-0.1,0.1);
double b = blue()+IRand.get(-0.1,0.1);
new LineAgent(pt2, dir.cp().rot(IRand.get(PI/3,2*PI/3)), 0, 0).clr(r,g,b);
}

double r = red()+IRand.get(-0.03,0.03);
double g = green()+IRand.get(-0.03,0.03);
double b = blue()+IRand.get(-0.03,0.03);
//checking bend
if(bendCount == 0 && minDist >=0 && frontAngle!=0){
if(frontAngle < 0){ frontAngle+=PI; }//going in only one direction
bendCount = bendSize;
bendAngle = frontAngle/bendCount;
}
if(bendCount > 0){ //bending
dir.rot(bendAngle);
new LineAgent(pt2, dir, bendAngle, bendCount-1).clr(r,g,b);
}
else{ //go straight
new LineAgent(pt2, dir, 0, 0).clr(r,g,b);
}
}
}
}
}
```

(back to the list of tutorials)