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

Tension to Particles (requires iGeo version 7.5.4 or higher)

     Force Vector of Tension

A force of tension between two particles can be defined by calculation of vectors. The direction of the tension force is same with the difference vector between two particles. The amount of the force is proportional to the distance between them because when they are farther, the tension is larger like a rubber band pulled apart.

These conditions can be described with vectors. When there are two particles of particle 1 at the position P1 and particle 2 at P2, the difference vector between the vector P1 and the vector P2 is P2 - P1. Then because the amount of the force corresponds to the length of the vector, for the force to be proportional to the length, you simply multiply some constant number "k" as a proportional coefficient, as the following figure.

This tensile force F needs to be applied to both of the particles and the direction of each applied force needs to be opposite to satisfy the third law of Newton's laws of motion, the action-reaction law.


     Tension Agent Class

Implementation of tension to particle agents can be done by creating tension agent class. The following code shows the tension agent class MyTension and its fields contain two vector variable of particle agents particle1 and particle2, and one scalar variable of proportional coefficient of tension tension.

The tension force is applied to particles inside interact method. First a difference vector (dif) of two position of particles is calculated. Then the coefficient (tension) is multiplied to the difference to resize the vector as a force vector. Finally this force vector is applied to each particle with opposite direction by the particle's method push and pull.

One detailed technical note. Because the code inside interact method doesn't use its input argument agents, it would seem better to write this code in update method. However conceptually speaking, codes to influence other agents should be written inside interact method and codes to update its own internal state should be written inside update method. Technically speaking, inside iGeo server, interact method is executed first for all existing agents and then update method is executed second in each time frame. Because particle agents are updating velocities and positions out of forces they received, application of force should happen before the cycle of update method.

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

void setup(){
  size(480, 360, IG.GL);
  IG.duration(20);
  MyParticle pt1 = new MyParticle(IG.v(-80,80,0),IG.v(0,0,0));
  MyParticle pt2 = new MyParticle(IG.v(80,-80,0),IG.v(0,0,0));
  pt1.clr(0);
  pt2.clr(1.0,0,0);
  new MyTension(pt1, pt2, 3.0);
}

class MyTension extends IAgent{
  MyParticle particle1, particle2;
  double tension; // proportial coefficient

  MyTension(MyParticle p1, MyParticle p2, double t){
    particle1 = p1;
    particle2 = p2;
    tension = t;
  }

  void interact(ArrayList < IDynamics > agents){
    IVec dif = particle2.pos().dif(particle1.pos());
    dif.mul(tension);
    particle1.push(dif);
    particle2.pull(dif); //opposite force
  }
}

class MyParticle extends IParticle{
  MyParticle(IVec pos, IVec vel){ super(pos,vel); }
  void update(){
    new IPoint(pos.cp()).clr(clr()); // putting point every frame
  }
}


     Tension Line

The tension agent class in the previous code didn't have any geometry to show where a tension force is applied. If you want to show the tension agent as a line connecting two particle agents, you can add a field of ICurve for the tension agent to contain geometry and update the geometry in each time frame when the position of particle changes as the following example.

The field line in a type of ICurve is added to the tension agent class and it creates a new line having two particles' positions as end points of the line when line is not initialized yet (When no value is assigned to field variable of some class, it contains a value of "null". Please also see this page about "null"). Then if it's already has content in line, ICurve's internal graphical representation is updated by updateGraphic() method.

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

void setup(){
  size(480, 360, IG.GL);
  IG.duration(15);
  MyParticle pt1 = new MyParticle(IG.v(-80,80,0),IG.v(0,0,0));
  MyParticle pt2 = new MyParticle(IG.v(80,-80,0),IG.v(0,0,0));
  pt1.clr(0);
  pt2.clr(1.0,0,0);
  new MyTension(pt1, pt2, 3.0).clr(1,0.5,0);
}

class MyTension extends IAgent{
  MyParticle particle1, particle2;
  double tension; // proportial coefficient
  ICurve line;

  MyTension(MyParticle p1, MyParticle p2, double t){
    particle1 = p1;
    particle2 = p2;
    tension = t;
  }

  void interact(ArrayList < IDynamics > agents){
    IVec dif = particle2.pos().dif(particle1.pos());
    dif.mul(tension);
    particle1.push(dif);
    particle2.pull(dif); //opposite force
  }

  void update(){
    if(line==null){ 
      line = new ICurve(particle1.pos(),particle2.pos()).clr(clr()); 
    }
    else{ 
      line.updateGraphic();
    }
  }
}

class MyParticle extends IParticle{
  MyParticle(IVec pos, IVec vel){ super(pos,vel); }
  void update(){
    new IPoint(pos.cp()).clr(clr()); // putting point every frame
  }
}


     Using ITensionLine Class

iGeo library has a class to simulate a tensile force and represent it as a line connecting two particles. It is ITensionLine class. You can use this to simulate tensile force behaviors if you don't need to define your custom tension behavior.

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

void setup(){
  size(480, 360, IG.GL);
  IG.duration(15);
  MyParticle pt1 = new MyParticle(IG.v(-80,80,0),IG.v(0,0,0));
  MyParticle pt2 = new MyParticle(IG.v(80,-80,0),IG.v(0,0,0));
  pt1.clr(0);
  pt2.clr(1.0,0,0);
  new ITensionLine(pt1, pt2, 3.0).clr(1,0.5,0);
}

class MyParticle extends IParticle{
  MyParticle(IVec pos, IVec vel){ super(pos,vel); }
  void update(){
    new IPoint(pos.cp()).clr(clr()); // putting point every frame
  }
}

     Interaction of Particles with Tension

The following three examples shows behaviors of particles when they are connected with tension. They show traces of particle agents and the behavior of particles can be tuned by positions and velocities of particles and the coefficient of tension strength.

The first code below has 3 particles with zero initial velocity and 2 pairs of particles are connected by tension with the same tension strength.

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

void setup(){
  size(480, 360, IG.GL);
  IG.duration(800);
  MyParticle pt1 = new MyParticle(IG.v(-80,80,0),IG.v(0,0,0));
  MyParticle pt2 = new MyParticle(IG.v(80,-80,0),IG.v(0,0,0));
  MyParticle pt3 = new MyParticle(IG.v(0,-80,0),IG.v(0,0,0));
  pt1.clr(0);
  pt2.clr(1.0,0,0);
  pt3.clr(0.5,0,1);
  new ITensionLine(pt1, pt2, 3.0).clr(1,0.5,0);
  new ITensionLine(pt2, pt3, 3.0).clr(1,0.5,0);
}

class MyParticle extends IParticle{
  IVec prevPos;
  MyParticle(IVec pos, IVec vel){ super(pos,vel); }
  void update(){
    IVec curPos = pos().cp();
    if(prevPos != null){ new ICurve(prevPos,curPos).clr(clr()); }
    prevPos = curPos;
  }
}

The next example changes one of 2 tensions to have smaller tension strength.

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

void setup(){
  size(480, 360, IG.GL);
  IG.duration(600);
  MyParticle pt1 = new MyParticle(IG.v(-80,80,0),IG.v(0,0,0));
  MyParticle pt2 = new MyParticle(IG.v(80,-80,0),IG.v(0,0,0));
  MyParticle pt3 = new MyParticle(IG.v(0,-80,0),IG.v(0,0,0));
  pt1.clr(0);
  pt2.clr(1.0,0,0);
  pt3.clr(0.5,0,1);
  new ITensionLine(pt1, pt2, 3.0).clr(1,0.5,0);
  new ITensionLine(pt2, pt3, 1.5).clr(1,0.5,0);
}

class MyParticle extends IParticle{
  IVec prevPos;
  MyParticle(IVec pos, IVec vel){ super(pos,vel); }
  void update(){
    IVec curPos = pos().cp();
    if(prevPos != null){ new ICurve(prevPos,curPos).clr(clr()); }
    prevPos = curPos;
  }
}

The next example below has same strength coefficient in 2 tensions but one of particle agent has non-zero initial velocity towards X direction.

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

void setup(){
  size(480, 360, IG.GL);
  IG.duration(1200);
  MyParticle pt1 = new MyParticle(IG.v(-80,80,0),IG.v(0,0,0));
  MyParticle pt2 = new MyParticle(IG.v(80,-80,0),IG.v(0,0,0));
  MyParticle pt3 = new MyParticle(IG.v(0,-80,0),IG.v(50,0,0));
  pt1.clr(0);
  pt2.clr(1.0,0,0);
  pt3.clr(0.5,0,1);
  new ITensionLine(pt1, pt2, 3.0).clr(1,0.5,0);
  new ITensionLine(pt2, pt3, 3.0).clr(1,0.5,0);
}

class MyParticle extends IParticle{
  IVec prevPos;
  MyParticle(IVec pos, IVec vel){ super(pos,vel); }
  void update(){
    IVec curPos = pos().cp();
    if(prevPos != null){ new ICurve(prevPos,curPos).clr(clr()); }
    prevPos = curPos;
  }
}


     Tensile Line Network

Using tension line agents, you can set up a larger network of tensile lines. The following code creates an orthogonal grid of lines and simulates tensile behavior between particle nodes of the grid. (The particle agent below doesn't generate lines because it's intended to show the state of the moment, not the trace of all particles in time).

The code sets friction of particle agents to converge into one state, without oscillating. This is done by a particle agent's method fric(). Some value between 0.0 and 1.0 should be set in the friction method and 0.0 means no friction and 1.0 means full friction making the particle hardly move. Usually, very small number like 0.01 or 0.001 is enough to converge a movement behavior of particles. To fix the corners of the grid, the method of particle agent fix() is used. When a particle is fixed with this method, all the forces applied to it is ignored and it doesn't move. If you want to unfix once fixed particle, please use unfix() method.

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

void setup(){
  size(480, 360, IG.GL);
  IG.duration(800);
  int num = 10;
  MyParticle[][] pts = new MyParticle[num+1][num+1];
  for(int i=0; i <= num; i++){
    for(int j=0; j <= num; j++){
      pts[i][j] = new MyParticle(IG.v(10*i,10*j,0), IG.v(0,0,0));
      pts[i][j].fric(0.01); //friction
      if(i > 0){ //tension line in X
        new ITensionLine(pts[i-1][j], pts[i][j],1).clr(0); 
      }
      if(j > 0){ //tension line in Y
        new ITensionLine(pts[i][j-1], pts[i][j],1).clr(0); 
      }
    }
  }
  pts[0][0].fix().clr(0.5,0,0); //fix the corner particle
  pts[num][0].fix().clr(0.5,0,0); //fix the corner particle
  pts[0][num].fix().clr(0.5,0,0); //fix the corner particle
  pts[num][num].fix().clr(0.5,0,0); //fix the corner particle
}

class MyParticle extends IParticle{
  IVec prevPos;
  MyParticle(IVec pos, IVec vel){ super(pos,vel); }
}

The code below creates a triangulated orthogonal grid of tensile lines and nodes of the grid is randomly removed.

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

void setup(){
  size(480, 360, IG.GL);
  IG.duration(500);
  int num = 30;
  MyParticle[][] pts = new MyParticle[num+1][num+1];
  for(int i=0; i <= num; i++){
    for(int j=0; j <= num; j++){
      if(IRand.pct(87)){
        pts[i][j] = new MyParticle(IG.v(10*i,10*j,0), IG.v(0,0,0));
        pts[i][j].fric(0.01); //friction
        if(i > 0 && pts[i-1][j]!=null){ 
          new ITensionLine(pts[i-1][j], pts[i][j],1).clr(0); 
        }
        if(j > 0 && pts[i][j-1]!=null){ 
          new ITensionLine(pts[i][j-1], pts[i][j],1).clr(0); 
        }
        if(i > 0 && j > 0 && pts[i-1][j-1]!=null){ 
          new ITensionLine(pts[i-1][j-1], pts[i][j],1).clr(0); 
        }
        if(i==0 || j==0 || i==num || j==num){ // edge
          pts[i][j].fix().clr(0.5,0,0);
        }
      }
    }
  }
}

class MyParticle extends IParticle{
  IVec prevPos;
  MyParticle(IVec pos, IVec vel){ super(pos,vel); }
}


     Combining Tension and Other Forces

The tutorial codes below show examples to combine tensile forces and other type of forces to the particle agents. It's done by creating agent classes for each type of force.

The code below includes an agent class of a gravity force, MyGravity. Each particle is pulled by the tensile lines as well as the gravity force.

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

void setup(){
  size(480, 360, IG.GL);
  IG.duration(2000);
  int num = 30;
  MyParticle[][] pts = new MyParticle[num+1][num+1];
  for(int i=0; i <= num; i++){
    for(int j=0; j <= num; j++){
      if(IRand.pct(75)){
        pts[i][j] = new MyParticle(IG.v(10*i,10*j,0), IG.v(0,0,0));
        pts[i][j].fric(0.01); //friction
        if(i > 0 && pts[i-1][j]!=null){ 
          new ITensionLine(pts[i-1][j], pts[i][j],1).clr(0); 
        }
        if(j > 0 && pts[i][j-1]!=null){ 
          new ITensionLine(pts[i][j-1], pts[i][j],1).clr(0); 
        }
        if(i > 0 && j > 0 && pts[i-1][j-1]!=null){ 
          new ITensionLine(pts[i-1][j-1], pts[i][j],1).clr(0); 
        }
        if(i==0 || j==0 || i==num || j==num){ // edge
          pts[i][j].fix().clr(0.5,0,0);
        }
      }
    }
  }
  new MyGravity(IG.v(0,0,-10));
}

class MyGravity extends IAgent{
  IVec gravity;
  MyGravity(IVec g){ gravity=g; }
  void interact(IDynamics agent){
    if(agent instanceof MyParticle){
      MyParticle particle = (MyParticle)agent;
      particle.push(gravity);
    }
  }
}

class MyParticle extends IParticle{
  IVec prevPos;
  MyParticle(IVec pos, IVec vel){ super(pos,vel); }
}

The next code has an agent class to implement a repulsive force, RepulsionAgent. This agent has the same algorithm with attractor agents for particles. It just has opposite direction of force to be applied to particles.

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

void setup(){
  size(480, 360, IG.GL);
  IG.duration(1000);
  int num = 30;
  MyParticle[][] pts = new MyParticle[num+1][num+1];
  for(int i=0; i <= num; i++){
    for(int j=0; j <= num; j++){
      if(IRand.pct(75)){
        pts[i][j] = new MyParticle(IG.v(10*i,10*j,0), IG.v(0,0,0));
        pts[i][j].fric(0.01); //friction
        if(i > 0 && pts[i-1][j]!=null){ 
          new ITensionLine(pts[i-1][j], pts[i][j],1).clr(0); 
        }
        if(j > 0 && pts[i][j-1]!=null){ 
          new ITensionLine(pts[i][j-1], pts[i][j],1).clr(0); 
        }
        if(i > 0 && j > 0 && pts[i-1][j-1]!=null){ 
          new ITensionLine(pts[i-1][j-1], pts[i][j],1).clr(0); 
        }
        if(i==0 || j==0 || i==num || j==num){ // edge
          pts[i][j].fix().clr(0.5,0,0);
        }
      }
    }
  }
  for(int i=0; i < 10; i++){
    new RepulsionAgent(IRand.pt(-50,-50,-10,350,350,-10)).clr(1.,0,0);
  }
}

class RepulsionAgent extends IPointAgent{
  double threshold = 100;
  double minDist = 1.0;
  double repulsion = 400;

  RepulsionAgent(IVec v){ super(v); }
  
  void interact(IDynamics agent){
    if(agent instanceof MyParticle){
      MyParticle particle = (MyParticle)agent;
      IVec dif = particle.pos().dif(pos()); //force from here to particle
      double dist = dif.len();
      if(dist < threshold){
        if(dist < minDist){ dist = minDist; }
        double strength = repulsion/dist; //the closer the larger
        IVec force = dif.len(strength);
        particle.push(force);
      }
    }
  }
}

class MyParticle extends IParticle{
  IVec prevPos;
  MyParticle(IVec pos, IVec vel){ super(pos,vel); }
}


(back to the list of tutorials)

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