xxxxxxxxxx
184
// global variables
let discs;
let colonies;
let prevColonies;
let spread;
let numColonies;
let fixed;
let uv;
let plateColor;
let colonyColor;
let colonyBorder;
let discColor;
let warning;
let warningFrame;
// functions
function setup() {
createCanvas(400, 400);
frameRate(15);
background("#F8F5C9");
spread = width / 30;
numColonies = 5;
fixed = false;
uv = false;
discs = [];
colonies = [];
prevColonies = [];
uvbutton = createButton("Toggle UV ");
uvbutton.position((3 * width) / 40, height / 20);
uvbutton.mousePressed(uvToggle);
fixbutton = createButton("Fix Plate");
fixbutton.position((16.5 * width) / 40, height / 20);
fixbutton.mousePressed(fixPlate);
clrbutton = createButton("Wash Plate");
clrbutton.position((28.5 * width) / 40, height / 20);
clrbutton.mousePressed(clearPlate);
}
function draw() {
if (uv) {
plateColor = "#33257520"; // transparency to give "growth/death effect"
colonyColor = "#08FF08";
colonyBorder = "#BBFF00B1";
discColor = "#8383837F";
} else {
plateColor = "#F8F5C920"; // transparency to give "growth/death effect"
colonyColor = "#D5CC7E";
colonyBorder = "#E4E0B2B1";
discColor = "#FDFAE8";
}
if (!fixed) {
background(plateColor);
// create arrays to store colony position and size information
prevColonies = colonies;
colonies = [];
for (
let d = 0, colonyX, colonyY, colonyR, colonySpecs;
d < discs.length;
d++
) {
// discs
fill(discColor);
discs[d].display();
// create sub-array to store specific info for each disc
colonies.push([]);
// bacterial colonies
for (let i = 0; i < numColonies; i++) {
// Gaussian random to ensure aggregation towards discs
colonyX = randomGaussian(discs[d].x, spread);
colonyY = randomGaussian(discs[d].y, spread);
colonyR = random(2, 15);
colonySpecs = { x: colonyX, y: colonyY, r: colonyR }; // dictionary containing colony info
colonies[d].push(colonySpecs);
stroke(colonyBorder);
strokeWeight(3);
fill(colonyColor);
ellipse(colonyX, colonyY, colonyR);
}
// gradually have bacteria spawn further away from disc to simulate diffusion
if (spread < width / 10) {
spread+=0.005;
}
// gradually increase number of colonies with time
if (numColonies < 30) {
numColonies+=0.01
}
}
} else {
// fix the plate without removing ability to shine UV
// uses data from last 2 generations
background(plateColor.slice(0, -2));
// Last gen
for (let d = 0, colonyX, colonyY, colonyR; d < discs.length; d++) {
fill(discColor);
discs[d].display();
for (let i = 0; i < numColonies; i++) {
stroke(colonyBorder);
strokeWeight(3);
fill(colonyColor);
colonyX = colonies[d][i].x;
colonyY = colonies[d][i].y;
colonyR = colonies[d][i].r;
ellipse(colonyX, colonyY, colonyR);
}
}
// Second-to-last gen
for (let d = 0, colonyX, colonyY, colonyR; d < discs.length; d++) {
fill(discColor);
discs[d].display();
for (let i = 0; i < numColonies; i++) {
stroke(colonyBorder.slice(0, -2) + "20");
strokeWeight(3);
fill(colonyColor + "a0");
colonyX = prevColonies[d][i].x;
colonyY = prevColonies[d][i].y;
colonyR = prevColonies[d][i].r;
ellipse(colonyX, colonyY, colonyR);
}
}
}
// warning when placing disc is not allowed
if (frameCount - warningFrame < 40) {
fill("red");
stroke("red");
strokeWeight(1);
textSize(16);
textAlign(CENTER, CENTER);
text(warning, width / 2, height / 5);
}
}
// creates new disc at mouse position around which bacteria grow
function mouseClicked() {
// prevents discs from being placed when clicking on the buttons
// also prevents discs from being placed when plate is fixed or under UV
if (mouseY > (2 * height) / 20 && !fixed && !uv) {
var newDisc = new Disc(mouseX, mouseY, width / 15);
discs.push(newDisc);
} else if (mouseY > (2 * height) / 20 && fixed) {
warning = "Plate is fixed. Get fresh plate.";
warningFrame = frameCount; // counts frames to ensure warning will stay up for some time
} else if (mouseY > (2 * height) / 20 && uv) {
warning = "Remove plate from UV before handling";
warningFrame = frameCount;
}
}
// uv blacklight + fluorescence
function uvToggle() {
uv = !uv;
}
// freeze in time
function fixPlate() {
fixed = true;
// noLoop();
}
// reset
function clearPlate() {
clear();
discs = [];
fixed = false;
uv = false;
spread = width / 30;
numColonies = 5;
// loop();
}
// classes
// class of nutrient disc
class Disc {
constructor(x, y, dim) {
this.x = x;
this.y = y;
this.diameter = dim;
}
display() {
noStroke();
ellipse(this.x, this.y, this.diameter);
}
}