Накoнец-то чуточку продвинулся в решении поставленной задачи.
Если раньше пытался решать её со стороны графики - сканированием изображения, что просаживало производительность.
То теперь реализован математический подход.
И можно видеть, чертя мышью лабиринт на чёрном поле, насколько отзывчиво производится его предварительная обработка в мерцающую фигуру.
<html>
<head>
<style>
body {
margin :0px 0px 0px 0px;
padding :0px 0px 0px 0px;
}
</style>
<script>
var hPprCnv;
var hPprCtx;
var hSrcCnv;
var hSrcCtx;
var hCtrCnv;
var hCtrCtx;
var hPrvCnv;
var hPrvCtx;
var hRslCnv;
var hRslCtx;
var vecs;
var circle;
var iStepFactor = 32;
// sharp
// Процедура бинарной резкости
CanvasRenderingContext2D.prototype.sharp =
function () {
var imd = this.getImageData(0, 0, this.canvas.width, this.canvas.height);
var p = imd.data;
var i = p.length,
r, g, b,
x, y, z = 384;
var mask;
while(i >= 4) {
-- i;
r = (p[-- i] >> 7) * 255; g = (p[-- i] >> 7) * 255; b = (p[-- i] >> 7) * 255;
mask = z < r + g + b ? 255 : 0;
p[i + 3] = 255, p[i + 2] = mask, p[i + 1] = mask, p[i + 0] = mask;
if(mask)
y = i >> 2;
}
x = y % this.canvas.width;
y = (y - x) / this.canvas.width;
this.putImageData(imd, 0, 0);
return {
x :x + 1,
y :y + 1
}
}
// Процедура обнаружения границ
CanvasRenderingContext2D.prototype.findEdge =
function () {
var w = this.canvas.width;
var j = i - w * 4 - 4;
var x, y, z = 384;
var r, g, b;
var k, m, r;
var dx, dy;
var minx = 1 << 30;
var miny = 1 << 30;
var maxx = 0;
var maxy = 0;
var mindy;
var maxdy;
var arr = [];
var corners = [];
var borders = [];
var tmp;
var lasta = "";
var lastx, lasty;
//
this.save();
this.globalCompositeOperation = "difference";
this.drawImage(this.canvas, 1, 1);
this.restore();
var imd = this.getImageData(1, 1, this.canvas.width, this.canvas.height);
var buf = imd.data;
var i = imd.data.length;
var pixels = new Uint32Array(imd.data.buffer);
var j = pixels.length;
var fnd = -1;
do {
fnd = Array.prototype.indexOf.call(pixels, 0xFFFFFFFF, fnd + 1);
if(fnd >= 0) {
x = fnd % this.canvas.width,
y = Math.floor(fnd / this.canvas.width);
minx = Math.min(minx, x);
miny = Math.min(miny, y);
maxx = Math.min(maxx, x);
maxy = Math.min(maxy, y);
arr.push({
x :x,
y :y,
a :""
});
}
} while(fnd >= 0);
// Сортировка пикселей контура в цепочку
for(i = 0; i < arr.length - 1; ++ i) {
x = arr[i].x;
y = arr[i].y;
k = 0;
for(j = i + 1; j < arr.length; ++ j) {
dx = arr[j].x - x, dy = arr[j].y - y;
r = Math.sqrt(dx * dx + dy * dy);
if(k == 0 || m > r)
m = r,
k = j;
}
arr.splice(i + 1, 0, arr.splice(k, 1)[0]);
}
/*var point = { x: arr[0].x, y: arr[0].y };
arr.sort((a, b) => {
var d = (a.x - point.x) ** 2 + (a.y - point.y) ** 2 - (b.x - point.x) ** 2 + (b.y - point.y) ** 2;
point = { x: b.x, y: b.y};
return d;
});*/
// Построение "маршрута" движения (векторной ориентации) линий контура
for(i = 0; i < arr.length; ++ i) {
var east = 0;
var west = 0;
var south = 0;
var north = 0;
for(j = 1; j <= iStepFactor; ++ j) {
tmp = arr[j];
dx = tmp.x - arr[0].x, dy = tmp.y - arr[0].y;
if(Math.abs(dx) > Math.abs(dy)) {
if(dx < 0)
east ++;
else
west ++;
} else
if(dy < 0)
south ++;
else
north ++;
}
var max = Math.max(east, west, north, south);
arr[0].a = max == east ? "E" : max == west ? "W" : max == north ? "N" : "S";
if(lasta != arr[0].a) {
corners.push({
x :arr[0].x,
y :arr[0].y,
a :lasta
});
if(lasta != "") {
if(!(lasta == "E" || "W" == lasta))
borders.push({
x1 :lastx,
y1 :lasty,
x2 :arr[0].x,
y2 :lasty
});
else
borders.push({
x1 :lastx,
y1 :lasty,
x2 :lastx,
y2 :arr[0].y
});
}
lastx = arr[0].x,
lasty = arr[0].y;
}
lasta = arr[0].a;
arr.push(arr.shift());
}
for(i = 0; i < corners.length - 1; ++ i) {
x = corners[i].x;
y = corners[i].y;
for(j = i + 1; j < corners.length; ++ j) {
}
}
//
corners.push({
x :arr[0].x,
y :arr[0].y,
a :lasta
});
return {
arr :arr,
corners :corners,
borders :borders
};
}
var vecmax = 0;
var vecidx = 0;
var paths = [{len: []}];
var car = {
x :0,
y :100,
dx :1,
dy :1,
dir :0.0,
cdx :0, // Counter by dx
cdy :0 // Counter by dy
};
var pen = {
x :0,
y :100
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
var lapping = 0;
var race = [];
var curdir = "";
var cntdir = "";
var waydir = "";
var lastdir = "";
function run() {
var colors = {
"N" :"red",
"S" :"magenta",
"E" :"blue",
"W" :"cyan"
};
pixels.unshift(pixels.pop());
hRslCtx.clearRect(0, 0, hRslCnv.width, hRslCnv.height);
hRslCtx.beginPath();
var x, y, nx, ny;
for(i = 0; i < corners.length; ++ i) {
nx = corners[i].x, ny = corners[i].y;
if(corners[i].a == "E" || "W" == corners[i].a) {
hRslCtx.lineTo(x, ny);
} else {
hRslCtx.lineTo(nx, y);
}
dx = nx - x, dy = ny - y;
;
x = nx, y = ny;
if(i)
hRslCtx.lineTo(x, y);
else
hRslCtx.moveTo(x, y);
}
hRslCtx.fillStyle = "grey brown red orange yellow green cyan blue magenta black".split(" ")[Math.floor(Math.random() * 10)];
hRslCtx.fillStyle = "grey silver".split(" ")[Math.floor(Math.random() * 2)];
hRslCtx.closePath();
hRslCtx.fill();
//hRslCtx.stroke();
hRslCtx.beginPath();
for(i = 0; i < borders.length; ++ i) {
nx = borders[i].x1, ny = borders[i].y1;
hRslCtx.moveTo(nx, ny);
hRslCtx.lineTo(borders[i].x2, borders[i].y2);
}
hRslCtx.strokeStyle = "grey brown red orange yellow green cyan blue magenta black".split(" ")[Math.floor(Math.random() * 10)];
hRslCtx.closePath();
hRslCtx.lineWidth = 4;
hRslCtx.stroke();
return;
////////////////////////////////////////////////////////////////////////////////
}
var hqr;
var pixels;
var corners;
var borders;
function main() {
hPprCnv = document.getElementById("Paper");
hSrcCnv = document.getElementById("Source");
hCtrCnv = document.getElementById("Contour");
hPrvCnv = document.getElementById("Preview");
hRslCnv = document.getElementById("Result");
hSrcCnv.setAttribute('crossOrigin', '');
hPprCtx = hPprCnv.getContext("2d");
hCtrCtx = hCtrCnv.getContext("2d");
hSrcCtx = hSrcCnv.getContext("2d");
hPrvCtx = hPrvCnv.getContext("2d");
hRslCtx = hRslCnv.getContext("2d");
hPprCtx.fillStyle = "#FF00000";
hPprCtx.fillRect(0, 0, hPprCnv.width, hPprCnv.height);
hSrcCtx.fillStyle = "#FF000000";
hPrvCtx.fillStyle = "#CCCCCC";
hSrcCtx.fillRect(0, 0, hSrcCnv.width, hSrcCnv.height);
hPprCnv.addEventListener("mousemove",
function(e) {
x = e.offsetX;
y = e.offsetY;
if(e.buttons == 1) {
hPprCtx.fillStyle = "white";
hPprCtx.fillRect(x, y, 16, 16);
hSrcCtx.drawImage(hPprCnv, 0, 0);
localStorage.draftImage = hPprCnv.toDataURL();
Start();
}
}
);
hPprCnv.addEventListener("mouseup",
function(e) {
hSrcCtx.drawImage(hPprCnv, 0, 0);
localStorage.draftImage = hPprCnv.toDataURL();
Start();
}
);
hSrcCnv.addEventListener("mousemove",
function(e) {
x = e.offsetX;
y = e.offsetY;
if(e.buttons == 1) {
hSrcCtx.fillStyle = "white";
hSrcCtx.fillRect(x, y, 16, 16);
}
}
);
if(localStorage.draftImage) {
var im = new Image();
im.src = localStorage.draftImage;
im.addEventListener("load", function(e) {
hPprCtx.drawImage(e.target, 0, 0);
}
);
}
}
function loadDefault() {
hSrcCtx.drawImage(document.getElementsByTagName("img")[0], 0, 0, hSrcCnv.width, hSrcCnv.height);
setTimeout('Start(); hSrcCnv.style.qdisplay="none"; setInterval("run(); run(); run();", 1);', 1000);
}
function Start() {
vecs = [hSrcCtx.sharp()];
car.x = vecs[0].x;
car.y = vecs[0].y;
pen.x = car.x;
pen.y = car.y;
tmp = hSrcCtx.findEdge();
pixels = tmp.arr;
corners = tmp.corners;
borders = tmp.borders;
car.x = pixels[0].x;
car.y = pixels[0].y;
lapping = 0;
race = [];
waydir = "";
curdir = "";
cntdir = 0;
}
function Clear() {
hSrcCtx.fillStyle="black";
hSrcCtx.fillRect(0, 0, hSrcCnv.width, hSrcCnv.height);
hPprCtx.fillStyle = "black";
hPprCtx.fillRect(0, 0, hPprCnv.width, hPprCnv.height);
}
</script>
<style>
img,
canvas#Source,
canvas#Contour,
canvas#Preview {
display :none;
}
</style>
</head>
<body onload='main()'>
<button onclick='hqr.Bug()' style=display:none>Step</button>
<button onclick='Clear()'>Clear</button>
<button onclick='Start()' style=display:none>Start</button><hr />
<canvas id='Paper' width='320' height='240'></canvas><canvas id='Source' width='320' height='240'></canvas>
<canvas id='Contour' width='320' height='240' style='display:none'></canvas><canvas id='Preview' width='320' height='240'></canvas>
<canvas id='Result' width='320' height='240'></canvas>
<img style=display:none title='animanu.gif' src1='https://i.imgur.com/oZBA50h.png' src='https://i.imgur.com/NDa8KXn.gif' crossorigin = '' onload='setTimeout(loadDefault, 100)' /></img>
<div style=display:none>
<pre id='log'>---</pre>
<pre id='Log'></pre>
</div>
</body>
</html>