投稿記事

ProcessingJS 勉強 - Forces

28 Jun 2016


Khan Academy での学習

Forces

学習元 Forces

var Mover = function() {
    // 簡単にするため質量は 1 とする
    this.mass = 1;
    this.position = new processing.PVector(30, 30);
    this.velocity = new processing.PVector(0, 0);
    this.acceleration = new processing.PVector(0, 0);
};

// ニュートンの第2法則をシミュレートする
// force を受取り, 質量で割り, 加速度に加える
Mover.prototype.applyForce = function(force) {
    var f = processing.PVector.div(force, this.mass);
    this.acceleration.add(f);
};
  
Mover.prototype.update = function() {
    // Vectorsの最初の例でシミュレートする
    this.velocity.add(this.acceleration);
    this.position.add(this.velocity);
    // 毎回加速度をクリア
    this.acceleration.mult(0);
};

Mover.prototype.display = function() {
    processing.stroke(0);
    processing.strokeWeight(2);
    processing.fill(255, 255, 255, 127);
    // 質量により、サイズを変更する
    processing.ellipse(this.position.x, this.position.y, this.mass*30, this.mass*30);
};

// mover をエッジでバウンドさせるため、速度を変えています
Mover.prototype.checkEdges = function() {
    if (this.position.x > width) {
        this.position.x = width;
        this.velocity.x *= -1;
    } else if (this.position.x < 0) {
        this.velocity.x *= -1;
        this.position.x = 0;
    }
    if (this.position.y > height) {
        this.velocity.y *= -1;
        this.position.y = height;
    }
};
// setup
processing.setup = function(){
    // canvas size 
    processing.size(width,height);
};
// mover生成
var m = new Mover(); 

processing.draw = function() {
    processing.background(66, 66, 66);
    //風と重力
    var wind = new processing.PVector(0.01, 0);
    var gravity = new processing.PVector(0, 0.1);
    m.applyForce(wind);
    m.applyForce(gravity);
    
    m.update();
    m.display();
    m.checkEdges();
}; 


Many objects

var Mover = function(m, x, y) {
    this.mass = m;
    this.position = new processing.PVector(x, y);
    this.velocity = new processing.PVector(0, 0);
    this.acceleration = new processing.PVector(0, 0);
};

.....

Mover.prototype.display = function() {
    processing.stroke(0);
    processing.strokeWeight(2);
    processing.fill(255, 255, 255, 127);
    processing.ellipse(this.position.x, this.position.y, this.mass*30, tmass*30);
};
....
var m = [];
for (var i = 0; i < 20; i++) {
     m[i] = new Mover(processing.random(0.1, 5), 0, 0); 
};

.....

Acceleration

Many objects 2


Mover.prototype.calculateWallForce = function() {
    var fx = 0;
    var fy = 0;
    if (this.position.x > width) {
        fx = -1;
    } else if (this.position.x < 0) {
        fx =1; 
    }
    if (this.position.y > height) {
        fy = -1;
    } else if (this.position.y < 0) {
        fy = 1;
    }
    return new processing.PVector(fx,fy);
};
....
processing.draw = function() {
    processing.background(66, 66, 66);
    for (var i = 0; i < m.length; i++) {
        var wind = new processing.PVector(0.01, 0);
        var gravity = new processing.PVector(0, 0.1);
 
        m[i].applyForce(wind);
        m[i].applyForce(gravity);
        m[i].applyForce(m[i].calculateWallForce());
        m[i].update();
        m[i].display();
    };
};

Modeling gravity and friction

gravity


processing.draw = function() {
    processing.background(66, 66, 66);
    
    for (var i = 0; i < m.length; i++) {
        var wind = new processing.PVector(0.01, 0);
        
        var gravity = new processing.PVector(0, 0.1 * m[i].mass);
        
        m[i].applyForce(wind);
        m[i].applyForce(gravity);
        m[i].update();
        m[i].display();
        m[i].checkEdges();
    };
}; 

friction


for (var i = 0; i < movers.length; i++) {
    var wind = new processing.PVector(0.01, 0);
    var gravity = new processing.PVector(0, 0.1*movers[i].mass);
    
    var c = 0.01; // 摩擦係数
    var normal = 1; // 垂直抗力
    var frictionMag = c * normal; // 抗力
    var friction = movers[i].velocity.get(); // 速度の取得
    friction.mult(-1); // 速度の向きを反転
    friction.normalize(); // 単位ベクトル化
    friction.mult(frictionMag); // 摩擦力の大きさ

    movers[i].applyForce(friction); // 摩擦力を適用
    movers[i].applyForce(wind); // 風力を適用
    movers[i].applyForce(gravity); // 重力を適用
    movers[i].update(); 
    movers[i].display();
    movers[i].checkEdges();
}
 

Air and fluid resistance


$$F_{d} = -\frac{1}{2}\rho v^{2}AC_{d}\hat{v}$$ $$F_{d} : Drag \quad force(抗力)$$ $$-\frac{1}{2} : 定数$$ $$\rho : 流体の密度$$ $$v^2 : v は物体のスピードで、速度ベクトルの大きさ -- velocity.mag()$$ $$A : 流体を押す物体の正面の面積$$ $$C_{d} : 抗力係数$$ $$_hat{v} : 速度の単位ベクトル -- velocity.normalize()$$
// Calculate drag force
Liquid.prototype.calculateDrag = function(m) {
    // Magnitude is coefficient * speed squared
    var speed = m.velocity.mag();
    var dragMagnitude = this.c * speed * speed;
    
    // Direction is inverse of velocity
    var dragForce = m.velocity.get();
    dragForce.mult(-1);
    
    // Scale according to magnitude
    // dragForce.setMag(dragMagnitude);
    dragForce.normalize();
    dragForce.mult(dragMagnitude);
    return dragForce;
};
....
    for (var i = 0; i < movers.length; i++) {
    
        // Is the Mover in the liquid?
        if (liquid.contains(movers[i])) {
            // Calculate drag force
            var dragForce = liquid.calculateDrag(movers[i]);
            // Apply drag force to Mover
            movers[i].applyForce(dragForce);
        }
        ....

Gravitational attraction


$$F=G\frac{m_{1} \dot m_{2}}{r^2}\hat{r}$$ $$F:引力$$ $$G:万有引力定数=6.67408x10^{-11}m^{3}kg^{-1}s^{-2}$$ $$m_{1},m_{2}:物体の質量$$ $$r:物体間の距離$$ $$\hat{r}:距離の単位ベクトル$$
Attractor.prototype.calculateAttraction = function(mover) {
    // Calculate direction of force
    var force = processing.PVector.sub(this.position, mover.position);
    // Distance between objects       
    var distance = force.mag();
    // Limiting the distance to eliminate "extreme" results
    // for very close or very far objects                            
    distance = processing.constrain(distance, 5, 25);
    // Normalize vector                    
    force.normalize();
    // Calculate gravitional force magnitude  
    var strength = (this.G * this.mass * mover.mass) / (distance * distance);
    // Get force vector --> magnitude * direction
    force.mult(strength);
    return force;
};

Gravitational attraction


 
    

Mutual attraction


Mover.prototype.calculateAttraction = function(m, i) {
    // Calculate direction of force
    var force = processing.PVector.sub(this.position, m.position);
    // Distance between objects
    var distance = force.mag();
    // Limiting the distance to eliminate "extreme" results for very closevery far objects
    distance = processing.constrain(distance, 5.0, 25.0);
    // Normalize vector (distance doesn't matter here, we just want tvector for direction                            
    force.normalize();
    // Calculate gravitional force magnitude
    var strength = (G * this.mass * m.mass) / (distance * distance);
    // Get force vector --> magnitude * direction
    force.mult(strength);
    return force;
};

...
 
processing.draw = function() {
    processing.background(50, 50, 50);

    for (var i = 0; i < movers.length; i++) {
        for (var j = 0; j < movers.length; j++) {
            if (i !== j) {
                var force = movers[j].calculateAttraction(movers[i]);
                movers[i].applyForce(force);
            }
        }

        movers[i].update();
        movers[i].display();
    }
};