research-spin.ts

2.5 kB · typescript · 72 lines

1import * as core from "mrlyjs/core";2import * as math from "mrlyjs/math";3import type { Ink, Pen } from "mrlyjs/view";45type Profile = { rings: Float64Array; lo: number; hi: number };67const LEVEL = 5;8const SIDE = 243;9const RINGS = 1600;1011export const units = { core, math };1213// FIELD1415function mirror() {16  const carpet = math.two.create(495, 3, LEVEL, 0, 3);17  if (carpet.shape[0] !== SIDE || carpet.shape[1] !== SIDE) throw new Error(`research-spin: carpet ${carpet.shape}, want ${SIDE} square`);18  const flat = { shape: carpet.shape, data: carpet.types };19  const down = core.tensor.flip(flat, 0);20  const quarters = [core.tensor.flip(down, 1), down, core.tensor.flip(flat, 1), flat];21  const whole = core.cell.merge(quarters.map((q) => ({ shape: q.shape, types: q.data })), new Uint32Array([2, 2]));22  return Float32Array.from(whole.types);23}2425let memo: Profile | null = null;2627function profile(): Profile {28  if (memo) return memo;29  const field = mirror();30  const rings = new Float64Array(RINGS + 1);31  for (let k = 0; k <= RINGS; k++) rings[k] = math.spin.ring(field, 2 * SIDE, (SIDE * k) / RINGS);32  let lo = Number.MAX_VALUE;33  let hi = -Number.MAX_VALUE;34  for (const v of rings) {35    lo = Math.min(lo, v);36    hi = Math.max(hi, v);37  }38  if (!(hi - lo > 0.5)) throw new Error(`research-spin: profile spans ${hi - lo}, want over 0.5`);39  memo = { rings, lo, hi };40  return memo;41}4243// DRAW4445export default function draw(pen: Pen, ink: Ink) {46  const { rings, lo, hi } = profile();47  const ramp = new ink.Ramp([ink.ground, ink.blue, ink.yellow]);48  const box = pen.frame(0.08);49  const [cx, cy] = box.center();50  const reach = box.radius();51  const x0 = Math.floor(Math.max(cx - reach - 1, 0));52  const y0 = Math.floor(Math.max(cy - reach - 1, 0));53  const x1 = Math.min(Math.floor(cx + reach + 1), pen.width);54  const y1 = Math.min(Math.floor(cy + reach + 1), pen.height);55  const w = x1 - x0;56  const colors = new Uint8ClampedArray(w * (y1 - y0) * 4);57  for (let py = y0; py < y1; py++) {58    for (let px = x0; px < x1; px++) {59      const dx = px + 0.5 - cx;60      const dy = py + 0.5 - cy;61      const d = Math.sqrt(dx * dx + dy * dy);62      const cover = Math.min(Math.max(reach + 0.5 - d, 0), 1);63      if (cover <= 0) continue;64      const t = (d / reach) * RINGS;65      const i = Math.min(Math.floor(t), RINGS - 1);66      const f = t - i;67      const value = rings[i] * (1 - f) + rings[i + 1] * f;68      colors.set(ink.fade(ramp.at((value - lo) / (hi - lo)), cover), ((py - y0) * w + px - x0) * 4);69    }70  }71  pen.image(x0, y0, w, y1 - y0, { shape: [y1 - y0, w], colors });72}