力的建模


在〈牛頓運動定律〉中使用 p5.Vector 建立加速度向量,然後乘上物體的質量來取得力的向量,然而力有各種類型,為了區別,這邊先基於 F = ma,針對力定義基礎類別:

class Force {
   constructor(mass, acceleration) {
       this.mass = mass;
       this.acceleration = acceleration;
   }
}

BodyapplyForce 現在接受 Force 實例,取得加速度後來修改速度:

class Body {
    constructor(coordinate, velocity, mass = 1) {
        this.coordinate = coordinate;
        this.velocity = velocity;
        this.mass = mass;
    }

    applyForce(force) {
        this.velocity.add(force.acceleration);
    }

    update() {
        this.coordinate.add(this.velocity);
    }
}

現在〈牛頓運動定律〉中的球反彈範例,可以修改如下:

在上面的實作中,將邊界的反彈抽取為 checkEdges,邊界的反彈其實屬於物體碰撞的問題,碰撞涉及物體的形狀,這是個複雜的議題,目前暫且忽略,單純使用 checkEdges 搪塞一下,實作中在球超出邊界時,套用了反作用力,並馬上調整球的位置,這樣球就不會畫出邊界外。

談到力,最基本的就是重力了吧!以地球的重力而言,G = mgm 為質量,若不在意地球是橢圓的事實,一般都假設 g 為 9.8,可以定義如下:

class Gravity extends Force {
    constructor(mass, g = createVector(0, 2)) {
        super(mass, g);
    }
}

咦?g 不是 9.8 嗎?那是在地球上,在模擬的世界中,g 要怎麼定義,要看你的世界怎麼定義,就這邊的文件來說,只要運動起來的效果像是在有重力的環境就可以了。

來為方才的範例套上重力:

咦?為什麼球不會停下來?現實世界中的球下落後,不是會慢慢停下來嗎?當然,這是因為目前模擬的世界中只有重力,現實世界中還會有空氣阻力、摩擦力等,它們的施力方向與物體的速度方向相反,因此球才會慢慢停下來。

來為模擬的世界加個空氣阻力,空氣是流體的一種,根據維基百科的阻力方程,流體的阻力大小是:

力的建模

其中 v 是速度,A 是參考面積,ρ 為流體密度、CD 為阻力係數,如果你只模擬一種流體,例如空氣,這兩個的值基本上就是取個合理的常數就可以了,也就是在簡單的模擬時,可以簡化為 c * v2 * A。

那麼要怎麼用來建立 Force 實例呢?A 是參考面積,ρ 為流體密度,兩者的乘積隱含著質量,這邊就簡單地以 A 來表示質量,而因為 F = ma,在計算出阻力大小時,除以 A 就可以當作加速度,方向會與指定的速度相反,因此可以定義出 Drag

class Drag extends Force {
    constructor(area, velocity, c = 2) {
        const acceleration = 
                   velocity.copy()
                           .mult(-1)
                           .normalize()
                           .mult(pow(velocity.mag() * c, 2) / area);
        super(area, acceleration);
    }
}

到這邊可以看到,對於力的模擬,視你的世界如何定義,多半都會有一定程度的簡化,當然,如果你有多種流體,在公式的部份就可以更講究。

來試著為方才的範例加上空氣阻力:

因為目前沒有地面摩擦力,範例中只有在球離地面有一定高度時套用重力、空氣阻力等動作,以免球在穩定之前震盪過久而看來不自然。