Last active
November 3, 2016 12:39
-
-
Save zzandy/c83740c23255944254f89ca7998987d6 to your computer and use it in GitHub Desktop.
Streamlined stencil, independent x/y scales.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
</head> | |
<body style="background-color: goldenrod"> | |
<!-- loop --> | |
<script type="text/javascript"> | |
"use strict"; | |
function makeLoop(fixedDelta, fixedUpdate, update) { | |
var acc = 0; | |
return function(delta) { | |
if (delta === undefined || isNaN(delta)) { | |
delta = fixedDelta; | |
} | |
acc += delta; | |
while (acc >= fixedDelta) { | |
fixedUpdate(fixedDelta); | |
acc -= fixedDelta; | |
} | |
update(delta); | |
} | |
} | |
function loop(loopFunction, sheduleFunction) { | |
var then; | |
var iteration = function() { | |
var now = new Date().getTime(); | |
var delta = now - then; | |
then = now; | |
loopFunction(delta); | |
sheduleFunction(iteration); | |
} | |
iteration(); | |
} | |
</script> | |
<!-- canvas --> | |
<script type="text/javascript"> | |
"use strict"; | |
function fullscreenCanvas() { | |
var c = document.createElement('canvas'); | |
var ctx = c.getContext('2d'); | |
ctx.canvas.width = window.innerWidth; | |
ctx.canvas.height = window.innerHeight; | |
ctx.canvas.style.position = 'absolute'; | |
ctx.canvas.style.top = 0; | |
ctx.canvas.style.left = 0; | |
ctx.clear = function() { | |
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); | |
}; | |
var q = 1 / Math.sqrt(3); | |
ctx.getHexPath = function(h) { | |
var dx = q * h / 2; | |
var dy = h / 2; | |
return [ | |
2 * dx, 0, dx, -dy, -dx, -dy, -2 * dx, 0, -dx, dy, dx, dy | |
]; | |
} | |
ctx.makePath = function(vertices) { | |
this.beginPath(); | |
this.moveTo(vertices[0], vertices[1]); | |
for (var i = 2; i < vertices.length; i += 2) { | |
this.lineTo(vertices[i], vertices[i + 1]); | |
} | |
this.closePath(); | |
} | |
ctx.pathHex = function(h) { | |
this.makePath(this.getHexPath(h)); | |
}; | |
ctx.fillHex = function(x, y, h) { | |
this.save(); | |
this.translate(x, y); | |
this.pathHex(h); | |
this.fill(); | |
this.restore(); | |
}; | |
ctx.strokeHex = function(x, y, a) { | |
this.save(); | |
this.translate(x, y); | |
this.pathHex(a); | |
this.stroke(); | |
this.restore(); | |
}; | |
document.body.appendChild(c); | |
return ctx; | |
} | |
</script> | |
<script type="text/javascript"> | |
"use strict"; | |
var ctx = fullscreenCanvas(); | |
var w = 30; | |
var h = 20; | |
var s = new Pos(2, 1); | |
var data = array(h, w, function(i, j) { | |
return { | |
value: (j === 0 || j === w - 1 || (i === 0 && j % 2 === 1) || i === h - 1 && !!j % 2) ? 0 : Math.random() < .7 | |
} | |
}); | |
function rnd(n) { | |
return Math.floor(Math.random() * n); | |
} | |
var overflow = 0; | |
while (1) { | |
var i = rnd(h); | |
var j = rnd(w / 2); | |
var k = h - i - 1; | |
var l = w - j - 1; | |
if (data[i][j].value && data[k][l].value) { | |
data[i][j].color = [128, 0, 0]; | |
data[k][l].color = [0, 128, 0]; | |
break; | |
} | |
if (++overflow > 100) | |
throw new Error("Failed to place source and destination"); | |
} | |
var o = getHexPos(h / 2, w / 2); | |
o = new Pos((-o.x * s.x + ctx.canvas.width / 2) | 0, (-o.y * s.y + ctx.canvas.height / 2) | 0); | |
var ready = false; | |
var updated = true; | |
var renderer = null; | |
var stencil = ("" | |
+ ". . . . . . . ^ ^ ^ ^ . . . . . . . . . . . . .\n" | |
+ ". . . . . . ^ ^ ^ ^ ^ # # # # T T . . . . . . .\n" | |
+ ". . . . . ^ ^ # # # # # # # # # T T T T . . . .\n" | |
+ ". . . . # # # # # # # # # # # # # T T T . . . .\n" | |
+ ". . . # # # # # # # # # # # # # # # # T T . . .\n" | |
+ ". . < # # # # # # # # # # # # # # # # # T . . .\n" | |
+ ". < < # # # # # # # # # # # # # # # # # # . . .\n" | |
+ "< < < # # # # # # # # # # # # # # # # # # # . .\n" | |
+ "< < # # # # # # # # # # # # # # # # # # # # . .\n" | |
+ ". < # # # # # # # # # # # # # # # # # # # # > .\n" | |
+ ". < # # # # # # # # # # # # # # # # # # # # > .\n" | |
+ ". < # # # # # # # # # # # # # # # # # # # # > .\n" | |
+ ". . # # # # # # # # # # # # # # # # # # # # > >\n" | |
+ ". . # # # # # # # # # # # # # # # # # # # > > >\n" | |
+ ". . . # # # # # # # # # # # # # # # # # # > > .\n" | |
+ ". . . L # # # # # # # # # # # # # # # # # > . .\n" | |
+ ". . . L L # # # # # # # # # # # # # # # # . . .\n" | |
+ ". . . . L L L # # # # # # # # # # # # # . . . .\n" | |
+ ". . . . L L L L # # # # # # # # # v v . . . . .\n" | |
+ ". . . . . . . L L # # # # v v v v v . . . . . .\n" | |
+ ". . . . . . . . . . . . . v v v v . . . . . . .") | |
.split('\n') | |
.map(function(row) { | |
return row.split(' ').map(function(c) { | |
switch (c) { | |
case '#': | |
return 7; | |
case '^': | |
return 1; | |
case 'T': | |
return 2; | |
case '>': | |
return 3; | |
case 'v': | |
return 4; | |
case 'L': | |
return 5; | |
case '<': | |
return 6; | |
default: | |
return 0; | |
} | |
}); | |
}); | |
function present(a) { return '[[' + a.map(function (r) { return r.join(', ') }).join('],\n [') + ']]' }; | |
/* hex: 0 | |
___ | |
5 / \ 1 | |
4 \___/ 2 | |
3 */ | |
function renderSprites(renderer) { | |
for (var i = 0; i < data.length; ++i) | |
for (var j = 0; j < data[i].length; ++j) { | |
var v = data[i][j]; | |
data[i][j].sprite = renderer.render(v, | |
getValue(getN(0, i, j, data)), | |
getValue(getN(1, i, j, data)), | |
getValue(getN(2, i, j, data)), | |
getValue(getN(3, i, j, data)), | |
getValue(getN(4, i, j, data)), | |
getValue(getN(5, i, j, data))); | |
} | |
return renderer; | |
} | |
function getValue(obj) { | |
return obj == null ? false : obj.value; | |
} | |
function setReady(r) { | |
renderer = r; | |
loop(makeLoop(1000 / 60, fixedUpdate, update), window.requestAnimationFrame); | |
function fixedUpdate(delta) { | |
} | |
function update(delta) { | |
if (!updated) | |
return; | |
ctx.clear(); | |
ctx.save(); | |
render(delta); | |
ctx.restore(); | |
updated = false; | |
} | |
} | |
function render(delta) { | |
for (var i = 0; i < data.length; ++i) { | |
for (var j = 0; j < data[i].length; ++j) { | |
renderCell(i, j, data[i][j], data); | |
} | |
} | |
} | |
var onceMap = {}; | |
function once() { | |
var key = [].shift.apply(arguments); | |
if (key in onceMap) | |
return; | |
onceMap[key] = true; | |
} | |
function renderCell(i, j, cell, data) { | |
ctx.beginPath(); | |
var pos = getHexPos(i, j); | |
var imagedata = cell.sprite; | |
ctx.drawImage(imagedata, o.x + pos.x * renderer.scale.x, o.y + pos.y * renderer.scale.y); | |
return; | |
} | |
// Get neighbour | |
function getN(n, i, j, data) { | |
var ni = i, nj = j; | |
switch (n) { | |
case 0: // top | |
ni -= 1; | |
break; | |
case 1: | |
ni -= j % 2 ? 1 : 0; | |
nj += 1; | |
break; | |
case 2: | |
ni += j % 2 ? 0 : 1; | |
nj += 1; | |
break; | |
case 3: | |
ni += 1; | |
break; | |
case 4: | |
ni += j % 2 ? 0 : 1; | |
nj -= 1; | |
break; | |
case 5: | |
ni -= j % 2 ? 1 : 0; | |
nj -= 1; | |
break; | |
} | |
return ni >= 0 && nj >= 0 && ni < data.length && nj < data[ni].length ? data[ni][nj] : null; | |
} | |
function getHexPos(i, j) { | |
return new Pos(j * 20 - i * 4 - (j / 2 | 0) * 4, i * 19 - (j % 2) * 5 + (j / 2 | 0) * 9); | |
} | |
function Pos(x, y) { | |
this.x = x; | |
this.y = y; | |
} | |
Pos.prototype.toString = function() { | |
return '(' + this.x + '; ' + this.y + ')'; | |
} | |
function array(rows, cols, gen) { | |
var res = []; | |
for (var i = 0; i < rows; ++i) { | |
var row = []; | |
for (var j = 0; j < cols; ++j) | |
row.push(gen(i, j)); | |
res.push(row); | |
} | |
return res; | |
} | |
function Renderer(s, stencil) { | |
this.scale = s; | |
this.stencil = stencil; | |
} | |
Renderer.prototype.render = function(v, n0, n1, n2, n3, n4, n5) { | |
var can = document.createElement('canvas'); | |
can.widht = 24 * this.scale.x; | |
can.height = 21 * this.scale.y; | |
var s = this.scale; | |
var ctx = can.getContext('2d'); | |
var id = ctx.createImageData(24 * s.x, 21 * s.y); | |
var data = id.data; | |
var l = rnd(0); | |
var c = [l, l, l, 160]; | |
for (var i = 0; i < 21; ++i) { | |
for (var j = 0; j < 24; ++j) { | |
var isSet = this.isSet(v.value, i, j, n0, n1, n2, n3, n4, n5); | |
var col = isSet ? v.color || c | |
: [0, 0, 0, 0]; | |
for (var si = 0; si < s.y; ++si) { | |
for (var sj = 0; sj < s.x; ++sj) { | |
var index = ((s.y * i + si) * 24 * s.x + s.x * j + sj) * 4; | |
data[index] = col[0]; | |
data[index + 1] = col[1]; | |
data[index + 2] = col[2]; | |
data[index + 3] = col.length > 3 ? col[3] : 160; | |
} | |
} | |
} | |
} | |
ctx.putImageData(id, 0, 0); | |
return can; | |
} | |
Renderer.prototype.isSet = function (v, i, j, n0, n1, n2, n3, n4, n5) { | |
var p = this.stencil[i][j]; | |
return (v && p === 7) | |
|| (((v && n5) || (v && n0) || (n5 && n0)) && p === 1) | |
|| (((v && n0) || (v && n1) || (n0 && n1)) && p === 2) | |
|| (((v && n1) || (v && n2) || (n1 && n2)) && p === 3) | |
|| (((v && n2) || (v && n3) || (n2 && n3)) && p === 4) | |
|| (((v && n3) || (v && n4) || (n3 && n4)) && p === 5) | |
|| (((v && n4) || (v && n5) || (n4 && n5)) && p === 6); | |
} | |
setReady(renderSprites(new Renderer(s, stencil))); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment