Python 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.

```add_library('igeo')

def setup() :
size(480, 360, IG.GL)
IG.duration(100)
LineAgent(IVec(0,0,0), IVec(1,0,0))

class LineAgent(IAgent) :
length = 2
clearance = 1.99 #less than length

def __init__(self, pt, dir) :
self.pt1 = pt
self.isColliding = False

def interact(self, agents) :
if self.time() == 0 : #only in the first time
for agent in agents :
if isinstance(agent, LineAgent) :
if agent is not self :
# checking clearance of end point
if agent.pt2.dist(self.pt2) < LineAgent.clearance :
self.isColliding=True

def update(self) :
if self.isColliding :
self.del()
elif self.time() == 0 : #if not colliding
IG.squarePipe(self.pt1,self.pt2,.2).clr(IRand.gray())
dir = self.pt2.dif(self.pt1)
#rotation axis with random direction
axis = IRand.pt(-1,1).len(1)
if IRand.pct(50) : #bend
LineAgent(self.pt2, dir.dup().rot(axis,IRand.get(PI/3,PI/3*2)))
if IRand.pct(50) : #bend the other way
LineAgent(self.pt2, dir.dup().rot(axis,-IRand.get(PI/3,PI/3*2)))
if IRand.pct(80) : #straight
LineAgent(self.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.

```add_library('igeo')

def setup() :
size(480, 360, IG.GL)
IG.duration(100)
LineAgent(IVec(0,0,0), IVec(1,0,0)).clr(0)

class LineAgent(IAgent) :
length = 2
clearance = 1.99 #less than length

def __init__(self, pt, dir) :
self.pt1 = pt
self.isColliding = False

def interact(self, agents) :
if self.time() == 0 : #only in the first time
for agent in agents :
if isinstance(agent, LineAgent) :
if agent is not self :
# checking clearance of end point
if agent.pt2.dist(self.pt2) < LineAgent.clearance :
self.isColliding=True

def update(self) :
if self.isColliding :
self.del()
elif self.time() == 0 : #if not colliding
dir = self.pt2.dif(self.pt1)
axis = IRand.pt(-1,1).len(1)
if IRand.pct(100) : #bend
nextDir1 = dir.dup().rot(axis,IRand.get(PI/3,PI/3*2))
#degree 2 curve at midpoint of pt1&pt2, pt2, and midpoint of pt2 and next agent's point
ICurve([ self.pt1.mid(self.pt2),self.pt2, \
self.pt2.mid(self.pt2.cp(nextDir1)) ], 2).clr(self.clr())
LineAgent(self.pt2, nextDir1).clr(self.clr())

if IRand.pct(50) : #bend the other way
#degree 2 curve at midpoint of pt1&pt2, pt2, and midpoint of pt2 and next agent's point
nextDir2 = dir.dup().rot(axis,-IRand.get(PI/3,PI/3*2))
ICurve([ self.pt1.mid(self.pt2),self.pt2, \
self.pt2.mid(self.pt2.cp(nextDir2)) ],2).clr(self.clr())
LineAgent(self.pt2, nextDir2).clr(self.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.

```add_library('igeo')

def setup() :
size(480, 360, IG.GL)
IRand.init(8)
IG.duration(120)
LineAgent(IVec(0,0,0), IVec(1,0,0)).clr(0)
IG.fill()

class LineAgent(IAgent) :
length = 2
clearance = 1.99 #less than length

def __init__(self, pt, dir) :
self.pt1 = pt
self.isColliding = False

def interact(self, agents) :
if self.time() == 0 : #only in the first time
for agent in agents :
if isinstance(agent, LineAgent) :
if agent is not self :
#checking clearance of end point
if agent.pt2.dist(self.pt2) < LineAgent.clearance :
self.isColliding = True

def update(self) :
if self.isColliding :
self.del()
elif self.time() == 0 : #if not colliding
dir = self.pt2.dif(self.pt1)
axis = IRand.pt(-1,1).len(1)
nextDir1 = dir.dup().rot(axis,PI/3)
nextDir2 = dir.dup().rot(axis,-PI/3)
#degree 2 surface with 3x3 control points
cpts = []
cpts.append([])
cpts.append([])
cpts.append([])
cpts[0].append(self.pt1.mid(self.pt2))
cpts[0].append(self.pt1.mid(self.pt2))
cpts[0].append(self.pt1.mid(self.pt2))
cpts[1].append(self.pt2)
cpts[1].append(self.pt2)
cpts[1].append(self.pt2)
cpts[2].append(self.pt2.mid(self.pt2.cp(nextDir1)))
cpts[2].append(self.pt2)
cpts[2].append(self.pt2.mid(self.pt2.cp(nextDir2)))
ISurface(cpts, 2, 2).clr(self.clr())
r = self.clr().getRed() + IRand.getInt(-10, 10)
g = self.clr().getGreen() + IRand.getInt(-10, 10)
b = self.clr().getBlue() + IRand.getInt(-10, 10)
if IRand.pct(80) : #bend
LineAgent(self.pt2, nextDir1).clr(r,g,b)
if IRand.pct(50) : #bend the other way
LineAgent(self.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.

```add_library('igeo')

def setup() :
size(480, 360, IG.GL)
IRand.init(3)
IG.duration(30)
LineAgent(IVec(0,0,0), IVec(1,0,0)).clr(0)
IG.fill()

class LineAgent(IAgent) :
length = 2
clearance = 1.99 #less than length

def __init__(self, pt, dir) :
self.pt1 = pt
self.isColliding = False

def interact(self, agents) :
if self.time() == 0 : #only in the first time
for agent in agents :
if isinstance(agent, LineAgent) :
if agent is not self :
#checking clearance of end point
if agent.pt2.dist(self.pt2) < LineAgent.clearance :
self.isColliding = True

def update(self) :
if self.isColliding :
self.del()
elif self.time() == 0 : #if not colliding
ICurve(self.pt1,self.pt2) # center line

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

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

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

if IRand.pct(80) : #bend
LineAgent(self.pt2, nextDir1)
if IRand.pct(50) : #bend the other way
LineAgent(self.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.

```add_library('igeo')

def setup() :
size(480, 360, IG.GL)
IRand.init(3)
IG.duration(30)
LineAgent(IVec(0,0,0), IVec(1,0,0)).clr(0)
IG.fill()

class LineAgent(IAgent) :
length = 2
clearance = 1.99 #less than length

def __init__(self, pt, dir) :
self.pt1 = pt
self.isColliding = False

def interact(self, agents) :
if self.time() == 0 : #only in the first time
for agent in agents :
if isinstance(agent, LineAgent) :
if agent is not self :
#checking clearance of end point
if agent.pt2.dist(self.pt2) < LineAgent.clearance :
self.isColliding = True

def update(self) :
if self.isColliding :
self.del()
elif self.time() == 0 : #if not colliding
ICurve(self.pt1,self.pt2) # center line

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

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

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

depth = 0.5
depthVec = axis.dup().len(depth)

cpts1 = []
cpts1.append([])
cpts1.append([])
cpts1.append([])
cpts1.append([])
cpts1[0].append(edgePt11.dup().sub(depthVec))
cpts1[1].append(edgePt12.dup().sub(depthVec))
cpts1[2].append(edgePt13.dup().sub(depthVec))
cpts1[3].append(edgePt14.dup().sub(depthVec))

cpts2 = []
cpts2.append([])
cpts2.append([])
cpts2.append([])
cpts2.append([])
cpts2[0].append(edgePt21.dup().sub(depthVec))
cpts2[1].append(edgePt22.dup().sub(depthVec))
cpts2[2].append(edgePt23.dup().sub(depthVec))
cpts2[3].append(edgePt24.dup().sub(depthVec))

cpts3 = []
cpts3.append([])
cpts3.append([])
cpts3.append([])
cpts3.append([])
cpts3[0].append(edgePt31.dup().sub(depthVec))
cpts3[1].append(edgePt32.dup().sub(depthVec))
cpts3[2].append(edgePt33.dup().sub(depthVec))
cpts3[3].append(edgePt34.dup().sub(depthVec))

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

if IRand.pct(80) : #bend
LineAgent(self.pt2, nextDir1)
if IRand.pct(50) : #bend the other way
LineAgent(self.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.

```add_library('igeo')

def setup() :
size(480, 360, IG.GL)
IRand.init(3)
IG.duration(30)
#second and third vector needs to be perpendicular
LineAgent(IVec(0,0,0),IVec(1,0,0),IVec(0,0,1)).clr(0)
IG.fill()

class LineAgent(IAgent) :
length = 2
clearance = 1.99 #less than length

def __init__(self, pt, dir, ax) :
self.pt1 = pt
self.axis = ax
self.isColliding = False

def interact(self, agents) :
if self.time() == 0 : #only in the first time
for agent in agents :
if isinstance(agent, LineAgent) :
if agent is not self :
#checking clearance of end point
if agent.pt2.dist(self.pt2) < LineAgent.clearance :
self.isColliding = True

def update(self) :
if self.isColliding :
self.del()
elif self.time() == 0 : #if not colliding
ICurve(self.pt1,self.pt2) # center line

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

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

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

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

if IRand.pct(80) : #bend
LineAgent(self.pt2, nextDir1, nextAxis1)
if IRand.pct(50) : #bend the other way
LineAgent(self.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.

```add_library('igeo')

def setup() :
size(480, 360, IG.GL)
IRand.init(3)
IG.duration(30)
#second and third vector needs to be perpendicular
LineAgent(IVec(0,0,0),IVec(1,0,0),IVec(0,0,1)).clr(0)
IG.fill()

class LineAgent(IAgent) :
length = 2
clearance = 1.99 #less than length

def __init__(self, pt, dir, ax) :
self.pt1 = pt
self.axis = ax
self.isColliding = False

def interact(self, agents) :
if self.time() == 0 : #only in the first time
for agent in agents :
if isinstance(agent, LineAgent) :
if agent is not self :
#checking clearance of end point
if agent.pt2.dist(self.pt2) < LineAgent.clearance :
self.isColliding = True

def update(self) :
if self.isColliding :
self.del()
elif self.time() == 0 : #if not colliding
ICurve(self.pt1,self.pt2) # center line

dir = self.pt2.dif(self.pt1)

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

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

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

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

depth = 0.5
depthVec1 = self.axis.dup().len(depth)
depthVec2 = nextAxis1.dup().len(depth)
depthVec3 = nextAxis2.dup().len(depth)

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

if IRand.pct(80) : #bend
LineAgent(self.pt2, nextDir1, nextAxis1)
if IRand.pct(50) : #bend the other way
LineAgent(self.pt2, nextDir2, nextAxis2)

def createEdgeSurface(self,pt1,pt2,pt3,pt4,extrudeDir1,extrudeDir2) :
cpts = []
cpts.append([])
cpts.append([])
cpts.append([])
cpts.append([])
cpts[0].append(pt1.dup().sub(extrudeDir1))
cpts[1].append(pt2.dup().sub(extrudeDir1))
cpts[2].append(pt3.dup().sub(extrudeDir2))
cpts[3].append(pt4.dup().sub(extrudeDir2))
return 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).

```add_library('igeo')

def setup() :
size(480, 360, IG.GL)
IRand.init(3)
IG.duration(30)
#second and third vector needs to be perpendicular
LineAgent(IVec(0,0,0),IVec(1,0,0),IVec(0,0,1)).clr(0)
IG.fill()

class LineAgent(IAgent) :
length = 2
clearance = 1.99 #less than length

def __init__(self, pt, dir, ax) :
self.pt1 = pt
self.axis = ax
self.isColliding = False

def interact(self, agents) :
if self.time() == 0 : #only in the first time
for agent in agents :
if isinstance(agent, LineAgent) :
if agent is not self :
#checking clearance of end point
if agent.pt2.dist(self.pt2) < LineAgent.clearance :
self.isColliding = True

def update(self) :
if self.isColliding :
self.del()
elif self.time() == 0 : #if not colliding
ICurve(self.pt1,self.pt2) # center line

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

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

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

depth = 0.5
depthVec1 = self.axis.dup().len(depth)
depthVec2 = nextAxis1.dup().len(depth)
depthVec3 = nextAxis2.dup().len(depth)

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

if IRand.percent(80) : #bend
LineAgent(self.pt2, nextDir1, nextAxis1)
if IRand.percent(50) :  #bend the other way
LineAgent(self.pt2, nextDir2, nextAxis2)

def createEdgeSurface(self,pt1,pt2,pt3,pt4,offsetDir1,\
offsetDir2,extrudeDir1,extrudeDir2) :
cpts = []
cpts.append([])
cpts.append([])
cpts.append([])
cpts.append([])
cpts[0].append(pt1.dup().sub(extrudeDir1))
cpts[1].append(pt2.dup().sub(extrudeDir1))
cpts[2].append(pt3.dup().sub(extrudeDir2))
cpts[3].append(pt4.dup().sub(extrudeDir2))
return 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.

```add_library('igeo')

def setup() :
size(480, 360, IG.GL)
IRand.init(3)
IG.duration(30)
#second and third vector needs to be perpendicular
LineAgent(IVec(0,0,0),IVec(1,0,0),IVec(0,0,1)).clr(0)
IG.fill()

class LineAgent(IAgent) :
length = 2
clearance = 1.99 #less than length

def __init__(self, pt, dir, ax) :
self.pt1 = pt
self.axis = ax
self.isColliding = False

def interact(self, agents) :
if self.time() == 0 : #only in the first time
for agent in agents :
if isinstance(agent, LineAgent) :
if agent is not self :
#checking clearance of end point
if agent.pt2.dist(self.pt2) < LineAgent.clearance :
self.isColliding = True

def update(self) :
if self.isColliding :
self.del()
elif self.time() == 0 : #if not colliding
ICurve(self.pt1,self.pt2) # center line

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

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

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

depth = 0.5
depthVec1 = self.axis.dup().len(depth)
depthVec2 = nextAxis1.dup().len(depth)
depthVec3 = nextAxis2.dup().len(depth)

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

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

if IRand.pct(80) : #bend
LineAgent(self.pt2, nextDir1, nextAxis1)
if IRand.pct(50) : #bend the other way
LineAgent(self.pt2, nextDir2, nextAxis2)

def createEdgeSurface(self,pt1,pt2,pt3,pt4,offsetDir1,\
offsetDir2,extrudeDir1,extrudeDir2) :
cpts = []
cpts.append([])
cpts.append([])
cpts.append([])
cpts.append([])
cpts[0].append(pt1.dup().sub(extrudeDir1))
cpts[1].append(pt2.dup().sub(extrudeDir1))
cpts[2].append(pt3.dup().sub(extrudeDir2))
cpts[3].append(pt4.dup().sub(extrudeDir2))
return ISurface(cpts, 3, 1)

def createCapSurface(self,pt1,pt2,pt3,pt4,pt5,pt6,\
shiftDir1,shiftDir2,shiftDir3) :
cpts = []
cpts.append([])
cpts.append([])
cpts.append([])
cpts.append([])
return ISurface(cpts, 3, 3)
```

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

```add_library('igeo')

def setup() :
size(480, 360, IG.GL)
IRand.init(3)
IG.duration(30)
#second and third vector needs to be perpendicular
LineAgent(IVec(0,0,0),IVec(1,0,0),IVec(0,0,1)).clr(0)
IG.fill()

class LineAgent(IAgent) :
length = 2
clearance = 1.99 #less than length

def __init__(self, pt, dir, ax) :
self.pt1 = pt
self.axis = ax
self.isColliding = False

def interact(self, agents) :
if self.time() == 0 : #only in the first time
for agent in agents :
if isinstance(agent, LineAgent) :
if agent is not self :
#checking clearance of end point
if agent.pt2.dist(self.pt2) < LineAgent.clearance :
self.isColliding = True

def update(self) :
if self.isColliding :
self.del()
elif self.time() == 0 : #if not colliding
ICurve(self.pt1,self.pt2) # center line

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

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

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

depth = 0.75
depthVec1 = self.axis.dup().len(depth)
depthVec2 = nextAxis1.dup().len(depth)
depthVec3 = nextAxis2.dup().len(depth)

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

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

if IRand.pct(80) : #bend
LineAgent(self.pt2, nextDir1, nextAxis1)
if IRand.pct(50) : #bend the other way
LineAgent(self.pt2, nextDir2, nextAxis2)

def createEdgeSurface(self,pt1,pt2,pt3,pt4,offsetDir1,
offsetDir2,extrudeDir1, extrudeDir2) :
cpts = []
cpts.append([])
cpts.append([])
cpts.append([])
cpts.append([])
cpts[0].append(pt1.dup().sub(extrudeDir1))
cpts[1].append(pt2.dup().sub(extrudeDir1))
cpts[2].append(pt3.dup().sub(extrudeDir2))
cpts[3].append(pt4.dup().sub(extrudeDir2))
return ISurface(cpts, 3, 3)

def createCapSurface(self,pt1,pt2,pt3,pt4,pt5,pt6,\
shiftDir1,shiftDir2,shiftDir3) :
cpts = []
cpts.append([])
cpts.append([])
cpts.append([])
cpts.append([])
return 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.

```add_library('igeo')

def setup() :
size(480, 360, IG.GL)
IRand.init(4)
IG.duration(30)
#second and third vector needs to be perpendicular
LineAgent(IVec(0,0,0),IVec(1,0,0),IVec(0,0,1),True,True).clr(0.4,0,0)
IG.fill()

class LineAgent(IAgent) :
length = 2
clearance = 1.99 #less than length

def __init__(self, pt, dir, ax, createLeft, createRight) :
self.pt1 = pt
self.axis = ax
self.isColliding = False
self.createLeftSrf = createLeft
self.createRightSrf = createRight

def interact(self, agents) :
if self.time() == 0 : #only in the first time
for agent in agents :
if isinstance(agent, LineAgent) :
if agent is not self :
#checking clearance of end point
if agent.pt2.dist(self.pt2) < LineAgent.clearance :
self.isColliding = True

def update(self) :
if self.isColliding :
self.del()
elif self.time() == 0 : #if not colliding
ICurve(self.pt1,self.pt2) # center line

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

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

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

depth = 1.0
depthVec1 = self.axis.dup().len(depth)
depthVec2 = nextAxis1.dup().len(depth)
depthVec3 = nextAxis2.dup().len(depth)

if self.createLeftSrf :
self.createEdgeSurface(mid1,quarter1,quarter2,mid2,\
offset1,offset2,depthVec1,depthVec2).clr(self.clr())
if not self.createLeftSrf or not self.createRightSrf :
self.createEdgeSurface(mid2,quarter2,quarter3,mid3,\
offset2.dup().flip(),offset3,depthVec2, depthVec3).clr(self.clr())
if self.createRightSrf :
self.createEdgeSurface(mid3,quarter3,quarter1,mid1,\
offset3.dup().flip(), offset1.dup().flip(),depthVec3, depthVec1).clr(self.clr())

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

#child agents color
r = self.clr().getRed() + IRand.getInt(-10,10)
g = self.clr().getGreen()
b = self.clr().getBlue() + IRand.getInt(-10,10)
if IRand.pct(80) : #bend
LineAgent(self.pt2, nextDir1, nextAxis1,
self.createLeftSrf,
not self.createLeftSrf or not self.createRightSrf).clr(r,g,b)
if IRand.pct(50) : #bend the other way
LineAgent(self.pt2, nextDir2, nextAxis2,
not self.createLeftSrf or not self.createRightSrf,
self.createRightSrf).clr(r,g,b)

def createEdgeSurface(self,pt1,pt2,pt3,pt4,\
offsetDir1,offsetDir2,extrudeDir1,extrudeDir2) :
cpts = []
cpts.append([])
cpts.append([])
cpts.append([])
cpts.append([])
cpts[0].append(pt1.dup().sub(extrudeDir1))
cpts[1].append(pt2.dup().sub(extrudeDir1))
cpts[2].append(pt3.dup().sub(extrudeDir2))
cpts[3].append(pt4.dup().sub(extrudeDir2))
return ISurface(cpts, 3, 3)

def createCapSurface(self,pt1,pt2,pt3,pt4,pt5,pt6,\
shiftDir1,shiftDir2,shiftDir3) :
cpts = []
cpts.append([])
cpts.append([])
cpts.append([])
cpts.append([])