「がんばれない」けど「がんばりたい」

ITエンジニアの仕事のこと。AI、機械学習、ディープラーニング。地頭力。車のこと。

Air and Fluid Resistance|Nature of Code


Air and Fluid Resistance

Friction also occurs in any circumstances and conditions.

[計算式]
Fd = -0.5 * rho * v * v * A * Cd * velocity;

  1. -0.5の0.5部分はコーディングするにあたり、とりあえず置いておいて。マイナスの意味の方が重要。オブジェクトが進む方向に対して逆方向を示す。

  2. rho refers to the density of the liquid, something we don’t need to worry about. We can simplify the problem and consider this to have a constant value of 1. rhoは液体(空気など)の密度を示すみたいだが、1とみなして良いようです。

  3. v は、オブジェクトのスピードを示す。v * vはそのスピードの2乗。

  4. Aは液体(空気)と直接最初に接触し、どかしていくエリア。プログラミング上では省く。

  5. Cdは抵抗係数。Constant係数であり、抵抗の大きい/小さいを示すために決める。

  6. velocityはvelocity。normalizeして使う。

[単純系計算式]
Fd = ||v||(2乗) * Cd * (-1) * velocity;

[Liquidオブジェクト]
前記事での単純なfrictionの派生系として今回はAirとFluid=Liquidという少々複雑なfrictionをシミュレーションするにあたり、Liquidクラスを作り、その位置と抵抗係数およびdrawメソッドを実装する。

これによりLiquidオブジェクトを作れるようになるということは、もう少し言うと、frictionオブジェクトを作る事が出来るということであり、前記事のfrictionはMoverオブジェクトのvelocityと密だったが、それとは切り離された外的frictionとして定義される。では、MoverオブジェクトがLiquid Frictionの影響を受ける時は何時になるのか? それはMoverオブジェクトがLiquidオブジェクトの範囲内にある時なので、MoverクラスにisInside()エソッドを追加している。

  1. bool Mover::isInside(Liquid l)|MoverオブジェクトがLiquidオブジェクト内にあるか?
  2. void Mover::addDrag(Liquid l)|#1 = trueの時に、Moverオブジェクトに外的frictionであるLiquid抵抗を与える
#include 'testApp.h'


#define NUM_MOVERS 30

class Liquid
{
public:
    
    Liquid(){
    }
    
    virtual ~Liquid(){}
    
    //抵抗係数
    float c;
    
    //位置
    float x, y;
    
    //大きさ
    float w, h;
    
    void draw()
    {
        ofFill();
        ofSetColor(175);
        ofRect(x, y, w, h);
    }
};


class Mover
{
    
public:
    
    Mover(){
    }
    
    virtual ~Mover()
    {
    }

    float mass;
    ofVec3f acceleration;
    ofVec3f velocity;
    ofVec3f location;
    
    /***********
     Liquid
     **********/
    bool isInside(Liquid l)
    {
        if (location.x > l.x && location.x < l.x + l.w && location.y > l.y && location.y < l.y + l.h)
        {
            return true;
        }
        return false;
    }
    
    void addDrag(Liquid l)
    {
        
        //speed is the length of the velocity
        float speed = sqrt(velocity.x * velocity.x + velocity.y * velocity.y);
        float dragMagnitude = l.c * speed * speed;

        ofVec3f drag;
        drag = velocity;
        drag *= (-1);
        drag.normalize();
        drag *= dragMagnitude;
        
        applyForces(drag);
    }
    
    void applyForces(ofVec3f force)
    {
        ofVec3f f = force / mass;
        acceleration += f;
    }
    
    void checkEdige()
    {
        
        if (location.x >= ofGetWidth())
        {
            velocity.x *= -1;
            location.x = ofGetWidth();
        }

        if (location.x <= 0.0)
        {
            velocity.x *= -1;
            location.x = 0.0;
        }
        
        if (location.y <= 0)
        {
            velocity.y *= -1;
            location.y = 0.0;
        }

        if (location.y >= ofGetHeight())
        {
            velocity.y *= -1;
            location.y = ofGetHeight();
        }
    }
    
    void setup()
    {
        mass = ofRandom(2,30);
        velocity.set(0.0, 0.0, 0.0);
        location.set(ofRandom(0, ofGetWidth() - mass), 0.0, 0.0);
        acceleration.set(0.0, 0.0, 0.0);
    }
    
    void update()
    {
        velocity += acceleration;
        location += velocity;
        
        acceleration.set(0.0, 0.0, 0.0);
    }
    
    void draw()
    {
        ofSetColor(255);
        ofPushMatrix();
        ofTranslate(location);
        ofCircle(0.0, 0.0, 0.0, mass);
        ofPopMatrix();

        checkEdige();
    }
};


vector<Mover> movers;
ofVec3f gravity;

Liquid liquid;



//--------------------------------------------------------------
void testApp::setup(){
    
    ofSetFrameRate(60);
    ofEnableSmoothing();
    ofSetVerticalSync(true);
    ofBackground(0);
    
    
    movers.resize(NUM_MOVERS);
    
    //自然界のforceをセット
    gravity.set(0.0, 0.2, 0.0);
    
    for (int i = 0; i < NUM_MOVERS; i++)
    {
        Mover *m = &movers[i];
        m->setup();
    }
    
    //Liquid drag
    liquid.x = 0.0;
    liquid.y = ofGetHeight() * 0.5;
    liquid.w = ofGetWidth();
    liquid.h = ofGetHeight() * 0.5;
    liquid.c = 0.1;
}

//--------------------------------------------------------------
void testApp::update(){

    for (int i = 0; i < NUM_MOVERS; i++) {
        
        Mover* m = &movers[i];
        
        //Liquid drag
        if (m->isInside(liquid))
        {
            m->addDrag(liquid);
        }
        
        float mass = m->mass;
        
        //重力フォース
        m->applyForces(gravity * mass);
        

        m->update();
    }
}

//--------------------------------------------------------------
void testApp::draw(){

    liquid.draw();
    for (int i = 0; i < NUM_MOVERS; i++) {
        Mover* m = &movers[i];
        m->draw();
    }
}