Показать сообщение отдельно
  #17 (permalink)  
Старый 15.05.2021, 07:36
Аватар для Paguo-86PK
Профессор
Отправить личное сообщение для Paguo-86PK Посмотреть профиль Найти все сообщения от Paguo-86PK
 
Регистрация: 16.09.2009
Сообщений: 253

Human Quick Response Labyrintic Text
Нак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>

Последний раз редактировалось Paguo-86PK, 16.05.2021 в 04:12.
Ответить с цитированием