The Nature of Code(PDF版)から流体の抗力について取り上げます。PVectorを用いて、流体の抗力について学びます。Processingでプログラムを書いて、動作を確認します。動作を確認できるところがProcessingの楽しいところです。
流体の抗力を示す式
流体の抗力を示す式は、以下のようになるそうです。
- :抗力
- :流体の密度
- :物体と流体の相対速度
- :投影面積
- :抗力係数
この式をベクトルを使い、以下のように表します。
2次元の世界でそれっぽく見えれば良いので、式を以下のように簡略化して考えます。
- :抗力
- :物体と流体の相対速度の大きさ
- :抗力係数
- :速度の単位ベクトル(=velocity.normalize())
式から抜け落ちたρとAは、以下のように扱うこととします。
- :1とする
- :オブジェクトが球体なので無視することにする
この式を向きと大きさに分解して考えます。
- 向き
- :速度の単位ベクトルの-1倍
- 抗力は速度(移動方向)と逆方向
- 大きさ
- :物体と流体の相対速度の2乗
- :任意の値
流体の抗力を適用する例
以下は、流体の抗力を適用したプログラムの参考例です。ボールが液体内にあれば、ボールに流体の抗力を適用します。ここでは、液体部分をグレーに色付けしています。
class Mover { PVector location; PVector velocity; PVector acceleration; float mass; //質量 //コンストラクタ Mover() { location = new PVector(30,30); velocity = new PVector(0,0); acceleration = new PVector(0,0); mass = 1; } Mover(float m, float x, float y) { location = new PVector(x,y); velocity = new PVector(0,0); acceleration = new PVector(0,0); mass = m; } //力を足し合わせる(力の蓄積) void applyForce(PVector force) { PVector f = PVector.div(force,mass); acceleration.add(f); } //ボールの位置の更新 void update() { velocity.add(acceleration); location.add(velocity); acceleration.mult(0); //力の蓄積をリセットする } //ボールの描画 void display() { stroke(0); strokeWeight(2); fill(0, 127); ellipse(location.x,location.y,mass*16,mass*16); } //ボールがバウンドしたときの処理 void checkEdges() { if (location.y > height) { velocity.y *= -0.9; location.y = height; } } }
class Liquid{ float x,y,w,h; float c; //抗力の係数 //コンストラクタ Liquid(float x, float y, float w, float h, float c){ this.x = x; this.y = y; this.w = w; this.h = h; this.c = c; } //ボールが液体内にあるか判定する boolean contains(Mover m){ PVector l = m.location; if(l.x > x && l.x < x+w && l.y > y && l.y < y+h){ return true; }else{ return false; } } //液体の抗力を計算する PVector drag(Mover m){ //大きさを計算する float speed = m.velocity.mag(); float dragMagnitude = c * speed * speed; //力の向きを求める PVector dragForce = m.velocity.copy(); dragForce.mult(-1); dragForce.normalize(); dragForce.mult(dragMagnitude); return dragForce; //抗力を返す } //液体の描画 void display(){ noStroke(); fill(175); rect(x, y, w, h); } }
//Fluid Resistance Mover[] movers = new Mover[11]; Liquid liquid; void setup() { size(800,200); randomSeed(1); smooth(); //液体の生成 liquid = new Liquid(0, height/2, width, height/2, 0.1); //ボールの生成 for(int i = 0 ; i < movers.length; i++){ movers[i] = new Mover(random(0.5,3), 40+i*70,0); } } void draw() { background(255); liquid.display(); //ボールに対しての処理 for(int i = 0; i < movers.length; i++){ if(liquid.contains(movers[i])){ //ボールが液体内にあるか判定する PVector dragForce = liquid.drag(movers[i]); movers[i].applyForce(dragForce); //液体の抗力を適用する } float m = 0.1 * movers[i].mass; PVector gravity = new PVector(0,m); //重力 movers[i].applyForce(gravity); movers[i].update(); movers[i].display(); movers[i].checkEdges(); } }
プログラムを実行して確認することをおすすめします。
プログラムの解説
Liquidクラスのdrag()メソッドで流体の抗力を計算します。抗力の大きさと向きを計算し、流体の抗力を求めます。
PVector drag(Mover m){ //大きさを計算する float speed = m.velocity.mag(); float dragMagnitude = c * speed * speed; //力の向きを求める PVector dragForce = m.velocity.copy(); dragForce.mult(-1); dragForce.normalize(); dragForce.mult(dragMagnitude); return dragForce; //抗力を返す }
draw()メソッド内でボールが液体内にあるか判定します。ボールが液体内にあれば、ボールに流体の抗力を適用します。
void draw() { ... for(int i = 0; i < movers.length; i++){ if(liquid.contains(movers[i])){ //ボールが液体内にあるか判定する PVector dragForce = liquid.drag(movers[i]); movers[i].applyForce(dragForce); //液体の抗力を適用する } ... } }
メモ
- copy():ベクトルのコピーを取得し、PVectorを返す
- saveFrame():画像に保存する
//画像として保存する if (frameCount % 20 == 0) saveFrame("frames/####.png");
まとめ
The Nature of Code(PDF版)から流体の抗力について取り上げました。今回は、PVectorを用いて流体の抗力について学びました。引き続き、The Nature of Code(PDF版)の内容を勉強します。