Python Tutorials (back to the list of tutorials)

## Multi-Agent 2D Examples

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

The example code below defines an agent to draw constantly curving arc shape and occasionally create a new branch. The agent direction dir is bent left and/or right depending on the probability percentages pctL and pctR. Those percentages of the left and the right are stochastically swapped changing the major direction to bend. Because given initial percentages are a large percentage (99%)and a small percentage (1.5%), the agent draw an arc shape for a while and then change the bending direction or create a branch.

```add_library('igeo')

def setup() :
size(480, 360, IG.GL)
IG.duration(700)
# left 99.0%, right 1.5%
LineAgent(IG.v(0,0,0), IG.v(2,0,0), 99.0, 1.5).clr(0)

class LineAgent(IAgent) :
def __init__(self, pt, dir, pctL, pctR) :
self.pos = pt
self.dir = dir
self.pctL = pctL
self.pctR = pctR

def update(self) :
if self.time()==0 :
#putting line geometry
ICurve(self.pos, pos2).clr(self.clr())

r = self.red() + IRand.get(-0.05, 0.05)
g = self.green() + IRand.get(-0.05, 0.05)
b = self.blue() + IRand.get(-0.05, 0.05)

if IRand.pct(3.0) : #swap L/R percent
tmp = self.pctL
self.pctL = self.pctR
self.pctR = tmp

if IRand.pct(self.pctL) : #bend left
dir2 = self.dir.dup()
dir2.rot(PI/30)
LineAgent(pos2, dir2, self.pctL, self.pctR).clr(r,g,b)

if IRand.pct(self.pctR) : #bend right
dir2 = self.dir.dup()
dir2.rot(-PI/30)
LineAgent(pos2, dir2, self.pctL, self.pctR).clr(r,g,b)

self.del()

```

The next code adds interact() method to the previous code to detect collision of line agents. A line agent stops when it collides into other existing agents. Then the algorithm to swap left and right probability is also changed to swap them only when an agent creates a new branch.

```add_library('igeo')

def setup() :
size(480, 360, IG.GL)
IG.duration(350)
# left 99.0%, right 7.0%
LineAgent(IG.v(0,0,0), IG.v(2,0,0), 99.0, 7.0).clr(0)

class LineAgent(IAgent) :
def __init__(self, pt, dir, pctL, pctR) :
self.pos = pt
self.dir = dir
self.pctL = pctL
self.pctR = pctR
self.isColliding = False

def interact(self, agents) :
if self.time()==0 :
for agent in agents :
if self.isColliding :
return
if isinstance(agent, LineAgent) and agent is not self :
if agent.pos.dist(self.pos.cp(self.dir)) < self.dir.len()*0.999 :
self.isColliding=True

def update(self) :
if self.time()==0 :
if self.isColliding :
self.del()
return
#putting line geometry
ICurve(self.pos, pos2).clr(self.clr())

r = self.red() + IRand.get(-0.05, 0.05)
g = self.green() + IRand.get(-0.05, 0.05)
b = self.blue() + IRand.get(-0.05, 0.05)

branchL = IRand.pct(self.pctL) #boolean switch L
branchR = IRand.pct(self.pctR) #boolean switch R

if branchL : #bend left
dir2 = self.dir.dup()
dir2.rot(PI/30)
if branchR and self.pctR > self.pctL :#swap L/R% when branching both
LineAgent(pos2, dir2, self.pctR, self.pctL).clr(r,g,b)
else :
LineAgent(pos2, dir2, self.pctL, self.pctR).clr(r,g,b)

if branchR : #bend right
dir2 = self.dir.dup()
dir2.rot(-PI/30)
if branchL and self.pctR < self.pctL :#swap L/R% when branching both
LineAgent(pos2, dir2, self.pctR, self.pctL).clr(r,g,b)
else :
LineAgent(pos2, dir2, self.pctL, self.pctR).clr(r,g,b)
```

The code below changes the length of agent's line at every update. The lengths of all agents are slightly scaled down constantly, creating swirling curves. On top of it, the length is scaled up or down stochastically, only when the agent is creating a branch. In intersect() method, the collision detection algorithm is changed. It's using the method of
IVec.intersectLine(IVec line1Pt1, IVec line1Pt2, IVec line2Pt1, IVec line2Pt2) to calculate intersection of two line segments because the previous algorithm of collision detection wouldn't work when there are different lengths of lines.

```add_library('igeo')

def setup() :
size(480, 360, IG.GL)
IG.duration(400)
# left 100%, right 4.5%
LineAgent(IG.v(0,0,0), IG.v(2,0,0), 100, 4.5).clr(0)

class LineAgent(IAgent) :
def __init__(self, pt, dir, pctL, pctR) :
self.pos = pt
self.dir = dir
self.pctL = pctL
self.pctR = pctR
self.isColliding = False

def interact(self, agents) :
if self.time()==0 :
for agent in agents :
if self.isColliding :
return
if isinstance(agent, LineAgent) and agent is not self :
pos2 = self.pos.cp(self.dir)
apos2 = agent.pos.cp(agent.dir)
# not sharing root and intersecting
if not apos2.eq(self.pos) and not agent.pos.eq(self.pos) and \
IVec.intersectLine(agent.pos,apos2,self.pos,pos2) is not None :
self.isColliding=True

def update(self) :
if self.time()==0 :
if self.isColliding :
self.del()
return

ICurve(self.pos, pos2).clr(self.clr())

r = self.red() + IRand.get(-0.05, 0.05)
g = self.green() + IRand.get(-0.05, 0.05)
b = self.blue() + IRand.get(-0.05, 0.05)

branchL = IRand.pct(self.pctL)
branchR = IRand.pct(self.pctR)

lenL = self.dir.len()
lenR = self.dir.len()
lenL*=0.995 #shrinking length
lenR*=0.995 #shrinking length

if branchL and branchR :  #only when branching both
if IRand.pct(50) :
if self.pctL < self.pctR :
lenL *= 0.9
else :
lenR *= 0.9
elif IRand.pct(6.0) :
if self.pctL < self.pctR :
lenL *= 0.4
else :
lenR *= 0.4
elif IRand.pct(5.0) :
if self.pctL < self.pctR :
lenL *= 4.0
else :
lenR *= 4.0

if branchL : #bend left
dir2 = self.dir.dup()
dir2.len(lenL) #update length
dir2.rot(PI/30)

if branchR and self.pctR > self.pctL : #swap L/R% when branching both
LineAgent(pos2, dir2, self.pctR, self.pctL).clr(r,g,b)
else :
LineAgent(pos2, dir2, self.pctL, self.pctR).clr(r,g,b)

if branchR : #bend right
dir2 = self.dir.dup()
dir2.len(lenR) #update length
dir2.rot(-PI/30)

if branchL and self.pctR < self.pctL : #swap L/R% when branching both
LineAgent(pos2, dir2, self.pctR, self.pctL).clr(r,g,b)
else :
LineAgent(pos2, dir2, self.pctL, self.pctR).clr(r,g,b)

```

(back to the list of tutorials)