home processing download documents tutorials gallery source(github) 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;

  MyLineAgent(IVec p, IVec d){
    pos = p;
    dir = d;
  }
  
  void interact(ArrayList < IDynamics > agents){
    //searching the closest attractor
    MyAttractor closestAttractor=null;
    double minDist=-1;
    for(int i=0; i < agents.size(); i++){
      if(agents.get(i) instanceof MyAttractor){
        MyAttractor attractor = (MyAttractor)agents.get(i);
        double dist = attractor.pos.dist(pos);
        //first attractor to check
        if(minDist < 0){
          closestAttractor = attractor;
          minDist = dist;
        }
        //if less than minimum, it's new minimum
        else if(dist < minDist){
          closestAttractor = attractor;
          minDist = dist;
        }
      }
    }
    //in case no attractor found, if-condition is used
    if(closestAttractor!=null){
      IVec diff = closestAttractor.pos.diff(pos);
      diff.len(dir.len());
      dir = diff;
    }
  }
  
  void update(){
    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 in array of data. You use for loop to check all the data and prepare value to be searched outside of the for-loop. The prepared values on this code are closestAgent and minDist.

Inside for-loop, you need to initialize those value when they are accessed for the first time. Because minDist is initialized with -1, and because any distance is positive number, you can check if it's the first time or not by "if(minDist < 0){". After the first check, the current distance (dist) is compared with the minimum (minDist) so far and if the current distance is smaller than minimum, minimum value is updated by the current value. And at the end, you get the minimum value.


    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( ArrayList < IDynamics > agents ){
    for(int i=0; i < agents.size(); i++){
      if(agents.get(i) instanceof MyBoundary){
        MyBoundary boundary = (MyBoundary)agents.get(i);
        //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(ArrayList < IDynamics > agents){
    super.interact(agents);
    
    for(int i=0; i < agents.size(); i++){
      if(agents.get(i) instanceof LineAgent){
        LineAgent lineAgent = (LineAgent)agents.get(i);
        if(lineAgent.pt2.dist(pos) < radius){
          lineAgent.del();
        }
      }
    }
  }

  void update(){ 
    super.update();
    if(time==0){ new ISphere(pos,radius).clr(0,1,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;
    pt2 = pt.dup().add(dir.dup().len(length));
  }

  void interact(ArrayList < IDynamics > agents){
    super.interact(agents);
    if(time == 0){ //only in the first time
      for(int i=0; i < agents.size() && !isColliding; i++){
        if(agents.get(i) instanceof LineAgent){
          LineAgent lineAgent = 
            (LineAgent)agents.get(i);
          if(lineAgent != this){ //agents include "this"
            // checking clearance of end point
            if(lineAgent.pt2.dist(pt2) < clearance){
              isColliding=true;
            }
          }
        }
      }
    }
  }

  void update(){
    super.update();

    if(isColliding){
      del();
    }
    else if(time == 0){ //if not colliding
      new ICurve(pt1,pt2).clr(0);
      IVec dir = pt2.diff(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 Tutorials

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