1// Perturbed twin: rerun with every hit scaled by (1 + eps) and measure, at
2// each frame, the largest difference in any bead coordinate, relative to the
3// largest displacement of the original run.
5import { simulate } from "./solver";
6import type { BeadSystem } from "./types";
8export function runTwin(
9 sys: BeadSystem,
10 saved: Float64Array[], // positions of the original run at each frame
11 dt: number,
12 eps: number,
13 onProgress?: (t: number) => void
14): { audio: Float32Array; sep: Float64Array } {
15 let scale = 0;
16 for (const x of saved) for (let q = 0; q < x.length; q++) scale = Math.max(scale, Math.abs(x[q] - sys.pos0[q]));
17 const sep = new Float64Array(saved.length);
18 const f = 1 + eps;
19 const r = simulate(
20 {
21 ...sys,
22 dt,
23 hits: sys.hits.map(h => ({
24 ...h,
25 velocity: [h.velocity[0] * f, h.velocity[1] * f, h.velocity[2] * f],
26 force: [h.force[0] * f, h.force[1] * f, h.force[2] * f],
27 })),
28 },
29 {
30 maxAnimBytes: 0,
31 onProgress,
32 onFrame: (fr, _t, x) => {
33 const y = saved[fr];
34 if (!y) return;
35 let d = 0;
36 for (let q = 0; q < x.length; q++) d = Math.max(d, Math.abs(x[q] - y[q]));
37 sep[fr] = scale > 0 ? d / scale : 0;
38 },
39 }
40 );
41 return { audio: r.audio, sep };
42}