home processing download documents tutorial python tutorial gallery source about
 チュートリアル (トピック一覧へ戻る)

パーティクルに対する張力

     張力の力ベクトル

2つのパーティクルの間に張力を引き起こす力は、 各々向きの異なるパーティクル間の差ベクトルから計算できます。 力の強さは、長さに比例します。 そのため、以下の図のように 力ベクトルFは、 P1P2の2点間の差ベクトルに係数kをかけたもの で定義されます。 この力ベクトルは、作用反作用の法則のため、向きを反転して2点のどちらにも 適用されます。


     張力エージェント・クラス

張力は、2つのパーティクルに作用を及ぼすエージェントとして実装できます。 以下のコードでは、MyTensionクラスが パーティクルのインスタンスである particle1particle2、 あと力の係数kとなるtensionフィールドに持ちます。

張力ベクトルの計算と、2つのパーティクルへの適用は interactメソッドでなされます。 差ベクトルdifがまず計算され、tensionが それに乗算されます。 そしてpushメソッドとpullメソッドを用いて 力がそれぞれ別方向に適用されます。

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


     張力線エージェント

上のコードでは、張力は目に見えない力でしたが、 次の例では、張力に線を組み合わせて張力線エージェントを作成します。 MyTensionクラスにICurve型のフィールドlineが追加し、 updateメソッド内で線が生成され、また位置が更新されます。 ICurveの線は、線の生成に用いられた点が更新され、参照が保持される限り updateGraphic()メソッドで、線が更新できます。

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


     ITensionLineクラスの利用

iGeoライブラリには張力と線を組み合わせたエージェントとして ITensionLineクラスが用意されています。 上のコードと同様に2つのパーティクルと張力の強さを示す張力係数を指定して生成します。

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

     パーティクルと張力のインタラクション

以下の例では、3つのパーティクルを2つの張力線で繋いだ場合の 様々な振る舞いを示します。なお、例で使われているパーティクルは、軌跡を線として残す動作がアップデート・メソッドに 定義されています。 一つめの例ではパーティクルの初期速度ゼロ、2つの張力線の張力係数は同じとしています。

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;
  }
}

二つめの例では、片方の張力係数を小さくしています。

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;
  }
}

三つめの例では再び張力係数は同じですが、 一つのパーティクルにX方向の初期速度が与えられています。

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;
  }
}


     張力線のネットワーク

多くの張力線をつなぎ合わせると、張力のネットワークを構成することができます。 以下の例では、 格子状に生成したパーティクルを縦横につないで、 張力の格子線を構成しています。

この例ではfric()メソッドを用いて パーティクルに摩擦度が指定されています。 fric()の引数に入れる摩擦度は0から1のdouble型の値で、 0の時は摩擦が無く速度は全く減衰せず、 1は最大の摩擦で、速度は常にゼロに戻されています。

また、パーティクルがその位置で固定されて動かなくなるようにするメソッド fix()も用いられています。 一度固定されたパーティクルはunfix()メソッドで また動けるようにできます。

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); }
}

次の例では、格子線の張力ネットワークに 斜めの張力線を追加しています。 更に、格子点上のパーティクルと 張力線の生成を確率的にコントロールすることにより 格子線ネットワークの上に、穴が空くような状況をつくりだし、 そのネットワークが張力によってどのように変形するかを 観察しています。

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); }
}


     張力と他の力の組み合わせ

以下のコードでは張力と他の力を組み合わせてパーティクルに与えています。 まずは、重力エージェントMyGravityを加えて、 各パーティクルが張力で移動しながらも、下方へ引き寄せられ、 しかし固定された縁のパーティクルと張力により、放物面のような垂れ下がった形状をとります。

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); }
}

次の例では、斥力エージェントRepulsionAgentを作成し、多く配置することによって 張力格子で形成される形状を膨らませるような形で変形しています。

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); }
}


(トピック一覧へ戻る)

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