Skip to content

Instantly share code, notes, and snippets.

@reedspool
Created July 21, 2014 05:27
Show Gist options
  • Save reedspool/1b1da85561d4fdee7068 to your computer and use it in GitHub Desktop.
Save reedspool/1b1da85561d4fdee7068 to your computer and use it in GitHub Desktop.
A Pen by Reed Spool.
<svg class="container"></svg>
// Start by building our model
var fieldWidth = 40,
fieldHeight = 40,
field = randomField(),
// Define our visual space
pxWidth = 300,
pxHeight = 300,
pxWidthSquare = pxWidth / fieldWidth,
pxHeightSquare = pxHeight / fieldHeight,
// How fast does it go?
msTimestep = 60;
// Scales to turn field coordinates into rendering coordinates
var x = d3.scale.linear()
.domain([0, fieldWidth])
.range([0, pxWidth])
var y = d3.scale.linear()
.domain([0, fieldHeight])
.range([0, pxHeight]);
var svg = d3.select('svg.container')
.attr('height', pxHeight)
.attr('width', pxWidth),
time = 0;
// Render initial
render(field);
// Then continuously iterate
d3.timer(iterate, msTimestep)
function iterate() {
// If we've been looking at this one a while...
if (time % 200 == 0) {
// Make a new one!
field = randomField()
} else {
// Otherwise, just refresh the old one
field = createNewGeneration(field)
}
render(field)
time++
d3.timer(iterate, msTimestep)
return true;
}
// Actual d3 rendering. Should not be redrawing the whole thing every time
// But it does reselect everything. Is that healthy?
function render(data) {
var up = svg.selectAll('g.row').data(data),
en = up.enter(),
ex = up.exit();
en.append('svg:g')
.classed('row', true)
var upSquare = up.selectAll('g.square')
.data(function (d) { return d } ),
enSquare = upSquare.enter();
enSquare.append('svg:g')
.classed('square', true)
.append('rect')
.attr('x', function(d, i, j) { return x(i) })
.attr('y', function(d, i, j) { return y(j) })
.attr('width', pxWidthSquare)
.attr('height', pxHeightSquare);
// Finally, just switch class on and off to visualize binary state
upSquare
.classed('on', function (d) { return d == 1;})
}
// Utility to generate a fresh totally random field
function randomField() {
return _.map(d3.range(0, fieldWidth), function (i) {
return _.map(d3.range(0, fieldHeight), function (j) {
return Math.random() < 0.5 ? 1 : 0
})
});
}
// Taken from: http://www.janwillemtulp.com/2011/03/22/tutorial-conways-game-of-life-in-d3/
// (and edited by me to not rely on globals/closures so much)
function createNewGeneration(states) {
var nextGen = new Array()
var ccx = states.length;
for (x = 0; x < ccx; x++) {
var ccy = states[x].length;
nextGen[x] = new Array()
for (y = 0; y < ccy; y++) {
var ti = y - 1 < 0 ? ccy - 1 : y - 1 // top index
var ri = x + 1 == ccx ? 0 : x + 1 // right index
var bi = y + 1 == ccy ? 0 : y + 1 // bottom index
var li = x - 1 < 0 ? ccx - 1 : x - 1 // left index
var thisState = states[x][y]
var liveNeighbours = 0
liveNeighbours += states[li][ti] ? 1 : 0
liveNeighbours += states[x][ti] ? 1 : 0
liveNeighbours += states[ri][ti] ? 1 : 0
liveNeighbours += states[li][y] ? 1 : 0
liveNeighbours += states[ri][y] ? 1 : 0
liveNeighbours += states[li][bi] ? 1 : 0
liveNeighbours += states[x][bi] ? 1 : 0
liveNeighbours += states[ri][bi] ? 1 : 0
var newState = false
if (thisState) {
newState = liveNeighbours == 2 || liveNeighbours == 3 ? 1 : 0
} else {
newState = liveNeighbours == 3 ? 1 : 0
}
nextGen[x][y] = newState
}
}
return nextGen
}
.container {
.square {
rect {
fill: #f0cdaa;
}
}
.square.on {
rect {
fill: #f0aaad;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment