// The Web Worker of the linear model. A worker holds the linear system for // the eigenvalue jobs of modes.ts (inertia counts and slices) and, later, a // slice of the modes for the per-mode losses of damping.ts and the synthesis // parts of synth.ts. pool.ts drives a set of these. import { type LinearSystem } from "../model/assemble.ts"; import { runJob, type Job } from "./modes.ts"; import { type DampingPlate, radiationRates, thermoelasticRates } from "./damping.ts"; import { type ContactResult, type ModalModel, modalTail, movieFrames, nodalAccelerationTable, radiationGains } from "./synth.ts"; export type WorkerRequest = | { type: "system"; n: number; bw: number; band: Float64Array; mass: Float64Array } | { type: "job"; id: number; job: Job } | { type: "model"; model: ModalModel; plate: DampingPlate | null } | { type: "rates"; id: number } | { type: "synth"; id: number; sigma: Float64Array; contact: ContactResult; weight: Float64Array; delay: Float64Array; tStart: number; len: number; fs: number; nSamples: number; audio: boolean; movie: { fps: number; nFrames: number } | null; }; export type WorkerResponse = | { type: "ready" } | { type: "job"; id: number; result: unknown } | { type: "rates"; id: number; rad: Float64Array; te: Float64Array } | { type: "synth"; id: number; table: Float32Array | null; audio: Float32Array | null; frames: Float32Array | null } | { type: "error"; id: number; message: string }; let sys: LinearSystem | null = null; let model: ModalModel | null = null; let plate: DampingPlate | null = null; self.onmessage = (ev: MessageEvent) => { const msg = ev.data; try { if (msg.type === "system") { sys = { n: msg.n, bw: msg.bw, band: msg.band, mass: msg.mass, fields: 1, dofNode: new Int32Array(0), nodeDof: new Int32Array(0) }; post({ type: "ready" }); } else if (msg.type === "job") { if (!sys) throw new Error("no system"); const result = runJob(sys, msg.job); const transfer: ArrayBuffer[] = result.kind === "slice" ? result.shapes.map((s) => s.buffer as ArrayBuffer) : []; post({ type: "job", id: msg.id, result }, transfer); } else if (msg.type === "model") { model = msg.model; plate = msg.plate; post({ type: "ready" }); } else if (msg.type === "rates") { if (!model || !plate) throw new Error("no model or plate"); const rad = radiationRates(model, plate, 0, model.count); const te = thermoelasticRates(model, plate, 0, model.count); post({ type: "rates", id: msg.id, rad, te }, [rad.buffer as ArrayBuffer, te.buffer as ArrayBuffer]); } else if (msg.type === "synth") { if (!model) throw new Error("no model"); const c = msg.contact; let table: Float32Array | null = null, audio: Float32Array | null = null, frames: Float32Array | null = null; if (msg.audio) { table = nodalAccelerationTable(model, c, msg.len); const gains = radiationGains(model, msg.sigma, msg.weight, msg.delay); audio = modalTail(model, msg.sigma, c, gains, msg.fs, msg.tStart, msg.nSamples); } if (msg.movie) frames = movieFrames(model, msg.sigma, c, msg.movie.fps, msg.movie.nFrames); const transfer: ArrayBuffer[] = []; for (const a of [table, audio, frames]) if (a) transfer.push(a.buffer as ArrayBuffer); post({ type: "synth", id: msg.id, table, audio, frames }, transfer); } } catch (e) { post({ type: "error", id: (msg as { id?: number }).id ?? -1, message: e instanceof Error ? e.message : String(e) }); } }; function post(r: WorkerResponse, transfer: ArrayBuffer[] = []): void { (self as unknown as Worker).postMessage(r, transfer); }