Log in to post a comment.

Canvas.setpenopacity(1);
const turtle = new Turtle();
turtle.penup();

const QT16 = "QT16|256|256|1021|3062|3igCCHCBJEGECFCqggCCHECDhgBhwgQQHChQBhhgwgALwQIEUQ4DChhQIMEChhQIcIECDhQYIIMEChDhQYIECDhQYIECHChwQIEGChwQIEeIECDhQYIECDhQ4QIEGChwQIEGCCHChwQIEGChwQIEOEChhQIMEChhQI8gQYIECDhQYIECHChwQIEGChwQIEOEChhQIMECGCBTghgQChHChwQIEKIMEChDBhhQIMEEGChwCCDhghQIkgDBSIUQYIEC1IkQogwQIEsADhQQB4JEGChwQIEGChwhQIMEChhQIMEChDhQYIECDBGChwhQwQIEGCCDChHChwgghQIYBhhQIUQYIECHQgDBGEChChwKEYIIcIECDCGCBhFYIECFEGChwhQwQghQIMIEOkgBCFCmQYADhQYQwCBDYIECDChDBhBBFEGChwPEChBBDhQYIECVQgDhQYIECDhQYIECZIEEeQIkghgwQIE0QQgDCI8QIEGChwQIYgwhQIMEChECF4QIEGChwQIEGChwBhQChhQIMIEeQIkQYIwAMBGChQChCFChwHCCQQTwgQYADhQQDhQwQIEGSQIUDhQQCB.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qpqanMu7u7qqmJipqZiHqqmZmJmZiHZ2doiHdmZlVUVVRId2ZWV2dlRERUVDIyREQzMiEhMhIbvMnczLx2mWmqysyru4epZpi6yqq7zLy8y7urzMu8u6qqnLmaqZiHeJibqpmZmoqIh3h4d4Z3ZmZlVldmh5l2iGZVVFV3iHeIqamZmZmkRWVnh3iImZipqqqaqqmZmImIeZiHeIh3dmZVVVZWZmVmVWZlVWSqmYiZiKd1Z4Z0VEVTSJiJVkWIZ7qLh2dri5epxminiry8u8vLm5rKu6uqqpy7u7q6qZqoi7qaq7h4d2ZZipdpm7qru7iLuZvLy8zMyZm4eYm5mYd7uqqpioh3d2iHd3d3dlZVRmmENlmHd2dzJlRld3Znhmd2ZmVVVVRVVVVmZEVERmmWeZRUVVVXeZd5m7vLvMzLvLqrrLVVZVZneId4dmd5mqiJmph4i7zLu6y7usu6qqmZipmIiHmIh4d2Zqq6u5mIe7m7u4iYe7u5m7d2ZnZXaIu4i7Zndld5m7mru7uqqZmZmIeHiIh3dpiIdnZlZUZWVIiId3dndmZmVVVEQzQyESEhIiIjIhIRIiMyMiMjMiISIiIjMzIiIzMzIyQ0MiMjIjIzIyMxIjMjIjIzMzIjMjIzNDIyMiIjIjNDIzMzMyMzIiMzMjNEIiJDQiMyM0RSQkRCMjMjMkI3ZmZlVVRVVndmd6q7u7tmiWaru1VVVlVnVVVmebiruJu6u7u7REREVVVWZERVVVV4Zqq7ZoZrm7Zqe7uru7VWWmW5u2WGW7u4u7tlhlp7llZruGp7ubq7QzMkNDIjMzIjMzMzQ0NDQ0Q0NFREJCMzIzMzNEREMyREQyMjQjIkJCMjI0NDU0JDNEUyMzRTQzQ0MyNERCNEQjQ0IyM0MyQzMjIiIyNEQyIyREMxIVQ1RldodlRlMkNDKXqHqbqHmGZ2urqYqbp3h2mJh0RURUMjNlZUNDJDIiMiIiIjIyIiMjQ1M1RUNCISEyMiIiMzRUVEVFVEQyMiMzMjI0IyMjIjMzIiNDUzMjMzIyRVUjJFRVU1RUQ0MhISQkIkJFRTMiJCQiIiM1NCMzNDUA";

const circles = [];

function decodeQT16(blob, dstW, dstH) {
	const A = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._";
	const D = new Int16Array(128).fill(-1);
	for (let i = 0; i < 64; i++) D[A.charCodeAt(i)] = i;

	function parseHeader(s) {
		if (s.slice(0, 4) !== "QT16") return null;
		let p = 5;
		const n = [0, 0, 0, 0];
		for (let k = 0; k < 4; k++) {
			let v = 0;
			for (; ;) {
				const c = s.charCodeAt(p++);
				if (c === 124) break;
				v = v * 10 + (c - 48);
			}
			n[k] = v;
		}
		return { W: n[0], H: n[1], T: n[2], L: n[3], off: p };
	}

	const h = parseHeader(blob);
	if (!h) return null;

	const tree = blob.slice(h.off, h.off + h.T);
	const leaf = blob.slice(h.off + h.T, h.off + h.T + h.L);

	const W = h.W, H = h.H;

	const vw = dstW || W;
	const vh = dstH || H;
	const sx = vw / W;
	const sy = vh / H;

	let ti = 0,
		tb = 0,
		tn = 0;
	function bit() {
		if (!tn) {
			tb = D[tree.charCodeAt(ti++)] | 0;
			tn = 6;
		}
		const b = tb & 1;
		tb >>= 1;
		tn--;
		return b;
	}

	let li = 0,
		lp = 0,
		ln = 0;
	function nib() {
		if (!ln) {
			const a = D[leaf.charCodeAt(li++)] | 0;
			const b = D[leaf.charCodeAt(li++)] | 0;
			lp = (a << 6) | b;
			ln = 3;
		}
		let v;
		if (ln === 3) v = (lp >> 8) & 15;
		else if (ln === 2) v = (lp >> 4) & 15;
		else v = lp & 15;
		ln--;
		return v;
	}

	function drawLeaf(x0, y0, w, h, v) {
		let vv = 15 - v;
		if (vv <= 1) return;

		const r = w * 0.5;
        const cx = (x0 + r) * sx - vw * 0.5;
        const cy = (y0 + r) * sy - vh * 0.5;

		const rMax = (r * 0.95) * sx;

		const t = vv / 15;
		const rr = rMax * t * t;
		if (rr < 0.1) return;

		circles.push({ cx, cy, rr });

	}

	function walk(x0, y0, w, h) {
		if (!bit()) {
			const v = nib();
			drawLeaf(x0, y0, w, h, v);
			return;
		}
		const hw = w >> 1, hh = h >> 1;
		const wR = w - hw, hB = h - hh;

		walk(x0, y0, hw, hh);
		walk(x0 + hw, y0, wR, hh);
		walk(x0, y0 + hh, hw, hB);
		walk(x0 + hw, y0 + hh, wR, hB);
	}

	walk(0, 0, W, H);
	return { W, H };
}

function circlePoly(cx, cy, r) {
    if (r < 0) return;
	const steps = Math.max(10, Math.min(48, (r * 10) | 0));
	const a0 = 0;
	const da = (Math.PI * 2) / steps;

	turtle.penup();
	turtle.goto(cx + r, cy);
	turtle.pendown();
	for (let i = 1; i <= steps; i++) {
		const a = a0 + da * i;
		turtle.goto(cx + Math.cos(a) * r, cy + Math.sin(a) * r);
	}
	turtle.penup();
}

circles.length = 0;
decodeQT16(QT16, 195, 195);

circles.sort((a, b) => {
	const da = a.cx * a.cx + a.cy * a.cy;
	const db = b.cx * b.cx + b.cy * b.cy;
	return a.rr * da - b.rr * db;
});

let id = 0;
let t0 = performance.now();
const DT = 0.5; // ms

function walk(i) {
	const t = performance.now();
	if (t < t0 + DT) return true;
	t0 = t;

	const c = circles[id++];
	circlePoly(c.cx, c.cy, c.rr);
	circlePoly(c.cx, c.cy, c.rr - 0.15);
	return id < circles.length - 1;
}