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}