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 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.

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

void setup() {
  size(480, 360, IG.GL);
  IG.duration(700);
  // left 99.0%, right 1.5%
  new LineAgent(IG.v(0,0,0), IG.v(2,0,0), 99.0, 1.5).clr(0);
}

static class LineAgent extends IAgent{
  IVec pos;
  IVec dir;
  double pctL, pctR;

  LineAgent(IVec pt, IVec dir, double pctL, double pctR) { 
    pos = pt; 
    this.dir = dir;
    this.pctL = pctL;
    this.pctR = pctR;
  }
  
  public void update() {
    if(time()==0){
      //putting line geometry
      IVec pos2 = pos.dup().add(dir);
      new ICurve(pos, pos2).clr(clr());

      double r = red() + IRand.get(-0.05, 0.05);
      double g = green() + IRand.get(-0.05, 0.05);
      double b = blue() + IRand.get(-0.05, 0.05);
      
      if(IRand.pct(3.0)){ //swap L/R percent
        double tmp = pctL;
        pctL = pctR;
        pctR = tmp;
      }

      if (IRand.pct(pctL)) { //bend left
        IVec dir2 = dir.dup();
        dir2.rot(PI/30);
        new LineAgent(pos2, dir2, pctL, pctR).clr(r,g,b);
      }

      if (IRand.pct(pctR)) { //bend right
        IVec dir2 = dir.dup();
        dir2.rot(-PI/30);
        new LineAgent(pos2, dir2, pctL, pctR).clr(r,g,b);
      }
      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.

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

void setup() {
  size(480, 360, IG.GL);
  IG.duration(350);
  // left 99.0%, right 7.0%
  new LineAgent(IG.v(0,0,0), IG.v(2,0,0), 99.0, 7.0).clr(0);
}

static class LineAgent extends IAgent{
  IVec pos;
  IVec dir;
  double pctL, pctR;
  boolean isColliding=false;

  LineAgent(IVec pt, IVec dir, double pctL, double pctR) { 
    pos = pt; 
    this.dir = dir;
    this.pctL = pctL;
    this.pctR = pctR;
  }
  
  public void interact(ArrayList agents){
    if(time()==0){
      for(int i=0; i < agents.size() && !isColliding ; i++){
        if(agents.get(i) instanceof LineAgent){
          LineAgent a = (LineAgent)agents.get(i);
          if(a != this){
            if(a.pos.dist(pos.cp(dir)) < dir.len()*0.999){
              isColliding=true;
            }
          }
        }
      }
    }
  }
  
  public void update() {
    if(time()==0){
      if(isColliding){ del(); return; }
      //putting line geometry
      IVec pos2 = pos.dup().add(dir);
      new ICurve(pos, pos2).clr(clr());

      double r = red() + IRand.get(-0.05, 0.05);
      double g = green() + IRand.get(-0.05, 0.05);
      double b = blue() + IRand.get(-0.05, 0.05);
      
      boolean branchL = IRand.pct(pctL); //boolean switch L
      boolean branchR = IRand.pct(pctR); //boolean switch R

      if (branchL) { //bend left
        IVec dir2 = dir.dup();
        dir2.rot(PI/30);
        if(branchR && pctR > pctL){//swap L/R% when branching both
          new LineAgent(pos2, dir2, pctR, pctL).clr(r,g,b);
        }
        else{
          new LineAgent(pos2, dir2, pctL, pctR).clr(r,g,b);
	}
      }

      if (branchR) { //bend right
        IVec dir2 = dir.dup();
        dir2.rot(-PI/30);
        if(branchL && pctR < pctL){//swap L/R% when branching both
          new LineAgent(pos2, dir2, pctR, pctL).clr(r,g,b);
        }
        else{
          new LineAgent(pos2, dir2, pctL, 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.

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

void setup() {
  size(480, 360, IG.GL);
  IG.duration(400);
  // left 100%, right 4.5%
  new LineAgent(IG.v(0,0,0), IG.v(2,0,0), 100.0, 4.5).clr(0);
}

static class LineAgent extends IAgent{
  IVec pos;
  IVec dir;
  double pctL, pctR;
  boolean isColliding=false;

  LineAgent(IVec pt, IVec dir, double pctL, double pctR) { 
    pos = pt; 
    this.dir = dir;
    this.pctL = pctL;
    this.pctR = pctR;
  }
  
  public void interact(ArrayList agents){
    if(time()==0){
      for(int i=0; i < agents.size() && !isColliding ; i++){
        if(agents.get(i) instanceof LineAgent){
          LineAgent a = (LineAgent)agents.get(i);
          if(a != this){
            IVec pos2 = pos.cp(dir);
            IVec apos2 = a.pos.cp(a.dir);
            if(!apos2.eq(pos) && !a.pos.eq(pos) && //not sharing root
              IVec.intersectLine(a.pos,apos2,pos,pos2)!=null){ //intersecting
               isColliding=true;
            }
          }
        }
      }
    }
  }
  
  public void update() {
    if(time()==0){
      if(isColliding){ del(); return; }
      
      IVec pos2 = pos.dup().add(dir);
      new ICurve(pos, pos2).clr(clr());

      double r = red() + IRand.get(-0.05, 0.05);
      double g = green() + IRand.get(-0.05, 0.05);
      double b = blue() + IRand.get(-0.05, 0.05);
      
      boolean branchL = IRand.pct(pctL);
      boolean branchR = IRand.pct(pctR);

      double lenL = dir.len();
      double lenR = dir.len();
      lenL*=0.995; //shrinking length
      lenR*=0.995; //shrinking length
      
      if (branchL && branchR) { //only when branching both
        if(IRand.pct(50.0)){
          if (pctL < pctR) { lenL *= 0.9; }
          else{ lenR *= 0.9; }
	}
        else if(IRand.pct(6.0)){
          if (pctL < pctR) { lenL *= 0.4; }
          else{ lenR *= 0.4; }
        }
        else if(IRand.pct(5.0)){
          if (pctL < pctR) { lenL *= 4.0; }
          else{ lenR *= 4.0; }
        }
      }
      
      if (branchL) { //bend left
        IVec dir2 = dir.dup();
        dir2.len(lenL); //update length
        dir2.rot(PI/30);
        
        if(branchR && pctR > pctL){//swap L/R% when branching both
          new LineAgent(pos2, dir2, pctR, pctL).clr(r,g,b);
        }
        else{
          new LineAgent(pos2, dir2, pctL, pctR).clr(r,g,b);
	}
      }

      if (branchR) { //bend right
        IVec dir2 = dir.dup();
        dir2.len(lenR); //update length
        dir2.rot(-PI/30);

        if(branchL && pctR < pctL){//swap L/R% when branching both
          new LineAgent(pos2, dir2, pctR, pctL).clr(r,g,b);
        }
        else{
          new LineAgent(pos2, dir2, pctL, pctR).clr(r,g,b);
        }
      }
    }
  }
}


(back to the list of tutorials)

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