File size: 2,459 Bytes
f108d90
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import {smoothMotion,smoothSequence,samplePose} from '../smoothing.js';
import {boundsFor,renderStage} from '../motion.js';
const raw=JSON.parse(fs.readFileSync(new URL('../assets/data/fall-real-synthetic-2.json',import.meta.url)));
const acceleration=seq=>{let sum=0,n=0;for(let f=75;f<seq.length-1;f++)for(let j=0;j<22;j++)for(let c=0;c<3;c++){sum+=(seq[f+1][j][c]-2*seq[f][j][c]+seq[f-1][j][c])**2;n++;}return Math.sqrt(sum/n);};
test('Smoothing reduces late-frame jitter without changing saved data or frame count',()=>{
  const before=JSON.stringify(raw),smooth=smoothMotion(raw);
  assert.equal(JSON.stringify(raw),before);
  assert.equal(smooth.sourceA.length,raw.sourceA.length);
  for(const key of ['1','5','9'])assert.ok(acceleration(smooth.variants[key])<acceleration(raw.variants[key])*.3);
  for(let f=0;f<raw.sourceA.length;f++)for(let j=0;j<22;j++)for(let c=0;c<3;c++){
    const neighbors=raw.sourceA.slice(Math.max(0,f-4),f+5).map(p=>p[j][c]);
    assert.ok(smooth.sourceA[f][j][c]>=Math.min(...neighbors)-1e-12&&smooth.sourceA[f][j][c]<=Math.max(...neighbors)+1e-12);
  }
});
test('Filter preserves constant motion and subframe sampling interpolates safely',()=>{
  const constant=Array.from({length:9},()=>[[1,2,3]]);
  for(const frame of smoothSequence(constant))frame[0].forEach((v,i)=>assert.ok(Math.abs(v-(i+1))<1e-12));
  assert.deepEqual(samplePose([[[0,0,0]],[[2,4,6]]],.5),[[1,2,3]]);
  assert.deepEqual(samplePose([[[0,0,0]],[[2,4,6]]],5),[[2,4,6]]);
});
test('Mannequin and skeleton overlay render finite geometry through late fall frames',()=>{
  let fills=0;const context=new Proxy({},{get:(t,k)=>t[k]??((...args)=>{for(const arg of args)if(typeof arg==='number')assert.ok(Number.isFinite(arg),k);if(k==='fill')fills++;if(k==='createLinearGradient')return{addColorStop(){}};}),set:(t,k,v)=>(t[k]=v,true)});
  const canvas={clientWidth:360,clientHeight:244,getContext:()=>context};
  for(const frame of [0,74,90.5,130])for(const yaw of [-90,20,90])for(const skeleton of [false,true])renderStage(canvas,[{key:'sourceA',sequence:raw.sourceA,color:'#587997'}],raw.parents,frame,yaw,1,boundsFor(raw,yaw),{human:true,skeleton});
  assert.ok(fills>100);
});
console.log('Example 2, frames 76–130: second-difference RMS',JSON.stringify({original:acceleration(raw.variants['5']),smoothed:acceleration(smoothMotion(raw).variants['5'])}));