コードは参照サイト様のもので、
それに私の理解用コメントを追加しています。
ベクトル場を生成して、
パーティクルを加速させていくことで、
風のような表現ができています。
ポイントは以下のコードです。
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; } } }