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 });