home processing download documents tutorial python tutorial gallery source about
 Python Tutorials (back to the list of tutorials)

Multi-Agent Algorithm: Inter-Class Interaction (requires iGeo version 7.4.0 or higher)

     Agents and Attractors

In this section, techniques to use interaction of different classes is shown. You can define a specific role to each of different classes to have composite behavior in multi-agent system.

To control a large number of agents, an attractor is often introduced. You define attractors to be located somewhere in the space and agents are oriented towards the location of the attractor.

add_library('igeo')

def setup() : 
    size(480,360,IG.GL)
    IG.duration(500)
    
    for i in range(3) : 
        MyAttractor(IRand.pt(0, 0, 20, 100, 100, 20))
    
    #agents in a matrix
    for i in range(10) : 
        for j in range(10) :
            MyLineAgent(IVec(i*10,j*10,0),IVec(0,0,0.5)).clr(.5,i*0.1,j*0.1)


class MyAttractor(IAgent) :
    
    def __init__(self, p) : 
        self.pos = p
        self.point = IPoint(self.pos).clr(1.0,0,0)

    def update(self) : 
        # random walk
        self.pos.add(IRand.pt(-5,5))

class MyLineAgent(IAgent) :
    
    def __init__(self, p, d) : 
        self.pos = p
        self.dir = d
        self.attractor = None
    
    def interact(self, agents) : 
        #searching the closest attractor
        minDist = -1 
        for agent in agents : 
            if isinstance(agent, MyAttractor) : 
                if agent is not self :
                    dist = agent.pos.dist(self.pos)
                    #first attractor to check
                    if self.attractor is None :
                        self.attractor = agent
                        minDist = dist
                    #if less than minimum, it's minimum
                    elif dist < minDist : 
                        self.attractor = agent
                        minDist = dist
  
    def update(self) : 
        #if any closest attractor found, attractor is not null
        if self.attractor is not None : 
            dif = self.attractor.pos.dif(self.pos)
            dif.len(self.dir.len())
            self.dir = dif
            #reset attractor and minDist
            attractor = None
        
        ICurve(self.pos.dup(), self.pos.dup().add(self.dir)).clr(self.clr())
        self.pos.add(self.dir)

One important part of the code is the code to find the closest attractor out of multiple attractors in the space. This is a typical algorithm to search data which have a minimum or maximum value. Each time interact(IDynamics agent) is called, it checks if the new agent is closer than the minimum distance so far in minDist except for the first time to check when attractor is null.


     Simple Boundary of Agents

This example shows a way to define rectangular boundary on XY plane. MyBoundary class is containing the definition of the rectangular boundary. The boundary is defined by 4 values of xmin, ymin, xmax and ymax. The agents of MyHexAgent check the boundary inside the interact method if if it's about to go out of the boundary, it reflects the direction. Another note to add is that the class of MyHexAgent has the geometry creation method separated from the update method to have the code more organized.

add_library('igeo')

def setup() : 
    size(480, 360, IG.GL)
    IG.duration(1000)
    MyBoundary(-200,-200,200,200)
    MyHexAgent(IVec(0,0,0), IVec(0,10,0))

class MyBoundary(IAgent) : 
    
    def __init__(self, x1, y1, x2, y2) : 
        self.minx = x1
        self.miny = y1
        self.maxx = x2
        self.maxy = y2
        IG.rect(IVec(x1,y1,0), x2-x1, y2-y1)


class MyHexAgent(IAgent) : 
    
    def __init__(self, p, d) : 
        self.pos = p
        self.dir = d
        self.depth = 0
        self.depthInc = 0.5
        self.hue = 0
    
    def interact(self, agents) : 
        for agent in agents : 
            if isinstance(agent, MyBoundary) : 
                #checking if next position is out of the boundary
                nextPos = self.pos.cp(self.dir)
                if nextPos.x() < agent.minx : 
                    self.dir.ref(IG.xaxis) #reflect on x-plane
                elif nextPos.x() > agent.maxx : 
                    self.dir.ref(IG.xaxis) #reflect on x-plane
                if nextPos.y() < agent.miny : 
                    self.dir.ref(IG.yaxis) #reflect on y-plane
                elif nextPos.y() > agent.maxy : 
                    self.dir.ref(IG.yaxis) #reflect on y-plane
  
    def update(self) : 
        self.createHexGeometry()
        self.pos.add(self.dir)
        #random shift of direction
        if IRand.pct(10) :  
            self.dir.rot(PI/3)
        elif IRand.pct(10) :
            self.dir.rot(-PI/3)
        #random shift of height
        if IRand.pct(10) : 
            self.depthInc *= -1
        self.depth += self.depthInc
        self.hue += 0.002
    
    def createHexGeometry(self) : 
        #creating hexagonal extrusion
        cpts = []
        for i in range(6) : 
            cpts.append(self.dir.dup().rot(PI/3*i+PI/6).div(2).add(self.pos))
        IG.extrude(cpts, 1, True, self.depth).hsb(self.hue,1,1)


     Blocking Agents

The code below shows an example to have another class agents to block the line agents shown before, by adding one blocking class "LineBlockAgent" to the code of branching line agents shown at this section.

add_library('igeo')

def setup() : 
    size(480, 360, IG.GL)
    IG.duration(150)
    LineAgent(IVec(0,0,0), IVec(1,0,0))
    
    for i in range(-1,2,2) : # -1, 1
        for j in range(-1,2,2) : 
            for k in range(-1,2,2) : 
                LineBlockAgent(IVec(40*i,40*j,40*k), 40)

class LineBlockAgent(IAgent) : 
    
    def __init__(self, p, rad) : 
        self.pos = p
        self.radius = rad
  
    def interact(self, agents) : 
        for agent in agents : 
            if isinstance(agent, LineAgent) :
                if agent.pt2.dist(self.pos) < self.radius :
                    agent.del()
    
    def update(self) :
        if self.time()==0 : 
            ISphere(self.pos,self.radius).clr(0,1,1,0.2)

class LineAgent(IAgent) : 
    length = 2
    clearance = 1.99 #less than length
    
    def __init__(self, pt, dir) : 
        self.pt1 = pt
        self.pt2 = pt.dup().add(dir.dup().len(LineAgent.length))
        self.isColliding = False
    def interact(self, agents) :
        for agent in agents : 
            if self.time() == 0 : #only in the first time
                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).clr(0)
            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(90) : #straight 
                LineAgent(self.pt2, dir.dup())


(back to the list of tutorials)

HOME
FOR PROCESSING
DOWNLOAD
DOCUMENTS
TUTORIALS (Java / Python)
GALLERY
SOURCE CODE(GitHub)
ABOUT