<div class="canvas" name="polyline-jsfeat">
<canvas id="polyline-jsfeat" width="300" height="200"></canvas>
<script src="jsfeat-min.js"></script>
<script id="polyline-jsfeat.js">
(function(){
	var canvas = document.getElementById('polyline-jsfeat');
	var ctx = canvas.getContext('2d');
	ctx.lineWidth = 2;	// stroke style

	// mouse
	var x, y;	// Array
	var k = 0;	// smooth factor
	var pressed = false;
	canvas.onmousedown = function(e) {
		pressed = true;
		x = new Array();
		y = new Array();
		x.push(e.offsetX);
		y.push(e.offsetY);
		ctx.strokeStyle = 'lightgray';
		ctx.clearRect(0, 0, canvas.width, canvas.height);
		text();
		ctx.beginPath();
		ctx.moveTo(e.offsetX, e.offsetY);
	};
	canvas.onmousemove = function(e) {
		if (!pressed) return;
		x.push(e.offsetX);
		y.push(e.offsetY);
		ctx.lineTo(e.offsetX, e.offsetY);
		ctx.stroke();
		ctx.beginPath();
		ctx.moveTo(e.offsetX, e.offsetY);
	}
	canvas.onmouseup = function() {
		pressed = false;
		ctx.clearRect(0, 0, canvas.width, canvas.height);
//		draw(x, y, 'lightgray');
		k = 0;
		eigen();
		draw(...smooth(k), 'red');
		text();
	};

	// keyboard
	canvas.tabIndex = 0;
	canvas.onmouseover = canvas.focus;
	canvas.onmouseout = canvas.blur;
	canvas.onkeydown = function(e) {
		if (pressed) return;
		if      (e.keyCode == 90) k = Math.max(0, k - 1);	// z
		else if (e.keyCode == 88) k = Math.min(200, k + 1);	// x
		ctx.clearRect(0, 0, canvas.width, canvas.height);
		draw(x, y, 'lightgray');
		draw(...smooth(k), 'red');
		text();
	};

	text();
	function text() {
		ctx.fillStyle = "green";
		ctx.font = "12pt Verdana";
		ctx.textAlign = "center";
		ctx.textBaseline = "top";
		ctx.fillText("draw closed curve", canvas.width/2, 0);
		ctx.textBaseline = "bottom";
		ctx.fillText("smooth " + k + " times   [z] -1  [x] +1", canvas.width/2, canvas.height);
	}

	var n;
	var matrix;
	var evectors;
	var evalues;
	var xo, xc, xc2;
	var yo, yc, yc2;

	function eigen() {
		n = x.length;
		matrix = new jsfeat.matrix_t(n, n, jsfeat.F64_t | jsfeat.C1_t);
		for (var i=0; i<n; ++i) {
			matrix.data[i * n + (i-1+n)%n] = 0.5;
			matrix.data[i * n + i        ] = 0;
			matrix.data[i * n + (i+1)%n  ] = 0.5;
		}
		// eigenvector is filled row by row
		evectors = new jsfeat.matrix_t(n, n, jsfeat.F64_t | jsfeat.C1_t);
		evalues  = new jsfeat.matrix_t(1, n, jsfeat.F64_t | jsfeat.C1_t);
		jsfeat.linalg.eigenVV(matrix, evectors, evalues);

		xo  = new jsfeat.matrix_t(1, n, jsfeat.F64_t | jsfeat.C1_t);
		yo  = new jsfeat.matrix_t(1, n, jsfeat.F64_t | jsfeat.C1_t);
		xc  = new jsfeat.matrix_t(1, n, jsfeat.F64_t | jsfeat.C1_t);
		yc  = new jsfeat.matrix_t(1, n, jsfeat.F64_t | jsfeat.C1_t);
		xc2 = new jsfeat.matrix_t(1, n, jsfeat.F64_t | jsfeat.C1_t);
		yc2 = new jsfeat.matrix_t(1, n, jsfeat.F64_t | jsfeat.C1_t);
		for (var i=0; i<n; ++i) xo.data[i] = x[i];
		for (var i=0; i<n; ++i) yo.data[i] = y[i];
		jsfeat.matmath.multiply(xc, evectors, xo);
		jsfeat.matmath.multiply(yc, evectors, yo);
	}

	function smooth(k) {
		for (var i=0; i<n; ++i) {
			var scalar = Math.pow(evalues.data[i], k);
			xc2.data[i] = xc.data[i] * scalar;
			yc2.data[i] = yc.data[i] * scalar;
		}
		jsfeat.matmath.multiply_AtB(xo, evectors, xc2);
		jsfeat.matmath.multiply_AtB(yo, evectors, yc2);
		return [xo.data, yo.data];
	}

	function draw(x, y, color) {
		var n = x.length;
		ctx.strokeStyle = color;
		ctx.beginPath();
		ctx.moveTo(x[n-1], y[n-1]);
		for (var i=0; i<n; ++i) ctx.lineTo(x[i], y[i]);
		ctx.stroke();
	}
})();
</script>
</div>
<div class="canvas" name="polyline-mathjs">
<canvas id="polyline-mathjs" width="300" height="200"></canvas>
<script src="math.js"></script>
<script id="polyline-mathjs.js">
(function(){
	var canvas = document.getElementById('polyline-mathjs');
	var ctx = canvas.getContext('2d');
	ctx.lineWidth = 2;	// stroke style

	// mouse
	var x, y;	// Array
	var k = 0;	// smooth factor
	var pressed = false;
	canvas.onmousedown = function(e) {
		pressed = true;
		x = new Array();
		y = new Array();
		x.push(e.offsetX);
		y.push(e.offsetY);
		ctx.strokeStyle = 'lightgray';
		ctx.clearRect(0, 0, canvas.width, canvas.height);
		text();
		ctx.beginPath();
		ctx.moveTo(e.offsetX, e.offsetY);
	};
	canvas.onmousemove = function(e) {
		if (!pressed) return;
		x.push(e.offsetX);
		y.push(e.offsetY);
		ctx.lineTo(e.offsetX, e.offsetY);
		ctx.stroke();
		ctx.beginPath();
		ctx.moveTo(e.offsetX, e.offsetY);
	}
	canvas.onmouseup = function() {
		pressed = false;
		ctx.clearRect(0, 0, canvas.width, canvas.height);
//		draw(x, y, 'lightgray');
		k = 0;
		eigen();
		draw(...smooth(k), 'red');
		text();
	};

	// keyboard
	canvas.tabIndex = 0;
	canvas.onmouseover = canvas.focus;
	canvas.onmouseout = canvas.blur;
	canvas.onkeydown = function(e) {
		if (pressed) return;
		if      (e.keyCode == 90) k = Math.max(0, k - 1);	// z
		else if (e.keyCode == 88) k = Math.min(200, k + 1);	// x
		ctx.clearRect(0, 0, canvas.width, canvas.height);
		draw(x, y, 'lightgray');
		draw(...smooth(k), 'red');
		text();
	};

	text();
	function text() {
		ctx.fillStyle = "green";
		ctx.font = "12pt Verdana";
		ctx.textAlign = "center";
		ctx.textBaseline = "top";
		ctx.fillText("draw closed curve", canvas.width/2, 0);
		ctx.textBaseline = "bottom";
		ctx.fillText("smooth " + k + " times   [z] -1  [x] +1", canvas.width/2, canvas.height);
	}

	var n;
	var A;
	var eig, E, ET;
	var xc, yc;

	function eigen() {
		n = x.length;
		A = math.zeros(n, n);
		A._data[0][1] = A._data[0][n-1] = .5;
		A._data[n-1][n-1-1] = A._data[n-1][0] = .5;
		for (var i=1; i<n-1; ++i) {
			A._data[i][i-1] = .5;
			A._data[i][i+1] = .5;
		}
		eig = math.eigs(A, .1);

		E = math.matrixFromColumns(...eig.eigenvectors.map(x => x.vector));
		ET = math.transpose(E);
		xc = math.multiply(ET, x);
		yc = math.multiply(ET, y);
		xc2 = math.clone(xc);
		yc2 = math.clone(yc);
	}

	function smooth(k) {
		for (var i=0; i<n; ++i) {
			var scalar = math.pow(eig.values._data[i], k);
			xc2._data[i] = xc._data[i] * scalar;
			yc2._data[i] = yc._data[i] * scalar;
		}
		var xs = math.multiply(E, xc2);
		var ys = math.multiply(E, yc2);
		return [xs._data, ys._data];
	}

	function draw(x, y, color) {
		var n = x.length;
		ctx.strokeStyle = color;
		ctx.beginPath();
		ctx.moveTo(x[n-1], y[n-1]);
		for (var i=0; i<n; ++i) ctx.lineTo(x[i], y[i]);
		ctx.stroke();
	}
})();
</script>
<!--
A = [0 1 0 0 0; 1 0 1 0 0; 0 1 0 1 0; 0 0 1 0 1; 0 0 0 1 0];
eigs(A)

A = [2 0 0 0; 1 0 1 0; 0 1 0 1; 0 0 0 2];
[V,D] = eigs(A)

A = [2 0 0 0 0; 1 0 1 0 0; 0 1 0 1 0; 0 0 1 0 1; 0 0 0 0 2];
[V,D] = eigs(A)

A = [2 0 0 0 0 0;
     1 0 1 0 0 0;
     0 1 0 1 0 0;
     0 0 1 0 1 0;
     0 0 0 1 0 1;
     0 0 0 0 0 2];
[V,D] = eigs(A)

A = [2 0 0 0 0 0 0;
     1 0 1 0 0 0 0;
     0 1 0 1 0 0 0;
     0 0 1 0 1 0 0;
     0 0 0 1 0 1 0;
     0 0 0 0 1 0 1;
     0 0 0 0 0 0 2];

[V,D] = eigs(A)
-->
</div>