// Figures for Part II: the square's modes, the anisotropy of the grid, the // three stencils of Chapter 5, and the two rims of Chapter 6. import type { ReactElement } from 'react' import { Figure, Label, Node } from './svg.tsx' import { faded, familyColor, ink, method } from '../plot/palette.ts' const pts = (xy: [number, number][]) => xy.map(([x, y], i) => `${i ? 'L' : 'M'}${x.toFixed(1)} ${y.toFixed(1)}`).join('') /** Six modes of the square membrane, with their frequencies relative to the lowest. */ export function Modes() { const modes: [number, number][] = [ [1, 1], [2, 1], [2, 2], [3, 1], [3, 2], [3, 3], ] const s = 92 const gap = 22 const x0 = (716 - (6 * s + 5 * gap)) / 2 const y0 = 18 const n = 18 return (
{modes.map(([m, k], q) => { const ox = x0 + q * (s + gap) const cells: ReactElement[] = [] for (let i = 0; i < n; i++) { for (let j = 0; j < n; j++) { const v = Math.sin((m * Math.PI * (j + 0.5)) / n) * Math.sin((k * Math.PI * (i + 0.5)) / n) const c = v > 0 ? method.boundary : method.fd2 cells.push( , ) } } return ( {cells} ) })}
) } /** Where a circular wavefront ends up on the grid, for a short wavelength. */ export function Anisotropy() { const cx = 250 const cy = 200 const R = 170 const P = 3 const kh = (2 * Math.PI) / P const speed = (th: number, lam: number) => { const s2 = Math.sin((kh * Math.cos(th)) / 2) ** 2 + Math.sin((kh * Math.sin(th)) / 2) ** 2 return (2 * Math.asin(lam * Math.sqrt(s2))) / (lam * kh) } const lams = [0.3, 0.5, Math.SQRT1_2] const front = (lam: number) => { const out: [number, number][] = [] for (let i = 0; i <= 90; i++) { const th = (Math.PI / 2) * (i / 90) const r = R * speed(th, lam) out.push([cx + r * Math.cos(th), cy - r * Math.sin(th)]) } return pts(out) } return (
{lams.map((l, k) => ( ))} {lams.map((l, k) => ( ))}
) } /** The nodes each of Chapter 5's schemes reads to update one node. */ export function Stencils() { const g = 22 const panel = (ox: number, title: string, sub: string, set: [number, number, string][]) => { const cy = 100 const grid: ReactElement[] = [] for (let i = -2; i <= 2; i++) { for (let j = -2; j <= 2; j++) { grid.push() } } return ( {grid} {set.map(([i, j, c]) => ( ))} ) } const five: [number, number, string][] = [ [0, 0, method.fd2], [0, 1, method.fd2], [0, -1, method.fd2], [1, 0, method.fd2], [-1, 0, method.fd2], ] const cross = (c: string): [number, number, string][] => [ [0, 0, c], [0, 1, c], [0, -1, c], [1, 0, c], [-1, 0, c], [0, 2, c], [0, -2, c], [2, 0, c], [-2, 0, c], ] const both: [number, number, string][] = [ ...cross(method.fd4), [1, 1, faded(method.fd4, 0.35)], [1, -1, faded(method.fd4, 0.35)], [-1, 1, faded(method.fd4, 0.35)], [-1, -1, faded(method.fd4, 0.35)], ] return (
{panel(130, 'second order', 'five-point cross', five)} {panel(358, 'fourth order in space', 'nine-point cross', cross(method.waveguide))} {panel(586, 'fourth order in both', 'thirteen nodes', both)}
) } /** A curved rim on a square grid: staircase against Shortley-Weller. */ export function Rim() { const g = 34 const panel = (ox: number, sw: boolean) => { // a circle of radius R centred below-left of the panel, so that its arc // runs diagonally across it const R = 7.3 const cxg = -2.2 const cyg = -2.6 const inside = (i: number, j: number) => (j - cxg) ** 2 + (i - cyg) ** 2 < R * R const X = (j: number) => ox + j * g const Y = (i: number) => 206 - i * g const arc: [number, number][] = [] for (let k = 0; k <= 60; k++) { const t = (Math.PI / 2) * (k / 60) const j = cxg + R * Math.cos(t) const i = cyg + R * Math.sin(t) if (j >= -0.4 && j <= 6.4 && i >= -0.4 && i <= 5.4) arc.push([X(j), Y(i)]) } const els: ReactElement[] = [] for (let i = 0; i <= 5; i++) { for (let j = 0; j <= 6; j++) { const inn = inside(i, j) els.push( , ) if (inn && sw) { // where the rim crosses the grid line to an outside neighbour if (!inside(i, j + 1) && j + 1 <= 6.4) { const jc = cxg + Math.sqrt(R * R - (i - cyg) ** 2) els.push() els.push() } if (!inside(i + 1, j) && i + 1 <= 5.4) { const ic = cyg + Math.sqrt(R * R - (j - cxg) ** 2) els.push() els.push() } } } } // the staircase: the stencil reaches outside neighbours, which are held // at zero as if the rim ran through them const stair: ReactElement[] = [] if (!sw) { for (let i = 0; i <= 5; i++) { for (let j = 0; j <= 6; j++) { if (!inside(i, j)) continue const nb: [number, number][] = [ [i, j + 1], [i + 1, j], ] for (const [a, b] of nb) { if (a > 5 || b > 6 || inside(a, b)) continue stair.push() stair.push() } } } } return ( {stair} {els} ) } return (
{panel(70, false)} {panel(430, true)}
) }