xxxxxxxxxx
221
//See Sketch Files for classes.
//See About folder for TODO, background, and references.
/**** VARIABLES ****/
let fps = 24; //frame rate (frames per second)
let grid; //grid in which cells live
let population; //population of cells
let plot1, plot2; //separate plots for simulation and graph
let win; //graph window (includes origin and scale for axes)
let popCurve; //population curve
let meanPopCurve; //mean population curve
let popCurves; //all population curves
let popIndex = 0; //index of population in current sim run
let icon; //refresh icon
let iconRadius = 35; //radius of refresh icon in px
let buttonX; //plot2 X-coord of top left of "Show/hide mean" button
let buttonY; //plot2 Y-coord of top left of "Show/hide mean" button
let buttonW; //width of "Show/hide mean" button
let buttonH; //height of "Show/hide mean" button
let buttonColor;
let meanHidden = true; //if true, mean curve is not drawn
/**** SETUP ****/
function preload() {
icon = loadImage('icon.png');
}
function setup() {
frameRate(fps);
createCanvas(704, 396);
//create plots, graph window, and grid
plot1 = createGraphics(width / 2, height);
plot2 = createGraphics(width / 2, height);
win = createGraphWindow(15, height - 11, 22, 1 / 30);
grid = createGrid(width / 2, height, 4);
//set button values
buttonX = plot2.width - 200;
buttonY = 10;
buttonW = 180;
buttonH = 30;
buttonColor = color(255,157,91);
//create population
population = createPopulation(10, grid);
//create population curve consisting of [time, population count] pairs,
//from population history, in given graphing window
popCurve = createCurve(population.history, win);
//include population curve in array that will store all curves,
//and build a Curves instance from that array
//(Curves class comes with a drawing method and an averaging method)
popCurves = createCurves([popCurve]);
}
/**** CUSTOM PLOT FUNCTIONS ****/
//PLOT 1
//img: optional parameter
//("icon" without quotation marks is only possible value)
function drawPlot1(img) {
//make plot
plot1.background(229, 255, 234);
plot1.fill(232, 51, 232, 160);
population.draw(plot1);
//make label
plot1.textSize(28);
plot1.fill(0);
plot1.text("Population: " + population.count, 10, height - 10);
//make icon
if (typeof img !== 'undefined') {
plot1.imageMode(CENTER);
plot1.image(img, plot1.width / 2, plot1.height / 2, 2 * iconRadius, 2 * iconRadius);
}
//show plot at given canvas position
image(plot1, 0, 0)
}
//PLOT 2
//buttonText is an optional string parameter
function drawPlot2(buttonText) {
//make window
plot2.background(51);
plot2.stroke(255);
plot2.strokeWeight(1);
win.axis('horizontal', plot2);
win.axis('vertical', plot2);
//make labels
plot2.noStroke();
plot2.fill(255);
plot2.textSize(18);
plot2.textStyle(NORMAL);
plot2.textAlign(LEFT, BASELINE);
plot2.text("Population", 30, 30);
plot2.text("Time", plot2.width - 50, plot2.height - 20);
//make population curves
plot2.strokeWeight(2);
if (meanHidden) {
plot2.stroke(255);
popCurves.draw(plot2);
}
else {
plot2.stroke('rgba(255, 255, 255, 0.25)');
popCurves.draw(plot2);
meanPopCurve = popCurves.meanCurve(); //compute mean curve
plot2.stroke(buttonColor);
plot2.strokeWeight(3);
meanPopCurve.draw(plot2);
}
//make button
if (typeof buttonText !== 'undefined') {
plot2.fill(buttonColor);
plot2.stroke(255);
plot2.strokeWeight(2);
plot2.rect(buttonX, buttonY, buttonW, buttonH, 10);
plot2.fill(255, 255, 255);
plot2.noStroke();
plot2.textSize(18);
plot2.textStyle(BOLD);
plot2.textAlign(CENTER, CENTER);
plot2.text(buttonText, buttonX + buttonW / 2, buttonY + buttonH / 2);
}
//show plot at given canvas position
image(plot2, width / 2, 0);
}
/**** DRAW LOOP ****/
function draw() {
//SIMULATION
drawPlot1();
//GRAPH
drawPlot2();
//UPDATE POPULATION AND CURVE
if (population.count < grid.capacity) {
population.update(fps);
//the Curve instances are stored in the array popCurves.curvesArray;
//so, popCurves.curvesArray[popIndex] is population Curve instance
//for current sim run
popCurves.curvesArray[popIndex].points = population.history;
}
else {
drawPlot1(icon);
drawPlot2("Show/hide mean");
noLoop(); //at capacity, so stop iterating
}
}
/**** INTERACTIVITY ****/
//returns true if given point is on refresh icon
function isOnIcon (X, Y) {
let isOnIconX = plot1.width / 2 - iconRadius < X && X < plot1.width / 2 + iconRadius;
let isOnIconY = plot1.height / 2 - iconRadius < Y && Y < plot1.height / 2 + iconRadius;
return isOnIconX && isOnIconY;
}
//returns true if given point is on "Show/hide mean" button
function isOnButton (X, Y) {
let w1 = plot1.width;
//Note that ButtonX and ButtonY are in plot2 coordinates
let isOnButtonX = w1 + buttonX < X && X < w1 + buttonX + buttonW;
let isOnButtonY = buttonY < Y && Y < buttonY + buttonH;
return isOnButtonX && isOnButtonY;
}
function mouseMoved() {
if (population.count === grid.capacity) {
if (isOnIcon(mouseX, mouseY) || isOnButton(mouseX, mouseY)) {
cursor(HAND);
}
else {
cursor(ARROW);
}
}
}
function mouseClicked() {
if (population.count === grid.capacity) {
//if clicked on refresh icon, create new grid, population, and curve;
//then set mean curve to hidden
if (isOnIcon(mouseX, mouseY)) {
//create new grid (old grid has no more vacancies)
grid = createGrid(width / 2, height, 4);
//create new population and its curve;
population = createPopulation(10, grid);
popCurve = createCurve(population.history, win);
//include curve at end of array of population curves
popCurves.curvesArray.push(popCurve);
popIndex++; //increment population index
meanHidden = true;
}
//else if clicked on "Show/hide mean" button,
//toggle mean curve state
else if (isOnButton(mouseX, mouseY)) {
meanHidden = !meanHidden;
}
loop(); //restart draw() loop
}
}