import processing.core.PVector;
NavierStokesSolver fluidSolver;
double visc, diff, vScale, velocityScale;
float oldMouseX = 1, oldMouseY = 1;
fluidSolver = new NavierStokesSolver();
numParticles = (int)pow(2, 16);
particles = new Particle[numParticles];
private void initParticles() {
for (int i = 0; i < numParticles - 1; i++) {
particles[i] = new Particle();
particles[i].x = random(width);
particles[i].y = random(height);
double dt = 1 / frameRate;
fluidSolver.tick(dt, visc, diff);
vScale = velocityScale * 60. / frameRate;
if (mousePressed && key == CODED && keyCode == CONTROL) {
float camX = radius * cos(angleY) * sin(angleX);
float camY = radius * sin(angleY);
float camZ = radius * cos(angleY) * cos(angleX);
camera(camX, camY, camZ, 0, 0, 0, 0, 1, 0);
void mouseWheel(MouseEvent event) {
zoom += event.getCount() / 500.0;
zoom = constrain(zoom, 0.1, 5);
private void drawParticlesPixels() {
int n = NavierStokesSolver.N;
float cellHeight = height / n;
float cellWidth = width / n;
for (Particle p : particles) {
int cellX = floor(p.x / cellWidth);
int cellY = floor(p.y / cellHeight);
float dx = (float) fluidSolver.getDx(cellX, cellY);
float dy = (float) fluidSolver.getDy(cellX, cellY);
float lX = p.x - cellX * cellWidth - cellWidth / 2;
float lY = p.y - cellY * cellHeight - cellHeight / 2;
v = Math.min(n, cellX + 1);
v = Math.max(0, cellX - 1);
h = Math.min(n, cellY + 1);
h = Math.max(0, cellY - 1);
float dxv = (float) fluidSolver.getDx(v, cellY);
float dxh = (float) fluidSolver.getDx(cellX, h);
float dxvh = (float) fluidSolver.getDx(v, h);
float dyv = (float) fluidSolver.getDy(v, cellY);
float dyh = (float) fluidSolver.getDy(cellX, h);
float dyvh = (float) fluidSolver.getDy(v, h);
dx = lerp(lerp(dx, dxv, vf * lX / cellWidth),
lerp(dxh, dxvh, vf * lX / cellWidth),
dy = lerp(lerp(dy, dyv, vf * lX / cellWidth),
lerp(dyh, dyvh, vf * lX / cellWidth),
if (p.x < 0 || p.x >= width) {
if (p.y < 0 || p.y >= height) {
set((int) p.x, (int) p.y, c);
private void handleMouseMotion() {
int n = NavierStokesSolver.N;
float cellHeight = height / n;
float cellWidth = width / n;
float mouseDx = mouseX - oldMouseX;
float mouseDy = mouseY - oldMouseY;
int cellX = floor(mouseX / cellWidth);
int cellY = floor(mouseY / cellHeight);
fluidSolver.applyForce(cellX, cellY, mouseDx, mouseDy);
public class NavierStokesSolver {
final static int SIZE = (N + 2) * (N + 2);
double[] u = new double[SIZE];
double[] v = new double[SIZE];
double[] u_prev = new double[SIZE];
double[] v_prev = new double[SIZE];
public NavierStokesSolver() {
public double getDx(int x, int y) {
return u[INDEX(x + 1, y + 1)];
public double getDy(int x, int y) {
return v[INDEX(x + 1, y + 1)];
public void applyForce(int cellX, int cellY, double vx, double vy) {
double dx = u[INDEX(cellX, cellY)];
double dy = v[INDEX(cellX, cellY)];
u[INDEX(cellX, cellY)] = (vx != 0) ? lerp((float) vx,
v[INDEX(cellX, cellY)] = (vy != 0) ? lerp((float) vy,
void tick(double dt, double visc, double diff) {
vel_step(u, v, u_prev, v_prev, visc, dt);
final int INDEX(int i, int j) {
double[] tmp = new double[SIZE];
final void SWAP(double[] x0, double[] x) {
arraycopy(x0, 0, tmp, 0, SIZE);
arraycopy(x, 0, x0, 0, SIZE);
arraycopy(tmp, 0, x, 0, SIZE);
void add_source(double[] x, double[] s, double dt) {
int i, size = (N + 2) * (N + 2);
for (i = 0; i < size; i++)
void diffuse(int b, double[] x, double[] x0, double diff, double dt) {
double a = dt * diff * N * N;
for (k = 0; k < 20; k++) {
for (i = 1; i <= N; i++) {
for (j = 1; j <= N; j++) {
x[INDEX(i, j)] = (x0[INDEX(i, j)] + a
* (x[INDEX(i - 1, j)] + x[INDEX(i + 1, j)]
+ x[INDEX(i, j - 1)] + x[INDEX(i, j + 1)]))
void advect(int b, double[] d, double[] d0, double[] u, double[] v,
int i, j, i0, j0, i1, j1;
double x, y, s0, t0, s1, t1, dt0;
for (i = 1; i <= N; i++) {
for (j = 1; j <= N; j++) {
x = i - dt0 * u[INDEX(i, j)];
y = j - dt0 * v[INDEX(i, j)];
* (t0 * d0[INDEX(i0, j0)] + t1 * d0[INDEX(i0, j1)])
* (t0 * d0[INDEX(i1, j0)] + t1 * d0[INDEX(i1, j1)]);
void set_bnd(int b, double[] x) {
for (i = 1; i <= N; i++) {
x[INDEX(0, i)] = (b == 1) ? -x[INDEX(1, i)] : x[INDEX(1, i)];
x[INDEX(N + 1, i)] = b == 1 ? -x[INDEX(N, i)] : x[INDEX(N, i)];
x[INDEX(i, 0)] = b == 2 ? -x[INDEX(i, 1)] : x[INDEX(i, 1)];
x[INDEX(i, N + 1)] = b == 2 ? -x[INDEX(i, N)] : x[INDEX(i, N)];
x[INDEX(0, 0)] = 0.5 * (x[INDEX(1, 0)] + x[INDEX(0, 1)]);
x[INDEX(0, N + 1)] = 0.5 * (x[INDEX(1, N + 1)] + x[INDEX(0, N)]);
x[INDEX(N + 1, 0)] = 0.5 * (x[INDEX(N, 0)] + x[INDEX(N + 1, 1)]);
x[INDEX(N + 1, N + 1)] = 0.5 * (x[INDEX(N, N + 1)] + x[INDEX(N + 1, N)]);
void dens_step(double[] x, double[] x0, double[] u, double[] v,
double diff, double dt) {
diffuse(0, x, x0, diff, dt);
advect(0, x, x0, u, v, dt);
void vel_step(double[] u, double[] v, double[] u0, double[] v0,
double visc, double dt) {
diffuse(1, u, u, visc, dt);
diffuse(2, v, v, visc, dt);
void project(double[] u, double[] v, double[] p, double[] div) {
for (i = 1; i <= N; i++) {
for (j = 1; j <= N; j++) {
* (u[INDEX(i + 1, j)] - u[INDEX(i - 1, j)]
+ v[INDEX(i, j + 1)] - v[INDEX(i, j - 1)]);
for (k = 0; k < 20; k++) {
for (i = 1; i <= N; i++) {
for (j = 1; j <= N; j++) {
p[INDEX(i, j)] = (div[INDEX(i, j)] + p[INDEX(i - 1, j)]
+ p[INDEX(i + 1, j)] + p[INDEX(i, j - 1)] + p[INDEX(
for (i = 1; i <= N; i++) {
for (j = 1; j <= N; j++) {
* (p[INDEX(i + 1, j)] - p[INDEX(i - 1, j)]) / h;
* (p[INDEX(i, j + 1)] - p[INDEX(i, j - 1)]) / h;