import { useMemo } from 'react';
import { ready, ink, fit } from '../../../lib/mrly.js';
import { mount, Page, Group, Slider, Check, Stats, Stat, Note } from '../../../lib/app.jsx';
import { Sketch } from '../../../lib/draw.jsx';
import { useQuery } from '../../../lib/query.js';
import { useSeeds, seeded, Picker } from '../../../lib/select.jsx';
const m = await ready();
const FIRST = { 2: '7', 3: '495', 4: '57343', 5: '33550335' };
const TALL = 640;
const PAD = 12;
const QUARTER = Math.PI / 2;
function sheet(code, base, level) {
const grid = m.arcs_grid(code, base, level);
const out = { side: grid.width, types: grid.types };
grid.free();
return out;
}
function arcs({ side: n, types }, x0, y0, cell) {
const r = cell / 2;
const loops = new Path2D();
const strands = new Path2D();
const fills = new Path2D();
const sweep = (path, cx, cy, from) => {
path.moveTo(cx + r * Math.cos(from), cy + r * Math.sin(from));
path.arc(cx, cy, r, from, from + QUARTER);
};
for (let y = 0; y < n; y++) {
const top = y0 + (n - 1 - y) * cell;
const bottom = top + cell;
for (let x = 0; x < n; x++) {
const byte = types[y * n + x];
const left = x0 + x * cell;
const right = left + cell;
const lower = byte & 2 ? loops : strands;
const upper = byte & 4 ? loops : strands;
if (byte & 1) {
fills.rect(left, top, cell, cell);
sweep(lower, left, bottom, -QUARTER);
sweep(upper, right, top, QUARTER);
} else {
sweep(lower, right, bottom, Math.PI);
sweep(upper, left, top, 0);
}
}
}
return { loops, strands, fills };
}
function App() {
const s = useSeeds();
const [q, set] = useQuery({ code: seeded(s, 2, 2, '7'), base: 2, level: 5, cells: true });
const cap = m.arcs_cap(q.base);
const level = Math.max(1, Math.min(q.level, cap));
const code = q.code.trim();
const view = useMemo(() => {
try {
return {
name: m.name_of(code, 2, q.base),
read: JSON.parse(m.arcs_read(code, q.base, level)),
grid: sheet(code, q.base, level),
};
} catch (error) {
return { error };
}
}, [code, q.base, level]);
const choose = (patch) => {
if (patch.base !== undefined && patch.code === undefined) {
s.drop();
set({ base: patch.base, code: FIRST[patch.base] });
return;
}
set(patch);
};
const draw = (canvas) => {
const [ctx, w, h] = fit(canvas, Math.min(canvas.clientWidth, TALL));
ctx.clearRect(0, 0, w, h);
if (!view.grid) return;
const span = Math.min(w, h) - 2 * PAD;
const cell = span / view.grid.side;
const paths = arcs(view.grid, (w - span) / 2, (h - span) / 2, cell);
if (q.cells) {
ctx.fillStyle = ink.line;
ctx.fill(paths.fills);
}
ctx.lineWidth = Math.max(0.6, Math.min(3, cell * 0.16));
ctx.strokeStyle = ink.blue;
ctx.stroke(paths.strands);
ctx.strokeStyle = ink.orange;
ctx.stroke(paths.loops);
};
const read = view.read;
const law = read?.law;
const controls = (
set({ level: v })} />
set({ cells: v })} />
);
return (
Every count on this page comes from the crate through wasm, by union-find over the edge midpoints; the page only draws. Row 0 is at the bottom, so code 7 at base 2 leaves its upper-right quarter empty. A law shows when one is proved: the carpet, base 3 code 495, and all sixteen codes at base 2. Nearby: the universe lists the designs, the tile repeats one.>}>
{read && (
{view.name}
{read.side}
{`${read.filled} of ${read.cells}`}
{read.loops}
{read.strands}
strands = 2 side
)}
{read && (
{law ? (
<>
{`L(n) = ${law.formula}`}
{law.loops}
{law.loops === read.loops ? 'the law meets the count' : 'the law misses the count'}
>
) : (
no closed form proved for this code
)}
)}
);
}
mount();