Tutorials (back to the list of tutorials)

## Multi-Agent 2D Examples

### Multi-Agent 2D Example 2 (requires iGeo version 7.5.1 or higher)

The code below defines an agent to make many branches in limited probability. When it doesn't create branches, it goes straight. The interact() method takes care of collision detection. This method uses intersectLine() method to check the collision instead of measuring distance of end points as done in the previous collision detection example .

IVec.intersectLine(pt1,pt2,a.pt1,a.pt2)!=null

The input argument of the intersectLine() method is 4 vector varaibles of one end point of the first line, another end point of the first line, one end point of the second line, and another end point of the second line. It returns an intersection point of two line as IVec variable but if they don't intersect, it returns null value. The two more conditions !pt1.eq(a.pt1) and !pt1.eq(a.pt2) inside the if-condition statement exclude the case of the parent line agent or other branching line agents which share the same parent agent.

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

void setup(){
size(480, 360, IG.GL);
IG.duration(300);
new LineAgent(new IVec(0,0,0), new IVec(0,1,0)).clr(0);
}

static class LineAgent extends IAgent{
IVec pt1, pt2;
boolean isColliding=false;

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

void interact(ArrayList< IDynamics > agents){
if(time == 0){ //only in the first time
for(int i=0; i < agents.size() && !isColliding; i++){
if(agents.get(i) instanceof LineAgent){
LineAgent a = (LineAgent)agents.get(i);
// checking clearance of end point
if(!pt1.eq(a.pt1) && !pt1.eq(a.pt2) &&
IVec.intersectLine(pt1,pt2,a.pt1,a.pt2)!=null){
isColliding=true;
}
}
}
}
}

void update(){
if(isColliding){ del(); }
else if(time == 0){ //if not colliding
new ICurve(pt1,pt2).clr(clr());
IVec dir = pt2.dif(pt1);

double r = red() + IRand.get(-0.06,0.06);
double g = green() + IRand.get(-0.06,0.06);
double b = blue() + IRand.get(-0.06,0.06);

if(IRand.pct(2.5)){ //branching
int num = 15;
for(int i=1; i < num; i++){
if(IRand.pct(40)){
IVec dir2 = dir.dup().rev();
dir2.rot(2*PI*i/num);
new LineAgent(pt2,dir2).clr(r,g,b);
}
}
}
else if(IRand.pct(99)){ //going straight
new LineAgent(pt2,dir).clr(r,g,b);
}
}
}
}
```

The next code adds just one line to let the agent turn slightly instead of going straight by rotating the direction vector dir.

dir.rot( PI/80 );

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

void setup(){
size(480, 360, IG.GL);
IG.duration(300);
new LineAgent(new IVec(0,0,0), new IVec(0,1,0)).clr(0);
}

static class LineAgent extends IAgent{
IVec pt1, pt2;
boolean isColliding=false;

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

void interact(ArrayList< IDynamics > agents){
if(time == 0){ //only in the first time
for(int i=0; i < agents.size() && !isColliding; i++){
if(agents.get(i) instanceof LineAgent){
LineAgent a = (LineAgent)agents.get(i);
// checking clearance of end point
if(!pt1.eq(a.pt1) && !pt1.eq(a.pt2) &&
IVec.intersectLine(pt1,pt2,a.pt1,a.pt2)!=null){
isColliding=true;
}
}
}
}
}

void update(){
if(isColliding){ del(); }
else if(time == 0){ //if not colliding
new ICurve(pt1,pt2).clr(clr());
IVec dir = pt2.dif(pt1);

double r = red() + IRand.get(-0.06,0.06);
double g = green() + IRand.get(-0.06,0.06);
double b = blue() + IRand.get(-0.06,0.06);

if(IRand.pct(2.5)){ //branching
int num = 15;
for(int i=1; i < num; i++){
if(IRand.pct(40)){
IVec dir2 = dir.dup().rev();
dir2.rot(2*PI*i/num);
new LineAgent(pt2,dir2).clr(r,g,b);
}
}
}
else if(IRand.pct(99)){ //bending
dir.rot(PI/80);
new LineAgent(pt2,dir).clr(r,g,b);
}
}
}
}
```

The code below randomize the move of an agent when it doesn't create branches by randomly rotating the direction vector dir.

dir.rot( IRand.get(-PI/8, PI/8) );

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

void setup(){
size(480, 360, IG.GL);
IG.duration(300);
new LineAgent(new IVec(0,0,0), new IVec(0,1,0)).clr(0);
}

static class LineAgent extends IAgent{
IVec pt1, pt2;
boolean isColliding=false;

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

void interact(ArrayList< IDynamics > agents){
if(time == 0){ //only in the first time
for(int i=0; i < agents.size() && !isColliding; i++){
if(agents.get(i) instanceof LineAgent){
LineAgent a = (LineAgent)agents.get(i);
// checking clearance of end point
if(!pt1.eq(a.pt1) && !pt1.eq(a.pt2) &&
IVec.intersectLine(pt1,pt2,a.pt1,a.pt2)!=null){
isColliding=true;
}
}
}
}
}

void update(){
if(isColliding){ del(); }
else if(time == 0){ //if not colliding
new ICurve(pt1,pt2).clr(clr());
IVec dir = pt2.dif(pt1);

double r = red() + IRand.get(-0.06,0.06);
double g = green() + IRand.get(-0.06,0.06);
double b = blue() + IRand.get(-0.06,0.06);

if(IRand.pct(2.5)){ //branching
int num = 15;
for(int i=1; i < num; i++){
if(IRand.pct(40)){
IVec dir2 = dir.dup().rev();
dir2.rot(2*PI*i/num);
new LineAgent(pt2,dir2).clr(r,g,b);
}
}
}
else if(IRand.pct(99)){ //random bend
dir.rot(IRand.get(-PI/20,PI/20));
new LineAgent(pt2,dir).clr(r,g,b);
}
}
}
}
```

This code below changes two parts of the second rotating agent code. First, the range of branching angle is limited to Pi, instead of 2 Pi, by changing this line in the previous code
dir2.rot( 2*PI*i/num );
to
dir2.rot( PI*i/num );
The second change is to scale down (or sometimes up) the length of line agents in both cases of creating branch agents and just going with a single agent. This is done by multiplying some number to the direction vector dir for a single agent or dir2 for branching agents in those lines.
dir2.mul( IRand.get(0.9, 1.35) ); //scale up or down
dir.mul( 0.98 ); //scale down

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

void setup(){
size(480, 360, IG.GL);
IG.duration(250);
new LineAgent(new IVec(0,0,0), new IVec(0,1,0)).clr(0);
}

static class LineAgent extends IAgent{
IVec pt1, pt2;
boolean isColliding=false;

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

void interact(ArrayList< IDynamics > agents){
if(time == 0){ //only in the first time
for(int i=0; i < agents.size() && !isColliding; i++){
if(agents.get(i) instanceof LineAgent){
LineAgent a = (LineAgent)agents.get(i);
// checking clearance of end point
if(!pt1.eq(a.pt1) && !pt1.eq(a.pt2) &&
IVec.intersectLine(pt1,pt2,a.pt1,a.pt2)!=null){
isColliding=true;
}
}
}
}
}

void update(){
if(isColliding){ del(); }
else if(time == 0){ //if not colliding
new ICurve(pt1,pt2).clr(clr());
IVec dir = pt2.dif(pt1);

double r = red() + IRand.get(-0.06,0.06);
double g = green() + IRand.get(-0.06,0.06);
double b = blue() + IRand.get(-0.06,0.06);

if(IRand.pct(2.5)){ //branching
int num = 15;
for(int i=1; i < num; i++){
if(IRand.pct(40)){
IVec dir2 = dir.dup().rev();
dir2.rot(PI*i/num);
dir2.mul(IRand.get(0.9, 1.35)); //scale up or down
new LineAgent(pt2,dir2).clr(r,g,b);
}
}
}
else if(IRand.pct(99)){ //bend and scale
dir.rot(PI/40);
dir.mul(0.98); //scale down
new LineAgent(pt2,dir).clr(r,g,b);
}
}
}
}
```

(back to the list of tutorials)