261 lines
7.4 KiB
JavaScript
261 lines
7.4 KiB
JavaScript
"use strict";
|
|
|
|
function matrix2path(matrix) {
|
|
var N = matrix.length;
|
|
var filled = [];
|
|
for (var row = -1; row <= N; row++) {
|
|
filled[row] = [];
|
|
}
|
|
|
|
var path = [];
|
|
for (var row = 0; row < N; row++) {
|
|
for (var col = 0; col < N; col++) {
|
|
if (filled[row][col]) continue;
|
|
filled[row][col] = 1;
|
|
if (isDark(row, col)) {
|
|
if (!isDark(row - 1, col)) {
|
|
path.push(plot(row, col, 'right'));
|
|
}
|
|
} else {
|
|
if (isDark(row, col - 1)) {
|
|
path.push(plot(row, col, 'down'));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return path;
|
|
|
|
function isDark(row, col) {
|
|
if (row < 0 || col < 0 || row >= N || col >= N) return false;
|
|
return !!matrix[row][col];
|
|
}
|
|
|
|
function plot(row0, col0, dir) {
|
|
filled[row0][col0] = 1;
|
|
var res = [];
|
|
res.push(['M', col0, row0 ]);
|
|
var row = row0;
|
|
var col = col0;
|
|
var len = 0;
|
|
do {
|
|
switch (dir) {
|
|
case 'right':
|
|
filled[row][col] = 1;
|
|
if (isDark(row, col)) {
|
|
filled[row - 1][col] = 1;
|
|
if (isDark(row - 1, col)) {
|
|
res.push(['h', len]);
|
|
len = 0;
|
|
dir = 'up';
|
|
} else {
|
|
len++;
|
|
col++;
|
|
}
|
|
} else {
|
|
res.push(['h', len]);
|
|
len = 0;
|
|
dir = 'down';
|
|
}
|
|
break;
|
|
case 'left':
|
|
filled[row - 1][col - 1] = 1;
|
|
if (isDark(row - 1, col - 1)) {
|
|
filled[row][col - 1] = 1;
|
|
if (isDark(row, col - 1)) {
|
|
res.push(['h', -len]);
|
|
len = 0;
|
|
dir = 'down';
|
|
} else {
|
|
len++;
|
|
col--;
|
|
}
|
|
} else {
|
|
res.push(['h', -len]);
|
|
len = 0;
|
|
dir = 'up';
|
|
}
|
|
break;
|
|
case 'down':
|
|
filled[row][col - 1] = 1;
|
|
if (isDark(row, col - 1)) {
|
|
filled[row][col] = 1;
|
|
if (isDark(row, col)) {
|
|
res.push(['v', len]);
|
|
len = 0;
|
|
dir = 'right';
|
|
} else {
|
|
len++;
|
|
row++;
|
|
}
|
|
} else {
|
|
res.push(['v', len]);
|
|
len = 0;
|
|
dir = 'left';
|
|
}
|
|
break;
|
|
case 'up':
|
|
filled[row - 1][col] = 1;
|
|
if (isDark(row - 1, col)) {
|
|
filled[row - 1][col - 1] = 1;
|
|
if (isDark(row - 1, col - 1)) {
|
|
res.push(['v', -len]);
|
|
len = 0;
|
|
dir = 'left';
|
|
} else {
|
|
len++;
|
|
row--;
|
|
}
|
|
} else {
|
|
res.push(['v', -len]);
|
|
len = 0;
|
|
dir = 'right';
|
|
}
|
|
break;
|
|
}
|
|
} while (row != row0 || col != col0);
|
|
return res;
|
|
}
|
|
}
|
|
|
|
function pushSVGPath(matrix, stream, margin) {
|
|
matrix2path(matrix).forEach(function(subpath) {
|
|
var res = '';
|
|
for (var k = 0; k < subpath.length; k++) {
|
|
var item = subpath[k];
|
|
switch (item[0]) {
|
|
case 'M':
|
|
res += 'M' + (item[1] + margin) + ' ' + (item[2] + margin);
|
|
break;
|
|
default:
|
|
res += item.join('');
|
|
}
|
|
}
|
|
res += 'z';
|
|
stream.push(res);
|
|
});
|
|
}
|
|
|
|
function SVG_object(matrix, margin) {
|
|
var stream = [];
|
|
pushSVGPath(matrix, stream, margin);
|
|
|
|
var result = {
|
|
size: matrix.length + 2 * margin,
|
|
path: stream.filter(Boolean).join('')
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
function SVG(matrix, stream, margin, size) {
|
|
var X = matrix.length + 2 * margin;
|
|
stream.push('<svg xmlns="http://www.w3.org/2000/svg" ');
|
|
if (size > 0) {
|
|
var XY = X * size;
|
|
stream.push('width="' + XY + '" height="' + XY + '" ');
|
|
}
|
|
stream.push('viewBox="0 0 ' + X + ' ' + X + '">');
|
|
stream.push('<path d="');
|
|
pushSVGPath(matrix, stream, margin);
|
|
stream.push('"/></svg>');
|
|
stream.push(null);
|
|
}
|
|
|
|
function EPS(matrix, stream, margin) {
|
|
var N = matrix.length;
|
|
var scale = 9;
|
|
var X = (N + 2 * margin) * scale;
|
|
stream.push([
|
|
'%!PS-Adobe-3.0 EPSF-3.0',
|
|
'%%BoundingBox: 0 0 ' + X + ' ' + X,
|
|
'/h { 0 rlineto } bind def',
|
|
'/v { 0 exch neg rlineto } bind def',
|
|
'/M { neg ' + (N + margin) + ' add moveto } bind def',
|
|
'/z { closepath } bind def',
|
|
scale + ' ' + scale + ' scale',
|
|
''
|
|
].join('\n'));
|
|
|
|
matrix2path(matrix).forEach(function(subpath) {
|
|
var res = '';
|
|
for (var k = 0; k < subpath.length; k++) {
|
|
var item = subpath[k];
|
|
switch (item[0]) {
|
|
case 'M':
|
|
res += (item[1] + margin) + ' ' + item[2] + ' M ';
|
|
break;
|
|
default:
|
|
res += item[1] + ' ' + item[0] + ' ';
|
|
}
|
|
}
|
|
res += 'z\n';
|
|
stream.push(res);
|
|
});
|
|
|
|
stream.push('fill\n%%EOF\n');
|
|
stream.push(null);
|
|
}
|
|
|
|
function PDF(matrix, stream, margin) {
|
|
// TODO deflate
|
|
var N = matrix.length;
|
|
var scale = 9;
|
|
var X = (N + 2 * margin) * scale;
|
|
var data = [
|
|
'%PDF-1.0\n\n',
|
|
'1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj\n',
|
|
'2 0 obj << /Type /Pages /Count 1 /Kids [ 3 0 R ] >> endobj\n',
|
|
];
|
|
data.push('3 0 obj << /Type /Page /Parent 2 0 R /Resources <<>> ' +
|
|
'/Contents 4 0 R /MediaBox [ 0 0 ' + X + ' ' + X + ' ] >> endobj\n');
|
|
|
|
var path = scale + ' 0 0 ' + scale + ' 0 0 cm\n';
|
|
path += matrix2path(matrix).map(function(subpath) {
|
|
var res = '';
|
|
var x, y;
|
|
for (var k = 0; k < subpath.length; k++) {
|
|
var item = subpath[k];
|
|
switch (item[0]) {
|
|
case 'M':
|
|
x = item[1] + margin;
|
|
y = N - item[2] + margin;
|
|
res += x + ' ' + y + ' m ';
|
|
break;
|
|
case 'h':
|
|
x += item[1];
|
|
res += x + ' ' + y + ' l ';
|
|
break;
|
|
case 'v':
|
|
y -= item[1];
|
|
res += x + ' ' + y + ' l ';
|
|
break;
|
|
}
|
|
}
|
|
res += 'h';
|
|
return res;
|
|
}).join('\n');
|
|
path += '\nf\n';
|
|
data.push('4 0 obj << /Length ' + path.length + ' >> stream\n' +
|
|
path + 'endstream\nendobj\n');
|
|
|
|
var xref = 'xref\n0 5\n0000000000 65535 f \n';
|
|
for (var i = 1, l = data[0].length; i < 5; i++) {
|
|
xref += ('0000000000' + l).substr(-10) + ' 00000 n \n';
|
|
l += data[i].length;
|
|
}
|
|
data.push(
|
|
xref,
|
|
'trailer << /Root 1 0 R /Size 5 >>\n',
|
|
'startxref\n' + l + '\n%%EOF\n'
|
|
);
|
|
stream.push(data.join(''));
|
|
stream.push(null);
|
|
}
|
|
|
|
module.exports = {
|
|
svg: SVG,
|
|
eps: EPS,
|
|
pdf: PDF,
|
|
svg_object: SVG_object
|
|
}
|