File size: 2,024 Bytes
1955a0f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import { describe, it, expect } from "vitest";
import { mulberry32, hashModuleId, buildPieceSpecs, ASSEMBLY_EASE } from "./ModuleLaunchAnimation";

describe("launch animation randomness engine", () => {
  it("mulberry32 is deterministic per seed and well-distributed", () => {
    const a = mulberry32(12345);
    const b = mulberry32(12345);
    const seqA = [a(), a(), a(), a()];
    const seqB = [b(), b(), b(), b()];
    expect(seqA).toEqual(seqB);
    seqA.forEach((v) => {
      expect(v).toBeGreaterThanOrEqual(0);
      expect(v).toBeLessThan(1);
    });
  });

  it("different seeds produce different assemblies (re-seed per launch)", () => {
    const specs1 = buildPieceSpecs(mulberry32(hashModuleId("e") ^ 1), 11);
    const specs2 = buildPieceSpecs(mulberry32(hashModuleId("e") ^ 2), 11);
    const sig1 = JSON.stringify(specs1.map((s) => [s.from.x, s.from.y, s.from.rotate, s.from.delay]));
    const sig2 = JSON.stringify(specs2.map((s) => [s.from.x, s.from.y, s.from.rotate, s.from.delay]));
    expect(sig1).not.toEqual(sig2);
  });

  it("piece transforms stay inside the mandated motion budget", () => {
    const specs = buildPieceSpecs(mulberry32(42), 11);
    expect(specs).toHaveLength(11);
    specs.forEach((s) => {
      const distance = Math.hypot(s.from.x, s.from.y);
      expect(distance).toBeGreaterThanOrEqual(240);
      expect(distance).toBeLessThanOrEqual(720);
      expect(Math.abs(s.from.rotate)).toBeGreaterThanOrEqual(8);
      expect(Math.abs(s.from.rotate)).toBeLessThanOrEqual(45);
      expect(s.from.scale).toBeGreaterThanOrEqual(0.55);
      expect(s.from.scale).toBeLessThanOrEqual(1.35);
      // stagger: i*55 + 40..90ms
      expect(s.from.delay).toBeGreaterThan(0);
      // slow-motion duration 2.4–3.2s
      expect(s.from.duration).toBeGreaterThanOrEqual(2.4);
      expect(s.from.duration).toBeLessThanOrEqual(3.2);
    });
  });

  it("uses the mandated slow-motion easing curve", () => {
    expect(Array.from(ASSEMBLY_EASE)).toEqual([0.22, 1, 0.36, 1]);
  });
});