Skip to content

Instantly share code, notes, and snippets.

@zzandy
Last active November 3, 2016 12:39
Show Gist options
  • Save zzandy/c83740c23255944254f89ca7998987d6 to your computer and use it in GitHub Desktop.
Save zzandy/c83740c23255944254f89ca7998987d6 to your computer and use it in GitHub Desktop.
Streamlined stencil, independent x/y scales.
<!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