Skip to content

Instantly share code, notes, and snippets.

@nathanielarking
Created May 17, 2020 03:48
Show Gist options
  • Save nathanielarking/b680a751eef243ade95015286bbc60ba to your computer and use it in GitHub Desktop.
Save nathanielarking/b680a751eef243ade95015286bbc60ba to your computer and use it in GitHub Desktop.
Simple fluid simulation in processing
class Entity{
boolean spawned = false;
boolean freeze = false;
PVector pos, vel, acc;
float mass, size;
int[] colour = new int[3];
Entity(){
pos = new PVector();
vel = new PVector();
acc = new PVector(0, 0);
mass = 2;
size = random(20, 60);
for(int i = 0; i < 3; i++){
colour[i] = int(random(50, 255));
}
}
void randomize(){
size = random(20, 100);
for(int i = 0; i < 3; i++){
colour[i] = int(random(50, 255));
}
}
void updateEntity(){
vel.add(acc);
pos.add(vel);
acc.mult(0);
}
void collideEntity(int mode){
if(mode == 0){
if(pos.x > (width - size/2)){
pos.x = (width - size/2);
vel.x *= -1;
}else if(pos.x < (0 + size/2)){
pos.x = (0 + size/2);
vel.x *= -1;
}
if(pos.y > (height - size/2)){
pos.y = (height - size/2);
vel.y *= -1;
}else if(pos.y < (0 + size/2)){
pos.y = (0 + size/2);
vel.y *= -1;
}
}else{
if(pos.x < 0) pos.x = width;
if(pos.x > width) pos.x = 0;
if(pos.y < 0) pos.y = height;
if(pos.y > height) pos.y = 0;
}
}
void displayEntity(){
stroke(0);
strokeWeight(2);
fill(colour[0], colour[1], colour[2]);
if(spawned) ellipse(pos.x, pos.y, size, size);
}
void applyForce(PVector force){
if(!freeze){
PVector f = PVector.div(force,mass);
f.y *= -1;
acc.add(f);
}
}
}
class Fluid{
//Upper left corner coords, width and height
int x, y, w, h;
//Coefficent of drag
float d;
//Colour
int[] colour = new int[3];
//Current
PVector current = new PVector();
Fluid(int xpixel, int ypixel, int wide, int high, float drag, int r, int g, int b, float xcurrent, float ycurrent){
x = xpixel;
y = ypixel;
w = wide;
h = high;
d = drag;
colour[0] = r;
colour[1] = g;
colour[2] = b;
current.x = xcurrent;
current.y = ycurrent;
}
void displayFluid(){
stroke(0);
strokeWeight(1);
fill(colour[0], colour[1], colour[2]);
rect(x, y, w, h);
}
boolean isContained(Entity o){
if(((o.pos.x > x) && (o.pos.x < x + w)) && ((o.pos.y > y) && (o.pos.y < y + h))){
return true;
}else return false;
}
PVector drag(Entity o){
PVector dforce = o.vel.get();
dforce.normalize();
dforce.mult(1);
dforce.x *= -1;
float ospeed = o.vel.mag();
dforce.mult(d * ospeed * ospeed);
return dforce;
}
PVector buoy(Entity o){
PVector bforce = new PVector(0, 1);
bforce.y *= o.size;
bforce.y = map(bforce.y, 1, 100, 1, 8);
return bforce;
}
}
float pi = 3.14159265;
//Press stores whether or not the mouse is being held
boolean press = false;
//Vectors to store mouse coordinates
PVector click = new PVector();
PVector mouse = new PVector(mouseX, mouseY);
//Count stores which entity is next to be spawned our of our array of entities
int count = 0;
Entity[] objects = new Entity[5];
//Defining some forces
PVector gravity = new PVector(0, -2);
//Define our fluids
Fluid[] fluids = new Fluid[3];
void setup(){
size(1280, 720);
frameRate(45);
background(255);
strokeWeight(1);
stroke(0);
fill(100);
for(int i = 0; i < objects.length; i++) objects[i] = new Entity();
fluids[0] = new Fluid(0, height/2, width/3, height/2, 0.1, 255, 255, 102, 1, 1);
fluids[1] = new Fluid(width/3, height/2, width/3, height/2, 0.01, 102, 255, 255, 0, 0);
fluids[2] = new Fluid((width * 2)/3, height/2, width/3, height/2, 0.001, 255, 102, 0, -1, -1);
}
void draw(){
background(255);
updateFluids();
checkPress();
updateEntities(objects);
}
void checkPress(){
if(mousePressed){
//This says: if the mouse is being pressed for the first time after being releasd
if(!press){
//Update our click vector to the spot where the mouse is clicked
click.x = mouseX;
click.y = mouseY;
//Set the position of one of the objects to our cursor, reset the velocity & accelleration, randomize the shape and colour, and then enable displaying of the entity. Also freeze the entity so gravity doesn't affect it yet
objects[count].pos = click.get();
objects[count].vel.mult(0);
objects[count].acc.mult(0);
objects[count].freeze = true;
objects[count].randomize();
objects[count].spawned = true;
//This informs the next draw loop that the mouse is now being held down
press = true;
//This says: if the mouse is currently being held
}else{
//Update our mouse vector to current cursor position
mouse.x = mouseX;
mouse.y = mouseY;
//Define a new vector that is the line from our original click spot to our current mouse position
PVector traj = mouse.sub(click);
//Draw a line from our original click position along our trajectory vector to our current mouse position, weighted based on the traj length
stroke(100);
strokeWeight(traj.mag() * 0.01);
line(click.x, click.y, (click.x + traj.x), (click.y + traj.y));
}
//This says: if the mouse is not pressed but on the previous frame it was
}else if(press){
//Update mouse vector
mouse.x = mouseX;
mouse.y = mouseY;
//Update traj vector
PVector traj = mouse.sub(click);
//Set velocity of entity based on the traj vector, reversing it and tying it to mass. Then unfreeze
objects[count].vel = traj.get();
objects[count].vel.mult(-0.1 / objects[count].mass);
objects[count].freeze = false;
//This resets our press variable for the next time the mouse is pressed, incrementing the object counter and wrapping it to 0 to keep within the bounds of our array
press = false;
count++;
if(count == objects.length) count = 0;
}
}
void updateEntities(Entity[] objects){
for(Entity o : objects){
//Apply gravity
PVector gravity = new PVector(0, -2);
gravity.mult(o.mass);
o.applyForce(gravity);
//Apply current
//Apply drag and buoyancy
for(Fluid f : fluids){
if(f.isContained(o)) {
o.applyForce(f.drag(o));
o.applyForce(f.buoy(o));
}
}
//Update objects
o.displayEntity();
o.collideEntity(0);
o.updateEntity();
}
}
void updateFluids(){
fluids[0].displayFluid();
fluids[1].displayFluid();
fluids[2].displayFluid();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment