Unveil
Her image will never leave my mind.
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;
}