home processing download documents tutorial python tutorial gallery source about
 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.

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

void setup(){
  size(480,360,IG.GL);
  IG.duration(500);

  for(int i=0; i < 3; i++){
    new MyAttractor(IRand.pt(0, 0, 20, 100, 100, 20));
  }
  //agents in a matrix
  for(int i=0; i < 10; i++){
    for(int j=0; j < 10; j++){
      new MyLineAgent(new IVec(i*10, j*10, 0),
                      new IVec(0,0,0.5)).clr(.5,i*0.1,j*0.1);
    }
  }
}

static class MyAttractor extends IAgent{
  IVec pos;
  IPoint point;
  
  MyAttractor(IVec p){
    pos = p;
    point = new IPoint(pos).clr(1.0,0,0);
  }
  void update(){
    // random walk
    pos.add(IRandom.pt(-5,5));
  }
}

static class MyLineAgent extends IAgent{
  IVec pos, dir;
  MyAttractor attractor = null;
  double minDist = -1;

  MyLineAgent(IVec p, IVec d){
    pos = p;
    dir = d;
  }
  
  void interact(IDynamics agent){
    //searching the closest attractor
    if(agent instanceof MyAttractor){
      MyAttractor attr = (MyAttractor)agent;
      double dist = attr.pos.dist(pos);
      //first attractor to check
      if(attractor == null){
        attractor = attr;
        minDist = dist;
      }
      //if less than minimum, it's new minimum
      else if(dist < minDist){
        attractor = attr;
        minDist = dist;
      }
    }
  }
  
  void update(){
    //if any closest attractor found, attractor is not null
    if(attractor!=null){
      IVec dif = attractor.pos.dif(pos);
      dif.len(dir.len());
      dir = dif;
      //reset attractor and minDist
      attractor = null;
      minDist = -1;
    }

    new ICurve(pos.dup(), pos.dup().add(dir)).clr(clr());
    pos.add(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.

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

void setup(){
  size(480, 360, IG.GL);
  IG.duration(1000);

  new MyBoundary(-200,-200,200,200);
  new MyHexAgent(new IVec(0,0,0), new IVec(0,10,0));
}

class MyBoundary extends IAgent{
  double minx, maxx, miny, maxy;
  MyBoundary(double x1, double y1, double x2, double y2){
    minx = x1;
    miny = y1;
    maxx = x2;
    maxy = y2;
    IG.rect(new IVec(minx, miny, 0), maxx-minx, maxy-miny);
  }
}

class MyHexAgent extends IAgent{
  IVec pos, dir;
  double depth=0, depthInc=0.5, hue=0;

  MyHexAgent(IVec p, IVec d){ pos = p; dir = d; }
  
  void interact(IDynamics agent){
    if(agent instanceof MyBoundary){
      MyBoundary boundary = (MyBoundary)agent;
      //checking if next position is out of the boundary
      IVec nextPos = pos.cp(dir);
      if(nextPos.x < boundary.minx){
        dir.ref(IG.xaxis); //reflect on x-plane
      }
      else if(nextPos.x > boundary.maxx){
        dir.ref(IG.xaxis); //reflect on x-plane
      }
      if(nextPos.y < boundary.miny){
        dir.ref(IG.yaxis); //reflect on y-plane
      }
      else if(nextPos.y > boundary.maxy){
        dir.ref(IG.yaxis); //reflect on y-plane
      }
    }
  }
  
  void update(){
    createHexGeometry();
    pos.add(dir);
    //random shift of direction
    if(IRand.percent(10)){ dir.rot(IG.zaxis,PI/3); }
    else  if(IRand.percent(10)){ dir.rot(IG.zaxis,-PI/3); }
    //random shift of height
    if(IRandom.percent(10)){ depthInc *= -1; }
    depth += depthInc;
    hue += 0.002;
  }
  
  void createHexGeometry(){
    //creating hexagonal extrusion
    IVec[] cpts = new IVec[6];
    for(int i=0; i < 6; i++){
      cpts[i] =
        dir.dup().rot(IG.zaxis,PI/3*i+PI/6).div(2).add(pos);
    }
    IG.extrude(cpts, 1, true, depth).hsb(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.

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

void setup(){
  size(480, 360, IG.GL);
  IG.duration(150);
  new LineAgent(new IVec(0,0,0), new IVec(1,0,0));
  
  for(int i=-1; i<=1; i+=2){
    for(int j=-1; j<=1; j+=2){
      for(int k=-1; k<=1; k+=2){
        new LineBlockAgent(new IVec(40*i,40*j,40*k), 40);
      }
    }
  }
}

static class LineBlockAgent extends IAgent{
  IVec pos;
  double radius;
  
  LineBlockAgent(IVec p, double rad){
    pos = p; radius = rad;
  }
  
  void interact(IDynamics agent){
    if(agent instanceof LineAgent){
      LineAgent lineAgent = (LineAgent)agent;
      if(lineAgent.pt2.dist(pos) < radius){
        lineAgent.del();
      }
    }
  }

  void update(){ 
    if(time==0){ new ISphere(pos,radius).clr(0,1,1,0.2); }
  }  
}

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;
    pt2 = pt.dup().add(dir.dup().len(length));
  }

  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).clr(0);
      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(90)){ //straight 
        new LineAgent(pt2, dir.dup());
      }
    }
  }
}


(back to the list of tutorials)

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