Created
October 11, 2015 17:56
-
-
Save hexmoire/a5e7c3740fa33034a173 to your computer and use it in GitHub Desktop.
a basic simulation of droplets in processing
This file contains 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
/* | |
* | |
* droplets 1 | |
* | |
* coded in Processing 2.2.1 IDE | |
* i appreciate a mention/link back in derivative works | |
* | |
* hexmoire / michael mcknight | |
* http://hexmoire.tumblr.com/ | |
* | |
* this is updated code with a few bugfixes | |
* the original version generated the gif at: | |
* http://hexmoire.tumblr.com/post/130642524324/droplets-1 | |
* | |
*/ | |
//declare globals. | |
ArrayList<Droplet> droplets; | |
int dropletsPerFrame; | |
float dropletMinR; | |
float dropletMaxR; | |
float frameCap; | |
float spawnCap; | |
boolean exporting; | |
//once and only once. (hi daniel shiffman!) | |
void setup(){ | |
//change the size all you want, the sketch will scale to height. | |
size (540,540,P3D); | |
//this code ripped from the processing documentation and then modified a bit. | |
//change "view" to alter the focal length of the camera without changing the visible | |
//bounds at z=0. | |
float view=PI/6; | |
float cZ=height/2. / tan(view/2); | |
beginCamera(); | |
camera(0, 0, cZ, 0, 0, 0, 0, 1, 0); | |
perspective(view, width/(float)height,cZ/10,cZ*10); | |
scale (height/100.); | |
endCamera(); | |
//instantiate/initialize the globals. | |
droplets = new ArrayList<Droplet>(); | |
//edit these initial values to make interesting things happen. | |
dropletsPerFrame=3000; | |
dropletMinR=.2; | |
dropletMaxR=.5; | |
frameCap=60; | |
//spawnCap is not what it sounds like - it's the frame when spawning new droplets stops. | |
spawnCap=45; | |
//setting exporting to true will spit out a gif of every frame | |
exporting=false; | |
//get ready to draw a whole bunch of spheres the same way every time. | |
noStroke(); | |
fill(255); | |
} | |
//over and over and over again. (hello again, mr. shiffman.) | |
void draw(){ | |
//clear screen, | |
background(0); | |
//spawn some droplets | |
if(frameCount<spawnCap){ | |
for(int k=0;k<dropletsPerFrame;k++){ | |
//place a significant portion beyond screen boundary to ensure some | |
//interaction and causation out of view. | |
droplets.add(new Droplet(new PVector( | |
random(-70,70)*width/height, | |
random(-70,70),0), | |
random(dropletMinR,dropletMaxR) | |
)); | |
} | |
} | |
//merge any droplets that touch. | |
for(int i=droplets.size()-1;i>=0;i--){ | |
for(int j=i-1;j>=0;j--){ | |
Droplet di=droplets.get(i); | |
Droplet dj=droplets.get(j); | |
if(dj.intersect(di)){ | |
dj.absorb(di); | |
droplets.remove(i); | |
//goofy index math to keep intersection checks moving through the list | |
//without skipping or double checking. there will be missed intersections when | |
//droplet j grows to intersect a particle it was already checked against. | |
//it will be taken care of next frame. | |
i--; | |
j=i; | |
} | |
} | |
} | |
//if no longer spawning, remove a group of droplets. | |
//group size is linear progress towards zero droplets in last frame. | |
if(frameCount>=spawnCap){ | |
int c=(int)(droplets.size()/(frameCap+1-frameCount)); | |
c=min(c,droplets.size()); | |
for (int i=0;i<c;i++){ | |
droplets.remove(0); | |
} | |
} | |
//draw the droplets as spheres. | |
for(int i=droplets.size()-1;i>=0;i--){ | |
Droplet d=droplets.get(i); | |
pushMatrix(); | |
translate(d.pos.x,d.pos.y,d.pos.z); | |
sphere(d.r); | |
popMatrix(); | |
} | |
//save frame if exporting. | |
if(exporting) { | |
saveFrame("frames/frame"+nf(frameCount,3)+".gif"); | |
} | |
//quit at frameCap. | |
if(frameCount==frameCap) exit(); | |
} | |
//the droplet itself. | |
class Droplet{ | |
//it would be trivial to add velocity. | |
protected PVector pos; | |
protected float v; | |
protected float r; | |
//constructor - maybe it would be better to set volume than radius, depending on the application. | |
Droplet(PVector _pos, float _r){ | |
pos = _pos; | |
setR(_r); | |
} | |
//set radius and update volume. | |
void setR(float _r){ | |
r=_r; | |
v=4./3*PI*pow(r,3); | |
} | |
//set volume and update radius. | |
void setV(float _v){ | |
v=_v; | |
r=pow(v/(4./3*PI),1./3); | |
} | |
//check for intersection, return boolean. | |
public boolean intersect(Droplet d){ | |
return ( pos.dist(d.pos) < r+d.r ); | |
} | |
//absorb a droplet | |
public void absorb(Droplet d){ | |
//use PVector lerp to move surviving drop to center of mass. | |
pos.lerp ( d.pos , d.v / (v+d.v) ); | |
//set new volume | |
setV ( v+d.v ); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment