cacatoo
Version:
Building, exploring, and sharing spatially structured models
147 lines (117 loc) • 7.74 kB
HTML
<!--
Extention on EXAMPLE FILE 01: Game of Life: draw your own initial state!
-->
<!-- ---------------Do not change the part below ------------------ -->
<html>
<script src="../../dist/cacatoo.js"></script> <!-- Include cacatoo library (compiled with rollup) -->
<script src="../../lib/all.js"></script> <!-- Load other packages -->
<link rel="stylesheet" href="../../style/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.
/**
* 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: "Game of Life (draw your own initial pattern)", // The name of your cacatoo-simulation
description: "Use the 0/1 keys to swap between drawing 1's or 0's.<br> Use '[' and ']' to change the brush size.", // And a description if you wish
maxtime: 10000000, // How many time steps the model continues to run
ncol: 200, // Number of columns (width of your grid)
nrow: 200, // Number of rows (height of your grid)
wrap: [true, true], // Wrapped boundary conditions? [COLS, ROWS]
scale: 2, // Scale of the grid (nxn pixels per grid point)
statecolours: { 'alive': { 1: 'white' } }, // Colours for each state. Background (0) defaults to black.
printcursor: false
}
/*
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("gol") // Build a new Gridmodel within the simulation called "gol" (Game Of Life)
sim.initialGrid(sim.gol, 'alive', 0,1, 0.0) // Set half (50%) of the Gridmodel's grid points to 1 (alive)
sim.createDisplay("gol", "alive", "Game of life (white=alive)") // Create a display so we can see our newly made gridmodel
sim.createDisplay("gol", "alive", "Pattern", 20, 20, 20) // Create a display so we can see our newly made gridmodel
/*
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.gol.nextState = function (x, y) {
let gridpoint = this.grid[x][y]
// So, first we need to know how many cells are alive around a grid point
let neighbours = sim.gol.countMoore8(this, x, y,'alive',1) // In the Moore8 neighbourhood of this GridModel (CA) count # of 1's for the 'alive' property
// Then, let's see if this cell is dead or alive
let state = gridpoint.alive
// Then, apply the rules of game of life shown above
if (state == 0 && neighbours == 3)
gridpoint.alive = 1
else if (state == 1 && (neighbours < 2 || neighbours > 3))
gridpoint.alive = 0
else
gridpoint.alive = state
}
/*
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.gol.update = function () {
this.synchronous() // Applied as many times as it can in 1/60th of a second
}
/*
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.canvases[1].offset_x = 90//sim.ncol/2
sim.canvases[1].offset_y = 90//sim.nrow/2
sim.addPatternButton(sim.gol, 'alive') // Note, for security reasons, this needs a local server to be run (try VSC's 'live server' plugin!)
sim.addButton("play/pause", function () { sim.toggle_play() })
sim.addButton("step", function () { sim.step(); sim.display() })
sim.addButton("reset", function () { sim.initialGrid(sim.gol, 'alive', 1, 0.0); sim.pause=true; sim.display(); })
sim.modifysleep = function(newvalue) { this.sleep = newvalue}
sim.addCustomSlider("Sleeping time (ms)",function (new_value) { sim.sleep = new_value },0,1000,1,0) // addCustomSlider(function, minimal, maximal, step-size, default, label)
sim.start()
sim.pause = true
// BELOW IS SOME CUSTOM HACKS TO ENABLE DRAWING ON THE SMALLER (pattern) GRID
var mouseDown = false
sim.place_value = 1
sim.place_size = 1
let drawing_canvas = sim.canvases[1] // the second (zoomed) canvas is selected to draw on
drawing_canvas.elem.addEventListener('mousemove', (e) => {
if(mouseDown){
let coords = sim.getCursorPosition(drawing_canvas,e,20)
sim.putSpot(sim.gol, 'alive', sim.place_value, sim.place_size, coords.x, coords.y)
sim.display()
}
})
sim.canvases[0].elem.addEventListener('mousemove', (e) => {
if(mouseDown){
let coords = sim.getCursorPosition(sim.canvases[0],e,2)
sim.putSpot(sim.gol, 'alive', sim.place_value, sim.place_size, coords.x, coords.y)
sim.display()
}
})
window.addEventListener('keydown', function (e) {
if(e.key == "1") {sim.place_value = 1; console.log(`Placing value: ${sim.place_value}`)}
if(e.key == "0") {sim.place_value = 0; console.log(`Placing value: ${sim.place_value}`)}
if(e.key == "]") {sim.place_size += 1; console.log(`Brush size: ${sim.place_size}`)}
if(e.key == "[") {sim.place_size -= 1; console.log(`Brush size: ${sim.place_size}`)}
if(sim.place_size < 1) sim.place_size = 1
});
drawing_canvas.elem.addEventListener('mousedown', (e) => { mouseDown = true })
drawing_canvas.elem.addEventListener('mouseup', (e) => { mouseDown = false })
sim.canvases[0].elem.addEventListener('mousedown', (e) => { mouseDown = true })
sim.canvases[0].elem.addEventListener('mouseup', (e) => { mouseDown = false })
}
/*-------------------------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="form_holder"></div>
<div class="content" id="graph_holder"> </div>
<div class="footer" id="footer"></div>
</body>
</html>