Penrose Tiles

https://habr.com/post/359244/

by Georg Mierau

HTML

<html>
	<style>
		html {
			margin: 0;
			padding: 0;
		}
		body {
			margin: 0;
			padding: 0;		
		}
		canvas {
			margin: 0;
			padding: 0;
			position: absolute;
			z-index: 0;	
		}
		input {
			width: 75px;
		}
		div {
			margin: 10px;
			padding: 10px 10px 10px 20px;
			position: absolute;
			z-index: 1;
			background: rgba(255, 255, 255, 0.75);
		}
	</style>
	
	<body>
		<canvas id="a"></canvas>
		<div>
			<p>Canvas size: <input type="number" id="w" value="640"> &times; <input type="number" id="h" value="640"></p>
			<p>Tile colors: <input type="text" id="c1" value="#f1c40f"> <input type="text" id="c2" value="#f39c12"> <input type="text" id="c3" value="#95a5a6"> <input type="text" id="c4" value="#7f8c8d"> <input type="text" id="c5" value="#ecf0f1"> <input type="text" id="c6" value="#bdc3c7"></p>	
			<p>Line width: <input type="number" id="lw" value="0.5"></p> 
			<p>Line color: <input type="text" id="lc" value="#fff"></p> 
			<p>Levels: <input type="number" id="levels" value="3"></p>
		</div>
	</body>
</html>

CSS

html {
	margin: 0;
	padding: 0;
}
body {
	margin: 0;
	padding: 0;		
}
canvas {
	margin: 0;
	padding: 0;
	position: absolute;
	z-index: 0;	
}
input {
  width: 75px;
}
div {
	margin: 10px;
	padding: 10px 10px 10px 20px;
	position: absolute;
	z-index: 1;
	background: rgba(255, 255, 255, 0.75);
}

JavaScript

function start() 
{

	var canvas = document.getElementById("a");
	
	canvas.width = parseInt(document.getElementById("w").value);
	canvas.height = parseInt(document.getElementById("h").value);
	
	var b = canvas.getContext("2d");

	// начать описание контура
	function begin(){b.beginPath();}
	// начать линию с точки
	function from(p) {b.moveTo(s[8] + p[0], s[9] - p[1]);}
	// привести линию к точке
	function to(p){b.lineTo(s[8] + p[0], s[9] - p[1]);} 
	// привести линию к начальной точке, не требуется если выполняется только заливка
	function close(){b.closePath();}
	// заливка
	function fill(color){b.fillStyle = color; b.fill();}
	// обводка контура линией
	function line(){b.strokeStyle = document.getElementById("lc").value; b.lineWidth = parseInt(document.getElementById("lw").value); b.stroke();}
	function line_white(){b.strokeStyle = document.getElementById("lc").value; b.lineWidth = parseInt(document.getElementById("lw").value); b.stroke();}
	function line_black(){b.strokeStyle = "#444"; b.lineWidth = 1.5; b.stroke();}

	var s;
	function prepare()
	{
		var sqrt = Math.sqrt;
		var fi = (sqrt(5) - 1) / 2;
		var fb = (sqrt(5) + 1) / 2;
		var f3 = sqrt(3 + fi);
		var f2 = sqrt(2 - fi);
		//координаты базовых векторов для всех десяти направлений
		var vt = [[ 2, 0, 0, 0], [ 1, 1, 0, 1], [ 0, 1, 1, 0], [ 0,-1, 1, 0], [-1,-1, 0, 1],
				  [-2, 0, 0, 0], [-1,-1, 0,-1], [ 0,-1,-1, 0], [ 0, 1,-1, 0], [ 1, 1, 0,-1]];
		// Константы множители координат
		var c = [1/2, fi/2, f3/2, f2/2]
		// Общий массив констант
		// нулевой элемент для контекста
		// седьмой для дополнительных данных рисования.
		// восьмой и девятый для указания центра рисования
		// десятый для размера шага.
		var s = [0, vt, c, fi, f3, f2, 0, 0, 0, 0];
		return s;
	}

	s = prepare();
	s[0] = b; 
	s[7] = 1;      // доля последнего уровня
	s[8] = 500/2; // сдвих координаты x
	s[9] = 320/2;  // сдвиг координаты y

	var f = []; // слои разбиения.
	f[0] = [];
	f[0].push([0,[ 0, 0, 0, 0],0]); 
	f[0].push([1,[...