Monorepo for Aesthetic.Computer
aesthetic.computer
1// Test animation frame progression and timing
2import { KidLispMini, parse } from './kidlisp-mini/eval.mjs';
3import { TimingEngine } from './kidlisp-mini/timing.mjs';
4
5const source = `fade:red-blue-black-blue-red
6(ink (? rainbow white 0) (1s... 24 64))
7(line w/2 0 w/2 h)
8(spin (2s... -1.125 1.125))
9(zoom 1.1)
10(0.5s (contrast 1.05))
11(scroll (? -0.1 0 0.1) (? -0.1 0 0.1))
12(ink (? cyan yellow magenta) 8)
13(circle w/2 h/2 (? 2 4 8))`;
14
15const ast = parse(source);
16const kidlisp = new KidLispMini();
17const timing = new TimingEngine(42); // seed
18
19// Track API calls
20const apiCalls = [];
21const api = {
22 screen: { width: 1280, height: 800 },
23 wipe: (r, g, b, a) => apiCalls.push({ fn: 'wipe', args: [r, g, b, a] }),
24 ink: (r, g, b, a) => apiCalls.push({ fn: 'ink', args: [r, g, b, a] }),
25 line: (x0, y0, x1, y1) => apiCalls.push({ fn: 'line', args: [x0, y0, x1, y1] }),
26 circle: (cx, cy, r, mode) => apiCalls.push({ fn: 'circle', args: [cx, cy, r, mode] }),
27 fadeBackground: (colors, frame) => apiCalls.push({ fn: 'fadeBackground', colorCount: colors.length, frame }),
28 scroll: (dx, dy) => apiCalls.push({ fn: 'scroll', args: [dx, dy] }),
29 spin: (angle) => apiCalls.push({ fn: 'spin', args: [angle] }),
30 zoom: (factor) => apiCalls.push({ fn: 'zoom', args: [factor] }),
31 contrast: (factor) => apiCalls.push({ fn: 'contrast', args: [factor] }),
32};
33
34kidlisp.setApi(api);
35
36console.log('=== DEBUG: API and Environment ===');
37console.log('API set:', !!kidlisp.api);
38console.log('API.ink:', !!kidlisp.api?.ink);
39console.log('globalEnv.ink:', !!kidlisp.globalEnv.ink);
40console.log();
41
42console.log('=== FRAME-BY-FRAME ANIMATION TEST ===\n');
43
44// Test frames 0, 30 (0.5s at 60fps), 60 (1s), 120 (2s)
45const testFrames = [0, 30, 60, 120];
46
47testFrames.forEach(frameNum => {
48 apiCalls.length = 0; // reset
49 kidlisp.frameCount = frameNum;
50
51 console.log(`Frame ${frameNum} (${(frameNum/60).toFixed(2)}s):`);
52
53 // Evaluate only key expressions to see what's happening
54 // expr 1: ink with timing (1s... 24 64)
55 const inkExpr = ast[1];
56 const inkResult = kidlisp.evalExpr(inkExpr, kidlisp.globalEnv, api, frameNum);
57 console.log(` ink evalExpr result: ${inkResult}`);
58 const inkCall = apiCalls.find(c => c.fn === 'ink');
59 console.log(` ink API called: ${!!inkCall}`);
60 console.log(` ink alpha arg: ${inkCall?.args[3]} (interpolating 24→64 over 1s)`);
61
62 // expr 3: spin with timing (2s... -1.125 1.125)
63 apiCalls.length = 0;
64 const spinExpr = ast[3];
65 const spinResult = kidlisp.evalExpr(spinExpr, kidlisp.globalEnv, api, frameNum);
66 const spinCall = apiCalls.find(c => c.fn === 'spin');
67 const expectedSpin = -1.125 + ((1.125 - (-1.125)) * ((frameNum * 16) % 2000) / 2000);
68 const spinAngle = spinCall?.args[0];
69 console.log(` spin API called: ${!!spinCall}`);
70 console.log(` spin angle: ${typeof spinAngle === 'number' ? spinAngle.toFixed(4) : spinAngle} (expected: ${expectedSpin.toFixed(4)})`);
71
72 // expr 5: 0.5s (contrast 1.05)
73 apiCalls.length = 0;
74 const contrastExpr = ast[5];
75 const contrastResult = kidlisp.evalExpr(contrastExpr, kidlisp.globalEnv, api, frameNum);
76 console.log(` 0.5s contrast result: ${contrastResult?.toFixed(4)} (should be 0→1 over 0.5s)`);
77
78 console.log();
79});
80
81console.log('=== TIMING FORM EVALUATION ===');
82console.log('Checking timing form handling...');
83
84// Check if timing form [timing, startVal, endVal] is being evaluated
85const testTimingExpr = [{ type: 'timing-repeating', ms: 1000 }, 24, 64];
86console.log('Test expr: [timing-repeating(1000ms), 24, 64]');
87for (let f = 0; f <= 60; f += 15) {
88 const result = kidlisp.evalExpr(testTimingExpr, kidlisp.globalEnv, api, f);
89 const progress = (f * 16) % 1000 / 1000;
90 const expected = 24 + (64 - 24) * progress;
91 console.log(` Frame ${f}: result=${result?.toFixed(1)}, expected=${expected.toFixed(1)}`);
92}