Multi Fluids


Direct Link

Source code

<!DOCTYPE html>
<html>
<head>
<title>Fluid dynamics - Interactive DHTML demos</title>
<meta name="Author" content="Gerard Ferrandez at http://www.dhteumeuleu.com">
<style type="text/css">
body {
margin: 0px;
padding: 0px;
background: #111;
width: 100%;
height: 100%;
color:#fff;
font-size:12px;
font-family: arial,verdana;
}
#screen {
position: absolute;
left: 50%;
top: 50%;
background: #000;
overflow: hidden;
width: 400px;
height: 400px;
margin-left:-200px;
margin-top:-200px;
outline: #222 solid 1px;
}
#screen img {
position: absolute;
}
#particle, #particle2 {
visibility: hidden;
}
#fps {
position: absolute;
left: 50%;
top: 50%;
width: 400px;
margin-top: 205px;
margin-left:-200px;
text-align: right;
color:#666;
}
</style>
<script type="text/javascript">
// ===============================================================================
// ***** Particle-based JS multi-fluids Simulation *****
// -----------------------------------------------------------------------------
// JavaScript: Gerard Ferrandez - 14 May 2011
// http://www.dhteumeuleu.com/
// use under a CC-BY-NC license
// -----------------------------------------------------------------------------
// Freely adapted from Grant Kot visco-elastic Java implementation
// http://kotsoft.googlepages.com/Viscoelastic2.html
// Research paper -
// http://www.iro.umontreal.ca/labs/infographie/papers/Clavet-2005-PVFS/pvfs.pdf
// ===============================================================================
"use strict";
(function () {
// ----- private vars -----
var canvas, ctx, nw, nh, nx, ny, nbX, nbY,
particles = [], grid = [], neighbors = [], mousePressed,
mouseX = 0, mouseY = 0, fps = 0, nParticles = 0, gridResolution, rad;
// ----- Particle prototype -----
var Particle = function (type, img, x, y) {
this.type = type;
this.particleImage = img;
this.width = img.width * 0.5;
this.height = img.height * 0.5;
this.x = x;
this.y = y;
this.pprevx = x;
this.pprevy = y;
this.velx = 0;
this.vely = 0;
};
Particle.prototype.pass1 = function () {
// ----- maintain spatial hashing grid ------
var g = grid[Math.round(this.y / gridResolution) * nbX + Math.round(this.x / gridResolution)];
g.neighborsParticles[g.len++] = this;
// ----- mouse pressed -----
if (mousePressed) {
var vx = this.x - mouseX;
var vy = this.y - mouseY;
var vlen = Math.sqrt(vx * vx + vy * vy);
if (vlen >= 1 && vlen < 60) {
this.velx += 0.5 * rad * (vx / vlen) / vlen;
this.vely += 0.5 * rad * (vy / vlen) / vlen;
}
}
// apply gravity
this.vely += 0.01;
// save previous position
this.pprevx = this.x;
this.pprevy = this.y;
// advance to predicted position
this.x += this.velx;
this.y += this.vely;
}
// ----- Double Density Relaxation Algorithm -----
Particle.prototype.pass2 = function () {
var pressure = 0, presnear = 0, nl = 0;
// ----- get grid position -----
var xc = Math.round(this.x / gridResolution);
var yc = Math.round(this.y / gridResolution);
// ----- 3 x 3 grid cells -----
for (var xd = -1; xd < 2; xd++){
for (var yd = -1; yd < 2; yd++){
var h = grid[(yc + yd) * nbX + (xc + xd)];
if (h && h.len) {
// ----- foreach neighbors pair -----
for (var a = 0, l = h.len; a < l; a++) {
var pn = h.neighborsParticles[a];
if (pn != this) {
var vx = pn.x - this.x;
var vy = pn.y - this.y;
var vlen = Math.sqrt(vx * vx + vy * vy);
if (vlen < rad){
// ----- compute density and near-density -----
var q = 1 - (vlen / rad);
pressure += q * q; // quadratic spike
presnear += q * q * q; // cubic spike
pn.q = q;
pn.vxl = (vx / vlen) * q;
pn.vyl = (vy / vlen) * q;
neighbors[nl++] = pn;
}
}
}
}
}
}
// ----- screen limits -----
if (this.x < rad){
var q = 1 - Math.abs(this.x / rad);
this.x += q * q * 0.5;
} else if (this.x > nw - rad){
var q = 1 - Math.abs((nw - this.x) / rad);
this.x -= q * q * 0.5;
}
if (this.y < rad){
var q = 1 - Math.abs(this.y / rad);
this.y += q * q * 0.5;
} else if (this.y > nh - rad){
var q = 1 - Math.abs((nh - this.y) / rad);
this.y -= q * q * 0.5;
}
if (this.x < this.width) this.x = this.width; else if (this.x > nw - this.width) this.x = nw - this.width;
if (this.y < this.height) this.y = this.height; else if (this.y > nh - this.height) this.y = nh - this.height;
// ----- second pass of the relaxation -----
pressure = (pressure - 3) * 0.5;
presnear *= 0.5;
for (var a = 0; a < nl; a++){
var np = neighbors[a];
// apply displacements
var p = presnear * np.q + pressure + (this.type != np.type) * 0.35;
var dx = np.vxl * p;
var dy = np.vyl * p;
np.x += dx;
np.y += dy;
this.x -= dx;
this.y -= dy;
}
}
Particle.prototype.pass3 = function () {
// use previous position to compute next velocity
this.velx = this.x - this.pprevx;
this.vely = this.y - this.pprevy;
// draw particles - HTML5 canvas drawImage
ctx.drawImage(this.particleImage, this.x - this.width, this.y - this.height);
}
// ----- main loop -----
var run = function () {
ctx.clearRect(0, 0, nw, nh);
// ----- reset grid -----
for (var i = 0, l = nbX * nbY; i < l; i++) grid[i].len = 0;
// ----- simulation passes -----
for (var i = 0; i < nParticles; i++) particles[i].pass1();
for (var i = 0; i < nParticles; i++) particles[i].pass2();
for (var i = 0; i < nParticles; i++) particles[i].pass3();
fps++;
};
////////////////////////////////////////////////////////////////////////////////////////
// ----- initialization -----
var init = function (params) {
// ----- entry parameters -----
gridResolution = params.gridResolution;
rad = params.rad;
// ----- DOM elements -----
var d = canvas = document.getElementById("screen");
ctx = canvas.getContext("2d");
// ---- canvas dimensions ----
nw = d.offsetWidth;
nh = d.offsetHeight;
canvas.width = nw;
canvas.height = nh;
for (nx = 0, ny = 0; d != null; d = d.offsetParent) {
nx += d.offsetLeft;
ny += d.offsetTop;
}
nbX = Math.round(nw / gridResolution) + 1;
nbY = Math.round(nh / gridResolution) + 1;
// ----- init grid (static for better js performance) -----
for (var i = 0; i < nbX * nbY; i++) {
grid[i] = {
len: 0,
neighborsParticles: []
}
};
// ----- create particles -----
for (var k = 0; k < params.numParticles.length; k++) {
for (var i = 0; i < params.numParticles[k]; i++ ) {
particles[nParticles++] = new Particle(
k,
document.getElementById(params.imgParticles[k]),
Math.random() * nw,
Math.random() * nh
);
}
}
// ----- mouse events -----
document.onselectstart = function () { return false; }
document.ondrag = function () { return false; }
document.addEventListener('mousedown', function (e) { mousePressed = true; }, false);
document.addEventListener('mouseup', function (e) { mousePressed = false; }, false);
document.addEventListener('mousemove', function (e) {
mouseX = e.clientX - nx;
mouseY = e.clientY - ny;
}, false);
// ----- start engine -----
setInterval(run, 16);
// ----- frames per second -----
setInterval(function () {
document.getElementById('fps').innerHTML = fps + " fps";
fps = 0;
}, 1000);
};
return {
////////////////////////////////////////////////////////////////////////////
// ---- launch script -----
init : function (params) {
window.addEventListener('load', function () {
init(params);
}, false);
}
}
})().init({
// ----- init parameters -----
numParticles: [500, 500],
imgParticles: ["particle", "particle2"],
gridResolution: 20,
rad: 22
});
</script>
</head>
<body>
<canvas id="screen">If the brute force approach doesn't work, you're obviously not using enough brute force!</canvas>
<div id="fps"></div>
<img id = "particle" src="../images/bouton_green.png">
<img id = "particle2" src="../images/bouton_orange.png">
</body>
</html>

Tagged with:
 

Feed updates subscription

Enter your email address:

Delivered by FeedBurner

Donate

Want to give me some extra encouragement ?

License

Creative Commons License

Except where otherwise noted, all Javascript code on this site is licensed under a Creative Commons License.