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

物理シミュレーションとスウォーム・アルゴリズム例

     曲面上のスウォーム・エージェント・アルゴリズム例

このページでは、曲面上を動くスウォーム・エージェントを定義し、それらによって 曲面周辺に幾何学オブジェクトを生成するアルゴリズムの例を示します。

最初のコードでは、IBoidクラスを継承したMyBoidクラスを定義し、 XY平面上の座標値0 - 1だけを動くように調整します。 アップデート・メソッドの中で、もしエージェントが範囲外に出たら1を足したり引いたりすることによって 範囲内の座標に連れ戻しています。 ブーリアン変数jumpは範囲を右から左などにジャンプしたかどうかの判定値が与えられ、 範囲の逆側へ線が描かれないようにしています。
setup()メソッドでは、 IG.top()メソッドにより真上のビューに設定され IG.focus() メソッドにより、存在する幾何学オブジェクトへズームされます。

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

void setup() {
  size(480, 360, IG.GL);
  IG.duration(800);
  for(int i=0; i < 200; i++){
    new MyBoid(IRand.pt(1,1,0), 
               IRand.pt(-0.01,-0.01,0,0.01,0.01,0));
  }
  IG.top(); //only showing the top view
  IG.focus(); //focusing into existing geometries
}

class MyBoid extends IBoid {
  IVec prevPos;
  MyBoid(IVec pos, IVec vel) {
    super(pos, vel);
    cohesionDist(0.03);
    cohesionRatio(1);
    separationDist(0.04);
    separationRatio(2);
    alignmentDist(0.03);
    alignmentRatio(0);
  }
  

  void update() {
    boolean jump=false;
    if (pos().x() < 0.0 ) { 
      pos().add(1, 0, 0);
      jump=true;
    }
    else if (pos().x() > 1.0) { 
      pos().sub(1, 0, 0);
      jump=true;
    }
    if (pos().y() < 0.0 ) { 
      pos().add(0, 1, 0);
      jump=true;
    }
    else if (pos().y() > 1.0) { 
      pos().sub(0, 1, 0);
      jump=true;
    }
    
    IVec curPos = pos().cp();
    if(prevPos!=null && !jump){
      new ICurve(prevPos, curPos).clr(0);
    }
    prevPos = curPos;
  }
}

二つ目のコードでは、スウォーム・エージェントの動作は一つ目のコードと全く一緒ですが、 生成される線が、軌跡のとは異なるものを生成しています。 ここでの線の生成規則は、インタラクト・メソッドの中で与えられ、 もしも他のエージェントがある指定の距離(以下では0.08)よりも近くにいた場合にのみ、 相手に向かって線を生成します。 また、線が生成されすぎないように、生成は毎フレームはおこらず、 剰余演算子%とIG.time()を 用いて15フレームに一回だけ線を生成するようにしてあります。

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

void setup() {
  size(480, 360, IG.GL);
  IG.duration(800);
  for(int i=0; i < 200; i++){
    new MyBoid(IRand.pt(1,1,0), 
               IRand.pt(-0.01,-0.01,0,0.01,0.01,0));
  }
  IG.top(); //only showing the top view
  IG.focus(); //focusing into existing geometries
}

class MyBoid extends IBoid {

  MyBoid(IVec pos, IVec vel) {
    super(pos, vel);
    cohesionDist(0.03);
    cohesionRatio(1);
    separationDist(0.04);
    separationRatio(2);
    alignmentDist(0.03);
    alignmentRatio(0);
  }
  
  void interact(ArrayList < IDynamics > agents){
    super.interact(agents);
    for(int i=0; i < agents.size() && agents.get(i)!=this; i++){
      if(agents.get(i) instanceof MyBoid){
        MyBoid b = (MyBoid)agents.get(i);
        double dist = pos().dist(b.pos());
        if ( dist < 0.08 ) {
          if ( IG.time()%15==0 ) {
            new ICurve(pos().cp(), b.pos().cp()).clr(0);
          }
        }
      }
    }
  }

  void update() {
    if (pos().x() < 0.0 ) { 
      pos().add(1, 0, 0);
    }
    else if (pos().x() > 1.0) { 
      pos().sub(1, 0, 0);
    }
    if (pos().y() < 0.0 ) { 
      pos().add(0, 1, 0);
    }
    else if (pos().y() > 1.0) { 
      pos().sub(0, 1, 0);
    }
  }
}

次のコードは、スウォーム・エージェントの動きも、生成される線も全く一緒ですが、 線の色と背景色が異なります。
背景色はIG.bg(0)メソッドで指定され、 線の色は2つの引数のclrメソッドにおいて、一つ目がグレースケールの色の値、二つ目が透明度を指定することによって なされています。
また、スウォーム・エージェントの位置にある点は、hide()メソッドによって非表示になっています。

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

void setup() {
  size(480, 360, IG.GL);
  IG.duration(800);
  for(int i=0; i < 200; i++){
    new MyBoid(IRand.pt(1,1,0), 
               IRand.pt(-0.01,-0.01,0,0.01,0.01,0));
  }
  IG.bg(0); //black background
  IG.top(); //only showing the top view
  IG.focus(); //focusing into existing geometries
}

class MyBoid extends IBoid {

  MyBoid(IVec pos, IVec vel) {
    super(pos, vel);
    cohesionDist(0.03);
    cohesionRatio(1);
    separationDist(0.04);
    separationRatio(2);
    alignmentDist(0.03);
    alignmentRatio(0);
  }
  
  void interact(ArrayList < IDynamics > agents){
    super.interact(agents);
    for(int i=0; i < agents.size() && agents.get(i)!=this; i++){
      if(agents.get(i) instanceof MyBoid){
        MyBoid b = (MyBoid)agents.get(i);
        double dist = pos().dist(b.pos());
        if ( dist < 0.08 ) {
          if ( IG.time()%15==0 ) {
            new ICurve(pos().cp(), b.pos().cp()).clr(1.0,0.2); //transparent white
          }
        }
      }
    }
  }

  void update() {
    if ( time()==0 ) { hide(); } //hiding a point of boid
    if (pos().x() < 0.0 ) { 
      pos().add(1, 0, 0);
    }
    else if (pos().x() > 1.0) { 
      pos().sub(1, 0, 0);
    }
    if (pos().y() < 0.0 ) { 
      pos().add(0, 1, 0);
    }
    else if (pos().y() > 1.0) { 
      pos().sub(0, 1, 0);
    }
  }
}

次の例では、スウォーム・エージェントを曲面上へマッピングします。 入力ファイルから読み込まれた曲面は MyBoidOnSurfaceクラスのエージェントへ渡されます。 MyBoidOnSurfaceクラスには2つのフィールドがあります。
        ISurface surface;
        IVec surfPt;

これらのフィールドにより、エージェントの内部座標(XY平面、0-1)から、曲面上の一点へと変換が行われます。 アップデート・メソッドにおいて、pos()で与えられる内部座標が、surface.pt()メソッドを用いて 曲面上の3次元座標に変換され、それがsurfPtに保存されます。
インタラクト・メソッドでエージェント間の距離を測るときには、内部座標ではなく、変換後の座標であるsurfPtが 用いられます。
以下が例で用いられる入力曲面のファイルです。

surface13.3dm

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

void setup() {
  size(480, 360, IG.GL);
  IG.duration(800);
  IG.open("surface13.3dm");
  ISurface surf = IG.surface(0);
  for(int i=0; i < 300; i++){
    new MyBoidOnSurface(surf, 
                        IRand.pt(1,1,0), 
                        IRand.pt(-0.01,-0.01,0,0.01,0.01,0));
  }
  surf.del();
}

class MyBoidOnSurface extends IBoid {
  ISurface surface; 
  IVec surfPt;

  MyBoidOnSurface(ISurface srf, IVec pos, IVec vel) {
    super(pos, vel);
    surface = srf;
    surfPt = surface.pt(pos);
    hide(); //hiding a point of boid
    cohesionDist(0.03);
    cohesionRatio(1);
    separationDist(0.04);
    separationRatio(2);
    alignmentDist(0.03);
    alignmentRatio(0);
  }

  void interact(ArrayList < IDynamics > agents){
    super.interact(agents);
    for(int i=0; i < agents.size() && agents.get(i)!=this; i++){
      if(agents.get(i) instanceof MyBoidOnSurface){
        MyBoidOnSurface b = (MyBoidOnSurface)agents.get(i);
        double dist = surfPt.dist(b.surfPt);
        if ( dist < 5.0 ) {
          if ( IG.time()%15==0 ){
            new ICurve(surfPt.cp(), b.surfPt.cp()).clr(0);
          }
        }
      }
    }
  }

  void update() {
    if ( pos().x() < 0.0 ) { pos().add(1, 0, 0); }
    else if ( pos().x() > 1.0 ) { pos().sub(1, 0, 0); }
    if ( pos().y() < 0.0 ){ pos().add(0, 1, 0); }
    else if ( pos().y() > 1.0 ) { pos().sub(0, 1, 0); }

    surfPt = surface.pt(pos()); //update surface point
  }
}

またメッシュの位置は時間に応じて入力曲面の法線方向にある幅でsinを用いて振動しながら移動しているので、 最終的に生成・蓄積されるメッシュデータには厚みができることになります。 棒状メッシュの色もその振動に連動しています。

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

void setup() {
  size(480, 360, IG.GL);
  IG.duration(800);
  IG.open("surface13.3dm");
  ISurface surf = IG.surface(0);
  for(int i=0; i < 300; i++){
    new MyBoidOnSurface(surf, IRand.pt(1,1,0), 
                        IRand.pt(-0.01,-0.01,0,0.01,0.01,0));
  }
  surf.del();
}

class MyBoidOnSurface extends IBoid {
  ISurface surface;
  IVec surfPt;

  MyBoidOnSurface(ISurface srf, IVec pos, IVec vel) {
    super(pos, vel);
    surface=srf;
    surfPt = surface.pt(pos);
    hide(); //hiding a point of boid
    cohesionDist(0.03);
    cohesionRatio(1);
    separationDist(0.04);
    separationRatio(2);
    alignmentDist(0.03);
    alignmentRatio(0);
  }
  
  void interact(ArrayList < IDynamics > agents){
    super.interact(agents);
    for(int i=0; i < agents.size() && agents.get(i) != this; i++){
      if ( agents.get(i) instanceof MyBoidOnSurface ) {
        MyBoidOnSurface b = (MyBoidOnSurface)agents.get(i);
        double dist = surfPt.dist(b.surfPt);
        if ( dist < 5.0 && dist > 2.0 ) {
          if ( IG.time()%15==0 ) {
            //controlling depth
            double depth = sin(IG.time()*0.01)*1.5;
            IVec pt1 = surface.pt(pos().x, pos().y, depth);
            IVec pt2 = b.surface.pt(b.pos().x, b.pos().y, depth);
            double gray = sin(IG.time()*0.01)*0.5+0.5;
            IG.meshSquareStick(pt1,pt2,0.4).clr(gray);
          }
        }
      }
    }
  }

  void update() {
    if ( pos().x() < 0.0 ) { pos().add(1, 0, 0); }
    else if ( pos().x() > 1.0 ) { pos().sub(1, 0, 0); }
    if ( pos().y() < 0.0 ){ pos().add(0, 1, 0); }
    else if ( pos().y() > 1.0 ) { pos().sub(0, 1, 0); }
    
    surfPt = surface.pt(pos()); //update surface point
  }
}


(トピック一覧へ戻る)

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