xxxxxxxxxx
243
const GRID_WIDTH = 40;
const GRID_HEIGHT = 40;
let cells;
const CTYPE = {
AIR: "air",
BLOCK: "block",
FLUID: "fluid",
}
class Cell {
constructor(x, y, type, fill) {
this.x = x;
this.y = y;
this.type = type;
this.fill = fill;
}
updateNeighbors(x, y, cells) {
if (x - 1 < 0) {
this.left = null;
} else {
this.left = cells[x - 1][y];
}
if (x + 1 >= cells.length) {
this.right = null;
} else {
this.right = cells[x + 1][y];
}
if (y - 1 < 0) {
this.top = null;
} else {
this.top = cells[x][y - 1];
}
if (y + 1 >= cells[0].length) {
this.bottom = null;
} else {
this.bottom = cells[x][y + 1];
}
}
simulate(x, y, cells) {
this.updateNeighbors(x, y, cells);
// Rule 1: Flowing Into Bottom Neighboring Cell
if (this.bottom != null) {
if (this.bottom.type == CTYPE.AIR) {
this.bottom.type = CTYPE.FLUID;
this.bottom.fill = this.fill;
this.type = CTYPE.AIR;
this.fill = 0;
} else if (this.bottom.type == CTYPE.FLUID) {
// overpressure?
}
}
// Rule 2: Flowing Into Left and Right Neighboring Cells
let flow = this.fill / 3;
if (this.left != null &&
this.bottom != null &&
this.bottom.type != CTYPE.AIR) {
if (this.left.type == CTYPE.AIR) {
this.left.type = CTYPE.FLUID;
this.left.fill = flow;
this.fill -= flow;
} else if (this.left.type == CTYPE.FLUID) {
if (this.left.fill < this.fill) {
this.left.fill += flow;
this.fill -= flow;
}
}
}
if (this.right != null &&
this.bottom != null &&
this.bottom.type != CTYPE.AIR) {
if (this.right.type == CTYPE.AIR) {
this.right.type = CTYPE.FLUID;
this.right.fill = flow;
this.fill -= flow;
} else if (this.right.type == CTYPE.FLUID) {
if (this.right.fill < this.fill) {
this.right.fill += flow;
this.fill -= flow;
}
}
}
// Rule 3: Flowing Upwards with Pressure
if (this.fill > 1) {
if (this.top != null &&
this.top.type == CTYPE.AIR) {
let excess = this.fill - 1;
this.top.type = CTYPE.FLUID;
this.top.fill = excess;
this.fill = 1;
}
}
}
}
class Cells {
constructor(w, h) {
this.w = w;
this.h = h;
this.cells = [];
for (let i = 0; i < w; i++) {
let r = [];
for (let j = 0; j < h; j++) {
let c = new Cell(i, j, CTYPE.AIR, 1);
r.push(c);
}
this.cells.push(r);
}
}
draw() {
push();
noStroke();
for (let i = 0; i < this.w; i++) {
for (let j = 0; j < this.h; j++) {
let offset = 0;
switch(this.cells[i][j].type) {
case CTYPE.AIR:
fill(255);
break;
case CTYPE.FLUID:
fill(35, 190, 255);
offset = (1 - this.cells[i][j].fill)*(height/this.h);
break;
case CTYPE.BLOCK:
fill(0);
break;
}
if (this.cells[i][j].fill <= 1) {
rect(
i*(width/this.w),
j*(height/this.h)+offset,
(i+1)*(width/this.w),
(j+1)*(height/this.h)
);
} else {
fill(35,
map(this.cells[i][j].fill, 1, 2, 190, 87),
255);
rect(
i*(width/this.w),
j*(height/this.h),
(i+1)*(width/this.w),
(j+1)*(height/this.h)
);
}
}
}
pop();
}
simulate() {
for (let i = this.w - 1; i >= 0; i--) {
for (let j = this.h - 1; j >= 0; j--) {
if (this.cells[i][j].type == CTYPE.FLUID) {
this.cells[i][j].simulate(i, j, this.cells);
}
}
}
/*
for (let i = 0; i < this.w; i++) {
for (let j = 0; j < this.h; j++) {
if (this.cells[i][j].type == CTYPE.FLUID) {
this.cells[i][j].simulate(i, j, this.cells);
}
}
} */
}
}
function setup() {
createCanvas(600, 600);
for (let element of document.getElementsByClassName("p5Canvas")) {
element.addEventListener("contextmenu",
(e) => e.preventDefault());
}
cells = new Cells(GRID_WIDTH, GRID_HEIGHT);
buildWalls();
}
function draw() {
background(220);
cells.draw();
cells.simulate();
}
function mousePressed() {
if (mouseX > 0 &&
mouseX < width &&
mouseY > 0 &&
mouseY < height) {
let x = floor(mouseX / (width / GRID_WIDTH));
let y = floor(mouseY / (height / GRID_HEIGHT));
if (mouseButton == LEFT) {
cells.cells[x][y].type = CTYPE.BLOCK;
cells.cells[x][y].fill = 0;
} else if (mouseButton == RIGHT) {
if (cells.cells[x][y].type != CTYPE.BLOCK) {
cells.cells[x][y].type = CTYPE.FLUID;
cells.cells[x][y].fill = 1;
}
} else if (mouseButton == CENTER) {
cells.cells[x][y].type = CTYPE.AIR;
cells.cells[x][y].fill = 0;
}
}
}
function mouseDragged() {
if (mouseX > 0 &&
mouseX < width &&
mouseY > 0 &&
mouseY < height) {
let x = floor(mouseX / (width / GRID_WIDTH));
let y = floor(mouseY / (height / GRID_HEIGHT));
if (mouseButton == LEFT) {
cells.cells[x][y].type = CTYPE.BLOCK;
cells.cells[x][y].fill = 0;
} else if (mouseButton == RIGHT) {
if (cells.cells[x][y].type != CTYPE.BLOCK) {
cells.cells[x][y].type = CTYPE.FLUID;
cells.cells[x][y].fill = 1;
}
} else if (mouseButton == CENTER) {
cells.cells[x][y].type = CTYPE.AIR;
cells.cells[x][y].fill = 0;
}
}
}
function buildWalls() {
for (let i = 0; i < cells.w; i++) {
for (let j = 0; j < cells.h; j++) {
if (i == 0 ||
i == cells.w - 1 ||
j == cells.h - 1) {
cells.cells[i][j].type = CTYPE.BLOCK;
}
}
}
}