1import { afterEach, describe, expect, it, vi } from 'vitest';
2import { buildCymbal, buildCymbalExcitation, buildMic, deriveCymbal } from '../src/instruments/bead-cymbal/model';
3import { CYMBAL_PRESETS } from '../src/instruments/bead-cymbal/presets';
4import { simulate } from '../src/sim/solver';
5import type { SimResult } from '../src/sim/types';
7describe('partial results', () => {
8 afterEach(() => vi.restoreAllMocks());
10 it('are prefixes of the finished run (delayed pickup, mallet)', () => {
11 const p = { ...CYMBAL_PRESETS[0].params, rings: 6, duration: 0.01 };
12 const d = deriveCymbal(p);
13 const mic = buildMic(p, d);
14 const opt = { duration: p.duration, fs: p.fs, cfl: p.cfl, frameRate: p.frameRate, pickup: mic.weights, pickupDelay: mic.delay };
15 // a clock that runs fast, so partial results come often
16 let now = 0;
17 vi.spyOn(performance, 'now').mockImplementation(() => (now += 500));
18 const parts: SimResult[] = [];
19 const r = simulate(buildCymbal(d), buildCymbalExcitation(p, d), opt, undefined, (x) => parts.push(x));
20 expect(parts.length).toBeGreaterThan(3);
21 let prev = 0;
22 for (const x of parts) {
23 const S = x.audio.length;
24 const F = x.frameTimes.length;
25 expect(S).toBeGreaterThan(prev);
26 expect(S).toBeLessThan(r.audio.length);
27 prev = S;
28 expect(F).toBe(Math.floor((S - 1) / Math.round(p.fs / p.frameRate)) + 1);
29 expect(x.audio).toEqual(r.audio.subarray(0, S));
30 expect(x.contactForce).toEqual(r.contactForce!.subarray(0, S));
31 expect(x.frames).toEqual(r.frames.subarray(0, 3 * d.n * F));
32 expect(x.energy).toEqual(r.energy.subarray(0, F));
33 expect(x.balance).toEqual(r.balance.subarray(0, F));
34 expect(x.malletPos).toEqual(r.malletPos!.subarray(0, 3 * F));
35 }
36 });
37});