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