Tutorials (back to the list of tutorials)

## Multi-Agent 3D Examples

### Multi-Agent 3D Example 1

The following codes show examples to create polygon mesh geometries with branching algorithm. The first code below shows the basic branching algorithm on 2D as a start.

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

void setup() {
size(480, 360, IG.GL);
IG.duration(120);
new MyAgent(IG.v(0, 0, 0), IG.v(1, 0, 0)).clr(0);
}

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

MyAgent(IVec pos, IVec dir) {
pt1 = pos;
pt2 = pos.cp(dir);
}

void interact(ArrayList < IDynamics > agents) {
for (int i=0; i < agents.size() && !isColliding; i++) {
if (agents.get(i) instanceof MyAgent) {
MyAgent agent = (MyAgent)agents.get(i);
if (agent != this) {
double dist = agent.pt2.dist(pt2); //distance of end points
double tolerance = 0.5; //smaller number allows more collision
if (dist < pt1.dist(pt2)*tolerance) {
isColliding = true;
}
}
}
}
}

void update() {
if (isColliding) {
del();
}
else if (time()==0) {
new ICurve(pt1, pt2).clr(clr());

if (IRand.pct(95)) { //branch 1
IVec dir = pt2.dif(pt1);
double angle = IRand.get(-PI/6, PI/6);
dir.rot(IG.zaxis, angle);
double scale = IRand.get(0.90, 1.08);
dir.mul(scale);
new MyAgent(pt2.cp(), dir).clr(clr());
}

if (IRand.pct(50)) { //branch 2
IVec dir = pt2.dif(pt1);
double angle = IRand.get(-PI/3*2, PI/3*2);
dir.rot(IG.zaxis, angle);
double scale = IRand.get(0.90, 1.08);
dir.mul(scale);
new MyAgent(pt2, dir).clr(clr());
}
}
}
}
```

The second code below puts polygon mesh boxes on the start points of the agents. Polygon mesh box is created by this method in IG class.
IG.meshBox(corner, boxDir1, boxDir2, boxDir3)
The direction of the box is aligned to the direction of the agent and its size is randomized. The color is also randomized in gray scale.

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

void setup() {
size(480, 360, IG.GL);
IG.duration(80);
new MyAgent(IG.v(0, 0, 0), IG.v(1, 0, 0)).clr(0.5);
}

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

MyAgent(IVec pos, IVec dir) {
pt1 = pos;
pt2 = pos.cp(dir);
}

void interact(ArrayList < IDynamics > agents) {
for (int i=0; i < agents.size() && !isColliding; i++) {
if (agents.get(i) instanceof MyAgent) {
MyAgent agent = (MyAgent)agents.get(i);
if (agent != this) {
double dist = agent.pt2.dist(pt2); //distance of end points
double tolerance = 0.5; //smaller number allows more collision
if (dist < pt1.dist(pt2)*tolerance) {
isColliding = true;
}
}
}
}
}

void update() {
if (isColliding) {
del();
}
else if (time()==0) {
double boxScale = IRand.get(1.0, 2.5);
IVec boxDir1 = pt2.dif(pt1).mul(boxScale);
IVec boxDir2 = boxDir1.cp().rot(IG.zaxis, PI/2);
IVec boxDir3 = IG.v(0,0,boxDir1.len());
IVec corner = pt1.cp().sub(boxDir1.cp().div(2)).sub(boxDir2.cp().div(2));
IG.meshBox(corner, boxDir1, boxDir2, boxDir3).clr(clr());

if (IRand.pct(95)) { //branch 1
IVec dir = pt2.dif(pt1);
double angle = IRand.get(-PI/6, PI/6);
dir.rot(IG.zaxis, angle);
double scale = IRand.get(0.90, 1.08);
dir.mul(scale);
double gray = (red()+green()+blue())/3+ IRand.get(-0.05,0.05);
new MyAgent(pt2.cp(), dir).clr(gray);
}

if (IRand.pct(50)) { //branch 2
IVec dir = pt2.dif(pt1);
double angle = IRand.get(-PI/3*2, PI/3*2);
dir.rot(IG.zaxis, angle);
double scale = IRand.get(0.90, 1.08);
dir.mul(scale);
double gray = (red()+green()+blue())/3 + IRand.get(-0.05,0.05);
new MyAgent(pt2, dir).clr(gray);
}
}
}
}
```

The next code manipulates a rotational axis of branching to create 3 dimensional branches. The agent class has a new data field axis as the rotational axis of branching rotation and also as one of edge directions of the box geometry.

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

void setup() {
size(480, 360, IG.GL);
IRand.init(2);
IG.duration(40);
new MyAgent(IG.v(0, 0, 0), IG.v(1, 0, 0), IG.v(0, 0, 1)).clr(0.5);
}

class MyAgent extends IAgent {
IVec pt1, pt2, axis;
boolean isColliding=false;

MyAgent(IVec pos, IVec dir, IVec ax) {
pt1 = pos;
pt2 = pos.cp(dir);
axis = ax;
}

void interact(ArrayList < IDynamics > agents) {
for (int i=0; i < agents.size() && !isColliding; i++) {
if (agents.get(i) instanceof MyAgent) {
MyAgent agent = (MyAgent)agents.get(i);
if (agent != this) {
double dist = agent.pt2.dist(pt2); //distance of end points
double tolerance = 0.5; //smaller number allows more collision
if (dist < pt1.dist(pt2)*tolerance) {
isColliding = true;
}
}
}
}
}

void update() {
if (isColliding) {
del();
}
else if (time()==0) {
double boxScale = IRand.get(1.0, 2.5);
IVec boxDir1 = pt2.dif(pt1).mul(boxScale);
IVec boxDir2 = boxDir1.cp().rot(axis, PI/2);
IVec boxDir3 = boxDir1.cross(boxDir2).len(boxDir1.len()); // perpendicular to boxDir1 and boxDir2
IVec corner = pt1.cp().sub(boxDir1.cp().div(2)).sub(boxDir2.cp().div(2));
IG.meshBox(corner, boxDir1, boxDir2, boxDir3).clr(clr());
axis = boxDir3;

if (IRand.pct(95)) { //branch 1
IVec dir = pt2.dif(pt1);
double angle = IRand.get(-PI/6, PI/6);
dir.rot(axis, angle);
double scale = IRand.get(0.90, 1.08);
dir.mul(scale);
IVec axis2 = axis.cp().rot(dir, IRand.get(-PI/6, PI/6));
double gray = (red()+green()+blue())/3+ IRand.get(-0.05,0.05);
new MyAgent(pt2.cp(), dir, axis2).clr(gray);
}

if (IRand.pct(50)) { //branch 2
IVec dir = pt2.dif(pt1);
double angle = IRand.get(-PI/3*2, PI/3*2);
dir.rot(axis, angle);
double scale = IRand.get(0.90, 1.08);
dir.mul(scale);
IVec axis2 = axis.cp().rot(dir, IRand.get(-PI/6, PI/6));
double gray = (red()+green()+blue())/3 + IRand.get(-0.05,0.05);
new MyAgent(pt2, dir, axis2).clr(gray);
}
}
}
}
```

The code below takes a Rhino input file which contains one polygon mesh geometry and use this geometry as each agent's geometry instead of a box. The agent class has a new data field of mesh and the original template mesh geometry from the input file is passed to each agents. This template mesh geometry is copied, moved and rotated to the agent's orientation which is defined by pt1, pt2 and axis. The method to move and rotate is transform(xvec,yvec,zvec,translate).
mesh.cp().transform(meshDir1, meshDir2, meshDir3, pt1);
The first three input arguments of IVec are new vectors to map the original X, Y and Z vectors onto. The fourth input argument of IVec is to move the whole geometry, just like adding a vector to the geometry.

The input file with a polygon mesh geometry used in the example is the following.

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

void setup() {
size(480, 360, IG.GL);
IRand.init(2);
IG.duration(40);
IG.open("mesh1.3dm");
IMesh mesh = IG.mesh(0);
mesh.del(); //hide the original
new MyAgent(mesh, IG.v(0, 0, 0), IG.v(1, 0, 0), IG.v(0, 0, 1)).clr(0.5);
}

class MyAgent extends IAgent {
IVec pt1, pt2, axis;
boolean isColliding=false;
IMesh mesh;

MyAgent(IMesh m, IVec pos, IVec dir, IVec ax) {
pt1 = pos;
pt2 = pos.cp(dir);
axis = ax;
mesh = m;
}

void interact(ArrayList < IDynamics > agents) {
for (int i=0; i < agents.size() && !isColliding; i++) {
if (agents.get(i) instanceof MyAgent) {
MyAgent agent = (MyAgent)agents.get(i);
if (agent != this) {
double dist = agent.pt2.dist(pt2); //distance of end points
double tolerance = 0.5; //smaller number allows more collision
if (dist < pt1.dist(pt2)*tolerance) {
isColliding = true;
}
}
}
}
}

void update() {
if (isColliding) {
del();
}
else if (time()==0) {
double meshScale = IRand.get(2.0, 5.0);
IVec meshDir1 = pt2.dif(pt1).mul(meshScale);
IVec meshDir2 = meshDir1.cp().rot(axis, PI/2);
IVec meshDir3 = meshDir1.cross(meshDir2).len(meshDir1.len()); // perpendicular to meshDir1 and meshDir2

mesh.cp().transform(meshDir1, meshDir2, meshDir3, pt1).clr(clr());
axis = meshDir3;

if (IRand.pct(95)) { //branch 1
IVec dir = pt2.dif(pt1);
double angle = IRand.get(-PI/3, PI/3);
dir.rot(axis, angle);
double scale = IRand.get(0.90, 1.05);
dir.mul(scale);
IVec axis2 = axis.cp().rot(dir, IRand.get(-PI/3, PI/3));
double gray = (red()+green()+blue())/3+ IRand.get(-0.05,0.05);
new MyAgent(mesh, pt2.cp(), dir, axis2).clr(gray);
}

if (IRand.pct(50)) { //branch 2
IVec dir = pt2.dif(pt1);
double angle = IRand.get(-PI/3*2, PI/3*2);
dir.rot(axis, angle);
double scale = IRand.get(0.90, 1.05);
dir.mul(scale);
IVec axis2 = axis.cp().rot(dir, IRand.get(-PI/3, PI/3));
double gray = (red()+green()+blue())/3 + IRand.get(-0.05,0.05);
new MyAgent(mesh, pt2, dir, axis2).clr(gray);
}
}
}
}
```

(back to the list of tutorials)