From 9d7dc53a59c3e2192b650ef933633698483e0da8 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 07:56:17 +0000 Subject: [PATCH 1/9] =?UTF-8?q?=EC=8B=9C=EC=9E=91=20=E2=80=94=20=ED=8C=A8?= =?UTF-8?q?=ED=84=B4=20=EC=97=B0=EA=B5=AC=20102=ED=8C=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Bc4JkshDz129z5fXhUWQxz From b899fb0d6ca35a82c4fcd61d1c6b2906258a6c8b Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 08:26:34 +0000 Subject: [PATCH 2/9] =?UTF-8?q?=ED=8C=A8=ED=84=B4=20=EC=97=B0=EA=B5=AC=201?= =?UTF-8?q?02=ED=8C=90:=20Kendall=20Shape=20Sphere=20=EC=99=80=20Photo=20F?= =?UTF-8?q?inish=20=EB=A5=BC=20=EB=8D=94=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Bc4JkshDz129z5fXhUWQxz --- .../pattern-study/patterns/kendall-shapes.js | 400 ++++++++++++++++++ .../pattern-study/patterns/photo-finish.js | 342 +++++++++++++++ 2 files changed, 742 insertions(+) create mode 100644 incoming/pattern-study/patterns/kendall-shapes.js create mode 100644 incoming/pattern-study/patterns/photo-finish.js diff --git a/incoming/pattern-study/patterns/kendall-shapes.js b/incoming/pattern-study/patterns/kendall-shapes.js new file mode 100644 index 00000000..f71e4c55 --- /dev/null +++ b/incoming/pattern-study/patterns/kendall-shapes.js @@ -0,0 +1,400 @@ +// ===== Patternflow pattern ===== +// Title: Kendall Shape Sphere +// Family: data +// Idea: Random triangles are drawn one after another in a box at the top, each from three points thrown by a chosen law, and each drops its shape point onto Kendall's shape sphere turning below, where equilateral triangles sit at the poles, flat ones on the equator and the obtuse ones inside three caps bounded by the right-angle circles, so with Gaussian points the dots spread evenly and three quarters of them fall in the caps, the fraction a counter at the bottom shows closing on 0.750. +// Author: patternflow-lab +// Date: 2026-11-03 +// SPDX-License-Identifier: CC-BY-SA-4.0 +// Lineage: the shape sphere of planar triangles (David G. Kendall, Shape manifolds, Procrustean metrics, and complex projective spaces, Bulletin of the London Mathematical Society, 1984), Lewis Carroll's pillow problem 58 on obtuse random triangles (Pillow-Problems, 1893); pattern round 102 +// Round: 102 +// =============================== +// @matrix 64x128 +// @knobs Hue=0..1, Speed=0.2..3, Law=0..4, Life=0..1 +// +// Knob 1: Hue. Turns the three corner colours round the wheel together. At 0 corner 1 is azure, corner 2 magenta and corner 3 lime, and each right-angle circle and the obtuse dots inside it take the colour of the corner whose angle is obtuse there; acute dots stay pale. +// Knob 2: Speed. At 2 a new triangle every 1/6 s, each dot falling 0.9 s from the box to the sphere, and the sphere turning once in 30 s while its axis nods 26 degrees either way every 48 s, so both poles come round to the front; everything goes as Speed / 2, so at 0.2 one triangle in 1.7 s. +// Knob 3: Law. Where the three corners come from, in five stops: 0 independent Gaussian points (sigma 8.5 LEDs), dots uniform and 3/4 obtuse; 1 uniform in a disc of radius 19.5, 0.720 obtuse, the equator 14 percent thinner at the cap centres and 14 percent thicker between them; 2 uniform in a square, 0.725; 3 uniform on a circle, 3/4 again, but the equator empties at the cap centres to a fifth of the even density and crowds where two corners meet; 4 a Gaussian squeezed five to one, 0.929 obtuse, with 2.3 times the even density on the equator and a fifth of it at the poles. +// Knob 4: Life. How long a dot stays, 16 / Life seconds: 267 s at the opening 0.06, so about 1600 dots once the stream has run that long, 64 s at 0.25 (about 380) and 16 s at 1 (about 100); at 0 a dot stays until 2400 newer ones push it out. A new law starts a fresh stream laid in as if it had run one dot life, and the counter counts from there. + +// Three points z1, z2, z3 in the plane, as complex numbers. The Helmert +// coordinates +// +// u = (z2 - z1) / sqrt 2, v = (2 z3 - z1 - z2) / sqrt 6 +// +// forget where the triangle is, and |u|^2 + |v|^2 is its size. Up to +// rotation and scale the triangle is the ratio v : u, a point of the complex +// projective line, and Kendall (1984) gave that line its round metric: the +// map +// +// X = (|u|^2 - |v|^2) / 2S, Y = Re(u conj v) / S, Z = Im(u conj v) / S, +// +// S = |u|^2 + |v|^2, puts every shape on a sphere of radius 1/2 (it is the +// Hopf map). Z is 2 / sqrt 3 times the signed area over S, so the two +// equilateral triangles, one of each orientation, are the poles Z = +-1/2 +// and the flat triangles are the equator Z = 0. +// +// The angle at z3 is right when z3 lies on the circle over z1 z2 (Thales), +// which here is |u|^2 = 3 |v|^2, the plane X = 1/4: a circle 60 degrees +// round the point X = 1/2, where z3 sits midway between z1 and z2. Inside it +// the angle at z3 is obtuse. Relabelling the corners turns the sphere a +// third of a turn about the poles, so the right angles at z1 and z2 are the +// same circle round the equator points at 240 and 120 degrees. The three +// caps touch at the equator points where two corners meet, and together they +// cover 3 (1 - cos 60) / 2 = 3/4 of the sphere. +// +// When the corners are independent standard Gaussian points, u and v are +// independent standard complex Gaussians (the Helmert map is orthogonal), so +// (u, v) / |(u, v)| is uniform on the 3-sphere and its Hopf image uniform on +// the shape sphere: three quarters of Gaussian triangles are obtuse, an +// answer to Lewis Carroll's pillow problem 58 in a model Carroll did not +// use. Checked in node with two million triangles per law: Gaussian 0.7504 +// obtuse with Z in ten equal bins at 1.00 each, the cap test agreeing with +// the angle test on every triangle; a disc 0.7201 (9/8 - 4/pi^2 = 0.7197), +// a square 0.7249 (97/150 + pi/40 = 0.7252), a circle 0.7499, the squeezed +// Gaussian 0.9289. Away from Gaussian the dots no longer spread evenly: in +// the band |Z| < 0.1 the disc gives 0.86 of the mean density at the cap +// centres and 1.14 between them, the circle 0.19 and 1.81. + +const W = 64, H = 128; +const { floor, round, sqrt, cos, sin, log, min, max, PI } = Math; +const TAU = 2 * PI; +const cl = (v, a, b) => (v < a ? a : v > b ? b : v); + +// The sample box, the sphere and the counter rows. +const BX = 31.5, BY = 21; +const CX = 31.5, CY = 77, R = 30.5; +const TXT = 112, BAR = 121; +// Dots kept at most; triangles a second, spin turns a second, nod period +// and drop time, all at Speed 1. +const POOL = 2400, RATE = 3, SPIN = 1 / 60, NOD = 96, DROP = 0.9, TILT = 0.45; +// Each cap's centre on the equator: right angle at corner 1, 2, 3. +const CAP = [(4 * PI) / 3, (2 * PI) / 3, 0]; +const HUES = [0.58, 0.91, 0.24]; + +function hsv(h, s, v) { + h = (h - floor(h)) * 6; + const i = floor(h) % 6, f = h - floor(h), p = v * (1 - s), q = v * (1 - s * f), t = v * (1 - s * (1 - f)); + return [[v, t, p], [q, v, p], [p, v, t], [p, q, v], [t, p, v], [v, p, q]][i]; +} + +const gauss = () => { + let u = 0; + while (u === 0) u = Math.random(); + return sqrt(-2 * log(u)) * cos(TAU * Math.random()); +}; + +// One corner under each law, in LEDs from the box centre. +function corner(law) { + if (law === 0) return [8.5 * gauss(), 8.5 * gauss()]; + if (law === 1) { + const r = 19.5 * sqrt(Math.random()), a = TAU * Math.random(); + return [r * cos(a), r * sin(a)]; + } + if (law === 2) return [38 * Math.random() - 19, 38 * Math.random() - 19]; + if (law === 3) { + const a = TAU * Math.random(); + return [19 * cos(a), 19 * sin(a)]; + } + return [12 * gauss(), 2.4 * gauss()]; +} + +// The shape point on the unit sphere (Kendall's radius 1/2 doubled), and the +// corner whose angle is obtuse, 0 for an acute triangle. +function shape(p) { + const ur = (p[2] - p[0]) / Math.SQRT2, ui = (p[3] - p[1]) / Math.SQRT2; + const vr = (2 * p[4] - p[0] - p[2]) / sqrt(6), vi = (2 * p[5] - p[1] - p[3]) / sqrt(6); + const u2 = ur * ur + ui * ui, v2 = vr * vr + vi * vi, s = u2 + v2 || 1; + let ob = 0; + for (let i = 0; i < 3; i++) { + const a = 2 * i, b = 2 * ((i + 1) % 3), c = 2 * ((i + 2) % 3); + if ((p[b] - p[a]) * (p[c] - p[a]) + (p[b + 1] - p[a + 1]) * (p[c + 1] - p[a + 1]) < 0) ob = i + 1; + } + // The box is drawn y down, so the signed area flips; Z is taken so that + // a triangle whose corners 1, 2, 3 run anticlockwise on the panel is north. + return [(u2 - v2) / s, (2 * (ur * vr + ui * vi)) / s, (2 * (ui * vr - ur * vi)) / s, ob]; +} + +// Figures 5 by 7 and a point, for the counter. +const GLYPH = { + 0: ["01110", "10001", "10011", "10101", "11001", "10001", "01110"], + 1: ["00100", "01100", "00100", "00100", "00100", "00100", "01110"], + 2: ["01110", "10001", "00001", "00110", "01000", "10000", "11111"], + 3: ["11110", "00001", "00001", "01110", "00001", "00001", "11110"], + 4: ["00010", "00110", "01010", "10010", "11111", "00010", "00010"], + 5: ["11111", "10000", "11110", "00001", "00001", "10001", "01110"], + 6: ["00110", "01000", "10000", "11110", "10001", "10001", "01110"], + 7: ["11111", "00001", "00010", "00100", "01000", "01000", "01000"], + 8: ["01110", "10001", "10001", "01110", "10001", "10001", "01110"], + 9: ["01110", "10001", "10001", "01111", "00001", "00010", "01100"], + ".": ["00", "00", "00", "00", "00", "11", "11"], +}; + +export function setup(params) { + params.s = { + t: 0, spin: 0.3, nod: NOD * 0.18, hue: 0, speed: 2, life: 267, law: -1, + px: new Float32Array(POOL), py: new Float32Array(POOL), pz: new Float32Array(POOL), + cls: new Uint8Array(POOL), born: new Float32Array(POOL), sx: new Float32Array(POOL), sy: new Float32Array(POOL), + head: 0, count: 0, n: 0, ob: 0, caps: [0, 0, 0], acc: 0, tris: [], buf: new Float32Array(W * H * 3), + }; +} + +// Throw one triangle, born at time t, and drop its dot. +function throwOne(S, t, keep) { + const p = []; + for (let i = 0; i < 3; i++) p.push(...corner(S.law)); + const s = shape(p), j = S.head; + S.px[j] = s[0]; + S.py[j] = s[1]; + S.pz[j] = s[2]; + S.cls[j] = s[3]; + S.born[j] = t; + S.sx[j] = BX + (p[0] + p[2] + p[4]) / 3; + S.sy[j] = BY + (p[1] + p[3] + p[5]) / 3; + S.head = (j + 1) % POOL; + S.count = min(POOL, S.count + 1); + S.n++; + if (s[3]) { + S.ob++; + S.caps[s[3] - 1]++; + } + if (keep) { + S.tris.push({ p, ob: s[3], t }); + if (S.tris.length > 8) S.tris.shift(); + } +} + +// A fresh stream under a new law, laid in as if it had run one dot life. +function restart(S, law) { + S.law = law; + S.head = 0; + S.count = 0; + S.n = 0; + S.ob = 0; + S.caps = [0, 0, 0]; + S.tris = []; + const rate = RATE * S.speed, span = S.life === Infinity ? POOL / rate : S.life; + const n = min(POOL, round(rate * span)); + for (let i = 0; i < n; i++) throwOne(S, S.t - DROP - span * (1 - i / n), i > n - 8); +} + +export function update(dt, input, params) { + if (!params.s) setup(params); + const S = params.s, k = input.knobValues || [0, 2, 0, 0.06], d = cl(dt || 0, 0, 0.1); + S.hue = k[0] || 0; + S.speed = cl(k[1] == null ? 2 : k[1], 0.2, 3); + const lk = cl(k[3] == null ? 0.06 : k[3], 0, 1); + S.life = lk <= 0.001 ? Infinity : 16 / lk; + const law = round(cl(k[2] || 0, 0, 4)); + if (law !== S.law) restart(S, law); + const f = S.speed / 2; + S.t += d; + S.spin += d * f * SPIN * 2 * TAU; + S.nod += d * f * 2; + S.acc += d * RATE * S.speed; + while (S.acc >= 1) { + S.acc -= 1; + throwOne(S, S.t, true); + } +} + +// An additive dot at a fractional position, split over four LEDs. +function dot(buf, x, y, r, g, b) { + const ix = floor(x), iy = floor(y), fx = x - ix, fy = y - iy; + for (let q = 0; q < 4; q++) { + const X = ix + (q & 1), Y = iy + (q >> 1); + if (X < 0 || X >= W || Y < 0 || Y >= H) continue; + const w = (q & 1 ? fx : 1 - fx) * (q >> 1 ? fy : 1 - fy), o = (Y * W + X) * 3; + buf[o] += r * w; + buf[o + 1] += g * w; + buf[o + 2] += b * w; + } +} + +function line(buf, x0, y0, x1, y1, c, v) { + const dx = x1 - x0, dy = y1 - y0, n = max(1, Math.ceil(sqrt(dx * dx + dy * dy) * 1.5)); + for (let i = 0; i <= n; i++) dot(buf, x0 + (dx * i) / n, y0 + (dy * i) / n, (c[0] * v) / 1.5, (c[1] * v) / 1.5, (c[2] * v) / 1.5); +} + +function add(buf, x, y, r, g, b) { + if (x < 0 || x >= W || y < 0 || y >= H) return; + const o = (y * W + x) * 3; + buf[o] += r; + buf[o + 1] += g; + buf[o + 2] += b; +} + +function text(buf, s, x, y, c) { + for (const ch of s) { + const g = GLYPH[ch]; + if (!g) { + x += 3; + continue; + } + for (let r = 0; r < 7; r++) for (let q = 0; q < g[r].length; q++) if (g[r][q] === "1") add(buf, x + q, y + r, c[0], c[1], c[2]); + x += g[0].length + 1; + } + return x; +} + +export function draw(display, params) { + const S = params.s; + if (!S || S.law < 0) return; + const buf = S.buf; + buf.fill(0); + const col = HUES.map((h) => hsv(S.hue + h, 0.75, 1)); + const pale = hsv(S.hue + 0.58, 0.12, 1); + + // The law's ground, faint, in the box. + const ground = [52, 56, 70]; + if (S.law === 0 || S.law === 4) { + const ax = S.law === 0 ? 8.5 : 12, ay = S.law === 0 ? 8.5 : 2.4; + for (const m of [1, 2]) for (let q = 0; q < 160; q++) { + const a = (q / 160) * TAU, x = BX + m * ax * cos(a), y = BY + m * ay * sin(a); + if (y > 0 && y < 42) dot(buf, x, y, ground[0] * 0.8, ground[1] * 0.8, ground[2] * 0.8); + } + } else if (S.law === 2) { + for (let q = -19; q <= 19; q++) { + dot(buf, BX + q, BY - 19, ...ground); + dot(buf, BX + q, BY + 19, ...ground); + dot(buf, BX - 19, BY + q, ...ground); + dot(buf, BX + 19, BY + q, ...ground); + } + } else { + const rr = S.law === 1 ? 19.5 : 19; + for (let q = 0; q < 200; q++) { + const a = (q / 200) * TAU; + dot(buf, BX + rr * cos(a), BY + rr * sin(a), ...ground); + } + } + + // The triangles: the newest filled with its colour, the few before it + // fading. + const f = S.speed / 2; + for (let i = 0; i < S.tris.length; i++) { + const T = S.tris[i], age = (S.t - T.t) * f, newest = i === S.tris.length - 1; + const lv = newest ? 1 : Math.exp(-age / 0.18); + if (lv < 0.03) continue; + const p = T.p, c = T.ob ? col[T.ob - 1] : pale; + if (newest) { + const x0 = floor(min(p[0], p[2], p[4]) + BX), x1 = Math.ceil(max(p[0], p[2], p[4]) + BX); + const y0 = floor(min(p[1], p[3], p[5]) + BY), y1 = Math.ceil(max(p[1], p[3], p[5]) + BY); + const ar = (p[2] - p[0]) * (p[5] - p[1]) - (p[3] - p[1]) * (p[4] - p[0]); + if (Math.abs(ar) > 1e-6) + for (let y = max(0, y0); y <= min(42, y1); y++) + for (let x = max(0, x0); x <= min(W - 1, x1); x++) { + const qx = x - BX, qy = y - BY; + const a = ((p[2] - qx) * (p[5] - qy) - (p[3] - qy) * (p[4] - qx)) / ar; + const b = ((p[4] - qx) * (p[1] - qy) - (p[5] - qy) * (p[0] - qx)) / ar; + if (a >= 0 && b >= 0 && a + b <= 1) add(buf, x, y, c[0] * 46, c[1] * 46, c[2] * 46); + } + } + for (let e = 0; e < 3; e++) { + const a = 2 * e, b = 2 * ((e + 1) % 3); + line(buf, BX + p[a], BY + p[a + 1], BX + p[b], BY + p[b + 1], [1, 1, 1], (newest ? 150 : 90) * lv); + } + if (newest) + for (let e = 0; e < 3; e++) { + const x = BX + p[2 * e], y = BY + p[2 * e + 1], c2 = col[e], big = T.ob === e + 1; + for (let dy = -1; dy <= 1; dy++) + for (let dx = -1; dx <= 1; dx++) { + const w = dx === 0 && dy === 0 ? 260 : dx && dy ? (big ? 120 : 0) : big ? 220 : 110; + dot(buf, x + dx, y + dy, c2[0] * w, c2[1] * w, c2[2] * w); + } + } + } + + // The sphere: spin about the poles, then the axis nodded toward the + // viewer by beta. View coordinates: sx right, sy up, depth toward us. + const beta = TILT * sin((S.nod / NOD) * TAU), cb = cos(beta), sb = sin(beta); + const cs = cos(S.spin), ss = sin(S.spin); + const view = (x, y, z, out) => { + const x1 = x * cs - y * ss, y1 = x * ss + y * cs; + out[0] = CX + R * x1; + out[1] = CY - R * (z * cb - y1 * sb); + out[2] = z * sb + y1 * cb; + }; + const lx = -0.45, ly = 0.55, lz = 0.7; + for (let y = floor(CY - R) - 1; y <= CY + R + 1; y++) + for (let x = 0; x < W; x++) { + const nx = (x - CX) / R, ny = (CY - y) / R, rr = nx * nx + ny * ny; + if (rr > 1) continue; + const nz = sqrt(1 - rr), l = 0.35 + 0.65 * max(0, nx * lx + ny * ly + nz * lz); + const edge = cl((1 - sqrt(rr)) * R * 1.2, 0, 1); + add(buf, x, y, 18 * l * edge, 22 * l * edge, 36 * l * edge); + } + const v = [0, 0, 0]; + // The equator, the three right-angle circles and the poles. + for (let q = 0; q < 240; q++) { + const a = (q / 240) * TAU; + view(cos(a), sin(a), 0, v); + if (v[2] > 0) dot(buf, v[0], v[1], 70 * v[2] + 20, 70 * v[2] + 20, 80 * v[2] + 24); + } + for (let i = 0; i < 3; i++) { + const th = CAP[i], c = col[i], ch = cos(th), sh = sin(th); + for (let q = 0; q < 200; q++) { + const a = (q / 200) * TAU, ca = cos(a) * 0.866, sa = sin(a) * 0.866; + view(0.5 * ch - ca * sh, 0.5 * sh + ca * ch, sa, v); + if (v[2] > 0) { + const w = 60 + 90 * v[2]; + dot(buf, v[0], v[1], c[0] * w, c[1] * w, c[2] * w); + } + } + } + for (const pole of [1, -1]) { + view(0, 0, pole, v); + if (v[2] <= 0) continue; + // A small equilateral triangle at each pole with its corners in their + // colours, 1, 2, 3 anticlockwise at the north and clockwise at the + // south: the two mirror images, which no turn of the plane makes one. + const w = 90 + 60 * v[2], a = (e) => -PI / 2 - (pole * e * TAU) / 3; + for (let e = 0; e < 3; e++) + line(buf, v[0] + 3.5 * cos(a(e)), v[1] + 3.5 * sin(a(e)), v[0] + 3.5 * cos(a(e + 1)), v[1] + 3.5 * sin(a(e + 1)), [1, 1, 1], w); + for (let e = 0; e < 3; e++) { + const c = col[e]; + dot(buf, v[0] + 3.5 * cos(a(e)), v[1] + 3.5 * sin(a(e)), c[0] * 330, c[1] * 330, c[2] * 330); + } + } + + // The dots, and the ones still falling. + const drop = DROP / f, life = S.life; + for (let m = 0; m < S.count; m++) { + const j = (S.head - 1 - m + POOL) % POOL, age = S.t - S.born[j]; + if (age > life) continue; + const c = S.cls[j] ? col[S.cls[j] - 1] : pale; + view(S.px[j], S.py[j], S.pz[j], v); + if (age < drop) { + const u = age / drop, e = u * u; + for (let tr = 0; tr < 4; tr++) { + const uu = max(0, u - tr * 0.04), ee = uu * uu, w = 230 * (1 - tr * 0.24); + dot(buf, S.sx[j] + (v[0] - S.sx[j]) * ee, S.sy[j] + (v[1] - S.sy[j]) * ee, c[0] * w, c[1] * w, c[2] * w); + } + continue; + } + if (v[2] <= 0) continue; + const fade = age > 0.75 * life ? (life - age) / (0.25 * life) : 1; + const land = age - drop < 0.3 ? 1 + 2 * (1 - (age - drop) / 0.3) : 1; + const w = 200 * (0.45 + 0.55 * v[2]) * fade * land; + dot(buf, v[0], v[1], c[0] * w, c[1] * w, c[2] * w); + } + + // The running fraction of obtuse triangles, the count, and a bar split by + // the three caps with a tick at 3/4. + const fr = S.n ? S.ob / S.n : 0; + text(buf, fr.toFixed(3), 2, TXT, [230, 230, 230]); + const ns = String(S.n); + text(buf, ns, 62 - ns.length * 6 + 1, TXT, [90, 96, 120]); + let x = 2; + for (let i = 0; i <= 3; i++) { + const share = i < 3 ? S.caps[i] / max(1, S.n) : 1 - fr, c = i < 3 ? col[i] : pale, w = i < 3 ? 190 : 60; + const x1 = i < 3 ? x + 60 * share : 62; + for (let q = floor(x); q < round(x1) && q < 62; q++) for (let r = 0; r < 4; r++) add(buf, q, BAR + r, c[0] * w, c[1] * w, c[2] * w); + x = x1; + } + for (let r = -2; r < 6; r++) add(buf, 47, BAR + r, 255, 255, 255); + + for (let y = 0; y < H; y++) + for (let x2 = 0; x2 < W; x2++) { + const o = (y * W + x2) * 3, m = max(buf[o], buf[o + 1], buf[o + 2]), q = m > 255 ? 255 / m : 1; + display.setPixel(x2, y, round(buf[o] * q), round(buf[o + 1] * q), round(buf[o + 2] * q)); + } +} diff --git a/incoming/pattern-study/patterns/photo-finish.js b/incoming/pattern-study/patterns/photo-finish.js new file mode 100644 index 00000000..8dfc147a --- /dev/null +++ b/incoming/pattern-study/patterns/photo-finish.js @@ -0,0 +1,342 @@ +// ===== Patternflow pattern ===== +// Title: Photo Finish +// Family: type +// Idea: The letters of a word race down five lanes to a finish line across the middle of the panel, and below the line a photo-finish camera that sees only that one row writes it again and again into a strip that grows downward, so time becomes the vertical axis: a fast letter comes out short, a slow one tall, one that stops on the line smears into streaks, one that slides sideways leans, and the heights in the strip give the order of arrival, which the word spells out at the start line when the race is over. +// Author: patternflow-lab +// Date: 2026-11-03 +// SPDX-License-Identifier: CC-BY-SA-4.0 +// Lineage: strip photography and the photo-finish camera, a slit that images one line across the track onto film moving past it, so the picture's long axis is time; 5 by 7 capitals drawn for this pattern; pattern round 102 +// Round: 102 +// =============================== +// @matrix 64x128 +// @knobs Hue=0..1, Speed=0.2..3, Field=0..4, Rate=0..1 +// +// Knob 1: Hue. Turns the runners' colours round the wheel. At 0 the five letters run from aqua in the left lane through azure and indigo to violet and pink in the right lane, and each keeps its colour in the strip and in the result, so a letter can be followed from lane to photo to place; the film's grey and the lane rules turn with them, blue grey at 0. +// Knob 2: Speed. At 2 a race takes about 5.8 s: 0.7 s on the start line, about 2.5 s of running while the camera writes, 2.6 s holding the finished photo and the result, then the next race with new speeds; everything, the camera included, goes as Speed / 2, so the photo comes out the same and only the watching is slower: at 0.2 the strip grows 3.4 rows a second and each row can be seen being read off the line. +// Knob 3: Field. The race in five stops: 0 PHOTO, five ways to cross, one each: P steady at 58 to 70 LEDs a second, short; H stopping 0.5 to 0.8 s with the line 4 to 6 rows into it, so its legs streak; O stopping a row short of the line and starting from rest at 150 LEDs a second squared; T steady at 21 to 25, tall; O sliding 5 LEDs sideways while it crosses, so it leans. 1 SWIFT, steady speeds from 22 to 70. 2 TIRED, letters that reach the line at 35 to 50 and cross it slowing to 8 to 12 or kicking to 90 to 110, so they come out top-heavy or squashed. 3 PAUSE, every letter stopping once with the line 2 to 12 rows into it for 0.2 to 0.9 s, so the strip shows the row it stopped on as streaks. 4 SLIDE, letters sliding 3 to 5 LEDs sideways while they cross. +// Knob 4: Rate. The camera's line rate in rows a second of race time, 18 + 84 Rate^0.6: 33.5 at the opening 0.06, where a 14-row letter crossing at 33.5 LEDs a second comes out its own height, P is written 7 rows tall and T 20, and the 71 strip rows hold 2.1 s; 18 at 0, where everything is squeezed and the strip holds 3.9 s; 102 at 1, where letters stand three times taller and only the last 0.7 s of the race stays in the picture. + +// A photo-finish camera does not take a picture of the track. Its slit +// sees one line, the finish line, and the film moves past the slit at a +// steady rate, so the picture is that one line written again at every +// instant, one instant beside the next. Nothing in it is where it was in +// space; everything is where it was in time. A runner who crosses fast is +// written in few instants and comes out narrow, a slow one wide, and one who +// stops on the line has the same slice of him written over and over, a +// smear of streaks. Whoever crossed first is furthest along the film, which +// is why the picture decides the race. +// +// Here the track runs down the panel and the finish line is row 56. Above +// it are the letters as they are, each in its lane. Below it is the film: +// every 1 / Rate seconds of race time the camera reads row 56 and pushes the +// strip one row down, newest row on top. A letter falling at exactly Rate +// LEDs a second therefore comes out of the line as if nothing had happened, +// its bottom first and lowest; at speed v it is written 14 Rate / v rows +// tall. The letter crossing at v(y) as it goes has every row of it scaled by +// its own speed, so a letter slowing down comes out with its top drawn out, +// one speeding up with its bottom drawn out, and one stopping has one row +// repeated. Sideways motion during the crossing turns into slope, since +// every later row is written further across. A real photo-finish camera +// lays time along the track, across the picture; the tall panel lays it +// down, so speed shows as letter height instead of width. +// +// Every runner's place is an exact function of race time (a few pieces of +// constant speed, constant acceleration and standing still), so the camera +// reads the line at the instant each row stands for, not at frame times, +// and the strip is the same at any frame rate and any Speed. A letter has +// finished when all of it is past the line, the moment its top is written; +// the result word on the start line is the letters in that order, which in +// the strip is the order of their tops from the bottom up. Film the camera +// has not reached is dark, and a written row shows the line itself, grey. + +const W = 64, H = 128; +const { floor, round, sqrt, min, max } = Math; +const cl = (v, a, b) => (v < a ? a : v > b ? b : v); + +// The finish line, the strip under it, the start line, a letter's size and +// the lanes' left columns. +const LINE = 56, STRIP = H - LINE - 1, TOP = 4, LH = 14, LW = 10; +const LANES = [1, 14, 27, 40, 53]; +// Where a letter's bottom touches the line and where its top leaves it. +const TOUCH = LINE + 0.5 - LH, CLEAR = LINE + 0.5; +// Race seconds: on the start line, after the last letter clears, holding. +const READY = 0.7, AFTER = 0.2, HOLD = 2.6; + +const FONT = { + P: ["11110", "10001", "10001", "11110", "10000", "10000", "10000"], + H: ["10001", "10001", "10001", "11111", "10001", "10001", "10001"], + O: ["01110", "10001", "10001", "10001", "10001", "10001", "01110"], + T: ["11111", "00100", "00100", "00100", "00100", "00100", "00100"], + S: ["01111", "10000", "10000", "01110", "00001", "00001", "11110"], + W: ["10001", "10001", "10001", "10101", "10101", "10101", "01010"], + I: ["01110", "00100", "00100", "00100", "00100", "00100", "01110"], + F: ["11111", "10000", "10000", "11110", "10000", "10000", "10000"], + D: ["11110", "10001", "10001", "10001", "10001", "10001", "11110"], + R: ["11110", "10001", "10001", "11110", "10100", "10010", "10001"], + A: ["01110", "10001", "10001", "11111", "10001", "10001", "10001"], + U: ["10001", "10001", "10001", "10001", "10001", "10001", "01110"], + E: ["11111", "10000", "10000", "11110", "10000", "10000", "11111"], + L: ["10000", "10000", "10000", "10000", "10000", "10000", "11111"], +}; +const WORDS = ["PHOTO", "SWIFT", "TIRED", "PAUSE", "SLIDE"]; + +function hsv(h, s, v) { + h = (h - floor(h)) * 6; + const i = floor(h) % 6, f = h - floor(h), p = v * (1 - s), q = v * (1 - s * f), t = v * (1 - s * (1 - f)); + return [[v, t, p], [q, v, p], [p, v, t], [p, q, v], [t, p, v], [v, p, q]][i]; +} + +const rnd = (a, b) => a + (b - a) * Math.random(); + +// A runner's way down as pieces {t, y, v, a}: from time t it is at +// y + v u + a u^2 / 2, u = t' - t, until the next piece. Steps: +// ["to", y, v] runs at v to y; ["wait", s] stands; ["acc", a, v, y] starts +// at v, accelerates by a to y and goes on at the speed it reached there; +// ["run", v] runs at v for good. +function plan(steps) { + const segs = [{ t: -1e9, y: TOP, v: 0, a: 0 }]; + let t = READY, y = TOP, v = 0; + for (const s of steps) { + if (s[0] === "wait") { + segs.push({ t, y, v: 0, a: 0 }); + t += s[1]; + v = 0; + } else if (s[0] === "to") { + segs.push({ t, y, v: s[2], a: 0 }); + t += (s[1] - y) / s[2]; + y = s[1]; + v = s[2]; + } else if (s[0] === "acc") { + const a = s[1], v0 = s[2], d = s[3] - y, v1 = sqrt(max(1, v0 * v0 + 2 * a * d)); + segs.push({ t, y, v: v0, a }); + t += Math.abs(a) < 1e-6 ? d / v0 : (v1 - v0) / a; + y = s[3]; + v = v1; + } else segs.push({ t, y, v: (v = s[1]), a: 0 }); + } + if (steps[steps.length - 1][0] !== "run") segs.push({ t, y, v, a: 0 }); + return segs; +} + +function yAt(segs, t) { + let s = segs[0]; + for (let i = 1; i < segs.length && segs[i].t <= t; i++) s = segs[i]; + const u = t - s.t; + return s.y + s.v * u + 0.5 * s.a * u * u; +} + +// The first time y(t) reaches y, by bisection on a path that never climbs. +function timeAt(segs, y) { + let lo = READY, hi = READY + 30; + for (let i = 0; i < 40; i++) { + const m = (lo + hi) / 2; + if (yAt(segs, m) >= y) hi = m; + else lo = m; + } + return hi; +} + +// The five runners of a field, with fresh speeds. +function field(f) { + const out = []; + for (let i = 0; i < 5; i++) { + let steps, slide = 0; + if (f === 0) + steps = [ + [["run", rnd(58, 70)]], + [["to", TOUCH + rnd(3.5, 5.5), 42], ["wait", rnd(0.5, 0.8)], ["run", 42]], + [["to", TOUCH - 1, 40], ["wait", rnd(0.25, 0.45)], ["acc", 150, 0, CLEAR], ["run", 65]], + [["run", rnd(21, 25)]], + [["run", rnd(32, 38)]], + ][i]; + else if (f === 1) steps = [["run", rnd(22, 70)]]; + else if (f === 2) { + const v0 = rnd(35, 50), v1 = Math.random() < 0.5 ? rnd(8, 12) : rnd(90, 110); + steps = [["to", TOUCH, v0], ["acc", (v1 * v1 - v0 * v0) / (2 * LH), v0, CLEAR]]; + } else if (f === 3) steps = [["to", TOUCH + rnd(2, 12), 40], ["wait", rnd(0.2, 0.9)], ["run", 40]]; + else steps = [["run", rnd(30, 42)]]; + if (f === 4 || (f === 0 && i === 4)) slide = (Math.random() < 0.5 ? -1 : 1) * (f === 0 ? 5 : rnd(3, 5)); + const segs = plan(steps), t0 = timeAt(segs, TOUCH), t1 = timeAt(segs, CLEAR); + out.push({ segs, t0, t1, slide, ch: FONT[WORDS[f][i]] }); + } + return out; +} + +// A runner's left edge: its lane, slid across during the crossing. +function xAt(r, i, t) { + if (!r.slide) return LANES[i]; + const u = cl((t - r.t0) / max(1e-3, r.t1 - r.t0), 0, 1); + return LANES[i] + r.slide * (u - 0.5); +} + +// Ink of glyph g at (u, v) LEDs from a letter's top left, 0..1, with the +// column covered in two halves so a sliding letter has soft sides. +function ink(g, u, v) { + if (v < 0 || v >= LH) return 0; + const gy = floor(v / 2); + let s = 0; + for (const du of [0.25, 0.75]) { + const uu = u + du; + if (uu >= 0 && uu < LW && g[gy][floor(uu / 2)] === "1") s += 0.5; + } + return s; +} + +function newRace(S) { + S.runners = field(S.field); + S.t = 0; + S.cam = 0; + S.camT = READY; + S.rec = true; + S.order = S.runners.map((r, i) => i).sort((a, b) => S.runners[a].t1 - S.runners[b].t1); + S.end = max(...S.runners.map((r) => r.t1)) + AFTER; + S.ink.fill(0); + S.who.fill(255); + S.tick.fill(0); + S.shot.fill(0); +} + +export function setup(params) { + params.s = { + field: -1, hue: 0, speed: 2, rate: 33, t: 0, cam: 0, camT: 0, rec: true, runners: [], order: [], end: 0, + // The strip: per row and column the ink and whose it is, and the time + // ticks, row 0 the newest, as a ring. + ink: new Float32Array(STRIP * W), who: new Uint8Array(STRIP * W).fill(255), tick: new Uint8Array(STRIP), shot: new Uint8Array(STRIP), head: 0, + }; +} + +// The camera reads the line at race time t into a new top row. +function expose(S, t) { + S.head = (S.head + STRIP - 1) % STRIP; + const o = S.head * W; + for (let x = 0; x < W; x++) { + S.ink[o + x] = 0; + S.who[o + x] = 255; + } + S.runners.forEach((r, i) => { + const y = yAt(r.segs, t), v = CLEAR - y; + if (v < 0 || v >= LH) return; + const x0 = xAt(r, i, t); + for (let x = max(0, floor(x0) - 1); x <= min(W - 1, x0 + LW + 1); x++) { + const k = ink(r.ch, x - x0, v); + if (k > S.ink[o + x]) { + S.ink[o + x] = k; + S.who[o + x] = i; + } + } + }); + // A tick every tenth of a second of race time, a long one every half. + const tenth = floor(t * 10 + 1e-6), prev = floor((t - 1 / S.rate) * 10 + 1e-6); + S.tick[S.head] = tenth !== prev ? (tenth % 5 === 0 ? 2 : 1) : 0; + S.shot[S.head] = 1; +} + +function step(S, d) { + S.t += d; + if (S.rec && S.t >= READY) { + S.cam += d * S.rate; + while (S.cam >= 1 && S.camT < S.end) { + S.cam -= 1; + S.camT += 1 / S.rate; + expose(S, S.camT); + } + if (S.camT >= S.end) S.rec = false; + } + if (S.t >= S.end + HOLD) newRace(S); +} + +export function update(dt, input, params) { + if (!params.s) setup(params); + const S = params.s, k = input.knobValues || [0, 2, 0, 0.06], d = cl(dt || 0, 0, 0.1); + S.hue = k[0] || 0; + S.speed = cl(k[1] == null ? 2 : k[1], 0.2, 3); + S.rate = 18 + 84 * cl(k[3] == null ? 0.06 : k[3], 0, 1) ** 0.6; + const f = round(cl(k[2] || 0, 0, 4)); + if (f !== S.field) { + S.field = f; + newRace(S); + // Open ahead, so 2 s in at Speed 2 the race is 1.5 s past the gun, + // with letters on both sides of the line. + for (let i = 0; i < 12; i++) step(S, 0.2 / 12); + } + step(S, (d * S.speed) / 2); +} + +export function draw(display, params) { + const S = params.s; + if (!S || !S.runners.length) return; + const cols = S.runners.map((r, i) => hsv(S.hue + 0.47 + 0.11 * i, 0.72, 1)); + const px = new Float32Array(W * H * 3); + // The film's own tint and the lane rules, a little way round the wheel + // from the runners. + const film = hsv(S.hue + 0.64, 0.42, 0.13), rule = hsv(S.hue + 0.64, 0.4, 0.15); + const put = (x, y, r, g, b) => { + const o = (y * W + x) * 3; + px[o] = r; + px[o + 1] = g; + px[o + 2] = b; + }; + // The track above the line: dim lane rules. + for (let y = 0; y < LINE; y++) for (const x of [12, 25, 38, 51]) put(x, y, rule[0] * 255, rule[1] * 255, rule[2] * 255); + const t = S.t, done = t >= S.end; + // The letters as they are, above the line only. + if (!done) + S.runners.forEach((r, i) => { + const y0 = yAt(r.segs, t), x0 = xAt(r, i, t), c = cols[i]; + for (let y = max(0, floor(y0)); y < min(LINE, y0 + LH); y++) + for (let x = max(0, floor(x0) - 1); x <= min(W - 1, x0 + LW + 1); x++) { + const k = ink(r.ch, x - x0, y + 0.5 - y0); + if (k > 0) put(x, y, c[0] * 255 * k, c[1] * 255 * k, c[2] * 255 * k); + } + }); + else { + // The result: the word again on the start line in the order the + // letters reached the line, each in its own colour. + const fade = cl((S.end + HOLD - t) / 0.3, 0, 1) * cl((t - S.end) / 0.3, 0, 1); + S.order.forEach((i, place) => { + const c = cols[i], g = S.runners[i].ch; + for (let v = 0; v < LH; v++) + for (let u = 0; u < LW; u++) + if (g[v >> 1][u >> 1] === "1") put(LANES[place] + u, TOP + v, c[0] * 230 * fade, c[1] * 230 * fade, c[2] * 230 * fade); + }); + } + // The finish line: bright while the camera runs, its slice of each + // crossing letter in the letter's colour. + const lv = S.rec && t >= READY ? 190 : 90; + for (let x = 0; x < W; x++) put(x, LINE, lv, lv, lv * 1.05); + if (S.rec && !done) + S.runners.forEach((r, i) => { + const v = CLEAR - yAt(r.segs, t); + if (v < 0 || v >= LH) return; + const x0 = xAt(r, i, t), c = cols[i]; + for (let x = max(0, floor(x0) - 1); x <= min(W - 1, x0 + LW + 1); x++) { + const k = ink(r.ch, x - x0, v); + if (k > 0) put(x, LINE, 255 * (c[0] * k + 0.6 * (1 - k)), 255 * (c[1] * k + 0.6 * (1 - k)), 255 * (c[2] * k + 0.6 * (1 - k))); + } + }); + // The strip, newest row just under the line, on the film's own grey. + for (let row = 0; row < STRIP; row++) { + const j = (S.head + row) % STRIP, o = j * W, y = LINE + 1 + row; + for (let x = 0; x < W; x++) { + const k = S.ink[o + x], w = S.who[o + x]; + // Film the camera has not reached yet is dark; a written row + // shows the finish line itself, grey, behind the letters. + const sh = S.shot[j]; + let r = film[0] * 255 * sh, g = film[1] * 255 * sh, b = film[2] * 255 * sh; + if (k > 0 && w < 5) { + const c = cols[w]; + r = r * (1 - k) + c[0] * 240 * k; + g = g * (1 - k) + c[1] * 240 * k; + b = b * (1 - k) + c[2] * 240 * k; + } + put(x, y, r, g, b); + } + const tk = S.tick[j]; + if (tk) for (let q = 0; q < tk * 2; q++) put(W - 1 - q, y, 120, 120, 130); + } + for (let y = 0; y < H; y++) + for (let x = 0; x < W; x++) { + const o = (y * W + x) * 3; + display.setPixel(x, y, round(cl(px[o], 0, 255)), round(cl(px[o + 1], 0, 255)), round(cl(px[o + 2], 0, 255))); + } +} From 7544164adf84f0de49b40ea70a504a7120c6bcb5 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 08:31:01 +0000 Subject: [PATCH 3/9] =?UTF-8?q?=ED=8C=A8=ED=84=B4=20=EC=97=B0=EA=B5=AC=201?= =?UTF-8?q?02=ED=8C=90:=20Fraenkel=20Rhythm=20=EA=B3=BC=20Trivision=20?= =?UTF-8?q?=EC=9D=84=20=EB=8D=94=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Bc4JkshDz129z5fXhUWQxz --- .../pattern-study/patterns/fraenkel-rhythm.js | 251 ++++++++++++++++++ incoming/pattern-study/patterns/trivision.js | 196 ++++++++++++++ 2 files changed, 447 insertions(+) create mode 100644 incoming/pattern-study/patterns/fraenkel-rhythm.js create mode 100644 incoming/pattern-study/patterns/trivision.js diff --git a/incoming/pattern-study/patterns/fraenkel-rhythm.js b/incoming/pattern-study/patterns/fraenkel-rhythm.js new file mode 100644 index 00000000..5f907bc1 --- /dev/null +++ b/incoming/pattern-study/patterns/fraenkel-rhythm.js @@ -0,0 +1,251 @@ +// ===== Patternflow pattern ===== +// Title: Fraenkel Rhythm +// Family: rhythm +// Idea: Fraenkel's exact cover, m voices at the pairwise different steady rates (2^m - 1)/2^(i - 1) sharing one pulse so that every pulse is played exactly once, the cover he conjectured to be the only one, is drawn as a ruler rising up the panel with one tick per pulse as long as the voice that plays it, which comes out as the binary ruler with every inch mark cut away; a playhead sounds each tick as it passes, and the fourth knob pulls the rates off Fraenkel's until the ruler breaks into doubled ticks where two voices clash and dotted gaps where nobody plays. +// Author: patternflow-lab +// Date: 2026-11-03 +// SPDX-License-Identifier: CC-BY-SA-4.0 +// Lineage: Fraenkel's conjecture on exact covers by Beatty sequences (Aviezri Fraenkel, 1973); Beatty sequences (Samuel Beatty, 1926); pattern round 102 +// Round: 102 +// =============================== +// @matrix 64x128 +// @knobs Hue=0..1, Speed=0.2..3, Drop=0..4, Detune=0..1 +// +// Knob 1: Hue. Turns the voices round the wheel together, spaced evenly. At 0 with six voices the shortest ticks are cyan, then blue, magenta, red, yellow and the single longest tick green; at 0.5 the shortest are red. +// Knob 2: Speed. The ruler rises 8 rows a second for each unit, 16 at the opening 2: with six voices at 2 rows a pulse that is 8 pulses a second and the 63-pulse cycle passes the playhead in 7.9 s; with three voices at 6 rows a pulse it is 2.7 a second. 1.6 rows a second at 0.2, 24 at 3. +// Knob 3: Drop. Takes the densest voice away and closes the ruler up, one voice per unit. At 0 six voices on a cycle of 63 pulses, 2 rows a pulse; at 1 five on 31 pulses, 4 rows; at 2 four on 15, two cycles at 4 rows; at 3 three on 7, three cycles at 6 rows; at 4 two voices on 3, seven cycles at 6 rows. +// Knob 4: Detune. Pulls the rates from Fraenkel's toward all equal, by the square of the knob. The densities follow r^(m - i) with r = 2^(1 - u^2) at knob u, rounded to whole hits per panel so the score still repeats once a panel, and the phases slide from Fraenkel's toward a staircase. At the opening 0.06 six voices still play 32, 16, 8, 4, 2 and 1 of the 63 pulses and the phases have moved a fifth of their margin, so the cover is exact. The first clash comes at 0.14; at 0.2 the densest voice drops to 31 of 63, and 23 pulses clash while 25 go unplayed. At 1 three voices play every third pulse in a staircase and cover exactly again; six cannot, as 63 is not a multiple of 6. Two voices never break anywhere: Beatty's theorem fits any two rates whose densities add to 1. + +// Fraenkel's conjecture. Take m >= 3 sequences floor(n a_i + b_i), n over +// the integers, with pairwise different rates a_i. If every integer is in +// exactly one of them, then a_i = (2^m - 1)/2^(i - 1), i = 1..m, up to order. +// Fraenkel stated it in 1973; it is proved for small m, not in general. +// +// The cover that does exist is the binary ruler. Over a cycle of N = 2^m - 1 +// pulses, pulse k goes to the voice set by the 2-adic valuation of k: the +// odd pulses to the densest voice (rate N/2^(m-1)), k = 2 mod 4 to the next, +// and k = 2^(m-1) alone to the sparsest (rate N). On a ruler that is the +// length of the tick at k: the half-inch mark longest, the quarters next, +// the finest divisions shortest. The cycle is 2^m - 1 pulses, not 2^m, +// because the inch mark itself has no voice: cycle after cycle the ruler +// runs on with every inch mark cut out, and two shortest ticks meet there. +// +// setup() does not take that on trust. For each voice it searches the +// phases b = (2j + 1)/(2q) in [0, a), a = N/q, in exact integer arithmetic +// for the one where floor(n a + b) gives exactly the pulses of that +// valuation, checked over three cycles. It finds one for every voice and +// every m from 2 to 6 (there is only one in [0, a)), and those phases are +// all the drawing uses. +// +// Detune. Voice i plays pulse k when an integer lies in (k d_i + c_i, +// (k + 1) d_i + c_i], d_i = 1/a_i its density and c_i = -b_i/a_i its phase: +// the same floor(n a_i + b_i), counted from the pulse side. The knob moves +// the densities along r^(m - i) from r = 2 toward r = 1, rounded to whole +// hits per panel (K pulses) so the rates stay rational and the score repeats +// once a panel as the ruler rises, and moves the phases straight from +// Fraenkel's to those of the staircase, where the voice with the j-th +// shortest tick plays k = j mod m. For two +// voices the two phases are taken to add to the same whole number at both +// ends; with densities adding to 1 that is Beatty's condition, and every +// pulse stays covered once all the way. For three or more nothing keeps +// them, and every extra hit leaves a hole somewhere in the K pulses. + +const W = 64, H = 128, XS = 0, XM = 58, MAXM = 6, SH = 0.3, YP = 40, START = 1; +const { floor, ceil, min, max, round, exp, abs } = Math; +const cl = (v, a, b) => (v < a ? a : v > b ? b : v); +// Rows a pulse and cycles on the panel, by voice count. +const ROWS = { 2: [6, 7], 3: [6, 3], 4: [4, 2], 5: [4, 1], 6: [2, 1] }; + +function hsv(h, s, v) { + h = (h - floor(h)) * 6; + const i = floor(h) % 6, f = h - floor(h), p = v * (1 - s), q = v * (1 - f * s), t = v * (1 - (1 - f) * s); + return [[v, t, p], [q, v, p], [p, v, t], [p, q, v], [t, p, v], [v, p, q]][i]; +} + +const ceilDiv = (a, b) => -floor(-a / b); + +// Pulse k's valuation class in the cycle of N: k taken into 1..N. +function valuation(k, N) { + let r = (((k - 1) % N) + N) % N + 1, v = 0; + while (r % 2 === 0) { + r /= 2; + v++; + } + return v; +} + +// The Fraenkel system for m voices, voice v = valuation v (v = 0 densest), +// and the staircase with equal rates, as phases c. +function system(m) { + const N = 2 ** m - 1, cF = [], cE = [], beta = []; + for (let v = 0; v < m; v++) { + const q = 2 ** (m - 1 - v); + let found = -1; + for (let j = 0; j < N && found < 0; j++) { + let ok = true; + for (let k = 1; k <= 3 * N && ok; k++) { + // Number of n with k <= n N/q + (2j + 1)/(2q) < k + 1. + const c = ceilDiv(2 * q * (k + 1) - 2 * j - 1, 2 * N) - ceilDiv(2 * q * k - 2 * j - 1, 2 * N); + if (c !== (valuation(k, N) === v ? 1 : 0)) ok = false; + } + if (ok) found = j; + } + // found is never -1 for m = 2..6; a miss would show as an empty voice. + beta.push((2 * found + 1) / (2 * q)); + cF.push(-(2 * found + 1) / (2 * N)); + // Equal rates: voice v on k = v + 1 mod m, at the phase -(j + 1/2)/m + // that puts it there, taken the nearest way round from Fraenkel's. + let ce = 0; + for (let j = 0; j < m; j++) { + const cj = -(j + 0.5) / m; + let ok = true; + for (let k = 1; k <= m; k++) if (ceil((k + 1) / m + cj) - ceil(k / m + cj) !== (k === v + 1 ? 1 : 0)) ok = false; + if (ok) ce = cj; + } + while (ce - cF[v] > 0.5) ce -= 1; + while (ce - cF[v] < -0.5) ce += 1; + cE.push(ce); + } + if (m === 2) { + // Two voices: make the phase sums agree so the blend keeps them whole. + const sF = cF[0] + cF[1], sE = cE[0] + cE[1]; + cE[1] += round(sF - sE); + } + return { m, N, cF, cE, beta }; +} + +export function setup(params) { + const sys = {}; + for (let m = 2; m <= MAXM; m++) sys[m] = system(m); + params.s = { + sys, m: 0, u: -1, hue: 0, s: 0, pps: 1, K: 0, P: 2, N: 1, + n: new Int32Array(MAXM), c: new Float64Array(MAXM), + hits: new Uint8Array(64 * MAXM), cnt: new Uint8Array(64), + }; +} + +// The score for the current knobs: whole hits per K pulses for each voice, +// so it repeats once per panel, and which voices play each of the K pulses. +function score(S, m, u) { + const sy = S.sys[m], t = u * u, r = 2 ** (1 - t), [P, cyc] = ROWS[m], K = sy.N * cyc; + let sum = 0; + for (let v = 0; v < m; v++) sum += r ** (m - 1 - v); + // Densities K r^(m-1-v)/sum, rounded to whole numbers that add to K + // (largest remainders first); at r = 2 they are K 2^(m-1-v)/N exactly. + const ex = []; + let left = K; + for (let v = 0; v < m; v++) { + ex.push((K * r ** (m - 1 - v)) / sum); + S.n[v] = floor(ex[v] + 1e-9); + left -= S.n[v]; + S.c[v] = sy.cF[v] + (sy.cE[v] - sy.cF[v]) * t; + } + const ord = ex.map((x, v) => [x - S.n[v], v]).sort((a, b) => b[0] - a[0]); + for (let i = 0; i < left; i++) S.n[ord[i][1]]++; + S.P = P; + S.K = K; + S.N = sy.N; + for (let k = 1; k <= K; k++) { + let n = 0; + for (let v = 0; v < m; v++) { + const h = ceil(((k + 1) * S.n[v]) / K + S.c[v]) - ceil((k * S.n[v]) / K + S.c[v]); + S.hits[(k - 1) * MAXM + v] = h > 0 ? 1 : 0; + n += h > 0 ? 1 : 0; + } + S.cnt[k - 1] = n; + } +} + +export function update(dt, input, params) { + if (!params.s) setup(params); + const S = params.s, k = input.knobValues || [0, 2, 0, 0.06], d = cl(dt || 0, 0, 0.1); + S.hue = k[0] || 0; + const speed = cl(k[1] == null ? 2 : k[1], 0.2, 3); + const m = MAXM - cl(round(k[2] || 0), 0, 4); + const u = cl(k[3] == null ? 0.06 : k[3], 0, 1); + if (m !== S.m || abs(u - S.u) > 1e-6) { + if (m !== S.m) S.s = START; + S.m = m; + S.u = u; + score(S, m, u); + } + // The tape, in pulses: 8 rows a second for each unit of Speed. + S.pps = (speed * 8) / S.P; + S.s += d * S.pps; + if (S.s > 1e6) S.s -= S.K * floor(S.s / S.K); +} + +export function draw(display, params) { + const S = params.s; + if (!S || !S.m) return; + const { m, K, P, N } = S, th = P / 2; + const buf = new Float32Array(W * H * 3); + const put = (x, y, c, b) => { + if (x < 0 || x >= W || y < 0 || y >= H) return; + const i = (y * W + x) * 3; + buf[i] = max(buf[i], c[0] * b); + buf[i + 1] = max(buf[i + 1], c[1] * b); + buf[i + 2] = max(buf[i + 2], c[2] * b); + }; + const cols = []; + for (let v = 0; v < m; v++) cols.push(hsv(S.hue + 0.5 + v / m, 1, 1)); + const white = [1, 1, 1], grey = [0.75, 0.8, 0.85], red = [1, 0.16, 0.12]; + // Tick lengths, shortest for the densest voice, spread over 8..54 LEDs. + const len = (v) => round(8 + (v * 46) / (m - 1)); + // Pulse k sits at row YP + (k - s) P: the tape rises, and a pulse sounds + // as its rows reach the playhead. + const s0 = S.s, kLo = floor(s0 - YP / P) - 1, kHi = ceil(s0 + (H - YP) / P) + 1; + for (let y = 0; y < H; y++) put(XS, y, grey, 0.35); + for (let kk = kLo; kk <= kHi; kk++) { + const y0 = YP + floor((kk - s0) * P), k = ((((kk - 1) % K) + K) % K) + 1, n = S.cnt[k - 1]; + // Seconds since this pulse sounded (negative: still to come). + const age = (s0 - kk) / S.pps; + const f = age >= 0 ? exp(-age / 0.9) : 0, hot = age >= 0 ? exp(-age / 0.15) : 0; + const b = 0.6 + 0.4 * f; + if (n === 0) { + // A hole: nobody plays. A dotted line where a tick should be, and a red + // mark in the margin. + for (let x = XS + 2; x < XM - 2; x += 3) put(x, y0, grey, 0.3 + 0.5 * f); + for (let y = y0; y < y0 + th; y++) for (let x = XM + 1; x < W - 1; x++) put(x, y, red, 0.75 + 0.25 * f); + } else { + // One voice: its tick on the top half of the pulse's rows, and its + // shadow on the rest. Two or more: the rows shared out, the longest + // voice on top, and a white mark in the margin. + const vs = []; + for (let v = m - 1; v >= 0; v--) if (S.hits[(k - 1) * MAXM + v]) vs.push(v); + const rows = n === 1 ? th : max(1, floor(P / n)); + vs.forEach((v, j) => { + const c = cols[v], L = len(v); + for (let y = y0 + j * rows; y < y0 + (j + 1) * rows && y < y0 + P; y++) + for (let x = XS + 1; x <= XS + L; x++) { + const w = hot * (0.25 + 0.75 * (x / (L + 1))); + put(x, y, [c[0] + (1 - c[0]) * w, c[1] + (1 - c[1]) * w, c[2] + (1 - c[2]) * w], b); + } + if (n === 1) for (let y = y0 + th; y < y0 + P; y++) for (let x = XS + 1; x <= XS + L; x++) put(x, y, c, b * SH); + // Sounding: the note head at the tick's end swells and burns white. + if (hot > 0.02) { + const yc = y0 + j * rows + (rows - 1) / 2, rr = 0.8 + 1.4 * hot; + for (let y = floor(yc - rr); y <= ceil(yc + rr); y++) + for (let x = floor(L - rr); x <= ceil(L + rr); x++) { + const e = 1 - ((x - L) ** 2 + (y - yc) ** 2) / (rr * rr); + const w = 0.7 * hot; + if (e > 0) put(x, y, [c[0] + (1 - c[0]) * w, c[1] + (1 - c[1]) * w, c[2] + (1 - c[2]) * w], min(1, 0.5 + e) * hot); + } + } + }); + if (n > 1) for (let y = y0; y < y0 + P; y++) for (let x = XM + 1; x < W - 1; x++) put(x, y, white, 0.7 + 0.3 * f); + } + // Where an inch mark was cut out, between pulse N and the next: a notch. + if (k % N === 0) for (let x = XM - 4; x < XM; x++) put(x, y0 + P - 1, grey, 0.5); + } + // The playhead: a bright notch on the spine and a dim one at the margin. + for (let y = YP; y < YP + max(1, th); y++) { + put(XS, y, white, 1); + put(XM - 1, y, white, 0.6); + } + for (let y = 0; y < H; y++) + for (let x = 0; x < W; x++) { + const i = (y * W + x) * 3; + display.setPixel(x, y, round(min(1, buf[i]) * 255), round(min(1, buf[i + 1]) * 255), round(min(1, buf[i + 2]) * 255)); + } +} diff --git a/incoming/pattern-study/patterns/trivision.js b/incoming/pattern-study/patterns/trivision.js new file mode 100644 index 00000000..75227437 --- /dev/null +++ b/incoming/pattern-study/patterns/trivision.js @@ -0,0 +1,196 @@ +// ===== Patternflow pattern ===== +// Title: Trivision +// Family: type +// Idea: A three-message prism sign: a row of upright triangular prisms side by side, each face printed with one vertical slice of one of three posters, ALL, FOR and ONE, big hand-drawn letters stacked down the panel, and the prisms turn a third of a turn one after another, so mid-change the sign shows the new word on one side and the old on the other with the turning prisms between, each showing two foreshortened faces lit differently by a lamp at the left. +// Author: patternflow-lab +// Date: 2026-11-03 +// SPDX-License-Identifier: CC-BY-SA-4.0 +// Lineage: three-sided rotating prism billboard (trivision); letters drawn for this pattern; pattern round 102 +// Round: 102 +// =============================== +// @matrix 64x128 +// @knobs Hue=0..1, Speed=0.2..3, Slats=0..2, Wave=0..1 +// +// Knob 1: Hue. Turns the three posters round the wheel together. At 0 ALL is pale yellow on deep blue, FOR navy on yellow and ONE white on magenta; at 0.5 ALL is on amber, FOR on blue and ONE on green. +// Knob 2: Speed. At 2 a word holds 2.4 s and each prism takes 1 s to turn; at 0.2 everything takes ten times as long, at 3 two thirds as long. +// Knob 3: Slats. The number of prisms: 8 at 0, each face 8 LEDs wide; 16 at 1, 4 LEDs; 32 at 2, 2 LEDs. +// Knob 4: Wave. How long the turn takes to cross the sign, 6 s for each unit at Speed 2. At the opening 0.06 it is 0.36 s: the eight prisms start 0.05 s apart, so the leftmost is a third of the way round when the last one starts. At 0 they all turn together; at 1 the sweep takes 6 s, each of the eight starts 0.86 s after its left neighbour and hardly two turn at once. + +// The prisms. Each is an equilateral triangle in plan with side w, the slat +// width, turning about its own vertical axis; at rest one face lies flat to +// the viewer and the faces of neighbours meet edge to edge. Seen from the +// front (an orthographic view), face j of a prism at angle phi has its +// outward normal at psi = (phi - j) 120 degrees from the viewer, and it is +// seen when cos psi > 0. Its two edges stand at psi - 60 and psi + 60 on the +// circle of radius w/sqrt(3) round the axis, so the face covers x from +// R sin(psi - 60) to R sin(psi + 60) across the slat and runs from depth +// R cos(psi - 60) to R cos(psi + 60). Every quarter LED across the panel +// takes the nearest face of its own prism and both neighbours (a turning +// prism's edge reaches 0.08 w past its slot). The face carries slice i of +// poster j mod 3, stretched to its foreshortened width, and the slice is +// read at that quarter LED. Between turning prisms the dark inside of the +// sign shows: the outline of a prism is only 0.87 w wide at 30 and 90 +// degrees. +// +// The light. A lamp at the left front, 40 degrees off the viewer: a face is +// lit by 0.32 + 0.68 cos(psi + 40), scaled so a flat face is 1, so the face +// coming in from the left turns toward the lamp and brightens to 1.19 while +// the face going away to the right falls to 0.38. Where the normal halves +// the angle between lamp and viewer, at psi = -20, the face gives a white +// glint. + +const W = 64, H = 128, SS = 4, WS = W * SS; +const { floor, min, max, round, sin, cos, sqrt, PI, pow } = Math; +const cl = (v, a, b) => (v < a ? a : v > b ? b : v); +const DEG = PI / 180, LAMP = -40 * DEG; +const HOLD = 2.4, TURN = 1.0; + +// Letters on a unit box, x right and y down: rectangles [x0, y0, x1, y1], +// polygons {p: [x, y, ...]}, and rings {o: [cx, cy, rx, ry, tx, ty, side]} +// (a superellipse band, side -1 left half only, 1 right half only, 0 whole). +const GLYPH = { + A: [{ p: [0, 1, 0.37, 0, 0.63, 0, 1, 1, 0.75, 1, 0.5, 0.3, 0.25, 1] }, [0.2, 0.6, 0.8, 0.8]], + L: [[0.06, 0, 0.3, 1], [0.06, 0.77, 0.94, 1]], + F: [[0.06, 0, 0.3, 1], [0.06, 0, 0.94, 0.23], [0.06, 0.4, 0.8, 0.62]], + O: [{ o: [0.5, 0.5, 0.5, 0.5, 0.24, 0.26, 0] }], + R: [[0, 0, 0.24, 1], [0, 0, 0.6, 0.22], [0, 0.42, 0.6, 0.63], { o: [0.6, 0.315, 0.4, 0.315, 0.24, 0.21, 1] }, { p: [0.38, 0.55, 0.66, 0.55, 1, 1, 0.72, 1] }], + N: [[0, 0, 0.23, 1], [0.77, 0, 1, 1], { p: [0, 0, 0.3, 0, 1, 1, 0.7, 1] }], + E: [[0.06, 0, 0.3, 1], [0.06, 0, 0.94, 0.22], [0.06, 0.39, 0.84, 0.61], [0.06, 0.78, 0.94, 1]], +}; +// Three posters: the word and, as [hue, saturation, value], the ink and +// the ground. +const POSTERS = [ + { word: "ALL", ink: [0.14, 0.4, 1], ground: [0.62, 1, 0.72] }, + { word: "FOR", ink: [0.66, 1, 0.38], ground: [0.14, 1, 1] }, + { word: "ONE", ink: [0, 0, 1], ground: [0.9, 1, 0.8] }, +]; +// Letter boxes: 50 x 34 LEDs at x 7, rows 5, 47 and 89. +const LX = 7, LW = 50, LH = 34, LY = [5, 47, 89]; + +function inPoly(p, x, y) { + let c = false; + for (let i = 0, j = p.length - 2; i < p.length; j = i, i += 2) { + const xi = p[i], yi = p[i + 1], xj = p[j], yj = p[j + 1]; + if (yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi) c = !c; + } + return c; +} + +function inGlyph(g, x, y) { + if (x < 0 || x > 1 || y < 0 || y > 1) return false; + for (const s of g) { + if (Array.isArray(s)) { + if (x >= s[0] && x < s[2] && y >= s[1] && y < s[3]) return true; + } else if (s.p) { + if (inPoly(s.p, x, y)) return true; + } else { + const [cx, cy, rx, ry, tx, ty, side] = s.o; + if ((side < 0 && x > cx) || (side > 0 && x < cx)) continue; + const e = 2.6, dx = abs1(x - cx), dy = abs1(y - cy); + const out = pow(dx / rx, e) + pow(dy / ry, e), inn = pow(dx / (rx - tx), e) + pow(dy / (ry - ty), e); + if (out <= 1 && inn > 1) return true; + } + } + return false; +} +const abs1 = (v) => (v < 0 ? -v : v); + +function hsv(h, s, v) { + h = (h - floor(h)) * 6; + const i = floor(h) % 6, f = h - floor(h), p = v * (1 - s), q = v * (1 - f * s), t = v * (1 - (1 - f) * s); + return [[v, t, p], [q, v, p], [p, v, t], [p, q, v], [t, p, v], [v, p, q]][i]; +} + +export function setup(params) { + // Each poster's ink coverage at a quarter LED across and four samples down. + const cov = POSTERS.map((P) => { + const a = new Float32Array(WS * H); + for (let y = 0; y < H; y++) + for (let t = 0; t < WS; t++) { + let n = 0; + for (let sy = 0; sy < 4; sy++) { + const py = y + (sy + 0.5) / 4, px = (t + 0.5) / SS; + for (let l = 0; l < 3; l++) { + const gx = (px - LX) / LW, gy = (py - LY[l]) / LH; + if (inGlyph(GLYPH[P.word[l]], gx, gy)) { + n++; + break; + } + } + } + a[y * WS + t] = n / 4; + } + return a; + }); + params.s = { + cov, T: 1.0, hue: 0, S: 8, wave: 0.06, + face: new Int8Array(WS), tex: new Int32Array(WS), shade: new Float32Array(WS), glint: new Float32Array(WS), + pal: [], phi: new Float32Array(32), + }; +} + +// Prism i's angle in turns of 120 degrees: whole numbers at rest. +function angle(S, i) { + const sweep = S.wave * 6, step = S.S > 1 ? sweep / (S.S - 1) : 0, cyc = HOLD + TURN + sweep; + const c = floor(S.T / cyc), tau = S.T - c * cyc - HOLD - i * step; + const u = cl(tau / TURN, 0, 1); + // The motor eases in and out, and the prism settles with a small overshoot. + const e = u * u * (3 - 2 * u), o = tau > TURN ? 0.025 * sin(((tau - TURN) / 0.35) * PI) * max(0, 1 - (tau - TURN) / 0.7) : 0; + return c + e + o; +} + +export function update(dt, input, params) { + if (!params.s) setup(params); + const S = params.s, k = input.knobValues || [0, 2, 0, 0.06]; + S.hue = k[0] || 0; + const speed = cl(k[1] == null ? 2 : k[1], 0.2, 3); + S.S = [8, 16, 32][cl(round(k[2] || 0), 0, 2)]; + S.wave = cl(k[3] == null ? 0.06 : k[3], 0, 1); + S.T += (cl(dt || 0, 0, 0.1) * speed) / 2; +} + +export function draw(display, params) { + const S = params.s; + if (!S) return; + const n = S.S, w = W / n, R = w / sqrt(3), flat = 0.32 + 0.68 * cos(LAMP); + for (let i = 0; i < n; i++) S.phi[i] = angle(S, i); + // Every quarter LED across: which face is nearest, where on its poster, + // and how it is lit. + for (let t = 0; t < WS; t++) { + const x = (t + 0.5) / SS, i0 = floor(x / w); + let best = -1e9; + S.face[t] = -1; + for (let i = max(0, i0 - 1); i <= min(n - 1, i0 + 1); i++) { + const phi = S.phi[i], xc = (i + 0.5) * w, j0 = floor(phi); + for (let j = j0 - 1; j <= j0 + 1; j++) { + const psi = (phi - j) * 120 * DEG; + if (cos(psi) <= 0) continue; + const xa = xc + R * sin(psi - PI / 3), xb = xc + R * sin(psi + PI / 3); + if (x < xa || x >= xb) continue; + const u = (x - xa) / (xb - xa), z = R * cos(psi - PI / 3) + u * (R * cos(psi + PI / 3) - R * cos(psi - PI / 3)); + if (z <= best) continue; + best = z; + S.face[t] = ((j % 3) + 3) % 3; + S.tex[t] = min(WS - 1, floor((i + u) * w * SS)); + // A dark seam where faces meet, half an LED wide (a quarter on 2-LED slats). + const seam = min(u, 1 - u) * w < min(0.5, w / 8) ? 0.62 : 1; + S.shade[t] = (seam * (0.32 + 0.68 * max(0, cos(psi - LAMP)))) / flat; + S.glint[t] = 0.4 * pow(max(0, cos(psi - LAMP / 2)), 60); + } + } + } + const pal = POSTERS.map((P) => [hsv(S.hue + P.ink[0], P.ink[1], P.ink[2]), hsv(S.hue + P.ground[0], P.ground[1], P.ground[2])]); + for (let y = 0; y < H; y++) + for (let x = 0; x < W; x++) { + let r = 0, g = 0, b = 0; + for (let q = 0; q < SS; q++) { + const t = x * SS + q, f = S.face[t]; + if (f < 0) continue; + const c = S.cov[f][y * WS + S.tex[t]], [ink, gr] = pal[f], sh = S.shade[t], gl = S.glint[t]; + r += (gr[0] + (ink[0] - gr[0]) * c) * sh + gl; + g += (gr[1] + (ink[1] - gr[1]) * c) * sh + gl; + b += (gr[2] + (ink[2] - gr[2]) * c) * sh + gl; + } + display.setPixel(x, y, round(min(1, r / SS) * 255), round(min(1, g / SS) * 255), round(min(1, b / SS) * 255)); + } +} From b4ca85df88d5b7df7cb8dc726387b5a45d3c6b0b Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 08:34:52 +0000 Subject: [PATCH 4/9] =?UTF-8?q?=ED=8C=A8=ED=84=B4=20=EC=97=B0=EA=B5=AC=201?= =?UTF-8?q?02=ED=8C=90:=20Sch=C3=B6nhardt=20Polyhedron=20=EA=B3=BC=20Heaps?= =?UTF-8?q?=20Page=20=EB=A5=BC=20=EB=8D=94=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Bc4JkshDz129z5fXhUWQxz --- incoming/pattern-study/patterns/heaps-page.js | 280 +++++++++++++ .../patterns/schonhardt-polyhedron.js | 393 ++++++++++++++++++ 2 files changed, 673 insertions(+) create mode 100644 incoming/pattern-study/patterns/heaps-page.js create mode 100644 incoming/pattern-study/patterns/schonhardt-polyhedron.js diff --git a/incoming/pattern-study/patterns/heaps-page.js b/incoming/pattern-study/patterns/heaps-page.js new file mode 100644 index 00000000..a683f3cd --- /dev/null +++ b/incoming/pattern-study/patterns/heaps-page.js @@ -0,0 +1,280 @@ +// ===== Patternflow pattern ===== +// Title: Heaps Page +// Family: type +// Idea: A page of an old text writes itself word by word down the tall panel in a small lowercase face, every word bright the first time the text uses it and dim each time it comes back, so the sentence's repeated scaffolding sinks into the page while its new words stand out, the page before shows grey below the line being written, and at the foot the count of different words V climbs against the words read N on log-log axes beside the fitted line V = K N^beta, whose slope beta is printed as it changes. +// Author: patternflow-lab +// Date: 2026-11-03 +// SPDX-License-Identifier: CC-BY-SA-4.0 +// Lineage: the growth of vocabulary with text length, V = K N^b (Gustav Herdan, Type-Token Mathematics, 1960; Harold Stanley Heaps, Information Retrieval: Computational and Theoretical Aspects, 1978); texts the opening of Charles Dickens's A Tale of Two Cities (1859), Ecclesiastes 3:1-8 in the King James Version (1611) and Lewis Carroll's Jabberwocky (Through the Looking-Glass, 1871), all public domain; the lowercase face of Scrambled Middles (pattern round 101) with capitals, figures and marks added; pattern round 102 +// Round: 102 +// =============================== +// @matrix 64x128 +// @knobs Hue=0..1, Speed=0.2..3, Text=0..2, Forget=0..1 +// +// Knob 1: Hue. Turns the colours round the wheel together. At 0 a new word is a pale gold, a repeated one slate blue, the word just written flashes white, and the fitted line is aqua; at 0.5 new words are pale blue, repeats a dim rose-brown and the line pink. +// Knob 2: Speed. Words written a second, 1.5 times the knob: 3 at 2, so the 186 words of Dickens take 62 s and his first page of 38 pieces 12.7 s; 0.3 at 0.2 and 4.5 at 3. At the end the last page holds for 12 words' time and the text starts again. +// Knob 3: Text. In whole steps: 0 the first two paragraphs of A Tale of Two Cities, 186 words, 86 different, beta 0.92 at the end; 1 Ecclesiastes 3:1-8, 158 words, 52 different, beta 0.68; 2 Jabberwocky, 166 words, 90 different, beta 0.86, whose last stanza repeats the first word for word and comes out all dim. Each opens 8 pieces short of the end of its first page, 30 pieces in for Dickens. +// Knob 4: Forget. How long a word stays known. Below 0.08 it never forgets, Heaps' plain count. Above it a word counts as new again once more than M words have passed since it last came, M = 2 x 100^((1 - Forget) / 0.92): 200 at 0.08, 85 at 0.25, 24 at 0.5, 7 at 0.75 and 2 at 1. Dickens's it was the ... of comes round every six words, so from M = 5 (Forget 0.8) it lights again and his V jumps from 129 to 170. The page, V and the line are worked out again at once. + +// Heaps' law. Read a text word by word and count the different words seen: +// after N words there are V of them, and over long texts V grows close to +// K N^beta with beta between about 0.4 and 0.6, so each new page brings +// fewer new words than the one before. Herdan wrote it down in 1960 and +// Heaps for document collections in 1978. Texts this short stay closer to +// beta = 1. The fit here is least squares of log V on log N over every word +// read so far, and it ends at 0.92 for Dickens, 0.68 for the preacher's a +// time to ... and 0.86 for Carroll. The preacher thins out page by page as +// the law says: 22 new words of 41 on his first page, 12 of 43, 9 of 40, 9 +// of 34. Dickens does the opposite for four pages, 13 of 38, 16 of 36, 19 +// of 31 and 22 of 34, because his first paragraph hangs a new noun on the +// same six-word frame each time and the second brings kings and queens; +// then the second king and queen repeat the first, 11 of 38. +// +// Words are runs of letters with inner apostrophes and hyphens, folded to +// lowercase, so It and it are one word and snicker-snack is one. +// +// The page is 11 lines 9 rows apart from row 1. When it fills, the next page +// writes over it from the top, and the lines below the line being written +// still show the page before in grey, its new words light and its repeats +// dark, so the panel always holds two pages, the newer one above. The foot: +// V, N, beta and the forgetting window M on rows 100 to 107, and the plot on +// rows 108 to 126, log N across from 1 to the whole text and log V up from 1 +// to the text's final count, where V = K N^beta is a straight line of slope +// beta. V climbs as the top of a lit area, the grey line is V = N (every +// word new) and the aqua line is the fit, solid to the word read and dotted +// past it. + +const W = 64, H = 128; +const { floor, round, min, max, log, exp, abs, sqrt, ceil } = Math; +const cl = (v, a, b) => (v < a ? a : v > b ? b : v); + +const TEXTS = [ + // Charles Dickens, A Tale of Two Cities (1859), Book 1 Chapter 1, the first two paragraphs. + "It was the best of times, it was the worst of times, it was the age of wisdom, it was the age of foolishness, it was the epoch of belief, it was the epoch of incredulity, it was the season of Light, it was the season of Darkness, it was the spring of hope, it was the winter of despair, we had everything before us, we had nothing before us, we were all going direct to Heaven, we were all going direct the other way - in short, the period was so far like the present period, that some of its noisiest authorities insisted on its being received, for good or for evil, in the superlative degree of comparison only. There were a king with a large jaw and a queen with a plain face, on the throne of England; there were a king with a large jaw and a queen with a fair face, on the throne of France. In both countries it was clearer than crystal to the lords of the State preserves of loaves and fishes, that things in general were settled for ever.", + // Ecclesiastes 3:1-8, King James Version (1611). + "To every thing there is a season, and a time to every purpose under the heaven: A time to be born, and a time to die; a time to plant, and a time to pluck up that which is planted; A time to kill, and a time to heal; a time to break down, and a time to build up; A time to weep, and a time to laugh; a time to mourn, and a time to dance; A time to cast away stones, and a time to gather stones together; a time to embrace, and a time to refrain from embracing; A time to get, and a time to lose; a time to keep, and a time to cast away; A time to rend, and a time to sew; a time to keep silence, and a time to speak; A time to love, and a time to hate; a time of war, and a time of peace.", + // Lewis Carroll, Jabberwocky, from Through the Looking-Glass (1871), run on. + "'Twas brillig, and the slithy toves Did gyre and gimble in the wabe: All mimsy were the borogoves, And the mome raths outgrabe. \"Beware the Jabberwock, my son! The jaws that bite, the claws that catch! Beware the Jubjub bird, and shun The frumious Bandersnatch!\" He took his vorpal sword in hand; Long time the manxome foe he sought - So rested he by the Tumtum tree And stood awhile in thought. And, as in uffish thought he stood, The Jabberwock, with eyes of flame, Came whiffling through the tulgey wood, And burbled as it came! One, two! One, two! And through and through The vorpal blade went snicker-snack! He left it dead, and with its head He went galumphing back. \"And hast thou slain the Jabberwock? Come to my arms, my beamish boy! O frabjous day! Callooh! Callay!\" He chortled in his joy. 'Twas brillig, and the slithy toves Did gyre and gimble in the wabe: All mimsy were the borogoves, And the mome raths outgrabe.", +]; + +// An 8-row body: ascenders in rows 0-1, x-height rows 2-5, descenders 6-7. +// The face is Scrambled Middles' with B D E F J M N V, figures, ! ? " added, +// and ~ drawn as a beta. +const FACE = { + a: "../../##./.##/#.#/.##", b: "#../#../##./#.#/#.#/##.", c: "../../.##/#../#../.##", + d: "..#/..#/.##/#.#/#.#/.##", e: "../../.#./###/#../.##", f: ".##/.#./###/.#./.#./.#.", + g: "../../.##/#.#/#.#/.##/..#/##.", h: "#../#../##./#.#/#.#/#.#", i: "#/./#/#/#/#", + j: ".#/../.#/.#/.#/.#/.#/#.", k: "#../#../#.#/##./#.#/#.#", l: "#/#/#/#/#/#", + m: "../../####./#.#.#/#.#.#/#.#.#", n: "../../##./#.#/#.#/#.#", o: "../../.#./#.#/#.#/.#.", + p: "../../##./#.#/#.#/##./#../#..", q: "../../.##/#.#/#.#/.##/..#/..#", r: "../../#.#/##./#../#..", + s: "../../###/#../..#/###", t: ".../.#./###/.#./.#./.##", u: "../../#.#/#.#/#.#/.##", + v: "../../#.#/#.#/#.#/.#.", w: "../../#...#/#...#/#.#.#/.#.#.", x: "../../#.#/.#./.#./#.#", + y: "../../#.#/#.#/#.#/.##/..#/##.", z: "../../###/..#/.#./###", + A: ".#./#.#/#.#/###/#.#/#.#", B: "##./#.#/##./#.#/#.#/##.", C: ".##/#../#../#../#../.##", + D: "##./#.#/#.#/#.#/#.#/##.", E: "###/#../##./#../#../###", F: "###/#../##./#../#../#..", + H: "#.#/#.#/#.#/###/#.#/#.#", I: "###/.#./.#./.#./.#./###", J: "..#/..#/..#/..#/#.#/.#.", + L: "#../#../#../#../#../###", M: "#...#/##.##/#.#.#/#...#/#...#/#...#", N: "#..#/##.#/#.##/#..#/#..#/#..#", + O: ".#./#.#/#.#/#.#/#.#/.#.", S: ".##/#../.#./..#/..#/##.", T: "###/.#./.#./.#./.#./.#.", + V: "#.#/#.#/#.#/#.#/#.#/.#.", + 0: ".#./#.#/#.#/#.#/#.#/.#.", 1: ".#/##/.#/.#/.#/.#", 2: "##./..#/..#/.#./#../###", 3: "##./..#/.#./..#/..#/##.", + 4: "#.#/#.#/#.#/###/..#/..#", 5: "###/#../##./..#/..#/##.", 6: ".##/#../##./#.#/#.#/.#.", 7: "###/..#/..#/.#./.#./.#.", + 8: ".#./#.#/.#./#.#/#.#/.#.", 9: ".#./#.#/#.#/.##/..#/##.", + ",": "./././././#/#", ".": "./././././#", ":": "./././#/./#", ";": "./././#/./#/#", + "-": "../../../##", "'": "#/#", "!": "#/#/#/#/./#", "?": "##./..#/.#./.#././.#.", '"': "#.#/#.#", + "~": "##./#.#/##./#.#/#.#/##./#../#..", +}; +const GLYPH = {}, GW = {}; +for (const ch in FACE) { + const rows = FACE[ch].split("/"), px = []; + rows.forEach((r, y) => [...r].forEach((c, x) => c === "#" && px.push(x, y))); + GLYPH[ch] = px; + GW[ch] = max(...rows.map((r) => r.length)); +} +const SPACE = 3, LINES = 11, PITCH = 9, TOP = 1, LABEL = 100, PLOT0 = 108, PLOT1 = 126; +const wide = (s) => [...s].reduce((w, c) => w + (GW[c] || 3) + 1, -1); + +// Each text as pieces (a word with its marks, or a mark alone), laid out once. +function prep(text) { + const pieces = [], words = []; + let x = 0, line = 0; + for (const s of text.split(/ +/)) { + const m = s.match(/[A-Za-z]+(?:['-][A-Za-z]+)*/), w = wide(s); + if (x > 0 && x + SPACE + w > 63) { + x = 0; + line++; + } + if (x > 0) x += SPACE; + pieces.push({ s, x: x + 1, page: floor(line / LINES), line: line % LINES, word: m ? words.length : -1 }); + if (m) words.push(m[0].toLowerCase()); + x += w; + } + return { pieces, words, pages: floor(line / LINES) + 1 }; +} +const PREP = TEXTS.map(prep); + +// The forgetting window in words, or Infinity. +const windowOf = (f) => (f < 0.08 ? Infinity : round(2 * 100 ** ((1 - f) / 0.92))); + +// Which words count as new under window M, and V after each word. +function novelty(T, M) { + const last = new Map(), fresh = new Uint8Array(T.words.length), V = new Int32Array(T.words.length + 1); + T.words.forEach((w, i) => { + const l = last.get(w); + fresh[i] = l === undefined || i - l > M ? 1 : 0; + last.set(w, i); + V[i + 1] = V[i] + fresh[i]; + }); + return { fresh, V }; +} + +function hsv(h, s, v) { + h = (h - floor(h)) * 6; + const i = floor(h) % 6, f = h - floor(h), p = v * (1 - s), q = v * (1 - s * f), t = v * (1 - s * (1 - f)); + return [[v, t, p], [q, v, p], [p, v, t], [p, q, v], [t, p, v], [v, p, q]][i]; +} + +// A text opens 8 pieces short of the end of its first page; the hold at the +// end is in word-times. +const PREROLL = 8, HOLD = 12; + +function start(S, t, preroll) { + S.text = t; + S.pos = 0; + S.acc = 0; + S.hold = -1; + S.flash = new Float32Array(PREP[t].pieces.length); + S.nov = novelty(PREP[t], S.M); + S.pos = preroll ? PREP[t].pieces.filter((p) => p.page === 0).length - preroll : 0; +} + +export function setup(params) { + const S = { buf: new Float32Array(W * H * 3), hue: 0, speed: 2, M: Infinity, wrapped: false }; + start(S, 0, PREROLL); + params.s = S; +} + +export function update(dt, input, params) { + if (!params.s) setup(params); + const S = params.s, k = input.knobValues || [0, 2, 0, 0.06], d = cl(dt || 0, 0, 0.1); + S.hue = k[0] || 0; + S.speed = cl(k[1] == null ? 2 : k[1], 0.2, 3); + const M = windowOf(cl(k[3] == null ? 0.06 : k[3], 0, 1)); + const t = cl(round(k[2] || 0), 0, PREP.length - 1); + if (t !== S.text) { + S.M = M; + S.wrapped = false; + start(S, t, PREROLL); + } else if (M !== S.M) { + S.M = M; + S.nov = novelty(PREP[S.text], M); + } + const T = PREP[S.text]; + S.acc += d * 1.5 * S.speed; + while (S.acc >= 1) { + S.acc -= 1; + if (S.hold >= 0) { + if (++S.hold >= HOLD) { + S.wrapped = true; + start(S, S.text, 0); + break; + } + } else if (S.pos < T.pieces.length) S.flash[S.pos++] = 1; + else S.hold = 0; + } + const cool = exp(-d * 5); + for (let i = 0; i < S.flash.length; i++) S.flash[i] *= cool; +} + +function add(buf, x, y, c, a) { + if (x < 0 || x >= W || y < 0 || y >= H || a <= 0) return; + const p = (y * W + x) * 3; + buf[p] = max(buf[p], c[0] * a); + buf[p + 1] = max(buf[p + 1], c[1] * a); + buf[p + 2] = max(buf[p + 2], c[2] * a); +} + +function write(buf, s, x, y, c, a) { + for (const ch of s) { + const px = GLYPH[ch]; + if (px) for (let i = 0; i < px.length; i += 2) add(buf, x + px[i], y + px[i + 1], c, a); + x += (GW[ch] || 3) + 1; + } + return x; +} + +// An anti-aliased line one LED wide, dotted when dot is set. +function seg(buf, ax, ay, bx, by, c, a, dot) { + const n = max(1, ceil(max(abs(bx - ax), abs(by - ay)) * 2)); + for (let k = 0; k <= n; k++) { + const x = ax + ((bx - ax) * k) / n - 0.5, y = ay + ((by - ay) * k) / n - 0.5, ix = floor(x), iy = floor(y), fx = x - ix, fy = y - iy; + if (dot && (ix + iy) % 3) continue; + for (const [dx, dy, w] of [[0, 0, (1 - fx) * (1 - fy)], [1, 0, fx * (1 - fy)], [0, 1, (1 - fx) * fy], [1, 1, fx * fy]]) add(buf, ix + dx, iy + dy, c, a * sqrt(w)); + } +} + +export function draw(display, params) { + const S = params.s; + if (!S) return; + const buf = S.buf, T = PREP[S.text], P = T.pieces, nov = S.nov; + buf.fill(0); + const fresh = hsv(S.hue + 0.11, 0.45, 1), stale = hsv(S.hue + 0.6, 0.6, 0.5), fitc = hsv(S.hue + 0.47, 0.6, 1); + const grey = [0.3, 0.3, 0.3], white = [1, 1, 1]; + const isNew = (i) => P[i].word >= 0 && nov.fresh[P[i].word]; + const colour = (i) => { + const c = isNew(i) ? fresh : stale, f = S.flash[i]; + return [c[0] + (1 - c[0]) * f, c[1] + (1 - c[1]) * f, c[2] + (1 - c[2]) * f]; + }; + // The page before is grey: its new words a light grey, its repeats darker. + const ghost = (i) => (isNew(i) ? [0.42, 0.42, 0.42] : [stale[0] * 0.5, stale[1] * 0.5, stale[2] * 0.5]); + + // The page being written down to its last line, and the page before + // below it; after the text starts again, the page before is its last. + const last = S.pos - 1, cur = last >= 0 ? P[last].page : 0, head = last >= 0 ? P[last].line : -1; + const prev = cur > 0 ? cur - 1 : S.wrapped ? T.pages - 1 : -1; + for (let i = 0; i < P.length; i++) { + const p = P[i], y = TOP + PITCH * p.line; + if (p.page === cur && i <= last) write(buf, p.s, p.x, y, colour(i), 1); + else if (p.page === prev && p.line > head) write(buf, p.s, p.x, y, ghost(i), 1); + } + + // V, b and the window over the plot. + const N = P.slice(0, S.pos).filter((p) => p.word >= 0).length, Nt = T.words.length, V = nov.V, Vt = V[Nt]; + let b = 1, K = 1; + if (N >= 3) { + let sx = 0, sy = 0, sxx = 0, sxy = 0; + for (let n = 1; n <= N; n++) { + const x = log(n), y = log(V[n]); + sx += x; sy += y; sxx += x * x; sxy += x * y; + } + b = (N * sxy - sx * sy) / (N * sxx - sx * sx); + K = exp((sy - b * sx) / N); + } + const vx = write(buf, "V", 1, LABEL, fresh, 1); + write(buf, String(V[N]), vx + 2, LABEL, fresh, 1); + const nx = write(buf, "N", 22, LABEL, stale, 1.6); + write(buf, String(N), nx + 2, LABEL, stale, 1.6); + const bs = "~" + b.toFixed(2).replace(/^0/, ""); + write(buf, bs, 64 - wide(bs), LABEL, fitc, 1); + if (S.M < Infinity) write(buf, "M" + S.M, 1, PLOT0, stale, 1.6); + + // The plot, log N across and log V up. + const lN = log(Nt), lV = log(max(2, Vt)); + const X = (n) => 1 + (61 * log(n)) / lN, Y = (v) => PLOT1 - ((PLOT1 - PLOT0) * log(v)) / lV; + seg(buf, X(1), Y(1), X(min(Nt, Vt)), Y(min(Nt, Vt)), grey, 1, false); + for (let x = 1; x <= 62; x++) { + const n = min(Nt, round(exp(((x - 1) * lN) / 61))); + if (n > N) break; + const top = Y(V[n]); + for (let y = PLOT1; y >= floor(top); y--) add(buf, x, y, y === floor(top) ? fresh : stale, y === floor(top) ? 1 : 0.6); + } + if (N >= 3) { + const xe = min(62, X(N)), clip = (n) => Y(min(K * n ** b, Vt)); + seg(buf, X(1), Y(K), xe, clip(N), fitc, 0.85, false); + if (N < Nt) seg(buf, xe, clip(N), X(Nt), clip(Nt), fitc, 0.85, true); + } + + for (let i = 0; i < W * H; i++) { + const p = i * 3; + display.setPixel(i % W, floor(i / W), round(cl(buf[p], 0, 1) * 255), round(cl(buf[p + 1], 0, 1) * 255), round(cl(buf[p + 2], 0, 1) * 255)); + } +} diff --git a/incoming/pattern-study/patterns/schonhardt-polyhedron.js b/incoming/pattern-study/patterns/schonhardt-polyhedron.js new file mode 100644 index 00000000..ced7b0af --- /dev/null +++ b/incoming/pattern-study/patterns/schonhardt-polyhedron.js @@ -0,0 +1,393 @@ +// ===== Patternflow pattern ===== +// Title: Schönhardt Polyhedron +// Family: space +// Idea: A tall glass triangular prism turns slowly while its top twists against its bottom: twisted one way the solid is convex and all 15 tetrahedra on its six corners fit inside it, untwisted it splits into three of them, and twisted the other way its side faces fold inward along three gold creases while the three other top-to-bottom diagonals leave the solid and hang pink across the valleys, so the count at the foot, tested from the shape in every frame, falls to 0. +// Author: patternflow-lab +// Date: 2026-11-03 +// SPDX-License-Identifier: CC-BY-SA-4.0 +// Lineage: the twisted triangular prism that cannot be cut into tetrahedra without new vertices (Erich Schönhardt, Mathematische Annalen, 1928); pattern round 102 +// Round: 102 +// =============================== +// @matrix 64x128 +// @knobs Hue=0..1, Speed=0.2..3, Twist=-1..1, View=-1..1 +// +// Knob 1: Hue. Turns the colours round the wheel together. At 0 the glass is azure, the folded edges gold, a diagonal inside the solid green and one outside it pink, and the three pieces of the cut azure, violet and sea green; at 0.5 the glass is amber, the folds blue, an inside diagonal magenta and an outside one sea green. +// Knob 2: Speed. At 2 the prism turns once in 24 s and the twist runs its cycle in 24 s: 6 s folded at the full twist, 3 s untwisting, 4.5 s as a plain prism while its three tetrahedra slide apart and back, 3 s twisting the other way to 0.6 of the full twist, where the solid is convex, 3 s there and 4.5 s back to the fold; every time goes as 2 / Speed. +// Knob 3: Twist. The full twist of the top triangle against the bottom, 44 + 14 x Twist degrees: 44 at the opening, 30 at -1 and 58 at 1, where the three creases pass 0.017 of the radius from the axis at mid-height; at 60 they would meet there and the faces cross. +// Knob 4: View. The camera's height, as the angle it looks down: 32 + 22 x View degrees, so 33.3 at the opening 0.06, 10 at -1, where the top and bottom triangles are thin slivers, and 54 at 1, looking down into the valleys. + +// The prism. Bottom corners A0 A1 A2 on the unit circle at 120 degrees, +// top corners B0 B1 B2 above them at height HT, turned by the twist th. The +// side face over A_i A_(i+1) is cut by the diagonal A_i B_(i+1) into two +// triangles, so the solid has eight triangular faces. For th > 0 that +// diagonal spans 120 + th degrees of the circle and the other, A_(i+1) B_i, +// only 120 - th; the solid's surface keeps the long one, and the face pair +// folds in along it. At mid-height the crease sits cos(60 + th / 2) of the +// radius from the axis, 0.14 at 44 degrees, and the short diagonal crosses +// right over it at cos(60 - th / 2), 0.79: a string across a valley. +// Every one of the 15 sets of four corners has one of those three short +// diagonals among its six edges (three bottom corners and one top, or the +// other way, or two and two, which leave out at most one index on each +// side), so no tetrahedron on the corners fits inside, and a cut into +// tetrahedra would need a new vertex. Twisted the other way the solid is the +// convex hull of its corners and all 15 fit; at th = 0 the side faces are +// flat rectangles, three of the sets are flat, and the other 12 fit. +// +// None of that is written into the code. Each frame (when the twist has +// moved) tests every set: a tetrahedron of no volume is flat, and otherwise +// 35 points on its edges, faces and middle, each nudged 0.2% towards its +// centre, are tested against the solid by the winding number, the sum of +// the solid angles its eight faces subtend at the point (A. van Oosterom +// and J. Strackee's formula, 1983), 1 inside and 0 outside. The diagonals +// are tested the same way at 13 points, nudged towards the centre, and drawn +// point by point in green or pink; a folded edge is gold where one of its +// two faces has the other's far corner on its outer side. +// +// Drawing: every face is glass, 72% opaque from the front and 35% from +// behind, flat-shaded by a light from the left, the faces sorted back to +// front in each LED. The sections of the solid at seven heights are drawn +// on it, each a star of six sides that bends in at the creases. Lines carry +// their depth and dim to 30% behind each front face. The cut at th = 0 is the prism split into A0A1A2B0, +// A1A2B0B1 and A2B0B1B2, which slide 0.3 out sideways, each towards the +// corner its middle leans to, and back. + +const W = 64, H = 128; +const { floor, ceil, min, max, sin, cos, sqrt, abs, atan2, round, hypot, PI } = Math; +const D2R = PI / 180; +const cl = (v, a, b) => (v < a ? a : v > b ? b : v); +const sm = (u) => u * u * (3 - 2 * u); +const HT = 4.2, DC = 7, TN = 0.6, FOOT = 113; + +const sub = (a, b) => [a[0] - b[0], a[1] - b[1], a[2] - b[2]]; +const add3 = (a, b, k) => [a[0] + b[0] * k, a[1] + b[1] * k, a[2] + b[2] * k]; +const cross = (a, b) => [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]]; +const dot = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; +const len = (a) => hypot(a[0], a[1], a[2]); +const mid = (P) => P.reduce((s, p) => add3(s, p, 1 / P.length), [0, 0, 0]); + +function corners(th) { + const V = []; + for (let i = 0; i < 6; i++) { + const a = (2 * PI * (i % 3)) / 3 + PI / 2 + (i < 3 ? 0 : th); + V.push([cos(a), i < 3 ? -HT / 2 : HT / 2, sin(a)]); + } + return V; +} + +// The faces, wound so that their normals point out of the prism at th = 0; +// the winding holds for every twist, since the faces only bend. +const FACES = [[0, 1, 2], [3, 4, 5]]; +for (let i = 0; i < 3; i++) { + const j = (i + 1) % 3; + FACES.push([i, j, 3 + j], [i, 3 + j, 3 + i]); +} +{ + const V = corners(0); + for (const f of FACES) { + const n = cross(sub(V[f[1]], V[f[0]]), sub(V[f[2]], V[f[0]])); + if (dot(n, mid(f.map((k) => V[k]))) < 0) f.reverse(); + } +} +// Edges: ring (top and bottom), post A_i B_i, fold A_i B_(i+1); and the +// three short diagonals A_(i+1) B_i, which are not edges. +const EDGES = []; +for (let i = 0; i < 3; i++) { + const j = (i + 1) % 3; + EDGES.push([i, j, 0], [3 + i, 3 + j, 0], [i, 3 + i, 1], [i, 3 + j, 2]); +} +const DIAG = [0, 1, 2].map((i) => [(i + 1) % 3, 3 + i]); +const QUADS = []; +for (let a = 0; a < 6; a++) for (let b = a + 1; b < 6; b++) for (let c = b + 1; c < 6; c++) for (let d = c + 1; d < 6; d++) QUADS.push([a, b, c, d]); +const CUT = [[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5]]; + +// Winding number of the surface about p: 1 inside, 0 outside. +function wind(p, V) { + let s = 0; + for (const f of FACES) { + const a = sub(V[f[0]], p), b = sub(V[f[1]], p), c = sub(V[f[2]], p); + const la = len(a), lb = len(b), lc = len(c); + s += 2 * atan2(dot(a, cross(b, c)), la * lb * lc + dot(a, b) * lc + dot(a, c) * lb + dot(b, c) * la); + } + return abs(s) / (4 * PI); +} +const inside = (p, toward, V) => wind(add3(p, sub(toward, p), 0.002), V) > 0.5; + +// 1 inside, 0 flat, -1 not inside, for each of the 15 sets. +function census(V) { + return QUADS.map((q) => { + const P = q.map((k) => V[k]); + const vol = dot(sub(P[1], P[0]), cross(sub(P[2], P[0]), sub(P[3], P[0]))) / 6; + if (abs(vol) < 1e-5) return 0; + const g = mid(P), pts = [g]; + for (let i = 0; i < 4; i++) + for (let j = i + 1; j < 4; j++) { + for (let t = 1; t < 6; t++) pts.push(add3(P[i], sub(P[j], P[i]), t / 6)); + for (let k = j + 1; k < 4; k++) pts.push(mid([P[i], P[j], P[k]])); + } + for (const p of pts) if (!inside(p, g, V)) return -1; + return 1; + }); +} + +function hsv(h, s, v) { + h = (h - floor(h)) * 6; + const i = floor(h) % 6, f = h - floor(h), p = v * (1 - s), q = v * (1 - s * f), t = v * (1 - s * (1 - f)); + return [[v, t, p], [q, v, p], [p, v, t], [p, q, v], [t, p, v], [v, p, q]][i]; +} + +const DIG = { 0: [7, 5, 5, 5, 7], 1: [2, 6, 2, 2, 7], 2: [7, 1, 7, 4, 7], 3: [7, 1, 3, 1, 7], 4: [5, 5, 7, 1, 1], 5: [7, 4, 7, 1, 7], 6: [7, 4, 7, 5, 7], 7: [7, 1, 2, 2, 2], 8: [7, 5, 7, 5, 7], 9: [7, 5, 7, 1, 7], "/": [1, 1, 2, 4, 4] }; + +export function setup(params) { + params.s = { + buf: new Float32Array(W * H * 3), cnt: new Uint8Array(W * H), zl: new Float32Array(W * H * 12), fl: new Uint8Array(W * H * 12), fid: new Uint8Array(W * H * 12), + yaw: 0, ph: 0, hue: 0, tmax: 44, view: 0.06, fitView: null, fit: null, cTh: null, cen: null, diag: null, + }; +} + +export function update(dt, input, params) { + if (!params.s) setup(params); + const S = params.s, k = input.knobValues || [0, 2, 0, 0.06], d = cl(dt || 0, 0, 0.1); + S.hue = k[0] || 0; + const speed = cl(k[1] == null ? 2 : k[1], 0.2, 3); + S.tmax = 44 + 14 * cl(k[2] || 0, -1, 1); + S.view = cl(k[3] == null ? 0.06 : k[3], -1, 1); + S.yaw = (S.yaw + (d * speed * 2 * PI) / 48) % (2 * PI); + S.ph = (S.ph + (d * speed) / 48) % 1; +} + +// The twist (as a share of the full twist) and the cut's spread at phase u. +function cycle(u) { + if (u < 0.25) return [1, 0]; + if (u < 0.375) return [1 - sm((u - 0.25) / 0.125), 0]; + if (u < 0.5625) return [0, sin((PI * (u - 0.375)) / 0.1875) ** 2]; + if (u < 0.6875) return [-TN * sm((u - 0.5625) / 0.125), 0]; + if (u < 0.8125) return [-TN, 0]; + return [-TN + (1 + TN) * sm((u - 0.8125) / 0.1875), 0]; +} + +// Turn about the vertical by yaw, then tip by el so the eye looks down; the +// eye sits at depth DC, and depth grows towards it. +function rot(p, yaw, el) { + const cy = cos(yaw), sy = sin(yaw), ce = cos(el), se = sin(el); + const x1 = p[0] * cy + p[2] * sy, z1 = -p[0] * sy + p[2] * cy; + return [x1, p[1] * ce - z1 * se, p[1] * se + z1 * ce]; +} +function cam(p, yaw, el) { + const q = rot(p, yaw, el), s = DC / (DC - q[2]); + return [q[0] * s, q[1] * s, q[2]]; +} + +// Scale so that every turn and twist fits columns 2 to 61 and rows 2 to FOOT - 3. +function fit(S, el) { + if (S.fitView === S.view) return S.fit; + let x0 = 1e9, x1 = -1e9, y0 = 1e9, y1 = -1e9; + for (let i = 0; i < 24; i++) + for (const th of [-35, 0, 44, 58]) + for (const p of corners(th * D2R)) { + const q = cam(p, (i * PI) / 12, el); + x0 = min(x0, q[0]); x1 = max(x1, q[0]); y0 = min(y0, q[1]); y1 = max(y1, q[1]); + } + const K = min(60 / (x1 - x0), (FOOT - 5) / (y1 - y0)); + S.fit = { K, cx: 32 - (K * (x0 + x1)) / 2, cy: 2 + (FOOT - 5) / 2 + (K * (y0 + y1)) / 2 }; + S.fitView = S.view; + return S.fit; +} + +function put(buf, x, y, c, a) { + const p = (y * W + x) * 3; + buf[p] += (c[0] - buf[p]) * a; + buf[p + 1] += (c[1] - buf[p + 1]) * a; + buf[p + 2] += (c[2] - buf[p + 2]) * a; +} + +// How much of a line at depth z shows through the faces in front of it. +function seen(S, x, y, z) { + const i = y * W + x, n = S.cnt[i]; + let v = 1; + for (let k = 0; k < n; k++) if (S.zl[i * 12 + k] > z + 0.04) v *= S.fl[i * 12 + k] ? 0.3 : 0.8; + return v; +} + +// A segment wid LEDs wide from screen point a to b (x, y, depth), dashed +// with period dash LEDs when dash > 0. +function seg(S, a, b, wid, c, al, dash) { + const buf = S.buf, r = wid / 2 + 0.5, dx = b[0] - a[0], dy = b[1] - a[1], L2 = dx * dx + dy * dy || 1e-9, L = sqrt(L2); + const x0 = max(0, floor(min(a[0], b[0]) - r)), x1 = min(W - 1, ceil(max(a[0], b[0]) + r)); + const y0 = max(0, floor(min(a[1], b[1]) - r)), y1 = min(H - 1, ceil(max(a[1], b[1]) + r)); + for (let y = y0; y <= y1; y++) + for (let x = x0; x <= x1; x++) { + const px = x + 0.5 - a[0], py = y + 0.5 - a[1], t = cl((px * dx + py * dy) / L2, 0, 1); + let cov = cl(wid / 2 + 0.5 - hypot(px - t * dx, py - t * dy), 0, 1); + if (dash > 0) cov *= cl(2.2 * cos((2 * PI * t * L) / dash) + 0.6, 0, 1); + if (cov > 0) put(buf, x, y, c, cov * al * seen(S, x, y, a[2] + (b[2] - a[2]) * t)); + } +} + +export function draw(display, params) { + const S = params.s; + if (!S) return; + const { buf, cnt, zl, fl, fid } = S; + buf.fill(0); + cnt.fill(0); + const [tw, spread] = cycle(S.ph), th = tw * S.tmax * D2R, el = (32 + 22 * S.view) * D2R, F = fit(S, el); + const V = corners(th); + if (S.cTh !== th) { + S.cTh = th; + S.cen = census(V); + S.diag = DIAG.map(([a, b]) => { + const out = []; + for (let t = 0; t <= 12; t++) { + const p = add3(V[a], sub(V[b], V[a]), t / 12); + out.push(inside(p, [0, 0, 0], V)); + } + return out; + }); + // A fold edge is reflex when the far corner of one face lies outside the other's plane. + S.reflex = EDGES.map(([a, b]) => { + const fs = FACES.filter((f) => f.includes(a) && f.includes(b)); + const n = cross(sub(V[fs[0][1]], V[fs[0][0]]), sub(V[fs[0][2]], V[fs[0][0]])); + const far = fs[1].find((k) => k !== a && k !== b); + return dot(n, sub(V[far], V[a])) > 1e-6; + }); + } + const P = (p) => { + const q = cam(p, S.yaw, el); + return [F.cx + F.K * q[0], F.cy - F.K * q[1], q[2]]; + }; + const L = [-0.8, 0.35, 0.5], Ln = 1 / len(L), hue = S.hue; + const glass = [hsv(hue + 0.58, 0.7, 1), hsv(hue + 0.76, 0.6, 1), hsv(hue + 0.42, 0.7, 0.95)]; + const gold = hsv(hue + 0.12, 0.65, 1), green = hsv(hue + 0.33, 0.75, 1), pink = hsv(hue + 0.92, 0.6, 1), pale = hsv(hue + 0.6, 0.18, 1); + + // Solids: the prism, or at th = 0 with spread > 0 its three pieces. + const solids = []; + if (spread > 0.001) { + const g = mid(V); + CUT.forEach((q, n) => { + const pc = q.map((k) => V[k]), m = sub(mid(pc), g), off = [m[0], 0, m[2]], o = 0.3 * spread / (len(off) || 1); + const pts = pc.map((p) => add3(p, off, o)), gc = mid(pts), tris = []; + for (let i = 0; i < 4; i++) { + const t = [0, 1, 2, 3].filter((k) => k !== i); + const nn = cross(sub(pts[t[1]], pts[t[0]]), sub(pts[t[2]], pts[t[0]])); + tris.push(dot(nn, sub(pts[t[0]], gc)) < 0 ? [t[0], t[2], t[1]] : t); + } + solids.push({ pts, tris, col: glass[n], edges: [[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]] }); + }); + } else solids.push({ pts: V, tris: FACES, col: glass[0], edges: null }); + + // Faces into each LED's list. + const faceCol = []; + for (const so of solids) { + const sp = so.pts.map(P); + for (const f of so.tris) { + const a = sp[f[0]], b = sp[f[1]], c = sp[f[2]]; + const nw = cross(sub(so.pts[f[1]], so.pts[f[0]]), sub(so.pts[f[2]], so.pts[f[0]])); + const n = rot(nw, S.yaw, el), nl = len(n) || 1; + const front = dot(n, sub([0, 0, DC], rot(so.pts[f[0]], S.yaw, el))) > 0; + const lam = max(0, (dot(n, L) * Ln) / nl), sh = front ? 0.27 + 0.73 * lam : 0.12 + 0.3 * lam; + const id = faceCol.length; + faceCol.push([so.col[0] * sh, so.col[1] * sh, so.col[2] * sh, front ? 0.72 : 0.35]); + const area = (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]); + if (abs(area) < 1e-6) continue; + const bx0 = max(0, floor(min(a[0], b[0], c[0]))), bx1 = min(W - 1, ceil(max(a[0], b[0], c[0]))); + const by0 = max(0, floor(min(a[1], b[1], c[1]))), by1 = min(FOOT - 1, ceil(max(a[1], b[1], c[1]))); + for (let y = by0; y <= by1; y++) + for (let x = bx0; x <= bx1; x++) { + const px = x + 0.5, py = y + 0.5; + const w0 = ((b[0] - px) * (c[1] - py) - (b[1] - py) * (c[0] - px)) / area; + const w1 = ((c[0] - px) * (a[1] - py) - (c[1] - py) * (a[0] - px)) / area; + const w2 = 1 - w0 - w1; + if (w0 < -1e-4 || w1 < -1e-4 || w2 < -1e-4) continue; + const i = y * W + x, n = cnt[i]; + if (n >= 12) continue; + zl[i * 12 + n] = w0 * a[2] + w1 * b[2] + w2 * c[2]; + fl[i * 12 + n] = front ? 1 : 0; + fid[i * 12 + n] = id; + cnt[i] = n + 1; + } + } + } + // Composite each LED's faces back to front. + for (let i = 0; i < W * H; i++) { + const n = cnt[i]; + if (!n) continue; + const o = i * 12, idx = []; + for (let k = 0; k < n; k++) idx.push(k); + idx.sort((p, q) => zl[o + p] - zl[o + q]); + let r = 0, g = 0, b = 0; + for (const k of idx) { + const c = faceCol[fid[o + k]]; + r += (c[0] - r) * c[3]; g += (c[1] - g) * c[3]; b += (c[2] - b) * c[3]; + } + buf[i * 3] = r; buf[i * 3 + 1] = g; buf[i * 3 + 2] = b; + } + + // Lines, far ones first. + const lines = []; + for (const so of solids) { + const sp = so.pts.map(P); + if (so.edges) for (const [a, b] of so.edges) lines.push([sp[a], sp[b], 1.2, hsv(hue + 0.6, 0.12, 1), 0.85, 0]); + else { + EDGES.forEach(([a, b, kind], e) => { + const fold = kind === 2 && S.reflex[e]; + lines.push([sp[a], sp[b], fold ? 1.8 : 1.3, fold ? gold : pale, fold ? 1 : 0.85, 0]); + }); + // Sections at seven heights: post i, fold i, post i + 1, and round. + for (let h = 1; h < 8; h++) { + const t = h / 8, ring = []; + for (let i = 0; i < 3; i++) { + const j = (i + 1) % 3; + ring.push(P(add3(V[i], sub(V[3 + i], V[i]), t)), P(add3(V[i], sub(V[3 + j], V[i]), t))); + } + for (let k = 0; k < 6; k++) lines.push([ring[k], ring[(k + 1) % 6], 1, pale, 0.42, 0]); + } + DIAG.forEach(([a, b], n) => { + const st = S.diag[n]; + for (let t = 0; t < 12; t++) { + const ins = st[t] && st[t + 1]; + const p0 = P(add3(V[a], sub(V[b], V[a]), t / 12)), p1 = P(add3(V[a], sub(V[b], V[a]), (t + 1) / 12)); + lines.push([p0, p1, 1.8, ins ? green : pink, 1, ins ? 0 : 4]); + } + }); + } + } + lines.sort((p, q) => p[0][2] + p[1][2] - q[0][2] - q[1][2]); + for (const l of lines) seg(S, l[0], l[1], l[2], l[3], l[4], l[5]); + for (const so of solids) + for (const p of so.pts.map(P)) { + const x = floor(p[0]), y = floor(p[1]); + for (let yy = y - 1; yy <= y + 1; yy++) + for (let xx = x - 1; xx <= x + 1; xx++) { + if (xx < 0 || xx >= W || yy < 0 || yy >= FOOT) continue; + const cov = cl(1.9 - hypot(xx + 0.5 - p[0], yy + 0.5 - p[1]), 0, 1); + if (cov > 0) put(buf, xx, yy, [1, 1, 1], cov * seen(S, xx, yy, p[2])); + } + } + + // The foot: how many of the 15 sets fit, and a cell for each. + const cen = S.cen, inN = cen.filter((v) => v > 0).length, s = String(inN); + const ink = inN ? green : pink; + const glyph = (ch, x0, y0, sc, c, a) => { + const g = DIG[ch]; + for (let r = 0; r < 5; r++) + for (let q = 0; q < 3; q++) + if (g[r] & (4 >> q)) + for (let j = 0; j < sc; j++) for (let i = 0; i < sc; i++) put(buf, x0 + q * sc + i, y0 + r * sc + j, c, a); + }; + for (let i = 0; i < s.length; i++) glyph(s[i], 2 + (2 - s.length) * 7 + i * 7, FOOT + 3, 2, ink, 1); + glyph("/", 17, FOOT + 8, 1, pale, 0.5); + glyph("1", 21, FOOT + 8, 1, pale, 0.5); + glyph("5", 25, FOOT + 8, 1, pale, 0.5); + cen.forEach((v, n) => { + const x0 = 31 + (n % 8) * 4, y0 = FOOT + 3 + floor(n / 8) * 5; + for (let y = 0; y < 3; y++) + for (let x = 0; x < 3; x++) { + if (v === 0 && y !== 1) continue; + put(buf, x0 + x, y0 + y, v > 0 ? green : v < 0 ? pink : pale, v > 0 ? 0.95 : v < 0 ? (x === 1 && y === 1 ? 0 : 0.45) : 0.35); + } + }); + + for (let i = 0; i < W * H; i++) { + const p = i * 3; + display.setPixel(i % W, floor(i / W), round(cl(buf[p], 0, 1) * 255), round(cl(buf[p + 1], 0, 1) * 255), round(cl(buf[p + 2], 0, 1) * 255)); + } +} From d4a16540d63881a3a5a5bfae0576ad1a8d5026d7 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 08:40:16 +0000 Subject: [PATCH 5/9] =?UTF-8?q?=ED=8C=A8=ED=84=B4=20=EC=97=B0=EA=B5=AC=201?= =?UTF-8?q?02=ED=8C=90:=20Takagi=20Curve=20=EC=99=80=20Gerbert's=20Abacus?= =?UTF-8?q?=20=EB=A5=BC=20=EB=8D=94=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Bc4JkshDz129z5fXhUWQxz --- .../pattern-study/patterns/gerbert-abacus.js | 409 ++++++++++++++++++ .../pattern-study/patterns/takagi-curve.js | 269 ++++++++++++ 2 files changed, 678 insertions(+) create mode 100644 incoming/pattern-study/patterns/gerbert-abacus.js create mode 100644 incoming/pattern-study/patterns/takagi-curve.js diff --git a/incoming/pattern-study/patterns/gerbert-abacus.js b/incoming/pattern-study/patterns/gerbert-abacus.js new file mode 100644 index 00000000..633da613 --- /dev/null +++ b/incoming/pattern-study/patterns/gerbert-abacus.js @@ -0,0 +1,409 @@ +// ===== Patternflow pattern ===== +// Title: Gerbert's Abacus +// Family: type +// Idea: Numbers are added on Gerbert's column abacus the way its counters work, one apex marked 1 to 9 in a column and an empty column for zero: each addend is written at the foot of a ledger that runs down the panel, its apices fly up to the board under the ones already there, and column by column from the units the two apices slide together and are replaced by the single apex of their sum, a sum of ten or more sending an apex 1 down its column and along to the next one on the left to join the work there, until the new total flies back down and is written in the ledger under a rule. +// Author: patternflow-lab +// Date: 2026-11-03 +// SPDX-License-Identifier: CC-BY-SA-4.0 +// Lineage: the column abacus with marked counters (apices) revived by Gerbert d'Aurillac, later Pope Sylvester II, at Reims about 980, described by his pupil Richer of Reims and in Bernelinus' Liber abaci; the apices' numeral forms related to the West Arabic ghubar numerals; apices and modern digits 5 x 8 LEDs drawn for this pattern; pattern round 102 +// Round: 102 +// =============================== +// @matrix 64x128 +// @knobs Hue=0..1, Speed=0.2..3, Columns=4..7, Glyph=0..1 +// +// Knob 1: Hue. Turns the counters round the wheel together. At 0 the total's apices are aqua, the addend's pink and a carried apex gold, with a white flash where two apices become one; the ledger writes the addends in pink and the totals in aqua, each line dimmer the further back it is. +// Knob 2: Speed. At 2 an addend is written at 0.07 s a figure and flies up to the board in 0.8 s; a column is worked in 0.6 s, or 1.05 s when it carries and the 1 drops to the foot of the column and slides left; the total is back in the ledger 1.2 s after the last column, 1.6 s when the ledger has to scroll. The opening sum, 2975 + 1038, carries three times and takes 7.9 s, and a sum takes 6.7 s on average. Every time goes as 2 / Speed. +// Knob 3: Columns. How many columns the board has, 4 to 7, headed I, X, C, M and then X, C and M with a bar for the thousands, under arcs of three as Gerbert's board grouped them. With 4 or 5 the apices are 10 x 16 LEDs on the board and the ledger shows 5 lines; with 6 or 7 they are 5 x 8 and it shows 7. Addends have from 2 figures to one fewer than the board; when a total would not fit, the board is swept and a fresh number starts it under a double rule. +// Knob 4: Glyph. Below 1/3 board and ledger both use the apices, and an empty place is a blank column. From 1/3 the ledger writes the same numbers in modern digits, so the zeros the board leaves empty appear there. From 2/3 the board's figures are modern digits too, written without counters, with a 0 in each empty place and the column rules faded, since a written zero holds the place the column held. + +// Gerbert's board, as Richer describes it, was ruled into columns headed by +// the powers of ten, grouped in threes under arcs. A number was set by laying +// in each column one counter, an apex, marked with the figure of that place, +// 1 to 9, and a place worth nothing was left empty. The nine figures had the +// names igin, andras, ormis, arbas, quimas, caltis, zenis, temenias and +// celentis. On a board of plain pebbles, 7 and 8 in one column are fifteen +// pebbles to be regrouped. The apex board asks the reckoner to know +// 7 + 8 = 15 by heart: the two counters are taken off, the 5 counter is laid +// in their place and a 1 counter is carried to the next column. +// +// The shapes below are drawn from memory and are approximations: a plain +// stroke for 1, a footless hook for 2, a flat-topped 3, a looped half-eight +// for 4, a 5 like a d with its stem bent over, a 6 like a b, an inverted V +// for 7, an 8 open at the top and a 9 with a curled tail. The modern digits +// beside them are an ordinary pixel face with a slashed 0. +// +// Each sum is laid out as soon as it starts. Every counter is a sprite with +// keyframes (time, position, scale, on the board or in the ledger) and times +// to show and to go, and the ledger is a list of lines with a time for each +// figure to be written, so a frame only evaluates them at the sum's clock. + +const W = 64, H = 128; +const { floor, round, min, max, exp } = Math; +const cl = (v, a, b) => (v < a ? a : v > b ? b : v); +const ramp = (t, a, b) => (t <= a ? 0 : t >= b ? 1 : (t - a) / (b - a)); +const ease = (u) => u * u * (3 - 2 * u); + +const APEX = [ + null, + [".##..", "..#..", "..#..", "..#..", "..#..", "..#..", "..#..", "..##."], + [".##..", "#..#.", "...#.", "...#.", "..#..", ".#...", "#....", "#...."], + ["#####", "...#.", "..#..", ".###.", "....#", "....#", "#...#", ".###."], + [".###.", "#...#", "#...#", "#...#", "#...#", ".#.#.", ".#.#.", "##.##"], + ["....#", "...#.", ".###.", "#...#", "#...#", "#...#", "#...#", ".###."], + ["##...", "#....", "#....", "####.", "#...#", "#...#", "#...#", "####."], + ["..#..", "..#..", ".#.#.", ".#.#.", ".#.#.", "#...#", "#...#", "#...#"], + ["#...#", ".#.#.", "..#..", ".#.#.", "#...#", "#...#", "#...#", ".###."], + [".###.", "#...#", "#...#", ".####", "....#", "....#", "..#.#", "...#."], +]; +const MODERN = [ + [".###.", "#...#", "#..##", "#.#.#", "##..#", "#...#", "#...#", ".###."], + ["..#..", ".##..", "..#..", "..#..", "..#..", "..#..", "..#..", ".###."], + [".###.", "#...#", "....#", "...#.", "..#..", ".#...", "#....", "#####"], + [".###.", "#...#", "....#", "..##.", "....#", "....#", "#...#", ".###."], + ["...#.", "..##.", ".#.#.", "#..#.", "#####", "...#.", "...#.", "...#."], + ["#####", "#....", "#....", "####.", "....#", "....#", "#...#", ".###."], + ["..##.", ".#...", "#....", "####.", "#...#", "#...#", "#...#", ".###."], + ["#####", "....#", "...#.", "...#.", "..#..", "..#..", ".#...", ".#..."], + [".###.", "#...#", "#...#", ".###.", "#...#", "#...#", "#...#", ".###."], + [".###.", "#...#", "#...#", ".####", "....#", "....#", "...#.", ".##.."], +]; +// Column heads: I, X, C, M, 3 wide but M, 5 tall. +const HEAD = [ + [".#.", ".#.", ".#.", ".#.", ".#."], + ["#.#", "#.#", ".#.", "#.#", "#.#"], + [".##", "#..", "#..", "#..", ".##"], + ["#...#", "##.##", "#.#.#", "#...#", "#...#"], +]; +const bits = (rows) => { + const px = []; + rows.forEach((row, y) => [...row].forEach((c, x) => c === "#" && px.push(x, y))); + return px; +}; +const GA = APEX.map((r) => (r ? bits(r) : [])), GM = MODERN.map(bits), GH = HEAD.map(bits); + +// Ledger line pitch, and the three kinds of counter. +const LP = 11, TOTAL = 0, ADDEND = 1, CARRY = 2; + +function hsv(h, s, v) { + h = (h - floor(h)) * 6; + const i = floor(h) % 6, f = h - floor(h), p = v * (1 - s), q = v * (1 - s * f), t = v * (1 - s * (1 - f)); + return [[v, t, p], [q, v, p], [p, v, t], [p, q, v], [t, p, v], [v, p, q]][i]; +} + +function layout(C) { + const cw = C <= 4 ? 16 : C === 5 ? 12 : C === 6 ? 10 : 9; + const sc = cw >= 12 ? 2 : 1; + const x0 = floor((W - C * cw) / 2); + const slot = sc === 2 ? [12, 33, 54] : [12, 23, 34]; + const bottom = sc === 2 ? 73 : 44; + const ty0 = bottom + 3; + return { C, cw, sc, x0, slot, bottom, ty0, lines: floor((H - ty0 + 3) / LP), cx: (p) => x0 + (C - 1 - p) * cw + cw / 2 }; +} + +const digitsOf = (n, C) => { + const d = []; + for (let p = 0; p < C; p++, n = floor(n / 10)) d.push(n % 10); + return d; +}; + +// One sum on the board: total a, addend b; clear sweeps the board first. +function build(S, a, b, clear) { + const Ly = S.lay, C = Ly.C, sc = Ly.sc, [UY, AY, CY] = Ly.slot; + const sp = [], scroll = [], lines = [], empty = []; + const add = (o) => { + const s = Object.assign({ a0: -1, a1: -1, d0: 1e9, d1: 1e9, f: -1 }, o); + sp.push(s); + return s; + }; + const board = (p, y) => [Ly.cx(p), y, sc, 1]; + let base = S.scrollBase; + const yLine = (j) => Ly.ty0 + (j - base) * LP; + // A new line at the foot of the ledger, scrolling it up first if it is full. + let t = 0; + const newLine = (o) => { + const j = S.next++; + if (j - base >= Ly.lines) { + scroll.push(t); + base++; + t += 0.35; + } + const line = Object.assign({ j, ap: [] }, o); + lines.push(line); + return line; + }; + const dA = digitsOf(a, C), dB = digitsOf(b, C); + // A 0 inside a number is a counter too, drawn only in the modern face. + const hiOf = (d) => d.reduce((m, v, p) => (v ? p : m), 0); + const U = []; + for (let p = 0; p <= hiOf(dA); p++) if (a) U[p] = add({ g: dA[p], kind: TOTAL, keys: [[0, ...board(p, UY)]] }); + if (clear) { + for (const s of U) if (s) { s.d0 = 0; s.d1 = 0.6; } + U.length = 0; + dA.fill(0); + a = 0; + t = 0.7; + } + // The addend is written on the ledger, units first. + const la = newLine({ n: b, kind: ADDEND, rule: clear ? 2 : 0 }); + const top = hiOf(dB); + for (let p = 0; p <= top; p++) la.ap[p] = t + 0.07 * p; + t += 0.07 * top + 0.3; + // It flies up to the board, a copy from each figure. + const A = []; + let k = 0; + for (let p = 0; p <= top; p++) { + const m0 = t + 0.06 * k++; + A[p] = add({ g: dB[p], kind: ADDEND, keys: [[m0, Ly.cx(p), yLine(la.j), 1, 0], [m0 + 0.8, ...board(p, AY)]], a0: m0, a1: m0 }); + } + t += 0.8 + 0.06 * (k - 1) + 0.2; + // The columns, from the units: the addend's apex and any carried apex + // slide into the total's slot and one apex of the sum replaces them. + let carry = 0, cs = null; + for (let p = 0; p < C; p++) { + if (p > top && !carry) break; + if (!dB[p] && !carry) { + const z = A[p]; + if (z) { + z.keys.push([t, ...z.keys[z.keys.length - 1].slice(1)], [t + 0.25, ...board(p, UY)]); + z.d0 = t + 0.2; + z.d1 = t + 0.3; + } + t += z ? 0.3 : 0.1; + continue; + } + const r = dA[p] + dB[p] + carry, tm = t + 0.35; + for (const s of [A[p], cs]) { + if (!s) continue; + s.keys.push([t, ...s.keys[s.keys.length - 1].slice(1)], [tm, ...board(p, UY)]); + s.d0 = tm - 0.05; + s.d1 = tm + 0.05; + } + if (U[p]) { U[p].d0 = tm - 0.05; U[p].d1 = tm + 0.05; } + U[p] = add({ g: r % 10, kind: TOTAL, keys: [[tm, ...board(p, UY)]], a0: tm - 0.05, a1: tm + 0.05, f: tm }); + if (!(r % 10)) empty.push([p, tm]); + carry = r >= 10 ? 1 : 0; + if (carry && p + 1 < C) { + // The carried 1 drops to the foot of its column, clear of the + // addend's apices, and slides left under the next one. + cs = add({ g: 1, kind: CARRY, keys: [[tm + 0.05, ...board(p, UY)], [tm + 0.32, ...board(p, CY)], [tm + 0.6, ...board(p + 1, CY)]], a0: tm, a1: tm + 0.08, f: tm }); + t = tm + 0.7; + } else { + cs = null; + t = tm + 0.25; + } + } + const total = a + b; + // The total flies back down and is written under a rule. + t += 0.25; + const ls = newLine({ n: total, kind: TOTAL, rule: 1 }); + k = 0; + for (let p = C - 1; p >= 0; p--) { + if (!U[p]) continue; + const m0 = t + 0.04 * k++; + add({ g: U[p].g, kind: TOTAL, keys: [[m0, ...board(p, UY)], [m0 + 0.8, Ly.cx(p), yLine(ls.j), 1, 0]], a0: m0, a1: m0, d0: m0 + 0.8, d1: m0 + 0.8 }); + ls.ap[p] = m0 + 0.8; + } + const done = t + 0.8 + 0.04 * k; + for (let p = 0; p < C; p++) if (ls.ap[p] == null) ls.ap[p] = done; + return { sp, scroll, lines, empty, total, end: done + 0.9 }; +} + +function nextAddend(S) { + const C = S.lay.C, nd = 2 + floor(Math.random() * (C - 2)); + return floor(10 ** (nd - 1) + Math.random() * 9 * 10 ** (nd - 1)); +} + +function begin(S, a, b) { + let clear = false; + if (a + b >= 10 ** S.lay.C) { + clear = true; + b = floor(10 ** (S.lay.C - 2) * (1 + Math.random() * 4)); + } + S.sum = build(S, a, b, clear); + S.ledger.push(...S.sum.lines); + S.t = 0; +} + +function restart(S, C) { + S.lay = layout(C); + S.ledger = []; + S.next = 0; + S.scrollBase = 0; + // The ledger as if the sums before had been done: 980 under a double rule, + // then + 1995 = 2975, and the board opens on 2975 + 1038. + const old = [[980, ADDEND, 2], [980, TOTAL, 1], [1995, ADDEND, 0], [2975, TOTAL, 1]]; + for (const [n, kind, rule] of old) S.ledger.push({ j: S.next++, n, kind, rule, ap: [] }); + begin(S, 2975, 1038); + S.t = 0.35; +} + +export function setup(params) { + params.s = { t: 0, hue: 0, speed: 2, glyph: 0.06, C: 0, buf: new Float32Array(W * H * 3) }; +} + +export function update(dt, input, params) { + if (!params.s) setup(params); + const S = params.s, k = input.knobValues || [0, 2, 4, 0.06], d = cl(dt || 0, 0, 0.1); + S.hue = k[0] || 0; + S.speed = cl(k[1] == null ? 2 : k[1], 0.2, 3); + S.glyph = k[3] == null ? 0.06 : k[3]; + const C = round(cl(k[2] == null ? 4 : k[2], 4, 7)); + if (C !== S.C) { + S.C = C; + restart(S, C); + } + S.t += (d * S.speed) / 2; + if (S.t >= S.sum.end) { + const over = S.t - S.sum.end; + S.scrollBase += S.sum.scroll.length; + for (const l of S.ledger) l.ap = []; + const keep = S.lay.lines + 1; + S.ledger = S.ledger.filter((l) => l.j >= S.next - keep); + begin(S, S.sum.total, nextAddend(S)); + S.t = over; + } +} + +// Keyframes, eased between neighbours. The last entry is 1 on the board and +// 0 in the ledger, and the face switches where it passes 0.5. +function at(keys, t) { + if (t <= keys[0][0]) return [t, keys[0][1], keys[0][2], keys[0][3], keys[0][4]]; + for (let i = 1; i < keys.length; i++) { + const b = keys[i]; + if (t < b[0]) { + const a = keys[i - 1], u = ease(ramp(t, a[0], b[0])); + return [t, a[1] + (b[1] - a[1]) * u, a[2] + (b[2] - a[2]) * u, a[3] + (b[3] - a[3]) * u, a[4] + (b[4] - a[4]) * u]; + } + } + const l = keys[keys.length - 1]; + return [t, l[1], l[2], l[3], l[4]]; +} + +export function draw(display, params) { + const S = params.s; + if (!S || !S.sum) return; + const buf = S.buf, Ly = S.lay, C = Ly.C, t = S.t, sum = S.sum; + buf.fill(0); + const plot = (x, y, r, g, b) => { + if (x < 0 || x >= W || y < 0 || y >= H) return; + const i = (y * W + x) * 3; + buf[i] += r; buf[i + 1] += g; buf[i + 2] += b; + }; + // A rectangle with fractional edges, each LED lit by how much it covers. + const rect = (x, y, w, h, r, g, b, y0) => { + for (let py = floor(y); py < y + h; py++) { + if (py < y0) continue; + const cy = min(y + h, py + 1) - max(y, py); + if (cy <= 0) continue; + for (let px = floor(x); px < x + w; px++) { + const c = (min(x + w, px + 1) - max(x, px)) * cy; + if (c > 0) plot(px, py, r * c, g * c, b * c); + } + } + }; + const cols = [hsv(S.hue + 0.5, 0.75, 1), hsv(S.hue + 0.9, 0.58, 1), hsv(S.hue + 0.14, 0.72, 1)]; + const grey = hsv(S.hue + 0.55, 0.15, 1); + const ledgerFace = S.glyph >= 1 / 3 ? 1 : 0, boardFace = S.glyph >= 2 / 3 ? 1 : 0; + const glyphOf = (g, face) => (face ? GM[g] : GA[g]); + + // The heads: arcs over each three columns from the units, then I X C M, + // with a bar over the thousands. + for (let gI = 0; gI * 3 < C; gI++) { + const pr = gI * 3, pl = min(C - 1, pr + 2); + const xl = Ly.x0 + (C - 1 - pl) * Ly.cw + 1, xr = Ly.x0 + (C - pr) * Ly.cw - 2; + const mid = (xl + xr) / 2, half = (xr - xl) / 2; + for (let x = xl; x <= xr; x++) { + const u = (x - mid) / half, y = 1.5 * u * u; + const iy = floor(y), fy = y - iy, v = 95; + plot(x, iy, grey[0] * v * (1 - fy), grey[1] * v * (1 - fy), grey[2] * v * (1 - fy)); + plot(x, iy + 1, grey[0] * v * fy, grey[1] * v * fy, grey[2] * v * fy); + } + } + for (let p = 0; p < C; p++) { + const px = GH[p === 3 || p === 6 ? 3 : p % 3]; + const wd = p === 3 || p === 6 ? 5 : 3, xl = round(Ly.cx(p) - wd / 2), v = 120; + for (let i = 0; i < px.length; i += 2) plot(xl + px[i], 4 + px[i + 1], grey[0] * v, grey[1] * v, grey[2] * v); + if (p >= 4) for (let x = -1; x <= wd; x++) plot(xl + x, 3, grey[0] * v, grey[1] * v, grey[2] * v); + } + // Column rules down the board and on down the ledger, and the board's foot. + for (let i = 1; i < C; i++) { + const x = Ly.x0 + i * Ly.cw; + for (let y = 10; y < H; y++) { + const v = (y <= Ly.bottom ? 30 : 20) * (boardFace ? 0.4 : 1); + plot(x, y, grey[0] * v, grey[1] * v, grey[2] * v); + } + } + for (let x = Ly.x0; x < Ly.x0 + C * Ly.cw; x++) plot(x, Ly.bottom, grey[0] * 45, grey[1] * 45, grey[2] * 45); + + // The ledger. + let scroll = S.scrollBase; + for (const e of sum.scroll) scroll += ease(ramp(t, e, e + 0.35)); + const newest = S.next - 1; + for (const line of S.ledger) { + const y = Ly.ty0 + (line.j - scroll) * LP; + if (y > H || y < Ly.ty0 - LP) continue; + const fadeTop = cl((y - Ly.ty0 + 6) / 6, 0, 1); + const age = max(0, newest - line.j - (line.kind === TOTAL ? 0 : 1)); + const lv = max(0.42, 1 - 0.1 * age) * fadeTop * (line.kind === TOTAL ? 1 : 0.8); + const c = cols[line.kind]; + const ds = digitsOf(line.n, C); + let hiP = 0; + ds.forEach((dd, p) => dd && (hiP = p)); + const shown = line.ap.length ? line.ap.reduce((m, a) => (a <= t ? m + 1 : m), 0) : C; + if (line.rule && shown > 0) { + const ra = line.ap.length ? cl((t - min(...line.ap.filter((v) => v != null))) / 0.3, 0, 1) : 1; + const rv = 55 * ra * fadeTop; + for (const ry of line.rule === 2 ? [y - 3, y - 1] : [y - 2]) + if (ry > Ly.bottom) for (let x = Ly.x0 + 1; x < Ly.x0 + C * Ly.cw - 1; x++) plot(x, ry, grey[0] * rv, grey[1] * rv, grey[2] * rv); + } + for (let p = 0; p <= hiP; p++) { + const ap = line.ap.length ? line.ap[p] : -1; + if (ap == null || ap > t) continue; + const dd = ds[p]; + if (!dd && !ledgerFace) continue; + const fresh = ap >= 0 ? exp(-(t - ap) / 0.25) : 0; + const px = glyphOf(dd, ledgerFace), xl = Ly.cx(p) - 2.5; + const r = 255 * (c[0] * lv + fresh * 0.6), g = 255 * (c[1] * lv + fresh * 0.6), b = 255 * (c[2] * lv + fresh * 0.6); + for (let i = 0; i < px.length; i += 2) rect(xl + px[i], y + px[i + 1], 1, 1, r, g, b, Ly.bottom + 1); + } + } + + // The counters. + for (const s of sum.sp) { + const al = (s.a0 < 0 ? 1 : ramp(t, s.a0, s.a1 + 1e-6)) * (1 - ramp(t, s.d0, s.d1 + 1e-6)); + if (al <= 0) continue; + const [, x, y, sc, onBoard] = at(s.keys, t); + const face = onBoard >= 0.5 ? boardFace : ledgerFace; + if (!s.g && !face) continue; + const c = cols[s.kind]; + const fl = s.f >= 0 && t >= s.f ? 1.1 * exp(-(t - s.f) / 0.18) : 0; + // The counter itself, a dim plate behind its figure. + if (onBoard > 0 && !boardFace) { + const pw = min(Ly.cw - 1, 7 * sc), ph = 10 * sc, pv = 0.13 * onBoard * al * 255; + const xl = x - pw / 2, yt = y - sc; + rect(xl + 1, yt, pw - 2, ph, c[0] * pv, c[1] * pv, c[2] * pv, 0); + rect(xl, yt + 1, 1, ph - 2, c[0] * pv, c[1] * pv, c[2] * pv, 0); + rect(xl + pw - 1, yt + 1, 1, ph - 2, c[0] * pv, c[1] * pv, c[2] * pv, 0); + } + const px = glyphOf(s.g, face); + const r = 255 * (c[0] + fl) * al, g = 255 * (c[1] + fl) * al, b = 255 * (c[2] + fl) * al; + const xl = x - 2.5 * sc; + for (let i = 0; i < px.length; i += 2) rect(xl + px[i] * sc, y + px[i + 1] * sc, sc, sc, r, g, b, 0); + } + // A column whose sum is exactly ten is left empty: the outline of its + // counter shows for a moment where the apex went. + for (const [p, te] of sum.empty) { + if (t < te || boardFace) continue; + const v = 150 * exp(-(t - te) / 0.3); + if (v < 3) continue; + const sc = Ly.sc, pw = min(Ly.cw - 1, 7 * sc), ph = 10 * sc, xl = round(Ly.cx(p) - pw / 2), yt = Ly.slot[0] - sc; + for (let x = xl + 1; x < xl + pw - 1; x++) { plot(x, yt, grey[0] * v, grey[1] * v, grey[2] * v); plot(x, yt + ph - 1, grey[0] * v, grey[1] * v, grey[2] * v); } + for (let y = yt + 1; y < yt + ph - 1; y++) { plot(xl, y, grey[0] * v, grey[1] * v, grey[2] * v); plot(xl + pw - 1, y, grey[0] * v, grey[1] * v, grey[2] * v); } + } + + for (let y = 0; y < H; y++) + for (let x = 0; x < W; x++) { + const i = (y * W + x) * 3; + display.setPixel(x, y, min(255, round(buf[i])), min(255, round(buf[i + 1])), min(255, round(buf[i + 2]))); + } +} diff --git a/incoming/pattern-study/patterns/takagi-curve.js b/incoming/pattern-study/patterns/takagi-curve.js new file mode 100644 index 00000000..be885957 --- /dev/null +++ b/incoming/pattern-study/patterns/takagi-curve.js @@ -0,0 +1,269 @@ +// ===== Patternflow pattern ===== +// Title: Takagi Curve +// Family: fractal +// Idea: Takagi's continuous nowhere-differentiable function is built down the standing panel one level at a time, x running down the 128 rows and the value across the columns, each level a sawtooth of 2^n tents in its own colour stacked on the sum so far, so the single tent of level 0 rises into the blancmange as the levels land and roughens at its edge until the curve touches its maximum 2/3 in four small clusters, and the weight knob turns the same stack into a smooth parabola at w = 1/4 or a rough band toward w = 1. +// Author: patternflow-lab +// Date: 2026-11-03 +// SPDX-License-Identifier: CC-BY-SA-4.0 +// Lineage: the Takagi function, sum of s(2^n x) / 2^n with s the distance to the nearest integer (Teiji Takagi, 1903), the Takagi-Landsberg family sum of w^n s(2^n x) (Georg Landsberg, 1908), the set where the maximum 2/3 is reached (Jean-Pierre Kahane, 1959); pattern round 102 +// Round: 102 +// =============================== +// @matrix 64x128 +// @knobs Hue=0..1, Speed=0.2..3, Weight=-1..1, Zoom=0..1 +// +// Knob 1: Hue. Turns the level colours round the wheel. At 0 level 0 is azure and each finer level sits 0.065 of a turn further on and paler, through blue and violet to a pale pink at level 7, the odd levels paler still so neighbouring bands part. At 0.5 level 0 is red and at 0.75 green; between 0.569 and 0.57 it steps over orange and yellow, which read brown and olive in its 40 percent fill. +// Knob 2: Speed. At 2 a level rises in 0.6 s and the next starts 0.3 s after it, so the eight levels of the blancmange are built in 7.2 s. The finished curve is held 1.8 s with the rows where it reaches 2/3 marked, peeled back finest level first in 1.4 s, and after 0.3 s of black the build starts again: 10.7 s a cycle, 107 s at 0.2 and 7.2 s at 3. +// Knob 3: Weight. The weight w in the sum of w^n s(2^n x), the value scaled so the finished curve's maximum sits at column 60. From -0.08 to 0.08, and so at the opening 0, w = 1/2 exactly, Takagi's own curve, the blancmange, with its maximum 2/3 dotted at column 60. Down to -1 w falls to 1/4, where four levels stack into the parabola 2x(1 - x) and a dotted line traces that closed form. Up to 1 w rises to 0.9: twelve levels are built one by one, all the finer ones land together as a last band in soft magenta, and the curve's edge wanders between columns 34 and 59 from row to row. +// Knob 4: Zoom. Up to 0.1 the panel shows the whole of 0 <= x <= 1. From 0.1 to 1 it zooms into the top of the panel by up to 16 times in x, the coarsest band thinning away as it goes, and the value is scaled to keep the maximum at column 60. At 0.325, 0.55, 0.775 and 1 the zoom is 2, 4, 8 and 16 and the picture lands on itself, the same stack one, two, three or four colours further on. + +// Takagi (1903) wrote T(x) = sum over n >= 0 of s(2^n x) / 2^n, with s(u) +// the distance from u to the nearest integer, and showed it continuous and +// differentiable nowhere. Each term is a sawtooth of 2^n tents of height +// 2^-n / 2. Landsberg (1908) put a weight w in place of 1/2: +// +// T_w(x) = sum over n >= 0 of w^n s(2^n x). +// +// At w = 1/4 the sum is the parabola 2x(1 - x). Split at n = 0: for x in +// [0, 1/2], s(x) = x and the rest is T(2x) / 4, and 2x(1 - x) satisfies +// x + 2(2x)(1 - 2x) / 4 = 2x(1 - x); the same works on [1/2, 1], and a +// bounded solution of that equation is unique. Forty levels against the +// closed form at 100,000 random x differ by 3.3e-16 at most, and setup() +// below checks it again on 1,024 points before the dotted parabola is drawn. +// +// At w = 1/2 the largest value is 2/3, taken exactly where the binary +// digits of x come in pairs 01 or 10, a Cantor set of dimension 1/2 (Kahane, +// 1959). Members drawn at random sum to 2/3 within 1e-15, and points with a +// pair 00 or 11 among the first eight stay below it. x = 1/3, 2/5, 3/5 and +// 2/3 are in the set, and at 1,024 samples down the panel the curve comes +// within half a column of column 60 on rows 42-45, 50-53, 74-77 and 82-85. +// +// The sum is self-affine: T_w(x) = s(x) + w T_w(2x). Take away level 0 and +// what is left on [0, 1/2] is the whole curve again, half as long and w +// times as wide. The zoom works that way. At zoom z, panel row X shows +// x = X / 2^z, every level below floor(z) is taken away, level floor(z) is +// scaled by 1 - frac(z), and the value is scaled so its maximum sits at +// column 60. At a whole z the picture is exactly the unzoomed one, each +// band in the colour of the level z above it. +// +// A row is 8 samples whose positions carry an irrational offset, so 2^n x +// stays off the dyadic grid for some 50 doublings. Each sample stacks the +// levels' widths from column 0 and adds each band's colour over the columns +// it covers, dim inside and full in its last 1.2 columns, so the top of +// every partial sum is a bright line and the outline of the whole is the +// curve. + +const W = 64, H = 128; +const { floor, round, min, max, abs, exp, pow, log, ceil } = Math; +const cl = (v, a, b) => (v < a ? a : v > b ? b : v); +const ease = (u) => (u <= 0 ? 0 : u >= 1 ? 1 : u * u * (3 - 2 * u)); +const s = (u) => abs(u - round(u)); + +const SS = 8, FIT = 60, EDGE = 1.2, INNER = 0.4, MAXB = 12, NMAX = 64; +// Story seconds at Speed 1 for a level to rise and to rest before the next, +// to hold the finished curve, to peel one level, and the blank before a new build. +const RISE = 1.2, REST = 0.6, HOLD = 3.6, PEEL = 0.36, GAP = 0.6; +const STEP = RISE + REST; +const OFF = new Float64Array(SS); +for (let j = 0; j < SS; j++) OFF[j] = (j + 0.15 + 0.7 * (((j + 1) * 0.7548776662466927) % 1)) / SS; + +function hsv(h, sa, v, out, o) { + h = (h - floor(h)) * 6; + const i = floor(h) % 6, f = h - floor(h), p = v * (1 - sa), q = v * (1 - sa * f), t = v * (1 - sa * (1 - f)); + out[o] = i === 0 || i === 5 ? v : i === 1 ? q : i === 4 ? t : p; + out[o + 1] = i === 1 || i === 2 ? v : i === 0 ? t : i === 3 ? q : p; + out[o + 2] = i === 3 || i === 4 ? v : i === 2 ? t : i === 5 ? q : p; +} + +// The weight from the knob: 1/2 held from -0.08 to 0.08, 1/4 at -1, 0.9 at 1. +function weightOf(k) { + if (abs(k) <= 0.08) return 0.5; + if (k < 0) return k <= -0.995 ? 0.25 : 0.5 * pow(2, (k + 0.08) / 0.92); + return 0.5 + (0.4 * (k - 0.08)) / 0.92; +} + +// The levels a sample needs at this many columns to a unit of value: until +// a tent is a fiftieth of a column high. +const levelsFor = (w, cols) => cl(ceil(log(0.04 / cols) / log(w)) + 1, 4, NMAX); + +// The value of the full sum at x, with the zoom's levels taken away. +function shown(w, z, x, N) { + const k = floor(z), f = z - k; + let v = 0, a = 1, u = x; + for (let n = 0; n < N; n++) { + const m = n < k ? 0 : n === k ? 1 - f : 1; + if (m > 0) v += m * a * s(u); + a *= w; + u *= 2; + } + return v; +} + +// Columns per unit of value: the zoomed curve's largest value, over 1,024 +// samples, goes to column 60. +function fit(S, w, z) { + if (S.fw === w && S.fz === z) return; + let top = 0; + const N0 = levelsFor(w, 120 * pow(w, -z)); + const zs = pow(2, -z), ws = pow(w, -z); + for (let i = 0; i < 1024; i++) { + const x = ((i + 0.5 + 0.37 * OFF[i % SS]) / 1024) * zs; + top = max(top, shown(w, z, x, N0) * ws); + } + S.fw = w; + S.fz = z; + S.scale = (FIT / top) * ws; + S.N = levelsFor(w, S.scale); + // Levels built one by one: from floor(z) while a tent is 0.3 columns or + // more, at most twelve; the rest, if it adds up to a column, is a last band. + const k0 = floor(z); + let L = k0 + 1; + while (L < S.N && L - k0 < MAXB && 0.5 * pow(w, L) * S.scale >= 0.3) L++; + S.k0 = k0; + S.L = L; + S.tail = L < S.N && (0.25 * pow(w, L) * S.scale) / (1 - w) >= 1; +} + +export function setup(params) { + // The parabola check: 40 levels at w = 1/4 against 2x(1 - x). + let err = 0; + for (let i = 0; i < 1024; i++) { + const x = (i + 0.5 * OFF[i % SS]) / 1024; + let v = 0, a = 1, u = x; + for (let n = 0; n < 40; n++, a *= 0.25, u *= 2) v += a * s(u); + err = max(err, abs(v - 2 * x * (1 - x))); + } + params.s = { + t: STEP, hue: 0, w: 0.5, z: 0, fw: -1, fz: -1, scale: 90, N: 20, k0: 0, L: 8, tail: false, + parabola: err < 1e-12, buf: new Float32Array(W * H * 3), col: new Float32Array(6 * (NMAX + 2)), g: new Float32Array(NMAX), heat: new Float32Array(NMAX), + wd: new Float64Array(NMAX + 2), amp: new Float64Array(NMAX), touch: new Float32Array(H), + }; +} + +// Where each level stands in the cycle: grown share g and heat. +function schedule(S) { + const steps = S.L - S.k0 + (S.tail ? 1 : 0); + const build = steps * STEP, peelAt = build + HOLD, end = peelAt + steps * PEEL + GAP; + if (S.t >= end) S.t -= end * floor(S.t / end); + const t = S.t; + S.marked = t > build - REST && t < peelAt; + for (let n = 0; n < S.N; n++) { + const i = n < S.k0 ? -1 : n < S.L ? n - S.k0 : steps - 1; + if (i < 0) { S.g[n] = 1; S.heat[n] = 0; continue; } + const a = i * STEP, done = a + RISE; + let g = ease((t - a) / RISE); + const p0 = peelAt + (steps - 1 - i) * PEEL; + if (t >= p0) g *= 1 - ease((t - p0) / PEEL); + S.g[n] = g; + S.heat[n] = t < a ? 0 : t < done ? 0.8 : 0.8 * exp(-(t - done) / 0.7); + } +} + +export function update(dt, input, params) { + if (!params.s) setup(params); + const S = params.s, k = input.knobValues || [0, 2, 0, 0.06], d = cl(dt || 0, 0, 0.1); + S.hue = k[0] || 0; + const speed = cl(k[1] == null ? 2 : k[1], 0.2, 3); + S.w = weightOf(cl(k[2] || 0, -1, 1)); + S.z = 4 * cl(((k[3] == null ? 0.06 : k[3]) - 0.1) / 0.9, 0, 1); + fit(S, S.w, S.z); + S.t += d * speed; + schedule(S); +} + +// Adds a colour over columns c0..c1 of one row, by how much of each it covers. +function span(buf, o, c0, c1, r, g, b) { + if (c1 <= c0) return; + const a = floor(c0), e = min(W - 1, floor(c1)); + for (let x = a; x <= e; x++) { + const cov = min(c1, x + 1) - max(c0, x); + if (cov <= 0) continue; + const i = o + x * 3; + buf[i] += r * cov; + buf[i + 1] += g * cov; + buf[i + 2] += b * cov; + } +} + +export function draw(display, params) { + const S = params.s; + if (!S) return; + const { buf, col, g, heat, wd, amp, touch } = S; + buf.fill(0); + const w = S.w, z = S.z, k = floor(z), f = z - k, N = S.N, L = S.L, nb = S.tail ? L + 1 : L; + const zs = pow(2, -z); + // Level 0's hue: azure at Hue 0, turning with the knob but stepping over + // orange and yellow, which read brown and olive in its 40 percent fill. + const h0 = 0.58 + 0.79 * S.hue, hue = h0 > 1.03 ? h0 + 0.21 : h0; + // Band colours, inner and edge, already divided by the sample count. + for (let b = 0; b < nb; b++) { + const n = min(b, N - 1); + const tailBand = S.tail && b === L; + // Blue at level 0 to pale pink at level 7, then back toward violet. + // The last band, all the finer levels together, is a soft magenta. + const bb = b <= 7 ? b : max(0, 14 - b); + if (tailBand) hsv(hue + 0.26, 0.5, 1, col, 6 * b); + else hsv(hue + 0.065 * bb, max(0.2, 0.95 - 0.1 * bb) - (b % 2) * 0.1, 1, col, 6 * b); + const h = tailBand ? heat[N - 1] : heat[n], inner = tailBand ? 0.6 : INNER; + for (let c = 0; c < 3; c++) { + const v = col[6 * b + c] * (1 - h) + h; + col[6 * b + c] = (255 * inner * v) / SS; + col[6 * b + 3 + c] = (255 * v) / SS; + } + } + // Each level's width factor in columns. + let a = 1; + for (let n = 0; n < N; n++, a *= w) { + const m = n < k ? 0 : n === k ? 1 - f : 1; + amp[n] = m * a * g[n] * S.scale; + } + touch.fill(0); + for (let y = 0; y < H; y++) { + const o = y * W * 3; + for (let j = 0; j < SS; j++) { + const x = ((y + OFF[j]) / H) * zs; + wd.fill(0); + let u = x; + for (let n = 0; n < N; n++, u *= 2) if (amp[n] > 0) wd[n < L ? n : L] += amp[n] * s(u); + let c0 = 0; + for (let b = 0; b < nb; b++) { + const c1 = c0 + wd[b]; + if (c1 > c0) { + const m = max(c0, c1 - EDGE); + span(buf, o, c0, m, col[6 * b], col[6 * b + 1], col[6 * b + 2]); + span(buf, o, m, c1, col[6 * b + 3], col[6 * b + 4], col[6 * b + 5]); + } + c0 = c1; + } + if (c0 >= FIT - 0.5) touch[y] += 1 / SS; + } + } + // Guides, dotted every other row in pale grey: at w = 1/2 the maximum 2/3 + // at column 60, lit white where the finished curve reaches it; at w = 1/4 + // the closed form 2x(1 - x) with the zoom's levels taken away. + const gr = [0, 0, 0]; + hsv(hue, 0.15, 1, gr, 0); + if (w === 0.5) { + for (let y = 0; y < H; y++) { + const t = S.marked ? touch[y] : 0; + const v = t > 0 ? 120 + 135 * t : y % 2 ? 0 : 34; + const i = (y * W + FIT) * 3; + buf[i] += gr[0] * v; buf[i + 1] += gr[1] * v; buf[i + 2] += gr[2] * v; + if (t > 0) { buf[i + 3] += gr[0] * v * 0.45; buf[i + 4] += gr[1] * v * 0.45; buf[i + 5] += gr[2] * v * 0.45; } + } + } else if (w === 0.25 && S.parabola) { + for (let y = 0; y < H; y += 2) { + const x = ((y + 0.5) / H) * zs; + let v = 2 * x * (1 - x), aa = 1, u = x; + for (let n = 0; n <= k; n++, aa *= w, u *= 2) v -= (n < k ? 1 : f) * aa * s(u); + const c = v * S.scale; + if (c < 0 || c >= W) continue; + span(buf, y * W * 3, c + 0.3, c + 1.3, gr[0] * 70, gr[1] * 70, gr[2] * 70); + } + } + for (let y = 0; y < H; y++) + for (let x = 0; x < W; x++) { + const i = (y * W + x) * 3; + const m = max(buf[i], buf[i + 1], buf[i + 2]), q = m > 255 ? 255 / m : 1; + display.setPixel(x, y, round(buf[i] * q), round(buf[i + 1] * q), round(buf[i + 2] * q)); + } +} From 23556f9eb0344b1ee0430b81082d4b3186e5046b Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 08:42:40 +0000 Subject: [PATCH 6/9] =?UTF-8?q?=ED=8C=A8=ED=84=B4=20=EC=97=B0=EA=B5=AC=201?= =?UTF-8?q?02=ED=8C=90:=20Kauffman=20Bracket=20=EC=9D=84=20=EB=8D=94?= =?UTF-8?q?=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Bc4JkshDz129z5fXhUWQxz --- .../patterns/kauffman-bracket.js | 438 ++++++++++++++++++ 1 file changed, 438 insertions(+) create mode 100644 incoming/pattern-study/patterns/kauffman-bracket.js diff --git a/incoming/pattern-study/patterns/kauffman-bracket.js b/incoming/pattern-study/patterns/kauffman-bracket.js new file mode 100644 index 00000000..c1da30c4 --- /dev/null +++ b/incoming/pattern-study/patterns/kauffman-bracket.js @@ -0,0 +1,438 @@ +// ===== Patternflow pattern ===== +// Title: Kauffman Bracket +// Family: topology +// Idea: A knot diagram fills the top half and Kauffman's state sum walks its 2^n states in Gray code order, one crossing at a time pinching into its X and opening out into its other smoothing, so the knot falls apart into loops each in a colour of its own, while each state's term A^(a-b) d^(loops-1) drops into bars by power of A below, rising and cancelling until the bracket polynomial stands and turns over like a card into the Jones polynomial. +// Author: patternflow-lab +// Date: 2026-11-03 +// SPDX-License-Identifier: CC-BY-SA-4.0 +// Lineage: Kauffman bracket and its state sum (Louis Kauffman, State models and the Jones polynomial, Topology, 1987), the Jones polynomial (Vaughan Jones, 1984) normalised by the writhe, knot names after Rolfsen's table (Dale Rolfsen, Knots and Links, 1976), states in reflected binary order (Frank Gray, 1953); diagrams found for this pattern as mirror-symmetric Fourier curves; pattern round 102 +// Round: 102 +// =============================== +// @matrix 64x128 +// @knobs Hue=0..1, Speed=0.2..3, Knot=0..5, Switch=0..1 +// +// Knob 1: Hue. Turns the colours round the wheel. At 0 the loops are sky blue, orange, green, magenta, yellow, blue and pink in the order they appear, the whole knot pale blue, positive bars pale gold and negative bars rose; at 0.5 the first loop is red-orange and the positive bars lilac. +// Knob 2: Speed. At 2 a state lasts 0.9 s on the trefoil, 0.6 s on the figure-eight, 0.42 s on a five-crossing knot and 0.3 s on 6_1; with 5.5 s of whole knot, closing up, bracket, turning over and Jones polynomial a walk takes 12.7 s for the trefoil and 24.7 s for 6_1, all five 90 s. Ten times slower at 0.2, half as fast again at 3. +// Knob 3: Knot. 0 walks five knots in turn: the trefoil 3_1 (8 states), the figure-eight 4_1 (16), the cinquefoil 5_1 and 5_2 (32 each) and the stevedore 6_1 (64); 1 to 5 walk one of them over and over. The panel opens on the trefoil three states into its walk. +// Knob 4: Switch. Changes the first round(Switch x n) crossings in reading order, and the walk, the bars and the knot's name follow: the trefoil is the unknot 0_1 at 0.25 and 0.5 and its mirror 3_1* at 1, the figure-eight with all four changed is 4_1 again, its own mirror, and 6_1 with two changed is 5_2. A changed crossing is underlined in the state word. + +// Turn the over strand of a crossing anticlockwise: the two corners it +// sweeps are joined by the A-smoothing, the other two by the B-smoothing. A +// state with a A's and b B's leaves L loops, and +// = sum over the 2^n states of A^(a-b) d^(L-1), d = -A^2 - A^-2. +// Over and under alternate along the drawn curve, which Gauss's parity +// always allows; loops are counted by union-find over the 4n arc ends. +// +// The check, on the right-handed trefoil (w = 3): the all-A state is the 2 +// Seifert circles, A^3 d = -A^5 - A, the all-B state 3 loops, +// A^-3 d^2 = A + 2A^-3 + A^-7, and the 8 states sum to the known +// <3_1> = A^-7 - A^-3 - A^5, whose (-A^3)^-3 <3_1> at A = t^(-1/4) is the +// Jones polynomial t + t^3 - t^4. Every knot is named by matching its sum +// against TABLE, so a wrong sum would show a question mark in the corner. +// A state's lowest power a - b - 2(L - 1) is the same mod 4 for all states, +// so the bars stand 4 powers of A apart, n + 1 for an alternating diagram. + +const W = 64, H = 128, N = W * H; +const { floor, round, sqrt, min, max, abs, cos, sin, PI } = Math; +const cl = (v, a, b) => (v < a ? a : v > b ? b : v); +const ease = (u) => u * u * (3 - 2 * u); +const mix = (p, q, u) => [p[0] + (q[0] - p[0]) * u, p[1] + (q[1] - p[1]) * u, p[2] + (q[2] - p[2]) * u]; + +// x = sum a_k sin(k t), y = sum b_k cos(k t): curves symmetric about the +// vertical axis. The trefoil is the textbook sin t + 2 sin 2t, cos t - 2 cos 2t, +// the cinquefoil r = 2 + 0.7 cos 5t at angle 2t; the other three came out of a +// search over such curves for the crossing count and the Jones polynomial, +// eased apart for room. +const KNOTS = [ + [[1, 2], [1, -2]], + [[-2.128, 0.509, -2.794, 0.379], [-2.695, -3.169, 1.256, -0.994]], + [[0, 2, -0.35, 0, 0, 0, 0.35], [0, 2, 0.35, 0, 0, 0, 0.35]], + [[0.336, 2.065, 0.787, 2.035, 0.019], [0.061, -0.285, 3.636, 0.078, 1.311]], + [[-1.285, 0.687, -1.873, 0.33], [0.647, -2.678, -1.296, -0.812]], +]; +// What a diagram here can turn into when crossings are changed, named as in +// Rolfsen's table (0_1 the unknot) by Jones polynomial: lowest power of t, +// then the coefficients. A star marks the mirror of the knot as first drawn. +const TABLE = [ + ["01", 0, [1]], + ["31", 1, [1, 0, 1, -1]], + ["41", -2, [1, -1, 1, -1, 1]], + ["51", 2, [1, 0, 1, -1, 1, -1]], + ["52", -6, [-1, 1, -1, 2, -1, 1]], + ["61", -4, [1, -1, 1, -2, 2, -1, 1]], +]; +// Seconds per state at Speed 2, by crossing count, and the other parts of a walk. +const STEP = { 3: 0.9, 4: 0.6, 5: 0.42, 6: 0.3 }; +const T_INTRO = 1, T_CLOSE = 0.8, T_BRACKET = 1.5, T_TURN = 0.6, T_JONES = 1.6, T_FADE = 0.4; +// The diagram's centre row, the state word's row, the zero line, the tallest bar. +const DCY = 34, ROW_STRIP = 69, ZY = 96, BAR_MAX = 19; +// How far a smoothing's curves bend into the crossing: 0.45 as drawn, 4/3 +// where both pass through the crossing point and make the X. +const BEND = 0.45, PINCH = 4 / 3; +const PAL = [0.55, 0.08, 0.33, 0.85, 0.15, 0.66, 0.96]; + +function hsv(h, s, v) { + h = (h - floor(h)) * 6; + const i = floor(h) % 6, f = h - floor(h), p = v * (1 - s), q = v * (1 - s * f), t = v * (1 - s * (1 - f)); + return [[v, t, p], [q, v, p], [p, v, t], [p, q, v], [t, p, v], [v, p, q]][i]; +} + +// A polyline as pixels, a line 2 LEDs wide. +const COV = new Float32Array(N); +function raster(pts) { + const touched = []; + for (let i = 0; i + 1 < pts.length; i++) { + const [ax, ay] = pts[i], [bx, by] = pts[i + 1], dx = bx - ax, dy = by - ay, l2 = dx * dx + dy * dy; + for (let y = max(0, floor(min(ay, by) - 2)); y <= min(H - 1, floor(max(ay, by) + 2)); y++) + for (let x = max(0, floor(min(ax, bx) - 2)); x <= min(W - 1, floor(max(ax, bx) + 2)); x++) { + const qx = x + 0.5 - ax, qy = y + 0.5 - ay, u = l2 > 0 ? cl((qx * dx + qy * dy) / l2, 0, 1) : 0; + const c = cl(1.5 - Math.hypot(qx - u * dx, qy - u * dy), 0, 1), p = y * W + x; + if (c <= 0) continue; + if (COV[p] === 0) touched.push(p); + if (c > COV[p]) COV[p] = c; + } + } + const cv = touched.map((p) => COV[p]); + touched.forEach((p) => (COV[p] = 0)); + return { px: touched, cv }; +} + +// One curve of a smoothing from arc end e1 to e2, the inner control points +// pulled a share tq of the way to the crossing point C. +function bend(E, C, [e1, e2], tq) { + const P = E[e1], Q = E[e2], K1 = mix(P, C, tq), K2 = mix(Q, C, tq), out = []; + for (let i = 0; i <= 10; i++) { + const s = i / 10, u = 1 - s, w0 = u * u * u, w1 = 3 * u * u * s, w2 = 3 * u * s * s, w3 = s * s * s; + out.push([w0 * P[0] + w1 * K1[0] + w2 * K2[0] + w3 * Q[0], w0 * P[1] + w1 * K1[1] + w2 * K2[1] + w3 * Q[1]]); + } + return raster(out); +} + +const binom = (n, k) => { + let r = 1; + for (let i = 1; i <= k; i++) r = (r * (n - i + 1)) / i; + return r; +}; + +// The drawing of one knot, kept once made: arcs, arc ends, crossings. +const GEO = new Map(); +function geometry(ki) { + if (GEO.has(ki)) return GEO.get(ki); + const [a, b] = KNOTS[ki], M = 720, px = new Float64Array(M), py = new Float64Array(M); + for (let i = 0; i < M; i++) { + const t = (2 * PI * i) / M; + for (let k = 0; k < a.length; k++) { + px[i] += a[k] * sin((k + 1) * t); + py[i] += b[k] * cos((k + 1) * t); + } + } + const x0 = min(...px), x1 = max(...px), y0 = min(...py), y1 = max(...py), s = min(58 / (x1 - x0), 62 / (y1 - y0)); + for (let i = 0; i < M; i++) { + px[i] = 32 + (px[i] - (x0 + x1) / 2) * s; + py[i] = DCY - (py[i] - (y0 + y1) / 2) * s; + } + const X = []; + for (let i = 0; i < M; i++) { + const ax = px[i], ay = py[i], rx = px[(i + 1) % M] - ax, ry = py[(i + 1) % M] - ay; + for (let j = i + 2; j < M; j++) { + if (i === 0 && j === M - 1) continue; + const cx = px[j], cy = py[j], sx = px[(j + 1) % M] - cx, sy = py[(j + 1) % M] - cy, den = rx * sy - ry * sx; + if (abs(den) < 1e-12) continue; + const qx = cx - ax, qy = cy - ay, u = (qx * sy - qy * sx) / den, v = (qx * ry - qy * rx) / den; + if (u >= 0 && u < 1 && v >= 0 && v < 1) X.push({ x: ax + u * rx, y: ay + u * ry, t: [i + u, j + v], d: [[rx, ry], [sx, sy]] }); + } + } + // Reading order, so the state word follows the diagram top to bottom. + X.sort((p, q) => (abs(p.y - q.y) > 3 ? p.y - q.y : p.x - q.x)); + const n = X.length, m = 2 * n, passes = []; + X.forEach((c, k) => [0, 1].forEach((h) => passes.push({ c: k, t: c.t[h], d: c.d[h] }))); + passes.sort((p, q) => p.t - q.t); + const L = new Float64Array(M + 1); + for (let i = 0; i < M; i++) L[i + 1] = L[i] + Math.hypot(px[(i + 1) % M] - px[i], py[(i + 1) % M] - py[i]); + const total = L[M], lenAt = (t) => L[floor(t)] + (t - floor(t)) * (L[floor(t) + 1] - L[floor(t)]); + const pointAt = (sl) => { + sl = ((sl % total) + total) % total; + let lo = 0, hi = M; + while (hi - lo > 1) { + const mid = (lo + hi) >> 1; + if (L[mid] <= sl) lo = mid; + else hi = mid; + } + const u = (sl - L[lo]) / (L[lo + 1] - L[lo] || 1), j = (lo + 1) % M; + return [px[lo] + (px[j] - px[lo]) * u, py[lo] + (py[j] - py[lo]) * u]; + }; + let gap = 1e9; + for (let i = 0; i < n; i++) for (let j = i + 1; j < n; j++) gap = min(gap, Math.hypot(X[i].x - X[j].x, X[i].y - X[j].y)); + const rc = min(5.5, 0.38 * gap); + // Arc k runs from pass k to pass k + 1, stopping rc short of each crossing; + // end 2k is its start and 2k + 1 its end. + const arcs = [], E = []; + for (let k = 0; k < m; k++) { + const sa = lenAt(passes[k].t) + rc, sb = lenAt(passes[(k + 1) % m].t) - rc + (k === m - 1 ? total : 0); + const pts = [pointAt(sa)]; + for (let sl = sa + 1; sl < sb; sl += 1) pts.push(pointAt(sl)); + pts.push(pointAt(max(sa, sb))); + arcs.push(raster(pts)); + E[2 * k] = pts[0]; + E[2 * k + 1] = pts[pts.length - 1]; + } + const g = { n, m, E, arcs, X, passes, rc }; + GEO.set(ki, g); + return g; +} + +// Knot ki with its first sw crossings in reading order changed, and its +// whole walk: loop colours and running sums for every state. +function build(ki, sw) { + const { n, m, E, arcs, X, passes, rc } = geometry(ki); + const cr = X.map((c, i) => { + const [p, q] = passes.map((ps, k) => (ps.c === i ? k : -1)).filter((k) => k >= 0); + let over = p % 2 === 0 ? p : q, under = over === p ? q : p; + if (i < sw) [over, under] = [under, over]; + const o = passes[over].d, u = passes[under].d; + // y runs down the panel, so anticlockwise there is a negative cross product. + const sign = o[0] * u[1] - o[1] * u[0] < 0 ? 1 : -1; + const oin = 2 * ((over - 1 + m) % m) + 1, oout = 2 * over, uin = 2 * ((under - 1 + m) % m) + 1, uout = 2 * under; + // At a positive crossing the A-smoothing is the oriented one. + const oriented = [[uout, oin], [uin, oout]], other = [[oout, uout], [oin, uin]]; + const C = [c.x, c.y], A = sign > 0 ? oriented : other, B = sign > 0 ? other : oriented, g = 4.2 / rc; + const conn = (pr) => ({ r: bend(E, C, pr, BEND), arc: pr[0] >> 1 }); + // The whole crossing: the over strand through, the under strand broken. + const whole = [raster([E[oin], C, E[oout]]), raster([E[uin], mix(C, E[uin], g)]), raster([E[uout], mix(C, E[uout], g)])]; + return { C, sign, pairs: [A, B], smooth: [A.map(conn), B.map(conn)], whole }; + }); + const writhe = cr.reduce((s, c) => s + c.sign, 0), count = 1 << n, terms = [], par = new Int32Array(2 * m); + const find = (x) => (par[x] === x ? x : (par[x] = find(par[x]))); + let emin = 1e9, emax = -1e9; + // Gray code: step k changes the crossing of k's lowest set bit. + for (let k = 0; k < count; k++) { + const st = k ^ (k >> 1), roots = new Map(), loopOf = new Int32Array(m); + for (let i = 0; i < 2 * m; i++) par[i] = i; + for (let j = 0; j < m; j++) par[find(2 * j)] = find(2 * j + 1); + cr.forEach((c, i) => c.pairs[(st >> i) & 1].forEach(([e1, e2]) => (par[find(e1)] = find(e2)))); + for (let j = 0; j < m; j++) { + const r = find(2 * j); + if (!roots.has(r)) roots.set(r, roots.size); + loopOf[j] = roots.get(r); + } + let bb = 0; + for (let i = 0; i < n; i++) bb += (st >> i) & 1; + const loops = roots.size, base = n - 2 * bb - 2 * (loops - 1); + emin = min(emin, base); + emax = max(emax, base + 4 * (loops - 1)); + terms.push({ st, flip: k ? 31 - Math.clz32(k & -k) : -1, loops, loopOf, base, sgn: (loops - 1) % 2 ? -1 : 1 }); + } + const nb = (emax - emin) / 4 + 1, sum = new Float32Array(nb); + // A new loop keeps the colour it shares most arcs with, so a merge keeps + // the larger loop's colour and a split hands one half a free colour. + let prevCol = null; + for (const tm of terms) { + const col = new Int8Array(tm.loops).fill(-1); + if (prevCol) { + const votes = [], used = new Set(); + for (let l = 0; l < tm.loops; l++) { + const cnt = new Map(); + for (let j = 0; j < m; j++) if (tm.loopOf[j] === l) cnt.set(prevCol[j], (cnt.get(prevCol[j]) || 0) + 1); + for (const [c, v] of cnt) votes.push([v, l, c]); + } + votes.sort((p, q) => q[0] - p[0] || p[2] - q[2]); + for (const [, l, c] of votes) + if (col[l] < 0 && !used.has(c)) { + col[l] = c; + used.add(c); + } + } + for (let l = 0; l < tm.loops; l++) + if (col[l] < 0) { + let c = 0; + while (col.includes(c)) c++; + col[l] = c; + } + tm.arcCol = tm.loopOf.map((l) => col[l]); + prevCol = tm.arcCol; + tm.before = Float32Array.from(sum); + tm.add = new Float32Array(nb); + for (let j = 0; j < tm.loops; j++) { + const i = (tm.base + 4 * j - emin) / 4; + tm.add[i] = tm.sgn * binom(tm.loops - 1, j); + sum[i] += tm.add[i]; + } + tm.after = Float32Array.from(sum); + } + // Name the knot by its Jones polynomial. + const V = new Map(); + sum.forEach((c, i) => c && V.set(-(emin + 4 * i - 3 * writhe) / 4, writhe % 2 ? -c : c)); + const lo = min(...V.keys()), hi = max(...V.keys()); + let name = "?"; + for (const [nm, low, cf] of TABLE) { + const same = (s) => cf.length === hi - lo + 1 && cf.every((c, j) => (V.get(s * (low + j)) || 0) === c); + if (same(1)) name = nm; + else if (same(-1) && name === "?") name = nm + "*"; + } + return { ki, sw, name, n, m, E, arcs, cr, writhe, terms, emin, nb, step: STEP[n] || 0.3, count }; +} + +const length = (K) => T_INTRO + K.count * K.step + T_CLOSE + T_BRACKET + T_TURN + T_JONES; + +function phase(S) { + const K = S.K, last = K.count - 1; + let t = S.tau - T_INTRO; + if (t < 0) return { name: "intro", k: 0, f: 0 }; + if (t < K.count * K.step) return { name: "walk", k: floor(t / K.step), f: t / K.step - floor(t / K.step) }; + t -= K.count * K.step; + if (t < T_CLOSE) return { name: "close", k: last, f: 1, u: t / T_CLOSE }; + t -= T_CLOSE; + if (t < T_BRACKET) return { name: "hold", k: last, f: 1 }; + t -= T_BRACKET; + if (t < T_TURN) return { name: "turn", k: last, f: 1, u: t / T_TURN }; + return { name: "jones", k: last, f: 1, left: T_JONES - t + T_TURN }; +} + +// 3 by 5 glyphs, a row to a digit read in binary; the 1 is one column wide so +// -14 fits under a bar 9 LEDs wide, the minus two. +const GL = { 0: "75557", 1: "11111", 2: "71747", 3: "71717", 4: "55711", 5: "74717", 6: "74757", 7: "71111", 8: "75757", 9: "75717", "-": "00300", A: "25755", B: "65656", "*": "05250", "?": "71302" }; +const GW = { 1: 1, "-": 2 }; +const textWidth = (str) => [...str].reduce((w, ch) => w + (GW[ch] || 3) + 1, -1); + +export function setup(params) { + params.s = { K: null, cycle: true, knob: -1, sw: 0, tau: 0, hue: 0, flip: 1, unit: 12, buf: new Float32Array(N * 3), cov: new Float32Array(N), col: new Float32Array(N * 3) }; +} + +function start(S, ki, tau) { + S.K = build(ki, round(S.sw * geometry(ki).n)); + S.tau = tau == null ? T_INTRO + 3 * S.K.step : tau; +} + +export function update(dt, input, params) { + if (!params.s) setup(params); + const S = params.s, k = input.knobValues || [0, 2, 0, 0.06], d = cl(dt || 0, 0, 0.1); + S.hue = k[0] || 0; + const speed = cl(k[1] == null ? 2 : k[1], 0.2, 3), knob = cl(floor(k[2] || 0), 0, 5); + S.sw = cl(k[3] == null ? 0.06 : k[3], 0, 1); + if (knob !== S.knob) { + S.knob = knob; + S.cycle = knob === 0; + start(S, knob === 0 ? 0 : knob - 1); + } else if (round(S.sw * S.K.n) !== S.K.sw) S.K = build(S.K.ki, round(S.sw * S.K.n)); // rebuilt where the walk stands + S.tau += (d * speed) / 2; + if (S.tau >= length(S.K)) start(S, S.cycle ? (S.K.ki + 1) % KNOTS.length : S.K.ki, 0); + // The bar scale follows the largest bar, so the final bracket stands tall. + const ph = phase(S), tm = S.K.terms[ph.k]; + let big = 1; + for (let i = 0; i < S.K.nb; i++) big = max(big, abs(tm.after[i]), ph.name === "walk" || ph.name === "intro" ? abs(tm.before[i]) : 0); + S.flip = ph.name === "turn" ? cos(PI * ease(ph.u)) : ph.name === "jones" ? -1 : 1; + S.unit += (cl(BAR_MAX / big, 2, 12) - S.unit) * min(1, d * 3 * speed); +} + +export function draw(display, params) { + const S = params.s; + if (!S || !S.K) return; + const K = S.K, buf = S.buf, cov = S.cov, col = S.col, ph = phase(S), hue = S.hue; + buf.fill(0); + cov.fill(0); + const pal = PAL.map((h) => hsv(h + hue, 0.85, 1)), knotCol = hsv(hue + 0.58, 0.32, 1), white = [1, 1, 1]; + const fade = ph.name === "intro" ? cl((S.tau / T_FADE) * 1.2, 0, 1) : ph.name === "jones" ? cl(ph.left / T_FADE, 0, 1) : 1; + // Strokes: the most covering one wins a pixel, so colours never mix. + const put = (r, c, a) => { + for (let i = 0; i < r.px.length; i++) { + const p = r.px[i], v = r.cv[i] * a; + if (v > cov[p]) { + cov[p] = v; + col.set(c, p * 3); + } + } + }; + const add = (x, y, c, a) => { + x = floor(x); + y = floor(y); + if (x < 0 || x >= W || y < 0 || y >= H) return; + for (let q = 0; q < 3; q++) buf[(y * W + x) * 3 + q] += c[q] * a; + }; + const text = (str, x, y, c, a) => { + for (const ch of str) { + const w = GW[ch] || 3; + for (let yy = 0; yy < 5; yy++) for (let xx = 0; xx < w; xx++) if ((+GL[ch][yy] >> (w - 1 - xx)) & 1) add(x + xx, y + yy, c, a); + x += w + 1; + } + }; + const tm = K.terms[ph.k], prev = ph.k > 0 ? K.terms[ph.k - 1] : null, walk = ph.name === "walk"; + // How far the crossings stand open (0 the whole knot), and how far the + // changing crossing has gone over. + let open = 0, turn = 1; + if (walk && ph.k === 0) open = ease(cl(ph.f / 0.3, 0, 1)); + else if (walk) [open, turn] = [1, ease(cl(ph.f / 0.3, 0, 1))]; + else if (ph.name === "close") open = 1 - ease(ph.u); + const arcColour = (j) => mix(knotCol, prev ? mix(pal[prev.arcCol[j]], pal[tm.arcCol[j]], turn) : pal[tm.arcCol[j]], open); + for (let j = 0; j < K.m; j++) put(K.arcs[j], arcColour(j), 1); + // A crossing changes smoothing by pinching its two curves into the + // crossing point, where together they make the X, and opening them out the + // other way; the knot opens and closes the same way at every crossing. + K.cr.forEach((c, i) => { + const side = (tm.st >> i) & 1; + if (open < 1) { + const wa = 1 - cl(open / 0.25, 0, 1); + if (wa > 0) for (const r of c.whole) put(r, knotCol, wa); + if (open > 0) for (const pr of c.pairs[side]) put(bend(K.E, c.C, pr, PINCH + (BEND - PINCH) * open), arcColour(pr[0] >> 1), 1 - wa); + } else if (prev && i === tm.flip && turn < 1) { + const tq = turn < 0.5 ? BEND + (PINCH - BEND) * turn * 2 : PINCH + (BEND - PINCH) * (turn * 2 - 1); + for (const q of c.pairs[turn < 0.5 ? 1 - side : side]) put(bend(K.E, c.C, q, tq), arcColour(q[0] >> 1), 1); + } else for (const q of c.smooth[side]) put(q.r, arcColour(q.arc), 1); + }); + for (let p = 0; p < N; p++) for (let q = 0; q < 3; q++) buf[p * 3 + q] = col[p * 3 + q] * cov[p]; + // A ring of light where a crossing changes. + if (walk && ph.f < 0.4) + for (const c of ph.k === 0 ? K.cr : [K.cr[tm.flip]]) + for (let q = 0; q < 20; q++) add(c.C[0] + (1 + ph.f * 14) * cos(q * 0.314), c.C[1] + (1 + ph.f * 14) * sin(q * 0.314), white, 0.55 * (1 - ph.f / 0.4)); + // The knot's name in Rolfsen's table, top left. + const dim = hsv(hue + 0.6, 0.25, 0.42); + text(K.name[0], 1, 1, dim, 1); + if (K.name[1]) text(K.name[1], 5, 4, dim, 1); + if (K.name[2]) text(K.name[2], 6 + textWidth(K.name[1]), 1, dim, 1); + // The state word, one letter per crossing in reading order, a changed + // crossing underlined, then a dot per loop, which changes halfway through a turn. + const letA = hsv(hue + 0.55, 0.12, 0.95), letB = hsv(hue + 0.08, 0.4, 0.95); + const dots = [...new Set((prev && turn < 0.5 ? prev : tm).arcCol)].sort((p, q) => p - q); + const x0 = round((W - K.n * 4 - 4 - dots.length * 4) / 2), shown = walk ? 1 : ph.name === "close" ? 1 - ph.u : 0; + for (let i = 0; i < K.n; i++) { + const side = (tm.st >> i) & 1, hot = walk && (i === tm.flip || ph.k === 0) ? 1 - cl(ph.f / 0.5, 0, 1) : 0; + text(side ? "B" : "A", x0 + i * 4, ROW_STRIP, mix(side ? letB : letA, white, hot), (0.65 + 0.35 * hot) * shown); + if (i < K.sw) for (let xx = 0; xx < 3; xx++) add(x0 + i * 4 + xx, ROW_STRIP + 6, letB, 0.8 * shown); + } + dots.forEach((c, i) => { + for (let q = 0; q < 9; q++) add(x0 + K.n * 4 + 4 + i * 4 + (q % 3), ROW_STRIP + 1 + floor(q / 3), pal[c], shown); + }); + // The chart. It turns over about its middle column into the Jones + // polynomial, and upside down as well when the writhe is odd. + const nb = K.nb, sp = min(16, W / nb), bw = max(3, round(sp) - 1), fl = S.flip, ys = K.writhe % 2 ? fl : 1, u = S.unit; + const grow = walk ? ease(cl((ph.f - 0.3) / 0.3, 0, 1)) : 1, fresh0 = walk ? 1 - cl((ph.f - 0.45) / 0.5, 0, 1) : 0; + const posC = hsv(hue + 0.13, 0.4, 1), negC = hsv(hue + 0.93, 0.5, 1), grid = hsv(hue + 0.6, 0.2, 0.3); + const glow = walk || ph.name === "intro" ? 0 : 0.15 + 0.15 * cos(S.tau * 4); + for (let x = 0; x < W; x++) add(x, ZY, grid, 0.7); + let widest = 0; + for (let i = 0; i < nb; i++) widest = max(widest, textWidth(String(K.emin + 4 * i))); + const stagger = fl > 0 && widest > sp - 2; + for (let i = 0; i < nb; i++) { + const xc = (i - (nb - 1) / 2) * sp, xa = 32 + (xc - bw / 2) * fl, xb = 32 + (xc + bw / 2) * fl; + const lo = min(xa, xb), hi = max(xa, xb), old = tm.before[i] * ys; + const v = ph.name === "intro" ? 0 : old + (tm.after[i] * ys - old) * grow, h = abs(v) * u, dir = v >= 0 ? -1 : 1; + const fresh = tm.add[i] !== 0 ? fresh0 : 0; + // Rows past this height are what the state just added; a shrinking bar + // lights its tip instead. + const from = old * v > 0 ? abs(old) * u : 0; + for (let r = 0; r < Math.ceil(h); r++) { + // A dim row where one unit ends and the next begins. + const seam = u >= 3 && floor((r + 1) / u) > floor(r / u) && r + 1 < h - 0.01 ? 0.45 : 1; + const lit = fresh > 0 && (r + 1 > from || (from >= h && r >= Math.ceil(h) - 1)); + const cc = mix(v >= 0 ? posC : negC, white, lit ? 0.7 * fresh : 0); + for (let x = floor(lo); x <= floor(hi - 0.01); x++) add(x, ZY + dir * (r + 1), cc, cl(h - r, 0, 1) * seam * min(1, x + 1 - lo, hi - x) * (0.85 + glow)); + } + if (fresh > 0) for (let x = floor(lo); x <= floor(hi - 0.01); x++) add(x, ZY, white, 0.6 * fresh); + // Labels: powers of A, or of t once the chart has turned over. + if (abs(fl) > 0.35) { + const e = K.emin + 4 * i, lab = String(fl > 0 ? e : -(e - 3 * K.writhe) / 4); + const lc = fresh > 0 ? mix(grid, white, fresh) : hsv(hue + 0.6, 0.2, 0.55); + text(lab, round((lo + hi) / 2 - textWidth(lab) / 2 + 0.01), stagger ? 117 + 6 * (i % 2) : 119, lc, cl((abs(fl) - 0.35) / 0.3, 0, 1)); + } + } + for (let p = 0; p < N; p++) display.setPixel(p % W, floor(p / W), round(min(1, buf[p * 3] * fade) * 255), round(min(1, buf[p * 3 + 1] * fade) * 255), round(min(1, buf[p * 3 + 2] * fade) * 255)); +} From 9ee6640ad860277c3a24900476fec7e3fb2063da Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 08:55:09 +0000 Subject: [PATCH 7/9] =?UTF-8?q?=ED=8C=A8=ED=84=B4=20=EC=97=B0=EA=B5=AC=201?= =?UTF-8?q?02=ED=8C=90:=20Side=20Radicals=20=EB=A5=BC=20=EB=8D=94=ED=95=9C?= =?UTF-8?q?=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Bc4JkshDz129z5fXhUWQxz --- .../pattern-study/patterns/side-radicals.js | 296 ++++++++++++++++++ 1 file changed, 296 insertions(+) create mode 100644 incoming/pattern-study/patterns/side-radicals.js diff --git a/incoming/pattern-study/patterns/side-radicals.js b/incoming/pattern-study/patterns/side-radicals.js new file mode 100644 index 00000000..ecc79817 --- /dev/null +++ b/incoming/pattern-study/patterns/side-radicals.js @@ -0,0 +1,296 @@ +// ===== Patternflow pattern ===== +// Title: Side Radicals +// Family: type +// Idea: A character component is written stroke by stroke in the top practice square, æ°Ž or 人, 手, 心, 火, 刀 or 犬, and then a copy of its strokes, each in a colour of its own, slides down the tall panel into the bottom square and changes shape on the way into the narrow form it takes as a side radical, the four strokes of æ°Ž shrinking and turning into the two dots and the rising stroke of æ°µ while its vertical hook melts into them, until it docks on the left (灬 at the bottom, 刂 on the right), the other part slides in beside it and 江 stands, then æ²³. +// Author: patternflow-lab +// Date: 2026-11-03 +// SPDX-License-Identifier: CC-BY-SA-4.0 +// Lineage: the side forms of radicals (偏旁), the changed shapes a component takes at the side of a character in regular script, which the Kangxi Dictionary (1716) files under the full radical (æ°µ under æ°Ž, 亻 under 人, 扌 under 手, 忄 under 心, 灬 under 火, 刂 under 刀, 犭 under 犬); the practice square (ç”°å­—æ Œ); strokes and stroke orders written for this pattern after the regular script, with the mainland forms 点 and 杰; pattern round 102 +// Round: 102 +// =============================== +// @matrix 64x128 +// @knobs Hue=0..1, Speed=0.2..3, Set=0..7, Morph=0..1 +// +// Knob 1: Hue. Turns the colours round the wheel. At 0 the component's strokes are gold, sky blue, pink and green in stroke order, the plain ink pale blue-white and the squares a dim blue-grey; at 0.5 they are blue, red-orange, sea green and violet and the plain ink a pale peach. +// Knob 2: Speed. At 2 the pen writes 55 LEDs a second, so 人 takes 2.0 s and 手 3.6 s, then the change takes 3.2 s, the other part slides in in 0.9 s and each character holds 1.8 s: a set of two characters takes 12.3 s (人) to 13.9 s (手) and the seven come round in 92.5 s. Ten times slower at 0.2, half as fast again at 3. +// Knob 3: Set. 0 goes through the seven in turn: æ°Ž to æ°µ for 江 and æ²³, 人 to 亻 for 䌑 and 他, 手 to 扌 for 打 and 拉, 心 to 忄 for 忙 and å¿«, 火 to 灬 at the bottom for 杰 and 点, 刀 to 刂 at the right for 刊 and 利, 犬 to 犭 for 狂 and 狗. 1 to 7 keep one of them. The panel opens on æ°Ž just written, about to change. +// Knob 4: Morph. Up to 0.1 the change runs by itself. Past 0.1 the knob holds it by hand: at 0.1 the strokes stand in the full form in the top square, at 0.5 the first of them are halfway down and halfway changed, at 0.86 the radical has docked, and from 0.82 to 1 the other part slides in beside it. + +// Every component is a list of strokes on a 16 by 16 square, y down, in +// stroke order, and the radical forms and the other parts sit where they go +// in the finished character. A stroke is h (horizontal), v (vertical), p +// (left-falling), n (right-falling), d (dot), t (rising), hz (horizontal +// then down), hp (horizontal then left-falling), vz (vertical then right), +// vg (vertical with a hook), hzg (horizontal, down, hook), wg (curved hook), +// wo (lying hook) or vwg (vertical, bend, hook up). p, n, d and t with three +// points are quadratic curves through a control point; wg, wo and vwg are +// rounded except at the hook. Every stroke is resampled to 24 points by arc +// length, so any stroke can move point by point into any other. +// +// The changes are this pattern's, as a picture of what the side form keeps: +// æ°Ž's short upper stroke becomes the top dot of æ°µ, its 暪撇 the middle dot, +// its right-falling stroke turns end for end into the rising stroke and the +// vertical hook melts into it. 人's right-falling stroke stands up as the +// vertical of 亻. 手's first two strokes merge into the one horizontal of 扌 +// and its second horizontal tilts into the rising stroke. 心's lying hook +// stands up as the vertical of 忄 and its two right dots merge into one. 火's +// four strokes each become a dot of 灬. 刀's 暪折钩 straightens into the +// hooked vertical of 刂 and its left-falling stroke into the short one. 犬's +// right-falling stroke curls into the curved hook of 犭 and its horizontal +// melts into the dot that becomes the top stroke. + +const W = 64, H = 128, N = W * H; +const { floor, round, min, max, abs, hypot } = Math; +const cl = (v, a, b) => (v < a ? a : v > b ? b : v); +const ease = (u) => u * u * (3 - 2 * u); +const mix = (a, b, u) => [a[0] + (b[0] - a[0]) * u, a[1] + (b[1] - a[1]) * u, a[2] + (b[2] - a[2]) * u]; + +const G = { + shui: [["vg", 8.2, 1, 8.2, 14.8, 6.4, 13.2], ["hp", 2, 5.4, 6.6, 5.4, 4.8, 9.4, 1.2, 12.8], ["p", 13.8, 3.2, 12.6, 5.4, 9.6, 7.4], ["n", 9.2, 6.6, 10.8, 11, 15.2, 14.4]], + shui3: [["d", 1.4, 2.2, 2.6, 2.9, 3.8, 4.4], ["d", 0.8, 6.8, 2.1, 7.5, 3.3, 9], ["t", 1, 14.4, 2.4, 12.4, 4.8, 9.8]], + gong: [["h", 7.6, 3.4, 14.4, 3.4], ["v", 11, 3.4, 11, 13.8], ["h", 6.6, 13.8, 15.2, 13.8]], + ke: [["h", 6.4, 2.6, 15.4, 2.6], ["v", 7.8, 5.6, 7.8, 10.8], ["hz", 7.8, 5.6, 11.8, 5.6, 11.8, 10.4], ["h", 7.8, 10.2, 11.8, 10.2], ["vg", 13.9, 2.6, 13.9, 15.2, 12, 13.8]], + ren: [["p", 8.4, 1, 8, 9.6, 1.2, 15], ["n", 8, 6, 9.8, 11.6, 15, 15]], + ren2: [["p", 4.6, 1, 3.8, 4.6, 0.8, 8.2], ["v", 3.2, 5.4, 3.2, 15.2]], + mu: [["h", 6.6, 5.2, 15.4, 5.2], ["v", 11, 1, 11, 15.4], ["p", 10.6, 5.6, 9.6, 10, 6.6, 13.4], ["d", 11.6, 6.4, 12.8, 8.6, 15.2, 11.2]], + ye: [["hzg", 7, 7.6, 14, 6, 13.8, 10.4, 12.6, 9.6], ["v", 10.2, 2.4, 10.2, 11.2], ["vwg", 7.6, 3.6, 7.6, 13.6, 8.8, 14.9, 14.6, 14.9, 15.2, 12.6]], + shou: [["p", 12.8, 0.8, 8, 2.8, 3, 3.8], ["h", 3.4, 6.8, 12.6, 6.8], ["h", 1.4, 10.6, 14.6, 10.6], ["vg", 8, 2.6, 8, 15, 6.2, 13.6]], + shou3: [["h", 0.8, 5.4, 6.2, 5.2], ["vg", 3.6, 1, 3.6, 15, 2, 13.6], ["t", 0.6, 11.6, 3.2, 10.2, 6.2, 8.4]], + ding: [["h", 7, 3.2, 15.2, 3.2], ["vg", 11.6, 3.2, 11.6, 15, 9.8, 13.6]], + li: [["d", 10.4, 1, 11.1, 1.9, 11.8, 3.4], ["h", 7.6, 4.8, 14.8, 4.8], ["d", 8.8, 7.2, 9.4, 9.4, 10, 11.6], ["p", 13.8, 7, 13.2, 9.6, 12.2, 11.8], ["h", 7, 14.4, 15.4, 14.4]], + xin: [["d", 2.4, 6.8, 2.8, 8.4, 2.2, 10.4], ["wo", 4.6, 4.8, 5.2, 11.4, 7.4, 14.2, 12.4, 13.6, 10.8, 10.8], ["d", 7.4, 2.2, 8.6, 3.4, 9.4, 5.4], ["d", 12.2, 5.2, 13.8, 6.8, 15, 9.2]], + xin3: [["d", 1, 5, 1.4, 6.6, 1.4, 8.4], ["d", 4.8, 4.4, 5.6, 5, 6.2, 6.2], ["v", 3.4, 1, 3.4, 15.2]], + wang2: [["d", 10.6, 1, 11.4, 2, 12, 3.4], ["h", 7, 4.8, 15.2, 4.8], ["vz", 8.6, 4.8, 8.6, 14, 15.2, 14]], + guai: [["hz", 8, 4.6, 13.8, 4.6, 13.8, 8.6], ["h", 6.6, 8.6, 15.4, 8.6], ["p", 10.8, 1.4, 10.4, 10.4, 7, 15], ["n", 11.4, 10.2, 12.8, 12.6, 15.2, 15]], + huo: [["d", 3, 4.6, 3.8, 6.4, 4.8, 7.8], ["p", 12.8, 4, 12.4, 6, 11, 7.8], ["p", 8.2, 1, 8, 10.2, 1.2, 15.2], ["n", 8.4, 8.8, 10, 12.8, 15, 15.2]], + huo4: [["p", 2.8, 12, 2.4, 13.6, 1.2, 15], ["d", 5.6, 12.2, 6, 13.6, 6.9, 15], ["d", 9.3, 12.2, 9.8, 13.6, 10.7, 15], ["d", 12.8, 12, 13.6, 13.4, 15, 15]], + mutop: [["h", 1.4, 3.8, 14.6, 3.8], ["v", 8, 0.8, 8, 10.6], ["p", 7.6, 4.2, 6, 7.4, 1.4, 9.8], ["n", 8.4, 4.2, 10, 7.4, 14.6, 9.8]], + zhan: [["v", 6.4, 0.8, 6.4, 6.6], ["h", 6.4, 3.6, 11.8, 3.6], ["v", 3.2, 6.6, 3.2, 10.8], ["hz", 3.2, 6.6, 12.8, 6.6, 12.8, 10.6], ["h", 3.2, 10.4, 12.8, 10.4]], + dao: [["hzg", 2.4, 3.6, 13.2, 3.6, 13, 14.8, 10.8, 13.2], ["p", 8.2, 3.8, 7.6, 10.6, 1.4, 15]], + dao2: [["v", 11, 3.2, 11, 10.6], ["vg", 14.4, 1, 14.4, 15, 12.6, 13.4]], + gan: [["h", 1.6, 3.4, 8.6, 3.4], ["h", 0.8, 8.2, 9.4, 8.2], ["v", 5.1, 3.4, 5.1, 15.2]], + he: [["p", 8.4, 0.8, 5.6, 2.2, 1.6, 3.2], ["h", 1, 6.2, 9.4, 6.2], ["v", 5.1, 2.6, 5.1, 15.2], ["p", 4.7, 6.6, 3.6, 9.6, 0.8, 12.8], ["d", 5.6, 7.4, 7, 8.6, 8.9, 10.4]], + quan: [["h", 1.4, 6, 14.6, 6], ["p", 8, 1, 7.8, 10.2, 1.4, 15.2], ["n", 8.2, 6.6, 9.8, 12, 15, 15], ["d", 11.6, 1.8, 12.6, 2.6, 13.4, 3.8]], + quan3: [["p", 5, 1, 4, 2.8, 1.6, 4.6], ["wg", 2, 3, 3.8, 4.6, 4.6, 8.4, 4, 14.6, 2.2, 13.2], ["p", 5.6, 7.4, 3.8, 10.4, 0.8, 13.2]], + wang: [["h", 7.4, 2.8, 14.6, 2.8], ["h", 8, 8.4, 14, 8.4], ["v", 11, 2.8, 11, 14], ["h", 6.8, 14, 15.2, 14]], + gou: [["p", 9.6, 1, 8.8, 3, 6.8, 5.2], ["hzg", 8.6, 3.6, 14.6, 3.6, 14.2, 14.6, 12.4, 13.4], ["v", 8.9, 6.8, 8.9, 11.6], ["hz", 8.9, 6.8, 12, 6.8, 12, 11.2], ["h", 8.9, 11.2, 12, 11.2]], +}; + +// Full form, side form, where each full stroke goes ([side stroke, 1 turned +// end for end, 2 melting into a stroke another one becomes]), the side the +// radical docks on, and the two other parts. +const SETS = [ + ["shui", "shui3", [[2, 2], [1, 0], [0, 0], [2, 1]], "L", ["gong", "ke"]], + ["ren", "ren2", [[0, 0], [1, 0]], "L", ["mu", "ye"]], + ["shou", "shou3", [[0, 0], [0, 2], [2, 0], [1, 0]], "L", ["ding", "li"]], + ["xin", "xin3", [[0, 0], [2, 0], [1, 0], [1, 2]], "L", ["wang2", "guai"]], + ["huo", "huo4", [[1, 0], [2, 0], [0, 0], [3, 0]], "B", ["mutop", "zhan"]], + ["dao", "dao2", [[1, 0], [0, 0]], "R", ["gan", "he"]], + ["quan", "quan3", [[0, 2], [2, 0], [1, 0], [0, 0]], "L", ["wang", "gou"]], +]; +const HUES = [0.12, 0.55, 0.9, 0.32]; +// The squares: 62 LEDs, at rows 1 and 65; stroke weights in LEDs. +const SQ = 62, SY = [1, 65], WFULL = 3.8, WPART = 3.3, K = 24; +// Seconds at Speed 2. +const PEN = 55, REST = 0.12, T_PAUSE = 0.5, T_MORPH = 3.2, T_SLIDE = 0.9, T_HOLD = 1.8, T_SWAP = 1.3, T_CLEAR = 0.7; + +function profile(type, s) { + if (type === "p") return 1.05 - 0.65 * s; + if (type === "n") return s < 0.8 ? 0.55 + (0.85 * s) / 0.8 : 1.4 - 4.5 * (s - 0.8); + if (type === "d") return 0.75 + 0.6 * s; + if (type === "t") return 1.25 - 0.85 * s; + if (type === "h") return 0.95 + 0.15 * s; + if (type === "hp") return s < 0.45 ? 1 : 1.05 - 0.9 * (s - 0.45); + if (type[0] === "w" || type.endsWith("g")) return s < 0.85 ? 1 : 1 - 4 * (s - 0.85); + return 1; +} + +// A stroke as K points [x, y, width share] on the 16 by 16 square. +const SHAPES = new Map(); +function shape(st) { + if (SHAPES.has(st)) return SHAPES.get(st); + const [type, ...c] = st; + let pts = []; + if (c.length === 6 && "pndt".includes(type)) { + for (let i = 0; i <= 16; i++) { + const s = i / 16, a = (1 - s) * (1 - s), b = 2 * s * (1 - s), d = s * s; + pts.push([a * c[0] + b * c[2] + d * c[4], a * c[1] + b * c[3] + d * c[5]]); + } + } else { + for (let i = 0; i < c.length; i += 2) pts.push([c[i], c[i + 1]]); + // Chaikin's corner cutting, three times, rounds a curved stroke. + if (type[0] === "w" || type === "vwg") { + const hook = pts.pop(); + for (let it = 0; it < 3; it++) { + const r = [pts[0]]; + for (let i = 0; i + 1 < pts.length; i++) r.push(mix(pts[i], pts[i + 1], 0.25), mix(pts[i], pts[i + 1], 0.75)); + r.push(pts[pts.length - 1]); + pts = r; + } + pts.push(hook); + } + } + const L = [0]; + for (let i = 1; i < pts.length; i++) L.push(L[i - 1] + hypot(pts[i][0] - pts[i - 1][0], pts[i][1] - pts[i - 1][1])); + const T = L[L.length - 1], out = []; + for (let k = 0, j = 0; k < K; k++) { + const sl = (T * k) / (K - 1); + while (j < pts.length - 2 && L[j + 1] < sl) j++; + const u = (sl - L[j]) / (L[j + 1] - L[j] || 1); + out.push([pts[j][0] + (pts[j + 1][0] - pts[j][0]) * u, pts[j][1] + (pts[j + 1][1] - pts[j][1]) * u, profile(type, k / (K - 1))]); + } + out.len = (T / 16) * (SQ - 2); + SHAPES.set(st, out); + return out; +} + +// The square's grid point (gx, gy) on the panel. +const px = (gx) => 2 + (gx / 16) * (SQ - 2); +const py = (gy, row) => SY[row] + 1 + (gy / 16) * (SQ - 2); + +function hsv(h, s, v) { + h = (h - floor(h)) * 6; + const i = floor(h) % 6, f = h - floor(h), p = v * (1 - s), q = v * (1 - s * f), t = v * (1 - s * (1 - f)); + return [[v, t, p], [q, v, p], [p, v, t], [p, q, v], [t, p, v], [v, p, q]][i]; +} + +// A stroke from panel points [x, y, width]: coverage of the capsules +// between them, laid over what is there. +const COV = new Float32Array(N); +function ink(buf, P, col, alpha, y0) { + const touched = []; + for (let i = 0; i + 1 < P.length; i++) { + const [ax, ay, aw] = P[i], [bx, by, bw] = P[i + 1], dx = bx - ax, dy = by - ay, l2 = dx * dx + dy * dy, r = max(aw, bw) / 2 + 1; + for (let y = max(y0, floor(min(ay, by) - r)); y <= min(H - 1, floor(max(ay, by) + r)); y++) + for (let x = max(0, floor(min(ax, bx) - r)); x <= min(W - 1, floor(max(ax, bx) + r)); x++) { + const qx = x + 0.5 - ax, qy = y + 0.5 - ay, u = l2 > 0 ? cl((qx * dx + qy * dy) / l2, 0, 1) : 0; + const c = cl((aw + (bw - aw) * u) / 2 + 0.5 - hypot(qx - u * dx, qy - u * dy), 0, 1), p = y * W + x; + if (c <= 0) continue; + if (COV[p] === 0) touched.push(p); + if (c > COV[p]) COV[p] = c; + } + } + for (const p of touched) { + const a = COV[p] * alpha; + for (let q = 0; q < 3; q++) buf[p * 3 + q] += (col[q] - buf[p * 3 + q]) * a; + COV[p] = 0; + } +} + +// One set's clock: writing, pause, change, slide, hold, swap, hold, clear. +function plan(si) { + const full = G[SETS[si][0]]; + let t = 0; + const strokes = full.map((st) => { + const s = { t0: t, t1: t + shape(st).len / PEN }; + t = s.t1 + REST; + return s; + }); + const m0 = t + T_PAUSE, m1 = m0 + T_MORPH, s1 = m1 + T_SLIDE, h1 = s1 + T_HOLD, w1 = h1 + T_SWAP, h2 = w1 + T_HOLD; + return { strokes, m0, m1, s1, h1, w1, h2, total: h2 + T_CLEAR }; +} + +export function setup(params) { + params.s = { si: 0, setKnob: -1, t: 0, P: null, hue: 0, hand: -1, buf: new Float32Array(N * 3) }; +} + +export function update(dt, input, params) { + if (!params.s) setup(params); + const S = params.s, k = input.knobValues || [0, 2, 0, 0.06], d = cl(dt || 0, 0, 0.1); + S.hue = k[0] || 0; + const speed = cl(k[1] == null ? 2 : k[1], 0.2, 3), setKnob = cl(floor(k[2] || 0), 0, 7), morph = cl(k[3] == null ? 0.06 : k[3], 0, 1); + if (setKnob !== S.setKnob) { + // The panel opens on the component just written, about to change. + S.setKnob = setKnob; + S.si = setKnob ? setKnob - 1 : 0; + S.P = plan(S.si); + S.t = S.P.m0 - 0.3; + } + S.hand = morph > 0.1 ? (morph - 0.1) / 0.9 : -1; + if (S.hand >= 0) return; + S.t += (d * speed) / 2; + if (S.t >= S.P.total) { + S.si = setKnob ? setKnob - 1 : (S.si + 1) % SETS.length; + S.P = plan(S.si); + S.t = 0; + } +} + +export function draw(display, params) { + const S = params.s; + if (!S || !S.P) return; + const buf = S.buf, P = S.P, [fullName, sideName, map, side, parts] = SETS[S.si]; + const full = G[fullName], rad = G[sideName], hue = S.hue; + buf.fill(0); + // Where the set stands: a = 0 the full form, 1 the radical docked, then + // the first part in, the swap to the second, and the clearing. + let t = S.t, a, slide = 0, which = 0, out = 0, fade = 1; + if (S.hand >= 0) { + t = P.m0; + a = cl(S.hand / 0.85, 0, 1); + slide = cl((S.hand - 0.8) / 0.2, 0, 1); + } else { + a = cl((t - P.m0) / T_MORPH, 0, 1); + slide = cl((t - P.m1) / T_SLIDE, 0, 1); + if (t > P.h1) { + const u = (t - P.h1) / T_SWAP; + out = cl(u / 0.4, 0, 1); + if (u > 0.4) [which, out, slide] = [1, 0, cl((u - 0.4) / 0.6, 0, 1)]; + } + if (t > P.h2) fade = 1 - cl((t - P.h2) / T_CLEAR, 0, 1); + } + const plain = hsv(hue + 0.58, 0.16, 0.95), grid = hsv(hue + 0.6, 0.3, 0.22); + const tint = (i) => hsv(hue + HUES[i % 4], 0.8, 1); + // Practice squares: dashed edges and a dashed cross. + for (const row of [0, 1]) + for (let i = 0; i < SQ; i += 2) { + const y0 = SY[row]; + for (const [x, y] of [[1 + i, y0], [1 + i, y0 + SQ - 1], [1, y0 + i], [SQ, y0 + i], [1 + i, y0 + SQ / 2], [1 + SQ / 2, y0 + i]]) for (let q = 0; q < 3; q++) buf[(y * W + x) * 3 + q] = grid[q]; + } + const toPanel = (sh, row, wgt, dx, dy) => sh.map(([x, y, w]) => [px(x) + dx, py(y, row) + dy, w * wgt]); + // The full form in the top square: written by the pen, then left behind in + // its stroke colours, dimmed, once its copy has gone. + const leave = cl(a / 0.15, 0, 1); + full.forEach((st, i) => { + const sh = shape(st), s = P.strokes[i]; + const r = S.hand >= 0 ? 1 : cl((t - s.t0) / (s.t1 - s.t0), 0, 1); + if (r <= 0) return; + const n = max(2, Math.ceil(r * (K - 1)) + 1), pts = toPanel(sh.slice(0, n), 0, WFULL, 0, 0); + // The last point stops where the pen is. + const f = r * (K - 1) - (n - 2), q = sh[n - 1], p0 = sh[n - 2]; + pts[n - 1] = [px(p0[0] + (q[0] - p0[0]) * f), py(p0[1] + (q[1] - p0[1]) * f, 0), (p0[2] + (q[2] - p0[2]) * f) * WFULL]; + ink(buf, pts, mix(plain, mix(mix(tint(i), [1, 1, 1], 0.2), [0, 0, 0], 0.36), leave), fade, 0); + if (r < 1) { + const [hx, hy] = pts[n - 1]; + for (let y = floor(hy) - 2; y <= floor(hy) + 2; y++) + for (let x = floor(hx) - 2; x <= floor(hx) + 2; x++) { + if (x < 0 || x >= W || y < 0 || y >= H) continue; + const w = cl(1.3 - ((x + 0.5 - hx) ** 2 + (y + 0.5 - hy) ** 2) / 3, 0, 1); + for (let c = 0; c < 3; c++) buf[(y * W + x) * 3 + c] += (1 - buf[(y * W + x) * 3 + c]) * w; + } + } + }); + // The copy on its way down, each stroke a little after the one before, + // moving point by point from its full shape to its radical shape. + if (a > 0) { + const n = full.length, lag = 0.1; + full.forEach((st, i) => { + const [j, how] = map[i], e = ease(cl((a - lag * i) / (1 - lag * (n - 1)), 0, 1)); + const from = shape(st), to = how === 1 ? [...shape(rad[j])].reverse() : shape(rad[j]); + // A stroke that melts thins away at full colour; faded, gold goes brown. + const thin = how === 2 ? 1 - cl((e - 0.7) / 0.3, 0, 1) : 1; + const pts = from.map((p, k) => { + const q = to[k]; + return [px(p[0] + (q[0] - p[0]) * e), py(p[1], 0) + (py(q[1], 1) - py(p[1], 0)) * e, (p[2] * WFULL + (q[2] * WPART - p[2] * WFULL) * e) * thin]; + }); + if (thin > 0.05) ink(buf, pts, tint(i), fade * cl(a / 0.05, 0, 1), 0); + }); + } + // The other part slides in from the side the radical leaves open. + if (slide > 0 || out > 0) { + const part = G[parts[which]], u = out > 0 ? 1 - ease(out) : ease(slide), off = (1 - u) * 46; + const dx = side === "L" ? off : side === "R" ? -off : 0, dy = side === "B" ? -off : 0; + for (const st of part) ink(buf, toPanel(shape(st), 1, WPART, dx, dy), plain, u * fade, SY[1]); + } + for (let p = 0; p < N; p++) display.setPixel(p % W, floor(p / W), round(min(1, buf[p * 3]) * 255), round(min(1, buf[p * 3 + 1]) * 255), round(min(1, buf[p * 3 + 2]) * 255)); +} From 5c341abbf190e10881b480e5481f96d5a3893456 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 09:12:03 +0000 Subject: [PATCH 8/9] =?UTF-8?q?=ED=8C=A8=ED=84=B4=20=EC=97=B0=EA=B5=AC=201?= =?UTF-8?q?02=ED=8C=90:=20README=20=EC=97=90=20102=ED=8C=90=20=ED=91=9C?= =?UTF-8?q?=EC=99=80=20=EB=85=B8=ED=8A=B8,=20=EB=8B=A4=EC=9D=8C=20?= =?UTF-8?q?=ED=8C=90=20=EA=B0=90=EC=9D=84=20=EC=A0=81=EB=8A=94=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Bc4JkshDz129z5fXhUWQxz --- incoming/pattern-study/README.md | 107 +++++++++++++++++++++++++++++-- 1 file changed, 101 insertions(+), 6 deletions(-) diff --git a/incoming/pattern-study/README.md b/incoming/pattern-study/README.md index eaf19723..ececf1cb 100644 --- a/incoming/pattern-study/README.md +++ b/incoming/pattern-study/README.md @@ -1,6 +1,6 @@ # 팹턮 연구 2026-10 -새 팹턮 748개륌 쉰여섯 갈래로 나눠 만듀었얎요. 1판에서 엎섞 갈래로 23개륌 만듀고, 2판에서 1판에 없던 아홉 갈래로 16개륌, 3판에서 하나륌, 4판에서 새 갈래읞 직묌로 하나륌 더했얎요. 5판은 새 팹턮 없읎 1판의 Euclid Pulse 륌 닀시 ì§°ê³ , 6판은 섞욎 팚널의 128쀄을 위아래로 ë‹€ 쓰는 팹턮 하나륌 더했얎요. 7판은 또렷한 몚양읎 멀늬 흘러가는 지도 칞에 팹턮 하나륌 더했얎요. 8판은 새 갈래읞 Ʞ계로 하나륌 더했얎요. 9판은 새 팹턮 없읎 1판의 Sprout 을 닀시 ì§°ì–Žìš”. 10판은 섞욎 팚널을 떚얎지는 랔록의 우묌로 쓰는 팹턮 하나륌 더했얎요. 11판은 여러 색읎 ꜀ 멀늬 흘러가는 지도 칞에 팹턮 하나륌 더했얎요. 12판은 새 갈래읞 천첎로 하나륌 더했얎요. 13판은 새 팹턮 없읎 1판의 Glyph Rain 을 닀시 ì§°ì–Žìš”. 14판은 절찚가 바뀌Ʞ 전에 띄욎 바퀎띌서, 섞욎 팚널의 높읎륌 ë‹€ 떚얎지는 폭포 하나륌 더했얎요. 15판은 바뀐 절찚의 첫 판읎띌 ì—Ž 갈래에 새 팚턎을 하나씩, 몚두 ì—Ž 개륌 더했얎요. 도시와 음악 갈래는 귞때 새로 엎었얎요. 16판도 ì—Ž 갈래에 하나씩 ì—Ž 개륌 더했고, 바닀와 곀충 갈래륌 새로 엎었얎요. 17판도 ì—Ž 갈래에 하나씩 ì—Ž 개륌 더했고, 전자 회로와 장난감 갈래륌 새로 엎었얎요. 18판도 ì—Ž 갈래에 하나씩 ì—Ž 개륌 더했고, 유늬와 ꎑ묌·결정, 놀읎공원 갈래륌 새로 엎었얎요. 19판도 ì—Ž 갈래에 하나씩 ì—Ž 개륌 더했고, 부엌곌 버섯·균사 갈래륌 새로 엎었얎요. 20판도 ì—Ž 갈래에 하나씩 ì—Ž 개륌 더했고, 새와 화학·싀험 갈래륌 새로 엎었얎요. 21판도 ì—Ž 갈래에 하나씩 ì—Ž 개륌 더했고, 종읎와 욎동·겜Ʞ 갈래륌 새로 엎었얎요. 22판도 ì—Ž 갈래에 하나씩 ì—Ž 개륌 더했고, 몞곌 비행 갈래륌 새로 엎었얎요. 23판도 ì—Ž 갈래에 하나씩 ì—Ž 개륌 더했고, 묎대·공연곌 짐승 갈래륌 새로 엎었얎요. 24판도 ì—Ž 갈래에 하나씩 ì—Ž 개륌 더했고, 공사·걎섀곌 도자Ʞ 갈래륌 새로 엎었얎요. 25판도 ì—Ž 갈래에 하나씩 ì—Ž 개륌 더했고, 농사·밭곌 화산 갈래륌 새로 엎었얎요. 26판도 ì—Ž 갈래에 하나씩 ì—Ž 개륌 더했고, 얌음·극지와 믞생묌 갈래륌 새로 엎었얎요. 27판도 ì—Ž 갈래에 하나씩 ì—Ž 개륌 더했고, 철도와 필늄·영화 갈래륌 새로 엎었얎요. 28판은 죌제 표가 수학 쪜윌로 바뀐 첫 판읎띌 읎늄 있는 수학 대상 ì—Ž 개륌 ì—Ž 갈래에 하나씩 더했고, 수론 갈래륌 새로 엎었얎요. 29판은 죌제 표가 수학 여섯곌 화장품 팝업 넷윌로 나뉜 첫 판읎띌, 읎늄 있는 수학 대상 여섯곌 화장품 맀장 벜에 틀 질감 넷을 더했고, 위상·맀듭곌 화장품 팝업 갈래륌 새로 엎었얎요. 30판도 같은 표로 돌아서 읎늄 있는 수학 대상 여섯을 여섯 갈래에 하나씩 더하고, 팝업 맀장의 LED 벜에 틀 질감 넷을 화장품 팝업 갈래에 더했얎요. 31판도 같은 표로 돌아서 읎늄 있는 수학 대상 여섯을 여섯 갈래에 하나씩, 맀장 벜 질감 넷을 화장품 팝업 갈래에 더했고, 새 갈래는 ì—Žì§€ 않았얎요. 32판도 같은 표로 돌아서 수학 대상 여섯을 여섯 갈래에 하나씩, 맀장 벜의 질감 넷을 화장품 팝업 갈래에 더했얎요. 33판도 같은 표로 돌아서 수학 대상 여섯을 여섯 갈래에 하나씩, 맀장 벜 질감 넷을 화장품 팝업 갈래에 더했얎요. 34판도 수학 대상 여섯을 여섯 갈래에 하나씩, 맀장 벜 질감 넷을 화장품 팝업 갈래에 더했얎요. 35판도 수학 대상 여섯을 여섯 갈래에 하나씩, 맀장 벜 질감 넷을 화장품 팝업 갈래에 더했얎요. 36판도 수학 대상 여섯을 여섯 갈래에 하나씩, 맀장 벜 질감 넷을 화장품 팝업 갈래에 더했얎요. 37판도 수학 대상 여섯을 여섯 갈래에 하나씩, 맀장 벜 질감 넷을 화장품 팝업 갈래에 더했얎요. 38판도 수학 대상 여섯을 여섯 갈래에 하나씩, 맀장 벜 질감 넷을 화장품 팝업 갈래에 더했얎요. 39판도 수학 대상 여섯을 여섯 갈래에 하나씩, 맀장 벜 질감 넷을 화장품 팝업 갈래에 더했얎요. 40판도 수학 대상 여섯을 여섯 갈래에 하나씩, 맀장 벜 질감 넷을 화장품 팝업 갈래에 더했얎요. 41판은 죌제 표가 수학·추상 닀섯곌 할로윈 닀섯윌로 바뀐 첫 판읎띌, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더하고 할로윈 갈래륌 새로 ì—Žì–Ž 10월 31음에 틀 팹턮 닀섯을 넣었얎요. 42판도 같은 표로 돌아서 수학 대상 닀섯을 닀섯 갈래에 하나씩 더하고, 할로윈 밀에 틀 귞늌 닀섯을 41판읎 연 할로윈 갈래에 더했얎요. 43판도 같은 표로 돌아서 수학 대상 닀섯을 닀섯 갈래에 하나씩, 할로윈 귞늌 닀섯을 할로윈 갈래에 더했얎요. 44판도 같은 표로 돌아서 수학 대상 닀섯을 닀섯 갈래에 하나씩 더하고, 할로윈 밀에 틀 귞늌 닀섯을 할로윈 갈래에 더했얎요. 45판도 같은 표로 돌아서 수학 대상 닀섯을 팚턎읎 ì—Ž 개 아래읞 닀섯 갈래에 하나씩, 할로윈 귞늌 닀섯을 할로윈 갈래에 더했얎요. 46판도 같은 표로 돌아서 수학 대상 닀섯을 닀섯 갈래에 하나씩, 할로윈 귞늌 닀섯을 할로윈 갈래에 더했얎요. 47판도 같은 표로 돌아서 수학 대상 닀섯을 닀섯 갈래에 하나씩, 할로윈 밀에 틀 귞늌 닀섯을 할로윈 갈래에 더했얎요. 48판도 같은 표로 돌아서 수학 대상 닀섯을 닀섯 갈래에 하나씩, 할로윈 귞늌 닀섯을 할로윈 갈래에 더했얎요. 49판도 같은 표로 돌아서 수학 대상 닀섯을 닀섯 갈래에 하나씩, 할로윈 귞늌 닀섯을 할로윈 갈래에 더했얎요. 50판도 같은 표로 돌아서 수학 대상 닀섯을 팚턎읎 적은 닀섯 갈래에 하나씩, 할로윈 밀에 틀 귞늌 닀섯을 할로윈 갈래에 더했얎요. 51판도 같은 표로 돌아서 수학 대상 닀섯을 닀섯 갈래에 하나씩, 할로윈 밀에 틀 귞늌 닀섯을 할로윈 갈래에 더했얎요. 52판도 같은 표로 돌아서 수학 대상 닀섯을 팚턎읎 적은 닀섯 갈래에 하나씩, 할로윈 귞늌 닀섯을 할로윈 갈래에 더했얎요. 53판은 할로윈 몫읎 쉬게 된 첫 판읎띌, 수학 대상 ì—Ž 개륌 ì—Ž 갈래에 하나씩 더했얎요. 54판도 수학 대상 ì—Ž 개륌 팚턎읎 적은 ì—Ž 갈래에 하나씩 더했얎요. 55판도 수학 대상 ì—Ž 개륌 ì—Ž 갈래에 하나씩 더했얎요. 56판도 수학 대상 ì—Ž 개륌 ì—Ž 갈래에 하나씩 더했얎요. 57판도 수학 대상 ì—Ž 개륌 팚턎읎 적은 ì—Ž 갈래에 하나씩 더했얎요. 58판도 수학 대상 ì—Ž 개륌 팚턎읎 적은 ì—Ž 갈래에 하나씩 더했얎요. 59판도 수학 대상 ì—Ž 개륌 ì—Ž 갈래에 하나씩 더했얎요. 60판도 수학 대상 ì—Ž 개륌 ì—Ž 갈래에 하나씩 더했얎요. 61판도 수학 대상 ì—Ž 개륌 팚턎읎 적은 갈래부터 ì—Ž 갈래에 하나씩 더했얎요. 62판도 수학 대상 ì—Ž 개륌 ì—Ž 갈래에 하나씩 더했얎요. 63판은 64판곌 같읎 죌제 표에 「타읎포」가 듀얎옚 첫 판읎띌, Ꞁ자가 듀얎간 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 64판은 죌제 표가 바뀌얎 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 ë„€ 갈래에 더했얎요. 65판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 66판도 같은 표로 돌아서 Ꞁ자 팹턮 넷을 Ꞁ자 갈래에, 하나륌 데읎터 귞늌 갈래에 더하고, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 67판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 68판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 69판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 70판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 71판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 72판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 73판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 74판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 75판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 76판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 77판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 78판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 79판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 80판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 81판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 82판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 83판부터 99판까지는 작품 하나륌 깎는 공듀읎Ʞ 판읎었얎요. 100판은 닀시 ì—Ž 개륌 만드는 판윌로 돌아와, Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 101판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. [index.html](index.html) 을 람띌우저로 엎멎 전부 한 장에서 돌아가요. ë§š 위 지도에서 점을 누륎멎 ê·ž 팚턎윌로 가고, 칎드의 슬띌읎더가 녾뾌 넷읎에요. 마음에 드는 것은 「윔드 복사」로 가젞가 Pattern Lab 의 CODE 칞에 붙여 넣윌멎 같은 귞늌읎 나와요. 페읎지는 파음 하나띌서 바깥에서 받아 였는 것읎 없얎요. +새 팹턮 758개륌 쉰여섯 갈래로 나눠 만듀었얎요. 1판에서 엎섞 갈래로 23개륌 만듀고, 2판에서 1판에 없던 아홉 갈래로 16개륌, 3판에서 하나륌, 4판에서 새 갈래읞 직묌로 하나륌 더했얎요. 5판은 새 팹턮 없읎 1판의 Euclid Pulse 륌 닀시 ì§°ê³ , 6판은 섞욎 팚널의 128쀄을 위아래로 ë‹€ 쓰는 팹턮 하나륌 더했얎요. 7판은 또렷한 몚양읎 멀늬 흘러가는 지도 칞에 팹턮 하나륌 더했얎요. 8판은 새 갈래읞 Ʞ계로 하나륌 더했얎요. 9판은 새 팹턮 없읎 1판의 Sprout 을 닀시 ì§°ì–Žìš”. 10판은 섞욎 팚널을 떚얎지는 랔록의 우묌로 쓰는 팹턮 하나륌 더했얎요. 11판은 여러 색읎 ꜀ 멀늬 흘러가는 지도 칞에 팹턮 하나륌 더했얎요. 12판은 새 갈래읞 천첎로 하나륌 더했얎요. 13판은 새 팹턮 없읎 1판의 Glyph Rain 을 닀시 ì§°ì–Žìš”. 14판은 절찚가 바뀌Ʞ 전에 띄욎 바퀎띌서, 섞욎 팚널의 높읎륌 ë‹€ 떚얎지는 폭포 하나륌 더했얎요. 15판은 바뀐 절찚의 첫 판읎띌 ì—Ž 갈래에 새 팚턎을 하나씩, 몚두 ì—Ž 개륌 더했얎요. 도시와 음악 갈래는 귞때 새로 엎었얎요. 16판도 ì—Ž 갈래에 하나씩 ì—Ž 개륌 더했고, 바닀와 곀충 갈래륌 새로 엎었얎요. 17판도 ì—Ž 갈래에 하나씩 ì—Ž 개륌 더했고, 전자 회로와 장난감 갈래륌 새로 엎었얎요. 18판도 ì—Ž 갈래에 하나씩 ì—Ž 개륌 더했고, 유늬와 ꎑ묌·결정, 놀읎공원 갈래륌 새로 엎었얎요. 19판도 ì—Ž 갈래에 하나씩 ì—Ž 개륌 더했고, 부엌곌 버섯·균사 갈래륌 새로 엎었얎요. 20판도 ì—Ž 갈래에 하나씩 ì—Ž 개륌 더했고, 새와 화학·싀험 갈래륌 새로 엎었얎요. 21판도 ì—Ž 갈래에 하나씩 ì—Ž 개륌 더했고, 종읎와 욎동·겜Ʞ 갈래륌 새로 엎었얎요. 22판도 ì—Ž 갈래에 하나씩 ì—Ž 개륌 더했고, 몞곌 비행 갈래륌 새로 엎었얎요. 23판도 ì—Ž 갈래에 하나씩 ì—Ž 개륌 더했고, 묎대·공연곌 짐승 갈래륌 새로 엎었얎요. 24판도 ì—Ž 갈래에 하나씩 ì—Ž 개륌 더했고, 공사·걎섀곌 도자Ʞ 갈래륌 새로 엎었얎요. 25판도 ì—Ž 갈래에 하나씩 ì—Ž 개륌 더했고, 농사·밭곌 화산 갈래륌 새로 엎었얎요. 26판도 ì—Ž 갈래에 하나씩 ì—Ž 개륌 더했고, 얌음·극지와 믞생묌 갈래륌 새로 엎었얎요. 27판도 ì—Ž 갈래에 하나씩 ì—Ž 개륌 더했고, 철도와 필늄·영화 갈래륌 새로 엎었얎요. 28판은 죌제 표가 수학 쪜윌로 바뀐 첫 판읎띌 읎늄 있는 수학 대상 ì—Ž 개륌 ì—Ž 갈래에 하나씩 더했고, 수론 갈래륌 새로 엎었얎요. 29판은 죌제 표가 수학 여섯곌 화장품 팝업 넷윌로 나뉜 첫 판읎띌, 읎늄 있는 수학 대상 여섯곌 화장품 맀장 벜에 틀 질감 넷을 더했고, 위상·맀듭곌 화장품 팝업 갈래륌 새로 엎었얎요. 30판도 같은 표로 돌아서 읎늄 있는 수학 대상 여섯을 여섯 갈래에 하나씩 더하고, 팝업 맀장의 LED 벜에 틀 질감 넷을 화장품 팝업 갈래에 더했얎요. 31판도 같은 표로 돌아서 읎늄 있는 수학 대상 여섯을 여섯 갈래에 하나씩, 맀장 벜 질감 넷을 화장품 팝업 갈래에 더했고, 새 갈래는 ì—Žì§€ 않았얎요. 32판도 같은 표로 돌아서 수학 대상 여섯을 여섯 갈래에 하나씩, 맀장 벜의 질감 넷을 화장품 팝업 갈래에 더했얎요. 33판도 같은 표로 돌아서 수학 대상 여섯을 여섯 갈래에 하나씩, 맀장 벜 질감 넷을 화장품 팝업 갈래에 더했얎요. 34판도 수학 대상 여섯을 여섯 갈래에 하나씩, 맀장 벜 질감 넷을 화장품 팝업 갈래에 더했얎요. 35판도 수학 대상 여섯을 여섯 갈래에 하나씩, 맀장 벜 질감 넷을 화장품 팝업 갈래에 더했얎요. 36판도 수학 대상 여섯을 여섯 갈래에 하나씩, 맀장 벜 질감 넷을 화장품 팝업 갈래에 더했얎요. 37판도 수학 대상 여섯을 여섯 갈래에 하나씩, 맀장 벜 질감 넷을 화장품 팝업 갈래에 더했얎요. 38판도 수학 대상 여섯을 여섯 갈래에 하나씩, 맀장 벜 질감 넷을 화장품 팝업 갈래에 더했얎요. 39판도 수학 대상 여섯을 여섯 갈래에 하나씩, 맀장 벜 질감 넷을 화장품 팝업 갈래에 더했얎요. 40판도 수학 대상 여섯을 여섯 갈래에 하나씩, 맀장 벜 질감 넷을 화장품 팝업 갈래에 더했얎요. 41판은 죌제 표가 수학·추상 닀섯곌 할로윈 닀섯윌로 바뀐 첫 판읎띌, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더하고 할로윈 갈래륌 새로 ì—Žì–Ž 10월 31음에 틀 팹턮 닀섯을 넣었얎요. 42판도 같은 표로 돌아서 수학 대상 닀섯을 닀섯 갈래에 하나씩 더하고, 할로윈 밀에 틀 귞늌 닀섯을 41판읎 연 할로윈 갈래에 더했얎요. 43판도 같은 표로 돌아서 수학 대상 닀섯을 닀섯 갈래에 하나씩, 할로윈 귞늌 닀섯을 할로윈 갈래에 더했얎요. 44판도 같은 표로 돌아서 수학 대상 닀섯을 닀섯 갈래에 하나씩 더하고, 할로윈 밀에 틀 귞늌 닀섯을 할로윈 갈래에 더했얎요. 45판도 같은 표로 돌아서 수학 대상 닀섯을 팚턎읎 ì—Ž 개 아래읞 닀섯 갈래에 하나씩, 할로윈 귞늌 닀섯을 할로윈 갈래에 더했얎요. 46판도 같은 표로 돌아서 수학 대상 닀섯을 닀섯 갈래에 하나씩, 할로윈 귞늌 닀섯을 할로윈 갈래에 더했얎요. 47판도 같은 표로 돌아서 수학 대상 닀섯을 닀섯 갈래에 하나씩, 할로윈 밀에 틀 귞늌 닀섯을 할로윈 갈래에 더했얎요. 48판도 같은 표로 돌아서 수학 대상 닀섯을 닀섯 갈래에 하나씩, 할로윈 귞늌 닀섯을 할로윈 갈래에 더했얎요. 49판도 같은 표로 돌아서 수학 대상 닀섯을 닀섯 갈래에 하나씩, 할로윈 귞늌 닀섯을 할로윈 갈래에 더했얎요. 50판도 같은 표로 돌아서 수학 대상 닀섯을 팚턎읎 적은 닀섯 갈래에 하나씩, 할로윈 밀에 틀 귞늌 닀섯을 할로윈 갈래에 더했얎요. 51판도 같은 표로 돌아서 수학 대상 닀섯을 닀섯 갈래에 하나씩, 할로윈 밀에 틀 귞늌 닀섯을 할로윈 갈래에 더했얎요. 52판도 같은 표로 돌아서 수학 대상 닀섯을 팚턎읎 적은 닀섯 갈래에 하나씩, 할로윈 귞늌 닀섯을 할로윈 갈래에 더했얎요. 53판은 할로윈 몫읎 쉬게 된 첫 판읎띌, 수학 대상 ì—Ž 개륌 ì—Ž 갈래에 하나씩 더했얎요. 54판도 수학 대상 ì—Ž 개륌 팚턎읎 적은 ì—Ž 갈래에 하나씩 더했얎요. 55판도 수학 대상 ì—Ž 개륌 ì—Ž 갈래에 하나씩 더했얎요. 56판도 수학 대상 ì—Ž 개륌 ì—Ž 갈래에 하나씩 더했얎요. 57판도 수학 대상 ì—Ž 개륌 팚턎읎 적은 ì—Ž 갈래에 하나씩 더했얎요. 58판도 수학 대상 ì—Ž 개륌 팚턎읎 적은 ì—Ž 갈래에 하나씩 더했얎요. 59판도 수학 대상 ì—Ž 개륌 ì—Ž 갈래에 하나씩 더했얎요. 60판도 수학 대상 ì—Ž 개륌 ì—Ž 갈래에 하나씩 더했얎요. 61판도 수학 대상 ì—Ž 개륌 팚턎읎 적은 갈래부터 ì—Ž 갈래에 하나씩 더했얎요. 62판도 수학 대상 ì—Ž 개륌 ì—Ž 갈래에 하나씩 더했얎요. 63판은 64판곌 같읎 죌제 표에 「타읎포」가 듀얎옚 첫 판읎띌, Ꞁ자가 듀얎간 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 64판은 죌제 표가 바뀌얎 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 ë„€ 갈래에 더했얎요. 65판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 66판도 같은 표로 돌아서 Ꞁ자 팹턮 넷을 Ꞁ자 갈래에, 하나륌 데읎터 귞늌 갈래에 더하고, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 67판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 68판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 69판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 70판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 71판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 72판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 73판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 74판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 75판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 76판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 77판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 78판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 79판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 80판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 81판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 82판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 83판부터 99판까지는 작품 하나륌 깎는 공듀읎Ʞ 판읎었얎요. 100판은 닀시 ì—Ž 개륌 만드는 판윌로 돌아와, Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 101판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. 102판도 같은 표로 돌아서 Ꞁ자 팹턮 닀섯을 Ꞁ자 갈래에, 수학 대상 닀섯을 닀섯 갈래에 하나씩 더했얎요. [index.html](index.html) 을 람띌우저로 엎멎 전부 한 장에서 돌아가요. ë§š 위 지도에서 점을 누륎멎 ê·ž 팚턎윌로 가고, 칎드의 슬띌읎더가 녾뾌 넷읎에요. 마음에 드는 것은 「윔드 복사」로 가젞가 Pattern Lab 의 CODE 칞에 붙여 넣윌멎 같은 귞늌읎 나와요. 페읎지는 파음 하나띌서 바깥에서 받아 였는 것읎 없얎요. 팚널은 섞로로 서요. 64 LED 너비에 128 LED 높읎가 Ʞ볞읎띌서 몚든 팚턎의 뚞늬에 `@matrix 64x128` 읎 있얎요. 레포의 팹널 섀정은 귞대로예요. `shared/constants.json` 의 `PANEL_GEOM_DEFAULT` 는 `128x64_SINGLE` 읎고, 팚턎읎 가로섞로륌 바Ꟍ 판을 적윌멎 펌웚얎의 `firmware/patternflow/src/core_canvas.h` 가 90도 돌렀 팚널을 ꜉ 채워요. 1판 23개는 처음에 가로 128×64 로 만듀었닀가 2판에서 섞로로 닀시 ì§°ì–Žìš”. @@ -1186,6 +1186,21 @@ | [Gilbert Tessellation](patterns/gilbert-tessellation.js) | 볎로녞읎·셀 | 팚널에 흩은 씚앗마닀 바늘읎 양쪜윌로 같은 빠륎Ʞ로 자띌닀 읎믞 놓읞 바늘에 닿윌멎 T 자로 멈추고, 닫힌 칞은 마지막윌로 닫은 자늬에서부터 유앜 색윌로 찚올띌요. | | [Kloosterman Paths](patterns/kloosterman-paths.js) | 선·궀적 | 큎룚슀테륎만 합의 부분합을 읎은 Ꞟ을 두 펜읎 양 끝에서 서로의 거욞상윌로 ê·žë € 가욎데 엎에서 만나고, ë‹€ 귞늰 좌우 대칭 귞늌은 위로 올띌가 닀음 a 의 귞늌에 자늬륌 낎쀘요. | +### 102판 + +| 팹턮 | 갈래 | 한 쀄 | +| --- | --- | --- | +| [Side Radicals](patterns/side-radicals.js) | Ꞁ자 | 위 연습 칞에 æ°Ž, 人, 手 같은 부품을 획순대로 쓰멎 획마닀 사볞읎 아래 칞윌로 낎렀였며 æ°µ, 亻, 扌 ꌎ로 바뀌얎 붙고, 낚은 부분읎 믞끄러젞 듀얎와 江 곌 æ²³ 가 서요. | +| [Gerbert's Abacus](patterns/gerbert-abacus.js) | Ꞁ자 | 엎마닀 아플쌀슀 하나륌 놓는 제륎베륎의 셈판에서 장부 밑에 적힌 수가 판윌로 날아올띌, 엎마닀 두 말읎 합의 말 하나로 바뀌고 10 을 넘윌멎 ꞈ색 1 읎 왌쪜 엎로 넘얎가요. | +| [Heaps Page](patterns/heaps-page.js) | Ꞁ자 | 옛Ꞁ 한 쪜읎 낱말마닀 위에서 아래로 썚지는데 처음 나옚 낱말은 밝고 되풀읎된 낱말은 얎둡게 가띌앉고, 발치에서는 닀륞 낱말 수 V 가 로귞 눈ꞈ 위에서 êž°ìšžêž° β 의 곧은 선을 따띌 올띌가요. | +| [Trivision](patterns/trivision.js) | Ꞁ자 | 섞몚 êž°ë‘¥ 여덟 개의 섞 멎에 ALL, FOR, ONE 포슀터륌 섞로 조각윌로 나눠 붙읎고 Ʞ둥읎 왌쪜부터 찚례로 120도씩 돌아서, 도는 동안 Ʞ둥마닀 눌며 두 멎읎 닀륞 밝Ʞ로 볎읎며 낱말읎 조각조각 바뀌얎요. | +| [Photo Finish](patterns/photo-finish.js) | Ꞁ자 | Ꞁ자 닀섯읎 결승선을 지나멎 ê·ž 한 쀄만 볎는 칎메띌가 쀄 아래에 시간을 섞로로 쌓아서, 빠륞 Ꞁ자는 짧게, 멈춘 Ꞁ자는 쀄묎늬로 찍히고 띠 속 높읎가 도착 찚례가 돌요. | +| [Kauffman Bracket](patterns/kauffman-bracket.js) | 위상·맀듭 | 맀듭 귞늌의 교찚점을 하나씩 X 로 였므렞닀가 반대쪜윌로 ì—Žì–Ž 2^n 상태륌 ë‹€ 돌며 갈띌진 고늬륌 제 색윌로 칠하고, 상태마닀 더한 막대가 ꎄ혞 닀항식윌로 섰닀가 칎드처럌 뒀집혀 졎슀 닀항식읎 돌요. | +| [Takagi Curve](patterns/takagi-curve.js) | 프랙탈 | 선 팚널에 x 륌 128쀄로 낎늬고 닚계마닀 제 색의 톱니 천막을 지ꞈ까지의 합 위에 쌓아서, 천막 하나가 테두늬가 거친 랔랑망제로 솟고 최댓값 2/3 에 닿는 ë„€ 묎늬에 흰 눈ꞈ읎 쌜젞요. | +| [Schönhardt Polyhedron](patterns/schonhardt-polyhedron.js) | 공간·원귌 | 유늬 삌각Ʞ둥읎 돌며 윗멎읎 비틀늬멎 옆멎읎 ꞈ색 곚로 접혀 듀고, 안을 지나던 쎈록 대각선 셋읎 분홍 점선윌로 바뀌얎 곚 위 빈 곳에 걞늬며, 발치의 셈읎 15에서 12륌 지나 0윌로 ë–šì–Žì žìš”. | +| [Fraenkel Rhythm](patterns/fraenkel-rhythm.js) | 늬듬·펄슀 | 빠륎Ʞ가 닀륞 목소늬 여섯읎 펄슀륌 ꌭ 한 번씩 나눠 치는 프렌쌈의 덮개륌 눈ꞈ Ꞟ읎로 섞우멎 읎진 자가 되고, 자가 위로 흐륎며 재생선에서 욞늬닀가 K4 로 비윚을 틀멎 겹친 눈ꞈ곌 빈 자늬가 생겚요. | +| [Kendall Shape Sphere](patterns/kendall-shapes.js) | 데읎터 귞늌 | 위 상자에 던진 삌각형마닀 몚양 점읎 아래에서 도는 쌄듀 몚양 구에 떚얎지고, 섞 직각 원 안의 둔각 점읎 3/4 로 몚여 가는 몫을 ë§š 아래에 적얎요. | + ## 1판 갈래마닀 녾뾌 넷의 자늬는 갈래가 달띌도 거의 같아요. K1 은 색읎고 K2 는 빠륎Ʞ예요. K3 은 몚양을 바꟞고, K4 는 ê·ž 갈래만의 손잡읎예요. 귞래서 처음 볎는 팹턮도 K1 곌 K2 는 손읎 뚌저 알아요. @@ -5020,6 +5035,70 @@ Lab 에서 핮 볎Ʞ: 없음. Ꞁ읎 2~126쀄을 쓰고 였륞쪜 빈 곳도 죌제 5 「수학 · 추상」윌로 선·궀적 갈래에 서는 팚턎읎에요. 헹드멭 큎룚슀테륎만(Hendrik Kloosterman)읎 1926년 읎찚 형식을 닀룚며 쓎 합 Kl(a, 1; p) = Σ e((ax + x̄)/p) 륌 귞렀요. 앙드레 베유(André Weil)는 1948년 |Kl| ≀ 2√p 륌 볎였얎요. 에마뉘엘 윔발슀킀(Emmanuel Kowalski)와 윌늬엄 소읞(William Sawin)은 2016년 부분합을 √p 로 나눠 읎은 Ꞟ을 묎작위 핚수로 볎고, p 가 컀질 때 묎작위 푞늬에 ꞉수로 간닀는 것을 볎였얎요. p − x 번짞 항은 x 번짞 항의 쌀레예요. 귞래서 Ꞟ은 싀수축 위 Kl 에서 끝나고 Re z = Kl/2 섞로선에 대핮 좌우 대칭읎에요. 팚턎은 ê·ž 선을 판 가욎데 엎에 두고, 0 곌 Kl 에서 두 펜읎 마죌 나아가 (p − 1)/2 번짞 점에서 만나게 귞렀요. √p 하나륌 32 LED 로 두얎 베유 한계가 판 너비 64 LED 와 같고, 밑쀄마닀 양 가장자늬에 흐늰 눈ꞈ을 찍었얎요. Prime 은 61 에서 701 까지 여섯 자늬예요. 61 은 한 걞음 4.1 LED 의 60각형읎고, 701 은 얜힌 얌룩읎에요. 잰 값은 움직임 0.536, 쎘쎘핚 0.060, 가장자늬 0.654, 복잡도 0.523, 채움 0.141, 색 0.186, 몚양 바뀜 0.105 읎고, thirds 는 13/18/10, 녾뾌는 0.06, 0.08, 0.11, 0.04 예요. 첫 판은 덧셈 빛읎띌 큰 p 가 하얗게 탔얎요. 겹친 곳을 가장 밝은 채널로 나눠 색상을 지킀자 풀렞얎요. 3×3 번짐도 넣얎 뎀는데 팚널에서 선읎 뭉개젞 뺐얎요. 팚널에서 새로 볞 것은 킀예요. 40개륌 재 볎니 너비 가욎데값읎 √p 의 1.3ë°°, 높읎가 0.75배띌 납작할 쀄 알았얎요. 귞런데 몇몇은 토템처럌 Ꞟ게 서고, 귞늌마닀 얌굎읎나 벌레로 읜혀요. +## 102판 + +102판은 101판에 읎얎 새 팹턮 ì—Ž 개륌 더하는 판읎에요. 같읎 도는 바퀮는 없었얎요. 앞 바퀮 PR 에서 OWNER, MEMBER, COLLABORATOR 가 쓎 Ꞁ 가욎데 음Ꟍ Ꞁ읎 아닌 것은 100판곌 101판읎 볞 ê·ž 셋뿐읎띌 따륌 음은 없었얎요. 죌제 1 「지도의 빈 칞」은 읎번에도 채우지 못했얎요. ì§“êž° 전 757개에서 `*` 가 붙은 칞은 움직임 × 몚양 바뀜 1,4 하나읎고, 움직임 축의 두 끝읎 0.010 곌 4.910 윌로 101판곌 같아서 100판읎 셈한 대로 닿지 않는 칞읎에요. 죌제 4 「섞로 64×128」은 Takagi Curve 와 Heaps Page 가 채우고, 같읎 겚눈 Kendall Shape Sphere 도 thirds 가 12/66/36 읎띌 듀얎요. 타읎포 닀섯은 「타읎포 싀험」의 출발점을 나눠 맡았얎요. Side Radicals 는 Ꞁ자가 만듀얎지는 곌정, Gerbert's Abacus 는 숫자와 Ʞ혞 놀읎, Trivision 은 Ꞁ자의 몞읎에요. Heaps Page 와 Photo Finish 는 섞로 팚널닀욎 Ꞁ읎에요. 닀섯 ë‹€ 「닀음 판 감」의 100판 뒀와 101판 ë’€ 목록에서 곚랐고, Ꞁ자 ꌎ은 ë‹€ 윔드에 적었얎요. 수학 닀섯은 101판 ë’€ 슀묌여섯윌로 가장 적던 음곱 갈래 가욎데 위상·맀듭, 프랙탈, 공간·원귌, 늬듬·펄슀, 데읎터 귞늌에 하나씩 ì„°ì–Žìš”. 프렌쌈의 추잡은 「닀음 판 감」의 76판 ë’€ 목록에서 곚랐고, 나뚞지 넷은 목록 밖에서 찟았얎요. 「낯선 수학 예」에서는 고륎지 않았얎요. ì—Ž 개는 레포 밖 닀섯 자늬에서 수학 하나와 타읎포 하나씩 나눠 따로 짓고 합쳀얎요. + +### Side Radicals + +죌제 7 「타읎포」의 「Ꞁ자가 만듀얎지는 곌정」을 겚눈 팚턎읎에요. 부품읎 Ꞁ자 옆에 붙윌멎 ꌎ읎 바뀌는 펞방(偏旁)을 귞렀요. 『강희자전』(1716)은 æ°µ 륌 æ°Ž 부에, 亻 륌 人 부에 넣얎요. 음곱 벌은 æ°Ž 와 æ°µ(江 æ²³), 人 곌 亻(䌑 他), 手 와 扌(打 拉), 心 곌 忄(忙 å¿«), 火 와 灬(杰 点), 刀 와 刂(刊 利), 犬 곌 犭(狂 狗)읎에요. 부품곌 펞방곌 낚은 부분 28개의 획 99개륌 16×16 좌표에 손윌로 적었얎요. 62×62 연습 칞 둘읎 1번 쀄곌 65번 쀄에서 시작핎요. 위 칞에서 펜읎 1쎈에 55 LED 로 부품을 쓰멎, 획마닀 제 색을 띀 사볞읎 3.2쎈 동안 아래 칞윌로 낎렀였며 펞방 ꌎ로 바뀌얎요. 획은 몚두 24점윌로 닀시 찍얎서 점끌늬 옮겚 가고, 사볞은 획마닀 0.1 씩 늊게 떠나요. æ°Ž 의 짧은 삐칚은 윗점, 횡삐칚은 가욎뎃점읎 돌요. 파임은 끝을 뒀집얎 치킎읎 되고, 섞로갈고늬는 가늘얎지며 ê·ž 치킎에 녹아요. 心 의 누욎갈고늬는 음얎서서 忄 의 섞로획읎 돌요. 펞방읎 붙윌멎 낚은 부분읎 0.9쎈 동안 믞끄러젞 듀얎와 江 읎 1.8쎈 서요. 읎얎서 å·¥ 읎 빠지고 可 가 듀얎와 æ²³ 가 서요. 한 벌은 12.3쎈(人)에서 13.9쎈(手)읎고, 음곱 벌을 ë‹€ 도는 데 92.5쎈예요. 위 칞의 부품은 사볞읎 떠난 ë’€ 같은 색윌로 옅게 낚아서, 얎느 획읎 얎느 점읎 됐는지 위아래로 맞춰 볌 수 있얎요. K4 Morph 륌 0.1 위로 올늬멎 바뀜을 손윌로 잡고, 0.5 에서 획듀읎 칞 사읎륌 지나요. 잰 값은 움직임 0.159, 쎘쎘핚 0.052, 가장자늬 0.859, 복잡도 0.275, 채움 0.184, 색 0.157, 몚양 바뀜 0.083 읎고, thirds 는 21/17/16, 녾뾌는 0.04, 0.06, 0.09, 0.07 예요. 첫 판은 Hue 가 0.033 읎었얎요. 재는 2~3쎈에는 위 칞에 옅게 낚은 부품읎 쌜진 LED 의 대부분읞데, 귞것을 검정 쪜윌로 반 얎둡게 했Ʞ 때묞읎에요. 흰색 쪜윌로 20% 섞얎 64% 밝Ʞ로 두고, 획 채도륌 0.72 에서 0.8 로, 굵Ʞ륌 3.6/3.2 에서 3.8/3.3 LED 로 올늬자 0.043 읎 됐얎요. 반 얎두욎 ꞈ색은 刀 에서 갈색윌로 읜혔얎요. æ°Ž 의 섞로갈고늬도 투명도로 사띌지게 했더니 갈색 얌룩읎 낚아서, 제 색 귞대로 가늘얎지게 바꿚얎요. 팚널에서 새로 볞 것은 灬 ë„€ 점읎 3.3 LED 굵Ʞ로 13 LED ë–šì–Žì ž 있얎도 따로 읜힌닀는 것읎에요. 火 의 ꞎ 삐칚읎 왌쪜 첫 점윌로 쀄얎드는 것읎 넷 가욎데 가장 잘 볎여요. + +Lab 에서 핮 볎Ʞ: 없음. 연습 칞 둘읎 1~62엎곌 1~62쀄, 65~126쀄을 ë‹€ 썚요. 칞 사읎 63~64쀄은 바뀌는 획읎 지나가는 Ꞟ읎띌, Text 레읎얎 낱말읎 앉을 자늬가 없얎요. + +Ʞ능 감: Lab 에서 16×16 격자에 획을 찍얎 좌표 목록윌로 복사핎 죌는 획 펞집Ʞ요. 획 99개륌 숫자로 적었얎요. ꌎ을 볎렀고 귞늌을 뜑는 슀크늜튞륌 따로 짜서 거듭 돌렞고, 手 의 첫 삐칚곌 犭 의 굜은갈고늬는 귞늌을 볎고서알 ê³ ì¹  데륌 알았얎요. + +### Gerbert's Abacus + +죌제 7 「타읎포」의 「숫자와 Ʞ혞 놀읎」륌 겚눈 팚턎읎에요. 100판 「닀음 판 감」에 적힌 감을 곚랐얎요. 제륎베륎 도늬알크(Gerbert d'Aurillac)는 980년 묎렵 랭슀에서 ì—Ž 셈판을 되삎렞고, 999년에 교황 싀베슀테륎 2섞가 됐얎요. 제자 늬셰륎(Richer)가 적은 판은 엎마닀 10의 거듭제곱을 뚞늬에 읎고, 셋씩 활ꌎ로 묶었얎요. 수는 엎마닀 1~9 륌 새ꞎ 말(아플쌀슀) 하나로 놓고, 0 읞 자늬는 비워 둬요. 팹널 위 0~73쀄읎 판읎에요. 뚞늬에 활ꌎ곌 M C X I 륌 두고, 16 LED 너비 ì—Ž 넷에 합, 더할 수, 받아올늌 섞 칞을 아래로 놓았얎요. 아플쌀슀는 5×8 로 ê·žë € 판에서는 10×16 윌로 킀웠얎요. 76~127쀄은 장부예요. 11쀄 간격 닀섯 쀄에 더할 수륌 분홍윌로, 합을 청록윌로 적얎요. 더할 수가 장부 밑에 적히멎 숫자마닀 사볞읎 판윌로 날아올띌요. 음의 자늬부터 두 아플쌀슀가 합의 칞윌로 믞끄러젞 듀얎가고, 흰빛곌 핚께 ê·ž 합의 아플쌀슀 하나로 바뀌얎요. 합읎 10 읎상읎멎 ꞈ색 1 읎 제 ì—Ž 바닥윌로 떚얎졌닀가 왌쪜 엎로 옮겚 가요. 여는 합 2975 + 1038 은 13, 11, 10 에서 섞 번 받아올렀요. 백의 자늬는 10 읎 되얎 비멎서 말 테두늬만 0.3쎈 빛나요. 4 _ 1 3 읎 되Ʞ까지 7.9쎈읎고, 합 하나는 평균 6.7쎈예요. K3 Columns 는 엎을 4~7 로 바꿔요. 6 읎나 7 읎멎 아플쌀슀가 5×8 읎 되고 장부가 음곱 쀄을 볎여요. K4 Glyph 가 1/3 을 넘윌멎 장부가 같은 수륌 요슘 숫자로 적얎서, 판읎 비워 둔 자늬에 0 읎 나타나요. 2/3 부터는 판의 말도 요슘 숫자가 되고 말 바탕읎 사띌젞, 셈판읎 종읎에 쓎 덧셈처럌 볎여요. 잰 값은 움직임 0.203, 쎘쎘핚 0.047, 가장자늬 0.580, 복잡도 0.145, 채움 0.257, 색 0.153, 몚양 바뀜 0.066 읎고, thirds 는 45/16/14, 녾뾌는 0.05, 0.04, 0.10, 0.05 예요. 첫 판의 4 는 고늬 밑에 닀늬륌 엇걎 반쪜 8 읎었는데, 팚널에서 8 로 읜혀서 4 _ 1 3 읎 8 _ 1 3 윌로 볎였얎요. 닀늬륌 벌렀 Ω ꌎ로 바꿚얎요. y ꌎ 5 는 요슘 4 로 읜혀서 2975 가 2974 로 볎였고, 쀄Ʞ가 꺟읞 d ꌎ로 바꿚얎요. Glyph 는 처음에 ꌎ만 바뀌얎 반응읎 0.02 였얎요. 요슘 숫자 판에 0 말을 넣고 말 바탕을 빌고 ì—Ž 쀄을 40% 로 흐늬게 하자 0.05 가 됐얎요. 받아올늰 1 은 처음에 대각선윌로 날아 옆 엎의 더할 수 위륌 지나갔얎요. 귞래서 바닥윌로 떚얎졌닀가 옆윌로 가게 바꿚얎요. 팚널에서 새로 볞 것은 0 읎 판볎닀 장부에서 더 아쉜닀는 것읎에요. 판에서는 ì—Ž 쀄읎 빈자늬륌 지쌜 쀘요. 아플쌀슀로 적은 장부의 980 은 9 와 8 뒀에 흐늰 섞로쀄만 낚아서 98 로도 읜혀요. + +Lab 에서 핮 볎Ʞ: 없음. 판읎 0~73쀄, 장부가 76~127쀄을 썚요. 빈 곳은 받아올늌 칞(54~69쀄)뿐읞데, 받아올늰 1 읎 엎마닀 ê·ž 칞윌로 ë–šì–Žì ž 지나가서 Text 레읎얎 낱말곌 겹쳐요. + +Ʞ능 감: 녞람륌 돌며 딞깍 수륌 윔드 팚턎읎 정수로 받는 Ꞟ읎요. 엔윔더는 한 바퀎에 24칞 딞깍하는데 팚턎에는 범위 안의 싀수만 와요. 볎는 사람읎 K3 륌 한 칞씩 돌렀 엎마닀 말을 하나씩 놓듯 더할 수륌 적게 하고 싶었는데, ê·ž Ꞟ읎 없얎서 더할 수륌 묎작위로 뜑았얎요. + +### Heaps Page + +죌제 7 「타읎포」의 「섞로 팚널닀욎 Ꞁ」곌 죌제 4 「섞로 64×128」을 겚눈 팚턎읎에요. 구슀타프 헀륎닚(Gustav Herdan)읎 1960년 『Type-Token Mathematics』에, 핮럮드 슀탠늬 힙슀(Harold Stanley Heaps)가 1978년 묞서 몚음에서 적은 법칙읎에요. Ꞁ을 N 낱말 읜었을 때 서로 닀륞 낱말 수 V 는 K·N^β 로 늘고, ꞎ Ꞁ에서 β 는 0.5 귌처예요. 팚널에는 디킚슀 『두 도시 읎알Ʞ』(1859)의 첫 두 묞닚읎 썚젞요. Ꞁꌎ은 101판 Scrambled Middles 의 소묞자에 대묞자 여덟곌 숫자, β 륌 더 귞늰 것읎에요. 한 쪜은 1~98쀄에 9쀄 간격윌로 11쀄읎에요. Speed 2 에서 1쎈에 3낱말읎띌 186낱말읎 62쎈, 첫 쪜 38조각읎 12.7쎈에 썚젞요. 처음 나옚 낱말은 옅은 ꞈ색, 닀시 나옚 낱말은 청회색읎고, 방ꞈ 쓎 낱말은 흰빛에서 식얎요. 쪜읎 찚멎 닀음 쪜읎 위부터 덮얎 쓰고, 쓰는 쀄 아래에는 앞 쪜읎 회색윌로 낚아요. 100쀄에는 V, N, β 가 바뀌며 찍혀요. 108~126쀄은 로귞 눈ꞈ 귞늌읎띌 V = K·N^β 가 êž°ìšžêž° β 의 곧은 선읎에요. K3 Text 는 전도서 3장 1~8절(흠정역, 1611)곌 캐럎의 「재버워킀」(1871)로 바꿔요. K4 Forget 은 M 낱말읎 넘게 지나도록 안 나옚 낱말을 닀시 새로 쳐요. 디킚슀의 it was the 
 of 는 여섯 낱말마닀 돌아와서, M 읎 5 읎하멎 첫 쪜 38조각읎 ë‹€ 밝아지고 V 가 129 에서 170 윌로 ë›°ì–Žìš”. 잰 값은 움직임 0.029, 쎘쎘핚 0.101, 가장자늬 0.944, 복잡도 0.202, 채움 0.193, 색 0.108, 몚양 바뀜 0.053 읎고, thirds 는 16/15/26, 녾뾌는 0.06, 0.05, 0.12, 0.05 예요. 첫 판은 귞늌 축읎 선형읎띌 여는 화멎에서 V 12 가 86 가욎데 섞 쀄짜늬 띠였얎요. 로귞 눈ꞈ윌로 바꟞자 여는 선읎 41엎까지 왔얎요. Hue 와 Speed 는 0.03 읎었얎요. 새 낱말의 채도륌 0.3 에서 0.45 로 올렞얎요. 첫 쪜 끝 8조각 앞에서 엎게 하자 빠륞 Speed 에서는 3쎈 안에 쪜읎 넘얎가서 0.06 곌 0.05 가 됐얎요. s 는 위아래 획읎 짧아 was 가 wac 로 읜혀서 위아래 획을 ꜉ 채웠얎요. 앞 쪜을 35% 로 얎둡게 두었더니 ꞈ색읎 갈색윌로 볎여서 회색윌로 바꿚얎요. 팚널에서 새로 볞 것은 디킚슀가 힙슀와 거꟞로 간닀는 것읎에요. 쪜마닀 새 낱말읎 13/38, 16/36, 19/31, 22/34 로 늘닀가, 둘짞 왕곌 왕비가 첫짞륌 되풀읎하는 닀섯짞 쪜에서 11/38 로 ë–šì–Žì žìš”. 전도서는 22/41, 12/43, 9/40, 9/34 로 법칙대로 엷얎지고, β 는 디킚슀 186낱말읎 0.92, 전도서 158낱말읎 0.68 읎에요. + +Lab 에서 핮 볎Ʞ: 없음. Ꞁ읎 1~63엎곌 1~98쀄을 ë‹€ 쓰고 쀄 사읎 틈읎 한 쀄뿐읎띌, 얹은 낱말읎 쪜의 Ꞁ곌 겹쳐요. 귞늌 왌쪜 위의 빈 ê³³(108~117쀄)에는 Forget 을 돌늬멎 M 값읎 서요. + +Ʞ능 감: 윔드 팚턎읎 ꞎ Ꞁ을 묞자엎로 받는 칞읎요. 볎는 사람읎 붙여 넣은 제 Ꞁ의 V 와 β 륌 볎읎게 하고 싶었는데, 받을 Ꞟ읎 없얎서 섞 Ꞁ을 윔드에 박았얎요. Lab 픜셀 Ꞁꌎ에 소묞자와 β 같은 귞늬슀 Ꞁ자가 있었윌멎 101판 Ꞁꌎ을 빌렀 대묞자 여덟곌 β 륌 ê·žë € 넣을 음도 없었얎요. + +### Trivision + +죌제 7 「타읎포」의 「Ꞁ자의 몞」을 겚눈 팚턎읎에요. 낱말을 섞몚 Ʞ둥에 잘띌 붙였얎요. 섞 멎읎 도는 프늬슘 ꎑ고판(trivision)읎 볞읎에요. 낱말은 ALL, FOR, ONE 읎고, Ꞁ자는 50×34 LED 칞에 직접 귞늰 굵은 대묞자예요. 섞로획은 12 LED, 가로획은 7~8쀄읎에요. 한 포슀터에 섞 Ꞁ자륌 5, 47, 89번짞 쀄부터 쌓았얎요. ALL 은 깊은 파랑 바탕에 옅은 녞랑읎고, FOR 는 녾랑 바탕에 낚색, ONE 은 자홍 바탕에 흰색읎에요. Ʞ둥은 한 변읎 슬랫 너비 w 읞 정삌각형읎띌, 쉬는 동안에는 읎웃 멎곌 몚서늬가 딱 맞아요. 도는 동안 멎 j 의 법선은 볎는 쪜에서 (φ − j)·120도에 있얎요. 두 몚서늬륌 반지늄 w/√3 원 위에 잡아 정멎윌로 사영핎요. 4분의 1 LED 마닀 제 Ʞ둥곌 양옆 Ʞ둥의 볎읎는 멎 가욎데 가장 가까욎 것을 고륎고, ê·ž 멎에 붙은 포슀터 조각을 눌며 너비로 읜얎요. 30도와 90도에서는 êž°ë‘¥ 윀곜읎 0.87 w 로 쀄얎서 읎웃 사읎로 판 속의 얎둠읎 비쳐요. 등은 왌쪜 앞 40도에 있얎요. 왌쪜에서 듀얎였는 새 멎은 등 쪜윌로 돌며 1.19배까지 밝아지고, 였륞쪜윌로 나가는 옛 멎은 0.38 까지 얎두워젞요. 법선읎 등곌 눈 사읎륌 가륎는 −20도에서는 흰 반짝임읎 지나가요. Speed 2 에서 낱말은 2.4쎈 서 있얎요. êž°ë‘¥ 하나는 1쎈에 돌고, 멈출 때 3도쯀 넘쳀닀 돌아와요. K3 Slats 는 êž°ë‘¥ 8, 16, 32 개읎고 멎 너비는 8, 4, 2 LED 예요. K4 Wave 0.06 에서는 돌Ʞ가 0.36쎈에 판을 걎너서, 마지막 Ʞ둥읎 출발할 때 첫 Ʞ둥은 3분의 1쯀 돌아 있얎요. 1 읎멎 6쎈 걞늬는 느며 묌결읎에요. 잰 값은 움직임 0.409, 쎘쎘핚 0.044, 가장자늬 0.552, 복잡도 0.130, 채움 0.996, 색 0.623, 몚양 바뀜 0.434 읎고, thirds 는 99/99/99, 녾뾌는 0.57, 0.57, 0.08, 0.44 예요. 첫 판은 첫 돌Ʞ가 1쎈에 시작핎 2.4쎈에 끝났얎요. 귞래서 녞람륌 재는 2~3쎈에는 판읎 거의 쉬고 있었고 Slats 반응읎 0.03 읎었얎요. 쉬는 판에서 8, 16, 32 Ʞ둥은 읎음맀만 달띌요. 첫 돌Ʞ륌 1.4쎈에 시작하게 하고 읎음맀륌 반 LED 로 넓히자 0.08 읎 됐얎요. 나가는 멎도 처음에는 0.3 밝Ʞ띌서 ALL 의 옅은 녞랑읎 갈색윌로 볎였얎요. 바탕빛을 0.25 에서 0.32 로 올렀 풀었얎요. 팚널에서 새로 볞 것은 가로획읎 도는 동안에도 제 쀄에 낚는닀는 것읎에요. Ʞ둥읎 섞로띌서 사영은 가로만 누륎고 쀄은 걎드늬지 않아요. 돌Ʞ 한가욎데에서 섞로획은 Ʞ둥마닀 눌며 조각윌로 끊Ʞ는데, F 와 E 의 가로획곌 O 의 위아래는 높읎가 귞대로띌 조각읎 한 쀄로 읎얎젞서 뚌저 읜혀요. + +Lab 에서 핮 볎Ʞ: 없음. 판읎 64×128 을 ë‹€ 덮얎 채움읎 0.996 읎에요. 얹은 Text 레읎얎 Ꞁ자는 Ʞ둥곌 같읎 돌지 않아서 판 위에 붙은 슀티컀처럌 따로 놀아요. + +Ʞ능 감: 아래 레읎얎의 화멎을 윔드 팚턎읎 텍슀처로 읜는 Ꞟ읎요. Text 레읎얎 셋을 섞 멎에 붙음 수 있윌멎 볎는 사람읎 제 낱말로 판을 돌렀요. 지ꞈ은 ALL, FOR, ONE 의 음곱 Ꞁ자륌 닀각형곌 띠로 손수 적었얎요. + +### Photo Finish + +죌제 7 「타읎포」의 「섞로 팚널닀욎 Ꞁ」을 겚눈 팚턎읎에요. 사진 판정 칎메띌는 결승선 한 쀄만 볎는 틈 앞윌로 필늄을 음정한 빠륎Ʞ로 흘렀요. 귞래서 ê·ž 한 쀄읎 순간마닀 읎얎 붙고, 사진의 ꞎ 축은 공간읎 아니띌 시간읎 돌요. 팚턎은 Ꞁ자 닀섯을 닀섯 레읞의 4번짞 쀄에 섞우고, 0.7쎈 ë’€ 56번짞 쀄의 결승선을 향핎 아래로 달늬게 핎요. 쀄 아래 57~127쀄의 71쀄읎 필늄읎에요. 칎메띌는 겜죌 시간 1쎈에 33.5번 56번짞 쀄을 읜얎 ë§š 위에 새 쀄로 넣고, 띠륌 한 쀄씩 밀얎 낎렀요. 귞래서 1쎈에 33.5 LED 로 지나는 Ꞁ자만 제 í‚€ 14쀄로 찍혀요. 64 로 달늰 P 는 7쀄읎고, 23 윌로 달늰 T 는 20쀄읎에요. 결승선에 걞친 채 0.5~0.8쎈 서는 H 는 닀늬 두 쀄읎 17~27쀄 끌렀요. 옆윌로 5 LED 믞끄러지며 지난 O 는 Ʞ욞얎서 찍혀요. 마지막 Ꞁ자가 ë‹€ 지나가멎 칎메띌가 멈춰요. 귞러고는 출발선에 Ꞁ자듀읎 지나간 찚례대로 닀시 서서, PHOTO 가 POOHT 가 돌요. K3 Field 는 겜죌륌 닀섯 가지로 곚띌요. SWIFT 는 1쎈에 22~70 LED 로 고륎게 달렀서 킀만 달띌요. TIRED 는 결승선을 지나며 8~12 로 지치거나 90~110 윌로 치고 나가요. 지친 T 는 가로획읎 랔록처럌 굵얎지고, 치고 나간 D 는 위가 눌렀 u 처럌 볎여요. PAUSE 는 닀섯 Ꞁ자가 ë‹€ 한 번씩 결승선 위에 서서, 선 쀄의 닚멎읎 쀄묎늬로 낚아요. SLIDE 는 3~5 LED 옆윌로 믞끄러젞 êž°ìšžì–Žìš”. K4 Rate 는 쀄 속도륌 18 에서 102 까지 바꿔요. 18 읎멎 띠가 3.9쎈륌 닎고 Ꞁ자가 ë‹€ 눌렀요. 102 멎 Ꞁ자가 섞 배로 서지만 마지막 0.7쎈만 낚아요. 잰 값은 움직임 0.310, 쎘쎘핚 0.038, 가장자늬 0.621, 복잡도 0.042, 채움 0.452, 색 0.161, 몚양 바뀜 0.129 예요. thirds 는 13/60/61, 녾뾌는 0.05, 0.11, 0.06, 0.07 읎에요. 100판 Rolling Shutter 는 화멎 전첎륌 한 쀄씩 늊게 읜얎 Ꞁ자륌 휘게 했얎요. 읎 팚턎은 한 쀄만 읜고 ê·ž 쀄을 시간윌로 쌓아서, 빠륎Ʞ가 Ꞁ자 킀가 되고 띠 속 높읎가 도착 찚례가 돌요. 첫 판은 겜죌륌 2.1쎈 앞당겚 엎었얎요. 귞래서 2쎈 화멎읎 읎믞 끝난 사진읎었고, 위 절반에는 결곌 낱말만 있었얎요. 0.2쎈만 앞당Ʞ자 결승선 양쪜에 Ꞁ자가 ë‹€ ì„°ì–Žìš”. ê·ž 대신 Hue 가 0.03 윌로 앜핎젞서 필늄곌 레읞 선도 색을 따띌 돌게 했더니 0.05 가 됐얎요. Rate 는 0.0395 로 묞턱 아래였얎요. 칎메띌가 아직 닿지 않은 필늄을 검게 두자, 찍힌 부분읎 쀄 속도대로 자띌서 0.065 가 됐얎요. 팚널에서 새로 볞 것은 쀄묎늬가 된 Ꞁ자도 읜힌닀는 것읎에요. PAUSE 의 P 는 고늬 속 10001 쀄에서 서서 섞로 막대 둘읎 19쀄 끌렀요. 귞래도 위아래 가로획곌 쀄Ʞ가 낚아서 P 로 읜혀요. + +Lab 에서 핮 볎Ʞ: Text 레읎얎로 FINISH 륌 Silkscreen 8 px, 가욎데 맞춀 x 32, y 26, 색 #606878 로 얹얎 볎섞요. 22~29쀄은 한 겜죌 5.8쎈 가욎데 Ꞁ자가 지나는 1쎈쯀 말고는 레읞 선만 있는 빈 튞랙읎띌 읎늄표 자늬가 돌요. 칎메띌는 ê·ž 낱말을 찍지 않아서, 레읎얎가 겜죌 밖에 있닀는 것읎 띠에서 볎여요. + +Ʞ능 감: 레읎얎 하나의 한 쀄을 시간 축윌로 쌓는 「슬늿」 횚곌나, 윔드 팚턎읎 닀륞 레읎얎의 픜셀을 읜는 Ꞟ읎요. 대묞자 14자륌 5×7 로 직접 ê·žë žê³  낱말 닀섯을 윔드에 박았얎요. Text 레읎얎의 낱말읎 결승선을 지나가게 할 수 있윌멎, 볎는 사람읎 적은 낱말읎 귞대로 사진에 찍혀요. + +### Kauffman Bracket + +죌제 5 「수학 · 추상」윌로 위상·맀듭 갈래에 서는 팚턎읎에요. 룚읎슀 칎우프만(Louis Kauffman)읎 1987년 『Topology』의 「State models and the Jones polynomial」에서 낾 ꎄ혞 닀항식을 상태 합 귞대로 셈핎요. 교찚점의 위 가닥을 반시계로 돌멮 때 쓞고 지나가는 두 몚서늬륌 잇는 것읎 A 맀끈화읎고, 나뚞지 둘을 잇는 것읎 B 맀끈화예요. 상태 하나가 고늬 L 개륌 낚Ʞ멎 A^(a−b) d^(L−1), d = −A² − A⁻² 륌 더핎요. 맀듭은 튞레포음 3_1(상태 8개), 8자 맀듭 4_1(16개), 5_1 곌 5_2(32개씩), 슀티람도얎 6_1(64개)읎에요. 귞늌은 좌우 대칭 푞늬에 곡선읎에요. 튞레포음 말고 넷은 교찚 수와 졎슀 닀항식윌로 곡선을 찟았얎요. 교찚점은 귞늰 곡선에서 바로 읜고, 위아래는 곡선을 따띌 엇갈렀 쀘요. 맀듭은 위 58×62 상자에 서요. 69번 쀄에 상태 낱말(AABAB)곌 고늬 수만큌의 점읎 였고, 96번 쀄을 0 윌로 아래위 19쀄씩 막대가 서요. 막대는 A 의 지수가 4씩 ë–šì–Žì§„ n + 1 개예요. 귞레읎 윔드 찚례띌 한 걞음에 교찚점 하나만 바뀌얎요. ê·ž 교찚점의 두 곡선읎 한가욎데로 였므띌듀얎 X 가 됐닀가 반대쪜윌로 엎늬고, 고늬 하나가 둘로 갈늬거나 둘읎 하나로 합쳐젞요. Speed 2 에서 한 상태는 튞레포음 0.9쎈, 6_1 0.3쎈예요. ë‹€ 돌멎 맀듭읎 닫히고 ꎄ혞가 1.5쎈 서요. 찚튞는 가욎데 엎을 축윌로 0.6쎈 동안 칎드처럌 뒀집혀 (−A³)^(−w)⟹K⟩ 륌 t 의 지수로 1.6쎈 볎여 쀘요. 튞레포음 한 바퀮는 12.7쎈, 닀섯 ë‹€ 돌멎 90쎈예요. K4 Switch 는 읜는 찚례로 앞의 round(Switch × n) 교찚점에서 위아래륌 바꿔요. 튞레포음은 0.25 와 0.5 에서 0_1 읎 되고 1 에서 거욞상 3_1* 가 돌요. 6_1 은 둘을 바꟞멎 5_2 예요. 읎늄은 셈한 졎슀 닀항식을 표에 맞춰 왌쪜 위에 붙여요. 잰 값은 움직임 0.091, 쎘쎘핚 0.054, 가장자늬 0.824, 복잡도 0.285, 채움 0.171, 색 0.162, 몚양 바뀜 0.109 읎고, thirds 는 21/7/23, 녾뾌는 0.06, 0.07, 0.14, 0.09 예요. 첫 판의 K4 는 찚튞륌 손윌로 뒀집는 Jones 녞람였는데 0.036 읎었얎요. 막대가 LED 의 5% 쯀밖에 안 돌서예요. 뒀집Ʞ륌 걞음 끝에 넣고 K4 에 교찚점 바꟞Ʞ륌 죌자 0.09 가 됐얎요. 바뀌는 교찚점을 옛 맀끈화와 새 맀끈화륌 겹쳐 흐늬는 식윌로 바꟞니, 반쯀에서 둘 ë‹€ 반 밝Ʞ가 되얎 회색 얌룩읎 생게얎요. 귞래서 X 로 였므렞닀 여는 몚양 바꿈윌로 고쳀얎요. 아래 가닥의 틈은 처음에 교찚점에서 2.6 LED 였얎요. 2 LED 선을 빌멎 0.6 LED 만 낚아 교찚가 귞냥 엇갈늰 선윌로 볎였고, 4.2 LED 로 넓히자 위아래가 읜혀요. 곡선을 찟을 때 6교찚와 판별식 9 만 볎았더니 귞래니 맀듭 3_1#3_1 읎 뚌저 걞렞얎요. ê·ž 뒀로는 졎슀 닀항식 전첎륌 맞춰요. 팚널에서 새로 볞 것은 지워지는 곌정읎에요. 6_1 은 걞음 가욎데 계수 하나가 7 까지 였륎지만 끝의 ꎄ혞 계수는 많아알 2 띌, 막대 한 칞읎 2.7쀄에서 12쀄로 컀지며 끝에 찚튞가 확대돌요. 8자 맀듭은 ꎄ혞가 좌우 대칭읎띌 뒀집혀도 읎늄표만 바뀌얎요. + +### Takagi Curve + +죌제 5 「수학 · 추상」곌 죌제 4 「섞로 64×128」을 겚눈 팚턎읎에요. 닀칎Ʞ 데읎지(高朚貞治)가 1903년에 낾 T(x) = Σ s(2ⁿx)/2ⁿ 는 얎디서나 읎얎지고 얎디서도 믞분되지 않는 핚수예요. s 는 가장 가까욎 정수까지의 거늬띌서 n 번짞 항은 높읎 2⁻ⁿ/2 의 천막 2ⁿ 개가 늘얎선 톱니예요. 게였륎크 란잠베륎크(Georg Landsberg)가 1908년에 1/2 자늬에 w 륌 넣었얎요. 팚널은 x 륌 128쀄로 낎늬고 값을 왌쪜에서부터 엎로 쌓아요. 한 쀄은 표볞 여덟 개예요. 닚계마닀 제 색의 톱니가 지ꞈ까지의 합 위에 얹혀요. 띠 안은 40% 밝Ʞ읎고 띠 위 1.2엎은 ë‹€ 밝아서 부분합마닀 밝은 선읎 낚아요. Speed 2 에서 한 닚계가 0.6쎈에 솟고 0.3쎈 뒀에 닀음 닚계가 와요. 귞래서 w = 1/2 의 여덟 닚계가 7.2쎈에 서고, 64쀄에서 45엎까지 솟은 천막 하나가 테두늬가 거칠얎지는 랔랑망제가 돌요. ë‹€ 서멎 1.8쎈 동안 최댓값 2/3 읎 닿는 42~45, 50~53, 74~77, 82~85쀄의 60엎에 흰 눈ꞈ읎 쌜젞요. 2진 숫자가 01 읎나 10 짝윌로만 된 x 의 칞토얎 집합읎고, 장플에륎 칎안(Jean-Pierre Kahane)읎 1959년에 밝혔얎요. 귞닀음 1.4쎈 동안 가는 닚계부터 벗겚지고 10.7쎈마닀 닀시 지얎요. K1 Hue 0 에서 0닚계는 하늘색읎고, 0.569 와 0.57 사읎에서 0닚계가 죌황곌 녞랑을 걎너뛰얎요. K3 Weight 륌 −1 로 돌늬멎 w = 1/4 읎에요. ë„€ 닚계가 점선윌로 귞늰 포묌선 2x(1 − x) 에 ꌭ 얹혀요. 1 읎멎 w = 0.9 띌서 엎두 닚계와 나뚞지륌 묶은 자홍 띠가 쌓읎고, 테두늬가 쀄마닀 34엎곌 59ì—Ž 사읎륌 였가요. K4 Zoom 은 팹널 위 끝윌로 x 륌 16배까지 당겚요. 가장 굵은 띠가 가늘얎지며 빠지고, 0.325, 0.55, 0.775, 1 에서는 2, 4, 8, 16배로 컀진 귞늌읎 색만 한 닚계씩 옮ꞎ 처음 귞늌에 ꌭ 겹쳐요. T_w(x) = s(x) + wT_w(2x) 륌 귞대로 귞늰 셈읎에요. 잰 값은 움직임 0.014, 쎘쎘핚 0.057, 가장자늬 0.642, 복잡도 0.512, 채움 0.707, 색 0.280, 몚양 바뀜 0.055 읎고, thirds 는 61/89/60, 녾뾌는 0.19, 0.12, 0.16, 0.16 읎에요. 첫 판은 닚계마닀 색을 0.075 바퀎씩 돌렾더니 2~7닚계가 ë‹€ 비슷한 파랑읎었얎요. 하늘색에서 0.065 바퀎씩 돌늬고 채도륌 닚계마닀 0.1씩 빌서 테두늬가 옅은 분홍읎 되게 했얎요. w = 0.9 의 나뚞지 띠는 올늬람에서 회색윌로 볎여서 자홍윌로 바꟞고 안을 60% 로 밝혔얎요. 검산 첫 판은 표볞을 i/4096 에 두얎서 엎두 번쯀 두 ë°° 하멎 s 가 0 읎 됐고, w = 0.9 의 최댓값읎 2.52 로 나왔얎요. 표볞에 묎늬수 얎Ꞌ낚을 죌자 3.16 읎 됐고, 팹턮도 ê·ž 표볞을 썚요. 팚널에서 새로 볞 것은 최댓값읎 마지막 닚계에서알 닿는닀는 것읎에요. S_n(1/3) 읎 (2/3)(1 − 2⁻ⁿ) 읎띌 여덟짞 닚계가 앉아알 60엎에서 반 ì—Ž 안윌로 였고, 흰 눈ꞈ도 귞때 쌜젞요. + +### Schönhardt Polyhedron + +죌제 5 「수학 · 추상」윌로 공간·원귌 갈래에 서는 팚턎읎에요. 에늬히 쇀하륎튞(Erich Schönhardt)가 1928년 『Mathematische Annalen』에 낾 닀멎첎예요. 삌각Ʞ둥의 옆멎 셋을 대각선 하나씩윌로 나누고 윗멎을 돌늬멎 ꌭ짓점 여섯은 귞대로읞데 옆멎 대각선읎 안윌로 접힌 곚읎 돌요. 귞러멎 위아래륌 잇는 나뚞지 대각선 셋읎 몚두 닀멎첎 밖윌로 나가요. ë„€ ꌭ짓점 묶음 15개는 얎느 것읎나 ê·ž 셋 가욎데 하나륌 몚서늬로 가젞서, 새 ꌭ짓점 없읎는 사멎첎로 자륌 수 없얎요. 팚널에는 높읎가 반지늄의 4.2배읞 유늬 Ʞ둥읎 2~110쀄에 서서 24쎈에 한 바퀮 돌아요. Speed 2 에서 비틀늌도 24쎈 죌Ʞ예요. 44도로 비틀늰 채 6쎈 있닀가 3쎈 동안 풀렀요. 비틀늌 0 읞 4.5쎈 동안 Ʞ둥읎 사멎첎 셋윌로 갈띌졌닀 붙고, 반대로 26.4도 비틀늰 볌록한 몚양을 지나 닀시 접혀요. 116~126쀄의 큰 숫자와 칞 15개는 비틀늌읎 바뀐 프레임마닀 몚양에서 셈핎요. 사멎첎마닀 몚서늬와 멎곌 가욎데의 35점을 감김수(winding number)로 재요. 볌록하멎 15, 0도에서는 셋읎 납작핎서 12, 반대로 0.1도만 비틀얎도 0 읎에요. 대각선도 13점씩 재서 안읎멎 쎈록 싀선, 밖읎멎 분홍 점선윌로 귞렀요. 44도에서 곚은 가욎데 높읎에서 축윌로부터 반지늄의 0.14 에 있고, ê·ž 위륌 가로지륎는 대각선은 0.79 에 있얎요. K3 Twist 는 옚 비틀늌을 44 + 14 × Twist 도로 30~58도 사읎에서 바꟞고, K4 View 는 낎렀닀볎는 각을 10~54도로 바꿔요. 잰 값은 움직임 0.340, 쎘쎘핚 0.072, 가장자늬 0.484, 복잡도 0.972, 채움 0.507, 색 0.190, 몚양 바뀜 0.149 읎고, thirds 는 62/60/28, 녾뾌는 0.13, 0.17, 0.13, 0.14 예요. 첫 판은 30도에서 엎었고 유늬가 55% 불투명읎었얎요. 귞때는 대각선 X 가 ê·žì–Žì§„ 유늬 상자로만 볎였얎요. 여는 비틀늌을 44도로 올늬고 Ʞ둥을 3.3 에서 4.2 로 높였얎요. 빛을 왌쪜에서 비추고 음곱 높읎에 닚멎 별을 귞늬자 곚읎 읜혔얎요. 곚의 얎두욎 멎은 밝Ʞ가 20~47 읎띌 바탕 빛을 0.16 에서 0.27 로 올렞얎요. 바닥 ꌭ짓점의 대각선 끝은 축 쪜윌로 밀멎 바닥멎 위에 뚞묌러 밖윌로 셈됐얎요. 귞래서 닀멎첎 가욎데로 밀게 고쳀얎요. 팚널에서 새로 볞 것은 밖윌로 나간 대각선읎 곚읎 옆윌로 돌아섀 때 가장 잘 읜힌닀는 것읎에요. 귞때 분홍 점선읎 êž°ë‘¥ 싀룚엣 밖 빈 곳에 따로 서요. Speed 2 에서 귞런 때가 4쎈마닀 와요. + +### Fraenkel Rhythm + +죌제 5 「수학 · 추상」윌로 늬듬·펄슀 갈래에 서는 팚턎읎에요. 아비슈늬 프렌쌈(Aviezri Fraenkel)은 1973년에 읎런 추잡을 냈얎요. 비윚 α_i 가 서로 닀륞 비티 수엎 ⌊nα_i + β_i⌋ 가 m ≥ 3 개 있고 읎것듀읎 몚든 정수륌 ꌭ 한 번씩 나눠 가지멎, 비윚은 (2^m − 1)/2^(i−1) 밖에 없닀는 것읎에요. 비티 수엎은 새뮀얌 비티(Samuel Beatty)가 1926년에 낾 묞제에서 왔얎요. ê·ž 덮개륌 귞늬멎 읎진 자가 돌요. 2^m − 1 펄슀 한 죌Ʞ에서 펄슀 k 는 k 륌 2로 나눌 수 있는 횟수가 정하는 목소늬가 쳐요. 홀수 펄슀는 가장 쎘쎘한 목소늬가 치고, 2^(m−1) 하나만 가장 성ꞎ 목소늬가 쳐요. 팚턎은 펄슀마닀 눈ꞈ 하나륌 Ꞌ고, 눈ꞈ Ꞟ읎로 목소늬륌 나눠요. 여섯 목소늬멎 63펄슀륌 2쀄씩 126쀄에 놓고 눈ꞈ은 8, 17, 26, 36, 45, 54 LED 예요. 죌Ʞ가 64가 아니띌 63읎띌서 읞치 눈ꞈ읎 빠진 자늬에서는 가장 짧은 눈ꞈ 둘읎 맞붙얎요. ê·ž 자늬는 였륞쪜에 짧은 회색 쀄로 짚었얎요. setup 읎 목소늬마닀 β 륌 정수 셈윌로 ì°Ÿì•„ 섞 죌Ʞ 동안 맞는지 확읞핎요. 섞 목소늬멎 ⌊7n/4 + 1/8⌋, ⌊7n/2 + 11/4⌋, ⌊7n + 9/2⌋ 읎고, m = 2 부터 6 까지 [0, α) 안에 β 가 ꌭ 하나씩 있얎요. 자는 Speed 2 에서 1쎈에 16쀄 올띌가요. 40번짞 쀄 재생선에 닿은 눈ꞈ은 끝읎 하얗게 부풀며 욞늬고 0.9쎈 동안 식얎요. 여섯 목소늬멎 1쎈에 8펄슀띌 한 죌Ʞ가 7.9쎈예요. K3 Drop 은 가장 쎘쎘한 목소늬륌 하나씩 빌요. 짝수 펄슀만 낚겚 반윌로 접윌멎 한 닚계 작은 프렌쌈 덮개가 되Ʞ 때묞읎에요. K4 Detune 은 밀도륌 r^(m−i) 에 비례하게 두고 r 을 2 에서 1 쪜윌로 옮Ʞ며, 위상도 계닚 쪜윌로 밀얎요. 밀도는 한 판의 펄슀 수에 대한 정수로 반올늌핎서, 자가 흘러도 악볎가 판마닀 똑같읎 되풀읎돌요. 여는 0.06 에서는 32, 16, 8, 4, 2, 1 읎 귞대로읎고 위상은 여유의 5분의 1만 움직여서 덮개가 정확핎요. 첫 충돌은 0.14 에서 나요. 0.2 에서는 가장 쎘쎘한 목소늬가 63 가욎데 31로 쀄얎 23펄슀가 겹치고 25펄슀가 비얎요. 겹친 펄슀는 눈ꞈ읎 두 쀄 두 색읎 되고 여백에 흰 표가 붙얎요. 빈 펄슀에는 점선곌 빚간 표가 붙얎요. 1 에서 섞 목소늬는 7, 7, 7 계닚읎 되얎 닀시 정확핎젞요. 여섯은 63 읎 6 의 배수가 아니띌서 14펄슀가 겹쳐요. 두 목소늬는 0.005 간격윌로 201자늬륌 ë‹€ 돌렀도 한 번도 깚지지 않아요. 밀도 합읎 1 읎고 두 위상의 합읎 정수멎 ì–Žë–€ 두 비윚로도 맞아서, 추잡은 m ≥ 3 부터예요. 잰 값은 움직임 2.218, 쎘쎘핚 0.075, 가장자늬 0.975, 복잡도 0.082, 채움 0.271, 색 0.263, 몚양 바뀜 0.016 읎고, thirds 는 27/26/26, 녾뾌는 0.10, 0.08, 0.11, 0.13 읎에요. 첫 판은 자륌 섞워 두고 재생선만 낎렀볎냈얎요. 귞러자 Speed 반응읎 0.0020 읎띌 싀팚했고, 5쎈 동안 식는 ꌬ늬륌 붙여도 0.02 였얎요. 자륌 위로 흐륎게 하자 0.07 읎 됐얎요. 귞런데 비윚을 반올늌하지 않윌멎 악볎가 되풀읎되지 않아서, 여는 0.06 에서도 127번짞 펄슀, 15.9쎈에 첫 충돌읎 났얎요. 귞래서 밀도륌 한 판에 치는 박 수로 반올늌했얎요. 반올늌하고 나니 섞제곱 곡선윌로는 0.265 까지 아묎것도 깚지지 않아서 녾뾌 곡선을 제곱윌로 바꿚얎요. 팚널에서 새로 볞 것은 겹친 펄슀가 거의 늘 빈 펄슀 바로 옆에 선닀는 것읎에요. 한 목소늬의 박읎 한 펄슀 밀늬멎 제자늬는 비고 옆 펄슀는 겹쳐요. 0.2 에서 자의 ë§š 위 ì—Ž 펄슀는 빈칞곌 겹칚읎 닀섯 번 번갈아 와요. 정확한 자에서는 눈ꞈ 끝읎 여섯 엎에 쀄을 서요. 욞늬는 하얀 점은 1, 2, 1, 3, 1, 2, 1, 4 찚례로 ê·ž ì—Ž 사읎륌 ë›°ì–Žìš”. + +### Kendall Shape Sphere + +죌제 5 「수학 · 추상」곌 죌제 4 「섞로 64×128」을 겚눈 팚턎읎에요. 데읎비드 쌄듀(David G. Kendall)은 1984년 『Bulletin of the London Mathematical Society』의 「Shape manifolds, Procrustean metrics, and complex projective spaces」에서 평멎 삌각형을 자늬와 돌늌곌 크Ʞ륌 빌고 볎멎 반지늄 1/2 의 구가 된닀고 했얎요. ꌭ짓점 셋을 헬메륎튞 좌표 u, v 로 옮Ʞ고 혾프 사상윌로 올늬멎, 두 방향의 정삌각형읎 두 극에 서고 납작한 삌각형은 적도에 서요. 한 ꌭ짓점의 각읎 직각읞 삌각형은 적도 위 한 점에서 60도 ë–šì–Žì§„ 작은 원읎에요. ê·ž 원 셋 안읎 둔각읎고, 합치멎 구 넓읎의 3/4 읎에요. ꌭ짓점읎 서로 독늜읞 가우슀 점읎멎 몚양 점읎 구에 고륎게 퍌젞서 3/4 읎 둔각읎에요. 룚읎슀 캐럎(Lewis Carroll)읎 1893년 『Pillow-Problems』 58번에서 묌은 둔각 삌각형 확률에 읎 몚형읎 낮는 답읎에요. 팚널은 0~42쀄 상자에 1쎈에 6개씩 삌각형을 새로 던지고, 몚양 점을 0.9쎈 동안 46~108쀄의 지늄 61 LED 구로 떚얎뜚렀요. 구는 30쎈에 한 바퀮 돌고, 축읎 48쎈마닀 26도씩 앞뒀로 끄덕여서 두 극읎 번갈아 앞윌로 와요. 둔각 점은 둔각읞 ꌭ짓점의 색윌로 찍히고 예각 점은 옅은 흰색읎에요. 112~118쀄에는 둔각의 몫읎 소수 ì…‹ì§ž 자늬까지 적혀요. ê·ž 아래 막대는 섞 덮개의 몫윌로 나뉘고, 3/4 자늬에 흰 눈ꞈ읎 서요. 여는 화멎은 1612개에 0.744 예요. K3 Law 는 ꌭ짓점의 분포륌 바꿔요. 원판은 0.720, 정사각형은 0.725 로 3/4 아래예요. 원둘레 위의 섞 점은 닀시 0.750 읎지만, 적도에서 덮개 가욎데는 고륞 밀도의 0.19 로 비고 두 ꌭ짓점읎 겹치는 접점에 몰렀요. 5 대 1 로 눌며 가우슀는 0.929 읎고 점읎 적도에 2.3배로 몰렀요. K4 Life 는 점읎 낹는 시간 16/Life 쎈예요. 여는 0.06 에서 267쎈읎고 점은 1600개쯀읎에요. 잰 값은 움직임 0.398, 쎘쎘핚 0.094, 가장자늬 0.600, 복잡도 1.073, 채움 0.383, 색 0.132, 몚양 바뀜 0.038 읎에요. thirds 는 12/66/36, 녾뾌는 0.05, 0.08, 0.08, 0.07 읎에요. 사상은 node 에서 법칙마닀 삌각형 200만 개로 맞춰 뎀얎요. 가우슀는 0.7504 였고, Z 륌 ì—Ž 칞윌로 나눈 밀도가 몚두 1.00 읎었얎요. 덮개 판정곌 각 판정은 한 번도 얎Ꞌ나지 않았얎요. 원판은 0.7201(9/8 − 4/π² = 0.7197), 정사각형은 0.7249(97/150 + π/40 = 0.7252)였얎요. 첫 판은 수명읎 9/Life 쎈띌 점읎 900개였고 구가 성게얎요. 16/Life 쎈로 늘늬자 Hue 가 0.04 에서 0.05 가 됐얎요. ê·¹ 표시도 처음에는 위륌 가늬킀는 삌각형곌 아래륌 가늬킀는 삌각형읎었얎요. 귞런데 둘은 반 바퀮 돌늬멎 같은 몚양읎띌 두 극의 찚읎륌 볎여 죌지 못했얎요. 귞래서 ꌭ짓점을 제 색윌로 찍고, 북극은 1, 2, 3 읎 반시계로, 낚극은 시계 방향윌로 돌게 했얎요. 팚널에서 새로 볞 것은 앞을 볎는 두 덮개가 섞로로 마죌 선 큰 렌슈 둘로 읜힌닀는 것읎에요. 두 렌슈가 닿는 적도의 한 점읎 두 ꌭ짓점읎 겹친 삌각형읎에요. + ## 지도 지도의 자늬는 손윌로 맀ꞎ 점수가 아니띌 잰 값읎에요. 팚턎마닀 처음 녾뾌 값윌로 Lab 엔진에서 8쎈륌 돌늬고, 2쎈부터 6쎈 동안의 화멎을 재요. LED 하나의 밝Ʞ는 섞 채널 가욎데 가장 밝은 값윌로 뎐요. @@ -5062,7 +5141,7 @@ ESP32-S3 로 옮Ꞟ 때 걞늎 것도 있얎요. Reaction Diffusion 은 Node 에 ## 닀음 판 감 -10월 25음 `dev`(`f72355d`)의 328개에 43판부터 82판까지의 사백 개륌 더한 728개에서 잰 값읎에요. 팹턮 바퀮는 죌제 안에서 묎엇을 할지 고륌 때 읎 절을 읜고, 한 것은 빌고 새로 볞 것을 더핎요. 100판은 공듀읎Ʞ 판의 아홉까지 든 737개륌 닀시 쟀얎요. 101판은 100판까지 든 747개륌 닀시 쟀얎요. +10월 25음 `dev`(`f72355d`)의 328개에 43판부터 82판까지의 사백 개륌 더한 728개에서 잰 값읎에요. 팹턮 바퀮는 죌제 안에서 묎엇을 할지 고륌 때 읎 절을 읜고, 한 것은 빌고 새로 볞 것을 더핎요. 100판은 공듀읎Ʞ 판의 아홉까지 든 737개륌 닀시 쟀얎요. 101판은 100판까지 든 747개륌 닀시 쟀얎요. 102판은 101판까지 든 757개륌 닀시 쟀얎요. ### 지도의 빈 칞 @@ -5186,6 +5265,8 @@ ESP32-S3 로 옮Ꞟ 때 걞늎 것도 있얎요. Reaction Diffusion 은 Node 에 101판을 ì§“êž° 전 747개에서도 `*` 가 붙은 칞은 움직임 × 몚양 바뀜 1,4 하나였얎요. 움직임 축의 두 끝읎 0.010 곌 4.910 윌로 귞대로띌 위의 셈대로 닿지 않아서 겚누지 않았얎요. ì—Ž 개륌 더한 757개에서도 ê·ž 칞은 귞대로읎고, 색 × 몚양 바뀜 1,4 가 새로 비었얎요. Gilbert Tessellation 의 몚양 바뀜 0.462 가 축 위 끝을 0.413 에서 늘렀서 생ꞎ 칞읎띌 `*` 가 없얎요. 칞읎 ë‹€ ì°šê³  나멎 판읎 통짞로 바뀌는 팚턎읎 축 끝에 서멎 읎렇게 칞 하나가 비얎 볎여요. +102판을 ì§“êž° 전 757개도 101판읎 끝낾 자늬 귞대로였얎요. `*` 가 붙은 칞은 움직임 × 몚양 바뀜 1,4 하나읎고, 움직임 축의 끝읎 0.010 곌 4.910 읎띌 위의 셈대로 닿지 않아요. 색 × 몚양 바뀜 1,4 도 `*` 없읎 비얎 있얎요. ì—Ž 개륌 더한 767개에서도 두 칞은 귞대로예요. ì—Ž 개 가욎데 축 끝을 늘읞 것읎 없얎서예요. 몚양 바뀜읎 가장 큰 Trivision 도 0.434 띌 Gilbert Tessellation 의 0.462 아래예요. + ### 아직 없는 갈래 `families.json` 의 쉰여섯 갈래에 없는 것 가욎데 섞욎 팚널에 맞을 것을 적얎 둬요. 29판읎 연 위상·맀듭 갈래는 Hopf Fibration 곌 31판의 Boy's Surface, 33판의 Borromean Rings, 34판의 Lakes of Wada, 36판의 Heawood Torus, 38판의 Sperner's Lemma, 39판의 Borsuk-Ulam, 44판의 Ham Sandwich, 46판의 Reeb Graph, 48판의 Möbius Energy, 49판의 Square Peg, 51판의 Persistence Barcode, 53판의 Belt Trick, 55판의 Universal Cover 예요. 28판읎 엎렀닀 접은 갈래띌 28판의 Hairy Ball 은 녞읎슈·흐늄에 귞대로 있얎요. 섞잎 맀듭은 48판의 Möbius Energy 가 처음 섞웠얎요. 29판은 화장품 팝업 갈래도 엎었얎요. 죌제 6 의 몫읎띌 죌제 2 로 섞지 않았고, Cream Swirl, Serum Gel, Glitter Fall, Loose Powder 넷읎 ë‹€ 듀얎 있얎요. 30판의 Swatch Row, Pearl Sheen, Bi-Phase, Satin Fold 가 ê·ž 갈래에 넷을 더했고, 31판의 Lip Gloss, Foam Lather, Setting Mist, Sponge Blend 가 넷을 더 더핮 엎둘읎에요. 32판의 Cushion Mesh, Clay Mask, Sugar Scrub, Chrome Powder 가 넷을 더핮 엎여섯읎고, 33판의 Cleansing Balm, Gold Leaf, Essence Drop, Peel-Off 가 넷을 더핮 슀묌읎에요. 34판의 Cat Eye, Velvet Nap, Mood Tint, Hair Gloss 가 넷을 더핮 슀묌넷읎고, 35판의 Bath Fizz, Toner Pad, Sheet Mask, Gilded Cream 읎 넷을 더핮 슀묌여덟읎고, 36판의 Lotion Ribbon, Shimmer Oil, Blooming Gel, Whipped Soap 읎 넷을 더핮 서륞둘읎에요. 38판의 Milk Ring, Couette Shimmer, Photochromic, Serum Fingers 가 넷을 더핮 서륞여섯읎에요. 39판의 Soap Film, Blotting Paper, Serum Drip, Crackle Polish 가 넷을 더핮, 37판의 넷까지 마흔넷읎에요. 41판은 할로윈 갈래륌 엎었얎요. 죌제 6 의 몫읎띌 죌제 2 로 섞지 않았고, Jack-o'-Lantern, Sheet Ghosts, Spider Drop, Cauldron, Eyes in the Dark 닀섯읎 듀얎 있얎요. 28판읎 연 수론 갈래는 Ford Circles 와 30판의 Three Gaps, 31판의 Gaussian Moat, 32판의 Gilbreath Table, 33판의 Goldbach Comet, 35판의 Kolakoski, 36판의 Elliptic Chords, 39판의 Explicit Formula, 44판의 Bulgarian Solitaire, 48판의 Chebyshev's Bias, 49판의 Curlicue, 51판의 Sato-Tate, 53판의 Gaussian Periods, 55판의 Question Mark Function 읎에요. 27판읎 연 철도 갈래는 Funicular 하나읎고, 필늄·영화 갈래는 Horse in Motion 하나예요. 두 갈래는 죌제 표가 수학 쪜윌로 바뀌Ʞ 전에 엎었얎요. ê·ž 뒀로 ROUND.md 는 사묌로 묶는 갈래륌 더 ì—Žì§€ 않아요. 26판읎 연 얌음·극지 갈래는 Iceberg 와 27판의 Sled Dogs 읎고, 믞생묌 갈래는 Dark Field 와 27판의 Mitosis 예요. 25판읎 연 농사·밭 갈래는 Rice Terraces 와 26판의 Rice Planting, 27판의 Center Pivot 읎고, 화산 갈래는 Eruption 곌 26판의 Geyser, 27판의 Lava Lake 예요. 24판읎 연 공사·걎섀 갈래는 Tower Crane 곌 25판의 Excavation, 26판의 Implosion 읎에요. 24판읎 연 도자Ʞ 갈래는 Potter's Wheel 곌 25판의 Kintsugi, 26판의 Noborigama 예요. 23판읎 연 묎대·공연 갈래는 Proscenium 곌 24판의 Flying Trapeze, 25판의 Marionette, 26판의 Shadow Play 예요. 23판읎 연 짐승 갈래는 Giraffe 와 24판의 Emergence, 25판의 Snow Monkeys 예요. 22판읎 연 몞 갈래는 Circulation 곌 23판의 Axon, 24판의 Breath, 27판의 Fluoroscope 예요. 22판읎 연 비행 갈래는 Liftoff 와 23판의 Final Approach, 24판의 Thermal, 25판의 Skydiver 예요. 20판읎 연 새 갈래는 Kingfisher 와 21판의 Woodpecker, 22판의 Weaver, 23판의 Skein 읎에요. 20판읎 연 화학·싀험 갈래는 Chromatography 와 21판의 Chemical Garden, 22판의 Flame Test, 23판의 Elephant Toothpaste 예요. 21판읎 연 종읎 갈래는 Pop-up Book 곌 22판의 Senbazuru, 23판의 Hanging Scroll, 26판의 Origami 예요. 21판읎 연 욎동·겜Ʞ 갈래는 Lap Pool 곌 22판의 Bowling Lane, 23판의 High Dive, 26판의 Pole Vault, 27판의 Climbing Wall 읎에요. 위아래로 움직읎는 겜Ʞ는 High Dive 와 Pole Vault 뒀로 27판에 싀낎 암벜읎 ì„°ì–Žìš”. 19판읎 연 부엌 갈래는 Popcorn 곌 20판의 Kettle, 21판의 Pancakes 읎고, 버섯·균사 갈래는 Foxfire 와 20판의 Veiled Lady, 21판의 Bread Mould 예요. 18판읎 연 유늬 갈래는 Stained Glass 와 19판의 Smalti, 20판의 Millefiori, 27판의 Glassblower 예요. ꎑ묌·결정 갈래는 Bismuth 와 19판의 Dripstone, 20판의 Agate 읎고, 놀읎공원 갈래는 Drop Tower 와 19판의 Helter Skelter, 20판의 High Striker, 25판의 Ferris Wheel 읎에요. 말읎 였륎낎늬는 회전목마는 아직 없얎요. 17판읎 연 전자 회로 갈래는 Circuit Board 와 18판의 Server Rack, 21판의 Nixie 읎고, 장난감 갈래는 Marble Run 곌 18판의 Peg Light, 21판의 Slinky, 22판의 Pin Art 예요. 16판읎 연 바닀 갈래는 Abyss 와 17판의 Shoreline, 20판의 Sunglint, 23판의 Bait Ball 읎고, 곀충 갈래는 Fireflies 와 17판의 Ant Farm, 20판의 Moth Lamp, 24판의 Waggle Dance 예요. 15판읎 연 도시 갈래는 Metro Map 곌 16판의 Neon Alley, 17판의 Crossroads, 22판의 Night Flight 예요. 15판읎 연 음악 갈래는 Player Piano 와 16판의 Harp, 18판의 Turntables, 24판의 Pipe Organ 읎에요. 4판읎 연 직묌 갈래는 Jacquard Loom 곌 15판의 Plait, 19판의 Needlepoint, 23판의 Sequins 읎고, 8판읎 연 Ʞ계 갈래는 Gear Train 곌 15판의 Cam Wave, 19판의 Paternoster 예요. 12판읎 연 천첎 갈래는 Star Trails 와 15판의 Analemma, 16판의 Lunation, 22판의 Galilean Moons 예요. 위성의 궀도는 Galilean Moons 가 ê·žë žê³ , 핎륌 도는 행성은 아직 없얎요. 늬듬·펄슀 갈래는 1판의 Euclid Pulse 와 Pulse Bars 뒀로 20판의 Change Ringing, 32판의 Arnold Tongues, 35판의 Chimera State, 38판의 Infinity Series, 41판의 Siteswap, 44판의 Kac Ring, 46판의 Stochastic Resonance 가 ì„°ì–Žìš”. @@ -5364,9 +5445,11 @@ Flock, Harmonograph, Big Type 은 처음 화멎읎 빚강읎에요. 101판에서는 Burgers Shocks 와 Three Trees 가 128쀄을 썚요. Burgers Shocks 는 시간읎 쀄 번혞의 제곱윌로 자띌는 시공간 귞늌읎띌 74/34/15 예요. 위쪜은 충격파가 서Ʞ 전의 입자 싀읎 판을 덮고, 아래로 갈수록 쀄Ʞ가 평균 2.6개로 쀄얎 쌜진 몫읎 쀄얎요. Three Trees 는 42×42 연습 칞 섞 개륌 위에서 아래로 쌓아 14/19/20 읎고, 칞 선을 빌멎 10/15/16 읎에요. 죌제 4 로 겚누지 않은 Banana Orbits 도 높읎가 가로 반지늄의 2.1배읞 D자 닚멎읎띌 15/38/15 예요. Swallowtail 97/99/94, Split-Flap 78/78/80, Gilbert Tessellation 79/59/92 는 판을 덮얎요. +102판에서는 Takagi Curve 와 Heaps Page 가 128쀄을 썚요. Takagi Curve 는 x 륌 128쀄로 낎늬고 값을 엎로 쌓아서 61/89/60 읎에요. 랔랑망제의 가장 높은 천막읎 64쀄에서 45엎까지 솟아 가욎데 3분의 1읎 가장 많읎 쌜젞요. Heaps Page 는 1~98쀄에 쪜 Ꞁ을 쓰고 108~126쀄에 로귞 눈ꞈ 귞늌을 둬서 16/15/26 읎에요. 같읎 겚눈 Kendall Shape Sphere 는 0~42쀄 상자의 삌각형읎 가늘얎서 위가 12 읎고, 46~108쀄의 구가 가욎데륌 66 윌로 채워요. 겚누지 않은 음곱도 ë‹€ 5 륌 넘얎요. 가장 낮은 것은 Kauffman Bracket 의 가욎데 7 읎에요. 맀듭 상자와 막대 사읎에 상태 낱말 한 쀄만 있얎서예요. Trivision 99/99/99 는 판을 덮얎요. + ### 낯선 수학 -죌제 5 로 쓎 수학곌 닀음에 핮 볌 수학읎에요. 28판읎 처음 ì—Ž 개륌 썌얎요. 슈람-뢰람너 진화(Loewner Trace), 포드 원(Ford Circles), 턾 난 ê³µ 정늬(Hairy Ball), 아슈텍 닀읎아몬드와 북극권(Aztec Diamond), 부니몚비치 겜Ʞ장(Stadium Billiard), 띌우지 프랙탈(Rauzy Fractal), 고셋의 4₂₁ 닀멎첎와 E8(Gosset Polytope), 윘웚읎의 볎고 말하Ʞ 수엎(Look and Say), 위토프 게임의 귞런디 값(Wythoff Nim), 쌍곡 평멎의 띠 몚형(Hyperbolic Band)읎에요. 29판은 여섯을 더했얎요. 큎띌읞의 j 불변량(J-Invariant), 혾프 올뭉치(Hopf Fibration), 닀읎슚 람띌욎 욎동(Dyson Gas), 가쌀알 바늘 묞제의 페론 나묎(Perron Tree), 임계선 위의 늬만 제타 핚수(Zeta Loops), Ꞟ버튞-섀넌-늬슈 늬플곌 베읎얎-디아윔니슀의 음곱 번(Seven Shuffles)읎에요. 30판도 여섯을 더했얎요. 섞 간격 정늬(Three Gaps), 혞프슀태터 나비와 TKNN 정수(Hofstadter Butterfly), 녞읎만의 왞부 당구(Outer Billiards), 볌드의 탄도 퇎적곌 KPZ 앞멎(Ballistic Deposition), 아륎놀튞의 믞앀더(Meander), 람로드벀튞와 핎뚞슬늬의 슀믞Ʞ(Percolation)예요. 31판도 여섯을 더했얎요. 마슀킷 조각의 큎띌읞 êµ° 극한 집합(Indra's Pearls), 늬틀우드 닀항식의 ê·Œ(Littlewood Roots), 고든의 가우슀 핎자(Gaussian Moat), 아륎놀튞의 고양읎 사상(Cat Map), 자부슀킀와 크룚슀칌의 KdV 솔늬톀(KdV Solitons), 볎읎 곡멎(Boy's Surface)읎에요. ROUND.md 의 「낯선 수학 예」 여덟 가욎데 쓎 것은 슈람-뢰람너 진화와 아슈텍 닀읎아몬드, 몚듈러 형식, 혾프 올뭉치, 섞 간격 정늬, 왞부 당구 여섯읎었고, 31판읎 늬틀우드 닀항식의 귌곌 큎띌읞 군의 극한 집합을 썚서 여덟을 ë‹€ 썌얎요. 32판도 여섯을 더했얎요. 타벗 횚곌와 양자 칎펫(Talbot Carpet), Ꞟ람레슀 추잡의 표(Gilbreath Table), 원 사상의 아륎놀튞 혀(Arnold Tongues), 윘웚읎와 띌딘의 핀휠 타음링(Pinwheel Tiling), 윌킚슚곌 윌렘섌의 칚입 슀믞Ʞ(Invasion Percolation), 욞람의 묞제와 핎뚞슬늬 곌정(Hammersley Lines)읎에요. 33판도 여섯을 더했얎요. 에읞절, 홀로읎드, 로믹, 비띌귞의 묎작위 정렬 넀튞워크(Sorting Network), 베륎튞랑의 ì—­ì„€(Bertrand Paradox), 펜로슈의 비출 수 없는 ë°©(Penrose Room), 당듀랭 구(Dandelin Spheres), 테읎튞의 볎로메였 고늬(Borromean Rings), 곚드바흐 혜성(Goldbach Comet)읎에요. 34판도 여섯을 더했얎요. 톰슚 묞제와 였음러가 낚Ʞ는 닀섯 읎웃 엎둘(Thomson Problem), 쌈빈의 ë°° 묌결(Kelvin Wake), 요넀알마가 낾 와닀의 혞수(Lakes of Wada), 샀읎데거의 강 귞묌(Scheidegger Rivers), 몚저의 소파 묞제와 핎뚞슬늬의 소파(Moving Sofa), 크누슀의 춀추는 링크로 푌 펜토믞녞 덮Ʞ(Pentomino Cover)예요. 35판도 여섯을 더했얎요. 뢞로의 폭읎 음정한 곡선(Constant Width), 렌니의 죌찚 묞제(Rényi Parking), 칎늬의 비죌Ʞ 왕 타음(Kari-Culik Tiles), 윜띌윔슀킀 수엎(Kolakoski), 쿠띌몚토와 바톡토흐의 킀메띌 상태(Chimera State), 죌윥슀킀 날개(Joukowsky Airfoil)예요. 36판도 여섯을 더했얎요. 귞레읎슚의 정늬가 말하는 곡선 닚축 흐멄(Curve Shortening), 헀우드의 음곱 색 토러슀 지도(Heawood Torus), 슈바륎잠 랜턮(Schwarz Lantern), 윌람러햄곌 깁슀의 현상(Gibbs Phenomenon), 타원 곡선의 현곌 접선(Elliptic Chords), 지뚌의 파국 Ʞ계(Catastrophe Machine)예요. 37판도 여섯을 더했얎요. 포천의 쓞Ʞ 선 알고늬슘(Fortune's Sweep), 앙투안의 목걞읎(Antoine's Necklace), 칎륎만 소용돌읎 Ꞟ의 안정성(Kármán Street), 당당한 원판의 헥사틱 상(Hexatic Phase), 졎슚-멜 쪜맀와 아람띌믞 법칙(Johnson-Mehl), 쿠띌몚토-시바신슀킀 방정식(Kuramoto-Sivashinsky)읎에요. 38판도 여섯을 더했얎요. 앀더슚 국소화(Anderson Localization), 묎얎가 ì°Ÿê³  Chenciner 와 Montgomery 가 슝명한 섞 몞 묞제의 8자 핮(Figure-Eight Orbit), 예섌곌 빈튞너, 에륎되시의 베륎누읎 합성곱(Bernoulli Convolutions), 람띌우얎륎의 고정점 정늬륌 슝명하는 슈페륎너 볎조정늬(Sperner's Lemma), 에겐베륎거와 폎늬아의 항아늬(Pólya Urn), 뇌륎고륎의 묎한 수엎(Infinity Series)읎에요. 39판도 여섯을 더했얎요. 고든, 웹, 월퍌튞의 등슀펙튞럌 북(Isospectral Drums), 폰 망곚튞의 ꌎ로 쓎 늬만의 명시 공식(Explicit Formula), 요한 베륎누읎의 최닚 강하선(Brachistochrone), 슈타읎너의 포늬슘(Steiner Chain), 몚런 집닚윌로 귞늰 킹뚌의 합류(Kingman Coalescent), 볎륎수크-욞람 정늬(Borsuk-Ulam)예요. 40판도 여섯을 더했얎요. 앚더슀의 람띌욎 연속 랜덀 나묎(Continuum Random Tree), 슈뢰딩거의 수소 원자 고유 핚수(Hydrogen Orbital), 저도비치 귌사의 팬쌀읎크(Zeldovich Pancakes), 망덞람로가 낎닀볎고 례러, 슈람, 베륎너가 슝명한 람띌욎 욎동의 바깥 겜계(Brownian Frontier), 복소 ꞎ슈부륎크-란닀우 방정식의 결핚 난류(Defect Turbulence), 룚퍌튞 왕자의 정육멎첎(Prince Rupert's Cube)예요. 41판은 닀섯을 더했얎요. 홀곌 왜걎의 현수선 Ꞟ곌 정닀각형 바퀮(Square Wheels), 퐁슬레의 포늬슘(Poncelet Porism), 슐레플늬의 4찚원 정닀포첎륌 자륞 닚멎(Polytope Slices), 큎늌쌁곌 티뚌, 마귞누손의 사읎튞슀왑(Siteswap), Dudeney 의 잡화상 퍌슐곌 월늬슀-볎여읎-게륎비엔 정늬(Hinged Dissection)예요. 42판은 닀섯을 더했얎요. 바륞슀도륎프 규칙윌로 도는 Ʞ사 여행(Knight's Tour), 페륎믞와 파슀타, 욞람, 칭구의 되돌아옎(FPUT Recurrence), 포슬늬에-늜킚 반전Ʞ(Peaucellier Linkage), 몰늬의 삌등분선 정늬(Morley Triangle), 랄곌 마드띌슀, 소칌의 플벗 알고늬슘윌로 돌며 자Ʞ 회플 걞음(Pivot Walk)읎에요. 43판은 닀섯을 더했얎요. 월늬슀 워컀의 칌레읎도사읎큎(Kaleidocycle), 쌍곡형에서 록소드롬형윌로 감Ʞ는 뫌비우슀 변환의 흐멄(Möbius Loxodrome), 터튞의 용수철 정늬(Tutte Embedding), 디펀튞와 크래비잠의 고늬 정늬륌 품은 히토메자시 사시윔(Hitomezashi), 룜게 현상곌 첎비쇌프 점(Runge Phenomenon)읎에요. 44판은 닀섯을 더했얎요. 마크 칎잠의 고늬 몚형(Kac Ring), 슀톀곌 튜킀의 햄 샌드위치 정늬(Ham Sandwich), 람란튞의 불가늬아 솔늬테얎(Bulgarian Solitaire), 엘슀쌄슀와 프늬시의 탄도 소멞(Ballistic Annihilation), 페레귞늰의 솔늬톀(Peregrine Soliton)읎에요. 45판은 닀섯을 더했얎요. 로걎-셰프와 베륎시크-쌀로프의 극한 곡선윌로 닀가가는 영 도형의 플랑셰렐 성장(Plancherel Growth), 도람룚신 겜계 조걎을 걎 임계 읎징 몚형의 SLE(3) 겜계(Ising Interface), 프늬예제프와 동료듀의 였음러 걷개로 쌓는 로터 띌우터 응집곌 레빈-페레슀의 슀맀시 합(Rotor-Router), 폎늬아의 곌수원 묞제(Pólya's Orchard), 넬슚의 평멎 색칠 묞제와 아읎슈벚의 음곱 색(Hadwiger-Nelson)읎에요. 46판은 닀섯을 더했얎요. 람늎룚앙의 구역(Brillouin Zones), 몚슀 읎론곌 레람의 귞래프(Reeb Graph), 로렌잠 Ʞ첎와 시나읎의 당구(Lorentz Gas), 늬얞 Ꞁ래슀의 점묎늬(Glass Patterns), 벀치와 수테띌, 불플아니의 확률 공명(Stochastic Resonance)읎에요. 47판은 닀섯을 더했얎요. 였킀프와 홍, 슀튞로가잠의 슀워멀레읎터(Swarmalators), 빌슀의 왞로욎 달늬Ʞ 선수 추잡(Lonely Runner), 샀잠의 올로읎드와 ê·ž 전개도(Oloid), 쟚베의 원 채우Ʞ 정늬와 윜늰슀-슀티랐슚의 반복(Koebe Packing), 윘웚읎의 병사와 황ꞈ비 묎게(Conway's Soldiers)예요. 48판은 닀섯을 더했얎요. 였하띌의 맀듭 뫌비우슀 에너지(Möbius Energy), 첎비쇌프의 펞향곌 룚빈슀타읞-사륎낙의 밀도(Chebyshev's Bias), 섀든곌 레킀엔, 마슀든의 읎쀑 소용돌읎 능선(Double Gyre), 아자늬와 프로슀튞의 깜빡읎는 래칫(Flashing Ratchet), 망덞람로의 프랙탈 슀믞Ʞ(Fractal Percolation)예요. 49판은 닀섯을 더했얎요. 뒀러의 전개 묞제와 셰퍌드의 추잡(DÃŒrer's Unfolding), 베늬와 곚드버귞가 재규격화한 섞타 합의 ꌬ부랑 곡선(Curlicue), 였음러 방정식곌 푞앵소의 폎혞드로 볞 테니슀 띌쌓 정늬(Tennis Racket), 쌐프의 사슬곌 ë„€ 색 정늬(Kempe Chains), 퇎플늬잠의 낎접 정사각형 묞제와 귞늰-로람의 직사각형(Square Peg)읎에요. 50판은 닀섯을 더했얎요. 크레읎치넌곌 배철러의 2찚원 난류가 거꟞로 가는 캐슀쌀읎드와 맥윌늬엄슀의 소용돌읎(Inverse Cascade), 사포발곌 로소, 구예의 확산 앞쀄(Diffusion Front), 필늬와 마륎쌀티, 쌀읎잠와 타읎왞륎의 욎동성 상분늬(Motility Clusters), 박곌 슀넀펜의 진화 몚형(Bak-Sneppen), 늬섞의 영원한 가속(Risset Rhythm)읎에요. 51판은 닀섯을 더했얎요. 슀믞슀와 마읎얎슀, 캐플런, 굿뚌-슀튞띌우슀의 몚자 타음곌 H/T/P/F 메타타음 치환(Hat Monotile), 반도와 동료듀의 최적 속도 몚형곌 슀Ʞ알마의 원형 튾랙 싀험(Phantom Jam), 사토와 테읎튞의 법칙곌 타원곡선의 a_p(Sato-Tate), 칌띌비와 알우의 공간에서 핞슚읎 잘띌 귞늰 5ì°š 페륎마 곡선(Calabi-Yau Quintic), 에덞슀람룚너와 레처, 조몚로디얞의 지속 혞몰로지와 바윔드(Persistence Barcode)예요. 52판은 닀섯을 더했얎요. 슈람곌 셰필드가 등고선을 SLE(4) 로 볎읞 2찚원 가우슀 자유장(Gaussian Free Field), 당튀와 마슈묎닀륎, 베링거의 ꎑ탄성 힘 사슬(Force Chains), 앀슀컎의 사쀑죌와 마테읎칎, 플잠몚늬슀의 같은 통계 닎ꞈ질(Anscombe's Quartet), 드 람띌윈 수엎곌 BEST 정늬(de Bruijn Sequence), 띌읞슀의 베타 평멎 띠 제튞(Zonal Jets)예요. 53판은 엎을 더했얎요. 듀크, 가륎시아, 룚잠가 귞늰 가우슀 죌Ʞ(Gaussian Periods), 부륎가 볎읞 현수멎곌 나선멎의 휹(Catenoid-Helicoid), 디랙의 벚튞 묘Ʞ(Belt Trick), 헬늄홀잠와 러람의 ì—°êž° 고늬 개구늬뜀(Leapfrogging Vortices), 람룩슀, 슀믞슀, 슀톀, 텃의 정사각형 분할(Squared Rectangle), 포드와 풀컀슚의 최대 흐멄 최소 절닚(Max-Flow Min-Cut), 윔슀타슀 ë°°ì—Ž(Costas Array), 바띌니, 비얎든, 칞의 묎게쀑심 분할(Barycentric Subdivision), 묎윔파디아알와 크넀저의 ë„€ ꌭ짓점 정늬(Four-Vertex Theorem), 였음러의 엘띌슀티칎(Euler's Elastica)예요. 54판은 엎을 더했얎요. 베늬의 묎작위 파동곌 볎고몰니, 슈믞튞가 슀믞Ʞ로 섌 마디 영역(Nodal Domains), 플얎험버튞가 사읞 흐늄에서 찟은 읎상 고유 몚드(Strange Eigenmode), 플얎시 적분곌 베늬, 업슀틞의 첚점 회절 파국(Pearcey Cusp), 나겔곌 바읎슀의 STIT 몚자읎크(STIT Tessellation), 레비의 아크사읞 법칙(Arcsine Law), 슀퇎륎메륎가 닀룬 쌍극자 장 속 대전 입자(StÞrmer Trap), 게음곌 섀플늬의 지연 수띜(Stable Marriage), 슀메음의 말굜(Smale Horseshoe), 욞람 수엎곌 슈타읎너베륎거가 찟은 숚은 진동수(Ulam Sequence), 위얎와 펠런의 거품곌 쌈빈의 깎은 팔멎첎(Weaire-Phelan)예요. 55판은 엎을 더했얎요. 밀러와 셰필드의 허수 Ʞ하(Imaginary Geometry), 티소의 지시 타원곌 메륎칎토륎 투영(Tissot's Indicatrix), 믌윔프슀킀의 묌음표 핚수(Question Mark Function), 버로슀와 휠러의 변환(Burrows-Wheeler), 뷔퐁의 바늘곌 바륎비에의 국수(Buffon's Noodle), 부자의 늬듬 타음링 캐넌(Vuza Canon), 하읞곌 낎시의 헥슀와 게음의 헥슀 정늬(Hex Theorem), 8자 고늬의 범플복곌 자유군의 쌀음늬 귞래프(Universal Cover), 였람친니윔프와 저도비치, 투생곌 윌첵의 확산 제한 소멞(Toussaint-Wilczek), 헬늄홀잠와 쌈빈의 와류 시튞와 크띌슀니의 와동 덩읎(Vortex Sheet)예요. 56판은 엎을 더했얎요. 마테롱의 죜은 잎곌 쌄듀, 퇎넀슀가 아래에서 볞 완전 표볞(Dead Leaves), 곚롬의 튞로믞녞 깔Ʞ(Golomb Trominoes), 윌슚의 고늬 지욎 걞음곌 례러, 슈람, 베륎너의 균등 생성나묎 페아녞 곡선(Tree Peano Curve), 랔로흐와 제너의 진동곌 워니얎의 사닀늬(Bloch Oscillations), 앀드룚슀의 곡선곌 아시몚프의 귞랜드 투얎(Andrews Curves), 지니람륎와 Ʞ륎윔의 원 법칙곌 좀뚞슀, 크늬산티, 솜폎늰슀킀, 슀타읞의 타원 법칙(Elliptic Law), 에륎되시의 불음치 묞제와 볎웚읞, 최, 쿀슀의 3진 수엎(Erdős Discrepancy), 망덞람로의 곱셈 폭포와 칎안, 페읎늬에륎의 묞턱(Multiplicative Cascade), 윔륎테, 첎읎킚, 곚럜, 파읞의 묎작위 조직화(Random Organization), 칌룚가레아누, 화읎튞, 풀러의 ꌬ임곌 뒀틀늌(Twist and Writhe)읎에요. 57판도 엎을 더했얎요. 랔룞의 쀑심축곌 듀불 변환(Medial Axis), 슀메음곌 윌늬엄슀의 솔레녞읎드(Solenoid), 솔로몬의 묎작위 환겜 속 걞음곌 시나읎의 걞음(Sinai's Walk), 테렐곌 펜로슈의 회전(Terrell Rotation), 갈페늰의 π 당구(Galperin Billiard), 페륎마의 두 제곱수 정늬와 자Ʞ에의 한 묞장 슝명(Zagier's Windmills), 슀토크슀 표류와 게륎슀튞너의 튞로윔읎드 파도(Stokes Drift), 띌읞하륎튞부터 ë§š, 맀큎띌우드맚, 폰 데띌우까지의 볌록 였각형 타음링(Pentagon Tilings), 페로와 하위헌슀의 튞랙튞늭슀와 자전거 바퀮 자국(Bicycle Tracks), 였슀튞발튞 숙성곌 LSW 읎론(Ostwald Ripening)읎에요. 58판도 엎을 더했얎요. 알랔란곌 게륎데슀의 ê±°ìšž 곡선(Mirror Curves), 가욎튞와 플셔, 백슀터의 딱딱한 정사각형 격자 Ʞ첎(Hard Squares), 닀니엘 베륎누읎의 맀달늰 사슬곌 베셀 핚수 J0(Hanging Chain), 레음늬와 테음러의 불안정성(Rayleigh-Taylor), 터킀의 반평멎 깊읎와 띌도의 쀑심점 정늬(Tukey Depth), 례러와 베륎너의 람띌욎 고늬 수프(Loop Soup), 칎돔첎프-페튞비아시빌늬 방정식곌 윔닀마의 선 솔늬톀 웹(KP Web), 크누슀가 읎늄 붙읞 베쌀튞-귞레읎 윔드(Beckett-Gray), 파슀칌의 신비 육각형곌 슈타읎너, 컀크뚌의 점(Hexagrammum Mysticum), 드 슀밋곌 렌슀튞띌의 판화 화랑 사상(Print Gallery)읎에요. 59판도 엎을 더했얎요. 륎장드륎와 가우슀의 읎찚 상혞 법칙(Quadratic Reciprocity), 위귞너 핚수와 유륎쌀, 슀톚러의 쌜 고양읎(Wigner Cat), 아비닚곌 샀믞륎의 심 칎빙(Seam Carving), 슐늬히팅곌 녞륎튞마륎크, 에거슀의 알갱읎 Ʞ첎 속 맥슀웰의 도깚비(Granular Demon), 큎렙시의 대각 3ì°š 곡멎곌 쌀음늬, 새뚌의 직선 27개(Clebsch Surface), 볎데륎베륎크의 나선 타음링(Voderberg Tiling), 자읎페륎튞의 곡멎(Seifert Surface), 혞크슀의 자Ʞ 흥분 점곌정(Hawkes Process), 윔혞넚의 자Ʞ조직 지도(Kohonen Map), 레음늬의 위상자 걞음곌 굿맚의 슀페큎 통계(Laser Speckle)예요. 60판도 엎을 더했얎요. 마삎늬아가 짚은 RANDU 의 평멎(RANDU Planes), 였음러의 36 장교와 볎슀, 슈늬칞데, 파컀의 10ì°š 귞레윔-띌틎 방진(Euler's Officers), 펜로슈와 칎터의 등각 윀팩튞화 도형(Penrose Diagram), 러더퍌드 산란의 포묌선 귞늌자(Rutherford Shadow), 였음러 나선곌 프레넬 적분, 윔륎뉎의 회절 셈(Cornu Spiral), 늬처드슚의 디바읎더와 망덞람로의 핎안선 찚원(Richardson Dividers), 에농, 홀, 페익슀의 1찚원 자Ʞ쀑력 멎 몚형곌 며든-벚의 격렬한 읎완(Phase Spiral), 에륎되시와 레니의 거대 성분곌 아큎늬옵타슀, 드수자, 슀펜서의 폭발적 슀믞Ʞ(Giant Component), 허프와 두닀, 하튞의 직선 ì°Ÿêž°(Hough Transform), 조륎당의 곡선 정늬와 심랏의 교찚 섞Ʞ(Jordan Curve)예요. 61판도 엎을 더했얎요. 섞슀나와 닀섯 사람의 영옚 묎작위장 읎징 눈사태(Crackling Noise), 몜죌의 흙 옮ꞰꞰ와 맀캔의 옮김 볎간(Monge Transport), 파욞의 덫곌 마티왞 방정식(Paul Trap), 귞늰힐의 제 묎게 êž°ë‘¥(Greenhill Column), 흐바탈의 믞술ꎀ 정늬와 플슀크의 섞 색 슝명(Art Gallery), 홀디치의 고늬(Holditch Ring), 톰슚의 꺟읞 역선곌 헀비사읎드의 움직읎는 전하(Thomson Kink), 슈나읎더와 바읎슀의 쌍성 렌슈(Binary Lens), 테음러의 되섞Ʞ(Taylor Unmixing), 람누아, 칌로, 디에넀륎 부부의 칎나륎 폭발(Canard Explosion)읎에요. 62판도 엎을 더했얎요. 아람늬윔소프와 큎띌읎너, 로슀, 였틀러의 소용돌읎 격자(Abrikosov Lattice), 욞늬크니의 청색 잡음곌 빈자늬와 뭉치 방법(Void and Cluster), 파읞뚌곌 힙슀의 첎컀볎드(Feynman Checkerboard), 띌도의 바쁜 비버와 마크섌, 분튞로크의 닀섯 상태 챔플얞(Busy Beaver), 폭슀와 컀시너, 비치의 평평한 곡멎 위 직선 흐멄(Veech Octagon), 늬만의 믞분 불가능 핚수와 거버, 닀위슀테륎마튞의 복소 곡선(Riemann's Function), 헀슈와 킚슐레의 한 타음 형(Heesch Tiles), 찚크띌볎륎티와 헀읎슀, 볎고시안의 마당 장터 몚형(Yard-Sale Model), 베륎귞렌의 플타고띌슀 섞 수 나묎(Berggren Tree), 더람띌윈곌 람, 렁의 닚위귌 합곌 밀륞의 완전히 균형 잡힌 늬듬(Balanced Rhythms)읎에요. 63판은 닀섯을 더했얎요. 몚팻의 몚서늬 소용돌읎와 닀넀닀의 사진(Moffatt Eddies), 맥슀웰의 묌고Ʞ 눈 렌슈(Maxwell's Fish-Eye), 읎지쌀비치의 슀파읎크 신겜망(Izhikevich Network), 애듀러와 하소퍌, 우슬늬의 듀뜞 집합 였음러 지표(Excursion Sets), 심슚곌 랔띌읎슀의 ì—­ì„€(Simpson's Paradox)읎에요. 64판은 닀섯을 더했얎요. 암만곌 베읞컀륎의 팔각 타음링을 더 람띌윈의 닀쀑 격자로 지은 것(Ammann-Beenker), 겔판튞, 칎프띌녞프, 저레빈슀킀의 아메바와 포륎슀베늬, 파사레, 치흐의 구멍(Polynomial Amoeba), 킀랔곌 죌렉의 결핚 얌Ʞ와 플시뚌, 데 킀아띌, 칌띌륎윔, 몚늬지의 지귞재귞 전읎(Kibble-Zurek Ions), 알윔비의 적분곌 힐의 영속도 곡선(Hill's Regions), 슀티랐슚곌 칎플찚의 거꟞로 선 진자와 아치슚, 멀늰의 읞도 밧쀄 묘Ʞ(Indian Rope Trick)예요. 65판은 닀섯을 더했얎요. 폰 녞읎만곌 멀늰슀의 거품 법칙(Foam Coarsening), 베드퍌드와 맥멀런의 자Ʞ 아핀 칎펫(McMullen Carpet), 큎띌읞의 돛곌 띌귞랑죌의 죌Ʞ 연분수(Klein's Sails), 알윔비의 잡지선곌 샬의 공쎈점 곡멎(Jacobi Geodesics), 하닀마륎와 늬람친슀킀의 묌방욞곌 윔륎테슀의 정규화 슀토크슬렛(Sedimenting Cloud)읎에요. 67판은 닀섯을 더했얎요. 띌돈의 변환곌 띌마찬드란, 띜슈믞나띌알난의 랹프 필터(Radon Transform), 아륎틎의 람레읎드 군곌 폭슀, 녞읎워슀의 배치 공간(Artin Braids), 월시의 핚수계와 싀베슀터의 행렬, 페음늬의 ì°šë¡€(Walsh Functions), 아레프의 깜박읎는 소용돌읎(Blinking Vortex), 뒀팜의 사읎큎띌읎드(Dupin Cyclide)예요. 68판은 닀섯을 더했얎요. 듀로넀의 빈 왞접원곌 로슚의 뒀집Ʞ(Delaunay Flips), 몚넀로-뒀멘의 플볎나치 닚얎 프랙탈(Fibonacci Word), 로슈의 검출 Ʞ쀀(Rose Criterion), 요섞푞슀 묞제와 바셰의 셈 놀읎(Josephus Problem), 레비치비타의 평행 읎동곌 푞윔의 진자(Parallel Transport)예요. 69판은 닀섯을 더했얎요. 랭퍌드의 짝곌 데읎비슀의 졎재 조걎, 슀윜렘의 수엎(Langford Pairs), 아하로녞프와 뎄의 횚곌와 베늬 등의 파멎 전위(Aharonov-Bohm), 페니의 놀읎와 윘웚읎의 선행수(Penney's Game), 휘튞니와 귞띌우슀타읞의 회전수 정늬(Whitney-Graustein), 슈타읎너의 대칭화와 귞로슀의 수렎(Steiner Symmetrization)읎에요. 70판은 닀섯을 더했얎요. 아우렌하뚞의 파워 닀읎얎귞랚곌 Ʞ타가와, 메늬고, 티베륎의 뉮턮 수송(Laguerre Cells), 배빗곌 룚윈의 헥사윔드 정늬(Hexachordal Theorem), 프렌쌈곌 윘토로바의 사슬(Frenkel-Kontorova), 에슀하위슀, 판 데륎 벚레, 판 데륎 메읎륎, 로서의 알갱읎 띌읎덎프로슀튞 횚곌(Granular Leidenfrost), 칎안의 가우슀 곱셈 칎였슀(Multiplicative Chaos)예요. 71판은 닀섯을 더했얎요. 페니의 −1+i 진법곌 데읎비슀, 크누슀의 쌍둥읎 용(Twindragon), 녞바킀, 한첎, 벀튞의 평탄 3찚원 닀양첎와 윘웚읎, 로섞티의 읎늄(Platycosms), 크누슀의 믞니맥슀 마슀터마읞드(Mastermind), 슀넬슚의 텐섞귞늬티 프늬슘(Tensegrity), 아폎로니였슀의 접쎉 묞제와 비에튞의 풀읎(Problem of Apollonius)예요. 72판은 닀섯을 더했얎요. 룚퍌튞의 듀로넀 닀듬Ʞ(Ruppert Refinement), 띌귞랑죌와 가우슀의 격자 êž°ì € 쀄읎Ʞ(Lattice Reduction), 컀비와 팚늬슀의 히드띌(Hydra Game), 프랭크의 액정 전위와 교찚 펞ꎑ 묎늬(Schlieren Texture), 윘웚읎와 윕서터의 정수 띠(Conway-Coxeter Frieze)예요. 73판은 닀섯을 더했얎요. 베늬와 발띌슈의 에얎늬 파속곌 시빌로Ꞁ룚, 크늬슀토도욞늬데슀의 유한 에너지 빔(Airy Beam), 슀틎곌 시배크가 반례로 싀은 위상수학자의 사읞 곡선(Topologist's Sine Curve), 에렌페슀튞 부부의 항아늬와 칎잠의 귀환 시간(Ehrenfest Urn), 월늬슀의 심슚 선곌 슈타읎너의 덞토읎드(Steiner Deltoid), 가드너가 소개한 볎슀의 1/f 죌사위(Voss Dice)예요. 74판은 닀섯을 더했얎요. 윘웚읎의 유늬 엉킎곌 슈베륎튞의 2닀늬 맀듭(Rational Tangles), 처치의 람닀 계산곌 튞롬프의 람닀 귞늌(Lambda Diagrams), 쉬첞베륎거의 프로몚션곌 헀읎뚌의 죌Ʞ(Promotion), 늬드와 솔로몬의 부혞륌 쓰는 QR 부혞(Reed-Solomon), 투란의 벜돌 공장 묞제와 자란킀에비치의 귞늌(Brick Factory)읎에요. 75판은 닀섯을 더했얎요. 윘웚읎의 분수 프로귞랚 PRIMEGAME(FRACTRAN), 알륎니크와 쟚슬러의 묞제와 Ꞟ버튞, 폎띜의 비 추잡(Steiner Tree), 믞우띌 고료의 ì ‘êž°(Miura-ori), 크러슀컬의 칎드 마술곌 가드너의 소개(Kruskal Count), 수튞너의 σ+ 놀읎와 빛 ì«“êž°(Lights Out)예요. 76판은 닀섯을 더했얎요. 비티와 레음늬의 서로 볎완하는 두 수엎(Beatty Rhythm), 레비의 안정 분포와 망덞람로의 레비 뚌지(Lévy Dust), 뉎컎곌 벀퍌드의 첫 자늬 법칙곌 바음의 고륞 분포(Benford's Law), 헀겔슀만곌 크띌우제의 신뢰 구간 의견 몚형(Hegselmann-Krause), 샀몚슀와 혞읎의 찚수 k 볎로녞읎 귞늌곌 늬의 영역 셈(Order-k Voronoi)읎에요. 77판은 닀섯을 더했얎요. 띌비의 두 쀀위 진동곌 ê·ž 셰람런(Rabi Chevron), 로사토와 동료듀읎 흔듀얎 크Ʞ로 가륞 몬테칎륌로(Brazil Nut), 시튞니윔프의 축 위 ì…‹ì§ž 몞곌 몚저의 Ʞ혞(Sitnikov Problem), 소딘곌 치렐손의 평멎 가우슀 핎석 핚수 영점(Gaussian Zeros), 컀크팚튞늭, 저띌튞, 베킀의 몚의 닎ꞈ질(Simulated Annealing)읎에요. 78판은 닀섯을 더했얎요. 에칎륎튞와 가슀파륎, 띌읎슀의 섞 원판 산란(Three-Disk Scattering), 폭슀의 맀듭 색칠곌 알렉산더의 닫힌 ë•‹êž°(Fox Colorings), 몰드의 사슬 분수와 비ꞎ슀, 워너의 풀읎(Chain Fountain), 였음러의 였각수 정늬와 프랭큎늰의 짝짓Ʞ(Franklin's Involution), 갈늎레였의 홀수 법칙곌 드레읎크가 읜은 프렛(Galileo's Frets)읎에요. 79판은 닀섯을 더했얎요. 랚지의 분늬 진동장(Ramsey Fringes), 아하로녞프와 닀비도비치, 자구늬의 양자 걞음곌 하닀마륎 동전(Quantum Walk), 부니몚비치의 버섯 당구(Mushroom Billiard), 하위헌슀의 읎상한 공감(Huygens' Clocks), 띌가늬아슀와 레읞슀, 슬론의 EKG 수엎(EKG Sequence)읎에요. 80판은 닀섯을 더했얎요. 비얎든곌 두베읎윔, 슀티랐슚읎 닀룬 도음 나선(Doyle Spiral), 프롭곌 윌슚의 곌거로부터의 결합(Coupling from the Past), ë©©ê±° 슀펀지의 대각선 닚멎(Menger Slices), 렌읎 적은 곧은 선의 쌍곡멎곌 슈혞프의 탑(Ruled Hyperboloid), 므나찚칎냔의 접선 쓞Ʞ 정늬(Mamikon's Theorem)예요. 81판은 닀섯을 더했얎요. 큎룚슀테륎만곌 베유의 셈윌로 섞는 몚듈러 쌍곡선(Modular Hyperbola), 몚혞로비치치의 뚞늬파와 페륎마의 최소 시간(Head Wave), 덎의 ꌬ임곌 늬윔늬시의 생성(Dehn Twist), 말파티의 섞 원곌 잘갈레륎, 로슀의 탐욕(Malfatti Circles), 폎띌드의 로 방법곌 플로읎드, 람렌튞의 고늬 ì°Ÿêž°(Pollard's Rho)예요. 82판은 닀섯을 더했얎요. 바젠녞프와 바슀넀쎈프, 소슀킚의 갈래 홀로귞랚(Fork Hologram), 아륎넀였도와 귞띌소, 홀슈나읎더가 읜은 칞토얎 계닚의 웚읎랔늿 변환(Cantor Scalogram), 슀플처의 완전 비대칭 배타 곌정곌 데늬닀, 도마니, 묎칎멜의 상 귞늌(Exclusion Process), 마음슀의 푞아송 직선곌 쌄듀의 추잡(Crofton Cell), 띌가에와 동료듀의 가볎륎 녞읎슈와 튞늬칎륎와 동료듀의 페읎저 녞읎슈(Phasor Noise)예요. 100판은 닀섯을 더했얎요. 낞캐로의 템포 칎녌곌 갠읎 읎늄 붙읞 수렎점(Tempo Canon), 고슀퍌의 섬곌 에읎젠슈타읞 정수의 밑 2 − ω Ʞ수법(Gosper Islands), 액섀로드의 묞화 전파 몚형(Axelrod Culture), 데자륎귞의 정늬와 볎슀가 낾 책(Desargues Configuration), 람래귞던의 마법 선곌 하륎묎튞의 마방 직사각형(Magic Lines)읎에요. 101판은 닀섯을 더했얎요. 뷔륎허륎슀의 방정식곌 혾프-윜 변환, 구륎바토프와 사읎첎프, 샚닀늰의 접착 몚형(Burgers Shocks), 톰의 제비ꌬ늬와 베늬, 업슀틞의 파국 ꎑ학(Swallowtail), 갈레예프와 사귞데예프의 바나나 궀도(Banana Orbits), Ꞟ버튞의 바늘 결정 몚형(Gilbert Tessellation), 큎룚슀테륎만 합곌 윔발슀킀, 소읞의 큎룚슀테륎만 êžž(Kloosterman Paths)읎에요. +죌제 5 로 쓎 수학곌 닀음에 핮 볌 수학읎에요. 28판읎 처음 ì—Ž 개륌 썌얎요. 슈람-뢰람너 진화(Loewner Trace), 포드 원(Ford Circles), 턾 난 ê³µ 정늬(Hairy Ball), 아슈텍 닀읎아몬드와 북극권(Aztec Diamond), 부니몚비치 겜Ʞ장(Stadium Billiard), 띌우지 프랙탈(Rauzy Fractal), 고셋의 4₂₁ 닀멎첎와 E8(Gosset Polytope), 윘웚읎의 볎고 말하Ʞ 수엎(Look and Say), 위토프 게임의 귞런디 값(Wythoff Nim), 쌍곡 평멎의 띠 몚형(Hyperbolic Band)읎에요. 29판은 여섯을 더했얎요. 큎띌읞의 j 불변량(J-Invariant), 혾프 올뭉치(Hopf Fibration), 닀읎슚 람띌욎 욎동(Dyson Gas), 가쌀알 바늘 묞제의 페론 나묎(Perron Tree), 임계선 위의 늬만 제타 핚수(Zeta Loops), Ꞟ버튞-섀넌-늬슈 늬플곌 베읎얎-디아윔니슀의 음곱 번(Seven Shuffles)읎에요. 30판도 여섯을 더했얎요. 섞 간격 정늬(Three Gaps), 혞프슀태터 나비와 TKNN 정수(Hofstadter Butterfly), 녞읎만의 왞부 당구(Outer Billiards), 볌드의 탄도 퇎적곌 KPZ 앞멎(Ballistic Deposition), 아륎놀튞의 믞앀더(Meander), 람로드벀튞와 핎뚞슬늬의 슀믞Ʞ(Percolation)예요. 31판도 여섯을 더했얎요. 마슀킷 조각의 큎띌읞 êµ° 극한 집합(Indra's Pearls), 늬틀우드 닀항식의 ê·Œ(Littlewood Roots), 고든의 가우슀 핎자(Gaussian Moat), 아륎놀튞의 고양읎 사상(Cat Map), 자부슀킀와 크룚슀칌의 KdV 솔늬톀(KdV Solitons), 볎읎 곡멎(Boy's Surface)읎에요. ROUND.md 의 「낯선 수학 예」 여덟 가욎데 쓎 것은 슈람-뢰람너 진화와 아슈텍 닀읎아몬드, 몚듈러 형식, 혾프 올뭉치, 섞 간격 정늬, 왞부 당구 여섯읎었고, 31판읎 늬틀우드 닀항식의 귌곌 큎띌읞 군의 극한 집합을 썚서 여덟을 ë‹€ 썌얎요. 32판도 여섯을 더했얎요. 타벗 횚곌와 양자 칎펫(Talbot Carpet), Ꞟ람레슀 추잡의 표(Gilbreath Table), 원 사상의 아륎놀튞 혀(Arnold Tongues), 윘웚읎와 띌딘의 핀휠 타음링(Pinwheel Tiling), 윌킚슚곌 윌렘섌의 칚입 슀믞Ʞ(Invasion Percolation), 욞람의 묞제와 핎뚞슬늬 곌정(Hammersley Lines)읎에요. 33판도 여섯을 더했얎요. 에읞절, 홀로읎드, 로믹, 비띌귞의 묎작위 정렬 넀튞워크(Sorting Network), 베륎튞랑의 ì—­ì„€(Bertrand Paradox), 펜로슈의 비출 수 없는 ë°©(Penrose Room), 당듀랭 구(Dandelin Spheres), 테읎튞의 볎로메였 고늬(Borromean Rings), 곚드바흐 혜성(Goldbach Comet)읎에요. 34판도 여섯을 더했얎요. 톰슚 묞제와 였음러가 낚Ʞ는 닀섯 읎웃 엎둘(Thomson Problem), 쌈빈의 ë°° 묌결(Kelvin Wake), 요넀알마가 낾 와닀의 혞수(Lakes of Wada), 샀읎데거의 강 귞묌(Scheidegger Rivers), 몚저의 소파 묞제와 핎뚞슬늬의 소파(Moving Sofa), 크누슀의 춀추는 링크로 푌 펜토믞녞 덮Ʞ(Pentomino Cover)예요. 35판도 여섯을 더했얎요. 뢞로의 폭읎 음정한 곡선(Constant Width), 렌니의 죌찚 묞제(Rényi Parking), 칎늬의 비죌Ʞ 왕 타음(Kari-Culik Tiles), 윜띌윔슀킀 수엎(Kolakoski), 쿠띌몚토와 바톡토흐의 킀메띌 상태(Chimera State), 죌윥슀킀 날개(Joukowsky Airfoil)예요. 36판도 여섯을 더했얎요. 귞레읎슚의 정늬가 말하는 곡선 닚축 흐멄(Curve Shortening), 헀우드의 음곱 색 토러슀 지도(Heawood Torus), 슈바륎잠 랜턮(Schwarz Lantern), 윌람러햄곌 깁슀의 현상(Gibbs Phenomenon), 타원 곡선의 현곌 접선(Elliptic Chords), 지뚌의 파국 Ʞ계(Catastrophe Machine)예요. 37판도 여섯을 더했얎요. 포천의 쓞Ʞ 선 알고늬슘(Fortune's Sweep), 앙투안의 목걞읎(Antoine's Necklace), 칎륎만 소용돌읎 Ꞟ의 안정성(Kármán Street), 당당한 원판의 헥사틱 상(Hexatic Phase), 졎슚-멜 쪜맀와 아람띌믞 법칙(Johnson-Mehl), 쿠띌몚토-시바신슀킀 방정식(Kuramoto-Sivashinsky)읎에요. 38판도 여섯을 더했얎요. 앀더슚 국소화(Anderson Localization), 묎얎가 ì°Ÿê³  Chenciner 와 Montgomery 가 슝명한 섞 몞 묞제의 8자 핮(Figure-Eight Orbit), 예섌곌 빈튞너, 에륎되시의 베륎누읎 합성곱(Bernoulli Convolutions), 람띌우얎륎의 고정점 정늬륌 슝명하는 슈페륎너 볎조정늬(Sperner's Lemma), 에겐베륎거와 폎늬아의 항아늬(Pólya Urn), 뇌륎고륎의 묎한 수엎(Infinity Series)읎에요. 39판도 여섯을 더했얎요. 고든, 웹, 월퍌튞의 등슀펙튞럌 북(Isospectral Drums), 폰 망곚튞의 ꌎ로 쓎 늬만의 명시 공식(Explicit Formula), 요한 베륎누읎의 최닚 강하선(Brachistochrone), 슈타읎너의 포늬슘(Steiner Chain), 몚런 집닚윌로 귞늰 킹뚌의 합류(Kingman Coalescent), 볎륎수크-욞람 정늬(Borsuk-Ulam)예요. 40판도 여섯을 더했얎요. 앚더슀의 람띌욎 연속 랜덀 나묎(Continuum Random Tree), 슈뢰딩거의 수소 원자 고유 핚수(Hydrogen Orbital), 저도비치 귌사의 팬쌀읎크(Zeldovich Pancakes), 망덞람로가 낎닀볎고 례러, 슈람, 베륎너가 슝명한 람띌욎 욎동의 바깥 겜계(Brownian Frontier), 복소 ꞎ슈부륎크-란닀우 방정식의 결핚 난류(Defect Turbulence), 룚퍌튞 왕자의 정육멎첎(Prince Rupert's Cube)예요. 41판은 닀섯을 더했얎요. 홀곌 왜걎의 현수선 Ꞟ곌 정닀각형 바퀮(Square Wheels), 퐁슬레의 포늬슘(Poncelet Porism), 슐레플늬의 4찚원 정닀포첎륌 자륞 닚멎(Polytope Slices), 큎늌쌁곌 티뚌, 마귞누손의 사읎튞슀왑(Siteswap), Dudeney 의 잡화상 퍌슐곌 월늬슀-볎여읎-게륎비엔 정늬(Hinged Dissection)예요. 42판은 닀섯을 더했얎요. 바륞슀도륎프 규칙윌로 도는 Ʞ사 여행(Knight's Tour), 페륎믞와 파슀타, 욞람, 칭구의 되돌아옎(FPUT Recurrence), 포슬늬에-늜킚 반전Ʞ(Peaucellier Linkage), 몰늬의 삌등분선 정늬(Morley Triangle), 랄곌 마드띌슀, 소칌의 플벗 알고늬슘윌로 돌며 자Ʞ 회플 걞음(Pivot Walk)읎에요. 43판은 닀섯을 더했얎요. 월늬슀 워컀의 칌레읎도사읎큎(Kaleidocycle), 쌍곡형에서 록소드롬형윌로 감Ʞ는 뫌비우슀 변환의 흐멄(Möbius Loxodrome), 터튞의 용수철 정늬(Tutte Embedding), 디펀튞와 크래비잠의 고늬 정늬륌 품은 히토메자시 사시윔(Hitomezashi), 룜게 현상곌 첎비쇌프 점(Runge Phenomenon)읎에요. 44판은 닀섯을 더했얎요. 마크 칎잠의 고늬 몚형(Kac Ring), 슀톀곌 튜킀의 햄 샌드위치 정늬(Ham Sandwich), 람란튞의 불가늬아 솔늬테얎(Bulgarian Solitaire), 엘슀쌄슀와 프늬시의 탄도 소멞(Ballistic Annihilation), 페레귞늰의 솔늬톀(Peregrine Soliton)읎에요. 45판은 닀섯을 더했얎요. 로걎-셰프와 베륎시크-쌀로프의 극한 곡선윌로 닀가가는 영 도형의 플랑셰렐 성장(Plancherel Growth), 도람룚신 겜계 조걎을 걎 임계 읎징 몚형의 SLE(3) 겜계(Ising Interface), 프늬예제프와 동료듀의 였음러 걷개로 쌓는 로터 띌우터 응집곌 레빈-페레슀의 슀맀시 합(Rotor-Router), 폎늬아의 곌수원 묞제(Pólya's Orchard), 넬슚의 평멎 색칠 묞제와 아읎슈벚의 음곱 색(Hadwiger-Nelson)읎에요. 46판은 닀섯을 더했얎요. 람늎룚앙의 구역(Brillouin Zones), 몚슀 읎론곌 레람의 귞래프(Reeb Graph), 로렌잠 Ʞ첎와 시나읎의 당구(Lorentz Gas), 늬얞 Ꞁ래슀의 점묎늬(Glass Patterns), 벀치와 수테띌, 불플아니의 확률 공명(Stochastic Resonance)읎에요. 47판은 닀섯을 더했얎요. 였킀프와 홍, 슀튞로가잠의 슀워멀레읎터(Swarmalators), 빌슀의 왞로욎 달늬Ʞ 선수 추잡(Lonely Runner), 샀잠의 올로읎드와 ê·ž 전개도(Oloid), 쟚베의 원 채우Ʞ 정늬와 윜늰슀-슀티랐슚의 반복(Koebe Packing), 윘웚읎의 병사와 황ꞈ비 묎게(Conway's Soldiers)예요. 48판은 닀섯을 더했얎요. 였하띌의 맀듭 뫌비우슀 에너지(Möbius Energy), 첎비쇌프의 펞향곌 룚빈슀타읞-사륎낙의 밀도(Chebyshev's Bias), 섀든곌 레킀엔, 마슀든의 읎쀑 소용돌읎 능선(Double Gyre), 아자늬와 프로슀튞의 깜빡읎는 래칫(Flashing Ratchet), 망덞람로의 프랙탈 슀믞Ʞ(Fractal Percolation)예요. 49판은 닀섯을 더했얎요. 뒀러의 전개 묞제와 셰퍌드의 추잡(DÃŒrer's Unfolding), 베늬와 곚드버귞가 재규격화한 섞타 합의 ꌬ부랑 곡선(Curlicue), 였음러 방정식곌 푞앵소의 폎혞드로 볞 테니슀 띌쌓 정늬(Tennis Racket), 쌐프의 사슬곌 ë„€ 색 정늬(Kempe Chains), 퇎플늬잠의 낎접 정사각형 묞제와 귞늰-로람의 직사각형(Square Peg)읎에요. 50판은 닀섯을 더했얎요. 크레읎치넌곌 배철러의 2찚원 난류가 거꟞로 가는 캐슀쌀읎드와 맥윌늬엄슀의 소용돌읎(Inverse Cascade), 사포발곌 로소, 구예의 확산 앞쀄(Diffusion Front), 필늬와 마륎쌀티, 쌀읎잠와 타읎왞륎의 욎동성 상분늬(Motility Clusters), 박곌 슀넀펜의 진화 몚형(Bak-Sneppen), 늬섞의 영원한 가속(Risset Rhythm)읎에요. 51판은 닀섯을 더했얎요. 슀믞슀와 마읎얎슀, 캐플런, 굿뚌-슀튞띌우슀의 몚자 타음곌 H/T/P/F 메타타음 치환(Hat Monotile), 반도와 동료듀의 최적 속도 몚형곌 슀Ʞ알마의 원형 튾랙 싀험(Phantom Jam), 사토와 테읎튞의 법칙곌 타원곡선의 a_p(Sato-Tate), 칌띌비와 알우의 공간에서 핞슚읎 잘띌 귞늰 5ì°š 페륎마 곡선(Calabi-Yau Quintic), 에덞슀람룚너와 레처, 조몚로디얞의 지속 혞몰로지와 바윔드(Persistence Barcode)예요. 52판은 닀섯을 더했얎요. 슈람곌 셰필드가 등고선을 SLE(4) 로 볎읞 2찚원 가우슀 자유장(Gaussian Free Field), 당튀와 마슈묎닀륎, 베링거의 ꎑ탄성 힘 사슬(Force Chains), 앀슀컎의 사쀑죌와 마테읎칎, 플잠몚늬슀의 같은 통계 닎ꞈ질(Anscombe's Quartet), 드 람띌윈 수엎곌 BEST 정늬(de Bruijn Sequence), 띌읞슀의 베타 평멎 띠 제튞(Zonal Jets)예요. 53판은 엎을 더했얎요. 듀크, 가륎시아, 룚잠가 귞늰 가우슀 죌Ʞ(Gaussian Periods), 부륎가 볎읞 현수멎곌 나선멎의 휹(Catenoid-Helicoid), 디랙의 벚튞 묘Ʞ(Belt Trick), 헬늄홀잠와 러람의 ì—°êž° 고늬 개구늬뜀(Leapfrogging Vortices), 람룩슀, 슀믞슀, 슀톀, 텃의 정사각형 분할(Squared Rectangle), 포드와 풀컀슚의 최대 흐멄 최소 절닚(Max-Flow Min-Cut), 윔슀타슀 ë°°ì—Ž(Costas Array), 바띌니, 비얎든, 칞의 묎게쀑심 분할(Barycentric Subdivision), 묎윔파디아알와 크넀저의 ë„€ ꌭ짓점 정늬(Four-Vertex Theorem), 였음러의 엘띌슀티칎(Euler's Elastica)예요. 54판은 엎을 더했얎요. 베늬의 묎작위 파동곌 볎고몰니, 슈믞튞가 슀믞Ʞ로 섌 마디 영역(Nodal Domains), 플얎험버튞가 사읞 흐늄에서 찟은 읎상 고유 몚드(Strange Eigenmode), 플얎시 적분곌 베늬, 업슀틞의 첚점 회절 파국(Pearcey Cusp), 나겔곌 바읎슀의 STIT 몚자읎크(STIT Tessellation), 레비의 아크사읞 법칙(Arcsine Law), 슀퇎륎메륎가 닀룬 쌍극자 장 속 대전 입자(StÞrmer Trap), 게음곌 섀플늬의 지연 수띜(Stable Marriage), 슀메음의 말굜(Smale Horseshoe), 욞람 수엎곌 슈타읎너베륎거가 찟은 숚은 진동수(Ulam Sequence), 위얎와 펠런의 거품곌 쌈빈의 깎은 팔멎첎(Weaire-Phelan)예요. 55판은 엎을 더했얎요. 밀러와 셰필드의 허수 Ʞ하(Imaginary Geometry), 티소의 지시 타원곌 메륎칎토륎 투영(Tissot's Indicatrix), 믌윔프슀킀의 묌음표 핚수(Question Mark Function), 버로슀와 휠러의 변환(Burrows-Wheeler), 뷔퐁의 바늘곌 바륎비에의 국수(Buffon's Noodle), 부자의 늬듬 타음링 캐넌(Vuza Canon), 하읞곌 낎시의 헥슀와 게음의 헥슀 정늬(Hex Theorem), 8자 고늬의 범플복곌 자유군의 쌀음늬 귞래프(Universal Cover), 였람친니윔프와 저도비치, 투생곌 윌첵의 확산 제한 소멞(Toussaint-Wilczek), 헬늄홀잠와 쌈빈의 와류 시튞와 크띌슀니의 와동 덩읎(Vortex Sheet)예요. 56판은 엎을 더했얎요. 마테롱의 죜은 잎곌 쌄듀, 퇎넀슀가 아래에서 볞 완전 표볞(Dead Leaves), 곚롬의 튞로믞녞 깔Ʞ(Golomb Trominoes), 윌슚의 고늬 지욎 걞음곌 례러, 슈람, 베륎너의 균등 생성나묎 페아녞 곡선(Tree Peano Curve), 랔로흐와 제너의 진동곌 워니얎의 사닀늬(Bloch Oscillations), 앀드룚슀의 곡선곌 아시몚프의 귞랜드 투얎(Andrews Curves), 지니람륎와 Ʞ륎윔의 원 법칙곌 좀뚞슀, 크늬산티, 솜폎늰슀킀, 슀타읞의 타원 법칙(Elliptic Law), 에륎되시의 불음치 묞제와 볎웚읞, 최, 쿀슀의 3진 수엎(Erdős Discrepancy), 망덞람로의 곱셈 폭포와 칎안, 페읎늬에륎의 묞턱(Multiplicative Cascade), 윔륎테, 첎읎킚, 곚럜, 파읞의 묎작위 조직화(Random Organization), 칌룚가레아누, 화읎튞, 풀러의 ꌬ임곌 뒀틀늌(Twist and Writhe)읎에요. 57판도 엎을 더했얎요. 랔룞의 쀑심축곌 듀불 변환(Medial Axis), 슀메음곌 윌늬엄슀의 솔레녞읎드(Solenoid), 솔로몬의 묎작위 환겜 속 걞음곌 시나읎의 걞음(Sinai's Walk), 테렐곌 펜로슈의 회전(Terrell Rotation), 갈페늰의 π 당구(Galperin Billiard), 페륎마의 두 제곱수 정늬와 자Ʞ에의 한 묞장 슝명(Zagier's Windmills), 슀토크슀 표류와 게륎슀튞너의 튞로윔읎드 파도(Stokes Drift), 띌읞하륎튞부터 ë§š, 맀큎띌우드맚, 폰 데띌우까지의 볌록 였각형 타음링(Pentagon Tilings), 페로와 하위헌슀의 튞랙튞늭슀와 자전거 바퀮 자국(Bicycle Tracks), 였슀튞발튞 숙성곌 LSW 읎론(Ostwald Ripening)읎에요. 58판도 엎을 더했얎요. 알랔란곌 게륎데슀의 ê±°ìšž 곡선(Mirror Curves), 가욎튞와 플셔, 백슀터의 딱딱한 정사각형 격자 Ʞ첎(Hard Squares), 닀니엘 베륎누읎의 맀달늰 사슬곌 베셀 핚수 J0(Hanging Chain), 레음늬와 테음러의 불안정성(Rayleigh-Taylor), 터킀의 반평멎 깊읎와 띌도의 쀑심점 정늬(Tukey Depth), 례러와 베륎너의 람띌욎 고늬 수프(Loop Soup), 칎돔첎프-페튞비아시빌늬 방정식곌 윔닀마의 선 솔늬톀 웹(KP Web), 크누슀가 읎늄 붙읞 베쌀튞-귞레읎 윔드(Beckett-Gray), 파슀칌의 신비 육각형곌 슈타읎너, 컀크뚌의 점(Hexagrammum Mysticum), 드 슀밋곌 렌슀튞띌의 판화 화랑 사상(Print Gallery)읎에요. 59판도 엎을 더했얎요. 륎장드륎와 가우슀의 읎찚 상혞 법칙(Quadratic Reciprocity), 위귞너 핚수와 유륎쌀, 슀톚러의 쌜 고양읎(Wigner Cat), 아비닚곌 샀믞륎의 심 칎빙(Seam Carving), 슐늬히팅곌 녞륎튞마륎크, 에거슀의 알갱읎 Ʞ첎 속 맥슀웰의 도깚비(Granular Demon), 큎렙시의 대각 3ì°š 곡멎곌 쌀음늬, 새뚌의 직선 27개(Clebsch Surface), 볎데륎베륎크의 나선 타음링(Voderberg Tiling), 자읎페륎튞의 곡멎(Seifert Surface), 혞크슀의 자Ʞ 흥분 점곌정(Hawkes Process), 윔혞넚의 자Ʞ조직 지도(Kohonen Map), 레음늬의 위상자 걞음곌 굿맚의 슀페큎 통계(Laser Speckle)예요. 60판도 엎을 더했얎요. 마삎늬아가 짚은 RANDU 의 평멎(RANDU Planes), 였음러의 36 장교와 볎슀, 슈늬칞데, 파컀의 10ì°š 귞레윔-띌틎 방진(Euler's Officers), 펜로슈와 칎터의 등각 윀팩튞화 도형(Penrose Diagram), 러더퍌드 산란의 포묌선 귞늌자(Rutherford Shadow), 였음러 나선곌 프레넬 적분, 윔륎뉎의 회절 셈(Cornu Spiral), 늬처드슚의 디바읎더와 망덞람로의 핎안선 찚원(Richardson Dividers), 에농, 홀, 페익슀의 1찚원 자Ʞ쀑력 멎 몚형곌 며든-벚의 격렬한 읎완(Phase Spiral), 에륎되시와 레니의 거대 성분곌 아큎늬옵타슀, 드수자, 슀펜서의 폭발적 슀믞Ʞ(Giant Component), 허프와 두닀, 하튞의 직선 ì°Ÿêž°(Hough Transform), 조륎당의 곡선 정늬와 심랏의 교찚 섞Ʞ(Jordan Curve)예요. 61판도 엎을 더했얎요. 섞슀나와 닀섯 사람의 영옚 묎작위장 읎징 눈사태(Crackling Noise), 몜죌의 흙 옮ꞰꞰ와 맀캔의 옮김 볎간(Monge Transport), 파욞의 덫곌 마티왞 방정식(Paul Trap), 귞늰힐의 제 묎게 êž°ë‘¥(Greenhill Column), 흐바탈의 믞술ꎀ 정늬와 플슀크의 섞 색 슝명(Art Gallery), 홀디치의 고늬(Holditch Ring), 톰슚의 꺟읞 역선곌 헀비사읎드의 움직읎는 전하(Thomson Kink), 슈나읎더와 바읎슀의 쌍성 렌슈(Binary Lens), 테음러의 되섞Ʞ(Taylor Unmixing), 람누아, 칌로, 디에넀륎 부부의 칎나륎 폭발(Canard Explosion)읎에요. 62판도 엎을 더했얎요. 아람늬윔소프와 큎띌읎너, 로슀, 였틀러의 소용돌읎 격자(Abrikosov Lattice), 욞늬크니의 청색 잡음곌 빈자늬와 뭉치 방법(Void and Cluster), 파읞뚌곌 힙슀의 첎컀볎드(Feynman Checkerboard), 띌도의 바쁜 비버와 마크섌, 분튞로크의 닀섯 상태 챔플얞(Busy Beaver), 폭슀와 컀시너, 비치의 평평한 곡멎 위 직선 흐멄(Veech Octagon), 늬만의 믞분 불가능 핚수와 거버, 닀위슀테륎마튞의 복소 곡선(Riemann's Function), 헀슈와 킚슐레의 한 타음 형(Heesch Tiles), 찚크띌볎륎티와 헀읎슀, 볎고시안의 마당 장터 몚형(Yard-Sale Model), 베륎귞렌의 플타고띌슀 섞 수 나묎(Berggren Tree), 더람띌윈곌 람, 렁의 닚위귌 합곌 밀륞의 완전히 균형 잡힌 늬듬(Balanced Rhythms)읎에요. 63판은 닀섯을 더했얎요. 몚팻의 몚서늬 소용돌읎와 닀넀닀의 사진(Moffatt Eddies), 맥슀웰의 묌고Ʞ 눈 렌슈(Maxwell's Fish-Eye), 읎지쌀비치의 슀파읎크 신겜망(Izhikevich Network), 애듀러와 하소퍌, 우슬늬의 듀뜞 집합 였음러 지표(Excursion Sets), 심슚곌 랔띌읎슀의 ì—­ì„€(Simpson's Paradox)읎에요. 64판은 닀섯을 더했얎요. 암만곌 베읞컀륎의 팔각 타음링을 더 람띌윈의 닀쀑 격자로 지은 것(Ammann-Beenker), 겔판튞, 칎프띌녞프, 저레빈슀킀의 아메바와 포륎슀베늬, 파사레, 치흐의 구멍(Polynomial Amoeba), 킀랔곌 죌렉의 결핚 얌Ʞ와 플시뚌, 데 킀아띌, 칌띌륎윔, 몚늬지의 지귞재귞 전읎(Kibble-Zurek Ions), 알윔비의 적분곌 힐의 영속도 곡선(Hill's Regions), 슀티랐슚곌 칎플찚의 거꟞로 선 진자와 아치슚, 멀늰의 읞도 밧쀄 묘Ʞ(Indian Rope Trick)예요. 65판은 닀섯을 더했얎요. 폰 녞읎만곌 멀늰슀의 거품 법칙(Foam Coarsening), 베드퍌드와 맥멀런의 자Ʞ 아핀 칎펫(McMullen Carpet), 큎띌읞의 돛곌 띌귞랑죌의 죌Ʞ 연분수(Klein's Sails), 알윔비의 잡지선곌 샬의 공쎈점 곡멎(Jacobi Geodesics), 하닀마륎와 늬람친슀킀의 묌방욞곌 윔륎테슀의 정규화 슀토크슬렛(Sedimenting Cloud)읎에요. 67판은 닀섯을 더했얎요. 띌돈의 변환곌 띌마찬드란, 띜슈믞나띌알난의 랹프 필터(Radon Transform), 아륎틎의 람레읎드 군곌 폭슀, 녞읎워슀의 배치 공간(Artin Braids), 월시의 핚수계와 싀베슀터의 행렬, 페음늬의 ì°šë¡€(Walsh Functions), 아레프의 깜박읎는 소용돌읎(Blinking Vortex), 뒀팜의 사읎큎띌읎드(Dupin Cyclide)예요. 68판은 닀섯을 더했얎요. 듀로넀의 빈 왞접원곌 로슚의 뒀집Ʞ(Delaunay Flips), 몚넀로-뒀멘의 플볎나치 닚얎 프랙탈(Fibonacci Word), 로슈의 검출 Ʞ쀀(Rose Criterion), 요섞푞슀 묞제와 바셰의 셈 놀읎(Josephus Problem), 레비치비타의 평행 읎동곌 푞윔의 진자(Parallel Transport)예요. 69판은 닀섯을 더했얎요. 랭퍌드의 짝곌 데읎비슀의 졎재 조걎, 슀윜렘의 수엎(Langford Pairs), 아하로녞프와 뎄의 횚곌와 베늬 등의 파멎 전위(Aharonov-Bohm), 페니의 놀읎와 윘웚읎의 선행수(Penney's Game), 휘튞니와 귞띌우슀타읞의 회전수 정늬(Whitney-Graustein), 슈타읎너의 대칭화와 귞로슀의 수렎(Steiner Symmetrization)읎에요. 70판은 닀섯을 더했얎요. 아우렌하뚞의 파워 닀읎얎귞랚곌 Ʞ타가와, 메늬고, 티베륎의 뉮턮 수송(Laguerre Cells), 배빗곌 룚윈의 헥사윔드 정늬(Hexachordal Theorem), 프렌쌈곌 윘토로바의 사슬(Frenkel-Kontorova), 에슀하위슀, 판 데륎 벚레, 판 데륎 메읎륎, 로서의 알갱읎 띌읎덎프로슀튞 횚곌(Granular Leidenfrost), 칎안의 가우슀 곱셈 칎였슀(Multiplicative Chaos)예요. 71판은 닀섯을 더했얎요. 페니의 −1+i 진법곌 데읎비슀, 크누슀의 쌍둥읎 용(Twindragon), 녞바킀, 한첎, 벀튞의 평탄 3찚원 닀양첎와 윘웚읎, 로섞티의 읎늄(Platycosms), 크누슀의 믞니맥슀 마슀터마읞드(Mastermind), 슀넬슚의 텐섞귞늬티 프늬슘(Tensegrity), 아폎로니였슀의 접쎉 묞제와 비에튞의 풀읎(Problem of Apollonius)예요. 72판은 닀섯을 더했얎요. 룚퍌튞의 듀로넀 닀듬Ʞ(Ruppert Refinement), 띌귞랑죌와 가우슀의 격자 êž°ì € 쀄읎Ʞ(Lattice Reduction), 컀비와 팚늬슀의 히드띌(Hydra Game), 프랭크의 액정 전위와 교찚 펞ꎑ 묎늬(Schlieren Texture), 윘웚읎와 윕서터의 정수 띠(Conway-Coxeter Frieze)예요. 73판은 닀섯을 더했얎요. 베늬와 발띌슈의 에얎늬 파속곌 시빌로Ꞁ룚, 크늬슀토도욞늬데슀의 유한 에너지 빔(Airy Beam), 슀틎곌 시배크가 반례로 싀은 위상수학자의 사읞 곡선(Topologist's Sine Curve), 에렌페슀튞 부부의 항아늬와 칎잠의 귀환 시간(Ehrenfest Urn), 월늬슀의 심슚 선곌 슈타읎너의 덞토읎드(Steiner Deltoid), 가드너가 소개한 볎슀의 1/f 죌사위(Voss Dice)예요. 74판은 닀섯을 더했얎요. 윘웚읎의 유늬 엉킎곌 슈베륎튞의 2닀늬 맀듭(Rational Tangles), 처치의 람닀 계산곌 튞롬프의 람닀 귞늌(Lambda Diagrams), 쉬첞베륎거의 프로몚션곌 헀읎뚌의 죌Ʞ(Promotion), 늬드와 솔로몬의 부혞륌 쓰는 QR 부혞(Reed-Solomon), 투란의 벜돌 공장 묞제와 자란킀에비치의 귞늌(Brick Factory)읎에요. 75판은 닀섯을 더했얎요. 윘웚읎의 분수 프로귞랚 PRIMEGAME(FRACTRAN), 알륎니크와 쟚슬러의 묞제와 Ꞟ버튞, 폎띜의 비 추잡(Steiner Tree), 믞우띌 고료의 ì ‘êž°(Miura-ori), 크러슀컬의 칎드 마술곌 가드너의 소개(Kruskal Count), 수튞너의 σ+ 놀읎와 빛 ì«“êž°(Lights Out)예요. 76판은 닀섯을 더했얎요. 비티와 레음늬의 서로 볎완하는 두 수엎(Beatty Rhythm), 레비의 안정 분포와 망덞람로의 레비 뚌지(Lévy Dust), 뉎컎곌 벀퍌드의 첫 자늬 법칙곌 바음의 고륞 분포(Benford's Law), 헀겔슀만곌 크띌우제의 신뢰 구간 의견 몚형(Hegselmann-Krause), 샀몚슀와 혞읎의 찚수 k 볎로녞읎 귞늌곌 늬의 영역 셈(Order-k Voronoi)읎에요. 77판은 닀섯을 더했얎요. 띌비의 두 쀀위 진동곌 ê·ž 셰람런(Rabi Chevron), 로사토와 동료듀읎 흔듀얎 크Ʞ로 가륞 몬테칎륌로(Brazil Nut), 시튞니윔프의 축 위 ì…‹ì§ž 몞곌 몚저의 Ʞ혞(Sitnikov Problem), 소딘곌 치렐손의 평멎 가우슀 핎석 핚수 영점(Gaussian Zeros), 컀크팚튞늭, 저띌튞, 베킀의 몚의 닎ꞈ질(Simulated Annealing)읎에요. 78판은 닀섯을 더했얎요. 에칎륎튞와 가슀파륎, 띌읎슀의 섞 원판 산란(Three-Disk Scattering), 폭슀의 맀듭 색칠곌 알렉산더의 닫힌 ë•‹êž°(Fox Colorings), 몰드의 사슬 분수와 비ꞎ슀, 워너의 풀읎(Chain Fountain), 였음러의 였각수 정늬와 프랭큎늰의 짝짓Ʞ(Franklin's Involution), 갈늎레였의 홀수 법칙곌 드레읎크가 읜은 프렛(Galileo's Frets)읎에요. 79판은 닀섯을 더했얎요. 랚지의 분늬 진동장(Ramsey Fringes), 아하로녞프와 닀비도비치, 자구늬의 양자 걞음곌 하닀마륎 동전(Quantum Walk), 부니몚비치의 버섯 당구(Mushroom Billiard), 하위헌슀의 읎상한 공감(Huygens' Clocks), 띌가늬아슀와 레읞슀, 슬론의 EKG 수엎(EKG Sequence)읎에요. 80판은 닀섯을 더했얎요. 비얎든곌 두베읎윔, 슀티랐슚읎 닀룬 도음 나선(Doyle Spiral), 프롭곌 윌슚의 곌거로부터의 결합(Coupling from the Past), ë©©ê±° 슀펀지의 대각선 닚멎(Menger Slices), 렌읎 적은 곧은 선의 쌍곡멎곌 슈혞프의 탑(Ruled Hyperboloid), 므나찚칎냔의 접선 쓞Ʞ 정늬(Mamikon's Theorem)예요. 81판은 닀섯을 더했얎요. 큎룚슀테륎만곌 베유의 셈윌로 섞는 몚듈러 쌍곡선(Modular Hyperbola), 몚혞로비치치의 뚞늬파와 페륎마의 최소 시간(Head Wave), 덎의 ꌬ임곌 늬윔늬시의 생성(Dehn Twist), 말파티의 섞 원곌 잘갈레륎, 로슀의 탐욕(Malfatti Circles), 폎띌드의 로 방법곌 플로읎드, 람렌튞의 고늬 ì°Ÿêž°(Pollard's Rho)예요. 82판은 닀섯을 더했얎요. 바젠녞프와 바슀넀쎈프, 소슀킚의 갈래 홀로귞랚(Fork Hologram), 아륎넀였도와 귞띌소, 홀슈나읎더가 읜은 칞토얎 계닚의 웚읎랔늿 변환(Cantor Scalogram), 슀플처의 완전 비대칭 배타 곌정곌 데늬닀, 도마니, 묎칎멜의 상 귞늌(Exclusion Process), 마음슀의 푞아송 직선곌 쌄듀의 추잡(Crofton Cell), 띌가에와 동료듀의 가볎륎 녞읎슈와 튞늬칎륎와 동료듀의 페읎저 녞읎슈(Phasor Noise)예요. 100판은 닀섯을 더했얎요. 낞캐로의 템포 칎녌곌 갠읎 읎늄 붙읞 수렎점(Tempo Canon), 고슀퍌의 섬곌 에읎젠슈타읞 정수의 밑 2 − ω Ʞ수법(Gosper Islands), 액섀로드의 묞화 전파 몚형(Axelrod Culture), 데자륎귞의 정늬와 볎슀가 낾 책(Desargues Configuration), 람래귞던의 마법 선곌 하륎묎튞의 마방 직사각형(Magic Lines)읎에요. 101판은 닀섯을 더했얎요. 뷔륎허륎슀의 방정식곌 혾프-윜 변환, 구륎바토프와 사읎첎프, 샚닀늰의 접착 몚형(Burgers Shocks), 톰의 제비ꌬ늬와 베늬, 업슀틞의 파국 ꎑ학(Swallowtail), 갈레예프와 사귞데예프의 바나나 궀도(Banana Orbits), Ꞟ버튞의 바늘 결정 몚형(Gilbert Tessellation), 큎룚슀테륎만 합곌 윔발슀킀, 소읞의 큎룚슀테륎만 êžž(Kloosterman Paths)읎에요. 102판은 닀섯을 더했얎요. 칎우프만의 ꎄ혞 닀항식곌 졎슀 닀항식(Kauffman Bracket), 닀칎Ʞ 핚수와 란잠베륎크의 w, 칎안의 최댓값 집합(Takagi Curve), 쇀하륎튞의 비틀늰 삌각Ʞ둥(Schönhardt Polyhedron), 비티 수엎곌 프렌쌈의 추잡(Fraenkel Rhythm), 쌄듀의 몚양 구와 캐럎의 베개 묞제(Kendall Shape Sphere)예요. 목록 밖에서 닀음에 핮 볌 것읎에요. 수론 갈래는 32판의 Gilbreath Table 까지 넷읎에요. Ꞟ람레슀 표는 소수 말고 닀륞 수엎로도 섞욞 수 있얎요. 였듀늬윔(1993)에 따륎멎 2 닀음에 간격읎 작고 고륎게 흩얎진 홀수가 였는 수엎에서는 1 의 엎읎 서고, 간격읎 크멎 깚젞요. 간격의 크Ʞ륌 녞람로 걞멎 1 의 엎읎 묎너지는 자늬가 볎여요. 핎뚞슬늬 곌정의 점 대신 격자 칞마닀 지수 분포 묎게륌 두멎 마지막 통곌 슀믞Ʞ(로슀튞, 1981)가 되고, 극한 몚양읎 쌍곡선에서 (√x+√y)² 의 겜계로 바뀌얎요. @@ -5484,6 +5567,8 @@ Flock, Harmonograph, Big Type 은 처음 화멎읎 빚강읎에요. 101판에서 볞 것읎에요. 둘은 셈읎 정한 축을 팚널에 맞춰 닀시 잡아알 귞늌읎 ì„°ì–Žìš”. Burgers Shocks 는 시간을 쀄에 곧게 두멎 합쳐짐읎 위쪜 40쀄에 몰렀서, 쀄 번혞의 제곱윌로 두자 합쳐지는 섞대마닀 높읎륌 나눠 가졌얎요. Swallowtail 은 z = −3.3 에서 엎멎 ë„€ ꎑ선읎 겹친 삌각형읎 18쀄뿐읎띌, x = 0 을 44번짞 쀄로 옮Ʞ고 쓞Ʞ륌 −5.8 에서 시작핎 삌각형을 팹널 너비의 90% 까지 킀웠얎요. 셋은 셈을 끝까지 지쌰얎요. Banana Orbits 는 안낎 쀑심 방정식을 RK4 로 풀얎서 에너지와 정쀀 토로읎달 욎동량읎 거욞점 슀묎 번 동안 천만분의 몇 안에서 낚아요. Gilbert Tessellation 은 두 직선읎 만나는 점을 늊게 닿는 시각 찚례로 한 번씩 정핎 멈춀을 정확하게 풀고, 바늘 n 개에 칞읎 ꌭ n + 1 개예요. Kloosterman Paths 는 40개륌 재 볎니 너비 가욎데값읎 √p 의 1.3ë°°, 높읎가 0.75배였는데, 팚널에서는 몇몇읎 토템처럌 Ꞟ게 서요. +102판에서 볞 것읎에요. 둘은 녞람가 귞늌에 볎읎게 하렀고 귞늌을 움직여알 했얎요. Fraenkel Rhythm 은 자륌 섞워 두고 재생선만 낎렀볎낎멎 Speed 반응읎 0.0020 읎띌, 자륌 1쎈에 16쀄씩 위로 흘렞얎요. 귞러자 비윚읎 반올늌되지 않은 자는 여는 자늬에서도 127번짞 펄슀에 충돌을 낎서, 밀도륌 한 판에 치는 박 수로 반올늌핎 악볎가 판마닀 되풀읎되게 했얎요. Kauffman Bracket 은 막대가 LED 의 5% 쯀읎띌 찚튞륌 뒀집는 녞람가 0.036 읎었고, 뒀집Ʞ륌 걞음 끝에 넣고 K4 에 교찚점 바꟞Ʞ륌 쀬얎요. 나뚞지는 셈을 윔드 안에서 닀시 뎐요. Takagi Curve 는 표볞을 i/4096 에 두멎 엎두 번 두 ë°° 한 ë’€ s 가 0 읎 되얎 w = 0.9 의 최댓값읎 2.52 로 나왔얎요. 표볞에 묎늬수 얎Ꞌ낚을 죌자 3.16 읎 됐얎요. Schönhardt Polyhedron 은 ë„€ ꌭ짓점 묶음 15개륌 프레임마닀 감김수로 재서, 비틀늌 0.1도부터 셈읎 0 읎 돌요. Kendall Shape Sphere 는 node 에서 삌각형 200만 개로 둔각 몫 0.7504 와 고륞 밀도륌 확읞하고, 원판 9/8 − 4/π² 와 정사각형 97/150 + π/40 도 0.0005 안에서 ë§žì·„ì–Žìš”. + 목록 밖에서 31판 뒀에 핮 볌 것읎에요. 페륎믞-파슀타-욞람-칭구의 되돌아옎(1955)은 KdV Solitons 와 같은 뿌늬띌 같은 판에 두지 않아요. 목록 밖에서 33판 뒀에 핮 볌 것읎에요. 토칎륎슀킀의 ë°©(1995)은 거욞로 된 닀각형 방에서 한 점의 빛읎 닀륞 한 점에만 닿지 못하는 방읎띌 Penrose Room 의 ꎑ선 추적을 귞대로 썚요. 프늬드뚌곌 슀윔띌(1987)는 둥귌 원 셋윌로는 볎로메였 고늬륌 만듀 수 없닀는 것을 볎였는데, 원을 밀얎 넣을 때 얎디서 부딪히는지가 귞늌읎 돌요. 정렬 넀튞워크의 시간 t 의 순엎을 점윌로 찍윌멎 대각선에서 원판을 지나 반대 대각선윌로 도는 점 구늄읎 돌요. ê·ž 원판읎 2007년 녌묞읎 낎닀볞 아륎킀메데슀 법칙읎에요. 간격읎 2k 읞 소수 쌍의 수는 Goldbach Comet 곌 같은 FFT 로 섞고, k 마닀 하디-늬틀우드 읞수로 갈늬는 쀄Ʞ 귞늌읎 돌요. @@ -5570,7 +5655,7 @@ Flock, Harmonograph, Big Type 은 처음 화멎읎 빚강읎에요. 목록 밖에서 75판 뒀에 핮 볌 것읎에요. 마빈 믌슀킀(Marvin Minsky)의 두 칎욎터 Ʞ계(1967)는 칎욎터 여럿을 2^a 3^b 5^c 하나로 ì ‘ì–Ž 칎욎터 둘로 묎엇읎든 셈핎요. FRACTRAN 읎 바로 ê·ž 접Ʞ띌서, 같은 구슬 êž°ë‘¥ 둘에 접힌 수가 얌마나 Ꞟ게 ì°šê³  비는지 볌 만핎요. 몚늬슀 핮넌(Maurice Hanan)읎 1966년에 볎읞 대로 직각 거늬의 슈타읎너 나묎는 닚자의 가로섞로 쀄읎 만드는 핮넌 격자 위에서 찟윌멎 되고, 비의 바닥은 프랭크 황(Frank K. Hwang)읎 1976년에 볎읞 2/3 읎에요. LED 격자에 맞는 직각 나묎륌 Steiner Tree 의 유큎늬드 나묎와 나란히 섞워 볌 만핎요. 마크 솅크(Mark Schenk)와 사읎뚌 게슀튞(Simon Guest)가 2013년 녌묞에서 핚께 닀룬 달걀판 접Ʞ는 두 지귞재귞륌 ë‹€ 높읎 방향윌로 섞욎 평행 읎동 멎읎에요. 한쪜읎 쀄 때 닀륞 쪜읎 늘얎 푞아송 비가 양수띌서, Miura-ori 의 지귞재귞 하나만 섞우멎 나란히 견쀄 수 있얎요. 제임슀 프롭(James Propp)곌 데읎비드 윌슚(David Wilson)의 곌거로부터의 결합(1996)은 몚든 상태에서 같은 난수로 사슬을 돌늬고, ë‹€ 합쳐지지 않윌멎 더 뚌 곌거에서 닀시 시작핎서 합쳐진 값읎 정확히 정상 분포륌 따띌요. Kruskal Count 의 사슬 합치Ʞ륌 작은 마륎윔프 사슬의 상태 엎에 걞멎 귞렀젞요. 수튞너는 얎느 귞래프에서든 ë‹€ 쌜진 판은 σ+ 놀읎로 ë‹€ 끌 수 있음을 볎였얎요. Lights Out 의 칞을 육각 격자나 Ꞁ자 몚양 영역윌로 바꿔 전첎 GF(2) 소거로 풀멎, 풀늬지 않는 판읎 많은 영역에서도 ë‹€ 쌠 판만은 늘 꺌지는 것을 볌 수 있얎요. -목록 밖에서 76판 뒀에 핮 볌 것읎에요. 아비슈늬 프렌쌈(Aviezri Fraenkel)읎 1973년에 낾 추잡은 비윚읎 서로 닀륞 비티 수엎 셋 읎상읎 정수륌 겹칚 없읎 나누렀멎 비윚읎 (2^m − 1)/2^(i−1) ꌎ읎얎알 한닀고 핎요. 목소늬 셋읎멎 7, 7/2, 7/4 띌서, Beatty Rhythm 의 두 êžž 옆에 êžž 하나륌 더 섞우멎 겹치지 않는 섞 목소늬 늬듬읎 돌요. Ʞ욀 데퓌앙(Guillaume Deffuant)곌 동료듀읎 2000년에 낾 몚형은 Hegselmann-Krause 와 같은 신뢰 구간을 쓰되 둘씩 만나 반씩 닀가가요. 두 몚형을 같은 처음 의견윌로 팹널 좌우에 섞우멎 같은 ε 에서 묎늬 수가 얎떻게 갈늬는지 볌 수 있얎요. +목록 밖에서 76판 뒀에 핮 볌 것읎에요. Ʞ욀 데퓌앙(Guillaume Deffuant)곌 동료듀읎 2000년에 낾 몚형은 Hegselmann-Krause 와 같은 신뢰 구간을 쓰되 둘씩 만나 반씩 닀가가요. 두 몚형을 같은 처음 의견윌로 팹널 좌우에 섞우멎 같은 ε 에서 묎늬 수가 얎떻게 갈늬는지 볌 수 있얎요. 목록 밖에서 77판 뒀에 핮 볌 것읎에요. 녞뚌 ëžšì§€(Norman Ramsey)의 분늬 진동장(1950)은 π/2 펄슀 둘 사읎에 자유 시간 T 륌 두얎, 띌비 뎉우늬 안에 읎조륌 따띌 너비 1/T 의 쎘쎘한 묎늬륌 섞워요. 가로륌 읎조, 섞로륌 T 로 두멎 Rabi Chevron 곌 짝읎 돌요. 큰 알읎 충분히 묎거우멎 거꟞로 가띌앉는 역 람띌질너튞 횚곌(대니얌 홍, 폎 퀾, 슈테판 룚딩, 2001)는 Brazil Nut 의 Ʞ둥에 묎게 비 녾뾌 하나륌 더하멎, 였륎는 쪜곌 가띌앉는 쪜읎 갈늬는 자늬륌 섞로 êž°ë‘¥ 하나에서 볎음 수 있얎요. 닚위 원판의 쌍곡 GAF Σ Ο_n z^n 의 영점(유발 페레슀, 벌늰튞 비띌귞, 2005)은 원판의 뫌비우슀 변환에 바뀌지 않고 가장자늬로 갈수록 끝없읎 쎘쎘핎젞서, Gaussian Zeros 의 영점 찟Ʞ륌 원판 둘에 옮겚 섞욎 팹널 위아래에 둘 만핎요. 읎웃 옚도끌늬 상태륌 맞바꟞는 레플늬칎 교환(로버튞 슀웬슚, 왕젠셩, 1986)은 Simulated Annealing 의 순회 넷을 같은 도시 위에 옚도만 달늬핎 섞로로 쌓윌멎, 찚가욎 순회가 뜚거욎 쪜에서 엉킎을 받아 갇힌 곚을 빠젞나였는 것읎 볎여요. @@ -5588,6 +5673,8 @@ Flock, Harmonograph, Big Type 은 처음 화멎읎 빚강읎에요. 목록 밖에서 101판 뒀에 핮 볌 것읎에요. 늬처드 아띌티아(Richard Arratia)가 1979년 박사 녌묞에서 닀룬 합쳐지는 람띌욎 욎동, ê³§ 람띌욎 웹은 Burgers Shocks 의 결정론적 합류 옆에 묎작위 합류륌 섞워요. 마읎큎 베늬, 졎 나읎, 프랜시슀 띌읎튞가 1979년에 셈한 타원 배ꌜ 회절 파국은 첚점 셋의 삌각 화선 안에 육각 격자가 서요. 적분읎 읎쀑읎띌 Swallowtail 처럌 닚멎을 몇 쀄씩 나눠 짓는 것볎닀 더 Ꞟ게 나눠알 핎요. 올레 헀귞슀튞룀(Olle HÀggström)곌 로날드 메슀터륎(Ronald Meester)의 늎늬팚드 몚형(1996)은 Ꞟ버튞의 바늘을 같은 빠륎Ʞ로 자띌는 원윌로 바꿔, 닿윌멎 멈추는 원판 묎늬가 돌요. 람띌읎얞 버치(Bryan Birch)의 1968년 합 Σ e((ax + bx³)/p) 도 같은 ê±°ìšž 대칭을 가젞서, 윔발슀킀와 소읞읎 같은 2016년 녌묞에서 닀룬 ê·ž Ꞟ을 Kloosterman Paths 의 두 펜윌로 귞늎 수 있얎요. +목록 밖에서 102판 뒀에 핮 볌 것읎에요. 졎 윘웚읎(John Conway)가 1969년에 쓎 슀쌀읞 ꎀ계로 교찚점을 하나씩 바꟞고 풀얎 가는 나묎륌 128쀄 아래로 킀우멎, 잎마닀 풀며 맀듭곌 고늬가 서고 알렉산더-윘웚읎 닀항식(James Alexander, 1928)읎 위로 몚여요. 믞하음 혾바녾프(Mikhail Khovanov)의 2000년 큐뾌 핎상도는 Kauffman Bracket 의 2^n 상태륌 잵마닀 놓는 귞늌읎띌 ê·ž 상태 셈을 귞대로 읎얎요. 판 데륎 바륎던(B. L. van der Waerden)읎 1930년에 낾 Σ s(10ⁿx)/10ⁿ 륌 Takagi Curve 의 틀에 올늬멎 닚계마닀 천막읎 ì—Ž 배씩 늘얎서, ì…‹ì§ž 닚계에서 읎믞 한 쀄에 여덟 개가 듀얎가요. 뵈륎게 예섌(BÞrge Jessen)읎 1967년에 낾 직교 정읎십멎첎는 였목 몚서늬가 여섯읞 닀멎첎띌, Schönhardt Polyhedron 의 유늬 귞늌윌로 접힌 곚읎 왜 움직읎지 않는지 볎음 수 있얎요. 베륎나륎 샀저(Bernard Chazelle)읎 1984년에 볎읞, 새 ꌭ짓점읎 n² 쯀 있얎알 잘늬는 닀멎첎도 같은 귞늌에 서요. 정수 비윚로 Fraenkel Rhythm 의 음을 하멎 폎 에륎되시(Paul Erdős)가 1950년에 낎놓은 덮개 첎계(covering system)가 돌요. 정확한 덮개는 가장 큰 법읎 ꌭ 겹친닀는 레옚 믞륎슀킀(Leon Mirsky)와 도널드 뉎뚌(Donald Newman)의 정늬륌 같은 자로 귞늬멎, 같은 빠륎Ʞ 둘읎 얎디서 만나알 하는지 볎여요. 크늬슀 묎얎(Cris Moore)가 1993년에 ì°Ÿê³  알랭 셎시넀(Alain Chenciner)와 늬처드 몜고메늬(Richard Montgomery)가 2000년에 슝명한 섞 묌첎 8자 궀도륌 Kendall Shape Sphere 의 몚양 구에 귞늬멎, 궀도가 적도의 음직선 배치륌 찚례로 지나며 구륌 감는 곡선읎 돌요. + ### 타읎포 죌제 7 로 핮 볞 Ꞁ자 싀험곌 닀음에 핮 볌 것읎에요. 63판곌 64판읎 같읎 돌며 닀섯씩 처음 만듀었얎요. 63판의 닀섯은 한Ꞁ 자몚가 날아와 몚아쓰Ʞ로 잠Ʞ는 조늜(Jamo Assembly), 놀턎곌 하뚌의 Ʞ혞 몚자읎크(Type Mosaic), 혞프슀태터의 튞늜렛 귞늌자(Trip-let), 칎프늬칎의 계산 종읎(Kaprekar's Routine), 캐럎의 낱말 사닀늬(Word Ladder)예요. @@ -5750,7 +5837,7 @@ Flock, Harmonograph, Big Type 은 처음 화멎읎 빚강읎에요. Ʞ능 감은 셋읎 Ꞁ을 윔드 팚턎곌 Lab 사읎에 죌고받는 Ꞟ을, 둘읎 Text 레읎얎의 귞늬Ʞ륌 넓히는 Ꞟ을 바랐얎요. Zipf Words 는 Text 레읎얎에 붙여 넣은 ꞎ Ꞁ을 팚턎읎 묞자엎로 받Ʞ륌, Dokkaebibul 은 Lab 에서 친 킀볎드 Ꞁ쇠가 윔드 팚턎의 input 윌로 였Ʞ륌, Roman Addition 은 윔드 팚턎읎 바뀌는 합을 Text 레읎얎로 넘ꞰꞰ륌 바랐얎요. Runestone 은 Text 레읎얎가 겜로 하나륌 따띌 Ꞁ쀄을 휘얎 놓Ʞ륌, Rolling Shutter 는 지난 프레임을 쌓아 두고 쀄마닀 닀륞 프레임에서 읜는 레읎얎 횚곌륌 바랐얎요. -100판 뒀에 핮 볌 것읎에요. 나뭇가지 룬(kvistrunor)은 룬 하나륌 쀄Ʞ 왌쪜 가지 수(몇짞 묶음)와 였륞쪜 가지 수(ê·ž 묶음의 몇짞)로 적는 룬 암혞예요. Runestone 의 같은 띠와 닀섯 획 틀에 Twig 대신 Cipher 녞람륌 걞얎, 낱말읎 볎통 룬곌 가지 룬 사읎륌 였가게 핮 볌 만핎요. 결승선 사진Ʞ(photo finish)처럌 섞로 틈 하나륌 시간윌로 펌치는 판읎에요. 가욎데 한 ì—Žë§Œ 읜얎 였륞쪜윌로 시간을 쌓윌멎 빚늬 지나간 Ꞁ자는 좁게, 느며 Ꞁ자는 넓게 찍혀요. 읎 팚턎의 쀄마닀 나읎륌 셈하는 틀에서 쀄 대신 엎을 시간 축윌로 쓰멎 돌요. 제륎베륎 도늬알크(Gerbert d'Aurillac)가 980년쯀 되삎늰 셈판은 1~9 륌 새ꞎ 말(아플쌀슀) 하나륌 엎마닀 놓아서, 같은 덧셈읎 Ʞ혞 쌓Ʞ 대신 엎마닀 넘Ʞ는 받아올늌읎 돌요. Roman Addition 의 두 수륌 아플쌀슀 엎로 나란히 섞우멎 자늬값읎 듀얎였는 순간읎 한 판에 볎여요. 한 Ꞁ을 쪜윌로 쓰되 처음 나옚 낱말만 밝게, 되풀읎된 낱말은 얎둡게 썚서 쪜읎 낎렀갈수록 밝은 Ꞁ자가 쀄얎드는 싀험읎에요. 새 낱말 수가 읜은 낱말 수의 거듭제곱윌로 자란닀는 Herdan(1960)곌 Heaps(1978)의 법칙을 쪜의 밝Ʞ로 읜게 돌요. 공병우가 1949년에 만든 섞벌식 타자Ʞ처럌 쎈성곌 받칚을 닀륞 킀로 치는 섞벌식윌로 같은 쀄을 아래 절반에 같읎 쳐 볎는 것읎에요. 섞벌식에는 도깚비불읎 없얎서, 위 두벌식 쀄에서 뛰는 자음읎 아래 쀄에서는 처음부터 제자늬에 앉는 것읎 나란히 볎여요. +100판 뒀에 핮 볌 것읎에요. 나뭇가지 룬(kvistrunor)은 룬 하나륌 쀄Ʞ 왌쪜 가지 수(몇짞 묶음)와 였륞쪜 가지 수(ê·ž 묶음의 몇짞)로 적는 룬 암혞예요. Runestone 의 같은 띠와 닀섯 획 틀에 Twig 대신 Cipher 녞람륌 걞얎, 낱말읎 볎통 룬곌 가지 룬 사읎륌 였가게 핮 볌 만핎요. 공병우가 1949년에 만든 섞벌식 타자Ʞ처럌 쎈성곌 받칚을 닀륞 킀로 치는 섞벌식윌로 같은 쀄을 아래 절반에 같읎 쳐 볎는 것읎에요. 섞벌식에는 도깚비불읎 없얎서, 위 두벌식 쀄에서 뛰는 자음읎 아래 쀄에서는 처음부터 제자늬에 앉는 것읎 나란히 볎여요. 101판의 닀섯은 한 부품을 거듭 썚서 지은 한자(Three Trees), TeX 의 늬앙 하읎픈 팹턮(Liang Hyphenation), 앞윌로만 도는 플랩 판(Split-Flap), Ꞁ자 안에 Ꞁ자륌 겹겹읎 넣은 나볞 Ꞁ자(Navon Letters), 첫 Ꞁ자와 끝 Ꞁ자만 낚Ʞ고 섞는 Ꞁ(Scrambled Middles)읎에요. @@ -5758,7 +5845,15 @@ Flock, Harmonograph, Big Type 은 처음 화멎읎 빚강읎에요. Ʞ능 감은 셋읎 윔드 팚턎곌 Lab 사읎에 Ꞁ읎나 Ꞁꌎ을 죌고받는 Ꞟ을, 둘읎 Text 레읎얎의 횚곌륌 바랐얎요. Split-Flap 은 Text 레읎얎 낱말읎나 êž°êž° 윘솔의 Ꞁ쀄을 윔드가 묞자엎로 읜Ʞ륌, Liang Hyphenation 은 볎는 사람읎 적은 낱말을 윔드가 받Ʞ륌, Navon Letters 는 Lab Ꞁꌎ의 고정 너비 Ꞁ자 비튞맵을 윔드가 받Ʞ륌 바랐얎요. Scrambled Middles 는 Text 레읎얎에 Ꞁ자 찚례륌 섞는 횚곌륌, Three Trees 는 획 찚례가 든 한자 Ꞁꌎ곌 「획순대로 쓰Ʞ」 횚곌륌 바랐얎요. -101판 뒀에 핮 볌 것읎에요. Three Trees 의 짝윌로는 부품읎 왌쪜 펞방윌로 가멎 ꌎ읎 아예 바뀌는 변형읎 있얎요. æ°Ž 가 æ°µ, 火 가 灬, 人 읎 亻 가 되는 것을 한 칞 안에서 읎얎지는 획 읎동윌로 볎음 만핎요. Scrambled Middles 는 「캠늿람지 대학의 연결구곌」 ꌎ의 한국얎판윌로 읎얎서, 낱말 안 음절 섞Ʞ와 자몚 섞Ʞ륌 녾뾌 하나의 양 끝에 두고 얎디서 읜Ʞ가 깚지는지 볌 수 있얎요. 튞띌읎비전 간판은 섞로 삌각 êž°ë‘¥ 엎여섯 개가 왌쪜부터 찚례로 돌며 낱말 셋을 번갈아 볎읎고, 도는 동안 앞 낱말 조각곌 닀음 낱말 조각읎 비슀듬히 섞여서 Split-Flap 의 짝읎 돌요. 나볞 Ꞁ자는 한Ꞁ로도 돌요. 큰 「숲」의 ㅅ 을 작은 ㅅ 윌로, ㅜ 륌 작은 ㅜ 로, ㅍ 을 작은 ㅍ 윌로 짜멎 음절곌 자몚가 한 귞늌에 서요. +101판 뒀에 핮 볌 것읎에요. Scrambled Middles 는 「캠늿람지 대학의 연결구곌」 ꌎ의 한국얎판윌로 읎얎서, 낱말 안 음절 섞Ʞ와 자몚 섞Ʞ륌 녾뾌 하나의 양 끝에 두고 얎디서 읜Ʞ가 깚지는지 볌 수 있얎요. 나볞 Ꞁ자는 한Ꞁ로도 돌요. 큰 「숲」의 ㅅ 을 작은 ㅅ 윌로, ㅜ 륌 작은 ㅜ 로, ㅍ 을 작은 ㅍ 윌로 짜멎 음절곌 자몚가 한 귞늌에 서요. + +102판의 닀섯은 æ°Ž 가 æ°µ 로, 人 읎 亻 로 바뀌며 붙는 펞방(Side Radicals), 엎마닀 말 하나륌 놓는 제륎베륎의 셈판(Gerbert's Abacus), 처음 나옚 낱말만 밝은 쪜(Heaps Page), 섞 멎 Ʞ둥에 잘띌 붙읞 낱말(Trivision), 한 쀄만 볎는 칎메띌가 쌓은 Ꞁ자(Photo Finish)예요. + +102판에서 볞 것읎에요. 닀섯 ë‹€ Ꞁ자 ꌎ을 윔드에 적었고, 옮겚 쓎 Ꞁꌎ은 101판 Scrambled Middles 의 소묞자륌 빌늰 Heaps Page 하나예요. 둘은 숫자와 Ꞁ자가 닀륞 Ꞁ자로 읜혀서 ꌎ을 닀시 ê·žë žì–Žìš”. Gerbert's Abacus 의 첫 4 는 고늬 밑에 닀늬륌 엇걎 반쪜 8 읎띌 팚널에서 8 로 읜혔고, y ꌎ 5 는 요슘 4 로 읜혀서 2975 가 2974 로 볎였얎요. Heaps Page 의 s 는 위아래 획읎 짧아 was 가 wac 로 읜혀서 획을 ꜉ 채웠얎요. 움직읎는 Ꞁ자에서도 가로획읎 읜Ʞ륌 붙듀었얎요. Trivision 은 Ʞ둥읎 섞로띌 사영읎 가로만 눌러서, 돌Ʞ 한가욎데에서 F 와 E 의 가로획곌 O 의 위아래가 한 쀄로 읎얎젞 뚌저 읜혀요. Photo Finish 의 P 는 결승선에 서서 섞로 막대 둘읎 19쀄 끌렀도, 위아래 가로획곌 쀄Ʞ가 낚아 P 로 읜혀요. Side Radicals 는 灬 ë„€ 점읎 3.3 LED 굵Ʞ로 13 LED ë–šì–Žì ž 있얎도 따로 읜혔얎요. Ꞁ읎 법칙곌 거꟞로 가Ʞ도 했얎요. Heaps Page 의 디킚슀는 쪜마닀 새 낱말읎 13/38 에서 22/34 로 늘닀가, 둘짞 왕곌 왕비가 첫짞륌 되풀읎하는 닀섯짞 쪜에서 11/38 로 ë–šì–Žì žìš”. + +Ʞ능 감은 셋읎 윔드 팚턎곌 Lab 사읎에 Ꞁ읎나 귞늌을 죌고받는 Ꞟ을, 둘읎 녞람와 펞집Ʞ륌 바랐얎요. Heaps Page 는 붙여 넣은 ꞎ Ꞁ을 윔드가 묞자엎로 받Ʞ륌, Photo Finish 는 한 쀄을 시간 축윌로 쌓는 「슬늿」 횚곌나 닀륞 레읎얎의 픜셀을 읜는 Ꞟ을, Trivision 은 아래 레읎얎 화멎을 텍슀처로 읜는 Ꞟ을 바랐얎요. Gerbert's Abacus 는 녞람의 딞깍 수륌 정수로 받Ʞ륌, Side Radicals 는 16×16 격자에 획을 찍얎 좌표로 낮 죌는 획 펞집Ʞ륌 바랐얎요. + +102판 뒀에 핮 볌 것읎에요. 진(ç§Š)의 소전(Ʞ원전 221년 묎렵)에서 한(挢)의 예서로 넘얎가며 획읎 곧아지고 끊ꞎ 예변(隞變)을, 소전곌 예서와 핎서 칞을 위에서 아래로 읎얎 ê·žë € 볌 만핎요. 같은 부품읎 林 의 왌쪜, 杏 의 위, 呆 의 아래에서 달늬 눌늬는 것을 한 칞에서 돌렀 볎는 것도 Side Radicals 의 점 옮ꞰꞰ륌 귞대로 썚요. 플볎나치(Leonardo of Pisa)의 『Liber abaci』(1202)가 퍌뜚늰 종읎 위 덧셈을 Gerbert's Abacus 의 장부 위쪜에 나란히 돌늬멎, 귞레고륎 띌읎슈(Gregor Reisch)의 『Margarita Philosophica』(1503) 목판화가 귞늰 셈판Ꟍ곌 펜 셈Ꟍ의 겚룞읎 한 팚널에 서요. 허버튞 사읎뚌(Herbert Simon)읎 1955년에 낾 몚형은 닀음 낱말을 확률 α 로 새로 짓고 나뚞지는 지난 Ꞁ에서 곚띌서, 읎 몚형윌로 팚널읎 Ꞁ을 지얎 쓰멎 Heaps Page 의 β 와 Zipf 의 Ʞ욞Ʞ륌 한 쪜에서 핚께 볌 수 있얎요. 볎는 사람읎 옆윌로 걞얎알 귞늌읎 바뀌는 알윔람 아감(Yaacov Agam)의 아가몚귞래프는 녾뾌 하나로 볎는 각도륌 돌늬는 죌늄 판윌로 Trivision 의 짝읎 돌요. 더Ꞁ러슀 튞럌불(Douglas Trumbull)읎 『2001: 슀페읎슀 였디섞읎』(1968)의 슀타게읎튞에 쓎 슬늿 슀캔은 틈읎 움직여서, ê·ž 틈읎 Ꞁ자판 위륌 지나게 하멎 Photo Finish 의 Ꞁ자가 ꞎ 빛의 Ꞟ로 늘얎나요. ### 화장품 팝업 From c191d3f6c1a3bc9d11edb5cec7f7d59ea7c14a6b Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 3 Nov 2026 09:58:00 +0000 Subject: [PATCH 9/9] =?UTF-8?q?=ED=8C=A8=ED=84=B4=20=EC=97=B0=EA=B5=AC=201?= =?UTF-8?q?02=ED=8C=90:=20index.html=20=EC=9D=84=20767=EA=B0=9C=EB=A1=9C?= =?UTF-8?q?=20=EB=8B=A4=EC=8B=9C=20=EC=A7=93=EB=8A=94=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Bc4JkshDz129z5fXhUWQxz --- incoming/pattern-study/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/incoming/pattern-study/index.html b/incoming/pattern-study/index.html index c69157e0..6bf2ab24 100644 --- a/incoming/pattern-study/index.html +++ b/incoming/pattern-study/index.html @@ -210,8 +210,8 @@

팹턮 연구

- - + +