widget.jsx
1.8 kB · jsx · 50 lines
1import { useMemo, useState } from 'react';2import { ready, ink, rgb } from '../../lib/mrly.js';3import { Row, Slider, Check } from '../../lib/app.jsx';4import { Sketch } from '../../lib/draw.jsx';5import { board, axis } from '../../lib/chart.js';6import { embed } from '../../lib/widget.jsx';78const m = await ready();910export const view = (q) => ({ nodes: JSON.parse(m.farey(q)), stack: JSON.parse(m.farey_novelty(q)) });1112export const bars = (seen, q, marks) => (canvas) => {13 const b = board(canvas, 220, { pad: 24, top: 12, bottom: 30 });14 const { ctx } = b;15 const pale = rgb(ink.fg).join(', ');16 axis(b, [[0, '0'], [1, '1']]);17 for (const [num, den, bright] of seen.nodes) {18 const x = b.x(num / den);19 ctx.strokeStyle = `rgba(${pale}, ${0.14 + 0.7 * bright / q})`;20 ctx.lineWidth = bright > q / 3 ? 1.5 : 0.7;21 ctx.beginPath();22 ctx.moveTo(x, b.floor);23 ctx.lineTo(x, b.y(bright / q));24 ctx.stroke();25 }26 if (marks) {27 ctx.fillStyle = ink.orange;28 for (const [num, den] of seen.nodes) {29 if (seen.stack.primes.includes(den)) ctx.fillRect(b.x(num / den) - 1, b.floor, 2, 7);30 }31 }32};3334export function stack() {35 const [q, setQ] = useState(24);36 const [marks, setMarks] = useState(true);37 const seen = useMemo(() => view(q), [q]);38 return (39 <>40 <Sketch draw={bars(seen, q, marks)} deps={[q, marks]} className="bars" role="img" aria-label="The Farey stack, one bar per fraction, taller where more scales draw it" />41 <Row>42 <Slider label="Q" value={q} min={2} max={80} onChange={setQ} />43 <Check label="mark the primes" checked={marks} onChange={setMarks} />44 </Row>45 <p className="sub">{`scales 1 to ${q} light ${seen.stack.lit} fractions; the primes are ${seen.stack.primes.join(', ')}`}</p>46 </>47 );48}4950embed('farey', { stack });