UNPKG

cannon

Version:

A lightweight 3D physics engine written in JavaScript.

214 lines (180 loc) 6.27 kB
module.exports = SPHSystem; var Shape = require('../shapes/Shape'); var Vec3 = require('../math/Vec3'); var Quaternion = require('../math/Quaternion'); var Particle = require('../shapes/Particle'); var Body = require('../objects/Body'); var Material = require('../material/Material'); /** * Smoothed-particle hydrodynamics system * @class SPHSystem * @constructor */ function SPHSystem(){ this.particles = []; /** * Density of the system (kg/m3). * @property {number} density */ this.density = 1; /** * Distance below which two particles are considered to be neighbors. * It should be adjusted so there are about 15-20 neighbor particles within this radius. * @property {number} smoothingRadius */ this.smoothingRadius = 1; this.speedOfSound = 1; /** * Viscosity of the system. * @property {number} viscosity */ this.viscosity = 0.01; this.eps = 0.000001; // Stuff Computed per particle this.pressures = []; this.densities = []; this.neighbors = []; } /** * Add a particle to the system. * @method add * @param {Body} particle */ SPHSystem.prototype.add = function(particle){ this.particles.push(particle); if(this.neighbors.length < this.particles.length){ this.neighbors.push([]); } }; /** * Remove a particle from the system. * @method remove * @param {Body} particle */ SPHSystem.prototype.remove = function(particle){ var idx = this.particles.indexOf(particle); if(idx !== -1){ this.particles.splice(idx,1); if(this.neighbors.length > this.particles.length){ this.neighbors.pop(); } } }; /** * Get neighbors within smoothing volume, save in the array neighbors * @method getNeighbors * @param {Body} particle * @param {Array} neighbors */ var SPHSystem_getNeighbors_dist = new Vec3(); SPHSystem.prototype.getNeighbors = function(particle,neighbors){ var N = this.particles.length, id = particle.id, R2 = this.smoothingRadius * this.smoothingRadius, dist = SPHSystem_getNeighbors_dist; for(var i=0; i!==N; i++){ var p = this.particles[i]; p.position.vsub(particle.position,dist); if(id!==p.id && dist.norm2() < R2){ neighbors.push(p); } } }; // Temp vectors for calculation var SPHSystem_update_dist = new Vec3(), SPHSystem_update_a_pressure = new Vec3(), SPHSystem_update_a_visc = new Vec3(), SPHSystem_update_gradW = new Vec3(), SPHSystem_update_r_vec = new Vec3(), SPHSystem_update_u = new Vec3(); // Relative velocity SPHSystem.prototype.update = function(){ var N = this.particles.length, dist = SPHSystem_update_dist, cs = this.speedOfSound, eps = this.eps; for(var i=0; i!==N; i++){ var p = this.particles[i]; // Current particle var neighbors = this.neighbors[i]; // Get neighbors neighbors.length = 0; this.getNeighbors(p,neighbors); neighbors.push(this.particles[i]); // Add current too var numNeighbors = neighbors.length; // Accumulate density for the particle var sum = 0.0; for(var j=0; j!==numNeighbors; j++){ //printf("Current particle has position %f %f %f\n",objects[id].pos.x(),objects[id].pos.y(),objects[id].pos.z()); p.position.vsub(neighbors[j].position, dist); var len = dist.norm(); var weight = this.w(len); sum += neighbors[j].mass * weight; } // Save this.densities[i] = sum; this.pressures[i] = cs * cs * (this.densities[i] - this.density); } // Add forces // Sum to these accelerations var a_pressure= SPHSystem_update_a_pressure; var a_visc = SPHSystem_update_a_visc; var gradW = SPHSystem_update_gradW; var r_vec = SPHSystem_update_r_vec; var u = SPHSystem_update_u; for(var i=0; i!==N; i++){ var particle = this.particles[i]; a_pressure.set(0,0,0); a_visc.set(0,0,0); // Init vars var Pij; var nabla; var Vij; // Sum up for all other neighbors var neighbors = this.neighbors[i]; var numNeighbors = neighbors.length; //printf("Neighbors: "); for(var j=0; j!==numNeighbors; j++){ var neighbor = neighbors[j]; //printf("%d ",nj); // Get r once for all.. particle.position.vsub(neighbor.position,r_vec); var r = r_vec.norm(); // Pressure contribution Pij = -neighbor.mass * (this.pressures[i] / (this.densities[i]*this.densities[i] + eps) + this.pressures[j] / (this.densities[j]*this.densities[j] + eps)); this.gradw(r_vec, gradW); // Add to pressure acceleration gradW.mult(Pij , gradW); a_pressure.vadd(gradW, a_pressure); // Viscosity contribution neighbor.velocity.vsub(particle.velocity, u); u.mult( 1.0 / (0.0001+this.densities[i] * this.densities[j]) * this.viscosity * neighbor.mass , u ); nabla = this.nablaw(r); u.mult(nabla,u); // Add to viscosity acceleration a_visc.vadd( u, a_visc ); } // Calculate force a_visc.mult(particle.mass, a_visc); a_pressure.mult(particle.mass, a_pressure); // Add force to particles particle.force.vadd(a_visc, particle.force); particle.force.vadd(a_pressure, particle.force); } }; // Calculate the weight using the W(r) weightfunction SPHSystem.prototype.w = function(r){ // 315 var h = this.smoothingRadius; return 315.0/(64.0*Math.PI*Math.pow(h,9)) * Math.pow(h*h-r*r,3); }; // calculate gradient of the weight function SPHSystem.prototype.gradw = function(rVec,resultVec){ var r = rVec.norm(), h = this.smoothingRadius; rVec.mult(945.0/(32.0*Math.PI*Math.pow(h,9)) * Math.pow((h*h-r*r),2) , resultVec); }; // Calculate nabla(W) SPHSystem.prototype.nablaw = function(r){ var h = this.smoothingRadius; var nabla = 945.0/(32.0*Math.PI*Math.pow(h,9)) * (h*h-r*r)*(7*r*r - 3*h*h); return nabla; };