index.jsx
8.1 kB · jsx · 136 lines
1import { useMemo } from 'react';2import { ready, ink } from '../../../lib/mrly.js';3import { board, line, axis, rules, tag } from '../../../lib/chart.js';4import { mount, Page, Group, Slider, Stats, Stat, Note } from '../../../lib/app.jsx';5import { Signs, Sketch } from '../../../lib/draw.jsx';6import { useQuery } from '../../../lib/query.js';78const m = await ready();9const PERIODS = 8;10const STEPS = 72;11const SIDE = 486;12const PLANES = 162;13const DOT = 0.75 / SIDE;14const PAD = 14;15const walk = JSON.parse(m.minkowski_walk(PERIODS, STEPS));16const COUNT = walk.u.length;17const START = walk.start;18const END = walk.start + PERIODS * walk.period;19const OPEN = Math.log(walk.cover / walk.edge);20const band = () => ({ plus: ink.fg, minus: ink.blue, empty: ink.deep });21const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, Math.round(v)));22const fixed = (v, n = 9) => (v === null || v === undefined ? 'no closed form' : v.toFixed(n));2324function App() {25 const [pick, set] = useQuery({ at: 140, plane: 81 });26 const at = clamp(pick.at, 0, COUNT - 1);27 const plane = clamp(pick.plane, 0, PLANES / 2);28 const radius = walk.radius[at];29 const u = walk.u[at];3031 const slice = useMemo(() => {32 try {33 return { dist: m.minkowski_slice(plane / PLANES, SIDE), error: null };34 } catch (error) {35 return { dist: null, error };36 }37 }, [plane]);3839 const read = useMemo(() => JSON.parse(m.minkowski_read(radius)), [radius]);4041 const cells = useMemo(() => {42 if (!slice.dist) return null;43 return { width: SIDE, height: SIDE, types: Uint8Array.from(slice.dist, (d) => (d <= DOT ? 0 : d <= radius ? 1 : 2)) };44 }, [slice.dist, radius]);4546 const open = read.profile === null;47 const phase = (((u - START) / walk.period) % 1) * 100;4849 const chart = (canvas) => {50 const b = board(canvas, 340, { pad: PAD, top: 20, bottom: 22 });51 const spread = walk.high - walk.low;52 const floor = walk.low - 1.6 * spread, roof = walk.high + 0.35 * spread;53 const fx = (v) => (v - START) / (END - START);54 const fy = (v) => (v - floor) / (roof - floor);55 b.ctx.fillStyle = ink.line;56 for (let k = 0; k < PERIODS; k += 1) {57 const a = b.x(fx(START + k * walk.period)), c = b.x(fx(START + k * walk.period + OPEN));58 b.ctx.fillRect(a, b.roof, c - a, b.floor - b.roof);59 }60 const marks = [];61 for (let k = 1; k < PERIODS; k += 1) marks.push(fx(START + k * walk.period));62 rules(b, marks, { dash: [2, 4] });63 const runs = (values) => {64 const out = [];65 let run = [];66 values.forEach((v, i) => {67 if (v === null || v < floor) {68 if (run.length > 1) out.push(run);69 run = [];70 return;71 }72 run.push([fx(walk.u[i]), fy(Math.min(v, roof))]);73 });74 if (run.length > 1) out.push(run);75 return out;76 };77 for (const run of runs(walk.profile)) line(b, run, ink.yellow, { width: 1.8 });78 for (const run of runs(walk.reading)) line(b, run, ink.blue, { width: 1.6 });79 rules(b, [fx(u)], { color: ink.pink });80 axis(b, [[0, `ln 1/r ${START.toFixed(2)}`], [1, END.toFixed(2)]], { wall: true });81 const edge = tag(b, 'reading', ink.blue);82 tag(b, `profile p, swing ${walk.swing.toFixed(4)}%`, ink.yellow, 'left', edge + 12);83 };8485 const seek = (f) => set({ at: clamp(f * COUNT - 0.5, 0, COUNT - 1) });8687 const controls = (88 <>89 <Group name="Radius">90 <Slider label="r" value={at} min={0} max={COUNT - 1} show={`${radius.toPrecision(5)}, 1/${(1 / radius).toFixed(1)}`} onChange={(v) => set({ at: v })} />91 </Group>92 <Group name="Slice">93 <Slider label="z" value={plane} min={0} max={PLANES / 2} show={`${plane}/${PLANES}`} onChange={(v) => set({ plane: v })} />94 </Group>95 </>96 );9798 return (99 <Page crumb="minkowski" title="The tube of the Menger sponge and the wave it never leaves"100 sub={<>Fatten the sponge by a radius <code>r</code> and weigh what it swallows. Multiply by <code>r^(D-3)</code>, the power its dimension <code>D</code> asks for, and you get the Minkowski reading, which would settle down if the sponge had a size in its own dimension. Drag the radius: the reading climbs onto a wave that repeats at every factor of 3 and never goes flat.</>}101 controls={controls}102 foot={<>The slice is the plane <code>z = {plane}/{PLANES}</code> through the unit cube, {SIDE} pixels a side, every pixel the exact distance from its centre to the sponge, computed in Rust: the digits of the point descend while each triple holds at most one <code>1</code>, and then the distance is the one to the twelve edges of the centre cube or to the four carpets walling an arm of the plus. Blue is every pixel within <code>r</code>; the sponge itself is the pixels within three quarters of a pixel width of it. <code>T(r)</code> is the tube inside the plus of seven removed cubes, in closed form on <code>(0, 1/6]</code>: the edge cylinders of the centre cube plus arcsine sums over the holes of the 24 wall carpets, with the paper's term <code>24 Deep</code> left out, <code>Deep</code> the corner volume beyond both wall tubes, at most <code>24 x 3.842e-5 < 9.23e-4</code> at <code>1/6</code> and <code>5.81e-9</code> at <code>1/12</code>; so the curves are the upper edge of the certificate, and only its two certified phases, <code>1/12</code> and <code>1/6</code>, carry the proof. Each of the 20 kept subcubes holds the whole picture at a third of the scale, so the volume inside the cube is <code>sum_k (20/27)^k T(3^k r)</code>, and the reading climbs onto the profile <code>p</code>, periodic in <code>ln 1/r</code> with period <code>ln 3</code>. The shaded phases are the radii <code>(1/6, sqrt(2)/6]</code> up to a power of 3, where some <code>T(3^k r)</code> has no closed form yet; there both curves stop, and that third of every period is the paper's open problem. The flat version is <a href="../tube/">the tube</a> of the carpet, and the sponge itself grows in <a href="../sponge/">the sponge</a>. Every distance, volume and profile value comes out of the crates through wasm; the page only draws.</>}>103 <p><span className="chip proved">Proved</span> The Menger sponge is not Minkowski measurable. By the criterion of Kombrink, Pearse and Winter it would be exactly when <code>p</code> is constant, and an interval-arithmetic certificate puts <code>p(1/12)</code> in <code>[2.122718, 2.122723]</code> and <code>p(1/6)</code> in <code>[2.135019, 2.136794]</code>, a swing of at least <code>0.5792%</code>. The proof is <a href="/papers/sponge-measurability/">the paper</a>; the theorem sits on <a href="/research/dimensions/">the dimensions page</a>.</p>104 <div className="arena">105 <div className="panel">106 <h2>The tube in a slice <span>{`z = ${plane}/${PLANES}, radius ${radius.toPrecision(4)}`}</span></h2>107 {cells && <Signs grid={cells} hues={band()} role="img" aria-label="A plane slice of the Menger sponge with the points within the radius in blue" />}108 </div>109 <div className="panel">110 <h2>The reading and its profile <span>{`${PERIODS} factors of 3, shaded where no closed form reaches`}</span></h2>111 <Sketch draw={chart} deps={[at]} onSeek={seek} role="img" aria-label="The Minkowski reading climbing onto the periodic profile against ln 1/r" />112 </div>113 </div>114 <Stats>115 <Stat label="radius r">{radius.toPrecision(9)}</Stat>116 <Stat label="ln 1/r">{u.toFixed(6)}</Stat>117 <Stat label="phase">{`${phase.toFixed(1)}% of the period`}</Stat>118 <Stat label="window">{open ? 'open, no closed form' : 'closed form'}</Stat>119 </Stats>120 <Stats>121 <Stat label="T in the plus">{fixed(read.tube)}</Stat>122 <Stat label="volume in the cube">{fixed(read.volume)}</Stat>123 <Stat label="reading">{fixed(read.reading)}</Stat>124 <Stat label="profile p">{fixed(read.profile)}</Stat>125 </Stats>126 <Stats>127 <Stat label="dimension D">{walk.dimension.toFixed(9)}</Stat>128 <Stat label="p over the walk">{`${walk.low.toFixed(6)} to ${walk.high.toFixed(6)}`}</Stat>129 <Stat label="swing">{`${walk.swing.toFixed(4)}%`}</Stat>130 </Stats>131 <Note error={slice.error} />132 </Page>133 );134}135136mount(<App />);