xxxxxxxxxx
151
// Wolfram Elementary CA
// The Coding Train / Daniel Shiffman
// https://thecodingtrain.com/challenges/179-wolfram-ca
// https://youtu.be/Ggxt06qSAe4
// Array to store the state of each cell.
let cells = [];
// Arrays to store cell colors
let hues = []
let sats = []
let sat_dir = []
// Rule value
let ruleValue = 30;
// The ruleset string
let ruleSet;
// Width of each cell
let thickness = 2;
// number of cells
let num_cells = 720
// starting radius
let radius = 0
// initial direction of drawing (1=outwards, -1=inwards)
let direction = 1
// points of a unit circle
let circle_x = []
let circle_y = []
function setup() {
createCanvas(600, 600);
colorMode(HSB, num_cells)
// Convert the rule value to a binary string.
ruleSet = ruleValue.toString(2).padStart(8, "0");
// setup unit circle
setup_circle()
// Initialize all cells to state 0 (inactive).
for (let i = 0; i < num_cells; i++) {
cells[i] = 0;
}
// Initialize color arrays
for (let i =0; i < num_cells; i++){
hues[i] = i
sats[i] = num_cells/2
sat_dir[i] = 1
}
// Set the middle cell to state 1 (active) as the initial condition.
cells[floor(num_cells / 2)] = 1;
background(0,0,0);
}
function draw() {
// call draw_ring() to make a cell ring
draw_ring(radius)
// Move to the next ring.
radius += thickness*direction;
if (radius > 300 || radius < 2){
direction *= -1
}
// Uncommenting the following produces alternative results
//thickness = radius*0.05
// Prepare an array for the next generation of cells.
let nextCells = [];
// Iterate over each cell to calculate its next state.
let len = cells.length;
for (let i = 0; i < len; i++) {
// Calculate the states of neighboring cells
let left = cells[(i - 1 + len) % len];
let right = cells[(i + 1) % len];
let state = cells[i];
// Set the new state based on the current state and neighbors.
let newState = calculateState(left, state, right);
nextCells[i] = newState;
// update colors
let hue_increment = newState*num_cells/500
let sat_increment = newState*sat_dir[i]*num_cells/400
hues[i] = (hues[i]+hue_increment) % num_cells
sats[i] = sats[i] + sat_increment
if(sats[i]>=num_cells || sats[i] <= num_cells/4){
sat_dir[i] *= -1
sats[i] = constrain(sats[i], num_cells/2, num_cells)
}
}
// Update the cells array for the next generation.
cells = nextCells;
}
function calculateState(a, b, c) {
// Create a string representing the state of the cell and its neighbors.
let neighborhood = "" + a + b + c;
// Convert the string to a binary number
let value = 7 - parseInt(neighborhood, 2);
// Return the new state based on the ruleset.
return parseInt(ruleSet[value]);
}
function setup_circle(){
// compute the cartesian coordinates of points along the unit circle
for (let i = 0; i < num_cells; i++){
let a = i * TWO_PI / num_cells;
circle_x[i] = cos(a);
circle_y[i] = sin(a);
}
}
function draw_ring(inner_radius){
// Draw a ring of cells.
noStroke();
//beginShape(QUADS);
let x0 = 300;
let y0 = 300;
for (let i = 0; i < num_cells; i++) {
// each cell's color is based on its state
fill(hues[i],sats[i],num_cells*cells[i]);
// create a quad
beginShape();
let j = (i+1) % num_cells;
let outer_radius = inner_radius + thickness;
let sx1 = x0 + inner_radius*circle_x[i];
let sy1 = y0 + inner_radius*circle_y[i];
let sx2 = x0 + outer_radius*circle_x[i];
let sy2 = x0 + outer_radius*circle_y[i];
let sx3 = x0 + outer_radius*circle_x[j];
let sy3 = y0 + outer_radius*circle_y[j];
let sx4 = x0 + inner_radius*circle_x[j];
let sy4 = x0 + inner_radius*circle_y[j];
vertex(sx1, sy1);
vertex(sx2, sy2);
vertex(sx3, sy3);
vertex(sx4, sy4);
endShape(CLOSE);
}
}