processingでベクトル場

f:id:peroon:20090804143632j:image
コードは参照サイト様のもので、
それに私の理解用コメントを追加しています。


ベクトル場を生成して、
パーティクルを加速させていくことで、
風のような表現ができています。


ポイントは以下のコードです。
random()ではなくnoise()を使って、
連続性のあるランダムな場を作っています。

for(int j=0;j<this.numOfCellY;j++){
  for(int i=0;i<this.numOfCellX;i++){
    cells[i][j] = new Cell(i,j,
                           noise( (i + offsetAX) * noiseFactor,  (j + offsetAY) * noiseFactor ) - 0.5, 
                           noise( (i + offsetBX) * noiseFactor,  (j + offsetBY) * noiseFactor ) - 0.5 );
  }
}


以下はソースコードです。そのまま動きます。

//領域をGRIDに分けて、それぞれにx,y方向の速度を決める。
//各パーティクルが踏んだGRIDから速度を加えられ、加速(減速)する。
//速度場(加速度場?)の生成には乱数を使うが、
//乱数だと連続性のない場になるので、
//random()ではなくnoise()を用いている。


/** 
 * <h1 style="font-family:Georgia,newyork,times;font-size:medium;">Stream line</h1>
 * Auther:<a href="http://gofar2.iobb.net/~htaka/">htaka</a><br />
 * <br />
 * Mouse click ... change vector field.
 */

VectorField vectorF;
int numOfLine = 100;
Particle[] myParticle = new Particle[numOfLine];
Random randGen;


void setup(){
  size(800,400);//
  background(240);//
  randGen = new Random();//現在の日時をseedとする

  for(int i= 0;i < numOfLine;i++){
    myParticle[i] = new Particle(width/2,height/2,randGen.nextFloat()*10 - 5,randGen.nextFloat()*10 - 5);
  }
  smooth();//
  strokeWeight(1);//
  stroke(255,0);//
  vectorF = new VectorField(width/20,height/20,0.1,54655465);
}

void draw(){
  //gradually white
  fill(255,15);
  noStroke();
  rect(0,0,width,height);

  //vectorF.fillWithValue(1.0);
  //vectorF.drawGrid();
  //vectorF.drawVector();
  for(int i = 0;i < numOfLine;i++){
    myParticle[i].draw();
  }

}

void mousePressed(){
  vectorF = new VectorField(width/20,height/20,0.1,54655465);
}

class VectorField{
  int numOfCellX;
  int numOfCellY;
  float cellWidth,cellHeight;
  Cell[][] cells;

  //Constructor ok
  VectorField(int numOfCellX,int numOfCellY){
    this.numOfCellX = numOfCellX;
    this.numOfCellY = numOfCellY;
    this.cellWidth = width/numOfCellX;
    this.cellHeight = height/numOfCellY;

    //オブジェクトの配列
    //まず配列宣言
    cells = new Cell[this.numOfCellX][this.numOfCellY];

    //配列の各要素を初期化
    for(int j=0;j<this.numOfCellY;j++){
      for(int i=0;i<this.numOfCellX;i++){
        cells[i][j] = new Cell(i,j,0,0);
      }
    }  
  }

  //コンストラクタの多重定義(overload)
  VectorField(int numOfCellX,int numOfCellY,float noiseFactor,int seed){
    this.numOfCellX = numOfCellX;
    this.numOfCellY = numOfCellY;
    this.cellWidth = width/numOfCellX;
    this.cellHeight = height/numOfCellY;

    cells = new Cell[this.numOfCellX][this.numOfCellY];

    //random()は離散、noise()は連続
    randomSeed(seed);
    noiseSeed(seed);
    float offsetAX = mouseX;
    float offsetAY = mouseY;
    float offsetBX = (randGen.nextFloat()*1200)-600;
    float offsetBY = (randGen.nextFloat()*800)-400;
    for(int j=0;j<this.numOfCellY;j++){
      for(int i=0;i<this.numOfCellX;i++){
        cells[i][j] = new Cell(i,j,
                               noise( (i + offsetAX) * noiseFactor,  (j + offsetAY) * noiseFactor ) - 0.5, 
                               noise( (i + offsetBX) * noiseFactor,  (j + offsetBY) * noiseFactor ) - 0.5 );
      }
    }
  }
  float getVecX(float posX,float posY){
    return cells[(int)(posX/cellWidth)][(int)(posY/cellHeight)].getVecX();
  }
  float getVecY(float posX,float posY){
    return cells[(int)(posX/cellWidth)][(int)(posY/cellHeight)].getVecY();
  }
  
  //Draw
  void drawGrid(){
    noSmooth();
    stroke(128,128);
    for(int i=0;i<this.numOfCellX;i++){
      line(cellWidth*i,0,cellWidth*i,height);
    }
    for(int j=0;j<this.numOfCellY;j++){
      line(0,cellHeight*j,width,cellHeight*j);
    }
  }

  void drawVector(){
    float drawVectorLength = (cellWidth + cellHeight) / 2 * 50;
    stroke(255,200);
    smooth();
    for(int j=0;j<this.numOfCellY;j++){
      for(int i=0;i<this.numOfCellX;i++){
        line(cellWidth*i+cellWidth/2, cellHeight*j+cellHeight/2,
             cellWidth*i+cellWidth/2+cells[i][j].vecX*drawVectorLength, cellHeight*j+cellHeight/2+cells[i][j].vecY*drawVectorLength);
      }
    }  
  }

  void fillWithValue(float padding){
    noStroke();
    rectMode(CENTER);
    for(int j=0;j<this.numOfCellY;j++){
      for(int i=0;i<this.numOfCellX;i++){
        fill(255*(cells[i][j].vecX+0.5), 255*(cells[i][j].vecY+0.5),0);
        rect(cellWidth*i+cellWidth/2, cellHeight*j+cellHeight/2, cellWidth*padding, cellHeight*padding);
      }
    }  
  }


}


//ok
class Cell{
  int posX,posY;
  float vecX,vecY;
  Cell(int posX,int posY,float vecX,float vecY){
    this.posX = posX;
    this.posY = posY;
    this.vecX = vecX;
    this.vecY = vecY;
  }
  float getVecX(){
    return vecX;
  }
  float getVecY(){
    return vecY;
  }
}


//ok
class Particle{
  float posX,posY,oldPosX,oldPosY;
  float velocityX,velocityY;
  boolean noLineFlag;
  
  //thisを使い、引数とクラス変数の名前を一致させる ok
  Particle(float posX,float posY,float velocityX,float velocityY){
    this.posX = posX;
    this.posY = posY;
    this.oldPosX = posX;
    this.oldPosY = posY;
    this.velocityX = velocityX;
    this.velocityY = velocityY;
    noLineFlag = false;
  }
  
  //メソッドでもdraw()を定義できるようだ ok
  void draw(){
    update();
    if(noLineFlag){
      oldPosX = posX;
      oldPosY = posY;
      noLineFlag = false;
    }
    else{
      stroke(50,70,135);
      line(oldPosX,oldPosY,posX,posY);
      oldPosX = posX;
      oldPosY = posY;
    }
  }
  
  //ok
  void update(){
    //reset
    if((posX < 0)||(posY < 0)||(posX > width)||(posY > height)){
      posX = mouseX;
      posY = mouseY;

      velocityX = randGen.nextFloat()*10 - 5;
      velocityY = randGen.nextFloat()*10 - 5;
      noLineFlag = true;
    }
    //speed addition
    else{
      velocityX += vectorF.getVecX(posX,posY)*10;
      velocityY += vectorF.getVecY(posX,posY)*10;
      posX += velocityX;
      posY += velocityY;
    }
  }
}