N bodies

Traces the path of N bodies with gravity.

Created by nxcho on 2019/4/14
174
0

Log in to post a comment.

Canvas.setpenopacity(-0.5);
const turtle = new Turtle();
const MAX_MASS = 25;
const INITIAL_SPEED = 5;
const SUN = true;
const SUN_FIXED = true;
const SUN_MASS = 400;
const STEPS = 1000;
const STEPS_BEFORE_DRAWING = 100; //let the simulation for a while before drawing trejectories
const CIRCLE_STEPS = 10; //The number of simulation steps between each circle is drawn
const DT = 0.01; //Simulation time step constant
const G = 400; //Gravitational constant
const N = 10; //Number of objects not including sun

function cbody(mass,x,y,vx,vy){
    this.mass = mass;
    this.x = x;
    this.y = y;
    this.prevx = x-vx;
    this.prevy = y-vy;
    this.fx = 0
    this.fy = 0
}

cbody.prototype.move = function(){
    const nx = 2 * this.x-this.prevx + this.fx/this.mass*DT;
    const ny = 2 * this.y-this.prevy + this.fy/this.mass*DT;
    this.prevx = this.x;
    this.prevy = this.y;
    this.x = nx;
    this.y = ny;
    this.fx=0;
    this.fy=0;
}

cbody.prototype.addForce = function(fx,fy){
    if(this.fixed){
        return;
    }
    this.fx += fx;
    this.fy += fy;
}


function gravity(body1,body2){
    const dx = body2.x-body1.x;
    const dy = body2.y-body1.y;
    const distsq = dx*dx + dy*dy;
    const dist = Math.sqrt(distsq)
    if(distsq == 0){
        return [0,0]
    }
    const Gm = G*body1.mass*body2.mass/(distsq*dist)
    return [dx*Gm,dy*Gm]
}

function forEachPairIn(collection,func){
    for(i = 0; i < collection.length; i++){
        for(j = i+1; j<collection.length; j++){
            func(collection[i], collection[j]);
        }
    }
}

function smallCircle(x,y,r){
    turtle.radians();
    const steps=30;
    turtle.seth(0);
    turtle.pu();
    turtle.goto(x,y);
    turtle.forward(r);
    turtle.pd();
    turtle.rt(Math.PI/2);
    for(i = 0; i<steps; i++){
        turtle.forward(r*Math.PI*2.0/steps);
        turtle.rt(Math.PI*2.0/steps);
    }
}


let bodies = [];

for(i = 0; i < N; i++){
    let m = 1+Math.random()*MAX_MASS
    let r = 50+Math.random()*25;
    let ang = Math.random()*2*Math.PI;
    let x = r*Math.cos(ang);
    let y = r*Math.sin(ang);

    let vx = -INITIAL_SPEED*Math.sin(ang);
    let vy = INITIAL_SPEED*Math.cos(ang);
    bodies.push(new cbody(m,x,
                            y,
                            vx,
                            vy,));
}
if(SUN){
    sun = new cbody(SUN_MASS,0,0,0,0);
    sun.fixed = SUN_FIXED;
    bodies.push(sun);
}

function walk(i) {
    forEachPairIn(bodies,(body1,body2)=>{
        const [fx,fy] = gravity(body1,body2);
        //console.log("fx: "+fx+",fy: "+fy)
        body1.addForce(fx,fy);
        body2.addForce(-fx,-fy);
    });
    for(b of bodies){
        b.move()
        if(i > STEPS_BEFORE_DRAWING){
            turtle.pu()
            turtle.goto(b.prevx,b.prevy)
            turtle.pd()
            turtle.goto(b.x,b.y)
            if (i%CIRCLE_STEPS == 0){
                smallCircle(b.x,b.y,Math.sqrt(b.mass))
            }
        }
    }

    return i < STEPS;
}