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

Making A Custom Agent (Particle Base) (requires iGeo version 0.8.1.9 or higher)

     Particle Based Agent

This tutorial shows a process to develop a custom agent class based on particle behaviors. When you develop a particle-based agent, you can create a class inheriting IParticle class (or IBoid class if you use the swarm behaviors). The following code would be a template to start your custom particle-based agent.

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

void setup(){
  size(480,360,IG.GL);
  new MyParticle(new IVec(0,0,0), new IVec(10,0,0));
}

class MyParticle extends IParticle{
  MyParticle(IVec p, IVec v){
    super(p, v); // initialize with position and velocity
  }
}

The code above shows just one moving particle. If you want to show a trajectory of a particle, you can use IParticleTrajectory instead of IParticle.

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

void setup(){
  size(480,360,IG.GL);
  new MyParticle(new IVec(0,0,0), new IVec(10,0,0));
}

class MyParticle extends IParticleTrajectory{
  MyParticle(IVec p, IVec v){
    super(p, v);
  }
}


     Random Walk Rule in Update Method

As a first step to add a behavior to a particle-based agent, we add a random walk behavior. Because this behavior is about updating an agent's own state, the behavior is described in update method. It has a rule to push itself with a random vector as a force in a random probability.

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

void setup(){
  size(480,360,IG.GL);
  new MyParticle(new IVec(0,0,0), new IVec(10,0,0));
}

class MyParticle extends IParticleTrajectory{
  MyParticle (IVec p, IVec v) {
    super(p, v);
    fric(0.05); // 5% friction
  }
  
  void update(){
    if (IRand.pct(10)) {
        push(IRand.pt(-1000, -1000, 0, 1000, 1000, 0));
    }
  }
}


     Attraction Force Rule in Interact Method

As a second rule, the agent gets a rule to attract each other. Because this behavior is about interaction between agents of the same class, the rule is written inside interact method. An agent checks other agents if they are within a certain distance. If so it pushes others towards itself by calculating a difference vector. When attraction force is applied, the random walk force is still applied because all forces are added up as a vector.

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

void setup(){
  size(480,360,IG.GL);
  for(int i=0; i < 50; i++){
    new MyParticle(IRand.pt(-50,-50,0,50,50,0), new IVec(0,0,0));
  }
}

class MyParticle extends IParticleTrajectory{
  MyParticle(IVec p, IVec v){
    super(p, v);
    fric(0.05); // 5% friction
  }
  
  void interact(ArrayList < IDynamics > agents){
    for(int i=0; i < agents.size(); i++){
      if(agents.get(i) instanceof MyParticle){
        MyParticle ptcl = (MyParticle)agents.get(i);
        if(ptcl != this){
          if(ptcl.pos().dist(pos()) < 15){ // closer than 15
            IVec dif = pos().dif(ptcl.pos()); //other to this
            dif.len(40); // intensity of force
            ptcl.push(dif);
          }
        }
      }
    }
  }
  
  void update(){
    if (IRand.pct(10)) {
        push(IRand.pt(-500, -500, 0, 500, 500, 0));
    }
  }
}


     Adding A State Property

The code below shows an example to add a property to a particle-based agent. In the example, the agent introduces a state property named state as an integer value. This integer value is used to switch the behavior in interact method either to attract, repel or do nothing (random walk is still applied in either case). Then the value of state is changed in a random probability in update method.

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

void setup(){
  size(480,360,IG.GL);
  for(int i=0; i < 100; i++){
    new MyParticle(IRand.pt(-50,-50,0,50,50,0), new IVec(0,0,0));
  }
}

class MyParticle extends IParticleTrajectory{
  int state = 0; // initial state
  
  MyParticle(IVec p, IVec v){
    super(p, v);
    fric(0.05); // 5% friction
  }
  
  void interact(ArrayList < IDynamics > agents){
    for(int i=0; i < agents.size(); i++){
      if(agents.get(i) instanceof MyParticle){
        MyParticle ptcl = (MyParticle)agents.get(i);
        if(ptcl != this){
          if(ptcl.pos().dist(pos()) < 15){ // closer than 15
            IVec dif = pos().dif(ptcl.pos()); //other to this
            dif.len(40); // intensity of force
            if(state==1){ // attraction
              ptcl.push(dif);
            }
            else if(state==2){ // repulsion
              ptcl.pull(dif);
            }
          }
        }
      }
    }
  }
  
  void update(){
    if (IRand.pct(10)) {
        push(IRand.pt(-500, -500, 0, 500, 500, 0));
    }
    
    if(IRand.pct(1)){ // switch state
      if(state==0){ // state 0 -> 1
        state = 1; // attraction
        clr(1.0,0,1.0);
      }
      else if(state==1){ // state 1 -> 2
        state = 2; // repulsion
        clr(0,0,1.0);
      }
      else{ // state 2 -> 0
        state = 0; // random walk only
        clr(0.5);
      }
    }
  }
}


     Other Agent Classes to Interact

The following code shows an example to bring another agent class to have interaction between different classes. An agent of class Anchor below is instantiated by MyParticle agent in a certain time interval (every 20 time frame) in update method. Then if it finds other Anchor agents created in the past within a certain distance (10), it draws a line between them. An Anchor agent also has a rule to delete itself after a a certain time frame (100).

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

void setup(){
  size(480,360,IG.GL);
  for(int i=0; i < 100; i++){
    new MyParticle(IRand.pt(-50,-50,0,50,50,0), new IVec(0,0,0));
  }
  IG.bg(0);
}

class MyParticle extends IParticle{
  int state = 0; // initial state
  
  MyParticle(IVec p, IVec v){
    super(p, v);
    fric(0.05); // 5% friction
  }
  
  void interact(ArrayList < IDynamics > agents){
    for(int i=0; i < agents.size(); i++){
      if(agents.get(i) instanceof MyParticle){
        MyParticle ptcl = (MyParticle)agents.get(i);
        if(ptcl != this){
          if(ptcl.pos().dist(pos()) < 15){ // closer than 15
            IVec dif = pos().dif(ptcl.pos()); //other to this
            dif.len(20); // intensity of force
            if(state==1){ // attraction
              ptcl.push(dif);
            }
            else if(state==2){ // repulsion
              ptcl.pull(dif);
            }
          }
        }
      }
    }
  }
  
  void update(){
    if (IRand.pct(10)) {
        push(IRand.pt(-500, -500, 0, 500, 500, 0));
    }
    
    if(IRand.pct(1)){ // switch state
      if(state==0){ // state 0 -> 1
        state = 1; // attraction
        clr(1.0,0,1.0);
      }
      else if(state==1){ // state 1 -> 2
        state = 2; // repulsion
        clr(0,0,1.0);
      }
      else{ // state 2 -> 0
        state = 0; // random walk only
        clr(0.5);
      }
    }
    
    if(time()%20 == 0){
      new Anchor(pos().cp());
    }
  }
}

class Anchor extends IAgent{
  IVec pos;
  IPoint point;
  Anchor(IVec p){ 
    pos = p; 
    point = new IPoint(pos).clr(1.0,0,0).size(2);
  }
  
  void interact(ArrayList < IDynamics > agents){
    if(time()==0){ // only when the first time
      for(int i=0; i < agents.size(); i++){
        if(agents.get(i) instanceof Anchor){
          Anchor a = (Anchor)agents.get(i);
          if(a!=this && a.time() > 0){ // exclude anchors just created
            if(a.pos.dist(pos) < 10){ // closer than 10
              new ICurve(a.pos, pos).clr(1.0,0.1);
            }
          }
        }
      }
    }
  }
  void update(){
    if(time()==100){ // delete after 100 time frame
      point.del();
      del();
    }
  }
}


(back to the list of tutorials)

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