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

Multi-Agent 2D Examples

     Multi-Agent 2D Example 4

The following example shows a line agent which turns when it finds another line agent in front of it. It turns into the parallel direction of the another agent and it turns only left, not right. In its interact method, the agent searches the closest other agent in front within the threshold range and measures the angle of the other agent. This angle is stored in frontAngle and this is used to rotate the agent in the update method.

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

void setup(){
  size(480,360,IG.GL);
  IRand.init(3);
  IG.duration(700);
  new LineAgent(IG.v(-0.1,0,0), IG.v(1,0,0)).clr(0);
  new LineAgent(IG.v(500,-0.1,0), IG.v(0,1,0)).clr(0);
  new LineAgent(IG.v(500+0.1,500,0), IG.v(-1,0,0)).clr(0);
  new LineAgent(IG.v(0,500+0.1,0), IG.v(0,-1,0)).clr(0);

  for(int i=0; i < 10; i++){
    new LineAgent(IRand.pt(100,100,0,400,400,0), IRand.pt(-10,-10,0,10,10,0)).clr(IRand.clr(0.4,0.4,0.4));
  }
}

static class LineAgent extends IAgent{
  // constants
  static final double length = 2;
  static final double clearance = 1.99; //less than length
  static final double angleThreshold = PI/20;
  static final double threshold = 10;

  IVec pt1, pt2;
  boolean isColliding=false;
  double angle=0;
  double minDist=0;
  double frontAngle=0;
  
  LineAgent(IVec pt, IVec dir){
    pt1 = pt;
    pt2 = pt.cp(dir.cp().len(length));
  }

  void interact(ArrayList < IDynamics > agents){
    if(time == 0){ //only in the first time
      minDist = -1; //reset
      for(int i=0; i < agents.size() && !isColliding; i++){
        if(agents.get(i) instanceof LineAgent){
          LineAgent agent = (LineAgent)agents.get(i);
          if(agent != this){ //agents include "this"
            // checking clearance of end point
            double dist = agent.pt2.dist(pt2);
            if(dist < clearance){ isColliding=true; }
            else if(dist < threshold){ //not colliding but close
              if(!pt1.eq(agent.pt2) && //not the parent
                  pt2.dif(pt1).angle(agent.pt2.dif(pt1)) < angleThreshold && //in front
                 (minDist < 0 || dist < minDist) ){ //closest one
                minDist = dist;
                frontAngle = pt2.dif(pt1).angle(agent.pt2.dif(agent.pt1),IG.zaxis);
              }
            }
          }
        }
      }
    }
  }
  
  void update(){
    if(time == 0){
      if(isColliding){ del(); }
      else{ //if not colliding
        new ICurve(pt1,pt2).clr(clr());
        IVec dir = pt2.dif(pt1);
        if(IRand.pct(1)){ //branching
          double r = red()+IRand.get(-0.1,0.1);
          double g = green()+IRand.get(-0.1,0.1);
          double b = blue()+IRand.get(-0.1,0.1);
          new LineAgent(pt2, dir.cp().rot(IRand.get(PI/3,2*PI/3))).clr(r,g,b);
        }
        if(frontAngle < 0){ frontAngle+=PI; } //going in only one direction
        double r = red()+IRand.get(-0.03,0.03);
        double g = green()+IRand.get(-0.03,0.03);
        double b = blue()+IRand.get(-0.03,0.03);
        new LineAgent(pt2, dir.rot(frontAngle)).clr(r,g,b);
      }
    }
  }
}

The next code change the previous code to round the turning corner by bending the lines with a few segments. To enable bending through multiple generation of agents, the instance field of bendAngle and bendCount are added to LineAgent class.

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

void setup(){
  size(480,360,IG.GL);
  IRand.init(2);
  IG.duration(700);
  new LineAgent(IG.v(-0.1,0,0),IG.v(1,0,0),0,0).clr(0);
  new LineAgent(IG.v(500,-0.1,0),IG.v(0,1,0),0,0).clr(0);
  new LineAgent(IG.v(500+0.1,500,0),IG.v(-1,0,0),0,0).clr(0);
  new LineAgent(IG.v(0,500+0.1,0),IG.v(0,-1,0),0,0).clr(0);

  new LineAgent(IG.v(500/2,0,0),IG.v(1,0,0),0,0).clr(0);
  new LineAgent(IG.v(500,500/2,0),IG.v(0,1,0),0,0).clr(0);
  new LineAgent(IG.v(500/2,500,0),IG.v(-1,0,0),0,0).clr(0);
  new LineAgent(IG.v(0,500/2,0),IG.v(0,-1,0),0,0).clr(0);
  
  for(int i=0; i < 30; i++){
    new LineAgent(IRand.pt(100,100,0,400,400,0),IRand.pt(-10,-10,0,10,10,0),0,0).clr(IRand.clr(0.4,0.4,0.4));
  }
}

static class LineAgent extends IAgent{
  // constants
  static final double length = 2;
  static final double clearance = 1.99; //less than length
  static final double angleThreshold = PI/20;
  static final double threshold = 10;
  static final int bendSize = 2;

  IVec pt1, pt2;
  boolean isColliding=false;
  double angle=0;
  double minDist=0;
  double frontAngle=0;
  double bendAngle=0;
  int bendCount = 0;
  
  LineAgent(IVec pt, IVec dir, double bend, int bcount){
    pt1 = pt;
    pt2 = pt.cp(dir.cp().len(length));
    bendAngle = bend;
    bendCount = bcount;
  }

  void interact(ArrayList < IDynamics > agents){
    if(time == 0){ //only in the first time
      minDist = -1; //reset
      for(int i=0; i < agents.size() && !isColliding; i++){
        if(agents.get(i) instanceof LineAgent){
          LineAgent agent = (LineAgent)agents.get(i);
          if(agent != this){ //agents include "this"
            // checking clearance of end point
            double dist = agent.pt2.dist(pt2);
            if(dist < clearance){ isColliding=true; }
            else if(dist < threshold){ //not colliding but close
              if(!pt1.eq(agent.pt2) && //not the parent
                  pt2.dif(pt1).angle(agent.pt2.dif(pt1)) < angleThreshold && //in front
                 (minDist < 0 || dist < minDist) ){ //closest one
                minDist = dist;
                frontAngle = pt2.dif(pt1).angle(agent.pt2.dif(agent.pt1),IG.zaxis); 
              }
            }
          }
        }
      }
    }
  }
  
  void update(){
    if(time == 0){
      if(isColliding){ del(); }
      else{ //if not colliding
        new ICurve(pt1,pt2).clr(clr());
        IVec dir = pt2.dif(pt1);
        if(IRand.pct(1)){ //branching
          double r = red()+IRand.get(-0.1,0.1);
          double g = green()+IRand.get(-0.1,0.1);
          double b = blue()+IRand.get(-0.1,0.1);
          new LineAgent(pt2, dir.cp().rot(IRand.get(PI/3,2*PI/3)), 0, 0).clr(r,g,b);
        }
      
        double r = red()+IRand.get(-0.03,0.03);
        double g = green()+IRand.get(-0.03,0.03);
        double b = blue()+IRand.get(-0.03,0.03);
        //checking bend
        if(bendCount == 0 && minDist >=0 && frontAngle!=0){
          if(frontAngle < 0){ frontAngle+=PI; }//going in only one direction
          bendCount = bendSize;
          bendAngle = frontAngle/bendCount;
        }
        if(bendCount > 0){ //bending
          dir.rot(bendAngle);
          new LineAgent(pt2, dir, bendAngle, bendCount-1).clr(r,g,b);
        }
        else{ //go straight
           new LineAgent(pt2, dir, 0, 0).clr(r,g,b);
        }
      }
    }
  }
}


(back to the list of tutorials)

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