Last active
December 2, 2015 02:08
-
-
Save coryk135/d6850ef788076a528f89 to your computer and use it in GitHub Desktop.
Particle simulation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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