Skip to content

Instantly share code, notes, and snippets.

@coryk135
Last active December 2, 2015 02:08
Show Gist options
  • Save coryk135/d6850ef788076a528f89 to your computer and use it in GitHub Desktop.
Save coryk135/d6850ef788076a528f89 to your computer and use it in GitHub Desktop.
Particle simulation
var l = document.body.children.length
for(var i = 0; i < l; i++){ document.body.children[0].remove() }
var c = document.createElement("canvas")
c.width = 400;
c.height = 400;
c.style.width = "800px"
c.style.height = "800px"
document.body.appendChild(c)
var style = document.createElement('style')
style.textContent = 'pre {display: block;\
font-family: monospace;\
font-size: 9.75px;\
height: 230px;\
margin-bottom: 9.75px;\
margin-left: 0px;\
margin-right: 0px;\
margin-top: 9.75px;\
white-space: pre;\
color: black;}\
'
document.head.appendChild(style)
var pre = document.createElement('pre')
document.body.appendChild(pre)
// function VectorSpace(){
// this.entities = []
// }
// function Vec2d(vec){
// this.vec = vec || [0,0]
// this.add = function(v){
// return [this.vec[0] + v[0], this.vec[1] + v[1]]
// }
// this.scale = function(a){
// return [this.vec[0] * a, this.vec[1] * a]
// }
// }
GRAVITY = 9.8
CO_STATIC_FRICTION = .065
CO_KINETIC_FRICTION = .045
FLUID_DENSITY = .01
function Particle(){
this.color = '#ff0000'
this.s = [0,0] // new Vec2d(); // position
this.v = [0,0] // new Vec2d(); // velocity
this.a = [0,0] // new Vec2d(); // acceleration
this.forces = [[0,0]]
this.mass = 1;
this.speed = function(){
return Math.sqrt(this.v[0]*this.v[0] + this.v[1]*this.v[1])
}
this.direction = function(){
return Math.atan2(this.v[1], this.v[0])
}
this.step = function(dt){
this.a[0] = 0;
this.a[1] = 0;
for(var i = 0; i < this.forces.length; i++){
this.a[0] += this.forces[i][0]/this.mass
this.a[1] += this.forces[i][1]/this.mass
}
// apply friction
var speed = this.speed()
var fric = 0;
var fric_dir = this.direction() + Math.PI
// if(speed > 0 && speed < 5) {
// fric = this.mass*GRAVITY*CO_STATIC_FRICTION
// } else if(speed > 5) {
// fric = this.mass*GRAVITY*CO_KINETIC_FRICTION
// }
// if(fric) {
// fric = [fric * Math.cos(fric_dir), fric * Math.sin(fric_dir)]
// this.a[0] += fric[0]/this.mass
// this.a[1] += fric[1]/this.mass
// }
// apply drag
var p = FLUID_DENSITY
var v = this.speed()
var Cd = 1
var A = 1
var Fd = (p*v*v*Cd*A)/2
Fd = [Fd * Math.cos(fric_dir), Fd * Math.sin(fric_dir)]
this.a[0] += Fd[0]/this.mass
this.a[1] += Fd[1]/this.mass
this.s[0] += this.v[0]*dt + this.a[0]*dt*dt/2
this.s[1] += this.v[1]*dt + this.a[1]*dt*dt/2
if(mousedown){
if(this.s[0] < 0 || this.s[0] > canvasWidth || this.s[1] < 0 || this.s[1] > canvasHeight){
this.s = [mousePos[0] + getRandom(-1,1), mousePos[1] + getRandom(-1, 1)]
}
} else {
this.s[0] = (this.s[0] + canvasWidth) % canvasWidth
this.s[1] = (this.s[1] + canvasHeight) % canvasHeight
}
this.v[0] += this.a[0]*dt
this.v[1] += this.a[1]*dt
}
this.applyForce = function(f){
var thisForce = [this.mass*this.a[0] + f[0], this.mass*this.a[1] + f[1]];
this.a[0] = thisForce[0]/this.mass
this.a[1] = thisForce[1]/this.mass
}
this.maxTurnAngle = 45/180*Math.PI;
}
function getRandom(min, max) {
return Math.random() * (max - min) + min;
}
var canvas = document.querySelector("canvas");
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;
var ctx = canvas.getContext("2d");
var canvasData = ctx.getImageData(0, 0, canvasWidth, canvasHeight);
var particles = [new Particle()]
for(var i = 0; i < 9999; i ++){
particles.push(new Particle())
}
var ambient_force = [getRandom(-5,5), getRandom(-5,5)]
function update(dt){
var ep1 = getRandom(-4,4)
var ep2 = getRandom(-4,4)
ambient_force[0] = (ambient_force[0] + ep1 > 0 ? 1 : -1) *
Math.min(14, Math.abs(ambient_force[0] + ep1))
ambient_force[1] = (ambient_force[1] + ep2 > 0 ? 1 : -1) *
Math.min(14, Math.abs(ambient_force[1] + ep2))
for(var i = 0; i < particles.length; i++){
var p = particles[i]
// if any elements are null, set them to 0
if([p.s[0],p.s[1],p.v[0],p.v[1],p.a[0],p.a[1]]
.some(function (element, index, array) {return element == null || element == undefined})
) {
p.s = [0,0]; p.v = [0,0]; p.a = [0,0]
}
//p.applyForce([getRandom(-1,1), getRandom(-1,1)])
p.forces[0] = [ambient_force[0] + getRandom(-2,2)*4, ambient_force[1] + getRandom(-2,2)*4]
if((mousePos == null || clickPos == null) && p.forces.length > 1){
p.forces.pop()
} else if(mousePos != null && clickPos != null) {
var G = 40 // gravitational constant
var mass2 = 50 // mass of mouse
var r = .0000001 + Math.sqrt(Math.pow(p.s[0]-mousePos[0], 2) + Math.pow(p.s[1]-mousePos[1], 2))
var Fg_mag = G * p.mass * mass2 / r
var Fg = [Fg_mag * (mousePos[0] - p.s[0]), Fg_mag * (mousePos[1] - p.s[1])]
if(mousedown) Fg = [-Fg[0], -Fg[1]]
p.forces[1] = Fg
}
p.step(dt)
}
}
function draw(){
ctx.fillStyle = 'rgba(0, 0, 0, 0.2)'
ctx.fillRect(0, 0, canvasWidth, canvasHeight)
ctx.fillStyle = '#ff0000'
canvasData = ctx.getImageData(0, 0, canvasWidth, canvasHeight);
for(var i = 0; i < particles.length; i++){
drawPixel(Math.floor(particles[i].s[0]), Math.floor(particles[i].s[1]), 255, 0, 0, 255)
}
updateCanvas();
pre.textContent = JSON.stringify(particles[0], null, '\t')
}
// That's how you define the value of a pixel //
function drawPixel (x, y, r, g, b, a) {
var index = (x + y * canvasWidth) * 4;
canvasData.data[index + 0] = r;
canvasData.data[index + 1] = g;
canvasData.data[index + 2] = b;
canvasData.data[index + 3] = a;
}
// That's how you update the canvas, so that your //
// modification are taken in consideration //
function updateCanvas() {
ctx.putImageData(canvasData, 0, 0);
}
// drawPixel(1, 1, 255, 0, 0, 255);
// drawPixel(1, 2, 255, 0, 0, 255);
// drawPixel(1, 3, 255, 0, 0, 255);
// updateCanvas();
var lastTime = null;
function step(timestamp) {
if (!lastTime) lastTime = timestamp;
var dt = timestamp - lastTime;
lastTime = timestamp;
update(dt/1000)
draw()
// if (progress < 2000) {
window.requestAnimationFrame(step);
// }
}
window.requestAnimationFrame(step);
var clickPos = null;
var mousePos = null;
var mousedown = false;
canvas.addEventListener('mousemove', function(event) {
mousePos = [event.x/2, event.y/2]
}, false);
canvas.addEventListener('mouseout', function(event) {
clickPos = null
mousePos = null
mousedown = false
}, false);
canvas.addEventListener('mousedown', function(event) {
mousedown = true
clickPos = [event.x/2, event.y/2]
}, false);
canvas.addEventListener('mouseup', function(event) {
mousedown = false
}, false);
/*
pos = pos + vel * dtime
particles = createParticles(100)
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment