Tutorials (back to the list of tutorials)

## Variation of Geometries on Multi Agent Algorithm (requires iGeo version 7.4.0 or higher)

### Pipes on Agents

This section shows examples of variation of geometries created along agents. Taking one of the examples of multi-agent algorithms already shown in the tutorials, different ways to put geometries on agents are described. The multi-agent algorithm used here is this branching algorithm. The following example shows a simple way to put one square pipe on each agent.

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

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

static class LineAgent extends IAgent{
static double length = 2;
static double clearance = 1.99; //less than length

IVec pt1, pt2;
boolean isColliding=false;

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

void interact(IDynamics agent){
if(time == 0){ //only in the first time
if(agent instanceof LineAgent){
LineAgent lineAgent = (LineAgent)agent;
// checking clearance of end point
if(lineAgent.pt2.dist(pt2) < clearance){
isColliding=true;
}
}
}
}

void update(){
if(isColliding){
del();
}
else if(time == 0){ //if not colliding
IG.squarePipe(pt1,pt2,.2).clr(IRandom.gray());
IVec dir = pt2.dif(pt1);
//rotation axis with random direction
IVec axis = IRandom.pt(-1,1).len(1);
if(IRandom.percent(50)){ //bend
new LineAgent(pt2, dir.dup().rot(axis,
IRandom.get(PI/3,PI/3*2)));
}
if(IRandom.percent(50)){ //bend the other way
new LineAgent(pt2, dir.dup().rot(axis,
-IRandom.get(PI/3,PI/3*2)));
}
if(IRandom.percent(80)){ //straight
new LineAgent(pt2, dir.dup());
}
}
}
}

### Tangent Curves on Agents

Here is an example to put curves whose tangents at the end points are matching with the tangents of the connected agents. Instead of putting a line between the start and end points, it puts a degree-2 curve on 3 points of the midpoint of the start and end points, the end point and the midpoint of the start and end points of the next agent.

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

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

static class LineAgent extends IAgent{
static double length = 2;
static double clearance = 1.99; //less than length

IVec pt1, pt2;
boolean isColliding=false;

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

void interact(IDynamics agent){
if(time == 0){ //only in the first time
if(agent instanceof LineAgent){
LineAgent lineAgent = (LineAgent)agent;
// checking clearance of end point
if(lineAgent.pt2.dist(pt2) < clearance){
isColliding=true;
}
}
}
}

void update(){
if(isColliding){
del();
}
else if(time == 0){ //if not colliding
IVec dir = pt2.dif(pt1);
IVec axis = IRandom.pt(-1,1).len(1);
if(IRandom.percent(100)){ //bend
IVec nextDir1 =
dir.dup().rot(axis,IRandom.get(PI/3,PI/3*2));
//degree 2 curve at midpoint of pt1&pt2, pt2, and midpoint of pt2 and next agent's point
new ICurve(new IVec[]{ pt1.mid(pt2),
pt2,
pt2.mid(pt2.cp(nextDir1))},
2).clr(clr());
new LineAgent(pt2, nextDir1).clr(clr());
}
if(IRandom.percent(50)){ //bend the other way
//degree 2 curve at midpoint of pt1&pt2, pt2, and midpoint of pt2 and next agent's point
IVec nextDir2 =
dir.dup().rot(axis,-IRandom.get(PI/3,PI/3*2));
new ICurve(new IVec[]{ pt1.mid(pt2),
pt2,
pt2.mid(pt2.cp(nextDir2))},
2).clr(clr());
new LineAgent(pt2, nextDir2).clr(clr());
}
}
}
}

### Surfaces with Tangent Edges on Agents

The following example is putting a surface whose edges are tangent to other connected agents' surface edges. The edges of surface are defined in the same way with the tangent curves in the previous example. The surface has 3 by 3 control points and its degree in U direction is 2 and also 2 in V direction. 3 of control points are same with those of the previous tangent curve's control points and other 3 are same with control points of another branching agent's curve.

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

void setup(){
size(480, 360, IG.GL);
IRand.init(8);
IG.duration(120);
new LineAgent(new IVec(0,0,0), new IVec(1,0,0)).clr(0);
IG.fill();
}

static class LineAgent extends IAgent{
static double length = 2;
static double clearance = 1.99; //less than length
IVec pt1, pt2;
boolean isColliding=false;

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

void interact(IDynamics agent){
if(time == 0){ //only in the first time
if(agent instanceof LineAgent){
LineAgent lineAgent = (LineAgent)agent;
//checking clearance of end point
if(lineAgent.pt2.dist(pt2) < clearance){
isColliding=true;
}
}
}
}

void update(){
if(isColliding){ del(); }
else if(time == 0){ //if not colliding
IVec dir = pt2.dif(pt1);
IVec axis = IRand.pt(-1,1).len(1);
IVec nextDir1 = dir.dup().rot(axis,PI/3);
IVec nextDir2 = dir.dup().rot(axis,-PI/3);
//degree 2 surface with 3x3 control points
IVec[][] cpts = new IVec[3][3];
cpts[0][0] = pt1.mid(pt2);
cpts[0][1] = pt1.mid(pt2);
cpts[0][2] = pt1.mid(pt2);
cpts[1][0] = pt2;
cpts[1][1] = pt2;
cpts[1][2] = pt2;
cpts[2][0] = pt2.mid(pt2.cp(nextDir1));
cpts[2][1] = pt2;
cpts[2][2] = pt2.mid(pt2.cp(nextDir2));
new ISurface(cpts, 2, 2).clr(clr());
int r = clr().getRed() + IRand.getInt(-10, 10);
int g = clr().getGreen() + IRand.getInt(-10, 10);
int b = clr().getBlue() + IRand.getInt(-10, 10);
if(IRand.percent(80)){ //bend
new LineAgent(pt2, nextDir1).clr(r,g,b);
}
if(IRand.percent(50)){ //bend the other way
new LineAgent(pt2, nextDir2).clr(r,g,b);
}
}
}
}

### Surfaces around Agents: 1

The code below shows an example to put surfaces on the edges offset from the Y-shape branch skeleton of an agent. First offset points are calculated with vectors which are perpendicular to the axis of the agent and the direction of each branch. (offset1, offset2, offset3). Those offset vectors are calculated by cross vector operation.

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

void setup(){
size(480, 360, IG.GL);
IRand.init(3);
IG.duration(30);
new LineAgent(new IVec(0,0,0), new IVec(1,0,0)).clr(0);
IG.fill();
}

static class LineAgent extends IAgent{
static double length = 2;
static double clearance = 1.99; //less than length
IVec pt1, pt2;
boolean isColliding=false;

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

void interact(IDynamics agent){
if(time == 0){ //only in the first time
if(agent instanceof LineAgent){
LineAgent lineAgent = (LineAgent)agent;
//checking clearance of end point
if(lineAgent.pt2.dist(pt2) < clearance){
isColliding=true;
}
}
}
}

void update(){
if(isColliding){ del(); }
else if(time == 0){ //if not colliding

new ICurve(pt1,pt2); // center line

IVec dir = pt2.dif(pt1);
//making axis perpendicular to dir
IVec axis = IRand.pt(-1,1).cross(dir);
// child dir & points
IVec nextDir1 = dir.dup().rot(axis,PI/3);
IVec nextDir2 = dir.dup().rot(axis,-PI/3);
IVec nextPt1 = pt2.cp(nextDir1);
IVec nextPt2 = pt2.cp(nextDir2);
//midpoints
IVec mid1 = pt1.mid(pt2);
IVec mid2 = pt2.mid(nextPt1);
IVec mid3 = pt2.mid(nextPt2);
//mid of midpoints
IVec quarter1 = pt2.mid(mid1);
IVec quarter2 = pt2.mid(mid2);
IVec quarter3 = pt2.mid(mid3);

double offsetWidth = -0.5;
IVec offset1 = dir.cross(axis).len(offsetWidth);
IVec offset2 = nextDir1.cross(axis).len(offsetWidth);
IVec offset3 = nextDir2.cross(axis).len(offsetWidth);

//offset edge points 1
IVec edgePt11 = mid1.cp(offset1);
IVec edgePt12 = quarter1.cp(offset1);
IVec edgePt13 = quarter2.cp(offset2);
IVec edgePt14 = mid2.cp(offset2);
//offset edge points 2
offset2.flip(); //offset to opposite
IVec edgePt21 = mid2.cp(offset2);
IVec edgePt22 = quarter2.cp(offset2);
IVec edgePt23 = quarter3.cp(offset3);
IVec edgePt24 = mid3.cp(offset3);
//offset edge points 3
offset1.flip(); //offset to opposite
offset3.flip(); //offset to opposite
IVec edgePt31 = mid3.cp(offset3);
IVec edgePt32 = quarter3.cp(offset3);
IVec edgePt33 = quarter1.cp(offset1);
IVec edgePt34 = mid1.cp(offset1);
//degree 3 curves
new ICurve(new IVec[]{ edgePt11,edgePt12,
edgePt13,edgePt14 }, 3).clr(0);
new ICurve(new IVec[]{ edgePt21,edgePt22,
edgePt23,edgePt24 }, 3).clr(0);
new ICurve(new IVec[]{ edgePt31,edgePt32,
edgePt33,edgePt34 }, 3).clr(0);

if(IRand.percent(80)){ //bend
new LineAgent(pt2, nextDir1);
}
if(IRand.percent(50)){ //bend the other way
new LineAgent(pt2, nextDir2);
}
}
}
}

Then next, a surface on the offset curve is calculated by shifting the control points on both direction of the axis with the depthVec vector.

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

void setup(){
size(480, 360, IG.GL);
IRand.init(3);
IG.duration(30);
new LineAgent(new IVec(0,0,0), new IVec(1,0,0)).clr(0);
IG.fill();
}

static class LineAgent extends IAgent{
static double length = 2;
static double clearance = 1.99; //less than length
IVec pt1, pt2;
boolean isColliding=false;

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

void interact(IDynamics agent){
if(time == 0){ //only in the first time
if(agent instanceof LineAgent){
LineAgent lineAgent = (LineAgent)agent;
//checking clearance of end point
if(lineAgent.pt2.dist(pt2) < clearance){
isColliding=true;
}
}
}
}

void update(){
if(isColliding){ del(); }
else if(time == 0){ //if not colliding

new ICurve(pt1,pt2); // center line

IVec dir = pt2.dif(pt1);
//making axis perpendicular to dir
IVec axis = IRand.pt(-1,1).cross(dir);
// child dir & points
IVec nextDir1 = dir.dup().rot(axis,PI/3);
IVec nextDir2 = dir.dup().rot(axis,-PI/3);
IVec nextPt1 = pt2.cp(nextDir1);
IVec nextPt2 = pt2.cp(nextDir2);
//midpoints
IVec mid1 = pt1.mid(pt2);
IVec mid2 = pt2.mid(nextPt1);
IVec mid3 = pt2.mid(nextPt2);
//mid of midpoints
IVec quarter1 = pt2.mid(mid1);
IVec quarter2 = pt2.mid(mid2);
IVec quarter3 = pt2.mid(mid3);

double offsetWidth = -0.5;
IVec offset1 = dir.cross(axis).len(offsetWidth);
IVec offset2 = nextDir1.cross(axis).len(offsetWidth);
IVec offset3 = nextDir2.cross(axis).len(offsetWidth);

//offset edge points 1
IVec edgePt11 = mid1.cp(offset1);
IVec edgePt12 = quarter1.cp(offset1);
IVec edgePt13 = quarter2.cp(offset2);
IVec edgePt14 = mid2.cp(offset2);
//offset edge points 2
offset2.flip(); //offset to opposite
IVec edgePt21 = mid2.cp(offset2);
IVec edgePt22 = quarter2.cp(offset2);
IVec edgePt23 = quarter3.cp(offset3);
IVec edgePt24 = mid3.cp(offset3);
//offset edge points 3
offset1.flip(); //offset to opposite
offset3.flip(); //offset to opposite
IVec edgePt31 = mid3.cp(offset3);
IVec edgePt32 = quarter3.cp(offset3);
IVec edgePt33 = quarter1.cp(offset1);
IVec edgePt34 = mid1.cp(offset1);

double depth = 0.5;
IVec depthVec = axis.dup().len(depth);

IVec[][] cpts1 = new IVec[4][2];
cpts1[0][1] = edgePt11.dup().sub(depthVec);
cpts1[1][1] = edgePt12.dup().sub(depthVec);
cpts1[2][1] = edgePt13.dup().sub(depthVec);
cpts1[3][1] = edgePt14.dup().sub(depthVec);

IVec[][] cpts2 = new IVec[4][2];
cpts2[0][1] = edgePt21.dup().sub(depthVec);
cpts2[1][1] = edgePt22.dup().sub(depthVec);
cpts2[2][1] = edgePt23.dup().sub(depthVec);
cpts2[3][1] = edgePt24.dup().sub(depthVec);

IVec[][] cpts3 = new IVec[4][2];
cpts3[0][1] = edgePt31.dup().sub(depthVec);
cpts3[1][1] = edgePt32.dup().sub(depthVec);
cpts3[2][1] = edgePt33.dup().sub(depthVec);
cpts3[3][1] = edgePt34.dup().sub(depthVec);

new ISurface(cpts1, 3, 1);
new ISurface(cpts2, 3, 1);
new ISurface(cpts3, 3, 1);

if(IRand.percent(80)){ //bend
new LineAgent(pt2, nextDir1);
}
if(IRand.percent(50)){ //bend the other way
new LineAgent(pt2, nextDir2);
}
}
}
}

In this example, the surface edges between an agent and the next child are not matching. A technique to make it connected and tangent is shown in the next example.

### Surfaces around Agents: 2

This example shows offset edge geometries connected smoothly with other child agents' geometries. To do this, each agent needs to keep information of axis direction around which the child branches are rotated. The added instance field is axis and the child agents' axis nextAxis1, nextAxis2 is defined inside update() method.

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

void setup(){
size(480, 360, IG.GL);
IRand.init(3);
IG.duration(30);
//second and third vector needs to be perpendicular
new LineAgent(new IVec(0,0,0), new IVec(1,0,0),
new IVec(0,0,1)).clr(0);
IG.fill();
}

static class LineAgent extends IAgent{
static double length = 2;
static double clearance = 1.99; //less than length
IVec pt1, pt2, axis;
boolean isColliding=false;

LineAgent(IVec pt, IVec dir, IVec ax){
pt1 = pt;
axis = ax;
}

void interact(IDynamics agent){
if(time == 0){ //only in the first time
if(agent instanceof LineAgent){
LineAgent lineAgent = (LineAgent)agent;
//checking clearance of end point
if(lineAgent.pt2.dist(pt2) < clearance){
isColliding=true;
}
}
}
}

void update(){
if(isColliding){ del(); }
else if(time == 0){ //if not colliding

new ICurve(pt1,pt2); // center line

IVec dir = pt2.dif(pt1);
// child dir & points
IVec nextDir1 = dir.dup().rot(axis, IRand.get(PI/4,PI/3));
IVec nextDir2 = dir.dup().rot(axis,-IRand.get(PI/4,PI/3));
IVec nextPt1 = pt2.cp(nextDir1);
IVec nextPt2 = pt2.cp(nextDir2);
//midpoints
IVec mid1 = pt1.mid(pt2);
IVec mid2 = pt2.mid(nextPt1);
IVec mid3 = pt2.mid(nextPt2);
//mid of midpoints
IVec quarter1 = pt2.mid(mid1);
IVec quarter2 = pt2.mid(mid2);
IVec quarter3 = pt2.mid(mid3);

//axis of child agents
IVec nextAxis1 = axis.dup().rot(nextDir1, IRand.get(-PI/3,PI/3));
IVec nextAxis2 = axis.dup().rot(nextDir2, IRand.get(-PI/3,PI/3));

double offsetWidth = -0.5;
IVec offset1 = dir.cross(axis).len(offsetWidth);
IVec offset2 = nextDir1.cross(nextAxis1).len(offsetWidth);
IVec offset3 = nextDir2.cross(nextAxis2).len(offsetWidth);

//offset edge points 1
IVec edgePt11 = mid1.cp(offset1);
IVec edgePt12 = quarter1.cp(offset1);
IVec edgePt13 = quarter2.cp(offset2);
IVec edgePt14 = mid2.cp(offset2);
//offset edge points 2
offset2.flip(); //offset to opposite
IVec edgePt21 = mid2.cp(offset2);
IVec edgePt22 = quarter2.cp(offset2);
IVec edgePt23 = quarter3.cp(offset3);
IVec edgePt24 = mid3.cp(offset3);
//offset edge points 3
offset1.flip(); //offset to opposite
offset3.flip(); //offset to opposite
IVec edgePt31 = mid3.cp(offset3);
IVec edgePt32 = quarter3.cp(offset3);
IVec edgePt33 = quarter1.cp(offset1);
IVec edgePt34 = mid1.cp(offset1);
//degree 3 curves
new ICurve(new IVec[]{ edgePt11,edgePt12,
edgePt13,edgePt14 }, 3).clr(0);
new ICurve(new IVec[]{ edgePt21,edgePt22,
edgePt23,edgePt24 }, 3).clr(0);
new ICurve(new IVec[]{ edgePt31,edgePt32,
edgePt33,edgePt34 }, 3).clr(0);

if(IRand.percent(80)){ //bend
new LineAgent(pt2, nextDir1, nextAxis1);
}
if(IRand.percent(50)){ //bend the other way
new LineAgent(pt2, nextDir2, nextAxis2);
}
}
}
}

The surfaces are created on the offset curves. The code to create a surface out of 4 control points and two different ex is separated in a method of createEdgeSurface.

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

void setup(){
size(480, 360, IG.GL);
IRand.init(3);
IG.duration(30);
//second and third vector needs to be perpendicular
new LineAgent(new IVec(0,0,0), new IVec(1,0,0),
new IVec(0,0,1)).clr(0);
IG.fill();
}

static class LineAgent extends IAgent{
static double length = 2;
static double clearance = 1.99; //less than length
IVec pt1, pt2, axis;
boolean isColliding=false;

LineAgent(IVec pt, IVec dir, IVec ax){
pt1 = pt;
axis = ax;
}

void interact(IDynamics agent){
if(time == 0){ //only in the first time
if(agent instanceof LineAgent){
LineAgent lineAgent = (LineAgent)agent;
//checking clearance of end point
if(lineAgent.pt2.dist(pt2) < clearance){
isColliding=true;
}
}
}
}

void update(){
if(isColliding){ del(); }
else if(time == 0){ //if not colliding

new ICurve(pt1,pt2); // center line

IVec dir = pt2.dif(pt1);
// child dir & points
IVec nextDir1 = dir.dup().rot(axis, IRand.get(PI/4,PI/3));
IVec nextDir2 = dir.dup().rot(axis,-IRand.get(PI/4,PI/3));
IVec nextPt1 = pt2.cp(nextDir1);
IVec nextPt2 = pt2.cp(nextDir2);
//midpoints
IVec mid1 = pt1.mid(pt2);
IVec mid2 = pt2.mid(nextPt1);
IVec mid3 = pt2.mid(nextPt2);
//mid of midpoints
IVec quarter1 = pt2.mid(mid1);
IVec quarter2 = pt2.mid(mid2);
IVec quarter3 = pt2.mid(mid3);

//axis of child agents
IVec nextAxis1 =
axis.dup().rot(nextDir1, IRand.get(-PI/4,PI/4));
IVec nextAxis2 =
axis.dup().rot(nextDir2, IRand.get(-PI/4,PI/4));

double offsetWidth = -0.5;
IVec offset1 = dir.cross(axis).len(offsetWidth);
IVec offset2 = nextDir1.cross(nextAxis1).len(offsetWidth);
IVec offset3 = nextDir2.cross(nextAxis2).len(offsetWidth);

//offset edge points 1
IVec edgePt11 = mid1.cp(offset1);
IVec edgePt12 = quarter1.cp(offset1);
IVec edgePt13 = quarter2.cp(offset2);
IVec edgePt14 = mid2.cp(offset2);
//offset edge points 2
offset2.flip(); //offset to opposite
IVec edgePt21 = mid2.cp(offset2);
IVec edgePt22 = quarter2.cp(offset2);
IVec edgePt23 = quarter3.cp(offset3);
IVec edgePt24 = mid3.cp(offset3);
//offset edge points 3
offset1.flip(); //offset to opposite
offset3.flip(); //offset to opposite
IVec edgePt31 = mid3.cp(offset3);
IVec edgePt32 = quarter3.cp(offset3);
IVec edgePt33 = quarter1.cp(offset1);
IVec edgePt34 = mid1.cp(offset1);

double depth = 0.5;
IVec depthVec1 = axis.dup().len(depth);
IVec depthVec2 = nextAxis1.dup().len(depth);
IVec depthVec3 = nextAxis2.dup().len(depth);

createEdgeSurface(edgePt11,edgePt12,edgePt13,edgePt14,
depthVec1, depthVec2);
createEdgeSurface(edgePt21,edgePt22,edgePt23,edgePt24,
depthVec2, depthVec3);
createEdgeSurface(edgePt31,edgePt32,edgePt33,edgePt34,
depthVec3, depthVec1);

if(IRand.percent(80)){ //bend
new LineAgent(pt2, nextDir1, nextAxis1);
}
if(IRand.percent(50)){ //bend the other way
new LineAgent(pt2, nextDir2, nextAxis2);
}
}
}

ISurface createEdgeSurface(IVec pt1, IVec pt2,
IVec pt3, IVec pt4,
IVec extrudeDir1,
IVec extrudeDir2){
IVec[][] cpts = new IVec[4][2];
cpts[0][1] = pt1.dup().sub(extrudeDir1);
cpts[1][1] = pt2.dup().sub(extrudeDir1);
cpts[2][1] = pt3.dup().sub(extrudeDir2);
cpts[3][1] = pt4.dup().sub(extrudeDir2);
return new ISurface(cpts, 3, 1);
}
}

### Surfaces around Agents: 3

The code below shows example to create surfaces of a L-shaped section around the agent whose edges are above and below the Y-shaped center lines of the agent and the middle fold edge is outer offset of the center lines. The method createEdgeSurface is changed to take input arguments of 4 control points on two arm of the agent's Y-shaped center lines (pt1 - pt4), vectors to offset outward on each arm of Y-shape, (offsetDir1, offsetDir2), and vectors to shift above and below the center lines on each arm (extrudeDir1, extrudeDir2).

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

void setup(){
size(480, 360, IG.GL);
IRand.init(3);
IG.duration(30);
//second and third vector needs to be perpendicular
new LineAgent(new IVec(0,0,0), new IVec(1,0,0),
new IVec(0,0,1)).clr(0);
IG.fill();
}

static class LineAgent extends IAgent{
static double length = 2;
static double clearance = 1.99; //less than length
IVec pt1, pt2, axis;
boolean isColliding=false;

LineAgent(IVec pt, IVec dir, IVec ax){
pt1 = pt;
axis = ax;
}

void interact(IDynamics agent){
if(time == 0){ //only in the first time
if(agent instanceof LineAgent){
LineAgent lineAgent = (LineAgent)agent;
//checking clearance of end point
if(lineAgent.pt2.dist(pt2) < clearance){
isColliding=true;
}
}
}
}

void update(){
if(isColliding){ del(); }
else if(time == 0){ //if not colliding

new ICurve(pt1,pt2); // center line

IVec dir = pt2.dif(pt1);
// child dir & points
IVec nextDir1 = dir.dup().rot(axis, IRand.get(PI/4,PI/3));
IVec nextDir2 = dir.dup().rot(axis,-IRand.get(PI/4,PI/3));
IVec nextPt1 = pt2.cp(nextDir1);
IVec nextPt2 = pt2.cp(nextDir2);
//midpoints
IVec mid1 = pt1.mid(pt2);
IVec mid2 = pt2.mid(nextPt1);
IVec mid3 = pt2.mid(nextPt2);
//mid of midpoints
IVec quarter1 = pt2.mid(mid1);
IVec quarter2 = pt2.mid(mid2);
IVec quarter3 = pt2.mid(mid3);

//axis of child agents
IVec nextAxis1 =
axis.dup().rot(nextDir1, IRand.get(-PI/4,PI/4));
IVec nextAxis2 =
axis.dup().rot(nextDir2, IRand.get(-PI/4,PI/4));

double offsetWidth = -0.5;
IVec offset1 = dir.cross(axis).len(offsetWidth);
IVec offset2 = nextDir1.cross(nextAxis1).len(offsetWidth);
IVec offset3 = nextDir2.cross(nextAxis2).len(offsetWidth);

double depth = 0.5;
IVec depthVec1 = axis.dup().len(depth);
IVec depthVec2 = nextAxis1.dup().len(depth);
IVec depthVec3 = nextAxis2.dup().len(depth);

createEdgeSurface(mid1,quarter1,quarter2,mid2,
offset1, offset2,
depthVec1, depthVec2);
createEdgeSurface(mid2,quarter2,quarter3,mid3,
offset2.dup().flip(), offset3,
depthVec2, depthVec3);
createEdgeSurface(mid3,quarter3,quarter1,mid1,
offset3.dup().flip(), offset1.dup().flip(),
depthVec3, depthVec1);

if(IRand.percent(80)){ //bend
new LineAgent(pt2, nextDir1, nextAxis1);
}
if(IRand.percent(50)){ //bend the other way
new LineAgent(pt2, nextDir2, nextAxis2);
}
}
}

ISurface createEdgeSurface(IVec pt1, IVec pt2,
IVec pt3, IVec pt4,
IVec offsetDir1,
IVec offsetDir2,
IVec extrudeDir1,
IVec extrudeDir2){
IVec[][] cpts = new IVec[4][3];
cpts[0][2] = pt1.dup().sub(extrudeDir1);
cpts[1][2] = pt2.dup().sub(extrudeDir1);
cpts[2][2] = pt3.dup().sub(extrudeDir2);
cpts[3][2] = pt4.dup().sub(extrudeDir2);
return new ISurface(cpts, 3, 1);
}
}

### Surfaces around Agents: 4

The code below shows an example to enclose the surrounding surface of the agents. To create cap surfaces at the triangular opening at the center of the agent, another method createCapSurface is implemented. This code creates channel shaped surface by having v-degree 1 NURBS surfaces.

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

void setup(){
size(480, 360, IG.GL);
IRand.init(3);
IG.duration(30);
//second and third vector needs to be perpendicular
new LineAgent(new IVec(0,0,0), new IVec(1,0,0),
new IVec(0,0,1)).clr(0);
IG.fill();
}

static class LineAgent extends IAgent{
static double length = 2;
static double clearance = 1.99; //less than length
IVec pt1, pt2, axis;
boolean isColliding=false;

LineAgent(IVec pt, IVec dir, IVec ax){
pt1 = pt;
axis = ax;
}

void interact(IDynamics agent){
if(time == 0){ //only in the first time
if(agent instanceof LineAgent){
LineAgent lineAgent = (LineAgent)agent;
//checking clearance of end point
if(lineAgent.pt2.dist(pt2) < clearance){
isColliding=true;
}
}
}
}

void update(){
if(isColliding){ del(); }
else if(time == 0){ //if not colliding

new ICurve(pt1,pt2); // center line

IVec dir = pt2.dif(pt1);
// child dir & points
IVec nextDir1 = dir.dup().rot(axis, IRand.get(PI/4,PI/3));
IVec nextDir2 = dir.dup().rot(axis,-IRand.get(PI/4,PI/3));
IVec nextPt1 = pt2.cp(nextDir1);
IVec nextPt2 = pt2.cp(nextDir2);
//midpoints
IVec mid1 = pt1.mid(pt2);
IVec mid2 = pt2.mid(nextPt1);
IVec mid3 = pt2.mid(nextPt2);
//mid of midpoints
IVec quarter1 = pt2.mid(mid1);
IVec quarter2 = pt2.mid(mid2);
IVec quarter3 = pt2.mid(mid3);

//axis of child agents
IVec nextAxis1 =
axis.dup().rot(nextDir1, IRand.get(-PI/4,PI/4));
IVec nextAxis2 =
axis.dup().rot(nextDir2, IRand.get(-PI/4,PI/4));

double offsetWidth = -0.5;
IVec offset1 = dir.cross(axis).len(offsetWidth);
IVec offset2 = nextDir1.cross(nextAxis1).len(offsetWidth);
IVec offset3 = nextDir2.cross(nextAxis2).len(offsetWidth);

double depth = 0.5;
IVec depthVec1 = axis.dup().len(depth);
IVec depthVec2 = nextAxis1.dup().len(depth);
IVec depthVec3 = nextAxis2.dup().len(depth);

createEdgeSurface(mid1,quarter1,quarter2,mid2,
offset1, offset2,
depthVec1, depthVec2);
createEdgeSurface(mid2,quarter2,quarter3,mid3,
offset2.dup().flip(), offset3,
depthVec2, depthVec3);
createEdgeSurface(mid3,quarter3,quarter1,mid1,
offset3.dup().flip(), offset1.dup().flip(),
depthVec3, depthVec1);

createCapSurface(mid1,quarter1,mid2,quarter2,mid3,quarter3,
depthVec1,depthVec2,depthVec3);
createCapSurface(mid1,quarter1,mid3,quarter3,mid2,quarter2,
depthVec1.flip(),depthVec3.flip(),depthVec2.flip());

if(IRand.percent(80)){ //bend
new LineAgent(pt2, nextDir1, nextAxis1);
}
if(IRand.percent(50)){ //bend the other way
new LineAgent(pt2, nextDir2, nextAxis2);
}
}
}

ISurface createEdgeSurface(IVec pt1, IVec pt2,
IVec pt3, IVec pt4,
IVec offsetDir1,
IVec offsetDir2,
IVec extrudeDir1,
IVec extrudeDir2){
IVec[][] cpts = new IVec[4][4];
cpts[0][3] = pt1.dup().sub(extrudeDir1);
cpts[1][3] = pt2.dup().sub(extrudeDir1);
cpts[2][3] = pt3.dup().sub(extrudeDir2);
cpts[3][3] = pt4.dup().sub(extrudeDir2);
return new ISurface(cpts, 3, 1);
}

ISurface createCapSurface(IVec pt1, IVec pt2,
IVec pt3, IVec pt4,
IVec pt5, IVec pt6,
IVec shiftDir1,
IVec shiftDir2,
IVec shiftDir3){
IVec[][] cpts = new IVec[4][4];
return new ISurface(cpts, 3, 3);
}
}

The code below creates surfaces with curved section having v-degree 3 NURBS surfaces.

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

void setup(){
size(480, 360, IG.GL);
IRand.init(3);
IG.duration(30);
//second and third vector needs to be perpendicular
new LineAgent(new IVec(0,0,0), new IVec(1,0,0),
new IVec(0,0,1)).clr(0);
IG.fill();
}

static class LineAgent extends IAgent{
static double length = 2;
static double clearance = 1.99; //less than length
IVec pt1, pt2, axis;
boolean isColliding=false;

LineAgent(IVec pt, IVec dir, IVec ax){
pt1 = pt;
axis = ax;
}

void interact(IDynamics agent){
if(time == 0){ //only in the first time
if(agent instanceof LineAgent){
LineAgent lineAgent = (LineAgent)agent;
//checking clearance of end point
if(lineAgent.pt2.dist(pt2) < clearance){
isColliding=true;
}
}
}
}

void update(){
if(isColliding){ del(); }
else if(time == 0){ //if not colliding

new ICurve(pt1,pt2); // center line

IVec dir = pt2.dif(pt1);
// child dir & points
IVec nextDir1 = dir.dup().rot(axis, IRand.get(PI/4,PI/3));
IVec nextDir2 = dir.dup().rot(axis,-IRand.get(PI/4,PI/3));
IVec nextPt1 = pt2.cp(nextDir1);
IVec nextPt2 = pt2.cp(nextDir2);
//midpoints
IVec mid1 = pt1.mid(pt2);
IVec mid2 = pt2.mid(nextPt1);
IVec mid3 = pt2.mid(nextPt2);
//mid of midpoints
IVec quarter1 = pt2.mid(mid1);
IVec quarter2 = pt2.mid(mid2);
IVec quarter3 = pt2.mid(mid3);

//axis of child agents
IVec nextAxis1 =
axis.dup().rot(nextDir1, IRand.get(-PI/4,PI/4));
IVec nextAxis2 =
axis.dup().rot(nextDir2, IRand.get(-PI/4,PI/4));

double offsetWidth = -0.75;
IVec offset1 = dir.cross(axis).len(offsetWidth);
IVec offset2 = nextDir1.cross(nextAxis1).len(offsetWidth);
IVec offset3 = nextDir2.cross(nextAxis2).len(offsetWidth);

double depth = 0.75;
IVec depthVec1 = axis.dup().len(depth);
IVec depthVec2 = nextAxis1.dup().len(depth);
IVec depthVec3 = nextAxis2.dup().len(depth);

createEdgeSurface(mid1,quarter1,quarter2,mid2,
offset1, offset2,
depthVec1, depthVec2);
createEdgeSurface(mid2,quarter2,quarter3,mid3,
offset2.dup().flip(), offset3,
depthVec2, depthVec3);
createEdgeSurface(mid3,quarter3,quarter1,mid1,
offset3.dup().flip(), offset1.dup().flip(),
depthVec3, depthVec1);

createCapSurface(mid1,quarter1,mid2,quarter2,mid3,quarter3,
depthVec1,depthVec2,depthVec3);
createCapSurface(mid1,quarter1,mid3,quarter3,mid2,quarter2,
depthVec1.flip(),depthVec3.flip(),depthVec2.flip());

if(IRand.percent(80)){ //bend
new LineAgent(pt2, nextDir1, nextAxis1);
}
if(IRand.percent(50)){ //bend the other way
new LineAgent(pt2, nextDir2, nextAxis2);
}
}
}

ISurface createEdgeSurface(IVec pt1, IVec pt2,
IVec pt3, IVec pt4,
IVec offsetDir1,
IVec offsetDir2,
IVec extrudeDir1,
IVec extrudeDir2){
IVec[][] cpts = new IVec[4][4];
cpts[0][3] = pt1.dup().sub(extrudeDir1);
cpts[1][3] = pt2.dup().sub(extrudeDir1);
cpts[2][3] = pt3.dup().sub(extrudeDir2);
cpts[3][3] = pt4.dup().sub(extrudeDir2);
return new ISurface(cpts, 3, 3);
}

ISurface createCapSurface(IVec pt1, IVec pt2,
IVec pt3, IVec pt4,
IVec pt5, IVec pt6,
IVec shiftDir1,
IVec shiftDir2,
IVec shiftDir3){
IVec[][] cpts = new IVec[4][4];
return new ISurface(cpts, 3, 3);
}
}

### Surfaces around Agents: 5

This code shows a way to control which surfaces to be created and others not to. On top of the previous code, it limits the number of edge surfaces to two, instead of three covering Y-shaped center lines of the agent, keeping one side of three always open. It introduces new instance fields in boolean type createLeftSrf and createRightSrf to propagate the control of creation of surfaces in child agents in continuous way.

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

void setup(){
size(480, 360, IG.GL);
IRand.init(4);
IG.duration(30);
//second and third vector needs to be perpendicular
new LineAgent(new IVec(0,0,0), new IVec(1,0,0),
new IVec(0,0,1), true, true).clr(0.4,0,0);
IG.fill();
}

static class LineAgent extends IAgent{
static double length = 2;
static double clearance = 1.99; //less than length
IVec pt1, pt2, axis;
boolean createLeftSrf, createRightSrf;
boolean isColliding=false;

LineAgent(IVec pt, IVec dir, IVec ax,
boolean createLeft, boolean createRight){
pt1 = pt;
axis = ax;
createLeftSrf = createLeft;
createRightSrf = createRight;
}

void interact(IDynamics agent){
if(time == 0){ //only in the first time
if(agent instanceof LineAgent){
LineAgent lineAgent = (LineAgent)agent;
//checking clearance of end point
if(lineAgent.pt2.dist(pt2) < clearance){
isColliding=true;
}
}
}
}

void update(){
if(isColliding){ del(); }
else if(time == 0){ //if not colliding

new ICurve(pt1,pt2); // center line

IVec dir = pt2.dif(pt1);
// child dir & points
IVec nextDir1 = dir.dup().rot(axis, IRand.get(PI/4,PI/3));
IVec nextDir2 = dir.dup().rot(axis,-IRand.get(PI/4,PI/3));
IVec nextPt1 = pt2.cp(nextDir1);
IVec nextPt2 = pt2.cp(nextDir2);
//midpoints
IVec mid1 = pt1.mid(pt2);
IVec mid2 = pt2.mid(nextPt1);
IVec mid3 = pt2.mid(nextPt2);
//mid of midpoints
IVec quarter1 = pt2.mid(mid1);
IVec quarter2 = pt2.mid(mid2);
IVec quarter3 = pt2.mid(mid3);

//axis of child agents
IVec nextAxis1 =
axis.dup().rot(nextDir1, IRand.get(-PI/4,PI/4));
IVec nextAxis2 =
axis.dup().rot(nextDir2, IRand.get(-PI/4,PI/4));

double offsetWidth = -1.0;
IVec offset1 = dir.cross(axis).len(offsetWidth);
IVec offset2 = nextDir1.cross(nextAxis1).len(offsetWidth);
IVec offset3 = nextDir2.cross(nextAxis2).len(offsetWidth);

double depth = 1.0;
IVec depthVec1 = axis.dup().len(depth);
IVec depthVec2 = nextAxis1.dup().len(depth);
IVec depthVec3 = nextAxis2.dup().len(depth);

if(createLeftSrf){
createEdgeSurface(mid1,quarter1,quarter2,mid2,
offset1, offset2,
depthVec1, depthVec2).clr(clr());
}
if(!createLeftSrf||!createRightSrf){
createEdgeSurface(mid2,quarter2,quarter3,mid3,
offset2.dup().flip(), offset3,
depthVec2, depthVec3).clr(clr());
}
if(createRightSrf){
createEdgeSurface(mid3,quarter3,quarter1,mid1,
offset3.dup().flip(), offset1.dup().flip(),
depthVec3, depthVec1).clr(clr());
}

createCapSurface(mid1,quarter1,mid2,quarter2,mid3,quarter3,
depthVec1,depthVec2,depthVec3).clr(clr());
createCapSurface(mid1,quarter1,mid3,quarter3,mid2,quarter2,
depthVec1.flip(),depthVec3.flip(),depthVec2.flip()).clr(clr());

//child agents color
int r = clr().getRed() + IRand.getInt(-10,10);
int g = clr().getGreen();
int b = clr().getBlue() + IRand.getInt(-10,10);
if(IRand.percent(80)){ //bend
new LineAgent(pt2, nextDir1, nextAxis1,
createLeftSrf,
!createLeftSrf||!createRightSrf).clr(r,g,b);
}
if(IRand.percent(50)){ //bend the other way
new LineAgent(pt2, nextDir2, nextAxis2,
!createLeftSrf||!createRightSrf,
createRightSrf).clr(r,g,b);
}
}
}

ISurface createEdgeSurface(IVec pt1, IVec pt2,
IVec pt3, IVec pt4,
IVec offsetDir1,
IVec offsetDir2,
IVec extrudeDir1,
IVec extrudeDir2){
IVec[][] cpts = new IVec[4][4];
cpts[0][3] = pt1.dup().sub(extrudeDir1);
cpts[1][3] = pt2.dup().sub(extrudeDir1);
cpts[2][3] = pt3.dup().sub(extrudeDir2);
cpts[3][3] = pt4.dup().sub(extrudeDir2);
return new ISurface(cpts, 3, 3);
}

ISurface createCapSurface(IVec pt1, IVec pt2,
IVec pt3, IVec pt4,
IVec pt5, IVec pt6,
IVec shiftDir1,
IVec shiftDir2,
IVec shiftDir3){
IVec[][] cpts = new IVec[4][4];