# PixAnvil Barnes-Hut Gravity Tree Demo

Er, JavaScript or WebGL doesn't seem to be running, so basically all you're going to see is the bare code...

 var tree_start=performance.now(); // Build tree over particles var tree=treeBuilder(SPS.particles,0); var lump_start=performance.now(); trace(" Tree build (ms): "+(lump_start-tree_start)); // Compute lumped mass for tree var lumpMass=function(tree) { var center=new vec3(0,0,0); var mass=0.0; if (tree.leaf) { // lump the particles for (var j = 0; j < tree.particles.length; j++) { var pj=tree.particles[j]; mass+=pj.mass; center.pe(pj.P.t(pj.mass)); } } else { // lump the children var L=lumpMass(tree.L); tree.L.lump=L; var R=lumpMass(tree.R); tree.R.lump=R; center.pe(L.center.t(L.mass)); mass+=L.mass; center.pe(R.center.t(R.mass)); mass+=R.mass; } center.te(1.0/mass); return {center:center,mass:mass}; } tree.lump=lumpMass(tree); var gravity_start=performance.now(); trace(" Lumping (ms): "+(gravity_start-lump_start)); // Loop over our particles, and update each one. var holeCenter=new vec3(0.0,0.0,0.0); var G=6.67408e-11; var mHole=1e+11; // black hole in center (Kg) // Return the force on a box due to gravity with this mass, at this distance vector R var gravity=function(m,mBox,R) { var r2=R.lengthSquared(); // r^2 var softening2=0.001; // softening length, squared r2+=softening2; // soften gravity (also eliminates self-gravity) // See: http://www.ifa.hawaii.edu/~barnes/research/smoothing/soft.pdf // Scalar f = G m1 m2 / r^2 // Vector F = normalize(R) * f // Vector F = R * G m1 m2 / r^3 var F=R.t(G*m*mBox/(r2*Math.sqrt(r2))); //var Fmax=10000.0; // force limit (hard to scale with mass) //if (F.length()>Fmax) F=normalize(F).te(Fmax); return F; } var totalGrav=0; for (var i = 0; i < SPS.nbParticles; i++) { var p=SPS.particles[i]; // Gravity from black hole in center var F=gravity(mHole,p.mass,holeCenter.m(p.P)); // Gravity from tree to this particle var particleGravity=function(tree) { var r=length(p.P.m(tree.bound.center)); var ang=tree.bound.diameter/r; if (ang>0.1) { // It's big / close: open the tree if (tree.leaf) { // compute gravity with particles for (var j = 0; j < tree.particles.length; j++) { var pj=tree.particles[j]; F.pe(gravity(pj.mass,p.mass,pj.P.m(p.P))); totalGrav++; } } else { // Recurse to tree.L, tree.R particleGravity(tree.L); particleGravity(tree.R); } } else { // lump the interaction F.pe(gravity(tree.lump.mass,p.mass,tree.lump.center.m(p.P))); } } particleGravity(tree); /* // Gravity from mutual particles (naive N^2) for (var j = 0; j < SPS.nbParticles; j++) { var pj=SPS.particles[j]; F.pe(gravity(pj.mass,p.mass,pj.P.m(p.P))); } */ // Show velocity vectors lib.tempVector(p.P,p.P.p(p.V)); p.V.pe(F.t(lib.dt/p.mass)); // p.V.z-=9.8*lib.dt; // gravity p.P.pe(p.V.t(lib.dt)); if (lib.key['w']) p.rotation.x+=1.0*lib.dt; //if (p.V.z<1.0) // p.color.a=p.V.z; // alpha blend to zero //if (p.V.z<=0.0) // recycleParticle(p); } trace("Total gravity ops: "+totalGrav); var export_start=performance.now(); trace(" Gravity (ms): "+(export_start-gravity_start)); // Export particles to screen: SPS.setParticles(); var end=performance.now(); trace(" Export (ms): "+(end-export_start)); // Make the sky black scene.clearColor = new BABYLON.Color3( 0.0, 0.1, 0.2); // See: https://doc.babylonjs.com/overviews/solid_particle_system var SPS=new BABYLON.SolidParticleSystem("SPS", scene); var box = BABYLON.MeshBuilder.CreateBox("box", {size:0.1}, scene); // Add n copies of the box to the SPS: var n=100; SPS.addShape(box,n); box.dispose(); // don't draw single box var mesh=SPS.buildMesh(); // Update the bounding box as stuff moves SPS.computeBoundingBox=true; /// (re) initialize a particle var recycleParticle = function(p) { // Generate position from a pancake var OK=false; do { p.P.rand(10.0); if (Math.abs(p.P.z)<0.3 // Z axis && p.P.length()>0.2 // hole && p.P.length()<3.0) OK=true; } while (!OK); // Orbit direction var Odir=normalize(cross(p.P,new vec3(0.0,0.0,-1.0))); var orbitalVelocity=Math.sqrt(7.0/p.P.length()); p.V=Odir.te(orbitalVelocity); p.rotation.set(0.0,0.0,0.0); var c=(new vec3).rand(1.0).pe(new vec3(0.5,0.5,0.5)); p.color.r=c.x; p.color.g=c.y; p.color.b=c.z; p.color.a=1.0; } // Initialize all particles: for (var i = 0; i < SPS.nbParticles; i++) { var p=SPS.particles[i]; p.i=i; // Link over to vector names: p.P=p.position; p.V=p.velocity; // Mass is constant p.mass=10.0e7; // Initial setup is treated like recycling. recycleParticle(p); } // Make a bounding volume with these dimensions var makeBound=function(lo,hi) { var r=0,g=0,b=0; var center=lo.p(hi).te(0.5); r=center.x*0.1+0.5, g=center.y*0.1+0.5, b=center.z*0.1+0.5; lib.tempBox(lo,hi,new BABYLON.Color3(r,g,b)); return {lo:lo, hi:hi, center:center, diameter:length(hi.m(lo)) }; } // Sort particles into a tree, recursively var treeBuilder=function(particles,axis) { // Find the bounding box: var big=1.0e30; var lo=new vec3(big,big,big); var hi=new vec3(-big,-big,-big); for (var i=0;i a-b); // sort ascending middle=split[0|((split.length-1)/2)]; // console.log(" -> median is "+middle); } else { // split at geometric center middle=0.5*(particleAxis(lo,axis)+particleAxis(hi,axis)); } // console.log("Split axis "+axis+": length "+split.length+" lo "+particleAxis(lo,axis)+" "+middle+" hi "+particleAxis(hi,axis)); // Split into two children, L and R: var L=[], R=[]; for (var i=0;i