UNPKG

cacatoo

Version:

Building, exploring, and sharing spatially structured models

173 lines (139 loc) 9.6 kB
<html> <script src="https://bramvandijk88.github.io/cacatoo/scripts/cacatoo.js"></script> <!-- Include cacatoo library (compiled with rollup) --> <script src="https://bramvandijk88.github.io/cacatoo/scripts/all.js"></script> <!-- Include other libraries (concattenated in 1 file) --> <link rel="stylesheet" href="https://bramvandijk88.github.io/cacatoo/styles/cacatoo.css"> <!-- Set style sheet --> <script> /*-----------------------Start user-defined code ---------------------*/ let sim; // Declare a variable named "sim" globally, so that we can access our cacatoo-simulation from wherever we need. var positive_feedback = 0; var done = 0 /** * function cacatoo() contains all the user-defined parts of a cacatoo-model. Configuration, update rules, what is displayed or plotted, etc. It's all here. */ function cacatoo() { /* 1. SETUP. First, set up a configuration-object. Here we define how large the grid is, how long will it run, what colours will the critters be, etc. */ let config = { title: "Quorum sensing", // The name of your cacatoo-simulation description: "", // And a description if you wish maxtime: 100000, // How many time steps the model continues to run ncol: 64, // Number of columns (width of your grid) nrow: 64, // Number of rows (height of your grid) seed: 35, sleep: 10, wrap: [true, true], // Wrapped boundary conditions? [COLS, ROWS] scale: 6, // Scale of the grid (nxn pixels per grid point) statecolours: {'type': { 'normal': "#0055AA", // Sets up colours of states (here 1,2,3 = A,B,C). Can be a colour name or a hexadecimal colour. 'bioluminescent': "#00CCCC" // If your state it not defined, it won't be drawn and you'll see the grid-background colour (default: black) }} } /* 1. SETUP. (continued) Now, let's use that configuration-object to generate a new Cacatoo simulation */ sim = new Simulation(config) // Initialise the Cacatoo simulation sim.makeGridmodel("cells") // Build a new Gridmodel within the simulation called "model" sim.makeGridmodel("autoinducer") // Build a new Gridmodel within the simulation called "model" let species = [{type:'normal',size:1,alive:1}] sim.initialise = function(){ document.getElementById('text_holder').innerHTML = `Not all cells are bioluminescent` done = 0 sim.time = 0 sim.cells.resetPlots() sim.initialGrid(sim.cells,'type',0,1.0) // Give 100% of grid points external resources (set to 1) sim.initialGrid(sim.cells,'alive',0,1.0) // Give 100% of grid points external resources (set to 1) sim.initialGrid(sim.autoinducer,'concentration',10e-30,1.0) // Give 100% of grid points external resources (set to 1) sim.populateSpot(sim.cells, species, [1.0], 1, config.ncol/2, config.nrow/2) // Place the three 'species' in a small spot in the middle of the grid } sim.initialise() sim.createDisplay_discrete({model:"cells", property:"type", label:"Normal and bioluminescent cells",drawdots:true, stroke:1,radius:3.2}) // Create a display in the same way we did in Tutorial 1 (display a discrete variable) sim.createDisplay_continuous({model:"autoinducer", property:"concentration", label:"Autoinducer concentration", // Createa a display for a continuous variable (ODE state for external resources) minval:0, maxval:20, fill:"viridis", num_colours:100}) /* 2. DEFINING THE RULES. Below, the user defines the nextState function. This function will be applied for each grid point when we will update the grid later. */ sim.cells.nextState = function (i, j) { let randomneigh = this.randomMoore8(this, i, j) // Random neighbour let num_neigh = this.countMoore8(this,i,j, 'alive', 1) sim.autoinducer.grid[i][j].concentration *= 0.9 let this_gp = this.grid[i][j] // This cell if (!this_gp.type) // If empty spot { if (num_neigh < 2 && randomneigh.type && randomneigh.size == 50 ) { // Random neighbour is alive and it has enough resources this_gp.type = randomneigh.type this_gp.alive = 1 // Empty spot becomes the parent type (reproduction) let childsize = sim.rng.genrand_int(1,49) randomneigh.size -= childsize this_gp.size = childsize //this_gp.uptake_rate = randomneigh.uptake_rate // Empty spot inherits uptake rate from the parent //randomneigh.internal_resources = this_gp.internal_resources = randomneigh.internal_resources / 2 // Resources are divided between parent and offpsring } } else { if(this_gp.size < 50) this_gp.size++ sim.autoinducer.grid[i][j].concentration += 1.0 if(positive_feedback && this_gp.type == 'bioluminescent') sim.autoinducer.grid[i][j].concentration += 1.0 if(sim.autoinducer.grid[i][j].concentration > 5) this_gp.type = 'bioluminescent' else this_gp.type = 'normal' } } /* 3. MAIN SIMULATION LOOP. Finally, we need to set the update-function, which is the mainwill be applied to the whole grid each time step. For now, all we will do is call "synchronous", which applies the next-state function shown above to each grid point. All cells are updated at the same time, rather than in turn (for this, use the function "asynchonous") */ sim.autoinducer.update = function () { } sim.cells.update = function () { this.asynchronous() // Applied as many times as it can in 1/60th of a second sim.autoinducer.diffuseStates('concentration',0.1) sim.autoinducer.diffuseStates('concentration',0.1) sim.autoinducer.diffuseStates('concentration',0.1) sim.autoinducer.diffuseStates('concentration',0.1) this.plotPopsizes('type', ['normal', 'bioluminescent']) this.MargolusDiffusion() // Let's count some stuff every update let sum_auto = 0 let num_normal = 0 let num_fluor = 0 for (let i = 0; i < this.nc; i++) // i are columns for (let j = 0; j < this.nr; j++) // j are rows { sum_auto += sim.autoinducer.grid[i][j].concentration if(this.grid[i][j].type == 'normal') num_normal++ if(this.grid[i][j].type == 'bioluminescent') num_fluor++ } // Update the plots. If the plot do not yet exist, a new plot will be automatically added by cacatoo this.plotArray(["Autoinducer concentration"], [sum_auto/(this.nr*this.nc)], ["green"], "Autoinducer") if(num_normal == 0 && done == false) { document.getElementById('text_holder').innerHTML = `All cells emit light after ${sim.time} time steps` done = true } } /* OPTIONAL: Now that we have everything setup, we can also add some interactive elements (buttons or sliders). See cheater.html for more examples of this. */ sim.mix = false sim.addButton("Play/pause sim", function () { sim.toggle_play() }) sim.addButton("Restart", function () { sim.initialise() }) sim.addButton("Toggle mix", function () { sim.toggle_mix() }) sim.addToggle("positive_feedback", "LuxR promotes LuxI") sim.addCustomSlider("Slow down simulation", function(new_value) { sim.sleep = new_value }, 0, 1000, 1, 0) // addCustomSlider(function, minimal, maximal, step-size, default, label) sim.start() } /*-------------------------End user-defined code ---------------------*/ </script> <body onload="cacatoo()"> <div class="header" id="header"> <h2>Cacatoo</h2> </div> <div class="content" id="canvas_holder"> </div> <div class="content" id="text_holder" style="font-size:30"> Not all cells are bioluminescent</div> <div class="content" id="form_holder"></div> <div class="content" id="graph_holder"> </div> <div class="footer" id="footer"></div> </body> </html>